From 62b4b76483e171e1e9a04c2226e455d427c4af37 Mon Sep 17 00:00:00 2001 From: Apertis CI <devel@lists.apertis.org> Date: Wed, 26 Feb 2025 11:25:49 +0000 Subject: [PATCH 1/3] Import Upstream version 2.12.0+dfsg --- .commitlintrc.js | 19 +- .ctags | 4 + .cz-adapter.cjs | 15 + .cz.json | 2 +- .editorconfig | 5 +- .husky/commit-msg | 3 - .husky/pre-commit | 3 - .husky/pre-commit.copyright | 72 +- .nvmrc | 2 +- .readthedocs.yaml | 5 +- .versionrc.js => .versionrc.cjs | 10 +- Makefile | 535 +- bl1/bl1.ld.S | 12 +- bl1/bl1.mk | 10 +- bl1/bl1_main.c | 29 +- bl2/bl2.ld.S | 6 + bl2/bl2.mk | 8 +- bl2/bl2_el3.ld.S | 6 + bl2/bl2_main.c | 11 +- bl2u/bl2u.ld.S | 6 + bl2u/bl2u.mk | 6 +- bl2u/bl2u_main.c | 5 +- bl31/aarch64/runtime_exceptions.S | 32 +- bl31/bl31.ld.S | 48 +- bl31/bl31.mk | 36 +- bl31/bl31_main.c | 21 +- bl31/bl31_traps.c | 215 +- bl31/ehf.c | 25 +- bl31/interrupt_mgmt.c | 2 +- bl32/sp_min/aarch32/entrypoint.S | 5 +- bl32/sp_min/sp_min.ld.S | 6 + bl32/sp_min/sp_min.mk | 19 +- bl32/sp_min/sp_min_main.c | 19 +- bl32/sp_min/sp_min_private.h | 6 +- bl32/tsp/tsp.ld.S | 6 + bl32/tsp/tsp.mk | 9 +- bl32/tsp/tsp_common.c | 5 +- bl32/tsp/tsp_context.c | 143 + bl32/tsp/tsp_ffa_main.c | 5 +- bl32/tsp/tsp_main.c | 25 +- changelog.yaml | 198 +- common/bl_common.c | 35 +- common/fdt_fixup.c | 19 + common/fdt_wrappers.c | 15 +- common/feat_detect.c | 261 +- docs/Makefile | 18 +- docs/about/contact.rst | 7 +- docs/about/features.rst | 2 +- docs/about/maintainers.rst | 236 +- docs/about/release-information.rst | 25 +- docs/change-log.md | 1963 +++- docs/components/arm-sip-service.rst | 357 +- .../components/context-management-library.rst | 568 ++ docs/components/cot-binding.rst | 70 +- docs/components/fconf/index.rst | 1 + docs/components/fconf/tb_fw_bindings.rst | 159 + docs/components/ffa-manifest-binding.rst | 73 +- docs/components/firmware-update.rst | 2 +- .../granule-protection-tables-design.rst | 93 +- docs/components/index.rst | 3 + .../platform-interrupt-controller-API.rst | 25 +- .../components/realm-management-extension.rst | 2 +- docs/components/rmm-el3-comms-spec.rst | 335 +- docs/components/romlib-design.rst | 19 +- docs/components/sdei.rst | 46 +- docs/components/secure-partition-manager.rst | 1456 +-- docs/components/ven-el3-debugfs.rst | 343 + docs/components/ven-el3-service.rst | 78 + docs/conf.py | 6 +- docs/design/auth-framework.rst | 7 +- docs/design/cpu-specific-build-macros.rst | 155 +- docs/design/firmware-design.rst | 59 +- docs/design/trusted-board-boot.rst | 148 +- docs/design_documents/cmake_framework.rst | 8 +- docs/design_documents/context_mgmt_rework.rst | 4 +- docs/design_documents/index.rst | 2 +- docs/design_documents/measured_boot.rst | 17 +- docs/design_documents/{rss.rst => rse.rst} | 499 +- docs/getting_started/build-internals.rst | 8 + docs/getting_started/build-options.rst | 337 +- docs/getting_started/docs-build.rst | 25 +- docs/getting_started/prerequisites.rst | 160 +- docs/getting_started/rt-svc-writers-guide.rst | 17 +- docs/global_substitutions.txt | 6 + docs/glossary.rst | 18 + docs/index.rst | 4 +- docs/license.rst | 29 + docs/perf/psci-performance-juno.rst | 339 +- docs/perf/psci-performance-n1sdp.rst | 304 +- docs/plat/amd-versal2.rst | 87 + docs/plat/arm/arm-build-options.rst | 33 +- docs/plat/arm/automotive_rd/index.rst | 50 + docs/plat/arm/fvp/fvp-aemv8-base.rst | 154 + docs/plat/arm/fvp/fvp-build-options.rst | 51 + docs/plat/arm/fvp/fvp-cortex-a32.rst | 47 + docs/plat/arm/fvp/fvp-cortex-a57-a53.rst | 52 + docs/plat/arm/fvp/fvp-foundation.rst | 42 + docs/plat/arm/fvp/fvp-specific-configs.rst | 209 + docs/plat/arm/fvp/fvp-support.rst | 102 + docs/plat/arm/fvp/index.rst | 648 +- docs/plat/arm/index.rst | 3 +- docs/plat/arm/juno/index.rst | 6 +- docs/plat/arm/tc/index.rst | 12 +- docs/plat/imx8ulp.rst | 69 + docs/plat/index.rst | 22 +- docs/plat/mt8195.rst | 4 +- docs/plat/rockchip.rst | 4 +- docs/plat/rpi5.rst | 78 + docs/plat/s32g274a.rst | 111 + docs/plat/st/stm32mp1.rst | 12 +- docs/plat/st/stm32mp2.rst | 38 +- docs/plat/st/stm32mpus.rst | 6 +- docs/plat/xilinx-versal-net.rst | 69 + docs/plat/xilinx-versal.rst | 82 +- docs/plat/xilinx-zynqmp.rst | 52 + docs/porting-guide.rst | 205 +- docs/process/code-review-guidelines.rst | 2 +- docs/process/coding-guidelines.rst | 2 - docs/process/coding-style.rst | 18 +- docs/process/commit-style.rst | 2 +- docs/process/contributing.rst | 157 +- docs/process/maintenance.rst | 4 +- docs/process/misra-compliance.csv | 174 + docs/process/security.rst | 7 +- .../diagrams/context_init_coldboot.png | Bin 0 -> 99885 bytes .../diagrams/context_init_warmboot.png | Bin 0 -> 97092 bytes .../diagrams/context_memory_allocation.png | Bin 0 -> 204249 bytes docs/resources/diagrams/cot-dualroot.jpg | Bin 0 -> 134378 bytes docs/resources/diagrams/cot-tbbr.jpg | Bin 0 -> 131981 bytes .../cpu_data_config_context_memory.png | Bin 0 -> 680091 bytes .../resources/diagrams/percpu-data-struct.png | Bin 0 -> 207339 bytes ...on_flow.puml => rse_attestation_flow.puml} | 2 +- .../plantuml/rse_measured_boot_flow.puml | 79 + .../plantuml/rss_measured_boot_flow.puml | 79 - .../{tfa_rss_dfd.puml => tfa_rse_dfd.puml} | 12 +- .../diagrams/root_context_sequence.png | Bin 0 -> 210925 bytes ...tion_flow.svg => rse_attestation_flow.svg} | 4 +- ...ot_flow.svg => rse_measured_boot_flow.svg} | 98 +- .../resources/diagrams/secure_sw_stack_sp.png | Bin 34909 -> 34589 bytes .../diagrams/secure_sw_stack_tos.png | Bin 34202 -> 34126 bytes docs/resources/diagrams/tf-a_attack_tree.png | Bin 0 -> 37881 bytes .../diagrams/tf-a_data_flow_diagram.png | Bin 0 -> 44179 bytes .../diagrams/tf-a_system_diagram.png | Bin 0 -> 191986 bytes docs/security_advisories/index.rst | 1 + .../security-advisory-tfv-10.rst | 2 +- .../security-advisory-tfv-11.rst | 86 + .../security-advisory-tfv-9.rst | 2 +- .../firmware_threat_model/index.rst | 41 + .../threat_model.rst | 143 +- .../threat_model_arm_cca.rst | 4 +- .../threat_model_el3_spm.rst | 4 +- .../threat_model_fvp_r.rst | 2 +- .../threat_model_fw_update_and_recovery.rst | 103 + .../threat_model_rse_interface.rst} | 30 +- docs/threat_model/index.rst | 32 +- .../supply_chain_threat_model.rst | 760 ++ docs/tools/cot-dt2c.rst | 119 + docs/tools/index.rst | 4 +- docs/tools/transfer-list-compiler.rst | 311 + drivers/arm/css/dsu/dsu.c | 135 + drivers/arm/css/scmi/scmi_common.c | 9 +- drivers/arm/css/scp/css_sds.c | 16 +- drivers/arm/css/sds/sds.c | 85 +- drivers/arm/dcc/dcc_console.c | 16 +- drivers/arm/gic/v3/arm_gicv3_common.c | 16 +- drivers/arm/gic/v3/gic600_multichip.c | 4 +- drivers/arm/gic/v3/gicv3_main.c | 102 +- drivers/arm/mhu/mhu_v3_x.c | 475 + drivers/arm/mhu/mhu_v3_x.h | 226 + drivers/arm/mhu/mhu_v3_x_private.h | 222 + drivers/arm/mhu/mhu_wrapper_v2_x.c | 7 +- drivers/arm/mhu/mhu_wrapper_v3_x.c | 462 + .../arm/{rss/rss_comms.c => rse/rse_comms.c} | 60 +- drivers/arm/rse/rse_comms.mk | 35 + .../rse_comms_protocol.c} | 28 +- drivers/arm/rse/rse_comms_protocol.h | 67 + drivers/arm/rse/rse_comms_protocol_common.h | 35 + .../rse_comms_protocol_embed.c} | 25 +- .../rse_comms_protocol_embed.h} | 22 +- .../rse_comms_protocol_pointer_access.c} | 25 +- .../rse_comms_protocol_pointer_access.h} | 18 +- drivers/arm/rss/rss_comms.mk | 22 - drivers/arm/rss/rss_comms_protocol.h | 67 - drivers/arm/smmu/smmu_v3.c | 51 +- drivers/auth/auth_mod.c | 10 +- drivers/auth/cca/bl1_cot.c | 145 + drivers/auth/cca/cot.c | 679 -- drivers/auth/dualroot/bl1_cot.c | 246 + drivers/auth/dualroot/cot.c | 962 -- drivers/auth/mbedtls/mbedtls_common.mk | 53 +- drivers/auth/mbedtls/mbedtls_crypto.c | 22 +- drivers/auth/mbedtls/mbedtls_psa_crypto.c | 456 +- drivers/auth/tbbr/tbbr_cot_bl1.c | 3 +- drivers/auth/tbbr/tbbr_cot_bl2.c | 18 +- drivers/auth/tbbr/tbbr_cot_common.c | 20 +- drivers/cadence/emmc/cdns_sdmmc.c | 981 +- drivers/cadence/nand/cdns_nand.c | 85 +- drivers/clk/clk.c | 21 + drivers/delay_timer/delay_timer.c | 24 +- drivers/delay_timer/generic_delay_timer.c | 28 +- drivers/fwu/fwu.c | 139 +- drivers/measured_boot/rse/dice_prot_env.c | 194 + drivers/measured_boot/rse/dice_prot_env.mk | 29 + drivers/measured_boot/rse/qcbor.mk | 23 + .../rse_measured_boot.c} | 20 +- .../rse_measured_boot.mk} | 14 +- drivers/nxp/auth/csf_hdr_parser/csf_hdr.mk | 16 +- .../nxp/clk/s32cc/include/s32cc-clk-regs.h | 115 + drivers/nxp/clk/s32cc/include/s32cc-mc-me.h | 16 + drivers/nxp/clk/s32cc/include/s32cc-mc-rgm.h | 14 + drivers/nxp/clk/s32cc/mc_me.c | 173 + drivers/nxp/clk/s32cc/mc_rgm.c | 84 + drivers/nxp/clk/s32cc/s32cc_clk.mk | 22 + drivers/nxp/clk/s32cc/s32cc_clk_drv.c | 1481 +++ drivers/nxp/clk/s32cc/s32cc_clk_modules.c | 257 + drivers/nxp/clk/s32cc/s32cc_clk_utils.c | 65 + drivers/nxp/clk/s32cc/s32cc_early_clks.c | 230 + drivers/nxp/console/console.mk | 7 +- drivers/nxp/console/linflex_console.S | 299 + drivers/nxp/ddr/phy-gen2/ddrphy.mk | 4 +- drivers/nxp/drivers.mk | 4 + drivers/nxp/gpio/nxp_gpio.c | 9 +- drivers/partition/partition.c | 128 +- drivers/renesas/common/io/io_rcar.c | 114 +- drivers/renesas/rcar/qos/D3/qos_init_d3.c | 4 +- drivers/renesas/rcar/qos/E3/qos_init_e3_v10.c | 4 +- drivers/renesas/rcar/qos/H3/qos_init_h3_v11.c | 4 +- drivers/renesas/rcar/qos/H3/qos_init_h3_v20.c | 4 +- drivers/renesas/rcar/qos/H3/qos_init_h3_v30.c | 4 +- .../renesas/rcar/qos/H3/qos_init_h3n_v30.c | 4 +- drivers/renesas/rcar/qos/M3/qos_init_m3_v10.c | 4 +- drivers/renesas/rcar/qos/M3/qos_init_m3_v11.c | 4 +- drivers/renesas/rcar/qos/M3/qos_init_m3_v30.c | 4 +- .../renesas/rcar/qos/M3N/qos_init_m3n_v10.c | 4 +- drivers/renesas/rcar/qos/V3M/qos_init_v3m.c | 4 +- drivers/rpi3/gpio/rpi3_gpio.c | 3 +- drivers/rpi3/rng/rpi3_rng.c | 15 +- drivers/scmi-msg/common.h | 10 +- drivers/scmi-msg/entry.c | 9 + drivers/scmi-msg/sensor.c | 277 + drivers/scmi-msg/sensor.h | 125 + drivers/st/bsec/bsec2.c | 340 +- drivers/st/bsec/bsec3.c | 533 ++ drivers/st/clk/clk-stm32-core.c | 79 +- drivers/st/clk/clk-stm32-core.h | 73 +- drivers/st/clk/clk-stm32mp13.c | 330 +- drivers/st/clk/clk-stm32mp2.c | 2357 +++++ drivers/st/clk/stm32mp1_clk.c | 1038 ++- drivers/st/clk/stm32mp_clkfunc.c | 50 +- drivers/st/crypto/stm32_hash.c | 10 +- .../firmware/include/mnpmusrammsgblock_ddr3.h | 935 ++ .../firmware/include/mnpmusrammsgblock_ddr4.h | 2203 +++++ .../include/mnpmusrammsgblock_lpddr4.h | 925 ++ .../phyinit/include/ddrphy_csr_all_cdefines.h | 6944 ++++++++++++++ .../ddr/phy/phyinit/include/ddrphy_phyinit.h | 37 + .../phyinit/include/ddrphy_phyinit_struct.h | 786 ++ .../include/ddrphy_phyinit_usercustom.h | 118 + .../ddr/phy/phyinit/include/ddrphy_wrapper.h | 20 + .../src/ddrphy_phyinit_c_initphyconfig.c | 1141 +++ .../phy/phyinit/src/ddrphy_phyinit_calcmb.c | 210 + .../phyinit/src/ddrphy_phyinit_d_loadimem.c | 43 + .../phyinit/src/ddrphy_phyinit_f_loaddmem.c | 70 + .../phy/phyinit/src/ddrphy_phyinit_g_execfw.c | 66 + .../src/ddrphy_phyinit_i_loadpieimage.c | 394 + .../phyinit/src/ddrphy_phyinit_initstruct.c | 262 + .../src/ddrphy_phyinit_isdbytedisabled.c | 88 + .../src/ddrphy_phyinit_loadpieprodcode.c | 189 + .../phyinit/src/ddrphy_phyinit_mapdrvstren.c | 282 + .../src/ddrphy_phyinit_progcsrskiptrain.c | 893 ++ .../phyinit/src/ddrphy_phyinit_reginterface.c | 170 + .../src/ddrphy_phyinit_restore_sequence.c | 78 + .../phy/phyinit/src/ddrphy_phyinit_sequence.c | 121 + .../phyinit/src/ddrphy_phyinit_softsetmb.c | 100 + .../phyinit/src/ddrphy_phyinit_writeoutmem.c | 79 + ...ddrphy_phyinit_usercustom_custompretrain.c | 90 + .../ddrphy_phyinit_usercustom_g_waitfwdone.c | 183 + .../ddrphy_phyinit_usercustom_saveretregs.c | 399 + drivers/st/ddr/stm32mp1_ddr.c | 20 +- drivers/st/ddr/stm32mp2_ddr.c | 479 + drivers/st/ddr/stm32mp2_ddr_helpers.c | 527 ++ drivers/st/ddr/stm32mp2_ram.c | 210 + drivers/st/ddr/stm32mp_ddr.c | 209 +- drivers/st/ddr/stm32mp_ddr_test.c | 73 +- drivers/st/gpio/stm32_gpio.c | 77 +- drivers/st/i2c/stm32_i2c.c | 67 +- drivers/st/mmc/stm32_sdmmc2.c | 6 +- drivers/st/pmic/stm32mp_pmic.c | 116 +- drivers/st/pmic/stm32mp_pmic2.c | 499 + drivers/st/pmic/stpmic2.c | 474 + drivers/st/regulator/regulator_core.c | 20 +- drivers/st/reset/stm32mp1_reset.c | 19 +- drivers/st/reset/stm32mp2_reset.c | 76 + drivers/ufs/ufs.c | 3 +- fdts/cca_cot_descriptors.dtsi | 269 + fdts/dualroot_cot_descriptors.dtsi | 314 + fdts/fvp-base-psci-common.dtsi | 12 +- fdts/fvp-foundation-gicv2-psci.dts | 4 +- fdts/fvp-foundation-gicv3-psci.dts | 4 +- fdts/rd1ae.dts | 416 + fdts/rtsm_ve-motherboard.dtsi | 19 + fdts/stm32mp1-cot-descriptors.dtsi | 18 +- fdts/stm32mp131.dtsi | 20 +- fdts/stm32mp135f-dk.dts | 25 +- fdts/stm32mp15-fw-config.dtsi | 13 +- fdts/stm32mp151.dtsi | 18 +- fdts/stm32mp151a-prtt1a.dts | 3 +- fdts/stm32mp157a-avenger96.dts | 85 +- fdts/stm32mp157c-ed1.dts | 89 +- fdts/stm32mp157c-odyssey-som.dtsi | 89 +- fdts/stm32mp15xx-dhcom-som.dtsi | 91 +- fdts/stm32mp15xx-dhcor-som.dtsi | 90 +- fdts/stm32mp15xx-dkx.dtsi | 89 +- fdts/stm32mp15xx-osd32.dtsi | 89 +- fdts/stm32mp25-bl2.dtsi | 37 +- fdts/stm32mp25-bl31.dtsi | 13 + fdts/stm32mp25-ddr.dtsi | 253 + ...2mp25-ddr4-2x16Gbits-2x16bits-1200MHz.dtsi | 249 + ...32mp25-ddr4-2x8Gbits-2x16bits-1200MHz.dtsi | 245 + fdts/stm32mp25-fw-config.dtsi | 42 + fdts/stm32mp25-pinctrl.dtsi | 66 +- fdts/stm32mp251.dtsi | 203 +- fdts/stm32mp257f-ev1-ca35tdcid-fw-config.dtsi | 23 + fdts/stm32mp257f-ev1-ca35tdcid-rcc.dtsi | 97 + fdts/stm32mp257f-ev1-fw-config.dts | 7 + fdts/stm32mp257f-ev1.dts | 162 +- ...riptors.dtsi => tbbr_cot_descriptors.dtsi} | 39 +- fdts/tc-base.dtsi | 683 ++ fdts/tc-common.dtsi | 9 + fdts/tc-fpga.dtsi | 36 + fdts/tc-fvp.dtsi | 84 + fdts/tc.dts | 599 -- fdts/tc2.dts | 300 + fdts/tc3-4-base.dtsi | 84 + fdts/tc3.dts | 121 + fdts/tc4.dts | 67 + include/arch/aarch32/arch.h | 38 +- include/arch/aarch32/arch_features.h | 250 +- include/arch/aarch32/arch_helpers.h | 11 +- include/arch/aarch32/asm_macros.S | 9 + include/arch/aarch32/el3_common_macros.S | 8 +- include/arch/aarch64/arch.h | 272 +- include/arch/aarch64/arch_features.h | 486 +- include/arch/aarch64/arch_helpers.h | 201 +- include/arch/aarch64/asm_macros.S | 8 + include/arch/aarch64/el2_common_macros.S | 6 +- include/arch/aarch64/el3_common_macros.S | 142 +- include/bl1/bl1.h | 6 +- include/bl31/bl31.h | 1 - include/bl31/interrupt_mgmt.h | 4 +- include/bl31/sync_handle.h | 5 + include/bl32/tsp/tsp.h | 13 +- include/bl32/tsp/tsp_el1_context.h | 16 + include/common/bl_common.h | 4 +- include/common/build_message.h | 14 + include/common/debug.h | 8 +- include/common/fdt_wrappers.h | 4 +- include/common/feat_detect.h | 7 +- include/common/par.h | 30 + include/common/sha_common_macros.h | 19 + include/drivers/arm/css/css_mhu_doorbell.h | 6 +- include/drivers/arm/css/dsu.h | 42 + include/drivers/arm/css/scmi.h | 4 +- include/drivers/arm/css/sds.h | 35 +- include/drivers/arm/dcc.h | 9 +- include/drivers/arm/fvp/fvp_cpu_pwr.h | 25 + include/drivers/arm/gicv2.h | 2 +- include/drivers/arm/gicv3.h | 6 +- .../drivers/arm/{rss_comms.h => rse_comms.h} | 8 +- .../drivers/auth/mbedtls/mbedtls_config-2.h | 152 - .../drivers/auth/mbedtls/mbedtls_config-3.h | 33 +- .../drivers/auth/mbedtls/psa_mbedtls_config.h | 3 +- include/drivers/auth/tbbr_cot_common.h | 4 +- include/drivers/cadence/cdns_nand.h | 1 + include/drivers/cadence/cdns_sdmmc.h | 327 +- include/drivers/clk.h | 5 + include/drivers/delay_timer.h | 25 +- include/drivers/fwu/fwu.h | 9 +- include/drivers/fwu/fwu_metadata.h | 64 +- .../measured_boot/event_log/event_log.h | 47 +- include/drivers/measured_boot/event_log/tcg.h | 7 +- include/drivers/measured_boot/metadata.h | 72 + .../drivers/measured_boot/rse/dice_prot_env.h | 50 + .../measured_boot/rse/rse_measured_boot.h | 40 + .../measured_boot/rss/rss_measured_boot.h | 57 - include/drivers/nxp/clk/s32cc/s32cc-clk-drv.h | 11 + include/drivers/nxp/clk/s32cc/s32cc-clk-ids.h | 106 + .../drivers/nxp/clk/s32cc/s32cc-clk-modules.h | 398 + .../drivers/nxp/clk/s32cc/s32cc-clk-utils.h | 23 + include/drivers/nxp/console/linflex.h | 18 + include/drivers/nxp/crypto/caam/hash.h | 4 +- include/drivers/partition/partition.h | 10 +- include/drivers/rpi3/mailbox/rpi3_mbox.h | 18 +- include/drivers/st/bsec.h | 102 +- include/drivers/st/bsec2_reg.h | 17 +- include/drivers/st/bsec3_reg.h | 103 + include/drivers/st/stm32_gpio.h | 13 +- include/drivers/st/stm32_i2c.h | 3 +- include/drivers/st/stm32mp1_clk.h | 4 +- include/drivers/st/stm32mp25_rcc.h | 30 +- include/drivers/st/stm32mp2_clk.h | 44 + include/drivers/st/stm32mp2_ddr.h | 147 + include/drivers/st/stm32mp2_ddr_helpers.h | 35 + include/drivers/st/stm32mp2_ddr_regs.h | 38 + include/drivers/st/stm32mp2_pwr.h | 478 + include/drivers/st/stm32mp2_ram.h | 13 + include/drivers/st/stm32mp_clkfunc.h | 6 +- include/drivers/st/stm32mp_ddr.h | 21 +- include/drivers/st/stm32mp_ddrctrl_regs.h | 33 +- include/drivers/st/stm32mp_pmic.h | 9 +- include/drivers/st/stm32mp_pmic2.h | 51 + include/drivers/st/stm32mp_reset.h | 7 +- include/drivers/st/stm32mp_risab_regs.h | 271 + include/drivers/st/stpmic2.h | 307 + include/drivers/usb_device.h | 3 +- include/dt-bindings/clock/stm32mp13-clks.h | 10 +- include/dt-bindings/clock/stm32mp15-clksrc.h | 660 +- include/dt-bindings/clock/stm32mp25-clks.h | 20 +- include/dt-bindings/clock/stm32mp25-clksrc.h | 15 +- include/dt-bindings/gpio/stm32-gpio.h | 41 + include/dt-bindings/reset/stm32mp25-resets.h | 30 +- include/lib/cpus/aarch32/cpu_macros.S | 63 +- include/lib/cpus/aarch64/cortex_a35.h | 5 +- include/lib/cpus/aarch64/cortex_a520.h | 21 +- include/lib/cpus/aarch64/cortex_a710.h | 7 +- include/lib/cpus/aarch64/cortex_a715.h | 17 +- include/lib/cpus/aarch64/cortex_a720.h | 17 +- .../{neoverse_hermes.h => cortex_a720_ae.h} | 16 +- .../{cortex_blackhawk.h => cortex_a725.h} | 17 +- include/lib/cpus/aarch64/cortex_a75.h | 7 +- include/lib/cpus/aarch64/cortex_a78c.h | 5 + include/lib/cpus/aarch64/cortex_arcadia.h | 24 + include/lib/cpus/aarch64/cortex_x2.h | 7 +- include/lib/cpus/aarch64/cortex_x3.h | 18 +- include/lib/cpus/aarch64/cortex_x4.h | 26 +- .../{cortex_chaberton.h => cortex_x925.h} | 17 +- include/lib/cpus/aarch64/cpu_macros.S | 80 +- include/lib/cpus/aarch64/dsu_def.h | 4 + include/lib/cpus/aarch64/neoverse_n3.h | 24 + include/lib/cpus/aarch64/neoverse_poseidon.h | 27 - include/lib/cpus/aarch64/neoverse_v1.h | 1 + include/lib/cpus/aarch64/neoverse_v2.h | 8 + include/lib/cpus/aarch64/neoverse_v3.h | 28 + include/lib/cpus/cpu_ops.h | 8 +- include/lib/cpus/errata.h | 26 +- include/lib/debugfs.h | 22 +- include/lib/dice/dice.h | 166 + include/lib/el3_runtime/aarch64/context.h | 476 +- include/lib/el3_runtime/context_debug.h | 19 + include/lib/el3_runtime/context_el1.h | 325 + include/lib/el3_runtime/context_el2.h | 391 + include/lib/el3_runtime/context_mgmt.h | 20 +- include/lib/el3_runtime/cpu_data.h | 23 +- include/lib/el3_runtime/simd_ctx.h | 97 + include/lib/extensions/brbe.h | 8 +- include/lib/extensions/debug_v8p9.h | 20 + include/lib/extensions/fgt2.h | 20 + include/lib/extensions/mpam.h | 4 +- include/lib/extensions/spe.h | 15 +- include/lib/extensions/sve.h | 8 +- include/lib/extensions/sysreg128.h | 36 + include/lib/extensions/tcr2.h | 24 + include/lib/extensions/trbe.h | 12 +- include/lib/extensions/trf.h | 24 +- include/lib/pmf/pmf.h | 31 +- include/lib/psa/cca_attestation.h | 20 + include/lib/psa/delegated_attestation.h | 16 +- include/lib/psa/dice_protection_environment.h | 100 + include/lib/psa/measured_boot.h | 23 +- include/lib/psa/psa/client.h | 20 +- include/lib/psa/psa_manifest/sid.h | 19 +- include/lib/psa/rse_crypto_defs.h | 79 + ...{rss_platform_api.h => rse_platform_api.h} | 18 +- include/lib/psa/rss_crypto_defs.h | 58 - include/lib/psci/psci_lib.h | 3 +- include/lib/smccc.h | 8 +- include/lib/spinlock.h | 12 +- include/lib/transfer_list.h | 115 +- include/lib/utils_def.h | 30 +- include/lib/xlat_tables/xlat_tables_defs.h | 6 +- include/plat/arm/board/common/board_css_def.h | 3 - .../plat/arm/board/common/rotpk/rotpk_def.h | 24 + include/plat/arm/board/common/v2m_def.h | 25 + include/plat/arm/common/arm_def.h | 33 +- include/plat/arm/common/arm_sip_svc.h | 18 +- include/plat/arm/common/arm_tzc_dram.ld.S | 3 + include/plat/arm/common/fconf_arm_sp_getter.h | 7 +- include/plat/arm/common/plat_arm.h | 64 +- include/plat/arm/css/common/css_def.h | 7 +- include/plat/common/common_def.h | 35 +- include/plat/common/plat_drtm.h | 6 +- include/plat/common/platform.h | 47 +- .../plat/nuvoton/common/npcm845x_arm_def.h | 16 +- include/plat/nuvoton/common/plat_macros.S | 3 +- include/plat/nuvoton/npcm845x/platform_def.h | 8 +- include/services/drtm_svc.h | 10 +- include/services/ffa_svc.h | 22 +- .../oem/chromeos/widevine_smc_handlers.h | 65 + include/services/rmm_core_manifest.h | 55 +- include/services/rmm_el3_token_sign.h | 63 + include/services/rmmd_svc.h | 48 +- include/services/spmd_svc.h | 3 +- include/services/ven_el3_svc.h | 32 + include/tools_share/cca_oid.h | 6 +- include/tools_share/tbbr_oid.h | 8 +- lib/aarch64/cache_helpers.S | 93 +- lib/aarch64/misc_helpers.S | 20 +- lib/compiler-rt/builtins/assembly.h | 5 +- lib/compiler-rt/builtins/int_lib.h | 10 +- lib/compiler-rt/builtins/int_types.h | 16 +- lib/cpus/aarch32/aem_generic.S | 10 +- lib/cpus/aarch32/cortex_a12.S | 4 +- lib/cpus/aarch32/cortex_a15.S | 4 +- lib/cpus/aarch32/cortex_a17.S | 4 +- lib/cpus/aarch32/cortex_a32.S | 4 +- lib/cpus/aarch32/cortex_a5.S | 4 +- lib/cpus/aarch32/cortex_a53.S | 4 +- lib/cpus/aarch32/cortex_a57.S | 4 +- lib/cpus/aarch32/cortex_a7.S | 4 +- lib/cpus/aarch32/cortex_a72.S | 4 +- lib/cpus/aarch32/cortex_a9.S | 4 +- lib/cpus/aarch64/a64fx.S | 9 - lib/cpus/aarch64/aem_generic.S | 11 +- lib/cpus/aarch64/cortex_a35.S | 4 +- lib/cpus/aarch64/cortex_a510.S | 4 +- lib/cpus/aarch64/cortex_a520.S | 37 +- lib/cpus/aarch64/cortex_a53.S | 4 +- lib/cpus/aarch64/cortex_a55.S | 4 +- lib/cpus/aarch64/cortex_a57.S | 6 +- lib/cpus/aarch64/cortex_a65.S | 22 +- lib/cpus/aarch64/cortex_a65ae.S | 4 +- lib/cpus/aarch64/cortex_a710.S | 12 +- lib/cpus/aarch64/cortex_a715.S | 93 +- lib/cpus/aarch64/cortex_a72.S | 4 +- lib/cpus/aarch64/cortex_a720.S | 38 +- .../{neoverse_hermes.S => cortex_a720_ae.S} | 41 +- .../{cortex_blackhawk.S => cortex_a725.S} | 40 +- lib/cpus/aarch64/cortex_a73.S | 5 +- lib/cpus/aarch64/cortex_a75.S | 6 +- lib/cpus/aarch64/cortex_a76.S | 6 +- lib/cpus/aarch64/cortex_a76ae.S | 4 +- lib/cpus/aarch64/cortex_a77.S | 5 +- lib/cpus/aarch64/cortex_a78.S | 6 +- lib/cpus/aarch64/cortex_a78_ae.S | 4 +- lib/cpus/aarch64/cortex_a78c.S | 27 +- .../{cortex_chaberton.S => cortex_arcadia.S} | 40 +- lib/cpus/aarch64/cortex_gelas.S | 6 +- lib/cpus/aarch64/cortex_x1.S | 2 - lib/cpus/aarch64/cortex_x2.S | 21 +- lib/cpus/aarch64/cortex_x3.S | 46 +- lib/cpus/aarch64/cortex_x4.S | 64 +- lib/cpus/aarch64/cortex_x925.S | 64 + lib/cpus/aarch64/cpu_helpers.S | 4 +- lib/cpus/aarch64/denver.S | 4 +- lib/cpus/aarch64/dsu_helpers.S | 27 +- lib/cpus/aarch64/generic.S | 3 +- lib/cpus/aarch64/neoverse_e1.S | 4 +- lib/cpus/aarch64/neoverse_n1.S | 6 +- lib/cpus/aarch64/neoverse_n2.S | 13 +- lib/cpus/aarch64/neoverse_n3.S | 69 + lib/cpus/aarch64/neoverse_poseidon.S | 86 - lib/cpus/aarch64/neoverse_v1.S | 13 +- lib/cpus/aarch64/neoverse_v2.S | 29 +- lib/cpus/aarch64/neoverse_v3.S | 88 + lib/cpus/aarch64/nevis.S | 4 +- lib/cpus/aarch64/qemu_max.S | 4 +- lib/cpus/aarch64/rainier.S | 4 +- lib/cpus/aarch64/travis.S | 6 +- lib/cpus/cpu-ops.mk | 148 +- lib/cpus/errata_common.c | 42 + lib/cpus/errata_report.c | 68 +- lib/el3_runtime/aarch32/context_mgmt.c | 12 +- lib/el3_runtime/aarch64/context.S | 544 +- lib/el3_runtime/aarch64/context_debug.c | 208 + lib/el3_runtime/aarch64/context_mgmt.c | 1006 +- lib/el3_runtime/simd_ctx.c | 81 + lib/extensions/brbe/brbe.c | 18 +- lib/extensions/debug/debugv8p9.c | 26 + lib/extensions/fgt/fgt2.c | 27 + lib/extensions/mpam/mpam.c | 8 +- lib/extensions/pmuv3/aarch32/pmuv3.c | 4 - lib/extensions/pmuv3/aarch64/pmuv3.c | 14 +- lib/extensions/sme/sme.c | 4 +- lib/extensions/spe/spe.c | 76 +- lib/extensions/sysreg128/sysreg128.S | 139 + lib/extensions/tcr/tcr2.c | 42 + lib/extensions/trbe/trbe.c | 33 +- lib/extensions/trf/aarch64/trf.c | 12 +- lib/fconf/fconf_cot_getter.c | 18 +- lib/gpt_rme/gpt_rme.c | 1328 ++- lib/gpt_rme/gpt_rme.mk | 16 +- lib/gpt_rme/gpt_rme_private.h | 229 +- lib/libc/libc.mk | 41 +- lib/libc/libc_asm.mk | 35 +- lib/libc/libc_common.mk | 42 + lib/libc/printf.c | 22 + lib/libfdt/libfdt.mk | 28 +- lib/locks/exclusive/aarch64/spinlock.S | 44 +- lib/pmf/pmf_smc.c | 16 +- lib/psa/cca_attestation.c | 66 + lib/psa/delegated_attestation.c | 12 +- lib/psa/dice_protection_environment.c | 370 + lib/psa/measured_boot.c | 58 +- lib/psa/measured_boot_private.h | 8 +- lib/psa/{rss_platform.c => rse_platform.c} | 26 +- lib/psci/aarch64/psci_helpers.S | 8 +- lib/psci/aarch64/runtime_errata.S | 27 - lib/psci/psci_common.c | 34 +- lib/psci/psci_lib.mk | 5 +- lib/romlib/Makefile | 98 +- lib/romlib/romlib_generator.py | 41 +- lib/romlib/templates/wrapper.S | 5 +- lib/romlib/templates/wrapper_bti.S | 5 +- lib/transfer_list/transfer_list.c | 205 +- lib/transfer_list/transfer_list.mk | 3 +- lib/xlat_mpu/aarch64/xlat_mpu_arch.c | 4 +- lib/xlat_tables/aarch64/xlat_tables.c | 6 +- lib/xlat_tables_v2/aarch32/xlat_tables_arch.c | 4 +- lib/xlat_tables_v2/aarch64/xlat_tables_arch.c | 19 +- lib/xlat_tables_v2/xlat_tables_core.c | 4 +- lib/xlat_tables_v2/xlat_tables_utils.c | 56 +- licenses/LICENSE-APACHE-2.0.txt | 202 + make_helpers/arch_features.mk | 145 +- make_helpers/build-rules.mk | 18 + make_helpers/build_env.mk | 9 +- make_helpers/build_macros.mk | 295 +- make_helpers/common.mk | 17 + make_helpers/defaults.mk | 55 +- make_helpers/march.mk | 15 +- make_helpers/plat_helpers.mk | 4 + make_helpers/toolchain.mk | 436 + make_helpers/toolchains/aarch32.mk | 39 + make_helpers/toolchains/aarch64.mk | 46 + make_helpers/toolchains/host.mk | 44 + make_helpers/toolchains/rk3399-m0.mk | 31 + make_helpers/unix.mk | 30 +- make_helpers/utilities.mk | 129 + make_helpers/windows.mk | 33 +- package-lock.json | 8240 ++++++----------- package.json | 19 +- plat/allwinner/common/include/sunxi_private.h | 8 + plat/allwinner/common/sunxi_bl31_setup.c | 5 +- plat/allwinner/common/sunxi_prepare_dtb.c | 2 + plat/allwinner/sun50i_a64/platform.mk | 4 + .../allwinner/sun50i_a64/platform_defaults.mk | 9 + plat/allwinner/sun50i_h616/platform.mk | 5 +- plat/allwinner/sun50i_h616/sunxi_h616_dtb.c | 72 + plat/allwinner/sun50i_h616/sunxi_power.c | 213 +- plat/amd/versal2/aarch64/common.c | 152 + plat/amd/versal2/aarch64/helpers.S | 68 + plat/amd/versal2/bl31_setup.c | 264 + .../versal2/gicv3.c} | 72 +- plat/amd/versal2/include/def.h | 188 + plat/amd/versal2/include/plat_ipi.h | 72 + plat/amd/versal2/include/plat_macros.S | 118 + plat/amd/versal2/include/plat_pm_common.h | 25 + plat/amd/versal2/include/plat_private.h | 55 + plat/amd/versal2/include/platform_def.h | 141 + plat/amd/versal2/include/scmi.h | 28 + plat/amd/versal2/include/versal2-scmi.h | 144 + plat/amd/versal2/plat_psci.c | 241 + plat/amd/versal2/plat_topology.c | 63 + plat/amd/versal2/platform.mk | 159 + plat/amd/versal2/scmi.c | 671 ++ plat/amd/versal2/sip_svc_setup.c | 117 + plat/amd/versal2/soc_ipi.c | 73 + plat/amd/versal2/tsp/tsp-versal2.mk | 10 + plat/amlogic/axg/platform.mk | 11 +- plat/amlogic/g12a/platform.mk | 11 +- plat/amlogic/gxbb/platform.mk | 6 +- plat/amlogic/gxl/platform.mk | 11 +- plat/arm/board/a5ds/fdts/a5ds_fw_config.dts | 5 +- plat/arm/board/a5ds/include/platform_def.h | 5 +- plat/arm/board/arm_fpga/fpga_def.h | 2 +- plat/arm/board/arm_fpga/platform.mk | 29 +- .../platform/rd1ae/fdts/rd1ae_fw_config.dts | 21 + .../platform/rd1ae/include/plat_macros.S | 25 + .../platform/rd1ae/include/platform_def.h | 157 + .../platform/rd1ae/include/rd1ae_helpers.S | 45 + .../automotive_rd/platform/rd1ae/platform.mk | 88 + .../rd1ae/rd1ae_bl2_mem_params_desc.c | 67 + .../platform/rd1ae/rd1ae_bl31_setup.c | 28 + .../automotive_rd/platform/rd1ae/rd1ae_err.c | 22 + .../automotive_rd/platform/rd1ae/rd1ae_plat.c | 56 + .../automotive_rd/platform/rd1ae/rd1ae_tbb.c | 34 + .../platform/rd1ae/rd1ae_topology.c | 70 + .../arm/board/common/board_arm_trusted_boot.c | 11 +- plat/arm/board/common/rotpk/arm_dev_rotpk.S | 11 +- .../swd_rotpk/arm_swd_rotpk_rsa_sha256.bin | 1 - .../common/corstone1000_bl2_mem_params_desc.c | 12 +- .../common/corstone1000_helpers.S | 28 +- .../corstone1000/common/corstone1000_plat.c | 3 +- .../corstone1000/common/corstone1000_pm.c | 58 +- .../common/include/platform_def.h | 60 +- plat/arm/board/corstone1000/platform.mk | 18 +- plat/arm/board/fvp/aarch64/fvp_helpers.S | 19 +- plat/arm/board/fvp/aarch64/fvp_lsp_ras_sp.c | 121 + .../board/fvp/fdts/fvp_cactus_sp_manifest.dts | 28 + plat/arm/board/fvp/fdts/fvp_cot_desc.dtsi | 16 + plat/arm/board/fvp/fdts/fvp_fw_config.dts | 5 +- .../fvp/fdts/fvp_spmc_el1_optee_manifest.dts | 22 + plat/arm/board/fvp/fdts/fvp_spmc_manifest.dts | 23 +- .../fvp/fdts/fvp_spmc_optee_sp_manifest.dts | 23 +- plat/arm/board/fvp/fdts/fvp_tb_fw_config.dts | 17 +- plat/arm/board/fvp/fdts/optee_sp_manifest.dts | 14 +- plat/arm/board/fvp/fvp_bl1_measured_boot.c | 42 +- plat/arm/board/fvp/fvp_bl2_measured_boot.c | 82 +- plat/arm/board/fvp/fvp_bl2_setup.c | 96 +- plat/arm/board/fvp/fvp_bl31_setup.c | 27 +- plat/arm/board/fvp/fvp_common.c | 174 +- plat/arm/board/fvp/fvp_common_measured_boot.c | 23 +- plat/arm/board/fvp/fvp_cpu_errata.mk | 61 +- plat/arm/board/fvp/fvp_cpu_pwr.c | 45 + plat/arm/board/fvp/fvp_el3_spmc.c | 15 +- plat/arm/board/fvp/fvp_el3_token_sign.c | 98 + plat/arm/board/fvp/fvp_plat_attest_token.c | 323 +- plat/arm/board/fvp/fvp_pm.c | 27 +- plat/arm/board/fvp/fvp_realm_attest_key.c | 2 +- plat/arm/board/fvp/fvp_topology.c | 6 +- .../arm/board/fvp/include/fvp_pas_def.h | 12 +- plat/arm/board/fvp/include/plat.ld.S | 30 +- plat/arm/board/fvp/include/platform_def.h | 50 +- plat/arm/board/fvp/jmptbl.i | 1 - plat/arm/board/fvp/platform.mk | 209 +- plat/arm/board/fvp_r/fvp_r_bl1_main.c | 27 +- plat/arm/board/fvp_r/fvp_r_bl1_setup.c | 4 +- plat/arm/board/fvp_r/include/platform_def.h | 13 +- plat/arm/board/fvp_r/platform.mk | 4 +- .../board/fvp_ve/fdts/fvp_ve_fw_config.dts | 5 +- plat/arm/board/fvp_ve/fvp_ve_def.h | 11 - plat/arm/board/fvp_ve/include/platform_def.h | 5 +- plat/arm/board/juno/fdts/juno_fw_config.dts | 5 +- plat/arm/board/juno/include/platform_def.h | 15 +- plat/arm/board/juno/juno_bl1_setup.c | 7 +- plat/arm/board/juno/juno_common.c | 19 +- plat/arm/board/juno/juno_tbbr_cot_bl2.c | 17 +- plat/arm/board/juno/platform.mk | 4 +- .../board/morello/fdts/morello_fw_config.dts | 5 +- plat/arm/board/morello/include/platform_def.h | 9 +- plat/arm/board/morello/morello_bl2_setup.c | 7 +- plat/arm/board/morello/morello_bl31_setup.c | 7 +- plat/arm/board/morello/morello_image_load.c | 14 +- plat/arm/board/morello/morello_plat.c | 17 +- plat/arm/board/n1sdp/include/platform_def.h | 9 +- plat/arm/board/n1sdp/n1sdp_bl2_setup.c | 8 +- plat/arm/board/n1sdp/n1sdp_bl31_setup.c | 7 +- plat/arm/board/n1sdp/n1sdp_image_load.c | 8 +- plat/arm/board/n1sdp/n1sdp_plat.c | 17 +- .../common/arch/aarch64/nrd_helper.S} | 16 +- .../common/include/nrd1/nrd_css_def1.h | 43 + .../common/include/nrd1/nrd_css_fw_def1.h | 92 + .../common/include/nrd1/nrd_plat_arm_def1.h | 249 + .../common/include/nrd1/nrd_ros_def1.h | 35 + .../common/include/nrd1/nrd_ros_fw_def1.h | 63 + .../common/include/nrd2/nrd_css_def2.h | 47 + .../common/include/nrd2/nrd_css_fw_def2.h | 107 + .../common/include/nrd2/nrd_plat_arm_def2.h | 366 + .../common/include/nrd2/nrd_ros_def2.h | 41 + .../common/include/nrd2/nrd_ros_fw_def2.h | 84 + .../common/include/nrd3/nrd_css_def3.h | 268 + .../common/include/nrd3/nrd_css_fw_def3.h | 128 + .../common/include/nrd3/nrd_pas_def3.h | 584 ++ .../common/include/nrd3/nrd_plat_arm_def3.h | 784 ++ .../common/include/nrd3/nrd_ros_def3.h | 29 + .../common/include/nrd3/nrd_ros_fw_def3.h | 44 + .../common/include/nrd_dmc620_tzc_regions.h} | 12 +- .../neoverse_rd/common/include/nrd_plat.h | 13 + .../neoverse_rd/common/include/nrd_ras.h} | 30 +- .../neoverse_rd/common/include/nrd_sdei.h | 25 + .../neoverse_rd/common/include/nrd_variant.h} | 30 +- .../neoverse_rd/common}/include/plat_macros.S | 2 +- .../neoverse_rd/common/nrd-common.mk} | 34 +- .../board/neoverse_rd/common/nrd_bl1_setup.c | 19 + .../board/neoverse_rd/common/nrd_bl31_setup.c | 259 + .../neoverse_rd/common/nrd_image_load.c} | 30 +- .../neoverse_rd/common/nrd_interconnect.c} | 2 +- .../neoverse_rd/common/nrd_plat1.c} | 61 +- .../neoverse_rd/common/nrd_plat2.c} | 73 +- plat/arm/board/neoverse_rd/common/nrd_plat3.c | 102 + .../neoverse_rd/common/nrd_topology.c} | 8 +- .../neoverse_rd/common/ras/nrd_ras_common.c} | 36 +- .../neoverse_rd/common/ras/nrd_ras_cpu.c} | 132 +- .../neoverse_rd/common/ras/nrd_ras_sram.c} | 25 +- .../rdn1edge/fdts/rdn1edge_fw_config.dts | 2 +- .../rdn1edge/fdts/rdn1edge_nt_fw_config.dts | 2 +- .../rdn1edge/fdts/rdn1edge_tb_fw_config.dts | 2 +- .../platform/rdn1edge/include/platform_def.h | 44 + .../platform}/rdn1edge/platform.mk | 38 +- .../platform}/rdn1edge/rdn1edge_err.c | 2 +- .../platform}/rdn1edge/rdn1edge_plat.c | 38 +- .../platform}/rdn1edge/rdn1edge_security.c | 6 +- .../platform}/rdn1edge/rdn1edge_topology.c | 16 +- .../rdn1edge/rdn1edge_trusted_boot.c | 2 +- .../platform/rdn2/fdts/rdn2_fw_config.dts} | 10 +- .../platform}/rdn2/fdts/rdn2_nt_fw_config.dts | 2 +- .../rdn2/fdts/rdn2_stmm_sel0_manifest.dts | 147 + .../platform}/rdn2/fdts/rdn2_tb_fw_config.dts | 2 +- .../platform/rdn2/include/platform_def.h | 93 + .../platform/rdn2/include/rdn2_ras.h | 14 + .../platform}/rdn2/platform.mk | 82 +- .../platform}/rdn2/rdn2_err.c | 2 +- .../platform}/rdn2/rdn2_plat.c | 96 +- .../platform}/rdn2/rdn2_ras.c | 27 +- .../platform}/rdn2/rdn2_security.c | 30 +- .../platform}/rdn2/rdn2_topology.c | 50 +- .../platform}/rdn2/rdn2_trusted_boot.c | 2 +- .../platform}/rdv1/fdts/rdv1_fw_config.dts | 2 +- .../platform}/rdv1/fdts/rdv1_nt_fw_config.dts | 2 +- .../platform}/rdv1/fdts/rdv1_tb_fw_config.dts | 2 +- .../platform}/rdv1/include/platform_def.h | 30 +- .../platform}/rdv1/platform.mk | 34 +- .../platform}/rdv1/rdv1_err.c | 2 +- .../platform}/rdv1/rdv1_plat.c | 13 +- .../platform}/rdv1/rdv1_security.c | 2 +- .../platform}/rdv1/rdv1_topology.c | 34 +- .../platform}/rdv1/rdv1_trusted_boot.c | 2 +- .../rdv1mc/fdts/rdv1mc_fw_config.dts | 2 +- .../rdv1mc/fdts/rdv1mc_nt_fw_config.dts | 2 +- .../rdv1mc/fdts/rdv1mc_tb_fw_config.dts | 2 +- .../platform}/rdv1mc/include/platform_def.h | 30 +- .../platform}/rdv1mc/platform.mk | 38 +- .../platform}/rdv1mc/rdv1mc_err.c | 2 +- .../platform}/rdv1mc/rdv1mc_plat.c | 78 +- .../platform}/rdv1mc/rdv1mc_security.c | 24 +- .../platform}/rdv1mc/rdv1mc_topology.c | 53 +- .../platform}/rdv1mc/rdv1mc_trusted_boot.c | 2 +- .../platform/rdv3/fdts/rdv3_fw_config.dts} | 6 +- .../platform/rdv3/fdts/rdv3_nt_fw_config.dts} | 5 +- .../platform/rdv3/fdts/rdv3_tb_fw_config.dts} | 2 +- .../platform/rdv3/include/platform_def.h | 51 + .../platform/rdv3/include/rdv3_mhuv3.h | 12 + .../platform/rdv3/include/rdv3_rse_comms.h | 12 + .../neoverse_rd/platform/rdv3/platform.mk | 156 + .../platform/rdv3/rdv3_bl1_measured_boot.c | 60 + .../platform/rdv3/rdv3_bl2_measured_boot.c | 69 + .../platform/rdv3/rdv3_bl2_setup.c | 97 + .../platform/rdv3/rdv3_bl31_setup.c | 221 + .../neoverse_rd/platform/rdv3/rdv3_common.c | 183 + .../platform/rdv3/rdv3_common_measured_boot.c | 41 + .../neoverse_rd/platform/rdv3/rdv3_err.c | 17 + .../neoverse_rd/platform/rdv3/rdv3_mhuv3.c | 23 + .../platform/rdv3/rdv3_plat_attest_token.c | 35 + .../platform/rdv3/rdv3_realm_attest_key.c | 26 + .../neoverse_rd/platform/rdv3/rdv3_security.c | 10 + .../neoverse_rd/platform/rdv3/rdv3_topology.c | 100 + .../platform/rdv3/rdv3_trusted_boot.c} | 2 +- .../sgi575/fdts/sgi575_fw_config.dts | 2 +- .../sgi575/fdts/sgi575_nt_fw_config.dts | 2 +- .../sgi575/fdts/sgi575_tb_fw_config.dts | 2 +- .../platform/sgi575/include/platform_def.h | 37 + .../platform}/sgi575/platform.mk | 36 +- .../platform}/sgi575/sgi575_err.c | 2 +- .../platform}/sgi575/sgi575_plat.c | 15 +- .../platform}/sgi575/sgi575_security.c | 6 +- .../platform}/sgi575/sgi575_topology.c | 6 +- .../platform}/sgi575/sgi575_trusted_boot.c | 2 +- .../arm/board/rde1edge/include/platform_def.h | 48 - plat/arm/board/rde1edge/platform.mk | 69 - plat/arm/board/rde1edge/rde1edge_err.c | 17 - plat/arm/board/rde1edge/rde1edge_plat.c | 29 - plat/arm/board/rde1edge/rde1edge_security.c | 36 - plat/arm/board/rde1edge/rde1edge_topology.c | 36 - .../arm/board/rdn1edge/include/platform_def.h | 54 - plat/arm/board/rdn2/include/platform_def.h | 126 - plat/arm/board/rdn2/include/rdn2_ras.h | 14 - plat/arm/board/sgi575/include/platform_def.h | 49 - plat/arm/board/tc/fdts/dice_prot_env.dtsi | 11 + plat/arm/board/tc/fdts/tc_fw_config.dts | 12 +- plat/arm/board/tc/fdts/tc_nt_fw_config.dts | 13 + .../tc/fdts/tc_spmc_common_sp_manifest.dtsi | 61 + ...pmc_manifest.dts => tc_spmc_manifest.dtsi} | 63 +- .../tc/fdts/tc_spmc_optee_sp_manifest.dts | 128 +- .../board/tc/fdts/tc_spmc_test_manifest.dts | 41 + .../tc/fdts/tc_spmc_trusty_sp_manifest.dts | 17 + plat/arm/board/tc/fdts/tc_tb_fw_config.dts | 14 +- plat/arm/board/tc/include/platform_def.h | 211 +- plat/arm/board/tc/include/tc_helpers.S | 44 +- plat/arm/board/tc/include/tc_plat.h | 4 +- plat/arm/board/tc/nv_counter_test.c | 20 +- plat/arm/board/tc/plat_def_fip_uuid.h | 16 +- plat/arm/board/tc/plat_tc_mbedtls_config.h | 25 +- plat/arm/board/tc/platform.mk | 237 +- plat/arm/board/tc/platform_test.mk | 70 +- plat/arm/board/tc/rotpk_test.c | 16 +- ...ss_ap_test_stubs.c => rse_ap_test_stubs.c} | 8 +- .../tc/{rss_ap_tests.c => rse_ap_tests.c} | 6 +- ...ss_ap_testsuites.c => rse_ap_testsuites.c} | 2 +- ...ss_ap_testsuites.h => rse_ap_testsuites.h} | 6 +- plat/arm/board/tc/tc_bl1_dpe.c | 158 + plat/arm/board/tc/tc_bl1_measured_boot.c | 26 +- plat/arm/board/tc/tc_bl1_setup.c | 19 + plat/arm/board/tc/tc_bl2_dpe.c | 255 + plat/arm/board/tc/tc_bl2_measured_boot.c | 33 +- plat/arm/board/tc/tc_bl31_setup.c | 106 +- plat/arm/board/tc/tc_common_dpe.c | 36 + plat/arm/board/tc/tc_common_measured_boot.c | 12 +- plat/arm/board/tc/tc_dpe.h | 53 + plat/arm/board/tc/tc_plat.c | 17 +- plat/arm/board/tc/tc_security.c | 6 +- plat/arm/board/tc/tc_topology.c | 19 +- plat/arm/board/tc/tc_trng.c | 43 + plat/arm/common/arm_bl1_setup.c | 94 +- plat/arm/common/arm_bl2_el3_setup.c | 41 +- plat/arm/common/arm_bl2_setup.c | 132 +- plat/arm/common/arm_bl31_setup.c | 132 +- plat/arm/common/arm_common.c | 49 +- plat/arm/common/arm_common.mk | 107 +- plat/arm/common/arm_dyn_cfg.c | 4 +- plat/arm/common/arm_dyn_cfg_helpers.c | 147 +- plat/arm/common/arm_image_load.c | 20 +- plat/arm/common/arm_io_storage.c | 1 + plat/arm/common/arm_ni.c | 167 + plat/arm/common/arm_pm.c | 6 +- plat/arm/common/arm_sip_svc.c | 14 +- plat/arm/common/arm_transfer_list.c | 76 + plat/arm/common/fconf/arm_fconf_sp.c | 30 +- plat/arm/common/plat_arm_mbedtls_config.h | 26 + plat/arm/common/plat_arm_psa_mbedtls_config.h | 29 + plat/arm/common/plat_arm_sip_svc.c | 82 +- plat/arm/common/sp_min/arm_sp_min_setup.c | 6 +- plat/arm/css/common/css_pm.c | 13 +- .../css/sgi/include/sgi_base_platform_def.h | 304 - plat/arm/css/sgi/include/sgi_plat.h | 13 - plat/arm/css/sgi/include/sgi_sdei.h | 25 - plat/arm/css/sgi/include/sgi_soc_css_def.h | 47 - plat/arm/css/sgi/include/sgi_soc_css_def_v2.h | 207 - .../css/sgi/include/sgi_soc_platform_def.h | 33 - .../css/sgi/include/sgi_soc_platform_def_v2.h | 31 - plat/arm/css/sgi/sgi_bl31_setup.c | 136 - plat/aspeed/ast2700/include/platform_def.h | 11 +- plat/aspeed/ast2700/include/platform_reg.h | 4 + plat/aspeed/ast2700/plat_bl31_setup.c | 88 + plat/aspeed/ast2700/plat_helpers.S | 6 - plat/brcm/common/brcm_bl31_setup.c | 4 +- plat/common/aarch32/plat_common.c | 11 +- plat/common/aarch64/crash_console_helpers.S | 4 +- plat/common/aarch64/plat_common.c | 24 +- plat/common/plat_bl1_common.c | 49 +- plat/common/plat_bl_common.c | 8 +- plat/common/plat_gicv3.c | 7 +- plat/hisilicon/hikey/hikey_bl31_setup.c | 6 +- plat/hisilicon/hikey/platform.mk | 14 +- plat/hisilicon/hikey960/hikey960_bl31_setup.c | 2 +- plat/hisilicon/hikey960/platform.mk | 14 +- plat/hisilicon/poplar/bl31_plat_setup.c | 7 +- plat/hisilicon/poplar/include/hi3798cv200.h | 4 + plat/hisilicon/poplar/plat_pm.c | 10 +- plat/imx/common/imx8_helpers.S | 47 +- plat/imx/common/imx_bl31_common.c | 23 + plat/imx/common/imx_common.c | 48 + plat/imx/common/imx_sip_handler.c | 97 +- plat/imx/common/imx_sip_svc.c | 22 +- plat/imx/common/include/imx_plat_common.h | 16 + plat/imx/common/include/imx_sip_svc.h | 26 +- plat/imx/common/include/plat_common.h | 17 + plat/imx/imx7/common/imx7.mk | 15 +- plat/imx/imx8m/ddr/clock.c | 6 +- plat/imx/imx8m/imx8m_ccm.c | 8 +- plat/imx/imx8m/imx8m_measured_boot.c | 13 +- plat/imx/imx8m/imx8mm/imx8mm_bl31_setup.c | 42 +- plat/imx/imx8m/imx8mm/include/imx_sec_def.h | 22 + plat/imx/imx8m/imx8mm/include/platform_def.h | 9 + plat/imx/imx8m/imx8mm/platform.mk | 36 +- plat/imx/imx8m/imx8mn/imx8mn_bl31_setup.c | 22 +- plat/imx/imx8m/imx8mn/include/imx_sec_def.h | 19 + plat/imx/imx8m/imx8mn/include/platform_def.h | 9 + plat/imx/imx8m/imx8mn/platform.mk | 17 +- plat/imx/imx8m/imx8mp/gpc.c | 16 +- plat/imx/imx8m/imx8mp/imx8mp_bl31_setup.c | 58 +- plat/imx/imx8m/imx8mp/include/imx_sec_def.h | 35 + plat/imx/imx8m/imx8mp/include/platform_def.h | 10 + plat/imx/imx8m/imx8mp/platform.mk | 30 +- plat/imx/imx8m/imx8mq/imx8mq_bl31_setup.c | 8 +- plat/imx/imx8m/imx8mq/include/platform_def.h | 7 + plat/imx/imx8m/imx8mq/platform.mk | 17 +- plat/imx/imx8m/imx_rdc.c | 69 +- plat/imx/imx8m/include/dram.h | 8 +- plat/imx/imx8m/include/imx8m_csu.h | 7 +- plat/imx/imx8m/include/imx_rdc.h | 2 +- plat/imx/imx8qm/imx8qm_bl31_setup.c | 7 +- plat/imx/imx8qx/imx8qx_bl31_setup.c | 7 +- plat/imx/imx8ulp/apd_context.c | 657 ++ plat/imx/imx8ulp/dram.c | 798 ++ plat/imx/imx8ulp/imx8ulp_bl31_setup.c | 186 + plat/imx/imx8ulp/imx8ulp_caam.c | 18 + plat/imx/imx8ulp/imx8ulp_psci.c | 555 ++ plat/imx/imx8ulp/include/dram.h | 13 + plat/imx/imx8ulp/include/imx8ulp_caam.h | 24 + plat/imx/imx8ulp/include/platform_def.h | 124 + plat/imx/imx8ulp/include/scmi.h | 100 + plat/imx/imx8ulp/include/scmi_sensor.h | 139 + plat/imx/imx8ulp/include/xrdc.h | 47 + plat/imx/imx8ulp/platform.mk | 69 + plat/imx/imx8ulp/scmi/scmi.c | 69 + plat/imx/imx8ulp/scmi/scmi_pd.c | 371 + plat/imx/imx8ulp/scmi/scmi_sensor.c | 85 + plat/imx/imx8ulp/upower/upmu.h | 279 + plat/imx/imx8ulp/upower/upower_api.c | 3095 +++++++ plat/imx/imx8ulp/upower/upower_api.h | 1629 ++++ plat/imx/imx8ulp/upower/upower_defs.h | 742 ++ plat/imx/imx8ulp/upower/upower_hal.c | 201 + plat/imx/imx8ulp/upower/upower_soc_defs.h | 1154 +++ plat/imx/imx8ulp/xrdc/xrdc_config.h | 136 + plat/imx/imx8ulp/xrdc/xrdc_core.c | 327 + plat/imx/imx93/imx93_bl31_setup.c | 9 +- plat/imx/imx93/include/platform_def.h | 3 + plat/imx/imx93/platform.mk | 4 +- plat/intel/soc/agilex/bl2_plat_setup.c | 35 +- plat/intel/soc/agilex/bl31_plat_setup.c | 86 +- .../soc/agilex/include/agilex_clock_manager.h | 1 + .../agilex/include/agilex_memory_controller.h | 3 +- .../agilex/include/agilex_system_manager.h | 18 +- .../soc/agilex/include/socfpga_plat_def.h | 71 +- plat/intel/soc/agilex/platform.mk | 39 +- .../soc/agilex/soc/agilex_clock_manager.c | 26 +- plat/intel/soc/agilex5/bl2_plat_setup.c | 115 +- plat/intel/soc/agilex5/bl31_plat_setup.c | 23 +- .../intel/soc/agilex5/include/agilex5_cache.h | 13 + .../agilex5/include/agilex5_clock_manager.h | 392 +- plat/intel/soc/agilex5/include/agilex5_ddr.h | 31 + .../agilex5/include/agilex5_iossm_mailbox.h | 155 + .../soc/agilex5/include/agilex5_pinmux.h | 21 +- .../agilex5/include/agilex5_power_manager.h | 5 +- .../agilex5/include/agilex5_system_manager.h | 39 +- .../soc/agilex5/include/socfpga_plat_def.h | 72 +- plat/intel/soc/agilex5/platform.mk | 42 +- plat/intel/soc/agilex5/soc/agilex5_cache.S | 114 + .../soc/agilex5/soc/agilex5_clock_manager.c | 653 +- plat/intel/soc/agilex5/soc/agilex5_ddr.c | 434 + .../soc/agilex5/soc/agilex5_iossm_mailbox.c | 811 ++ plat/intel/soc/agilex5/soc/agilex5_pinmux.c | 83 +- .../soc/agilex5/soc/agilex5_power_manager.c | 7 +- plat/intel/soc/common/aarch64/plat_helpers.S | 41 +- .../soc/common/aarch64/platform_common.c | 6 - .../soc/common/bl2_plat_mem_params_desc.c | 18 + plat/intel/soc/common/drivers/ccu/ncore_ccu.c | 575 +- plat/intel/soc/common/drivers/ccu/ncore_ccu.h | 529 +- plat/intel/soc/common/drivers/ddr/ddr.c | 141 + plat/intel/soc/common/drivers/ddr/ddr.h | 56 + plat/intel/soc/common/drivers/nand/nand.c | 10 +- .../soc/common/drivers/qspi/cadence_qspi.c | 13 +- plat/intel/soc/common/drivers/sdmmc/sdmmc.c | 611 +- plat/intel/soc/common/drivers/sdmmc/sdmmc.h | 13 +- plat/intel/soc/common/drivers/wdt/watchdog.h | 7 +- plat/intel/soc/common/include/platform_def.h | 35 +- .../soc/common/include/socfpga_handoff.h | 37 +- .../soc/common/include/socfpga_mailbox.h | 18 +- .../soc/common/include/socfpga_private.h | 10 +- .../common/include/socfpga_reset_manager.h | 2 + plat/intel/soc/common/include/socfpga_ros.h | 62 + .../soc/common/include/socfpga_sip_svc.h | 154 +- .../common/include/socfpga_system_manager.h | 6 +- plat/intel/soc/common/include/socfpga_vab.h | 48 +- plat/intel/soc/common/lib/sha/sha.c | 253 + plat/intel/soc/common/lib/sha/sha.h | 166 + plat/intel/soc/common/sip/socfpga_sip_fcs.c | 111 +- plat/intel/soc/common/soc/socfpga_handoff.c | 13 +- plat/intel/soc/common/soc/socfpga_mailbox.c | 24 +- .../soc/common/soc/socfpga_reset_manager.c | 693 +- .../soc/common/soc/socfpga_system_manager.c | 31 + plat/intel/soc/common/socfpga_delay_timer.c | 8 +- plat/intel/soc/common/socfpga_image_load.c | 7 + plat/intel/soc/common/socfpga_psci.c | 29 +- plat/intel/soc/common/socfpga_ros.c | 188 + plat/intel/soc/common/socfpga_sip_svc.c | 120 +- plat/intel/soc/common/socfpga_storage.c | 29 +- plat/intel/soc/common/socfpga_vab.c | 93 +- plat/intel/soc/n5x/bl31_plat_setup.c | 2 - .../intel/soc/n5x/include/n5x_clock_manager.h | 10 +- .../soc/n5x/include/n5x_system_manager.h | 18 +- plat/intel/soc/n5x/include/socfpga_plat_def.h | 68 +- plat/intel/soc/n5x/platform.mk | 27 +- plat/intel/soc/n5x/soc/n5x_clock_manager.c | 20 +- plat/intel/soc/stratix10/bl2_plat_setup.c | 4 +- plat/intel/soc/stratix10/bl31_plat_setup.c | 2 - .../soc/stratix10/include/s10_clock_manager.h | 3 +- .../stratix10/include/s10_memory_controller.h | 3 +- .../stratix10/include/s10_system_manager.h | 17 +- .../soc/stratix10/include/socfpga_plat_def.h | 56 +- plat/intel/soc/stratix10/platform.mk | 29 +- .../soc/stratix10/soc/s10_clock_manager.c | 62 +- .../marvell/armada/a3k/common/a3700_common.mk | 101 +- .../armada/a3k/common/cm3_system_reset.c | 132 + .../armada/a3k/common/include/a3700_pm.h | 2 + plat/marvell/armada/a3k/common/plat_pm.c | 2 +- plat/marvell/armada/a8k/common/a8k_common.mk | 20 +- plat/marvell/armada/a8k/common/ble/ble.mk | 2 +- .../armada/common/marvell_bl31_setup.c | 4 +- plat/marvell/armada/common/marvell_common.mk | 14 +- .../build_helpers/mtk_build_helpers.mk | 21 +- plat/mediatek/common/mtk_bl31_setup.c | 3 +- plat/mediatek/common/mtk_smc_handlers.c | 8 +- .../drivers/apusys/apusys_rv/2.0/apusys_rv.c | 89 - .../drivers/apusys/mt8188/apusys_devapc.c | 7 - plat/mediatek/drivers/emi_mpu/emi_mpu.h | 3 +- .../mediatek/drivers/emi_mpu/emi_mpu_common.c | 48 +- .../mediatek/drivers/emi_mpu/mt8188/emi_mpu.c | 96 +- .../drivers/emi_mpu/mt8188/emi_mpu_priv.h | 30 +- plat/mediatek/drivers/gic600/mt_gic_v3.c | 8 +- .../drivers/iommu/mt8188/mtk_iommu_plat.c | 18 +- .../drivers/iommu/mt8188/mtk_iommu_plat.h | 15 +- plat/mediatek/drivers/iommu/mtk_iommu_priv.h | 31 + plat/mediatek/drivers/iommu/mtk_iommu_smc.c | 83 +- plat/mediatek/drivers/iommu/mtk_iommu_smc.h | 15 + plat/mediatek/drivers/rng/mt8186/rng_plat.c | 153 + plat/mediatek/drivers/rng/mt8186/rng_plat.h | 30 + plat/mediatek/drivers/rng/mt8188/rng_plat.c | 96 + plat/mediatek/drivers/rng/mt8188/rng_plat.h | 46 + plat/mediatek/drivers/rng/rng.c | 29 + plat/mediatek/drivers/rng/rules.mk | 17 + .../{mt8188 => }/include/plat_helpers.h | 2 +- plat/mediatek/mt8186/include/plat_helpers.h | 12 - plat/mediatek/mt8186/include/platform_def.h | 11 +- plat/mediatek/mt8186/platform.mk | 10 +- plat/mediatek/mt8188/include/platform_def.h | 20 +- plat/mediatek/mt8188/plat_config.mk | 5 +- plat/mediatek/mt8188/platform.mk | 5 +- .../mediatek/mt8192/drivers/emi_mpu/emi_mpu.c | 19 +- plat/mediatek/mt8192/include/plat_helpers.h | 12 - plat/mediatek/mt8192/include/platform_def.h | 4 +- .../mediatek/mt8195/drivers/emi_mpu/emi_mpu.c | 2 +- plat/mediatek/mt8195/include/plat_helpers.h | 12 - plat/mediatek/mt8195/include/platform_def.h | 7 +- plat/nuvoton/common/nuvoton_helpers.S | 2 +- plat/nuvoton/npcm845x/npcm845x_bl31_setup.c | 33 +- plat/nuvoton/npcm845x/platform.mk | 48 +- plat/nvidia/tegra/common/tegra_bl31_setup.c | 2 +- plat/nvidia/tegra/common/tegra_fiq_glue.c | 4 +- plat/nvidia/tegra/platform.mk | 4 +- .../tegra/soc/t194/plat_psci_handlers.c | 4 +- plat/nxp/common/fip_handler/fuse_fip/fuse.mk | 12 +- plat/nxp/common/tbbr/tbbr.mk | 12 +- .../s32/s32g274ardb2/include/plat_console.h | 12 + .../s32/s32g274ardb2/include/plat_helpers.h | 12 + .../s32g274ardb2/include/plat_io_storage.h | 12 + .../s32/s32g274ardb2/include/plat_macros.S | 22 + .../s32/s32g274ardb2/include/platform_def.h | 77 + .../s32/s32g274ardb2/include/s32cc-ncore.h | 102 + .../nxp/s32/s32g274ardb2/plat_bl2_el3_setup.c | 80 + .../s32/s32g274ardb2/plat_bl2_image_desc.c | 41 + plat/nxp/s32/s32g274ardb2/plat_bl31_setup.c | 75 + plat/nxp/s32/s32g274ardb2/plat_console.c | 29 + plat/nxp/s32/s32g274ardb2/plat_helpers.S | 129 + plat/nxp/s32/s32g274ardb2/plat_io_storage.c | 135 + plat/nxp/s32/s32g274ardb2/platform.mk | 76 + plat/nxp/s32/s32g274ardb2/s32cc_ncore.c | 99 + plat/nxp/s32/s32g274ardb2/s32g2_psci.c | 20 + plat/nxp/s32/s32g274ardb2/s32g2_soc.c | 52 + plat/nxp/soc-lx2160a/ddr_fip.mk | 22 +- plat/nxp/soc-lx2160a/ddr_sb.mk | 2 +- plat/nxp/soc-lx2160a/ddr_tbbr.mk | 2 - plat/qemu/common/common.mk | 17 +- plat/qemu/common/qemu_bl2_mem_params_desc.c | 23 +- plat/qemu/common/qemu_bl2_setup.c | 181 +- plat/qemu/common/qemu_bl31_setup.c | 76 +- plat/qemu/common/qemu_common.c | 172 +- plat/qemu/common/qemu_io_storage.c | 17 +- plat/qemu/common/qemu_plat_attest_token.c | 232 + plat/qemu/common/qemu_pm.c | 17 - plat/qemu/common/qemu_realm_attest_key.c | 36 + plat/qemu/common/trp/qemu_trp_setup.c | 48 + plat/qemu/common/trp/trp-qemu-common.mk | 12 + plat/qemu/qemu/include/platform_def.h | 91 +- plat/qemu/qemu/include/qemu_pas_def.h | 108 + plat/qemu/qemu/platform.mk | 34 +- plat/qemu/qemu/qemu_measured_boot.c | 23 +- plat/qemu/qemu/trp/trp-qemu.mk | 8 + plat/qemu/qemu_sbsa/include/platform_def.h | 10 +- plat/qemu/qemu_sbsa/sbsa_sip_svc.c | 270 +- plat/qti/common/src/aarch64/qti_kryo4_gold.S | 12 +- .../qti/common/src/aarch64/qti_kryo4_silver.S | 13 +- plat/qti/common/src/aarch64/qti_kryo6_gold.S | 12 +- .../qti/common/src/aarch64/qti_kryo6_silver.S | 13 +- .../qtiseclib/src/qtiseclib_cb_interface.c | 6 +- plat/renesas/common/aarch64/plat_helpers.S | 7 +- plat/renesas/common/aarch64/platform_common.c | 26 +- plat/renesas/common/bl2_secure_setting.c | 98 +- plat/renesas/common/include/platform_def.h | 8 +- plat/renesas/common/include/rcar_def.h | 31 +- plat/renesas/common/include/rcar_version.h | 2 +- plat/renesas/rcar/bl2_plat_setup.c | 287 +- plat/renesas/rcar/platform.mk | 31 +- plat/renesas/rcar/rcar_stack_protector.c | 36 + plat/renesas/rzg/platform.mk | 24 +- .../rockchip/common/aarch32/platform_common.c | 2 +- plat/rockchip/common/aarch64/plat_helpers.S | 18 +- .../rockchip/common/aarch64/platform_common.c | 11 +- plat/rockchip/common/bl31_plat_setup.c | 11 +- plat/rockchip/common/include/plat_macros.S | 6 +- .../rockchip/common/include/plat_pm_helpers.h | 51 + plat/rockchip/common/include/plat_private.h | 10 +- .../common/include/rockchip_sip_svc.h | 1 + plat/rockchip/common/plat_pm_helpers.c | 213 + plat/rockchip/common/scmi/scmi.c | 89 + plat/rockchip/common/scmi/scmi_clock.c | 157 + plat/rockchip/common/scmi/scmi_clock.h | 50 + plat/rockchip/common/scmi/scmi_rstd.c | 74 + plat/rockchip/common/scmi/scmi_rstd.h | 45 + plat/rockchip/rk3328/platform.mk | 1 + plat/rockchip/rk3399/drivers/m0/Makefile | 42 +- plat/rockchip/rk3399/drivers/pmu/pmu_fw.S | 21 + plat/rockchip/rk3399/drivers/pmu/pmu_fw.c | 22 - plat/rockchip/rk3399/platform.mk | 9 +- .../rk3568/drivers/pmu/plat_pmu_macros.S | 18 + plat/rockchip/rk3568/drivers/pmu/pmu.c | 543 ++ plat/rockchip/rk3568/drivers/pmu/pmu.h | 304 + plat/rockchip/rk3568/drivers/soc/soc.c | 103 + plat/rockchip/rk3568/drivers/soc/soc.h | 63 + plat/rockchip/rk3568/include/plat.ld.S | 36 + plat/rockchip/rk3568/include/plat_sip_calls.h | 12 + plat/rockchip/rk3568/include/platform_def.h | 136 + plat/rockchip/rk3568/plat_sip_calls.c | 29 + plat/rockchip/rk3568/platform.mk | 96 + plat/rockchip/rk3568/rk3568_def.h | 104 + .../rk3588/drivers/pmu/plat_pmu_macros.S | 21 + plat/rockchip/rk3588/drivers/pmu/pm_pd_regs.c | 555 ++ plat/rockchip/rk3588/drivers/pmu/pm_pd_regs.h | 23 + plat/rockchip/rk3588/drivers/pmu/pmu.c | 1512 +++ plat/rockchip/rk3588/drivers/pmu/pmu.h | 589 ++ .../rockchip/rk3588/drivers/scmi/rk3588_clk.c | 2463 +++++ .../rockchip/rk3588/drivers/scmi/rk3588_clk.h | 104 + .../rk3588/drivers/scmi/rk3588_rstd.c | 96 + plat/rockchip/rk3588/drivers/secure/secure.c | 188 + plat/rockchip/rk3588/drivers/secure/secure.h | 54 + plat/rockchip/rk3588/drivers/soc/soc.c | 99 + plat/rockchip/rk3588/drivers/soc/soc.h | 198 + plat/rockchip/rk3588/include/plat.ld.S | 42 + plat/rockchip/rk3588/include/plat_sip_calls.h | 12 + plat/rockchip/rk3588/include/platform_def.h | 119 + plat/rockchip/rk3588/plat_sip_calls.c | 32 + plat/rockchip/rk3588/platform.mk | 98 + plat/rockchip/rk3588/rk3588_def.h | 224 + .../aarch64/armstub8_header.S | 2 +- plat/rpi/common/aarch64/plat_helpers.S | 31 +- .../{rpi4 => common}/include/plat_macros.S | 2 +- plat/rpi/common/include/rpi_shared.h | 18 +- plat/rpi/common/rpi3_common.c | 24 +- plat/rpi/common/rpi3_console_dual.c | 35 + plat/rpi/common/rpi3_console_pl011.c | 20 + plat/rpi/common/rpi3_pm.c | 18 +- plat/rpi/common/rpi3_topology.c | 21 +- plat/rpi/{rpi4 => common}/rpi4_bl31_setup.c | 111 +- .../rpi4_pci_svc.c => common/rpi_pci_svc.c} | 95 +- plat/rpi/rpi3/include/plat_macros.S | 20 - plat/rpi/rpi3/include/platform_def.h | 3 +- plat/rpi/rpi3/include/rpi_hw.h | 44 +- plat/rpi/rpi3/platform.mk | 34 +- plat/rpi/rpi3/rpi3_bl1_setup.c | 13 +- plat/rpi/rpi4/include/platform_def.h | 3 +- plat/rpi/rpi4/include/rpi_hw.h | 51 +- plat/rpi/rpi4/platform.mk | 16 +- plat/rpi/rpi4/rpi4_setup.c | 109 + plat/rpi/rpi5/include/plat.ld.S | 23 + plat/rpi/rpi5/include/platform_def.h | 141 + plat/rpi/rpi5/include/rpi_hw.h | 58 + plat/rpi/rpi5/platform.mk | 115 + plat/rpi/rpi5/rpi5_setup.c | 12 + plat/socionext/synquacer/platform.mk | 14 +- plat/socionext/synquacer/sq_bl31_setup.c | 2 +- plat/socionext/uniphier/platform.mk | 18 +- plat/st/common/bl2_io_storage.c | 147 +- plat/st/common/common.mk | 34 +- plat/st/common/common_rules.mk | 44 +- .../include/plat_def_fip_uuid.h | 6 +- plat/st/common/include/stm32mp_common.h | 29 +- .../common/include/stm32mp_mbedtls_config-2.h | 119 - .../common/include/stm32mp_mbedtls_config-3.h | 3 +- plat/st/common/stm32mp_common.c | 70 +- plat/st/common/stm32mp_crypto_lib.c | 9 +- plat/st/common/stm32mp_dt.c | 6 +- plat/st/common/stm32mp_fconf_io.c | 33 +- plat/st/common/stm32mp_gic.c | 21 +- plat/st/common/stm32mp_trusted_boot.c | 8 +- plat/st/stm32mp1/bl2_plat_setup.c | 29 +- plat/st/stm32mp1/cert_create_tbbr.mk | 3 +- plat/st/stm32mp1/include/platform_def.h | 16 +- plat/st/stm32mp1/include/stm32mp1_private.h | 49 +- plat/st/stm32mp1/plat_ddr.c | 135 + plat/st/stm32mp1/platform.mk | 44 +- plat/st/stm32mp1/services/bsec_svc.c | 11 +- plat/st/stm32mp1/sp_min/sp_min_setup.c | 7 +- plat/st/stm32mp1/stm32mp1_def.h | 47 +- plat/st/stm32mp1/stm32mp1_fip_def.h | 8 +- plat/st/stm32mp1/stm32mp1_pm.c | 11 +- plat/st/stm32mp1/stm32mp1_private.c | 83 +- plat/st/stm32mp1/stm32mp1_syscfg.c | 24 +- plat/st/stm32mp2/aarch64/stm32mp2_helper.S | 45 +- plat/st/stm32mp2/bl2_plat_setup.c | 372 +- plat/st/stm32mp2/bl31_plat_setup.c | 159 + plat/st/stm32mp2/include/boot_api.h | 2 +- plat/st/stm32mp2/include/plat_tbbr_img_def.h | 55 + plat/st/stm32mp2/include/platform_def.h | 78 +- plat/st/stm32mp2/include/stm32mp2_private.h | 53 + plat/st/stm32mp2/plat_bl2_mem_params_desc.c | 139 +- plat/st/stm32mp2/plat_ddr.c | 217 + plat/st/stm32mp2/platform.mk | 187 +- plat/st/stm32mp2/stm32mp2_def.h | 214 +- plat/st/stm32mp2/stm32mp2_pm.c | 124 + plat/st/stm32mp2/stm32mp2_private.c | 334 + plat/st/stm32mp2/stm32mp2_syscfg.c | 31 + plat/st/stm32mp2/stm32mp2_topology.c | 57 + plat/st/stm32mp2/stm32mp2_usb_dfu.c | 21 + .../k3/common/drivers/sec_proxy/sec_proxy.c | 2 +- plat/ti/k3/common/drivers/ti_sci/ti_sci.c | 51 +- plat/ti/k3/common/drivers/ti_sci/ti_sci.h | 50 +- .../common/drivers/ti_sci/ti_sci_protocol.h | 28 + plat/ti/k3/common/k3_bl31_setup.c | 38 +- plat/ti/k3/common/k3_psci.c | 24 +- plat/ti/k3/common/plat_common.mk | 2 + plat/xilinx/common/include/plat_clkfunc.h | 13 + plat/xilinx/common/include/plat_common.h | 12 + plat/xilinx/common/include/plat_console.h | 24 +- plat/xilinx/common/include/plat_fdt.h | 4 + plat/xilinx/common/include/plat_xfer_list.h | 15 + plat/xilinx/common/include/pm_api_sys.h | 11 +- plat/xilinx/common/include/pm_client.h | 3 +- plat/xilinx/common/include/pm_common.h | 5 +- plat/xilinx/common/include/pm_defs.h | 8 +- plat/xilinx/common/include/pm_svc_main.h | 7 +- plat/xilinx/common/ipi.c | 10 +- .../ipi_mailbox_service/ipi_mailbox_svc.c | 17 +- plat/xilinx/common/plat_clkfunc.c | 41 + plat/xilinx/common/plat_console.c | 319 +- plat/xilinx/common/plat_fdt.c | 169 +- plat/xilinx/common/plat_startup.c | 6 +- plat/xilinx/common/plat_xfer_list.c | 50 + plat/xilinx/common/pm_service/pm_api_sys.c | 247 +- plat/xilinx/common/pm_service/pm_ipi.c | 53 +- plat/xilinx/common/pm_service/pm_svc_main.c | 235 +- plat/xilinx/common/versal.c | 1 + plat/xilinx/versal/aarch64/versal_common.c | 65 +- plat/xilinx/versal/bl31_versal_setup.c | 66 +- plat/xilinx/versal/include/plat_macros.S | 4 +- plat/xilinx/versal/include/plat_private.h | 8 +- plat/xilinx/versal/include/platform_def.h | 20 +- plat/xilinx/versal/include/versal_def.h | 75 +- plat/xilinx/versal/plat_psci.c | 72 +- plat/xilinx/versal/platform.mk | 36 +- plat/xilinx/versal/pm_service/pm_client.c | 5 +- plat/xilinx/versal/sip_svc_setup.c | 27 +- plat/xilinx/versal/versal_gicv3.c | 4 +- .../versal_net/aarch64/versal_net_common.c | 37 +- .../versal_net/aarch64/versal_net_helpers.S | 11 + .../xilinx/versal_net/bl31_versal_net_setup.c | 50 +- plat/xilinx/versal_net/include/plat_ipi.h | 26 +- plat/xilinx/versal_net/include/plat_macros.S | 4 +- plat/xilinx/versal_net/include/plat_private.h | 1 + plat/xilinx/versal_net/include/platform_def.h | 11 +- .../versal_net/include/versal_net_def.h | 51 +- plat/xilinx/versal_net/plat_psci.c | 4 +- plat/xilinx/versal_net/plat_psci_pm.c | 90 +- plat/xilinx/versal_net/platform.mk | 30 +- plat/xilinx/versal_net/sip_svc_setup.c | 11 +- plat/xilinx/versal_net/tsp/tsp-versal_net.mk | 3 - plat/xilinx/versal_net/versal_net_ipi.c | 56 + plat/xilinx/zynqmp/aarch64/zynqmp_common.c | 21 +- plat/xilinx/zynqmp/bl31_zynqmp_setup.c | 13 +- plat/xilinx/zynqmp/custom_sip_svc.c | 8 + plat/xilinx/zynqmp/include/plat_private.h | 4 +- plat/xilinx/zynqmp/include/zynqmp_def.h | 37 +- plat/xilinx/zynqmp/plat_psci.c | 36 +- plat/xilinx/zynqmp/plat_topology.c | 2 + plat/xilinx/zynqmp/plat_zynqmp.c | 2 +- plat/xilinx/zynqmp/platform.mk | 37 +- plat/xilinx/zynqmp/pm_service/pm_api_clock.c | 58 +- plat/xilinx/zynqmp/pm_service/pm_api_ioctl.c | 10 +- .../xilinx/zynqmp/pm_service/pm_api_pinctrl.c | 10 +- plat/xilinx/zynqmp/pm_service/pm_client.c | 23 +- .../zynqmp/pm_service/zynqmp_pm_api_sys.c | 43 +- .../zynqmp/pm_service/zynqmp_pm_svc_main.c | 56 +- plat/xilinx/zynqmp/sip_svc_setup.c | 7 +- poetry.lock | 995 +- pyproject.toml | 9 +- readme.rst | 2 +- services/arm_arch_svc/arm_arch_svc_setup.c | 4 +- services/el3/ven_el3_svc.c | 97 + services/oem/chromeos/widevine_smc_handlers.c | 98 + services/spd/opteed/opteed.mk | 8 + services/spd/opteed/opteed_common.c | 15 +- services/spd/opteed/opteed_main.c | 227 +- services/spd/opteed/opteed_pm.c | 4 +- services/spd/opteed/opteed_private.h | 9 +- services/spd/pncd/pncd_common.c | 6 +- services/spd/pncd/pncd_main.c | 6 +- services/spd/tlkd/tlkd.mk | 4 +- services/spd/trusty/trusty.c | 25 +- services/spd/tspd/tspd_main.c | 7 +- services/std_svc/drtm/drtm_main.c | 20 +- services/std_svc/drtm/drtm_main.h | 9 +- services/std_svc/drtm/drtm_remediation.c | 6 +- services/std_svc/errata_abi/cpu_errata_info.h | 39 +- services/std_svc/errata_abi/errata_abi_main.c | 522 +- services/std_svc/rmmd/rmmd.mk | 7 +- services/std_svc/rmmd/rmmd_attest.c | 126 +- services/std_svc/rmmd/rmmd_main.c | 67 +- services/std_svc/rmmd/rmmd_private.h | 7 +- services/std_svc/rmmd/trp/trp.mk | 6 +- services/std_svc/rmmd/trp/trp_main.c | 5 +- services/std_svc/sdei/sdei_intr_mgmt.c | 59 +- services/std_svc/sdei/sdei_main.c | 4 +- services/std_svc/spm/el3_spmc/spmc.h | 12 +- services/std_svc/spm/el3_spmc/spmc_main.c | 401 +- services/std_svc/spm/el3_spmc/spmc_pm.c | 17 +- services/std_svc/spm/el3_spmc/spmc_setup.c | 331 +- services/std_svc/spm/spm_mm/spm_mm_main.c | 23 +- services/std_svc/spm/spm_mm/spm_mm_setup.c | 27 +- services/std_svc/spmd/spmd_logical_sp.c | 14 +- services/std_svc/spmd/spmd_main.c | 141 +- services/std_svc/std_svc_setup.c | 2 +- tools/amlogic/Makefile | 26 +- tools/cert_create/Makefile | 32 +- tools/cert_create/include/key.h | 24 +- tools/cert_create/src/cca/cot.c | 4 +- tools/cert_create/src/cert.c | 3 +- tools/cert_create/src/dualroot/cot.c | 4 +- tools/cert_create/src/key.c | 42 +- tools/cert_create/src/main.c | 32 +- tools/cert_create/src/tbbr/tbb_key.c | 4 +- .../conventional-changelog-tf-a/package.json | 2 +- tools/cot_dt2c/.gitignore | 176 + tools/cot_dt2c/cot_dt2c/__init__.py | 25 + tools/cot_dt2c/cot_dt2c/__main__.py | 10 + tools/cot_dt2c/cot_dt2c/cli.py | 39 + tools/cot_dt2c/cot_dt2c/cot_dt2c.py | 30 + tools/cot_dt2c/cot_dt2c/cot_parser.py | 673 ++ tools/cot_dt2c/cot_dt2c/dt_validator.py | 130 + tools/cot_dt2c/poetry.lock | 334 + tools/cot_dt2c/pyproject.toml | 63 + tools/cot_dt2c/tests/test.dtsi | 58 + tools/cot_dt2c/tests/test2.dtsi | 69 + .../cot_dt2c/tests/test_invalid_bracket.dtsi | 54 + .../tests/test_invalid_missing_attribute.dtsi | 108 + .../tests/test_invalid_missing_ctr.dtsi | 53 + .../tests/test_invalid_missing_root.dtsi | 242 + .../tests/test_invalid_undefined_parent.dtsi | 113 + tools/cot_dt2c/tests/test_util.py | 33 + tools/encrypt_fw/Makefile | 26 +- tools/encrypt_fw/src/main.c | 4 +- tools/fiptool/Makefile | 29 +- tools/fiptool/fiptool.c | 5 +- .../arm/board/juno}/plat_def_uuid_config.c | 0 .../arm/board/juno/plat_fiptool.mk | 4 +- .../arm/board/tc/plat_def_uuid_config.c | 48 +- .../st/{stm32mp1 => }/plat_def_uuid_config.c | 7 +- .../st/{stm32mp1 => }/plat_fiptool.mk | 8 +- tools/marvell/doimage/Makefile | 22 +- tools/marvell/doimage/doimage.c | 2 +- tools/memory/memory/mapparser.py | 4 +- tools/memory/memory/memmap.py | 4 +- .../nxp/cert_create_helper/src/pdef_tbb_key.c | 2 +- tools/nxp/create_pbl/Makefile | 36 +- tools/nxp/create_pbl/pbl_ch2.mk | 17 +- tools/nxp/create_pbl/pbl_ch3.mk | 18 +- tools/renesas/rcar_layout_create/makefile | 52 +- tools/renesas/rcar_layout_create/sa6.c | 4 +- tools/renesas/rzg_layout_create/makefile | 52 +- tools/sptool/Makefile | 27 +- tools/sptool/sp_mk_generator.py | 13 +- tools/stm32image/Makefile | 27 +- tools/tlc/.gitignore | 176 + tools/tlc/assets/images/coverage.svg | 21 + tools/tlc/poetry.lock | 1434 +++ tools/tlc/pyproject.toml | 151 + tools/tlc/setup.cfg | 4 + tools/tlc/tests/conftest.py | 72 + tools/tlc/tests/test_cli.py | 476 + tools/tlc/tests/test_transfer_list.py | 234 + tools/tlc/tlc/__init__.py | 27 + tools/tlc/tlc/__main__.py | 13 + tools/tlc/tlc/cli.py | 201 + tools/tlc/tlc/te.py | 56 + tools/tlc/tlc/templates/header.h.j2 | 16 + tools/tlc/tlc/tl.py | 363 + tools/tlc/tox.ini | 26 + 1473 files changed, 117058 insertions(+), 27515 deletions(-) create mode 100644 .ctags create mode 100644 .cz-adapter.cjs rename .versionrc.js => .versionrc.cjs (95%) create mode 100644 bl32/tsp/tsp_context.c create mode 100644 docs/components/context-management-library.rst create mode 100644 docs/components/fconf/tb_fw_bindings.rst create mode 100644 docs/components/ven-el3-debugfs.rst create mode 100644 docs/components/ven-el3-service.rst rename docs/design_documents/{rss.rst => rse.rst} (52%) create mode 100644 docs/plat/amd-versal2.rst create mode 100644 docs/plat/arm/automotive_rd/index.rst create mode 100644 docs/plat/arm/fvp/fvp-aemv8-base.rst create mode 100644 docs/plat/arm/fvp/fvp-build-options.rst create mode 100644 docs/plat/arm/fvp/fvp-cortex-a32.rst create mode 100644 docs/plat/arm/fvp/fvp-cortex-a57-a53.rst create mode 100644 docs/plat/arm/fvp/fvp-foundation.rst create mode 100644 docs/plat/arm/fvp/fvp-specific-configs.rst create mode 100644 docs/plat/arm/fvp/fvp-support.rst create mode 100644 docs/plat/imx8ulp.rst create mode 100644 docs/plat/rpi5.rst create mode 100644 docs/plat/s32g274a.rst create mode 100644 docs/process/misra-compliance.csv create mode 100644 docs/resources/diagrams/context_init_coldboot.png create mode 100644 docs/resources/diagrams/context_init_warmboot.png create mode 100644 docs/resources/diagrams/context_memory_allocation.png create mode 100644 docs/resources/diagrams/cot-dualroot.jpg create mode 100644 docs/resources/diagrams/cot-tbbr.jpg create mode 100644 docs/resources/diagrams/cpu_data_config_context_memory.png create mode 100644 docs/resources/diagrams/percpu-data-struct.png rename docs/resources/diagrams/plantuml/{rss_attestation_flow.puml => rse_attestation_flow.puml} (99%) create mode 100644 docs/resources/diagrams/plantuml/rse_measured_boot_flow.puml delete mode 100644 docs/resources/diagrams/plantuml/rss_measured_boot_flow.puml rename docs/resources/diagrams/plantuml/{tfa_rss_dfd.puml => tfa_rse_dfd.puml} (89%) create mode 100644 docs/resources/diagrams/root_context_sequence.png rename docs/resources/diagrams/{rss_attestation_flow.svg => rse_attestation_flow.svg} (99%) rename docs/resources/diagrams/{rss_measured_boot_flow.svg => rse_measured_boot_flow.svg} (90%) create mode 100755 docs/resources/diagrams/tf-a_attack_tree.png create mode 100755 docs/resources/diagrams/tf-a_data_flow_diagram.png create mode 100755 docs/resources/diagrams/tf-a_system_diagram.png create mode 100644 docs/security_advisories/security-advisory-tfv-11.rst create mode 100644 docs/threat_model/firmware_threat_model/index.rst rename docs/threat_model/{ => firmware_threat_model}/threat_model.rst (89%) rename docs/threat_model/{ => firmware_threat_model}/threat_model_arm_cca.rst (98%) rename docs/threat_model/{ => firmware_threat_model}/threat_model_el3_spm.rst (99%) rename docs/threat_model/{ => firmware_threat_model}/threat_model_fvp_r.rst (98%) create mode 100644 docs/threat_model/firmware_threat_model/threat_model_fw_update_and_recovery.rst rename docs/threat_model/{threat_model_rss_interface.rst => firmware_threat_model/threat_model_rse_interface.rst} (72%) create mode 100644 docs/threat_model/supply_chain_threat_model.rst create mode 100644 docs/tools/cot-dt2c.rst create mode 100644 docs/tools/transfer-list-compiler.rst create mode 100644 drivers/arm/css/dsu/dsu.c create mode 100644 drivers/arm/mhu/mhu_v3_x.c create mode 100644 drivers/arm/mhu/mhu_v3_x.h create mode 100644 drivers/arm/mhu/mhu_v3_x_private.h create mode 100644 drivers/arm/mhu/mhu_wrapper_v3_x.c rename drivers/arm/{rss/rss_comms.c => rse/rse_comms.c} (71%) create mode 100644 drivers/arm/rse/rse_comms.mk rename drivers/arm/{rss/rss_comms_protocol.c => rse/rse_comms_protocol.c} (62%) create mode 100644 drivers/arm/rse/rse_comms_protocol.h create mode 100644 drivers/arm/rse/rse_comms_protocol_common.h rename drivers/arm/{rss/rss_comms_protocol_embed.c => rse/rse_comms_protocol_embed.c} (69%) rename drivers/arm/{rss/rss_comms_protocol_embed.h => rse/rse_comms_protocol_embed.h} (55%) rename drivers/arm/{rss/rss_comms_protocol_pointer_access.c => rse/rse_comms_protocol_pointer_access.c} (58%) rename drivers/arm/{rss/rss_comms_protocol_pointer_access.h => rse/rse_comms_protocol_pointer_access.h} (58%) delete mode 100644 drivers/arm/rss/rss_comms.mk delete mode 100644 drivers/arm/rss/rss_comms_protocol.h create mode 100644 drivers/auth/cca/bl1_cot.c delete mode 100644 drivers/auth/cca/cot.c create mode 100644 drivers/auth/dualroot/bl1_cot.c delete mode 100644 drivers/auth/dualroot/cot.c create mode 100644 drivers/measured_boot/rse/dice_prot_env.c create mode 100644 drivers/measured_boot/rse/dice_prot_env.mk create mode 100644 drivers/measured_boot/rse/qcbor.mk rename drivers/measured_boot/{rss/rss_measured_boot.c => rse/rse_measured_boot.c} (86%) rename drivers/measured_boot/{rss/rss_measured_boot.mk => rse/rse_measured_boot.mk} (65%) create mode 100644 drivers/nxp/clk/s32cc/include/s32cc-clk-regs.h create mode 100644 drivers/nxp/clk/s32cc/include/s32cc-mc-me.h create mode 100644 drivers/nxp/clk/s32cc/include/s32cc-mc-rgm.h create mode 100644 drivers/nxp/clk/s32cc/mc_me.c create mode 100644 drivers/nxp/clk/s32cc/mc_rgm.c create mode 100644 drivers/nxp/clk/s32cc/s32cc_clk.mk create mode 100644 drivers/nxp/clk/s32cc/s32cc_clk_drv.c create mode 100644 drivers/nxp/clk/s32cc/s32cc_clk_modules.c create mode 100644 drivers/nxp/clk/s32cc/s32cc_clk_utils.c create mode 100644 drivers/nxp/clk/s32cc/s32cc_early_clks.c create mode 100644 drivers/nxp/console/linflex_console.S create mode 100644 drivers/scmi-msg/sensor.c create mode 100644 drivers/scmi-msg/sensor.h create mode 100644 drivers/st/bsec/bsec3.c create mode 100644 drivers/st/clk/clk-stm32mp2.c create mode 100644 drivers/st/ddr/phy/firmware/include/mnpmusrammsgblock_ddr3.h create mode 100644 drivers/st/ddr/phy/firmware/include/mnpmusrammsgblock_ddr4.h create mode 100644 drivers/st/ddr/phy/firmware/include/mnpmusrammsgblock_lpddr4.h create mode 100644 drivers/st/ddr/phy/phyinit/include/ddrphy_csr_all_cdefines.h create mode 100644 drivers/st/ddr/phy/phyinit/include/ddrphy_phyinit.h create mode 100644 drivers/st/ddr/phy/phyinit/include/ddrphy_phyinit_struct.h create mode 100644 drivers/st/ddr/phy/phyinit/include/ddrphy_phyinit_usercustom.h create mode 100644 drivers/st/ddr/phy/phyinit/include/ddrphy_wrapper.h create mode 100644 drivers/st/ddr/phy/phyinit/src/ddrphy_phyinit_c_initphyconfig.c create mode 100644 drivers/st/ddr/phy/phyinit/src/ddrphy_phyinit_calcmb.c create mode 100644 drivers/st/ddr/phy/phyinit/src/ddrphy_phyinit_d_loadimem.c create mode 100644 drivers/st/ddr/phy/phyinit/src/ddrphy_phyinit_f_loaddmem.c create mode 100644 drivers/st/ddr/phy/phyinit/src/ddrphy_phyinit_g_execfw.c create mode 100644 drivers/st/ddr/phy/phyinit/src/ddrphy_phyinit_i_loadpieimage.c create mode 100644 drivers/st/ddr/phy/phyinit/src/ddrphy_phyinit_initstruct.c create mode 100644 drivers/st/ddr/phy/phyinit/src/ddrphy_phyinit_isdbytedisabled.c create mode 100644 drivers/st/ddr/phy/phyinit/src/ddrphy_phyinit_loadpieprodcode.c create mode 100644 drivers/st/ddr/phy/phyinit/src/ddrphy_phyinit_mapdrvstren.c create mode 100644 drivers/st/ddr/phy/phyinit/src/ddrphy_phyinit_progcsrskiptrain.c create mode 100644 drivers/st/ddr/phy/phyinit/src/ddrphy_phyinit_reginterface.c create mode 100644 drivers/st/ddr/phy/phyinit/src/ddrphy_phyinit_restore_sequence.c create mode 100644 drivers/st/ddr/phy/phyinit/src/ddrphy_phyinit_sequence.c create mode 100644 drivers/st/ddr/phy/phyinit/src/ddrphy_phyinit_softsetmb.c create mode 100644 drivers/st/ddr/phy/phyinit/src/ddrphy_phyinit_writeoutmem.c create mode 100644 drivers/st/ddr/phy/phyinit/usercustom/ddrphy_phyinit_usercustom_custompretrain.c create mode 100644 drivers/st/ddr/phy/phyinit/usercustom/ddrphy_phyinit_usercustom_g_waitfwdone.c create mode 100644 drivers/st/ddr/phy/phyinit/usercustom/ddrphy_phyinit_usercustom_saveretregs.c create mode 100644 drivers/st/ddr/stm32mp2_ddr.c create mode 100644 drivers/st/ddr/stm32mp2_ddr_helpers.c create mode 100644 drivers/st/ddr/stm32mp2_ram.c create mode 100644 drivers/st/pmic/stm32mp_pmic2.c create mode 100644 drivers/st/pmic/stpmic2.c create mode 100644 drivers/st/reset/stm32mp2_reset.c create mode 100644 fdts/cca_cot_descriptors.dtsi create mode 100644 fdts/dualroot_cot_descriptors.dtsi create mode 100644 fdts/rd1ae.dts create mode 100644 fdts/stm32mp25-bl31.dtsi create mode 100644 fdts/stm32mp25-ddr.dtsi create mode 100644 fdts/stm32mp25-ddr4-2x16Gbits-2x16bits-1200MHz.dtsi create mode 100644 fdts/stm32mp25-ddr4-2x8Gbits-2x16bits-1200MHz.dtsi create mode 100644 fdts/stm32mp25-fw-config.dtsi create mode 100644 fdts/stm32mp257f-ev1-ca35tdcid-fw-config.dtsi create mode 100644 fdts/stm32mp257f-ev1-ca35tdcid-rcc.dtsi create mode 100644 fdts/stm32mp257f-ev1-fw-config.dts rename fdts/{cot_descriptors.dtsi => tbbr_cot_descriptors.dtsi} (89%) create mode 100644 fdts/tc-base.dtsi create mode 100644 fdts/tc-common.dtsi create mode 100644 fdts/tc-fpga.dtsi create mode 100644 fdts/tc-fvp.dtsi delete mode 100644 fdts/tc.dts create mode 100644 fdts/tc2.dts create mode 100644 fdts/tc3-4-base.dtsi create mode 100644 fdts/tc3.dts create mode 100644 fdts/tc4.dts create mode 100644 include/bl32/tsp/tsp_el1_context.h create mode 100644 include/common/build_message.h create mode 100644 include/common/par.h create mode 100644 include/common/sha_common_macros.h create mode 100644 include/drivers/arm/css/dsu.h create mode 100644 include/drivers/arm/fvp/fvp_cpu_pwr.h rename include/drivers/arm/{rss_comms.h => rse_comms.h} (53%) delete mode 100644 include/drivers/auth/mbedtls/mbedtls_config-2.h create mode 100644 include/drivers/measured_boot/metadata.h create mode 100644 include/drivers/measured_boot/rse/dice_prot_env.h create mode 100644 include/drivers/measured_boot/rse/rse_measured_boot.h delete mode 100644 include/drivers/measured_boot/rss/rss_measured_boot.h create mode 100644 include/drivers/nxp/clk/s32cc/s32cc-clk-drv.h create mode 100644 include/drivers/nxp/clk/s32cc/s32cc-clk-ids.h create mode 100644 include/drivers/nxp/clk/s32cc/s32cc-clk-modules.h create mode 100644 include/drivers/nxp/clk/s32cc/s32cc-clk-utils.h create mode 100644 include/drivers/nxp/console/linflex.h create mode 100644 include/drivers/st/bsec3_reg.h create mode 100644 include/drivers/st/stm32mp2_clk.h create mode 100644 include/drivers/st/stm32mp2_ddr.h create mode 100644 include/drivers/st/stm32mp2_ddr_helpers.h create mode 100644 include/drivers/st/stm32mp2_ddr_regs.h create mode 100644 include/drivers/st/stm32mp2_pwr.h create mode 100644 include/drivers/st/stm32mp2_ram.h create mode 100644 include/drivers/st/stm32mp_pmic2.h create mode 100644 include/drivers/st/stm32mp_risab_regs.h create mode 100644 include/drivers/st/stpmic2.h create mode 100644 include/dt-bindings/gpio/stm32-gpio.h rename include/lib/cpus/aarch64/{neoverse_hermes.h => cortex_a720_ae.h} (57%) rename include/lib/cpus/aarch64/{cortex_blackhawk.h => cortex_a725.h} (55%) create mode 100644 include/lib/cpus/aarch64/cortex_arcadia.h rename include/lib/cpus/aarch64/{cortex_chaberton.h => cortex_x925.h} (55%) create mode 100644 include/lib/cpus/aarch64/neoverse_n3.h delete mode 100644 include/lib/cpus/aarch64/neoverse_poseidon.h create mode 100644 include/lib/cpus/aarch64/neoverse_v3.h create mode 100644 include/lib/dice/dice.h create mode 100644 include/lib/el3_runtime/context_debug.h create mode 100644 include/lib/el3_runtime/context_el1.h create mode 100644 include/lib/el3_runtime/context_el2.h create mode 100644 include/lib/el3_runtime/simd_ctx.h create mode 100644 include/lib/extensions/debug_v8p9.h create mode 100644 include/lib/extensions/fgt2.h create mode 100644 include/lib/extensions/sysreg128.h create mode 100644 include/lib/extensions/tcr2.h create mode 100644 include/lib/psa/cca_attestation.h create mode 100644 include/lib/psa/dice_protection_environment.h create mode 100644 include/lib/psa/rse_crypto_defs.h rename include/lib/psa/{rss_platform_api.h => rse_platform_api.h} (74%) delete mode 100644 include/lib/psa/rss_crypto_defs.h create mode 100644 include/plat/arm/board/common/rotpk/rotpk_def.h create mode 100644 include/services/oem/chromeos/widevine_smc_handlers.h create mode 100644 include/services/rmm_el3_token_sign.h create mode 100644 include/services/ven_el3_svc.h rename lib/cpus/aarch64/{neoverse_hermes.S => cortex_a720_ae.S} (52%) rename lib/cpus/aarch64/{cortex_blackhawk.S => cortex_a725.S} (51%) rename lib/cpus/aarch64/{cortex_chaberton.S => cortex_arcadia.S} (51%) create mode 100644 lib/cpus/aarch64/cortex_x925.S create mode 100644 lib/cpus/aarch64/neoverse_n3.S delete mode 100644 lib/cpus/aarch64/neoverse_poseidon.S create mode 100644 lib/cpus/aarch64/neoverse_v3.S create mode 100644 lib/cpus/errata_common.c create mode 100644 lib/el3_runtime/aarch64/context_debug.c create mode 100644 lib/el3_runtime/simd_ctx.c create mode 100644 lib/extensions/debug/debugv8p9.c create mode 100644 lib/extensions/fgt/fgt2.c create mode 100644 lib/extensions/sysreg128/sysreg128.S create mode 100644 lib/extensions/tcr/tcr2.c create mode 100644 lib/libc/libc_common.mk create mode 100644 lib/psa/cca_attestation.c create mode 100644 lib/psa/dice_protection_environment.c rename lib/psa/{rss_platform.c => rse_platform.c} (59%) delete mode 100644 lib/psci/aarch64/runtime_errata.S create mode 100644 licenses/LICENSE-APACHE-2.0.txt create mode 100644 make_helpers/build-rules.mk create mode 100644 make_helpers/common.mk create mode 100644 make_helpers/toolchain.mk create mode 100644 make_helpers/toolchains/aarch32.mk create mode 100644 make_helpers/toolchains/aarch64.mk create mode 100644 make_helpers/toolchains/host.mk create mode 100644 make_helpers/toolchains/rk3399-m0.mk create mode 100644 make_helpers/utilities.mk create mode 100644 plat/allwinner/sun50i_a64/platform_defaults.mk create mode 100644 plat/allwinner/sun50i_h616/sunxi_h616_dtb.c create mode 100644 plat/amd/versal2/aarch64/common.c create mode 100644 plat/amd/versal2/aarch64/helpers.S create mode 100644 plat/amd/versal2/bl31_setup.c rename plat/{xilinx/versal_net/versal_net_gicv3.c => amd/versal2/gicv3.c} (72%) create mode 100644 plat/amd/versal2/include/def.h create mode 100644 plat/amd/versal2/include/plat_ipi.h create mode 100644 plat/amd/versal2/include/plat_macros.S create mode 100644 plat/amd/versal2/include/plat_pm_common.h create mode 100644 plat/amd/versal2/include/plat_private.h create mode 100644 plat/amd/versal2/include/platform_def.h create mode 100644 plat/amd/versal2/include/scmi.h create mode 100644 plat/amd/versal2/include/versal2-scmi.h create mode 100644 plat/amd/versal2/plat_psci.c create mode 100644 plat/amd/versal2/plat_topology.c create mode 100644 plat/amd/versal2/platform.mk create mode 100644 plat/amd/versal2/scmi.c create mode 100644 plat/amd/versal2/sip_svc_setup.c create mode 100644 plat/amd/versal2/soc_ipi.c create mode 100644 plat/amd/versal2/tsp/tsp-versal2.mk create mode 100644 plat/arm/board/automotive_rd/platform/rd1ae/fdts/rd1ae_fw_config.dts create mode 100644 plat/arm/board/automotive_rd/platform/rd1ae/include/plat_macros.S create mode 100644 plat/arm/board/automotive_rd/platform/rd1ae/include/platform_def.h create mode 100644 plat/arm/board/automotive_rd/platform/rd1ae/include/rd1ae_helpers.S create mode 100644 plat/arm/board/automotive_rd/platform/rd1ae/platform.mk create mode 100644 plat/arm/board/automotive_rd/platform/rd1ae/rd1ae_bl2_mem_params_desc.c create mode 100644 plat/arm/board/automotive_rd/platform/rd1ae/rd1ae_bl31_setup.c create mode 100644 plat/arm/board/automotive_rd/platform/rd1ae/rd1ae_err.c create mode 100644 plat/arm/board/automotive_rd/platform/rd1ae/rd1ae_plat.c create mode 100644 plat/arm/board/automotive_rd/platform/rd1ae/rd1ae_tbb.c create mode 100644 plat/arm/board/automotive_rd/platform/rd1ae/rd1ae_topology.c delete mode 100644 plat/arm/board/common/swd_rotpk/arm_swd_rotpk_rsa_sha256.bin create mode 100644 plat/arm/board/fvp/aarch64/fvp_lsp_ras_sp.c create mode 100644 plat/arm/board/fvp/fdts/fvp_cactus_sp_manifest.dts create mode 100644 plat/arm/board/fvp/fdts/fvp_cot_desc.dtsi create mode 100644 plat/arm/board/fvp/fdts/fvp_spmc_el1_optee_manifest.dts create mode 100644 plat/arm/board/fvp/fvp_cpu_pwr.c create mode 100644 plat/arm/board/fvp/fvp_el3_token_sign.c rename include/plat/arm/common/arm_pas_def.h => plat/arm/board/fvp/include/fvp_pas_def.h (95%) rename plat/arm/{css/sgi/aarch64/sgi_helper.S => board/neoverse_rd/common/arch/aarch64/nrd_helper.S} (84%) create mode 100644 plat/arm/board/neoverse_rd/common/include/nrd1/nrd_css_def1.h create mode 100644 plat/arm/board/neoverse_rd/common/include/nrd1/nrd_css_fw_def1.h create mode 100644 plat/arm/board/neoverse_rd/common/include/nrd1/nrd_plat_arm_def1.h create mode 100644 plat/arm/board/neoverse_rd/common/include/nrd1/nrd_ros_def1.h create mode 100644 plat/arm/board/neoverse_rd/common/include/nrd1/nrd_ros_fw_def1.h create mode 100644 plat/arm/board/neoverse_rd/common/include/nrd2/nrd_css_def2.h create mode 100644 plat/arm/board/neoverse_rd/common/include/nrd2/nrd_css_fw_def2.h create mode 100644 plat/arm/board/neoverse_rd/common/include/nrd2/nrd_plat_arm_def2.h create mode 100644 plat/arm/board/neoverse_rd/common/include/nrd2/nrd_ros_def2.h create mode 100644 plat/arm/board/neoverse_rd/common/include/nrd2/nrd_ros_fw_def2.h create mode 100644 plat/arm/board/neoverse_rd/common/include/nrd3/nrd_css_def3.h create mode 100644 plat/arm/board/neoverse_rd/common/include/nrd3/nrd_css_fw_def3.h create mode 100644 plat/arm/board/neoverse_rd/common/include/nrd3/nrd_pas_def3.h create mode 100644 plat/arm/board/neoverse_rd/common/include/nrd3/nrd_plat_arm_def3.h create mode 100644 plat/arm/board/neoverse_rd/common/include/nrd3/nrd_ros_def3.h create mode 100644 plat/arm/board/neoverse_rd/common/include/nrd3/nrd_ros_fw_def3.h rename plat/arm/{css/sgi/include/sgi_dmc620_tzc_regions.h => board/neoverse_rd/common/include/nrd_dmc620_tzc_regions.h} (72%) create mode 100644 plat/arm/board/neoverse_rd/common/include/nrd_plat.h rename plat/arm/{css/sgi/include/sgi_ras.h => board/neoverse_rd/common/include/nrd_ras.h} (66%) create mode 100644 plat/arm/board/neoverse_rd/common/include/nrd_sdei.h rename plat/arm/{css/sgi/include/sgi_variant.h => board/neoverse_rd/common/include/nrd_variant.h} (60%) rename plat/arm/{css/sgi => board/neoverse_rd/common}/include/plat_macros.S (86%) rename plat/arm/{css/sgi/sgi-common.mk => board/neoverse_rd/common/nrd-common.mk} (61%) create mode 100644 plat/arm/board/neoverse_rd/common/nrd_bl1_setup.c create mode 100644 plat/arm/board/neoverse_rd/common/nrd_bl31_setup.c rename plat/arm/{css/sgi/sgi_image_load.c => board/neoverse_rd/common/nrd_image_load.c} (84%) rename plat/arm/{css/sgi/sgi_interconnect.c => board/neoverse_rd/common/nrd_interconnect.c} (94%) rename plat/arm/{css/sgi/sgi_plat.c => board/neoverse_rd/common/nrd_plat1.c} (79%) rename plat/arm/{css/sgi/sgi_plat_v2.c => board/neoverse_rd/common/nrd_plat2.c} (73%) create mode 100644 plat/arm/board/neoverse_rd/common/nrd_plat3.c rename plat/arm/{css/sgi/sgi_topology.c => board/neoverse_rd/common/nrd_topology.c} (78%) rename plat/arm/{css/sgi/ras/sgi_ras_common.c => board/neoverse_rd/common/ras/nrd_ras_common.c} (65%) rename plat/arm/{css/sgi/ras/sgi_ras_cpu.c => board/neoverse_rd/common/ras/nrd_ras_cpu.c} (57%) rename plat/arm/{css/sgi/ras/sgi_ras_sram.c => board/neoverse_rd/common/ras/nrd_ras_sram.c} (81%) rename plat/arm/board/{ => neoverse_rd/platform}/rdn1edge/fdts/rdn1edge_fw_config.dts (86%) rename plat/arm/board/{ => neoverse_rd/platform}/rdn1edge/fdts/rdn1edge_nt_fw_config.dts (86%) rename plat/arm/board/{ => neoverse_rd/platform}/rdn1edge/fdts/rdn1edge_tb_fw_config.dts (89%) create mode 100644 plat/arm/board/neoverse_rd/platform/rdn1edge/include/platform_def.h rename plat/arm/board/{ => neoverse_rd/platform}/rdn1edge/platform.mk (65%) rename plat/arm/board/{ => neoverse_rd/platform}/rdn1edge/rdn1edge_err.c (71%) rename plat/arm/board/{ => neoverse_rd/platform}/rdn1edge/rdn1edge_plat.c (70%) rename plat/arm/board/{ => neoverse_rd/platform}/rdn1edge/rdn1edge_security.c (85%) rename plat/arm/board/{ => neoverse_rd/platform}/rdn1edge/rdn1edge_topology.c (86%) rename plat/arm/board/{ => neoverse_rd/platform}/rdn1edge/rdn1edge_trusted_boot.c (88%) rename plat/arm/board/{rde1edge/fdts/rde1edge_fw_config.dts => neoverse_rd/platform/rdn2/fdts/rdn2_fw_config.dts} (64%) rename plat/arm/board/{ => neoverse_rd/platform}/rdn2/fdts/rdn2_nt_fw_config.dts (90%) create mode 100644 plat/arm/board/neoverse_rd/platform/rdn2/fdts/rdn2_stmm_sel0_manifest.dts rename plat/arm/board/{ => neoverse_rd/platform}/rdn2/fdts/rdn2_tb_fw_config.dts (89%) create mode 100644 plat/arm/board/neoverse_rd/platform/rdn2/include/platform_def.h create mode 100644 plat/arm/board/neoverse_rd/platform/rdn2/include/rdn2_ras.h rename plat/arm/board/{ => neoverse_rd/platform}/rdn2/platform.mk (50%) rename plat/arm/board/{ => neoverse_rd/platform}/rdn2/rdn2_err.c (71%) rename plat/arm/board/{ => neoverse_rd/platform}/rdn2/rdn2_plat.c (60%) rename plat/arm/board/{ => neoverse_rd/platform}/rdn2/rdn2_ras.c (60%) rename plat/arm/board/{ => neoverse_rd/platform}/rdn2/rdn2_security.c (55%) rename plat/arm/board/{ => neoverse_rd/platform}/rdn2/rdn2_topology.c (77%) rename plat/arm/board/{ => neoverse_rd/platform}/rdn2/rdn2_trusted_boot.c (88%) rename plat/arm/board/{ => neoverse_rd/platform}/rdv1/fdts/rdv1_fw_config.dts (83%) rename plat/arm/board/{ => neoverse_rd/platform}/rdv1/fdts/rdv1_nt_fw_config.dts (83%) rename plat/arm/board/{ => neoverse_rd/platform}/rdv1/fdts/rdv1_tb_fw_config.dts (89%) rename plat/arm/board/{ => neoverse_rd/platform}/rdv1/include/platform_def.h (64%) rename plat/arm/board/{ => neoverse_rd/platform}/rdv1/platform.mk (64%) rename plat/arm/board/{ => neoverse_rd/platform}/rdv1/rdv1_err.c (71%) rename plat/arm/board/{ => neoverse_rd/platform}/rdv1/rdv1_plat.c (59%) rename plat/arm/board/{ => neoverse_rd/platform}/rdv1/rdv1_security.c (82%) rename plat/arm/board/{ => neoverse_rd/platform}/rdv1/rdv1_topology.c (77%) rename plat/arm/board/{ => neoverse_rd/platform}/rdv1/rdv1_trusted_boot.c (88%) rename plat/arm/board/{ => neoverse_rd/platform}/rdv1mc/fdts/rdv1mc_fw_config.dts (83%) rename plat/arm/board/{ => neoverse_rd/platform}/rdv1mc/fdts/rdv1mc_nt_fw_config.dts (83%) rename plat/arm/board/{ => neoverse_rd/platform}/rdv1mc/fdts/rdv1mc_tb_fw_config.dts (89%) rename plat/arm/board/{ => neoverse_rd/platform}/rdv1mc/include/platform_def.h (69%) rename plat/arm/board/{ => neoverse_rd/platform}/rdv1mc/platform.mk (65%) rename plat/arm/board/{ => neoverse_rd/platform}/rdv1mc/rdv1mc_err.c (71%) rename plat/arm/board/{ => neoverse_rd/platform}/rdv1mc/rdv1mc_plat.c (54%) rename plat/arm/board/{ => neoverse_rd/platform}/rdv1mc/rdv1mc_security.c (63%) rename plat/arm/board/{ => neoverse_rd/platform}/rdv1mc/rdv1mc_topology.c (70%) rename plat/arm/board/{ => neoverse_rd/platform}/rdv1mc/rdv1mc_trusted_boot.c (88%) rename plat/arm/board/{rdn2/fdts/rdn2_fw_config.dts => neoverse_rd/platform/rdv3/fdts/rdv3_fw_config.dts} (71%) rename plat/arm/board/{rde1edge/fdts/rde1edge_nt_fw_config.dts => neoverse_rd/platform/rdv3/fdts/rdv3_nt_fw_config.dts} (78%) rename plat/arm/board/{rde1edge/fdts/rde1edge_tb_fw_config.dts => neoverse_rd/platform/rdv3/fdts/rdv3_tb_fw_config.dts} (90%) create mode 100644 plat/arm/board/neoverse_rd/platform/rdv3/include/platform_def.h create mode 100644 plat/arm/board/neoverse_rd/platform/rdv3/include/rdv3_mhuv3.h create mode 100644 plat/arm/board/neoverse_rd/platform/rdv3/include/rdv3_rse_comms.h create mode 100644 plat/arm/board/neoverse_rd/platform/rdv3/platform.mk create mode 100644 plat/arm/board/neoverse_rd/platform/rdv3/rdv3_bl1_measured_boot.c create mode 100644 plat/arm/board/neoverse_rd/platform/rdv3/rdv3_bl2_measured_boot.c create mode 100644 plat/arm/board/neoverse_rd/platform/rdv3/rdv3_bl2_setup.c create mode 100644 plat/arm/board/neoverse_rd/platform/rdv3/rdv3_bl31_setup.c create mode 100644 plat/arm/board/neoverse_rd/platform/rdv3/rdv3_common.c create mode 100644 plat/arm/board/neoverse_rd/platform/rdv3/rdv3_common_measured_boot.c create mode 100644 plat/arm/board/neoverse_rd/platform/rdv3/rdv3_err.c create mode 100644 plat/arm/board/neoverse_rd/platform/rdv3/rdv3_mhuv3.c create mode 100644 plat/arm/board/neoverse_rd/platform/rdv3/rdv3_plat_attest_token.c create mode 100644 plat/arm/board/neoverse_rd/platform/rdv3/rdv3_realm_attest_key.c create mode 100644 plat/arm/board/neoverse_rd/platform/rdv3/rdv3_security.c create mode 100644 plat/arm/board/neoverse_rd/platform/rdv3/rdv3_topology.c rename plat/arm/board/{rde1edge/rde1edge_trusted_boot.c => neoverse_rd/platform/rdv3/rdv3_trusted_boot.c} (89%) rename plat/arm/board/{ => neoverse_rd/platform}/sgi575/fdts/sgi575_fw_config.dts (86%) rename plat/arm/board/{ => neoverse_rd/platform}/sgi575/fdts/sgi575_nt_fw_config.dts (86%) rename plat/arm/board/{ => neoverse_rd/platform}/sgi575/fdts/sgi575_tb_fw_config.dts (89%) create mode 100644 plat/arm/board/neoverse_rd/platform/sgi575/include/platform_def.h rename plat/arm/board/{ => neoverse_rd/platform}/sgi575/platform.mk (65%) rename plat/arm/board/{ => neoverse_rd/platform}/sgi575/sgi575_err.c (71%) rename plat/arm/board/{ => neoverse_rd/platform}/sgi575/sgi575_plat.c (52%) rename plat/arm/board/{ => neoverse_rd/platform}/sgi575/sgi575_security.c (85%) rename plat/arm/board/{ => neoverse_rd/platform}/sgi575/sgi575_topology.c (89%) rename plat/arm/board/{ => neoverse_rd/platform}/sgi575/sgi575_trusted_boot.c (88%) delete mode 100644 plat/arm/board/rde1edge/include/platform_def.h delete mode 100644 plat/arm/board/rde1edge/platform.mk delete mode 100644 plat/arm/board/rde1edge/rde1edge_err.c delete mode 100644 plat/arm/board/rde1edge/rde1edge_plat.c delete mode 100644 plat/arm/board/rde1edge/rde1edge_security.c delete mode 100644 plat/arm/board/rde1edge/rde1edge_topology.c delete mode 100644 plat/arm/board/rdn1edge/include/platform_def.h delete mode 100644 plat/arm/board/rdn2/include/platform_def.h delete mode 100644 plat/arm/board/rdn2/include/rdn2_ras.h delete mode 100644 plat/arm/board/sgi575/include/platform_def.h create mode 100644 plat/arm/board/tc/fdts/dice_prot_env.dtsi create mode 100644 plat/arm/board/tc/fdts/tc_nt_fw_config.dts create mode 100644 plat/arm/board/tc/fdts/tc_spmc_common_sp_manifest.dtsi rename plat/arm/board/tc/fdts/{tc_spmc_manifest.dts => tc_spmc_manifest.dtsi} (65%) create mode 100644 plat/arm/board/tc/fdts/tc_spmc_test_manifest.dts create mode 100644 plat/arm/board/tc/fdts/tc_spmc_trusty_sp_manifest.dts rename plat/arm/board/tc/{rss_ap_test_stubs.c => rse_ap_test_stubs.c} (92%) rename plat/arm/board/tc/{rss_ap_tests.c => rse_ap_tests.c} (94%) rename plat/arm/board/tc/{rss_ap_testsuites.c => rse_ap_testsuites.c} (93%) rename plat/arm/board/tc/{rss_ap_testsuites.h => rse_ap_testsuites.h} (76%) create mode 100644 plat/arm/board/tc/tc_bl1_dpe.c create mode 100644 plat/arm/board/tc/tc_bl1_setup.c create mode 100644 plat/arm/board/tc/tc_bl2_dpe.c create mode 100644 plat/arm/board/tc/tc_common_dpe.c create mode 100644 plat/arm/board/tc/tc_dpe.h create mode 100644 plat/arm/board/tc/tc_trng.c create mode 100644 plat/arm/common/arm_ni.c create mode 100644 plat/arm/common/arm_transfer_list.c create mode 100644 plat/arm/common/plat_arm_mbedtls_config.h create mode 100644 plat/arm/common/plat_arm_psa_mbedtls_config.h delete mode 100644 plat/arm/css/sgi/include/sgi_base_platform_def.h delete mode 100644 plat/arm/css/sgi/include/sgi_plat.h delete mode 100644 plat/arm/css/sgi/include/sgi_sdei.h delete mode 100644 plat/arm/css/sgi/include/sgi_soc_css_def.h delete mode 100644 plat/arm/css/sgi/include/sgi_soc_css_def_v2.h delete mode 100644 plat/arm/css/sgi/include/sgi_soc_platform_def.h delete mode 100644 plat/arm/css/sgi/include/sgi_soc_platform_def_v2.h delete mode 100644 plat/arm/css/sgi/sgi_bl31_setup.c create mode 100644 plat/imx/common/imx_bl31_common.c create mode 100644 plat/imx/common/imx_common.c create mode 100644 plat/imx/common/include/imx_plat_common.h create mode 100644 plat/imx/common/include/plat_common.h create mode 100644 plat/imx/imx8ulp/apd_context.c create mode 100644 plat/imx/imx8ulp/dram.c create mode 100644 plat/imx/imx8ulp/imx8ulp_bl31_setup.c create mode 100644 plat/imx/imx8ulp/imx8ulp_caam.c create mode 100644 plat/imx/imx8ulp/imx8ulp_psci.c create mode 100644 plat/imx/imx8ulp/include/dram.h create mode 100644 plat/imx/imx8ulp/include/imx8ulp_caam.h create mode 100644 plat/imx/imx8ulp/include/platform_def.h create mode 100644 plat/imx/imx8ulp/include/scmi.h create mode 100644 plat/imx/imx8ulp/include/scmi_sensor.h create mode 100644 plat/imx/imx8ulp/include/xrdc.h create mode 100644 plat/imx/imx8ulp/platform.mk create mode 100644 plat/imx/imx8ulp/scmi/scmi.c create mode 100644 plat/imx/imx8ulp/scmi/scmi_pd.c create mode 100644 plat/imx/imx8ulp/scmi/scmi_sensor.c create mode 100644 plat/imx/imx8ulp/upower/upmu.h create mode 100644 plat/imx/imx8ulp/upower/upower_api.c create mode 100644 plat/imx/imx8ulp/upower/upower_api.h create mode 100644 plat/imx/imx8ulp/upower/upower_defs.h create mode 100644 plat/imx/imx8ulp/upower/upower_hal.c create mode 100644 plat/imx/imx8ulp/upower/upower_soc_defs.h create mode 100644 plat/imx/imx8ulp/xrdc/xrdc_config.h create mode 100644 plat/imx/imx8ulp/xrdc/xrdc_core.c create mode 100644 plat/intel/soc/agilex5/include/agilex5_cache.h create mode 100644 plat/intel/soc/agilex5/include/agilex5_ddr.h create mode 100644 plat/intel/soc/agilex5/include/agilex5_iossm_mailbox.h create mode 100644 plat/intel/soc/agilex5/soc/agilex5_cache.S create mode 100644 plat/intel/soc/agilex5/soc/agilex5_ddr.c create mode 100644 plat/intel/soc/agilex5/soc/agilex5_iossm_mailbox.c create mode 100644 plat/intel/soc/common/include/socfpga_ros.h create mode 100644 plat/intel/soc/common/lib/sha/sha.c create mode 100644 plat/intel/soc/common/lib/sha/sha.h create mode 100644 plat/intel/soc/common/soc/socfpga_system_manager.c create mode 100644 plat/intel/soc/common/socfpga_ros.c create mode 100644 plat/mediatek/drivers/iommu/mtk_iommu_smc.h create mode 100644 plat/mediatek/drivers/rng/mt8186/rng_plat.c create mode 100644 plat/mediatek/drivers/rng/mt8186/rng_plat.h create mode 100644 plat/mediatek/drivers/rng/mt8188/rng_plat.c create mode 100644 plat/mediatek/drivers/rng/mt8188/rng_plat.h create mode 100644 plat/mediatek/drivers/rng/rng.c create mode 100644 plat/mediatek/drivers/rng/rules.mk rename plat/mediatek/{mt8188 => }/include/plat_helpers.h (71%) delete mode 100644 plat/mediatek/mt8186/include/plat_helpers.h delete mode 100644 plat/mediatek/mt8192/include/plat_helpers.h delete mode 100644 plat/mediatek/mt8195/include/plat_helpers.h create mode 100644 plat/nxp/s32/s32g274ardb2/include/plat_console.h create mode 100644 plat/nxp/s32/s32g274ardb2/include/plat_helpers.h create mode 100644 plat/nxp/s32/s32g274ardb2/include/plat_io_storage.h create mode 100644 plat/nxp/s32/s32g274ardb2/include/plat_macros.S create mode 100644 plat/nxp/s32/s32g274ardb2/include/platform_def.h create mode 100644 plat/nxp/s32/s32g274ardb2/include/s32cc-ncore.h create mode 100644 plat/nxp/s32/s32g274ardb2/plat_bl2_el3_setup.c create mode 100644 plat/nxp/s32/s32g274ardb2/plat_bl2_image_desc.c create mode 100644 plat/nxp/s32/s32g274ardb2/plat_bl31_setup.c create mode 100644 plat/nxp/s32/s32g274ardb2/plat_console.c create mode 100644 plat/nxp/s32/s32g274ardb2/plat_helpers.S create mode 100644 plat/nxp/s32/s32g274ardb2/plat_io_storage.c create mode 100644 plat/nxp/s32/s32g274ardb2/platform.mk create mode 100644 plat/nxp/s32/s32g274ardb2/s32cc_ncore.c create mode 100644 plat/nxp/s32/s32g274ardb2/s32g2_psci.c create mode 100644 plat/nxp/s32/s32g274ardb2/s32g2_soc.c create mode 100644 plat/qemu/common/qemu_plat_attest_token.c create mode 100644 plat/qemu/common/qemu_realm_attest_key.c create mode 100644 plat/qemu/common/trp/qemu_trp_setup.c create mode 100644 plat/qemu/common/trp/trp-qemu-common.mk create mode 100644 plat/qemu/qemu/include/qemu_pas_def.h create mode 100644 plat/qemu/qemu/trp/trp-qemu.mk create mode 100644 plat/renesas/rcar/rcar_stack_protector.c create mode 100644 plat/rockchip/common/include/plat_pm_helpers.h create mode 100644 plat/rockchip/common/plat_pm_helpers.c create mode 100644 plat/rockchip/common/scmi/scmi.c create mode 100644 plat/rockchip/common/scmi/scmi_clock.c create mode 100644 plat/rockchip/common/scmi/scmi_clock.h create mode 100644 plat/rockchip/common/scmi/scmi_rstd.c create mode 100644 plat/rockchip/common/scmi/scmi_rstd.h create mode 100644 plat/rockchip/rk3399/drivers/pmu/pmu_fw.S delete mode 100644 plat/rockchip/rk3399/drivers/pmu/pmu_fw.c create mode 100644 plat/rockchip/rk3568/drivers/pmu/plat_pmu_macros.S create mode 100644 plat/rockchip/rk3568/drivers/pmu/pmu.c create mode 100644 plat/rockchip/rk3568/drivers/pmu/pmu.h create mode 100644 plat/rockchip/rk3568/drivers/soc/soc.c create mode 100644 plat/rockchip/rk3568/drivers/soc/soc.h create mode 100644 plat/rockchip/rk3568/include/plat.ld.S create mode 100644 plat/rockchip/rk3568/include/plat_sip_calls.h create mode 100644 plat/rockchip/rk3568/include/platform_def.h create mode 100644 plat/rockchip/rk3568/plat_sip_calls.c create mode 100644 plat/rockchip/rk3568/platform.mk create mode 100644 plat/rockchip/rk3568/rk3568_def.h create mode 100644 plat/rockchip/rk3588/drivers/pmu/plat_pmu_macros.S create mode 100644 plat/rockchip/rk3588/drivers/pmu/pm_pd_regs.c create mode 100644 plat/rockchip/rk3588/drivers/pmu/pm_pd_regs.h create mode 100644 plat/rockchip/rk3588/drivers/pmu/pmu.c create mode 100644 plat/rockchip/rk3588/drivers/pmu/pmu.h create mode 100644 plat/rockchip/rk3588/drivers/scmi/rk3588_clk.c create mode 100644 plat/rockchip/rk3588/drivers/scmi/rk3588_clk.h create mode 100644 plat/rockchip/rk3588/drivers/scmi/rk3588_rstd.c create mode 100644 plat/rockchip/rk3588/drivers/secure/secure.c create mode 100644 plat/rockchip/rk3588/drivers/secure/secure.h create mode 100644 plat/rockchip/rk3588/drivers/soc/soc.c create mode 100644 plat/rockchip/rk3588/drivers/soc/soc.h create mode 100644 plat/rockchip/rk3588/include/plat.ld.S create mode 100644 plat/rockchip/rk3588/include/plat_sip_calls.h create mode 100644 plat/rockchip/rk3588/include/platform_def.h create mode 100644 plat/rockchip/rk3588/plat_sip_calls.c create mode 100644 plat/rockchip/rk3588/platform.mk create mode 100644 plat/rockchip/rk3588/rk3588_def.h rename plat/rpi/{rpi4 => common}/aarch64/armstub8_header.S (89%) rename plat/rpi/{rpi4 => common}/include/plat_macros.S (87%) create mode 100644 plat/rpi/common/rpi3_console_dual.c create mode 100644 plat/rpi/common/rpi3_console_pl011.c rename plat/rpi/{rpi4 => common}/rpi4_bl31_setup.c (67%) rename plat/rpi/{rpi4/rpi4_pci_svc.c => common/rpi_pci_svc.c} (76%) delete mode 100644 plat/rpi/rpi3/include/plat_macros.S create mode 100644 plat/rpi/rpi4/rpi4_setup.c create mode 100644 plat/rpi/rpi5/include/plat.ld.S create mode 100644 plat/rpi/rpi5/include/platform_def.h create mode 100644 plat/rpi/rpi5/include/rpi_hw.h create mode 100644 plat/rpi/rpi5/platform.mk create mode 100644 plat/rpi/rpi5/rpi5_setup.c rename plat/st/{stm32mp1 => common}/include/plat_def_fip_uuid.h (59%) delete mode 100644 plat/st/common/include/stm32mp_mbedtls_config-2.h create mode 100644 plat/st/stm32mp1/plat_ddr.c create mode 100644 plat/st/stm32mp2/bl31_plat_setup.c create mode 100644 plat/st/stm32mp2/include/plat_tbbr_img_def.h create mode 100644 plat/st/stm32mp2/include/stm32mp2_private.h create mode 100644 plat/st/stm32mp2/plat_ddr.c create mode 100644 plat/st/stm32mp2/stm32mp2_pm.c create mode 100644 plat/st/stm32mp2/stm32mp2_private.c create mode 100644 plat/st/stm32mp2/stm32mp2_syscfg.c create mode 100644 plat/st/stm32mp2/stm32mp2_topology.c create mode 100644 plat/st/stm32mp2/stm32mp2_usb_dfu.c create mode 100644 plat/xilinx/common/include/plat_clkfunc.h create mode 100644 plat/xilinx/common/include/plat_xfer_list.h create mode 100644 plat/xilinx/common/plat_clkfunc.c create mode 100644 plat/xilinx/common/plat_xfer_list.c create mode 100644 services/el3/ven_el3_svc.c create mode 100644 services/oem/chromeos/widevine_smc_handlers.c create mode 100644 tools/cot_dt2c/.gitignore create mode 100644 tools/cot_dt2c/cot_dt2c/__init__.py create mode 100644 tools/cot_dt2c/cot_dt2c/__main__.py create mode 100644 tools/cot_dt2c/cot_dt2c/cli.py create mode 100644 tools/cot_dt2c/cot_dt2c/cot_dt2c.py create mode 100644 tools/cot_dt2c/cot_dt2c/cot_parser.py create mode 100644 tools/cot_dt2c/cot_dt2c/dt_validator.py create mode 100644 tools/cot_dt2c/poetry.lock create mode 100644 tools/cot_dt2c/pyproject.toml create mode 100644 tools/cot_dt2c/tests/test.dtsi create mode 100644 tools/cot_dt2c/tests/test2.dtsi create mode 100644 tools/cot_dt2c/tests/test_invalid_bracket.dtsi create mode 100644 tools/cot_dt2c/tests/test_invalid_missing_attribute.dtsi create mode 100644 tools/cot_dt2c/tests/test_invalid_missing_ctr.dtsi create mode 100644 tools/cot_dt2c/tests/test_invalid_missing_root.dtsi create mode 100644 tools/cot_dt2c/tests/test_invalid_undefined_parent.dtsi create mode 100644 tools/cot_dt2c/tests/test_util.py rename {plat/arm/board/juno/fip => tools/fiptool/plat_fiptool/arm/board/juno}/plat_def_uuid_config.c (100%) rename tools/fiptool/plat_fiptool/st/{stm32mp1 => }/plat_def_uuid_config.c (71%) rename tools/fiptool/plat_fiptool/st/{stm32mp1 => }/plat_fiptool.mk (60%) create mode 100644 tools/tlc/.gitignore create mode 100644 tools/tlc/assets/images/coverage.svg create mode 100644 tools/tlc/poetry.lock create mode 100644 tools/tlc/pyproject.toml create mode 100644 tools/tlc/setup.cfg create mode 100644 tools/tlc/tests/conftest.py create mode 100644 tools/tlc/tests/test_cli.py create mode 100644 tools/tlc/tests/test_transfer_list.py create mode 100644 tools/tlc/tlc/__init__.py create mode 100644 tools/tlc/tlc/__main__.py create mode 100644 tools/tlc/tlc/cli.py create mode 100644 tools/tlc/tlc/te.py create mode 100644 tools/tlc/tlc/templates/header.h.j2 create mode 100644 tools/tlc/tlc/tl.py create mode 100644 tools/tlc/tox.ini diff --git a/.commitlintrc.js b/.commitlintrc.js index cfafbedc..53e3a635 100644 --- a/.commitlintrc.js +++ b/.commitlintrc.js @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, Arm Limited. All rights reserved. + * Copyright (c) 2021-2024, Arm Limited. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -8,10 +8,9 @@ "use strict"; -const fs = require("fs"); -const yaml = require("js-yaml"); - -const { "trailer-exists": trailerExists } = require("@commitlint/rules").default; +import fs from "fs"; +import rules from "@commitlint/rules"; +import yaml from "js-yaml"; /* * The types and scopes accepted by both Commitlint and Commitizen are defined by the changelog @@ -37,7 +36,7 @@ function getTypes(sections) { function getScopes(subsections) { return subsections.flatMap(subsection => { - const scope = subsection.scope ? [ subsection.scope ] : []; + const scope = subsection.scope ? [subsection.scope] : []; const subscopes = getScopes(subsection.subsections || []); return scope.concat(subscopes); @@ -47,13 +46,13 @@ function getScopes(subsections) { const types = getTypes(changelog.sections).sort(); /* Sort alphabetically */ const scopes = getScopes(changelog.subsections).sort(); /* Sort alphabetically */ -module.exports = { +export default { extends: ["@commitlint/config-conventional"], plugins: [ { rules: { - "signed-off-by-exists": trailerExists, - "change-id-exists": trailerExists, + "signed-off-by-exists": rules["trailer-exists"], + "change-id-exists": rules["trailer-exists"], }, }, ], @@ -64,7 +63,7 @@ module.exports = { "change-id-exists": [1, "always", "Change-Id:"], /* Warning */ "signed-off-by-exists": [1, "always", "Signed-off-by:"], /* Warning */ - "type-case": [2, "always", "lower-case" ], /* Error */ + "type-case": [2, "always", "lower-case"], /* Error */ "type-enum": [2, "always", types], /* Error */ "scope-case": [2, "always", "lower-case"], /* Error */ diff --git a/.ctags b/.ctags new file mode 100644 index 00000000..5e608e48 --- /dev/null +++ b/.ctags @@ -0,0 +1,4 @@ +--regex-Asm=/^func[ \t]+([a-zA-Z_0-9]+)$/\1/l,function/ +--regex-Asm=/^.*\.macro[ \t]+([a-zA-Z_0-9]+)$/\1/m,macro/ +--regex-Asm=/^vector_entry[ \t]+([a-zA-Z_0-9]+)$/\1/l,function/ +--regex-Asm=/^.equ[ \t]+([a-zA-Z_0-9]+),/\1/l,name/ diff --git a/.cz-adapter.cjs b/.cz-adapter.cjs new file mode 100644 index 00000000..26aaeb2a --- /dev/null +++ b/.cz-adapter.cjs @@ -0,0 +1,15 @@ +/* + * Copyright (c) 2024, Arm Limited. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +/* + * A workaround for: + * + * https://github.com/conventional-changelog/commitlint/issues/3949 + */ + +exports.prompter = async (inquirerIns, commit) => { + ; (await import('@commitlint/cz-commitlint')).prompter(inquirerIns, commit) +} diff --git a/.cz.json b/.cz.json index 556c39f1..969a73b8 100644 --- a/.cz.json +++ b/.cz.json @@ -1,3 +1,3 @@ { - "path": "@commitlint/cz-commitlint" + "path": "./.cz-adapter.cjs" } diff --git a/.editorconfig b/.editorconfig index 12f786de..1b29c880 100644 --- a/.editorconfig +++ b/.editorconfig @@ -1,5 +1,5 @@ # -# Copyright (c) 2017-2020, Arm Limited and Contributors. All rights reserved. +# Copyright (c) 2017-2024, Arm Limited and Contributors. All rights reserved. # # SPDX-License-Identifier: BSD-3-Clause # @@ -70,3 +70,6 @@ indent_style = space # [PEP8] Maximum Line Length # "Limit all lines to a maximum of 79 characters." max_line_length = 79 + +[.git/COMMIT_EDITMSG] +max_line_length = 72 diff --git a/.husky/commit-msg b/.husky/commit-msg index c1c96002..b5d407b7 100755 --- a/.husky/commit-msg +++ b/.husky/commit-msg @@ -1,7 +1,4 @@ #!/bin/sh -# shellcheck source=./_/husky.sh -. "$(dirname "$0")/_/husky.sh" - "$(dirname "$0")/commit-msg.gerrit" "$@" "$(dirname "$0")/commit-msg.commitlint" "$@" diff --git a/.husky/pre-commit b/.husky/pre-commit index afcb1f6d..f438ddbe 100755 --- a/.husky/pre-commit +++ b/.husky/pre-commit @@ -1,6 +1,3 @@ #!/bin/sh -# shellcheck source=./_/husky.sh -. "$(dirname "$0")/_/husky.sh" - "$(dirname "$0")/pre-commit.copyright" "$@" diff --git a/.husky/pre-commit.copyright b/.husky/pre-commit.copyright index a4dfee8e..5f838a68 100755 --- a/.husky/pre-commit.copyright +++ b/.husky/pre-commit.copyright @@ -17,10 +17,24 @@ ARM_RGX="\(ARM\|Arm\|arm\)" exit_code=0 +PLATPROV= +ORG=`echo "$GIT_AUTHOR_EMAIL" | awk -F '[@]' '{ print $2;}'` + +case $ORG in + amd.com) + PLATPROV="Advanced Micro Devices, Inc. All rights reserved." + ;; + *arm.com) + PLATPROV="$ARM_RGX" + ;; + *) + ;; +esac + function user_warning() { echo -e "Copyright of $RED$FILE$BLANK is out of date/incorrect" echo -e "Updated copyright to" - grep -nr "opyright.*$YEAR_RGX.*$ARM_RGX" "$FILE" + grep -nr "opyright.*$YEAR_RGX.*$PLATPROV" "$FILE" echo } @@ -29,31 +43,45 @@ while read -r FILE; do then break fi - # Check if correct copyright notice is in file. - # To reduce false positives, we assume files with no - # copyright notice do not require it. - if ! grep "opyright.*$YEAR_NOW.*$ARM_RGX" "$FILE">/dev/null 2>&1 + + # Check if copyright header exists for the org + if ! grep "opyright.*$YEAR_RGX.*$PLATPROV" "$FILE">/dev/null 2>&1 && [[ $ORG != *arm* ]] then - # If it is "from_date - to_date" type of entry - change to_date entry. - if grep "opyright.*$YEAR_RGX.*-.*$YEAR_RGX.*$ARM_RGX" "$FILE" >/dev/null 2>&1 - then - exit_code=1 - sed -i "s/\(opyright.*\)$YEAR_RGX\(.*$ARM_RGX\)/\1$(date +"%Y"), Arm/" $FILE - user_warning - # If it is single "date" type of entry - add the copyright extension to current year. - elif grep "opyright.*$YEAR_RGX.*$ARM_RGX" "$FILE" >/dev/null 2>&1 + echo -e "Copyright header ""$RED""$PLATPROV""$BLANK"" is missing in ""$YELLOW""$FILE""$BLANK" + fi + + # Check if the copyright year is updated for the org and update it + if [ ! -z "$PLATPROV" ] + then + if ! grep "opyright.*$YEAR_NOW.*$PLATPROV" "$FILE">/dev/null 2>&1 then - exit_code=1 - sed -i "s/\(opyright.*$YEAR_RGX\)\(.*$ARM_RGX\)/\1-$(date +"%Y"), Arm/" $FILE - user_warning + # If it is "from_date - to_date" type of entry - change to_date entry. + if grep "opyright.*$YEAR_RGX.*-.*$YEAR_RGX.*$PLATPROV" "$FILE" >/dev/null 2>&1 + then + exit_code=1 + sed -i "s/\(opyright.*\)$YEAR_RGX\(.*$PLATPROV\)/\1$(date +"%Y")\2/" $FILE + user_warning + # If it is single "date" type of entry - add the copyright extension to current year. + elif grep "opyright.*$YEAR_RGX.*$PLATPROV" "$FILE" >/dev/null 2>&1 + then + exit_code=1 + sed -i "s/\(opyright.*$YEAR_RGX\)\(.*$PLATPROV\)/\1-$(date +"%Y")\2/" $FILE + user_warning + fi + + # Even if the year is correct - verify that Arm copyright is formatted correctly. + if [[ $ORG == *arm* ]] + then + if grep "opyright.*\(ARM\|arm\)" "$FILE">/dev/null 2>&1 + then + exit_code=1 + sed -i "s/\(opyright.*\)\(ARM\|arm\)/\1Arm/" $FILE + user_warning + fi + fi fi - # Even if the year is correct - verify that Arm copyright is formatted correctly. - elif grep "opyright.*\(ARM\|arm\)" "$FILE">/dev/null 2>&1 - then - exit_code=1 - sed -i "s/\(opyright.*\)\(ARM\|arm\)/\1Arm/" $FILE - user_warning fi + done <<< "$FILES" if [ $exit_code -eq 1 ] diff --git a/.nvmrc b/.nvmrc index e0325e5a..ee09fac7 100644 --- a/.nvmrc +++ b/.nvmrc @@ -1 +1 @@ -v16.17.1 +v20.11.1 diff --git a/.readthedocs.yaml b/.readthedocs.yaml index 7b6a1f5c..2d1afab8 100644 --- a/.readthedocs.yaml +++ b/.readthedocs.yaml @@ -1,4 +1,4 @@ -# Copyright (c) 2023, Arm Limited. All rights reserved +# Copyright (c) 2023-2024, Arm Limited. All rights reserved # # SPDX-License-Identifier: BSD-3-Clause # @@ -19,9 +19,8 @@ build: jobs: post_create_environment: - pip install poetry=="1.3.2" - - poetry config virtualenvs.create false post_install: - - poetry install --with doc + - VIRTUAL_ENV=$READTHEDOCS_VIRTUALENV_PATH poetry install --with docs sphinx: configuration: docs/conf.py diff --git a/.versionrc.js b/.versionrc.cjs similarity index 95% rename from .versionrc.js rename to .versionrc.cjs index c7ee4a22..ac473b09 100644 --- a/.versionrc.js +++ b/.versionrc.cjs @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021-2023, Arm Limited. All rights reserved. + * Copyright (c) 2021-2024, Arm Limited. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -84,9 +84,9 @@ module.exports = { "filename": "pyproject.toml", "updater": { "readVersion": function (contents) { - const _ver = contents.match(/version\s=.*"(\d)\.(\d)\.(\d)/); + const _ver = contents.match(/version\s=.*"(\d+?)\.(\d+?)\.(\d+?)/); - return `${_ver[1]}.${_ver[2]}.${_ver[2]}`; + return `${_ver[1]}.${_ver[2]}.${_ver[3]}`; }, "writeVersion": function (contents, version) { @@ -104,9 +104,9 @@ module.exports = { "filename": "docs/conf.py", "updater": { "readVersion": function (contents) { - const _ver = contents.match(/version\s=.*"(\d)\.(\d)\.(\d)/); + const _ver = contents.match(/version\s=.*"(\d+?)\.(\d+?)\.(\d+?)/); - return `${_ver[1]}.${_ver[2]}.${_ver[2]}`; + return `${_ver[1]}.${_ver[2]}.${_ver[3]}`; }, "writeVersion": function (contents, version) { diff --git a/Makefile b/Makefile index 97c3c154..e4a1f311 100644 --- a/Makefile +++ b/Makefile @@ -1,5 +1,5 @@ # -# Copyright (c) 2013-2023, Arm Limited and Contributors. All rights reserved. +# Copyright (c) 2013-2024, Arm Limited and Contributors. All rights reserved. # # SPDX-License-Identifier: BSD-3-Clause # @@ -8,8 +8,9 @@ # Trusted Firmware Version # VERSION_MAJOR := 2 -VERSION_MINOR := 10 -VERSION_PATCH := 0 # Only used for LTS releases +VERSION_MINOR := 12 +# VERSION_PATCH is only used for LTS releases +VERSION_PATCH := 0 VERSION := ${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_PATCH} # Default goal is build all images @@ -23,17 +24,30 @@ MAKEOVERRIDES = MAKE_HELPERS_DIRECTORY := make_helpers/ include ${MAKE_HELPERS_DIRECTORY}build_macros.mk include ${MAKE_HELPERS_DIRECTORY}build_env.mk +include ${MAKE_HELPERS_DIRECTORY}build-rules.mk +include ${MAKE_HELPERS_DIRECTORY}common.mk ################################################################################ # Default values for build configurations, and their dependencies ################################################################################ include ${MAKE_HELPERS_DIRECTORY}defaults.mk +include ${MAKE_HELPERS_DIRECTORY}plat_helpers.mk + +# To be able to set platform specific defaults +ifneq ($(PLAT_DEFAULTS_MAKEFILE_FULL),) +include ${PLAT_DEFAULTS_MAKEFILE_FULL} +endif + +################################################################################ +# Configure the toolchains used to build TF-A and its tools +################################################################################ + +include ${MAKE_HELPERS_DIRECTORY}toolchain.mk # Assertions enabled for DEBUG builds by default ENABLE_ASSERTIONS := ${DEBUG} ENABLE_PMF := ${ENABLE_RUNTIME_INSTRUMENTATION} -PLAT := ${DEFAULT_PLAT} ################################################################################ # Checkpatch script options @@ -75,45 +89,8 @@ CHECK_PATHS := ${ROOT_DIRS_TO_CHECK} \ # Process build options ################################################################################ -# Verbose flag -ifeq (${V},0) - Q:=@ - ECHO:=@echo +ifeq ($(verbose),) CHECKCODE_ARGS += --no-summary --terse -else - Q:= - ECHO:=$(ECHO_QUIET) -endif - -ifneq ($(findstring s,$(filter-out --%,$(MAKEFLAGS))),) - Q:=@ - ECHO:=$(ECHO_QUIET) -endif - -export Q ECHO - -################################################################################ -# Toolchain -################################################################################ - -HOSTCC := gcc -export HOSTCC - -CC := ${CROSS_COMPILE}gcc -CPP := ${CROSS_COMPILE}cpp -AS := ${CROSS_COMPILE}gcc -AR := ${CROSS_COMPILE}ar -LINKER := ${CROSS_COMPILE}ld -OC := ${CROSS_COMPILE}objcopy -OD := ${CROSS_COMPILE}objdump -NM := ${CROSS_COMPILE}nm -PP := ${CROSS_COMPILE}gcc -E -DTC := dtc - -# Use ${LD}.bfd instead if it exists (as absolute path or together with $PATH). -ifneq ($(strip $(wildcard ${LD}.bfd) \ - $(foreach dir,$(subst :, ,${PATH}),$(wildcard ${dir}/${LINKER}.bfd))),) -LINKER := ${LINKER}.bfd endif ################################################################################ @@ -162,45 +139,20 @@ endif #(ARM_ARCH_MAJOR) ################################################################################ arch-features = ${ARM_ARCH_FEATURE} -# Set the compiler's architecture feature modifiers -ifneq ($(arch-features), none) - # Strip "none+" from arch-features - arch-features := $(subst none+,,$(arch-features)) - march-directive := $(march-directive)+$(arch-features) -# Print features - $(info Arm Architecture Features specified: $(subst +, ,$(arch-features))) -endif #(arch-features) - -ifneq ($(findstring clang,$(notdir $(CC))),) - ifneq ($(findstring armclang,$(notdir $(CC))),) +ifneq ($(filter %-clang,$($(ARCH)-cc-id)),) + ifeq ($($(ARCH)-cc-id),arm-clang) TF_CFLAGS_aarch32 := -target arm-arm-none-eabi TF_CFLAGS_aarch64 := -target aarch64-arm-none-eabi - LD := $(LINKER) else TF_CFLAGS_aarch32 = $(target32-directive) TF_CFLAGS_aarch64 := -target aarch64-elf - LD := $(shell $(CC) --print-prog-name ld.lld) - - AR := $(shell $(CC) --print-prog-name llvm-ar) - OD := $(shell $(CC) --print-prog-name llvm-objdump) - OC := $(shell $(CC) --print-prog-name llvm-objcopy) endif - CPP := $(CC) -E $(TF_CFLAGS_$(ARCH)) - PP := $(CC) -E $(TF_CFLAGS_$(ARCH)) - AS := $(CC) -c -x assembler-with-cpp $(TF_CFLAGS_$(ARCH)) -else ifneq ($(findstring gcc,$(notdir $(CC))),) - ifeq ($(ENABLE_LTO),1) - # Enable LTO only for aarch64 - ifeq (${ARCH},aarch64) - LTO_CFLAGS = -flto - # Use gcc as a wrapper for the ld, recommended for LTO - LINKER := ${CROSS_COMPILE}gcc - endif +else ifeq ($($(ARCH)-cc-id),gnu-gcc) + # Enable LTO only for aarch64 + ifeq (${ARCH},aarch64) + LTO_CFLAGS = $(if $(filter-out 0,$(ENABLE_LTO)),-flto) endif - LD = $(LINKER) -else - LD = $(LINKER) endif #(clang) # Process Debug flag @@ -235,8 +187,6 @@ endif #(AARCH32_INSTRUCTION_SET) TF_CFLAGS_aarch32 += -mno-unaligned-access TF_CFLAGS_aarch64 += -mgeneral-regs-only -mstrict-align -ASFLAGS += $(march-directive) - ############################################################################## # WARNINGS Configuration ############################################################################### @@ -299,14 +249,20 @@ else ifeq (${W},3) endif #(W) # Compiler specific warnings -ifeq ($(findstring clang,$(notdir $(CC))),) +ifeq ($(filter %-clang,$($(ARCH)-cc-id)),) # not using clang WARNINGS += -Wunused-but-set-variable -Wmaybe-uninitialized \ -Wpacked-bitfield-compat -Wshift-overflow=2 \ -Wlogical-op # https://gcc.gnu.org/bugzilla/show_bug.cgi?id=105523 -TF_CFLAGS += $(call cc_option, --param=min-pagesize=0) +TF_CFLAGS_MIN_PAGE_SIZE := $(call cc_option, --param=min-pagesize=0) +TF_CFLAGS += $(TF_CFLAGS_MIN_PAGE_SIZE) + +ifeq ($(HARDEN_SLS), 1) + TF_CFLAGS_MHARDEN_SLS := $(call cc_option, -mharden-sls=all) + TF_CFLAGS_aarch64 += $(TF_CFLAGS_MHARDEN_SLS) +endif else # using clang @@ -339,28 +295,31 @@ ifeq (${SANITIZE_UB},trap) -fsanitize-undefined-trap-on-error endif #(${SANITIZE_UB},trap) -GCC_V_OUTPUT := $(shell $(CC) -v 2>&1) +GCC_V_OUTPUT := $(if $($(ARCH)-cc),$(shell $($(ARCH)-cc) -v 2>&1)) TF_LDFLAGS += -z noexecstack # LD = armlink -ifneq ($(findstring armlink,$(notdir $(LD))),) +ifeq ($($(ARCH)-ld-id),arm-link) TF_LDFLAGS += --diag_error=warning --lto_level=O1 TF_LDFLAGS += --remove --info=unused,unusedsymbols TF_LDFLAGS += $(TF_LDFLAGS_$(ARCH)) # LD = gcc (used when GCC LTO is enabled) -else ifneq ($(findstring gcc,$(notdir $(LD))),) +else ifeq ($($(ARCH)-ld-id),gnu-gcc) # Pass ld options with Wl or Xlinker switches + TF_LDFLAGS += $(call ld_option,-Xlinker --no-warn-rwx-segments) TF_LDFLAGS += -Wl,--fatal-warnings -O1 TF_LDFLAGS += -Wl,--gc-sections TF_LDFLAGS += -Wl,-z,common-page-size=4096 #Configure page size constants TF_LDFLAGS += -Wl,-z,max-page-size=4096 + TF_LDFLAGS += -Wl,--build-id=none ifeq ($(ENABLE_LTO),1) ifeq (${ARCH},aarch64) TF_LDFLAGS += -flto -fuse-linker-plugin + TF_LDFLAGS += -flto-partition=one endif endif #(ENABLE_LTO) @@ -384,12 +343,13 @@ else TF_LDFLAGS += -z common-page-size=4096 # Configure page size constants TF_LDFLAGS += -z max-page-size=4096 + TF_LDFLAGS += --build-id=none # ld.lld doesn't recognize the errata flags, # therefore don't add those in that case. # ld.lld reports section type mismatch warnings, # therefore don't add --fatal-warnings to it. - ifeq ($(findstring ld.lld,$(notdir $(LD))),) + ifneq ($($(ARCH)-ld-id),llvm-lld) TF_LDFLAGS += $(TF_LDFLAGS_$(ARCH)) --fatal-warnings endif @@ -399,8 +359,8 @@ endif #(LD = armlink) # Setup ARCH_MAJOR/MINOR before parsing arch_features. ################################################################################ ifeq (${ENABLE_RME},1) - ARM_ARCH_MAJOR := 8 - ARM_ARCH_MINOR := 6 + ARM_ARCH_MAJOR := 9 + ARM_ARCH_MINOR := 2 endif ################################################################################ @@ -408,6 +368,15 @@ endif ################################################################################ include lib/compiler-rt/compiler-rt.mk +# Allow overriding the timestamp, for example for reproducible builds, or to +# synchronize timestamps across multiple projects. +# This must be set to a C string (including quotes where applicable). +BUILD_MESSAGE_TIMESTAMP ?= __TIME__", "__DATE__ + +DEFINES += -DBUILD_MESSAGE_TIMESTAMP='$(BUILD_MESSAGE_TIMESTAMP)' +DEFINES += -DBUILD_MESSAGE_VERSION_STRING='"$(VERSION_STRING)"' +DEFINES += -DBUILD_MESSAGE_VERSION='"$(VERSION)"' + BL_COMMON_SOURCES += common/bl_common.c \ common/tf_log.c \ common/${ARCH}/debug.S \ @@ -421,7 +390,7 @@ BL_COMMON_SOURCES += common/bl_common.c \ plat/common/${ARCH}/platform_helpers.S \ ${COMPILER_RT_SRCS} -ifeq ($(notdir $(CC)),armclang) +ifeq ($($(ARCH)-cc-id),arm-clang) BL_COMMON_SOURCES += lib/${ARCH}/armclang_printf.S endif @@ -445,7 +414,6 @@ include common/backtrace/backtrace.mk ################################################################################ # Generic definitions ################################################################################ -include ${MAKE_HELPERS_DIRECTORY}plat_helpers.mk ifeq (${BUILD_BASE},) BUILD_BASE := ./build @@ -481,14 +449,21 @@ ifneq (${SPD},none) ifeq ($(SPMC_AT_EL3),1) $(error SPM cannot be enabled in both S-EL2 and EL3.) endif + ifeq ($(CTX_INCLUDE_SVE_REGS),1) + $(error SVE context management not needed with Hafnium SPMC.) + endif endif ifeq ($(findstring optee_sp,$(ARM_SPMC_MANIFEST_DTS)),optee_sp) DTC_CPPFLAGS += -DOPTEE_SP_FW_CONFIG endif + ifeq ($(findstring trusty_sp,$(ARM_SPMC_MANIFEST_DTS)),trusty_sp) + DTC_CPPFLAGS += -DTRUSTY_SP_FW_CONFIG + endif + ifeq ($(TS_SP_FW_CONFIG),1) - DTC_CPPFLAGS += -DTS_SP_FW_CONFIG + DTC_CPPFLAGS += -DTS_SP_FW_CONFIG endif ifneq ($(ARM_BL2_SP_LIST_DTS),) @@ -602,14 +577,14 @@ include ${MAKE_HELPERS_DIRECTORY}arch_features.mk ifeq (${SUPPORT_STACK_MEMTAG},yes) ifdef mem_tag_arch_support # Check for armclang and clang compilers - ifneq ( ,$(filter $(notdir $(CC)),armclang clang)) + ifneq ($(filter %-clang,$($(ARCH)-cc-id)),) # Add "memtag" architecture feature modifier if not specified ifeq ( ,$(findstring memtag,$(arch-features))) arch-features := $(arch-features)+memtag endif # memtag - ifeq ($(notdir $(CC)),armclang) + ifeq ($($(ARCH)-cc-id),arm-clang) TF_CFLAGS += -mmemtag-stack - else ifeq ($(notdir $(CC)),clang) + else ifeq ($($(ARCH)-cc-id),llvm-clang) TF_CFLAGS += -fsanitize=memtag endif # armclang endif @@ -624,19 +599,11 @@ endif #(SUPPORT_STACK_MEMTAG) ################################################################################ # FEAT_RME ifeq (${ENABLE_RME},1) - # RME doesn't support BRBE - ENABLE_BRBE_FOR_NS := 0 - # RME doesn't support PIE ifneq (${ENABLE_PIE},0) $(error ENABLE_RME does not support PIE) endif - # RME doesn't support BRBE - ifneq (${ENABLE_BRBE_FOR_NS},0) - $(error ENABLE_RME does not support BRBE.) - endif - # RME requires AARCH64 ifneq (${ARCH},aarch64) $(error ENABLE_RME requires AArch64) @@ -680,6 +647,13 @@ ifeq (${CTX_INCLUDE_EL2_REGS}, 1) endif endif +################################################################################ +# Make 128-Bit sysreg read/writes availabe when FEAT_D128 is enabled. +################################################################################ +ifneq (${ENABLE_FEAT_D128}, 0) + BL_COMMON_SOURCES += lib/extensions/sysreg128/sysreg128.S +endif + ################################################################################ # Platform specific Makefile might provide us ARCH_MAJOR/MINOR use that to come # up with appropriate march values for compiler. @@ -687,6 +661,7 @@ endif include ${MAKE_HELPERS_DIRECTORY}march.mk TF_CFLAGS += $(march-directive) +ASFLAGS += $(march-directive) # This internal flag is common option which is set to 1 for scenarios # when the BL2 is running in EL3 level. This occurs in two scenarios - @@ -713,8 +688,6 @@ else FFH_SUPPORT := 0 endif -$(eval $(call MAKE_PREREQ_DIR,${BUILD_PLAT})) - ifeq (${ARM_ARCH_MAJOR},7) include make_helpers/armv7-a-cpus.mk endif @@ -722,12 +695,12 @@ endif PIE_FOUND := $(findstring --enable-default-pie,${GCC_V_OUTPUT}) ifneq ($(PIE_FOUND),) TF_CFLAGS += -fno-PIE -ifneq ($(findstring gcc,$(notdir $(LD))),) +ifeq ($($(ARCH)-ld-id),gnu-gcc) TF_LDFLAGS += -no-pie endif endif #(PIE_FOUND) -ifneq ($(findstring gcc,$(notdir $(LD))),) +ifeq ($($(ARCH)-ld-id),gnu-gcc) PIE_LDFLAGS += -Wl,-pie -Wl,--no-dynamic-linker else PIE_LDFLAGS += -pie --no-dynamic-linker @@ -929,16 +902,6 @@ ifeq ($(CTX_INCLUDE_PAUTH_REGS),1) endif endif #(CTX_INCLUDE_PAUTH_REGS) -ifeq ($(CTX_INCLUDE_MTE_REGS),1) - ifneq (${ARCH},aarch64) - $(error CTX_INCLUDE_MTE_REGS requires AArch64) - endif -endif #(CTX_INCLUDE_MTE_REGS) - -ifeq ($(PSA_FWU_SUPPORT),1) - $(info PSA_FWU_SUPPORT is an experimental feature) -endif #(PSA_FWU_SUPPORT) - ifeq ($(FEATURE_DETECTION),1) $(info FEATURE_DETECTION is an experimental feature) endif #(FEATURE_DETECTION) @@ -1006,25 +969,52 @@ ifeq (${ENABLE_SME_FOR_SWD},1) endif endif #(ENABLE_SME_FOR_SWD) +# Enabling SVE for SWD requires enabling SVE for NWD due to ENABLE_FEAT +# mechanism. ifeq (${ENABLE_SVE_FOR_SWD},1) - ifeq (${ENABLE_SVE_FOR_NS},0) - $(error "ENABLE_SVE_FOR_SWD requires ENABLE_SVE_FOR_NS") - endif -endif #(ENABLE_SVE_FOR_SWD) + ifeq (${ENABLE_SVE_FOR_NS},0) + $(error "ENABLE_SVE_FOR_SWD requires ENABLE_SVE_FOR_NS") + endif +endif -# SVE and SME cannot be used with CTX_INCLUDE_FPREGS since secure manager does -# its own context management including FPU registers. -ifeq (${CTX_INCLUDE_FPREGS},1) - ifneq (${ENABLE_SME_FOR_NS},0) - $(error "ENABLE_SME_FOR_NS cannot be used with CTX_INCLUDE_FPREGS") - endif +# Enabling SVE for both the worlds typically requires the context +# management of SVE registers. The only exception being SPMC at S-EL2. +ifeq (${ENABLE_SVE_FOR_SWD}, 1) + ifneq (${ENABLE_SVE_FOR_NS}, 0) + ifeq (${CTX_INCLUDE_SVE_REGS}-$(SPMD_SPM_AT_SEL2),0-0) + $(warning "ENABLE_SVE_FOR_SWD and ENABLE_SVE_FOR_NS together require CTX_INCLUDE_SVE_REGS") + endif + endif +endif - ifeq (${ENABLE_SVE_FOR_NS},1) - # Warning instead of error due to CI dependency on this - $(warning "ENABLE_SVE_FOR_NS cannot be used with CTX_INCLUDE_FPREGS") - $(warning "Forced ENABLE_SVE_FOR_NS=0") - override ENABLE_SVE_FOR_NS := 0 - endif +# Enabling SVE in either world while enabling CTX_INCLUDE_FPREGS requires +# CTX_INCLUDE_SVE_REGS to be enabled due to architectural dependency between FP +# and SVE registers. +ifeq (${CTX_INCLUDE_FPREGS}, 1) + ifneq (${ENABLE_SVE_FOR_NS},0) + ifeq (${CTX_INCLUDE_SVE_REGS},0) + # Warning instead of error due to CI dependency on this + $(warning "CTX_INCLUDE_FPREGS and ENABLE_SVE_FOR_NS together require CTX_INCLUDE_SVE_REGS") + $(warning "Forced ENABLE_SVE_FOR_NS=0") + override ENABLE_SVE_FOR_NS := 0 + endif + endif +endif #(CTX_INCLUDE_FPREGS) + +# SVE context management is only required if secure world has access to SVE/FP +# functionality. +ifeq (${CTX_INCLUDE_SVE_REGS},1) + ifeq (${ENABLE_SVE_FOR_SWD},0) + $(error "CTX_INCLUDE_SVE_REGS requires ENABLE_SVE_FOR_SWD to also be enabled") + endif +endif + +# SME cannot be used with CTX_INCLUDE_FPREGS since SPM does its own context +# management including FPU registers. +ifeq (${CTX_INCLUDE_FPREGS},1) + ifneq (${ENABLE_SME_FOR_NS},0) + $(error "ENABLE_SME_FOR_NS cannot be used with CTX_INCLUDE_FPREGS") + endif endif #(CTX_INCLUDE_FPREGS) ifeq ($(DRTM_SUPPORT),1) @@ -1041,16 +1031,14 @@ ifeq (${ENABLE_RME},1) endif endif -# Determine if FEAT_RNG is supported -ENABLE_FEAT_RNG = $(if $(findstring rng,${arch-features}),1,0) - -# Determine if FEAT_SB is supported -ENABLE_FEAT_SB = $(if $(findstring sb,${arch-features}),1,0) - ifeq ($(PSA_CRYPTO),1) $(info PSA_CRYPTO is an experimental feature) endif +ifeq ($(DICE_PROTECTION_ENVIRONMENT),1) + $(info DICE_PROTECTION_ENVIRONMENT is an experimental feature) +endif + ################################################################################ # Process platform overrideable behaviour ################################################################################ @@ -1163,7 +1151,9 @@ $(eval $(call assert_booleans,\ CREATE_KEYS \ CTX_INCLUDE_AARCH32_REGS \ CTX_INCLUDE_FPREGS \ + CTX_INCLUDE_SVE_REGS \ CTX_INCLUDE_EL2_REGS \ + CTX_INCLUDE_MPAM_REGS \ DEBUG \ DYN_DISABLE_AUTH \ EL3_EXCEPTION_HANDLING \ @@ -1171,7 +1161,6 @@ $(eval $(call assert_booleans,\ ENABLE_AMU_FCONF \ AMU_RESTRICT_COUNTERS \ ENABLE_ASSERTIONS \ - ENABLE_FEAT_SB \ ENABLE_PIE \ ENABLE_PMF \ ENABLE_PSCI_STAT \ @@ -1185,13 +1174,15 @@ $(eval $(call assert_booleans,\ GENERATE_COT \ GICV2_G0_FOR_EL3 \ HANDLE_EA_EL3_FIRST_NS \ + HARDEN_SLS \ HW_ASSISTED_COHERENCY \ MEASURED_BOOT \ + DICE_PROTECTION_ENVIRONMENT \ + RMMD_ENABLE_EL3_TOKEN_SIGN \ DRTM_SUPPORT \ NS_TIMER_SWITCH \ OVERRIDE_LIBC \ PL011_GENERIC_UART \ - PLAT_RSS_NOT_SUPPORTED \ PROGRAMMABLE_RESET_ADDRESS \ PSCI_EXTENDED_STATE_ID \ PSCI_OS_INIT_MODE \ @@ -1200,6 +1191,8 @@ $(eval $(call assert_booleans,\ SEPARATE_CODE_AND_RODATA \ SEPARATE_BL2_NOLOAD_REGION \ SEPARATE_NOBITS_REGION \ + SEPARATE_RWDATA_REGION \ + SEPARATE_SIMD_SECTION \ SPIN_ON_BL1_EXIT \ SPM_MM \ SPMC_AT_EL3 \ @@ -1227,6 +1220,7 @@ $(eval $(call assert_booleans,\ COT_DESC_IN_DTB \ USE_SP804_TIMER \ PSA_FWU_SUPPORT \ + PSA_FWU_METADATA_FW_STORE_DESC \ ENABLE_MPMM \ ENABLE_MPMM_FCONF \ FEATURE_DETECTION \ @@ -1237,6 +1231,9 @@ $(eval $(call assert_booleans,\ PSA_CRYPTO \ ENABLE_CONSOLE_GETC \ INIT_UNUSED_NS_EL2 \ + PLATFORM_REPORT_CTX_MEM_USE \ + EARLY_CONSOLE \ + PRESERVE_DSU_PMU_REGS \ ))) # Numeric_Flags @@ -1246,7 +1243,6 @@ $(eval $(call assert_numerics,\ ARM_ARCH_MINOR \ BRANCH_PROTECTION \ CTX_INCLUDE_PAUTH_REGS \ - CTX_INCLUDE_MTE_REGS \ CTX_INCLUDE_NEVE_REGS \ CRYPTO_SUPPORT \ DISABLE_MTPMU \ @@ -1257,22 +1253,30 @@ $(eval $(call assert_numerics,\ ENABLE_FEAT_AMU \ ENABLE_FEAT_AMUv1p1 \ ENABLE_FEAT_CSV2_2 \ + ENABLE_FEAT_CSV2_3 \ + ENABLE_FEAT_DEBUGV8P9 \ ENABLE_FEAT_DIT \ ENABLE_FEAT_ECV \ ENABLE_FEAT_FGT \ + ENABLE_FEAT_FGT2 \ ENABLE_FEAT_HCX \ + ENABLE_FEAT_LS64_ACCDATA \ + ENABLE_FEAT_MTE2 \ ENABLE_FEAT_PAN \ ENABLE_FEAT_RNG \ ENABLE_FEAT_RNG_TRAP \ ENABLE_FEAT_SEL2 \ ENABLE_FEAT_TCR2 \ + ENABLE_FEAT_THE \ + ENABLE_FEAT_SB \ ENABLE_FEAT_S2PIE \ ENABLE_FEAT_S1PIE \ ENABLE_FEAT_S2POE \ ENABLE_FEAT_S1POE \ + ENABLE_FEAT_SCTLR2 \ + ENABLE_FEAT_D128 \ ENABLE_FEAT_GCS \ ENABLE_FEAT_VHE \ - ENABLE_FEAT_MTE_PERM \ ENABLE_FEAT_MPAM \ ENABLE_RME \ ENABLE_SPE_FOR_NS \ @@ -1313,9 +1317,10 @@ $(eval $(call add_defines,\ COLD_BOOT_SINGLE_CPU \ CTX_INCLUDE_AARCH32_REGS \ CTX_INCLUDE_FPREGS \ + CTX_INCLUDE_SVE_REGS \ CTX_INCLUDE_PAUTH_REGS \ + CTX_INCLUDE_MPAM_REGS \ EL3_EXCEPTION_HANDLING \ - CTX_INCLUDE_MTE_REGS \ CTX_INCLUDE_EL2_REGS \ CTX_INCLUDE_NEVE_REGS \ DECRYPTION_SUPPORT_${DECRYPTION_SUPPORT} \ @@ -1326,12 +1331,14 @@ $(eval $(call add_defines,\ AMU_RESTRICT_COUNTERS \ ENABLE_ASSERTIONS \ ENABLE_BTI \ + ENABLE_FEAT_DEBUGV8P9 \ ENABLE_FEAT_MPAM \ ENABLE_PAUTH \ ENABLE_PIE \ ENABLE_PMF \ ENABLE_PSCI_STAT \ ENABLE_RME \ + RMMD_ENABLE_EL3_TOKEN_SIGN \ ENABLE_RUNTIME_INSTRUMENTATION \ ENABLE_SME_FOR_NS \ ENABLE_SME2_FOR_NS \ @@ -1350,18 +1357,22 @@ $(eval $(call add_defines,\ HW_ASSISTED_COHERENCY \ LOG_LEVEL \ MEASURED_BOOT \ + DICE_PROTECTION_ENVIRONMENT \ DRTM_SUPPORT \ NS_TIMER_SWITCH \ PL011_GENERIC_UART \ PLAT_${PLAT} \ - PLAT_RSS_NOT_SUPPORTED \ PROGRAMMABLE_RESET_ADDRESS \ PSCI_EXTENDED_STATE_ID \ PSCI_OS_INIT_MODE \ RESET_TO_BL31 \ + RME_GPT_BITLOCK_BLOCK \ + RME_GPT_MAX_BLOCK \ SEPARATE_CODE_AND_RODATA \ SEPARATE_BL2_NOLOAD_REGION \ SEPARATE_NOBITS_REGION \ + SEPARATE_RWDATA_REGION \ + SEPARATE_SIMD_SECTION \ RECLAIM_INIT_CODE \ SPD_${SPD} \ SPIN_ON_BL1_EXIT \ @@ -1399,6 +1410,7 @@ $(eval $(call add_defines,\ NR_OF_FW_BANKS \ NR_OF_IMAGES_IN_FW_BANK \ PSA_FWU_SUPPORT \ + PSA_FWU_METADATA_FW_STORE_DESC \ ENABLE_BRBE_FOR_NS \ ENABLE_TRBE_FOR_NS \ ENABLE_SYS_REG_TRACE_FOR_NS \ @@ -1407,19 +1419,25 @@ $(eval $(call add_defines,\ ENABLE_MPMM \ ENABLE_MPMM_FCONF \ ENABLE_FEAT_FGT \ + ENABLE_FEAT_FGT2 \ ENABLE_FEAT_ECV \ ENABLE_FEAT_AMUv1p1 \ ENABLE_FEAT_SEL2 \ ENABLE_FEAT_VHE \ ENABLE_FEAT_CSV2_2 \ + ENABLE_FEAT_CSV2_3 \ + ENABLE_FEAT_LS64_ACCDATA \ ENABLE_FEAT_PAN \ ENABLE_FEAT_TCR2 \ + ENABLE_FEAT_THE \ ENABLE_FEAT_S2PIE \ ENABLE_FEAT_S1PIE \ ENABLE_FEAT_S2POE \ ENABLE_FEAT_S1POE \ + ENABLE_FEAT_SCTLR2 \ + ENABLE_FEAT_D128 \ ENABLE_FEAT_GCS \ - ENABLE_FEAT_MTE_PERM \ + ENABLE_FEAT_MTE2 \ FEATURE_DETECTION \ TWED_DELAY \ ENABLE_FEAT_TWED \ @@ -1430,8 +1448,18 @@ $(eval $(call add_defines,\ PSA_CRYPTO \ ENABLE_CONSOLE_GETC \ INIT_UNUSED_NS_EL2 \ + PLATFORM_REPORT_CTX_MEM_USE \ + EARLY_CONSOLE \ + PRESERVE_DSU_PMU_REGS \ ))) +ifeq (${PLATFORM_REPORT_CTX_MEM_USE}, 1) +ifeq (${DEBUG}, 0) + $(warning "PLATFORM_REPORT_CTX_MEM_USE can be applied when DEBUG=1 only") + override PLATFORM_REPORT_CTX_MEM_USE := 0 +endif +endif + ifeq (${SANITIZE_UB},trap) $(eval $(call add_define,MONITOR_TRAPS)) endif #(SANITIZE_UB) @@ -1452,7 +1480,7 @@ ifeq (${DYN_DISABLE_AUTH},1) $(eval $(call add_define,DYN_DISABLE_AUTH)) endif -ifneq ($(findstring armlink,$(notdir $(LD))),) +ifeq ($($(ARCH)-ld-id),arm-link) $(eval $(call add_define,USE_ARM_LINK)) endif @@ -1474,24 +1502,23 @@ endif #(SPD) # Build targets ################################################################################ -.PHONY: all msg_start clean realclean distclean cscope locate-checkpatch checkcodebase checkpatch fiptool sptool fip sp fwu_fip certtool dtbs memmap doc enctool +.PHONY: all msg_start clean realclean distclean cscope locate-checkpatch checkcodebase checkpatch fiptool sptool fip sp tl fwu_fip certtool dtbs memmap doc enctool .SUFFIXES: all: msg_start msg_start: - @echo "Building ${PLAT}" + $(s)echo "Building ${PLAT}" ifeq (${ERROR_DEPRECATED},0) # Check if deprecated declarations and cpp warnings should be treated as error or not. -ifneq ($(findstring clang,$(notdir $(CC))),) +ifneq ($(filter %-clang,$($(ARCH)-cc-id)),) CPPFLAGS += -Wno-error=deprecated-declarations else CPPFLAGS += -Wno-error=deprecated-declarations -Wno-error=cpp endif endif #(!ERROR_DEPRECATED) -$(eval $(call MAKE_LIB_DIRS)) $(eval $(call MAKE_LIB,c)) # Expand build macros for the different images @@ -1575,12 +1602,12 @@ endif #(NEED_FDT) # Add Secure Partition packages ifeq (${NEED_SP_PKG},yes) -$(BUILD_PLAT)/sp_gen.mk : ${SP_MK_GEN} ${SP_LAYOUT_FILE} | ${BUILD_PLAT} - ${PYTHON} "$<" "$@" $(filter-out $<,$^) $(BUILD_PLAT) ${COT} ${SP_DTS_LIST_FRAGMENT} +$(BUILD_PLAT)/sp_gen.mk: ${SP_MK_GEN} ${SP_LAYOUT_FILE} | $$(@D)/ + $(q)${PYTHON} "$<" "$@" $(filter-out $<,$^) $(BUILD_PLAT) ${COT} ${SP_DTS_LIST_FRAGMENT} sp: $(DTBS) $(BUILD_PLAT)/sp_gen.mk $(SP_PKGS) - @${ECHO_BLANK_LINE} - @echo "Built SP Images successfully" - @${ECHO_BLANK_LINE} + $(s)echo + $(s)echo "Built SP Images successfully" + $(s)echo endif #(NEED_SP_PKG) locate-checkpatch: @@ -1593,37 +1620,37 @@ endif endif #(CHECKPATCH) clean: - @echo " CLEAN" + $(s)echo " CLEAN" $(call SHELL_REMOVE_DIR,${BUILD_PLAT}) ifdef UNIX_MK - ${Q}${MAKE} --no-print-directory -C ${FIPTOOLPATH} clean + $(q)${MAKE} --no-print-directory -C ${FIPTOOLPATH} clean else # Clear the MAKEFLAGS as we do not want # to pass the gnumake flags to nmake. - ${Q}set MAKEFLAGS= && ${MSVC_NMAKE} /nologo /f ${FIPTOOLPATH}/Makefile.msvc FIPTOOLPATH=$(subst /,\,$(FIPTOOLPATH)) FIPTOOL=$(subst /,\,$(FIPTOOL)) clean + $(q)set MAKEFLAGS= && ${MSVC_NMAKE} /nologo /f ${FIPTOOLPATH}/Makefile.msvc FIPTOOLPATH=$(subst /,\,$(FIPTOOLPATH)) FIPTOOL=$(subst /,\,$(FIPTOOL)) clean endif #(UNIX_MK) - ${Q}${MAKE} PLAT=${PLAT} --no-print-directory -C ${CRTTOOLPATH} clean - ${Q}${MAKE} PLAT=${PLAT} --no-print-directory -C ${ENCTOOLPATH} clean - ${Q}${MAKE} --no-print-directory -C ${ROMLIBPATH} clean + $(q)${MAKE} PLAT=${PLAT} --no-print-directory -C ${CRTTOOLPATH} clean + $(q)${MAKE} PLAT=${PLAT} --no-print-directory -C ${ENCTOOLPATH} clean + $(q)${MAKE} --no-print-directory -C ${ROMLIBPATH} clean realclean distclean: - @echo " REALCLEAN" + $(s)echo " REALCLEAN" $(call SHELL_REMOVE_DIR,${BUILD_BASE}) $(call SHELL_DELETE_ALL, ${CURDIR}/cscope.*) ifdef UNIX_MK - ${Q}${MAKE} --no-print-directory -C ${FIPTOOLPATH} clean + $(q)${MAKE} --no-print-directory -C ${FIPTOOLPATH} clean else # Clear the MAKEFLAGS as we do not want # to pass the gnumake flags to nmake. - ${Q}set MAKEFLAGS= && ${MSVC_NMAKE} /nologo /f ${FIPTOOLPATH}/Makefile.msvc FIPTOOLPATH=$(subst /,\,$(FIPTOOLPATH)) FIPTOOL=$(subst /,\,$(FIPTOOL)) realclean + $(q)set MAKEFLAGS= && ${MSVC_NMAKE} /nologo /f ${FIPTOOLPATH}/Makefile.msvc FIPTOOLPATH=$(subst /,\,$(FIPTOOLPATH)) FIPTOOL=$(subst /,\,$(FIPTOOL)) realclean endif #(UNIX_MK) - ${Q}${MAKE} PLAT=${PLAT} --no-print-directory -C ${CRTTOOLPATH} realclean - ${Q}${MAKE} PLAT=${PLAT} --no-print-directory -C ${ENCTOOLPATH} realclean - ${Q}${MAKE} --no-print-directory -C ${ROMLIBPATH} clean + $(q)${MAKE} PLAT=${PLAT} --no-print-directory -C ${CRTTOOLPATH} realclean + $(q)${MAKE} PLAT=${PLAT} --no-print-directory -C ${ENCTOOLPATH} realclean + $(q)${MAKE} --no-print-directory -C ${ROMLIBPATH} clean checkcodebase: locate-checkpatch - @echo " CHECKING STYLE" - @if test -d .git ; then \ + $(s)echo " CHECKING STYLE" + $(q)if test -d .git ; then \ git ls-files | grep -E -v 'libfdt|libc|docs|\.rst' | \ while read GIT_FILE ; \ do ${CHECKPATCH} ${CHECKCODE_ARGS} -f $$GIT_FILE ; \ @@ -1639,63 +1666,62 @@ checkcodebase: locate-checkpatch fi checkpatch: locate-checkpatch - @echo " CHECKING STYLE" - @if test -n "${CHECKPATCH_OPTS}"; then \ + $(s)echo " CHECKING STYLE" + $(q)if test -n "${CHECKPATCH_OPTS}"; then \ echo " with ${CHECKPATCH_OPTS} option(s)"; \ fi - ${Q}COMMON_COMMIT=$$(git merge-base HEAD ${BASE_COMMIT}); \ + $(q)COMMON_COMMIT=$$(git merge-base HEAD ${BASE_COMMIT}); \ for commit in `git rev-list --no-merges $$COMMON_COMMIT..HEAD`; \ do \ printf "\n[*] Checking style of '$$commit'\n\n"; \ - git log --format=email "$$commit~..$$commit" \ - -- ${CHECK_PATHS} | \ - ${CHECKPATCH} ${CHECKPATCH_OPTS} - || true; \ - git diff --format=email "$$commit~..$$commit" \ - -- ${CHECK_PATHS} | \ + ( git log --format=email "$$commit~..$$commit" \ + -- ${CHECK_PATHS} ; \ + git diff --format=email "$$commit~..$$commit" \ + -- ${CHECK_PATHS}; ) | \ ${CHECKPATCH} ${CHECKPATCH_OPTS} - || true; \ done certtool: ${CRTTOOL} ${CRTTOOL}: FORCE - ${Q}${MAKE} PLAT=${PLAT} USE_TBBR_DEFS=${USE_TBBR_DEFS} COT=${COT} OPENSSL_DIR=${OPENSSL_DIR} CRTTOOL=${CRTTOOL} DEBUG=${DEBUG} V=${V} --no-print-directory -C ${CRTTOOLPATH} all - @${ECHO_BLANK_LINE} - @echo "Built $@ successfully" - @${ECHO_BLANK_LINE} + $(q)${MAKE} PLAT=${PLAT} USE_TBBR_DEFS=${USE_TBBR_DEFS} COT=${COT} OPENSSL_DIR=${OPENSSL_DIR} CRTTOOL=${CRTTOOL} DEBUG=${DEBUG} --no-print-directory -C ${CRTTOOLPATH} all + $(s)echo + $(s)echo "Built $@ successfully" + $(s)echo ifneq (${GENERATE_COT},0) certificates: ${CRT_DEPS} ${CRTTOOL} - ${Q}${CRTTOOL} ${CRT_ARGS} - @${ECHO_BLANK_LINE} - @echo "Built $@ successfully" - @echo "Certificates can be found in ${BUILD_PLAT}" - @${ECHO_BLANK_LINE} + $(q)${CRTTOOL} ${CRT_ARGS} + $(s)echo + $(s)echo "Built $@ successfully" + $(s)echo "Certificates can be found in ${BUILD_PLAT}" + $(s)echo endif #(GENERATE_COT) ${BUILD_PLAT}/${FIP_NAME}: ${FIP_DEPS} ${FIPTOOL} $(eval ${CHECK_FIP_CMD}) - ${Q}${FIPTOOL} create ${FIP_ARGS} $@ - ${Q}${FIPTOOL} info $@ - @${ECHO_BLANK_LINE} - @echo "Built $@ successfully" - @${ECHO_BLANK_LINE} + $(q)${FIPTOOL} create ${FIP_ARGS} $@ + $(q)${FIPTOOL} info $@ + $(s)echo + $(s)echo "Built $@ successfully" + $(s)echo ifneq (${GENERATE_COT},0) fwu_certificates: ${FWU_CRT_DEPS} ${CRTTOOL} - ${Q}${CRTTOOL} ${FWU_CRT_ARGS} - @${ECHO_BLANK_LINE} - @echo "Built $@ successfully" - @echo "FWU certificates can be found in ${BUILD_PLAT}" - @${ECHO_BLANK_LINE} + $(q)${CRTTOOL} ${FWU_CRT_ARGS} + $(s)echo + $(s)echo "Built $@ successfully" + $(s)echo "FWU certificates can be found in ${BUILD_PLAT}" + $(s)echo endif #(GENERATE_COT) ${BUILD_PLAT}/${FWU_FIP_NAME}: ${FWU_FIP_DEPS} ${FIPTOOL} $(eval ${CHECK_FWU_FIP_CMD}) - ${Q}${FIPTOOL} create ${FWU_FIP_ARGS} $@ - ${Q}${FIPTOOL} info $@ - @${ECHO_BLANK_LINE} - @echo "Built $@ successfully" - @${ECHO_BLANK_LINE} + $(q)${FIPTOOL} create ${FWU_FIP_ARGS} $@ + $(q)${FIPTOOL} info $@ + $(s)echo + $(s)echo "Built $@ successfully" + $(s)echo fiptool: ${FIPTOOL} fip: ${BUILD_PLAT}/${FIP_NAME} @@ -1703,85 +1729,90 @@ fwu_fip: ${BUILD_PLAT}/${FWU_FIP_NAME} ${FIPTOOL}: FORCE ifdef UNIX_MK - ${Q}${MAKE} CPPFLAGS="-DVERSION='\"${VERSION_STRING}\"'" FIPTOOL=${FIPTOOL} OPENSSL_DIR=${OPENSSL_DIR} DEBUG=${DEBUG} V=${V} --no-print-directory -C ${FIPTOOLPATH} all + $(q)${MAKE} PLAT=${PLAT} CPPFLAGS="-DVERSION='\"${VERSION_STRING}\"'" FIPTOOL=${FIPTOOL} OPENSSL_DIR=${OPENSSL_DIR} DEBUG=${DEBUG} --no-print-directory -C ${FIPTOOLPATH} all else # Clear the MAKEFLAGS as we do not want # to pass the gnumake flags to nmake. - ${Q}set MAKEFLAGS= && ${MSVC_NMAKE} /nologo /f ${FIPTOOLPATH}/Makefile.msvc FIPTOOLPATH=$(subst /,\,$(FIPTOOLPATH)) FIPTOOL=$(subst /,\,$(FIPTOOL)) + $(q)set MAKEFLAGS= && ${MSVC_NMAKE} /nologo /f ${FIPTOOLPATH}/Makefile.msvc FIPTOOLPATH=$(subst /,\,$(FIPTOOLPATH)) FIPTOOL=$(subst /,\,$(FIPTOOL)) endif #(UNIX_MK) romlib.bin: libraries FORCE - ${Q}${MAKE} PLAT_DIR=${PLAT_DIR} BUILD_PLAT=${BUILD_PLAT} ENABLE_BTI=${ENABLE_BTI} ARM_ARCH_MINOR=${ARM_ARCH_MINOR} INCLUDES='${INCLUDES}' DEFINES='${DEFINES}' --no-print-directory -C ${ROMLIBPATH} all + $(q)${MAKE} PLAT_DIR=${PLAT_DIR} BUILD_PLAT=${BUILD_PLAT} ENABLE_BTI=${ENABLE_BTI} ARM_ARCH_MINOR=${ARM_ARCH_MINOR} INCLUDES=$(call escape-shell,$(INCLUDES)) DEFINES=$(call escape-shell,$(DEFINES)) --no-print-directory -C ${ROMLIBPATH} all memmap: all ifdef UNIX_MK - ${Q}PYTHONPATH=${CURDIR}/tools/memory \ + $(q)PYTHONPATH=${CURDIR}/tools/memory \ ${PYTHON} -m memory.memmap -sr ${BUILD_PLAT} else - ${Q}set PYTHONPATH=${CURDIR}/tools/memory && \ + $(q)set PYTHONPATH=${CURDIR}/tools/memory && \ ${PYTHON} -m memory.memmap -sr ${BUILD_PLAT} endif +tl: ${BUILD_PLAT}/tl.bin +${BUILD_PLAT}/tl.bin: ${HW_CONFIG} + $(if $(host-poetry),$(q)poetry -q install) + $(q)$(if $(host-poetry),poetry run )tlc create --fdt $< -s ${FW_HANDOFF_SIZE} $@ + doc: - @echo " BUILD DOCUMENTATION" - ${Q}${MAKE} --no-print-directory -C ${DOCS_PATH} html + $(s)echo " BUILD DOCUMENTATION" + $(q)${MAKE} --no-print-directory -C ${DOCS_PATH} html enctool: ${ENCTOOL} ${ENCTOOL}: FORCE - ${Q}${MAKE} PLAT=${PLAT} BUILD_INFO=0 OPENSSL_DIR=${OPENSSL_DIR} ENCTOOL=${ENCTOOL} DEBUG=${DEBUG} V=${V} --no-print-directory -C ${ENCTOOLPATH} all - @${ECHO_BLANK_LINE} - @echo "Built $@ successfully" - @${ECHO_BLANK_LINE} + $(q)${MAKE} PLAT=${PLAT} BUILD_INFO=0 OPENSSL_DIR=${OPENSSL_DIR} ENCTOOL=${ENCTOOL} DEBUG=${DEBUG} --no-print-directory -C ${ENCTOOLPATH} all + $(s)echo + $(s)echo "Built $@ successfully" + $(s)echo cscope: - @echo " CSCOPE" - ${Q}find ${CURDIR} -name "*.[chsS]" > cscope.files - ${Q}cscope -b -q -k + $(s)echo " CSCOPE" + $(q)find ${CURDIR} -name "*.[chsS]" > cscope.files + $(q)cscope -b -q -k help: - @echo "usage: ${MAKE} [PLAT=<platform>] [OPTIONS] [TARGET]" - @echo "" - @echo "PLAT is used to specify which platform you wish to build." - @echo "If no platform is specified, PLAT defaults to: ${DEFAULT_PLAT}" - @echo "" - @echo "platform = ${PLATFORM_LIST}" - @echo "" - @echo "Please refer to the User Guide for a list of all supported options." - @echo "Note that the build system doesn't track dependencies for build " - @echo "options. Therefore, if any of the build options are changed " - @echo "from a previous build, a clean build must be performed." - @echo "" - @echo "Supported Targets:" - @echo " all Build all individual bootloader binaries" - @echo " bl1 Build the BL1 binary" - @echo " bl2 Build the BL2 binary" - @echo " bl2u Build the BL2U binary" - @echo " bl31 Build the BL31 binary" - @echo " bl32 Build the BL32 binary. If ARCH=aarch32, then " - @echo " this builds secure payload specified by AARCH32_SP" - @echo " certificates Build the certificates (requires 'GENERATE_COT=1')" - @echo " fip Build the Firmware Image Package (FIP)" - @echo " fwu_fip Build the FWU Firmware Image Package (FIP)" - @echo " checkcodebase Check the coding style of the entire source tree" - @echo " checkpatch Check the coding style on changes in the current" - @echo " branch against BASE_COMMIT (default origin/master)" - @echo " clean Clean the build for the selected platform" - @echo " cscope Generate cscope index" - @echo " distclean Remove all build artifacts for all platforms" - @echo " certtool Build the Certificate generation tool" - @echo " enctool Build the Firmware encryption tool" - @echo " fiptool Build the Firmware Image Package (FIP) creation tool" - @echo " sp Build the Secure Partition Packages" - @echo " sptool Build the Secure Partition Package creation tool" - @echo " dtbs Build the Device Tree Blobs (if required for the platform)" - @echo " memmap Print the memory map of the built binaries" - @echo " doc Build html based documentation using Sphinx tool" - @echo "" - @echo "Note: most build targets require PLAT to be set to a specific platform." - @echo "" - @echo "example: build all targets for the FVP platform:" - @echo " CROSS_COMPILE=aarch64-none-elf- make PLAT=fvp all" + $(s)echo "usage: ${MAKE} [PLAT=<platform>] [OPTIONS] [TARGET]" + $(s)echo "" + $(s)echo "PLAT is used to specify which platform you wish to build." + $(s)echo "If no platform is specified, PLAT defaults to: ${DEFAULT_PLAT}" + $(s)echo "" + $(s)echo "platform = ${PLATFORM_LIST}" + $(s)echo "" + $(s)echo "Please refer to the User Guide for a list of all supported options." + $(s)echo "Note that the build system doesn't track dependencies for build " + $(s)echo "options. Therefore, if any of the build options are changed " + $(s)echo "from a previous build, a clean build must be performed." + $(s)echo "" + $(s)echo "Supported Targets:" + $(s)echo " all Build all individual bootloader binaries" + $(s)echo " bl1 Build the BL1 binary" + $(s)echo " bl2 Build the BL2 binary" + $(s)echo " bl2u Build the BL2U binary" + $(s)echo " bl31 Build the BL31 binary" + $(s)echo " bl32 Build the BL32 binary. If ARCH=aarch32, then " + $(s)echo " this builds secure payload specified by AARCH32_SP" + $(s)echo " certificates Build the certificates (requires 'GENERATE_COT=1')" + $(s)echo " fip Build the Firmware Image Package (FIP)" + $(s)echo " fwu_fip Build the FWU Firmware Image Package (FIP)" + $(s)echo " checkcodebase Check the coding style of the entire source tree" + $(s)echo " checkpatch Check the coding style on changes in the current" + $(s)echo " branch against BASE_COMMIT (default origin/master)" + $(s)echo " clean Clean the build for the selected platform" + $(s)echo " cscope Generate cscope index" + $(s)echo " distclean Remove all build artifacts for all platforms" + $(s)echo " certtool Build the Certificate generation tool" + $(s)echo " enctool Build the Firmware encryption tool" + $(s)echo " fiptool Build the Firmware Image Package (FIP) creation tool" + $(s)echo " sp Build the Secure Partition Packages" + $(s)echo " sptool Build the Secure Partition Package creation tool" + $(s)echo " dtbs Build the Device Tree Blobs (if required for the platform)" + $(s)echo " memmap Print the memory map of the built binaries" + $(s)echo " doc Build html based documentation using Sphinx tool" + $(s)echo "" + $(s)echo "Note: most build targets require PLAT to be set to a specific platform." + $(s)echo "" + $(s)echo "example: build all targets for the FVP platform:" + $(s)echo " CROSS_COMPILE=aarch64-none-elf- make PLAT=fvp all" .PHONY: FORCE FORCE:; diff --git a/bl1/bl1.ld.S b/bl1/bl1.ld.S index 49dda855..636aebe2 100644 --- a/bl1/bl1.ld.S +++ b/bl1/bl1.ld.S @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013-2023, Arm Limited and Contributors. All rights reserved. + * Copyright (c) 2013-2024, Arm Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -36,6 +36,9 @@ SECTIONS { #if SEPARATE_CODE_AND_RODATA .text . : { + ASSERT(. == ALIGN(PAGE_SIZE), + ".text address is not aligned on a page boundary."); + __TEXT_START__ = .; *bl1_entrypoint.o(.text*) @@ -80,6 +83,9 @@ SECTIONS { } >ROM #else /* SEPARATE_CODE_AND_RODATA */ .ro . : { + ASSERT(. == ALIGN(PAGE_SIZE), + ".ro address is not aligned on a page boundary."); + __RO_START__ = .; *bl1_entrypoint.o(.text*) @@ -110,6 +116,8 @@ SECTIONS { ASSERT(BL1_RW_BASE == ALIGN(PAGE_SIZE), "BL1_RW_BASE address is not aligned on a page boundary.") + __RW_START__ = .; + DATA_SECTION >RAM AT>ROM __DATA_RAM_START__ = __DATA_START__; @@ -142,6 +150,8 @@ SECTIONS { } >RAM #endif /* USE_COHERENT_MEM */ + __RW_END__ = .; + __BL1_RAM_START__ = ADDR(.data); __BL1_RAM_END__ = .; diff --git a/bl1/bl1.mk b/bl1/bl1.mk index 53946ab8..a8a00616 100644 --- a/bl1/bl1.mk +++ b/bl1/bl1.mk @@ -1,5 +1,5 @@ # -# Copyright (c) 2013-2023, Arm Limited and Contributors. All rights reserved. +# Copyright (c) 2013-2024, Arm Limited and Contributors. All rights reserved. # # SPDX-License-Identifier: BSD-3-Clause # @@ -12,13 +12,15 @@ BL1_SOURCES += bl1/${ARCH}/bl1_arch_setup.c \ lib/cpus/${ARCH}/cpu_helpers.S \ lib/cpus/errata_report.c \ lib/el3_runtime/${ARCH}/context_mgmt.c \ + lib/locks/exclusive/${ARCH}/spinlock.S \ plat/common/plat_bl1_common.c \ plat/common/${ARCH}/platform_up_stack.S \ ${MBEDTLS_SOURCES} ifeq (${ARCH},aarch64) BL1_SOURCES += lib/cpus/aarch64/dsu_helpers.S \ - lib/el3_runtime/aarch64/context.S + lib/el3_runtime/aarch64/context.S \ + lib/cpus/errata_common.c endif ifeq (${TRUSTED_BOARD_BOOT},1) @@ -29,9 +31,9 @@ ifeq (${ENABLE_PMF},1) BL1_SOURCES += lib/pmf/pmf_main.c endif -ifneq ($(findstring gcc,$(notdir $(LD))),) +ifeq ($($(ARCH)-ld-id),gnu-gcc) BL1_LDFLAGS += -Wl,--sort-section=alignment -else ifneq ($(findstring ld,$(notdir $(LD))),) +else ifneq ($(filter llvm-lld gnu-ld,$($(ARCH)-ld-id)),) BL1_LDFLAGS += --sort-section=alignment endif diff --git a/bl1/bl1_main.c b/bl1/bl1_main.c index 6fe55118..2b3a8271 100644 --- a/bl1/bl1_main.c +++ b/bl1/bl1_main.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013-2023, Arm Limited and Contributors. All rights reserved. + * Copyright (c) 2013-2024, Arm Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -13,6 +13,7 @@ #include <arch_helpers.h> #include <bl1/bl1.h> #include <common/bl_common.h> +#include <common/build_message.h> #include <common/debug.h> #include <drivers/auth/auth_mod.h> #include <drivers/auth/crypto_mod.h> @@ -38,32 +39,14 @@ uint64_t bl1_apiakey[2]; BL_TOTAL_IDS, PMF_DUMP_ENABLE) #endif -/******************************************************************************* - * Helper utility to calculate the BL2 memory layout taking into consideration - * the BL1 RW data assuming that it is at the top of the memory layout. - ******************************************************************************/ -void bl1_calc_bl2_mem_layout(const meminfo_t *bl1_mem_layout, - meminfo_t *bl2_mem_layout) -{ - assert(bl1_mem_layout != NULL); - assert(bl2_mem_layout != NULL); - - /* - * Remove BL1 RW data from the scope of memory visible to BL2. - * This is assuming BL1 RW data is at the top of bl1_mem_layout. - */ - assert(BL1_RW_BASE > bl1_mem_layout->total_base); - bl2_mem_layout->total_base = bl1_mem_layout->total_base; - bl2_mem_layout->total_size = BL1_RW_BASE - bl1_mem_layout->total_base; - - flush_dcache_range((uintptr_t)bl2_mem_layout, sizeof(meminfo_t)); -} - /******************************************************************************* * Setup function for BL1. ******************************************************************************/ void bl1_setup(void) { + /* Enable early console if EARLY_CONSOLE flag is enabled */ + plat_setup_early_console(); + /* Perform early platform-specific setup */ bl1_early_platform_setup(); @@ -94,7 +77,7 @@ void bl1_main(void) /* Announce our arrival */ NOTICE(FIRMWARE_WELCOME_STR); - NOTICE("BL1: %s\n", version_string); + NOTICE("BL1: %s\n", build_version_string); NOTICE("BL1: %s\n", build_message); INFO("BL1: RAM %p - %p\n", (void *)BL1_RAM_BASE, (void *)BL1_RAM_LIMIT); diff --git a/bl2/bl2.ld.S b/bl2/bl2.ld.S index db83a0c5..310e6fe7 100644 --- a/bl2/bl2.ld.S +++ b/bl2/bl2.ld.S @@ -25,6 +25,9 @@ SECTIONS { #if SEPARATE_CODE_AND_RODATA .text . : { + ASSERT(. == ALIGN(PAGE_SIZE), + ".text address is not aligned on a page boundary."); + __TEXT_START__ = .; #if ENABLE_RME @@ -65,6 +68,9 @@ SECTIONS { } >RAM #else /* SEPARATE_CODE_AND_RODATA */ .ro . : { + ASSERT(. == ALIGN(PAGE_SIZE), + ".ro address is not aligned on a page boundary."); + __RO_START__ = .; *bl2_entrypoint.o(.text*) diff --git a/bl2/bl2.mk b/bl2/bl2.mk index b70a3fbe..850d8266 100644 --- a/bl2/bl2.mk +++ b/bl2/bl2.mk @@ -1,5 +1,5 @@ # -# Copyright (c) 2013-2023, Arm Limited and Contributors. All rights reserved. +# Copyright (c) 2013-2024, Arm Limited and Contributors. All rights reserved. # # SPDX-License-Identifier: BSD-3-Clause # @@ -15,9 +15,9 @@ ifeq (${ARCH},aarch64) BL2_SOURCES += common/aarch64/early_exceptions.S endif -ifneq ($(findstring gcc,$(notdir $(LD))),) +ifeq ($($(ARCH)-ld-id),gnu-gcc) BL2_LDFLAGS += -Wl,--sort-section=alignment -else ifneq ($(findstring ld,$(notdir $(LD))),) +else ifneq ($(filter llvm-lld gnu-ld,$($(ARCH)-ld-id)),) BL2_LDFLAGS += --sort-section=alignment endif @@ -52,4 +52,4 @@ endif ifeq (${ENABLE_PMF},1) BL2_SOURCES += lib/pmf/pmf_main.c -endif \ No newline at end of file +endif diff --git a/bl2/bl2_el3.ld.S b/bl2/bl2_el3.ld.S index 4aa5cb04..811f41e1 100644 --- a/bl2/bl2_el3.ld.S +++ b/bl2/bl2_el3.ld.S @@ -55,6 +55,9 @@ SECTIONS { #if SEPARATE_CODE_AND_RODATA .text . : { + ASSERT(. == ALIGN(PAGE_SIZE), + ".text address is not aligned on a page boundary."); + __TEXT_START__ = .; __TEXT_RESIDENT_START__ = .; @@ -89,6 +92,9 @@ SECTIONS { "Resident part of BL2 has exceeded its limit.") #else /* SEPARATE_CODE_AND_RODATA */ .ro . : { + ASSERT(. == ALIGN(PAGE_SIZE), + ".ro address is not aligned on a page boundary."); + __RO_START__ = .; __TEXT_RESIDENT_START__ = .; diff --git a/bl2/bl2_main.c b/bl2/bl2_main.c index 923a554f..f12c1a5e 100644 --- a/bl2/bl2_main.c +++ b/bl2/bl2_main.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013-2023, Arm Limited and Contributors. All rights reserved. + * Copyright (c) 2013-2024, Arm Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -11,6 +11,7 @@ #include <bl1/bl1.h> #include <bl2/bl2.h> #include <common/bl_common.h> +#include <common/build_message.h> #include <common/debug.h> #include <drivers/auth/auth_mod.h> #include <drivers/auth/crypto_mod.h> @@ -41,6 +42,9 @@ void bl2_el3_setup(u_register_t arg0, u_register_t arg1, u_register_t arg2, u_register_t arg3) { + /* Enable early console if EARLY_CONSOLE flag is enabled */ + plat_setup_early_console(); + /* Perform early platform-specific setup */ bl2_el3_early_platform_setup(arg0, arg1, arg2, arg3); @@ -63,6 +67,9 @@ void bl2_el3_setup(u_register_t arg0, u_register_t arg1, u_register_t arg2, void bl2_setup(u_register_t arg0, u_register_t arg1, u_register_t arg2, u_register_t arg3) { + /* Enable early console if EARLY_CONSOLE flag is enabled */ + plat_setup_early_console(); + /* Perform early platform-specific setup */ bl2_early_platform_setup2(arg0, arg1, arg2, arg3); @@ -92,7 +99,7 @@ void bl2_main(void) PMF_CAPTURE_TIMESTAMP(bl_svc, BL2_ENTRY, PMF_CACHE_MAINT); #endif - NOTICE("BL2: %s\n", version_string); + NOTICE("BL2: %s\n", build_version_string); NOTICE("BL2: %s\n", build_message); /* Perform remaining generic architectural setup in S-EL1 */ diff --git a/bl2u/bl2u.ld.S b/bl2u/bl2u.ld.S index 7b1a1010..ee6a0206 100644 --- a/bl2u/bl2u.ld.S +++ b/bl2u/bl2u.ld.S @@ -27,6 +27,9 @@ SECTIONS { #if SEPARATE_CODE_AND_RODATA .text . : { + ASSERT(. == ALIGN(PAGE_SIZE), + ".text address is not aligned on a page boundary."); + __TEXT_START__ = .; *bl2u_entrypoint.o(.text*) @@ -60,6 +63,9 @@ SECTIONS { } >RAM #else /* SEPARATE_CODE_AND_RODATA */ .ro . : { + ASSERT(. == ALIGN(PAGE_SIZE), + ".ro address is not aligned on a page boundary."); + __RO_START__ = .; *bl2u_entrypoint.o(.text*) diff --git a/bl2u/bl2u.mk b/bl2u/bl2u.mk index 9fe20f50..a4051ecc 100644 --- a/bl2u/bl2u.mk +++ b/bl2u/bl2u.mk @@ -1,5 +1,5 @@ # -# Copyright (c) 2015-2023, Arm Limited and Contributors. All rights reserved. +# Copyright (c) 2015-2024, Arm Limited and Contributors. All rights reserved. # # SPDX-License-Identifier: BSD-3-Clause # @@ -14,8 +14,8 @@ endif BL2U_DEFAULT_LINKER_SCRIPT_SOURCE := bl2u/bl2u.ld.S -ifneq ($(findstring gcc,$(notdir $(LD))),) +ifeq ($($(ARCH)-ld-id),gnu-gcc) BL2U_LDFLAGS += -Wl,--sort-section=alignment -else ifneq ($(findstring ld,$(notdir $(LD))),) +else ifneq ($(filter llvm-lld gnu-ld,$($(ARCH)-ld-id)),) BL2U_LDFLAGS += --sort-section=alignment endif diff --git a/bl2u/bl2u_main.c b/bl2u/bl2u_main.c index fcb73b9c..cd13defe 100644 --- a/bl2u/bl2u_main.c +++ b/bl2u/bl2u_main.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015-2020, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2015-2024, Arm Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -14,6 +14,7 @@ #include <bl1/bl1.h> #include <bl2u/bl2u.h> #include <common/bl_common.h> +#include <common/build_message.h> #include <common/debug.h> #include <drivers/auth/auth_mod.h> #include <drivers/console.h> @@ -27,7 +28,7 @@ ******************************************************************************/ void bl2u_main(void) { - NOTICE("BL2U: %s\n", version_string); + NOTICE("BL2U: %s\n", build_version_string); NOTICE("BL2U: %s\n", build_message); #if SCP_BL2U_BASE diff --git a/bl31/aarch64/runtime_exceptions.S b/bl31/aarch64/runtime_exceptions.S index ed483111..74238056 100644 --- a/bl31/aarch64/runtime_exceptions.S +++ b/bl31/aarch64/runtime_exceptions.S @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013-2023, Arm Limited and Contributors. All rights reserved. + * Copyright (c) 2013-2024, Arm Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -229,7 +229,6 @@ vector_entry sync_exception_aarch64 save_x30 apply_at_speculative_wa sync_and_handle_pending_serror - unmask_async_ea handle_sync_exception end_vector_entry sync_exception_aarch64 @@ -237,7 +236,6 @@ vector_entry irq_aarch64 save_x30 apply_at_speculative_wa sync_and_handle_pending_serror - unmask_async_ea b handle_interrupt_exception end_vector_entry irq_aarch64 @@ -245,7 +243,6 @@ vector_entry fiq_aarch64 save_x30 apply_at_speculative_wa sync_and_handle_pending_serror - unmask_async_ea b handle_interrupt_exception end_vector_entry fiq_aarch64 @@ -258,7 +255,6 @@ vector_entry serror_aarch64 save_x30 apply_at_speculative_wa sync_and_handle_pending_serror - unmask_async_ea b handle_lower_el_async_ea #else b report_unhandled_exception @@ -279,7 +275,6 @@ vector_entry sync_exception_aarch32 save_x30 apply_at_speculative_wa sync_and_handle_pending_serror - unmask_async_ea handle_sync_exception end_vector_entry sync_exception_aarch32 @@ -287,7 +282,6 @@ vector_entry irq_aarch32 save_x30 apply_at_speculative_wa sync_and_handle_pending_serror - unmask_async_ea b handle_interrupt_exception end_vector_entry irq_aarch32 @@ -295,7 +289,6 @@ vector_entry fiq_aarch32 save_x30 apply_at_speculative_wa sync_and_handle_pending_serror - unmask_async_ea b handle_interrupt_exception end_vector_entry fiq_aarch32 @@ -308,7 +301,6 @@ vector_entry serror_aarch32 save_x30 apply_at_speculative_wa sync_and_handle_pending_serror - unmask_async_ea b handle_lower_el_async_ea #else b report_unhandled_exception @@ -450,7 +442,7 @@ sync_handler64: * * handler = (base + off) + (index << log2(size)) */ - adr x11, (__RT_SVC_DESCS_START__ + RT_SVC_DESC_HANDLE) + adr_l x11, (__RT_SVC_DESCS_START__ + RT_SVC_DESC_HANDLE) lsl w10, w15, #RT_SVC_SIZE_LOG2 ldr x15, [x11, w10, uxtw] @@ -476,21 +468,33 @@ sysreg_handler64: bl handle_sysreg_trap /* * returns: - * -1: unhandled trap, panic + * -1: unhandled trap, UNDEF injection into lower EL * 0: handled trap, return to the trapping instruction (repeating it) * 1: handled trap, return to the next instruction */ tst w0, w0 - b.mi elx_panic /* negative return value: panic */ - b.eq 1f /* zero: do not change ELR_EL3 */ + b.mi 2f /* negative: undefined exception injection */ - /* advance the PC to continue after the instruction */ + b.eq 1f /* zero: do not change ELR_EL3 */ + /* positive: advance the PC to continue after the instruction */ ldr x1, [x19, #CTX_EL3STATE_OFFSET + CTX_ELR_EL3] add x1, x1, #4 str x1, [x19, #CTX_EL3STATE_OFFSET + CTX_ELR_EL3] 1: b el3_exit +2: + /* + * UNDEF injection to lower EL, the support is only provided for lower + * EL in AArch64 mode, for AArch32 mode it will do elx_panic as before. + */ + mrs x0, spsr_el3 + tst x0, #(SPSR_M_MASK << SPSR_M_SHIFT) + b.ne elx_panic + /* Pass context pointer as an argument to inject_undef64 */ + mov x0, x19 + bl inject_undef64 + b el3_exit smc_unknown: /* diff --git a/bl31/bl31.ld.S b/bl31/bl31.ld.S index 773b41d3..867dedb9 100644 --- a/bl31/bl31.ld.S +++ b/bl31/bl31.ld.S @@ -19,6 +19,12 @@ MEMORY { #else /* SEPARATE_NOBITS_REGION */ # define NOBITS RAM #endif /* SEPARATE_NOBITS_REGION */ + +#if SEPARATE_RWDATA_REGION + RAM_RW (rw): ORIGIN = BL31_RWDATA_BASE, LENGTH = BL31_RWDATA_LIMIT - BL31_RWDATA_BASE +#else /* SEPARATE_RWDATA_REGION */ +#define RAM_RW RAM +#endif /* SEPARATE_RWDATA_REGION */ } #ifdef PLAT_EXTRA_LD_SCRIPT @@ -37,6 +43,9 @@ SECTIONS { #if SEPARATE_CODE_AND_RODATA .text . : { + ASSERT(. == ALIGN(PAGE_SIZE), + ".text is not aligned on a page boundary."); + __TEXT_START__ = .; *bl31_entrypoint.o(.text*) @@ -71,6 +80,9 @@ SECTIONS { } >RAM #else /* SEPARATE_CODE_AND_RODATA */ .ro . : { + ASSERT(. == ALIGN(PAGE_SIZE), + ".ro is not aligned on a page boundary."); + __RO_START__ = .; *bl31_entrypoint.o(.text*) @@ -130,10 +142,36 @@ SECTIONS { . = LOADADDR(.spm_shim_exceptions) + SIZEOF(.spm_shim_exceptions); #endif /* SPM_MM || (SPMC_AT_EL3 && SPMC_AT_EL3_SEL0_SP) */ - __RW_START__ = .; +#if SEPARATE_RWDATA_REGION + . = BL31_RWDATA_BASE; + ASSERT(BL31_RWDATA_BASE == ALIGN(PAGE_SIZE), + "BL31_RWDATA_BASE address is not aligned on a page boundary.") + + /* + * Define a linker symbol to mark the start of the RW memory area for this + * image. + */ + __RW_START__ = . ; + + DATA_SECTION >RAM_RW AT>RAM + __DATA_RAM_START__ = __DATA_START__; + __DATA_RAM_END__ = __DATA_END__; + __DATA_ROM_START__ = LOADADDR(.data); + + . = ALIGN(PAGE_SIZE); + __RW_END__ = .; + + RELA_SECTION >RAM +#else /* SEPARATE_RWDATA_REGION */ + /* + * Define a linker symbol to mark the start of the RW memory area for this + * image. + */ + __RW_START__ = . ; DATA_SECTION >RAM RELA_SECTION >RAM +#endif /* SEPARATE_RWDATA_REGION */ #ifdef BL31_PROGBITS_LIMIT ASSERT( @@ -145,7 +183,9 @@ SECTIONS { #if SEPARATE_NOBITS_REGION . = ALIGN(PAGE_SIZE); +#if !SEPARATE_RWDATA_REGION __RW_END__ = .; +#endif /* SEPARATE_RWDATA_REGION */ __BL31_END__ = .; ASSERT(. <= BL31_LIMIT, "BL31 image has exceeded its limit.") @@ -197,7 +237,13 @@ SECTIONS { ASSERT(. <= BL31_NOBITS_LIMIT, "BL31 NOBITS region has exceeded its limit.") #else /* SEPARATE_NOBITS_REGION */ + /* + * Define a linker symbol to mark the end of the RW memory area for this + * image. + */ +#if !SEPARATE_RWDATA_REGION __RW_END__ = .; +#endif /* SEPARATE_RWDATA_REGION */ __BL31_END__ = .; ASSERT(. <= BL31_LIMIT, "BL31 image has exceeded its limit.") diff --git a/bl31/bl31.mk b/bl31/bl31.mk index f0776c4e..336ad2be 100644 --- a/bl31/bl31.mk +++ b/bl31/bl31.mk @@ -1,5 +1,5 @@ # -# Copyright (c) 2013-2023, Arm Limited and Contributors. All rights reserved. +# Copyright (c) 2013-2024, Arm Limited and Contributors. All rights reserved. # # SPDX-License-Identifier: BSD-3-Clause # @@ -42,23 +42,33 @@ BL31_SOURCES += bl31/bl31_main.c \ bl31/bl31_context_mgmt.c \ bl31/bl31_traps.c \ common/runtime_svc.c \ + lib/cpus/errata_common.c \ lib/cpus/aarch64/dsu_helpers.S \ plat/common/aarch64/platform_mp_stack.S \ services/arm_arch_svc/arm_arch_svc_setup.c \ services/std_svc/std_svc_setup.c \ + lib/el3_runtime/simd_ctx.c \ ${PSCI_LIB_SOURCES} \ ${SPMD_SOURCES} \ ${SPM_MM_SOURCES} \ ${SPMC_SOURCES} \ ${SPM_SOURCES} +VENDOR_EL3_SRCS += services/el3/ven_el3_svc.c + ifeq (${ENABLE_PMF}, 1) -BL31_SOURCES += lib/pmf/pmf_main.c +BL31_SOURCES += lib/pmf/pmf_main.c \ + ${VENDOR_EL3_SRCS} endif include lib/debugfs/debugfs.mk ifeq (${USE_DEBUGFS},1) - BL31_SOURCES += $(DEBUGFS_SRCS) +BL31_SOURCES += ${DEBUGFS_SRCS} \ + ${VENDOR_EL3_SRCS} +endif + +ifeq (${PLATFORM_REPORT_CTX_MEM_USE},1) +BL31_SOURCES += lib/el3_runtime/aarch64/context_debug.c endif ifeq (${EL3_EXCEPTION_HANDLING},1) @@ -97,6 +107,14 @@ ifneq (${ENABLE_FEAT_AMU},0) BL31_SOURCES += ${AMU_SOURCES} endif +ifneq (${ENABLE_FEAT_FGT2},0) +BL31_SOURCES += lib/extensions/fgt/fgt2.c +endif + +ifneq (${ENABLE_FEAT_TCR2},0) +BL31_SOURCES += lib/extensions/tcr/tcr2.c +endif + ifeq (${ENABLE_MPMM},1) BL31_SOURCES += ${MPMM_SOURCES} endif @@ -112,6 +130,10 @@ ifneq (${ENABLE_FEAT_MPAM},0) BL31_SOURCES += lib/extensions/mpam/mpam.c endif +ifneq (${ENABLE_FEAT_DEBUGV8P9},0) +BL31_SOURCES += lib/extensions/debug/debugv8p9.c +endif + ifneq (${ENABLE_TRBE_FOR_NS},0) BL31_SOURCES += lib/extensions/trbe/trbe.c endif @@ -157,11 +179,15 @@ BL31_SOURCES += services/std_svc/drtm/drtm_main.c \ ${MBEDTLS_SOURCES} endif +ifeq ($(CROS_WIDEVINE_SMC),1) +BL31_SOURCES += services/oem/chromeos/widevine_smc_handlers.c +endif + BL31_DEFAULT_LINKER_SCRIPT_SOURCE := bl31/bl31.ld.S -ifneq ($(findstring gcc,$(notdir $(LD))),) +ifeq ($($(ARCH)-ld-id),gnu-gcc) BL31_LDFLAGS += -Wl,--sort-section=alignment -else ifneq ($(findstring ld,$(notdir $(LD))),) +else ifneq ($(filter llvm-lld gnu-ld,$($(ARCH)-ld-id)),) BL31_LDFLAGS += --sort-section=alignment endif diff --git a/bl31/bl31_main.c b/bl31/bl31_main.c index 925c6a69..83be0f6f 100644 --- a/bl31/bl31_main.c +++ b/bl31/bl31_main.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013-2023, Arm Limited and Contributors. All rights reserved. + * Copyright (c) 2013-2024, Arm Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -13,11 +13,13 @@ #include <bl31/bl31.h> #include <bl31/ehf.h> #include <common/bl_common.h> +#include <common/build_message.h> #include <common/debug.h> #include <common/feat_detect.h> #include <common/runtime_svc.h> #include <drivers/console.h> #include <lib/bootmarker_capture.h> +#include <lib/el3_runtime/context_debug.h> #include <lib/el3_runtime/context_mgmt.h> #include <lib/pmf/pmf.h> #include <lib/runtime_instr.h> @@ -82,7 +84,7 @@ uintptr_t get_arm_std_svc_args(unsigned int svc_mask) /******************************************************************************* * Simple function to initialise all BL31 helper libraries. ******************************************************************************/ -void __init bl31_lib_init(void) +static void __init bl31_lib_init(void) { cm_init(); } @@ -93,6 +95,9 @@ void __init bl31_lib_init(void) void bl31_setup(u_register_t arg0, u_register_t arg1, u_register_t arg2, u_register_t arg3) { + /* Enable early console if EARLY_CONSOLE flag is enabled */ + plat_setup_early_console(); + /* Perform early platform-specific setup */ bl31_early_platform_setup2(arg0, arg1, arg2, arg3); @@ -106,6 +111,9 @@ void bl31_setup(u_register_t arg0, u_register_t arg1, u_register_t arg2, */ assert(is_armv8_3_pauth_present()); #endif /* CTX_INCLUDE_PAUTH_REGS */ + + /* Prints context_memory allocated for all the security states */ + report_ctx_memory_usage(); } /******************************************************************************* @@ -124,7 +132,7 @@ void bl31_main(void) /* Init per-world context registers for non-secure world */ manage_extensions_nonsecure_per_world(); - NOTICE("BL31: %s\n", version_string); + NOTICE("BL31: %s\n", build_version_string); NOTICE("BL31: %s\n", build_message); #if FEATURE_DETECTION @@ -207,8 +215,6 @@ void bl31_main(void) */ bl31_prepare_next_image_entry(); - console_flush(); - /* * Perform any platform specific runtime setup prior to cold boot exit * from BL31 @@ -216,9 +222,12 @@ void bl31_main(void) bl31_plat_runtime_setup(); #if ENABLE_RUNTIME_INSTRUMENTATION - PMF_CAPTURE_TIMESTAMP(bl_svc, BL31_EXIT, PMF_CACHE_MAINT); console_flush(); + PMF_CAPTURE_TIMESTAMP(bl_svc, BL31_EXIT, PMF_CACHE_MAINT); #endif + + console_flush(); + console_switch_state(CONSOLE_FLAG_RUNTIME); } /******************************************************************************* diff --git a/bl31/bl31_traps.c b/bl31/bl31_traps.c index 2cfe14a8..984fdaa4 100644 --- a/bl31/bl31_traps.c +++ b/bl31/bl31_traps.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, ARM Limited. All rights reserved. + * Copyright (c) 2022-2024, Arm Limited. All rights reserved. * Copyright (c) 2023, NVIDIA Corporation. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause @@ -7,8 +7,11 @@ * Dispatch synchronous system register traps from lower ELs. */ +#include <arch_features.h> +#include <arch_helpers.h> #include <bl31/sync_handle.h> #include <context.h> +#include <lib/el3_runtime/context_mgmt.h> int handle_sysreg_trap(uint64_t esr_el3, cpu_context_t *ctx) { @@ -28,3 +31,213 @@ int handle_sysreg_trap(uint64_t esr_el3, cpu_context_t *ctx) return TRAP_RET_UNHANDLED; } + +static bool is_tge_enabled(void) +{ + u_register_t hcr_el2 = read_hcr_el2(); + + return ((is_feat_vhe_present()) && ((hcr_el2 & HCR_TGE_BIT) != 0U)); +} + +/* + * This function is to ensure that undef injection does not happen into + * non-existent S-EL2. This could happen when trap happens from S-EL{1,0} + * and non-secure world is running with TGE bit set, considering EL3 does + * not save/restore EL2 registers if only one world has EL2 enabled. + * So reading hcr_el2.TGE would give NS world value. + */ +static bool is_secure_trap_without_sel2(u_register_t scr) +{ + return ((scr & (SCR_NS_BIT | SCR_EEL2_BIT)) == 0); +} + +static unsigned int target_el(unsigned int from_el, u_register_t scr) +{ + if (from_el > MODE_EL1) { + return from_el; + } else if (is_tge_enabled() && !is_secure_trap_without_sel2(scr)) { + return MODE_EL2; + } else { + return MODE_EL1; + } +} + +static u_register_t get_elr_el3(u_register_t spsr_el3, u_register_t vbar, unsigned int target_el) +{ + unsigned int outgoing_el = GET_EL(spsr_el3); + u_register_t elr_el3 = 0; + + if (outgoing_el == target_el) { + /* + * Target EL is either EL1 or EL2, lsb can tell us the SPsel + * Thread mode : 0 + * Handler mode : 1 + */ + if ((spsr_el3 & (MODE_SP_MASK << MODE_SP_SHIFT)) == MODE_SP_ELX) { + elr_el3 = vbar + CURRENT_EL_SPX; + } else { + elr_el3 = vbar + CURRENT_EL_SP0; + } + } else { + /* Vector address for Lower EL using Aarch64 */ + elr_el3 = vbar + LOWER_EL_AARCH64; + } + + return elr_el3; +} + +/* + * Explicitly create all bits of SPSR to get PSTATE at exception return. + * + * The code is based on "Aarch64.exceptions.takeexception" described in + * DDI0602 revision 2023-06. + * "https://developer.arm.com/documentation/ddi0602/2023-06/Shared-Pseudocode/ + * aarch64-exceptions-takeexception" + * + * NOTE: This piece of code must be reviewed every release to ensure that + * we keep up with new ARCH features which introduces a new SPSR bit. + * + * TF-A 2.12 release review + * The latest version available is 2024-09, which has two extra features which + * impacts generation of SPSR, since these features are not implemented in TF-A + * at the time of release, just log the feature names here to be taken up when + * feature support is introduced. + * - FEAT_PAuth_LR (2023 extension) + * - FEAT_UINJ (2024 extension) + */ +u_register_t create_spsr(u_register_t old_spsr, unsigned int target_el) +{ + u_register_t new_spsr = 0; + u_register_t sctlr; + + /* Set M bits for target EL in AArch64 mode, also get sctlr */ + if (target_el == MODE_EL2) { + sctlr = read_sctlr_el2(); + new_spsr |= (SPSR_M_AARCH64 << SPSR_M_SHIFT) | SPSR_M_EL2H; + } else { + sctlr = read_sctlr_el1(); + new_spsr |= (SPSR_M_AARCH64 << SPSR_M_SHIFT) | SPSR_M_EL1H; + } + + /* Mask all exceptions, update DAIF bits */ + new_spsr |= SPSR_DAIF_MASK << SPSR_DAIF_SHIFT; + + /* If FEAT_BTI is present, clear BTYPE bits */ + new_spsr |= old_spsr & (SPSR_BTYPE_MASK_AARCH64 << SPSR_BTYPE_SHIFT_AARCH64); + if (is_feat_bti_present()) { + new_spsr &= ~(SPSR_BTYPE_MASK_AARCH64 << SPSR_BTYPE_SHIFT_AARCH64); + } + + /* If SSBS is implemented, take the value from SCTLR.DSSBS */ + new_spsr |= old_spsr & SPSR_SSBS_BIT_AARCH64; + if (is_feat_ssbs_present()) { + if ((sctlr & SCTLR_DSSBS_BIT) != 0U) { + new_spsr |= SPSR_SSBS_BIT_AARCH64; + } else { + new_spsr &= ~SPSR_SSBS_BIT_AARCH64; + } + } + + /* If FEAT_NMI is implemented, ALLINT = !(SCTLR.SPINTMASK) */ + new_spsr |= old_spsr & SPSR_ALLINT_BIT_AARCH64; + if (is_feat_nmi_present()) { + if ((sctlr & SCTLR_SPINTMASK_BIT) != 0U) { + new_spsr &= ~SPSR_ALLINT_BIT_AARCH64; + } else { + new_spsr |= SPSR_ALLINT_BIT_AARCH64; + } + } + + /* Clear PSTATE.IL bit explicitly */ + new_spsr &= ~SPSR_IL_BIT; + + /* Clear PSTATE.SS bit explicitly */ + new_spsr &= ~SPSR_SS_BIT; + + /* Update PSTATE.PAN bit */ + new_spsr |= old_spsr & SPSR_PAN_BIT; + if (is_feat_pan_present() && + ((target_el == MODE_EL1) || ((target_el == MODE_EL2) && is_tge_enabled())) && + ((sctlr & SCTLR_SPAN_BIT) == 0U)) { + new_spsr |= SPSR_PAN_BIT; + } + + /* Clear UAO bit if FEAT_UAO is present */ + new_spsr |= old_spsr & SPSR_UAO_BIT_AARCH64; + if (is_feat_uao_present()) { + new_spsr &= ~SPSR_UAO_BIT_AARCH64; + } + + /* DIT bits are unchanged */ + new_spsr |= old_spsr & SPSR_DIT_BIT; + + /* If FEAT_MTE2 is implemented mask tag faults by setting TCO bit */ + new_spsr |= old_spsr & SPSR_TCO_BIT_AARCH64; + if (is_feat_mte2_present()) { + new_spsr |= SPSR_TCO_BIT_AARCH64; + } + + /* NZCV bits are unchanged */ + new_spsr |= old_spsr & SPSR_NZCV; + + /* If FEAT_EBEP is present set PM bit */ + new_spsr |= old_spsr & SPSR_PM_BIT_AARCH64; + if (is_feat_ebep_present()) { + new_spsr |= SPSR_PM_BIT_AARCH64; + } + + /* If FEAT_SEBEP is present clear PPEND bit */ + new_spsr |= old_spsr & SPSR_PPEND_BIT; + if (is_feat_sebep_present()) { + new_spsr &= ~SPSR_PPEND_BIT; + } + + /* If FEAT_GCS is present, update EXLOCK bit */ + new_spsr |= old_spsr & SPSR_EXLOCK_BIT_AARCH64; + if (is_feat_gcs_present()) { + u_register_t gcscr; + if (target_el == MODE_EL2) { + gcscr = read_gcscr_el2(); + } else { + gcscr = read_gcscr_el1(); + } + new_spsr |= (gcscr & GCSCR_EXLOCK_EN_BIT) ? SPSR_EXLOCK_BIT_AARCH64 : 0; + } + + return new_spsr; +} + +/* + * Handler for injecting Undefined exception to lower EL which is caused by + * lower EL accessing system registers of which (old)EL3 firmware is unaware. + * + * This is a safety net to avoid EL3 panics caused by system register access + * that triggers an exception syndrome EC=0x18. + */ +void inject_undef64(cpu_context_t *ctx) +{ + u_register_t esr = (EC_UNKNOWN << ESR_EC_SHIFT) | ESR_IL_BIT; + el3_state_t *state = get_el3state_ctx(ctx); + u_register_t elr_el3 = read_ctx_reg(state, CTX_ELR_EL3); + u_register_t old_spsr = read_ctx_reg(state, CTX_SPSR_EL3); + u_register_t scr_el3 = read_ctx_reg(state, CTX_SCR_EL3); + u_register_t new_spsr = 0; + unsigned int to_el = target_el(GET_EL(old_spsr), scr_el3); + + if (to_el == MODE_EL2) { + write_elr_el2(elr_el3); + elr_el3 = get_elr_el3(old_spsr, read_vbar_el2(), to_el); + write_esr_el2(esr); + write_spsr_el2(old_spsr); + } else { + write_elr_el1(elr_el3); + elr_el3 = get_elr_el3(old_spsr, read_vbar_el1(), to_el); + write_esr_el1(esr); + write_spsr_el1(old_spsr); + } + + new_spsr = create_spsr(old_spsr, to_el); + + write_ctx_reg(state, CTX_SPSR_EL3, new_spsr); + write_ctx_reg(state, CTX_ELR_EL3, elr_el3); +} diff --git a/bl31/ehf.c b/bl31/ehf.c index 6f3d9412..3a14635c 100644 --- a/bl31/ehf.c +++ b/bl31/ehf.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2023, Arm Limited and Contributors. All rights reserved. + * Copyright (c) 2017-2024, Arm Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -203,10 +203,20 @@ void ehf_deactivate_priority(unsigned int priority) * one stashed earlier if there are no more to deactivate. */ cur_pri_idx = get_pe_highest_active_idx(pe_data); - if (cur_pri_idx == EHF_INVALID_IDX) + +#if GIC600_ERRATA_WA_2384374 + if (cur_pri_idx == EHF_INVALID_IDX) { + old_mask = plat_ic_deactivate_priority(pe_data->init_pri_mask); + } else { + old_mask = plat_ic_deactivate_priority(priority); + } +#else + if (cur_pri_idx == EHF_INVALID_IDX) { old_mask = plat_ic_set_priority_mask(pe_data->init_pri_mask); - else + } else { old_mask = plat_ic_set_priority_mask(priority); + } +#endif if (old_mask > priority) { ERROR("Deactivation priority (0x%x) lower than Priority Mask (0x%x)\n", @@ -478,13 +488,10 @@ void __init ehf_init(void) /* Route EL3 interrupts when in Non-secure. */ set_interrupt_rm_flag(flags, NON_SECURE); - /* - * Route EL3 interrupts when in secure, only when SPMC is not present - * in S-EL2. - */ -#if !(defined(SPD_spmd) && (SPMD_SPM_AT_SEL2 == 1)) + /* Route EL3 interrupts only when SPM_MM present in secure. */ +#if SPM_MM set_interrupt_rm_flag(flags, SECURE); -#endif /* !(defined(SPD_spmd) && (SPMD_SPM_AT_SEL2 == 1)) */ +#endif /* Register handler for EL3 interrupts */ ret = register_interrupt_type_handler(INTR_TYPE_EL3, diff --git a/bl31/interrupt_mgmt.c b/bl31/interrupt_mgmt.c index 68c7f10a..a2b2c068 100644 --- a/bl31/interrupt_mgmt.c +++ b/bl31/interrupt_mgmt.c @@ -34,7 +34,7 @@ * * All other bits are reserved and SBZ. ******************************************************************************/ -typedef struct intr_type_desc { +typedef struct { interrupt_type_handler_t handler; u_register_t scr_el3[2]; uint32_t flags; diff --git a/bl32/sp_min/aarch32/entrypoint.S b/bl32/sp_min/aarch32/entrypoint.S index 693dd4b8..ba9d90d4 100644 --- a/bl32/sp_min/aarch32/entrypoint.S +++ b/bl32/sp_min/aarch32/entrypoint.S @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016-2023, Arm Limited and Contributors. All rights reserved. + * Copyright (c) 2016-2024, Arm Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -118,8 +118,7 @@ func sp_min_entrypoint mov r1, r10 mov r2, r11 mov r3, r12 - bl sp_min_early_platform_setup2 - bl sp_min_plat_arch_setup + bl sp_min_setup /* Jump to the main function */ bl sp_min_main diff --git a/bl32/sp_min/sp_min.ld.S b/bl32/sp_min/sp_min.ld.S index dd819733..a2d9b7bf 100644 --- a/bl32/sp_min/sp_min.ld.S +++ b/bl32/sp_min/sp_min.ld.S @@ -29,6 +29,9 @@ SECTIONS { #if SEPARATE_CODE_AND_RODATA .text . : { + ASSERT(. == ALIGN(PAGE_SIZE), + ".text address is not aligned on a page boundary."); + __TEXT_START__ = .; *entrypoint.o(.text*) @@ -67,6 +70,9 @@ SECTIONS { } >RAM #else /* SEPARATE_CODE_AND_RODATA */ .ro . : { + ASSERT(. == ALIGN(PAGE_SIZE), + ".ro address is not aligned on a page boundary."); + __RO_START__ = .; *entrypoint.o(.text*) diff --git a/bl32/sp_min/sp_min.mk b/bl32/sp_min/sp_min.mk index 065468c5..b1f4343f 100644 --- a/bl32/sp_min/sp_min.mk +++ b/bl32/sp_min/sp_min.mk @@ -1,5 +1,5 @@ # -# Copyright (c) 2016-2023, Arm Limited and Contributors. All rights reserved. +# Copyright (c) 2016-2024, Arm Limited and Contributors. All rights reserved. # # SPDX-License-Identifier: BSD-3-Clause # @@ -13,16 +13,17 @@ include lib/psci/psci_lib.mk INCLUDES += -Iinclude/bl32/sp_min -BL32_SOURCES += bl32/sp_min/sp_min_main.c \ - bl32/sp_min/aarch32/entrypoint.S \ - common/runtime_svc.c \ - plat/common/aarch32/plat_sp_min_common.c\ +BL32_SOURCES += bl32/sp_min/sp_min_main.c \ + bl32/sp_min/aarch32/entrypoint.S \ + common/runtime_svc.c \ + plat/common/aarch32/plat_sp_min_common.c \ services/arm_arch_svc/arm_arch_svc_setup.c \ - services/std_svc/std_svc_setup.c \ + services/std_svc/std_svc_setup.c \ ${PSCI_LIB_SOURCES} ifeq (${ENABLE_PMF}, 1) -BL32_SOURCES += lib/pmf/pmf_main.c +BL32_SOURCES += services/el3/ven_el3_svc.c \ + lib/pmf/pmf_main.c endif ifneq (${ENABLE_FEAT_AMU},0) @@ -57,9 +58,9 @@ endif BL32_DEFAULT_LINKER_SCRIPT_SOURCE := bl32/sp_min/sp_min.ld.S -ifneq ($(findstring gcc,$(notdir $(LD))),) +ifeq ($($(ARCH)-ld-id),gnu-gcc) BL32_LDFLAGS += -Wl,--sort-section=alignment -else ifneq ($(findstring ld,$(notdir $(LD))),) +else ifneq ($(filter llvm-lld gnu-ld,$($(ARCH)-ld-id)),) BL32_LDFLAGS += --sort-section=alignment endif diff --git a/bl32/sp_min/sp_min_main.c b/bl32/sp_min/sp_min_main.c index 26cf2079..a26910cb 100644 --- a/bl32/sp_min/sp_min_main.c +++ b/bl32/sp_min/sp_min_main.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016-2023, Arm Limited and Contributors. All rights reserved. + * Copyright (c) 2016-2024, Arm Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -14,6 +14,7 @@ #include <arch.h> #include <arch_helpers.h> #include <common/bl_common.h> +#include <common/build_message.h> #include <common/debug.h> #include <common/runtime_svc.h> #include <context.h> @@ -169,13 +170,27 @@ uintptr_t get_arm_std_svc_args(unsigned int svc_mask) return (uintptr_t)&psci_args; } +/****************************************************************************** + * The SP_MIN setup function. Calls platforms init functions + *****************************************************************************/ +void sp_min_setup(u_register_t arg0, u_register_t arg1, u_register_t arg2, + u_register_t arg3) +{ + /* Enable early console if EARLY_CONSOLE flag is enabled */ + plat_setup_early_console(); + + /* Perform early platform-specific setup */ + sp_min_early_platform_setup2(arg0, arg1, arg2, arg3); + sp_min_plat_arch_setup(); +} + /****************************************************************************** * The SP_MIN main function. Do the platform and PSCI Library setup. Also * initialize the runtime service framework. *****************************************************************************/ void sp_min_main(void) { - NOTICE("SP_MIN: %s\n", version_string); + NOTICE("SP_MIN: %s\n", build_version_string); NOTICE("SP_MIN: %s\n", build_message); /* Perform the SP_MIN platform setup */ diff --git a/bl32/sp_min/sp_min_private.h b/bl32/sp_min/sp_min_private.h index 628581a4..9c6b5fb2 100644 --- a/bl32/sp_min/sp_min_private.h +++ b/bl32/sp_min/sp_min_private.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2016-2024, Arm Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -7,6 +7,10 @@ #ifndef SP_MIN_PRIVATE_H #define SP_MIN_PRIVATE_H +#include <stdint.h> + +void sp_min_setup(u_register_t arg0, u_register_t arg1, u_register_t arg2, + u_register_t arg3); void sp_min_main(void); void sp_min_warm_boot(void); void sp_min_fiq(void); diff --git a/bl32/tsp/tsp.ld.S b/bl32/tsp/tsp.ld.S index 22bf11da..5116b20a 100644 --- a/bl32/tsp/tsp.ld.S +++ b/bl32/tsp/tsp.ld.S @@ -25,6 +25,9 @@ SECTIONS { #if SEPARATE_CODE_AND_RODATA .text . : { + ASSERT(. == ALIGN(PAGE_SIZE), + ".text address is not aligned on a page boundary."); + __TEXT_START__ = .; *tsp_entrypoint.o(.text*) @@ -51,6 +54,9 @@ SECTIONS { } >RAM #else /* SEPARATE_CODE_AND_RODATA */ .ro . : { + ASSERT(. == ALIGN(PAGE_SIZE), + ".ro address is not aligned on a page boundary."); + __RO_START__ = .; *tsp_entrypoint.o(.text*) diff --git a/bl32/tsp/tsp.mk b/bl32/tsp/tsp.mk index 4c181311..696cdb2c 100644 --- a/bl32/tsp/tsp.mk +++ b/bl32/tsp/tsp.mk @@ -1,5 +1,5 @@ # -# Copyright (c) 2013-2023, Arm Limited and Contributors. All rights reserved. +# Copyright (c) 2013-2024, Arm Limited and Contributors. All rights reserved. # # SPDX-License-Identifier: BSD-3-Clause # @@ -7,7 +7,7 @@ INCLUDES += -Iinclude/bl32/tsp ifeq (${SPMC_AT_EL3},1) - BL32_SOURCES += bl32/tsp/tsp_ffa_main.c \ + BL32_SOURCES += bl32/tsp/tsp_ffa_main.c \ bl32/tsp/ffa_helpers.c else BL32_SOURCES += bl32/tsp/tsp_main.c @@ -19,14 +19,15 @@ BL32_SOURCES += bl32/tsp/aarch64/tsp_entrypoint.S \ bl32/tsp/tsp_interrupt.c \ bl32/tsp/tsp_timer.c \ bl32/tsp/tsp_common.c \ + bl32/tsp/tsp_context.c \ common/aarch64/early_exceptions.S \ lib/locks/exclusive/aarch64/spinlock.S BL32_DEFAULT_LINKER_SCRIPT_SOURCE := bl32/tsp/tsp.ld.S -ifneq ($(findstring gcc,$(notdir $(LD))),) +ifeq ($($(ARCH)-ld-id),gnu-gcc) BL32_LDFLAGS += -Wl,--sort-section=alignment -else ifneq ($(findstring ld,$(notdir $(LD))),) +else ifneq ($(filter llvm-lld gnu-ld,$($(ARCH)-ld-id)),) BL32_LDFLAGS += --sort-section=alignment endif diff --git a/bl32/tsp/tsp_common.c b/bl32/tsp/tsp_common.c index 908b4ff0..3a6c9d97 100644 --- a/bl32/tsp/tsp_common.c +++ b/bl32/tsp/tsp_common.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2022-2024, Arm Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -66,6 +66,9 @@ smc_args_t *set_smc_args(uint64_t arg0, ******************************************************************************/ void tsp_setup(void) { + /* Enable early console if EARLY_CONSOLE flag is enabled */ + plat_setup_early_console(); + /* Perform early platform-specific setup. */ tsp_early_platform_setup(); diff --git a/bl32/tsp/tsp_context.c b/bl32/tsp/tsp_context.c new file mode 100644 index 00000000..6307f72c --- /dev/null +++ b/bl32/tsp/tsp_context.c @@ -0,0 +1,143 @@ +/* + * Copyright (c) 2024, Arm Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <arch_features.h> +#include <arch_helpers.h> +#include <bl32/tsp/tsp_el1_context.h> +#include <common/debug.h> + +#define DUMMY_CTX_VALUE ULL(0xffffffff) +#define DUMMY_CTX_TCR_VALUE ULL(0xffff0000) +#define DUMMY_CTX_TRF_VALUE ULL(0xf) +#define DUMMY_CTX_GCS_VALUE ULL(0xffff0000) +#define DEFAULT_CTX_VALUE ULL(0x0) + +/** + * ------------------------------------------------------- + * Private Helper functions required to access and modify + * EL1 context registers at S-EL1. + * ------------------------------------------------------- + */ +static void modify_el1_common_regs(uint64_t cm_value) +{ + /** + * NOTE: Few EL1 registers "SCTLR_EL1, SPSR_EL1, ELR_EL1" are + * left out consciously as those are important registers for + * execution in each world and overwriting them with dummy value + * would cause unintended crash while executing the test. + */ + write_tcr_el1(cm_value); + write_cpacr_el1(cm_value); + write_csselr_el1(cm_value); + write_esr_el1(cm_value); + write_ttbr0_el1(cm_value); + write_ttbr1_el1(cm_value); + write_mair_el1(cm_value); + write_amair_el1(cm_value); + write_actlr_el1(cm_value); + write_tpidr_el1(cm_value); + write_tpidr_el0(cm_value); + write_tpidrro_el0(cm_value); + write_par_el1(cm_value); + write_far_el1(cm_value); + write_afsr0_el1(cm_value); + write_afsr1_el1(cm_value); + write_contextidr_el1(cm_value); + write_vbar_el1(cm_value); + write_mdccint_el1(cm_value); + write_mdscr_el1(cm_value); +} + +static void modify_el1_mte2_regs(uint64_t mte_value) +{ + if (is_feat_mte2_supported()) { + write_tfsre0_el1(mte_value); + write_tfsr_el1(mte_value); + write_rgsr_el1(mte_value); + write_gcr_el1(mte_value); + } +} + +static void modify_el1_ras_regs(uint64_t ras_value) +{ + if (is_feat_ras_supported()) { + write_disr_el1(ras_value); + } +} + +static void modify_el1_s1pie_regs(uint64_t s1pie_value) +{ + if (is_feat_s1pie_supported()) { + write_pire0_el1(s1pie_value); + write_pir_el1(s1pie_value); + } +} + +static void modify_el1_s1poe_regs(uint64_t s1poe_value) +{ + if (is_feat_s1poe_supported()) { + write_por_el1(s1poe_value); + } +} + +static void modify_el1_s2poe_regs(uint64_t s2poe_value) +{ + if (is_feat_s2poe_supported()) { + write_s2por_el1(s2poe_value); + } +} + +static void modify_el1_tcr2_regs(uint64_t tcr_value) +{ + if (is_feat_tcr2_supported()) { + write_tcr2_el1(tcr_value & DUMMY_CTX_TCR_VALUE); + } +} + +static void modify_el1_trf_regs(uint64_t trf_value) +{ + if (is_feat_trf_supported()) { + write_trfcr_el1(trf_value & DUMMY_CTX_TRF_VALUE); + } +} + +static void modify_el1_gcs_regs(uint64_t gcs_value) +{ + if (is_feat_gcs_supported()) { + write_gcscr_el1(gcs_value & DUMMY_CTX_GCS_VALUE); + write_gcscre0_el1(gcs_value & DUMMY_CTX_GCS_VALUE); + write_gcspr_el1(gcs_value & DUMMY_CTX_GCS_VALUE); + write_gcspr_el0(gcs_value & DUMMY_CTX_GCS_VALUE); + } +} + +/** + * ----------------------------------------------------- + * Public API, to modify/restore EL1 ctx registers: + * ----------------------------------------------------- + */ +void modify_el1_ctx_regs(const bool modify_option) +{ + uint64_t mask; + + if (modify_option == TSP_CORRUPT_EL1_REGS) { + VERBOSE("TSP(S-EL1): Corrupt EL1 Registers with Dummy values\n"); + mask = DUMMY_CTX_VALUE; + } else { + VERBOSE("TSP(S-EL1): Restore EL1 Registers with Default values\n"); + mask = DEFAULT_CTX_VALUE; + } + + modify_el1_common_regs(mask); + modify_el1_mte2_regs(mask); + modify_el1_ras_regs(mask); + modify_el1_s1pie_regs(mask); + modify_el1_s1poe_regs(mask); + modify_el1_s2poe_regs(mask); + modify_el1_tcr2_regs(mask); + modify_el1_trf_regs(mask); + modify_el1_gcs_regs(mask); +} diff --git a/bl32/tsp/tsp_ffa_main.c b/bl32/tsp/tsp_ffa_main.c index 1c8c68f0..8273060b 100644 --- a/bl32/tsp/tsp_ffa_main.c +++ b/bl32/tsp/tsp_ffa_main.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013-2022, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2013-2024, Arm Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -14,6 +14,7 @@ #include <arch_helpers.h> #include <bl32/tsp/tsp.h> #include <common/bl_common.h> +#include <common/build_message.h> #include <common/debug.h> #include "ffa_helpers.h" #include <lib/psci/psci.h> @@ -554,7 +555,7 @@ uint64_t tsp_main(void) { smc_args_t smc_args = {0}; - NOTICE("TSP: %s\n", version_string); + NOTICE("TSP: %s\n", build_version_string); NOTICE("TSP: %s\n", build_message); INFO("TSP: Total memory base : 0x%lx\n", (unsigned long) BL32_BASE); INFO("TSP: Total memory size : 0x%lx bytes\n", BL32_TOTAL_SIZE); diff --git a/bl32/tsp/tsp_main.c b/bl32/tsp/tsp_main.c index 1ab2260a..8c6b2edf 100644 --- a/bl32/tsp/tsp_main.c +++ b/bl32/tsp/tsp_main.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013-2022, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2013-2024, Arm Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -11,7 +11,9 @@ #include <arch_features.h> #include <arch_helpers.h> #include <bl32/tsp/tsp.h> +#include <bl32/tsp/tsp_el1_context.h> #include <common/bl_common.h> +#include <common/build_message.h> #include <common/debug.h> #include <lib/spinlock.h> #include <plat/common/platform.h> @@ -27,7 +29,7 @@ ******************************************************************************/ uint64_t tsp_main(void) { - NOTICE("TSP: %s\n", version_string); + NOTICE("TSP: %s\n", build_version_string); NOTICE("TSP: %s\n", build_message); INFO("TSP: Total memory base : 0x%lx\n", (unsigned long) BL32_BASE); INFO("TSP: Total memory size : 0x%lx bytes\n", BL32_TOTAL_SIZE); @@ -238,13 +240,13 @@ smc_args_t *tsp_smc_handler(uint64_t func, service_arg0 = (uint64_t)service_args; service_arg1 = (uint64_t)(service_args >> 64U); -#if CTX_INCLUDE_MTE_REGS /* - * Write a dummy value to an MTE register, to simulate usage in the + * Write a dummy value to an MTE2 register, to simulate usage in the * secure world */ - write_gcr_el1(0x99); -#endif + if (is_feat_mte2_supported()) { + write_gcr_el1(0x99); + } /* Determine the function to perform based on the function ID */ switch (TSP_BARE_FID(func)) { @@ -277,6 +279,17 @@ smc_args_t *tsp_smc_handler(uint64_t func, /* Toggle the dit bit */ write_dit(service_arg0 != 0U ? 0 : DIT_BIT); break; + case TSP_MODIFY_EL1_CTX: + /* + * Write dummy values to EL1 context registers, to simulate + * their usage in the secure world. + */ + if (arg1 == TSP_CORRUPT_EL1_REGS) { + modify_el1_ctx_regs(TSP_CORRUPT_EL1_REGS); + } else { + modify_el1_ctx_regs(TSP_RESTORE_EL1_REGS); + } + break; default: break; } diff --git a/changelog.yaml b/changelog.yaml index 1467ab4c..9d1c3a98 100644 --- a/changelog.yaml +++ b/changelog.yaml @@ -1,5 +1,5 @@ # -# Copyright (c) 2021-2023, Arm Limited. All rights reserved. +# Copyright (c) 2021-2024, Arm Limited. All rights reserved. # # SPDX-License-Identifier: BSD-3-Clause # @@ -104,9 +104,15 @@ subsections: - title: Extended Translation Control Register (FEAT_TCR2). scope: tcr2 + - title: Fine-grained Traps 2 (FEAT_FGT2). + scope: fgt2 + - title: CPU feature / ID register handling in general scope: cpufeat + - title: Debug Extension (FEAT_Debugv8p9) + scope: debugv8p9 + - title: Guarded Control Stack (FEAT_GCS) scope: gcs @@ -116,8 +122,11 @@ subsections: - title: Memory Partitioning and Monitoring (MPAM) Extension (FEAT_MPAM) scope: mpam - - title: Memory Tagging Extension - scope: mte + - title: Memory Tagging Extension2 + scope: mte2 + + deprecated: + - mte - title: Pointer Authentication Extension scope: pauth @@ -149,11 +158,27 @@ subsections: - title: Self-hosted Trace Extensions (FEAT_TRF) scope: trf + - title: DynamIQ Shared Unit (DSU) + scope: dsu + + - title: Extension to SCTLR_ELx (FEAT_SCTLR2) + scope: sctlr2 + + deprecated: + - feat_sctlr2 + + - title: 128-bit Translation Tables (FEAT_D128) + scope: d128 + + - title: Translation Hardening Extension (FEAT_THE) + scope: the + - title: Platforms scope: platforms deprecated: - plat/common + - plat subsections: - title: Allwinner @@ -169,6 +194,9 @@ subsections: - plat/arm subsections: + - title: Common + scope: common + - title: A5DS scope: a5ds @@ -212,25 +240,37 @@ subsections: - title: N1SDP scope: n1sdp - - title: RD - scope: rd + - title: Neoverse-RD + scope: neoverse-rd subsections: - - title: RD-N1 Edge + - title: SGI-575 + scope: sgi575 + + - title: RD-E1-Edge + scope: rde1edge + + - title: RD-N1-Edge scope: rdn1edge + - title: RD-V1 + scope: rdv1 + + - title: RD-V1-MC + scope: rdv1mc + - title: RD-N2 scope: rdn2 + - title: RD-V3 + scope: rdv3 + deprecated: - board/rdn2 - - - title: SGI - scope: sgi + - rdfremont deprecated: - - plat/sgi - - plat/arm/sgi + - neoverse - title: TC scope: tc @@ -248,6 +288,16 @@ subsections: - title: Corstone-1000 scope: corstone-1000 + deprecated: + - corstone1000 + + - title: Automotive RD + scope: automotive_rd + + subsections: + - title: RD-1 AE + scope: rd1ae + - title: Aspeed scope: aspeed @@ -271,6 +321,9 @@ subsections: - title: HiKey960 scope: hikey960 + - title: Poplar + scope: poplar + - title: Intel scope: intel @@ -413,6 +466,9 @@ subsections: - title: i.MX 8 scope: imx8 + - title: i.MX 8ULP + scope: imx8ulp + - title: i.MX 9 scope: imx9 @@ -496,6 +552,13 @@ subsections: - title: LS1088AQDS scope: ls1088aqds + - title: S32G274A + scope: s32g274a + + subsections: + - title: S32G274ARDB + scope: s32g274ardb + - title: QEMU scope: qemu @@ -541,6 +604,9 @@ subsections: - title: Raspberry Pi 4 scope: rpi4 + - title: Raspberry Pi 5 + scope: rpi5 + - title: Renesas scope: renesas @@ -569,6 +635,12 @@ subsections: - rockchip/rk3399 - rk3399/suspend + - title: RK3328 + scope: rk3328 + + - title: RK3588 + scope: rk3588 + - title: Socionext scope: socionext @@ -602,6 +674,10 @@ subsections: - title: STM32MP2 scope: stm32mp2 + subsections: + - title: STM32MP25 + scope: stm32mp25 + - title: Texas Instruments scope: ti @@ -630,12 +706,11 @@ subsections: - plat/xilinx/versal - plat/versal - subsections: - - title: Versal NET - scope: versal-net + - title: Versal NET + scope: versal-net - deprecated: - - versal_net + deprecated: + - versal_net - title: ZynqMP scope: zynqmp @@ -644,6 +719,13 @@ subsections: - plat/zynqmp - plat/xilinx/zynqmp + - title: AMD + scope: amd + + subsections: + - title: Versal Gen 2 + scope: versal2 + - title: Nuvoton scope: nuvoton @@ -677,6 +759,9 @@ subsections: - title: Services scope: services + deprecated: + - std_svc + subsections: - title: FF-A scope: ff-a @@ -731,6 +816,31 @@ subsections: deprecated: - errata_abi + - title: ChromeOS + scope: cros + + - title: Secure Payload Dispatcher + scope: spd + + subsections: + - title: OP-TEE + scope: optee + + deprecated: + - lib/optee + + - title: ProvenCore + scope: pncd + + - title: Trusted Little Kernel + scope: tlkd + + - title: Trusty + scope: trusty + + - title: TSP + scope: tspd + - title: Libraries scope: lib @@ -756,21 +866,21 @@ subsections: - title: RAS scope: ras + - title: SIMD + scope: simd + - title: FCONF scope: fconf - title: MPMM scope: mpmm - - title: OP-TEE - scope: optee - - deprecated: - - lib/optee - - title: PSCI scope: psci + - title: ROMlib + scope: romlib + - title: GPT scope: gpt @@ -795,6 +905,9 @@ subsections: deprecated: - lib/psa + - title: DICE Protection Environment + scope: dice + - title: Context Management scope: context-mgmt @@ -807,6 +920,9 @@ subsections: - title: Firmware Handoff scope: handoff + - title: Exception Handling Framework (EHF) + scope: ehf + - title: Drivers subsections: @@ -832,6 +948,9 @@ subsections: - title: Console scope: console + - title: Delay Timer + scope: delay-timer + - title: Generic Clock scope: clk @@ -937,11 +1056,12 @@ subsections: deprecated: - drivers/arm/mhu - - title: RSS - scope: rss + - title: RSE + scope: rse deprecated: - drivers/arm/rss + - rss - title: TZC scope: tzc @@ -1093,6 +1213,9 @@ subsections: - title: TRDC scope: imx-trdc + - title: Clock + scope: nxp-clk + - title: Renesas scope: renesas-drivers @@ -1260,6 +1383,10 @@ subsections: - title: STM32MP2 scope: stm32mp2-fdts + subsections: + - title: STM32MP25 + scope: stm32mp25-fdts + - title: PIE scope: pie @@ -1340,6 +1467,7 @@ subsections: - git-hooks - title: Tools + scope: tools subsections: - title: STM32 Image @@ -1360,12 +1488,34 @@ subsections: - title: Certificate Creation Tool scope: cert-create + - title: Firmware Encryption Tool + scope: encrypt-fw + - title: Memory Mapping Tool scope: memmap deprecated: - cert_create + - title: Marvell Tools + scope: marvell-tools + + - title: Renesas Tools + scope: renesas-tools + + subsections: + - title: R-Car Layout Tool + scope: rcar-layout + + - title: R/ZG Layout Tool + scope: rzg-layout + + - title: Transfer List Compiler + scope: tlc + + - title: Chain of Trust device tree to C source file + scope: cot-dt2c + - title: Dependencies scope: deps diff --git a/common/bl_common.c b/common/bl_common.c index 8fce02fb..2c452aa8 100644 --- a/common/bl_common.c +++ b/common/bl_common.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013-2022, Arm Limited and Contributors. All rights reserved. + * Copyright (c) 2013-2024, Arm Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -12,6 +12,7 @@ #include <arch_features.h> #include <arch_helpers.h> #include <common/bl_common.h> +#include <common/build_message.h> #include <common/debug.h> #include <drivers/auth/auth_mod.h> #include <drivers/io/io_storage.h> @@ -149,8 +150,7 @@ exit: * of trust. */ static int load_auth_image_recursive(unsigned int image_id, - image_info_t *image_data, - int is_parent_image) + image_info_t *image_data) { int rc; unsigned int parent_id; @@ -158,7 +158,7 @@ static int load_auth_image_recursive(unsigned int image_id, /* Use recursion to authenticate parent images */ rc = auth_mod_get_parent_id(image_id, &parent_id); if (rc == 0) { - rc = load_auth_image_recursive(parent_id, image_data, 1); + rc = load_auth_image_recursive(parent_id, image_data); if (rc != 0) { return rc; } @@ -192,7 +192,7 @@ static int load_auth_image_internal(unsigned int image_id, { #if TRUSTED_BOARD_BOOT if (dyn_is_auth_disabled() == 0) { - return load_auth_image_recursive(image_id, image_data, 0); + return load_auth_image_recursive(image_id, image_data); } #endif @@ -210,18 +210,18 @@ int load_auth_image(unsigned int image_id, image_info_t *image_data) { int err; -/* - * All firmware banks should be part of the same non-volatile storage as per - * PSA FWU specification, hence don't check for any alternate boot source - * when PSA FWU is enabled. - */ -#if PSA_FWU_SUPPORT - err = load_auth_image_internal(image_id, image_data); -#else - do { + if ((plat_try_img_ops == NULL) || (plat_try_img_ops->next_instance == NULL)) { err = load_auth_image_internal(image_id, image_data); - } while ((err != 0) && (plat_try_next_boot_source() != 0)); -#endif /* PSA_FWU_SUPPORT */ + } else { + do { + err = load_auth_image_internal(image_id, image_data); + if (err != 0) { + if (plat_try_img_ops->next_instance(image_id) != 0) { + return err; + } + } + } while (err != 0); + } if (err == 0) { /* @@ -275,6 +275,5 @@ void print_entry_point_info(const entry_point_info_t *ep_info) */ const char *get_version(void) { - extern const char version[]; - return version; + return build_version; } diff --git a/common/fdt_fixup.c b/common/fdt_fixup.c index 1bad74fe..59b75435 100644 --- a/common/fdt_fixup.c +++ b/common/fdt_fixup.c @@ -197,6 +197,7 @@ int fdt_add_reserved_memory(void *dtb, const char *node_name, uintptr_t base, size_t size) { int offs = fdt_path_offset(dtb, "/reserved-memory"); + int node; uint32_t addresses[4]; int ac, sc; unsigned int idx = 0; @@ -213,6 +214,24 @@ int fdt_add_reserved_memory(void *dtb, const char *node_name, fdt_setprop(dtb, offs, "ranges", NULL, 0); } + /* Check for existing regions */ + fdt_for_each_subnode(node, dtb, offs) { + uintptr_t c_base; + size_t c_size; + int ret; + + ret = fdt_get_reg_props_by_index(dtb, node, 0, &c_base, &c_size); + /* Ignore illegal subnodes */ + if (ret != 0) { + continue; + } + + /* existing region entirely contains the new region */ + if (base >= c_base && (base + size) <= (c_base + c_size)) { + return 0; + } + } + if (ac > 1) { addresses[idx] = cpu_to_fdt32(HIGH_BITS(base)); idx++; diff --git a/common/fdt_wrappers.c b/common/fdt_wrappers.c index 783b660e..b213ffa4 100644 --- a/common/fdt_wrappers.c +++ b/common/fdt_wrappers.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018-2023, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2018-2024, Arm Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -88,6 +88,19 @@ int fdt_read_uint64(const void *dtb, int node, const char *prop_name, return 0; } +uint64_t fdt_read_uint64_default(const void *dtb, int node, + const char *prop_name, uint64_t dflt_value) +{ + uint64_t ret = dflt_value; + int err = fdt_read_uint64(dtb, node, prop_name, &ret); + + if (err < 0) { + return dflt_value; + } + + return ret; +} + /* * Read bytes from a given property of the given node. Any number of * bytes of the property can be read. The fdt pointer is updated. diff --git a/common/feat_detect.c b/common/feat_detect.c index be22c6ed..8c03ab82 100644 --- a/common/feat_detect.c +++ b/common/feat_detect.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022-2023, Arm Limited and Contributors. All rights reserved. + * Copyright (c) 2022-2024, Arm Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -70,47 +70,214 @@ static void read_feat_pauth(void) #endif } -/************************************************ - * Feature : FEAT_MTE (Memory Tagging Extension) - ***********************************************/ -static void read_feat_mte(void) +static unsigned int read_feat_rng_trap_id_field(void) { -#if (CTX_INCLUDE_MTE_REGS == FEAT_STATE_ALWAYS) - unsigned int mte = get_armv8_5_mte_support(); + return ISOLATE_FIELD(read_id_aa64pfr1_el1(), ID_AA64PFR1_EL1_RNDR_TRAP_SHIFT, + ID_AA64PFR1_EL1_RNDR_TRAP_MASK); +} - feat_detect_panic((mte != MTE_UNIMPLEMENTED), "MTE"); -#endif +static unsigned int read_feat_bti_id_field(void) +{ + return ISOLATE_FIELD(read_id_aa64pfr1_el1(), ID_AA64PFR1_EL1_BT_SHIFT, + ID_AA64PFR1_EL1_BT_MASK); } -/**************************************************** - * Feature : FEAT_BTI (Branch Target Identification) - ***************************************************/ -static void read_feat_bti(void) +static unsigned int read_feat_sb_id_field(void) { -#if (ENABLE_BTI == FEAT_STATE_ALWAYS) - feat_detect_panic(is_armv8_5_bti_present(), "BTI"); -#endif + return ISOLATE_FIELD(read_id_aa64isar1_el1(), ID_AA64ISAR1_SB_SHIFT, + ID_AA64ISAR1_SB_MASK); } -/************************************************** - * Feature : FEAT_RME (Realm Management Extension) - *************************************************/ -static void read_feat_rme(void) +static unsigned int read_feat_csv2_id_field(void) { -#if (ENABLE_RME == FEAT_STATE_ALWAYS) - feat_detect_panic((get_armv9_2_feat_rme_support() != - ID_AA64PFR0_FEAT_RME_NOT_SUPPORTED), "RME"); -#endif + return ISOLATE_FIELD(read_id_aa64pfr0_el1(), ID_AA64PFR0_CSV2_SHIFT, + ID_AA64PFR0_CSV2_MASK); } -/****************************************************************** - * Feature : FEAT_RNG_TRAP (Trapping support for RNDR/RNDRRS) - *****************************************************************/ -static void read_feat_rng_trap(void) +static unsigned int read_feat_debugv8p9_id_field(void) { -#if (ENABLE_FEAT_RNG_TRAP == FEAT_STATE_ALWAYS) - feat_detect_panic(is_feat_rng_trap_present(), "RNG_TRAP"); -#endif + return ISOLATE_FIELD(read_id_aa64dfr0_el1(), ID_AA64DFR0_DEBUGVER_SHIFT, + ID_AA64DFR0_DEBUGVER_MASK); +} + +static unsigned int read_feat_pmuv3_id_field(void) +{ + return ISOLATE_FIELD(read_id_aa64dfr0_el1(), ID_AA64DFR0_PMUVER_SHIFT, + ID_AA64DFR0_PMUVER_MASK); +} + +static unsigned int read_feat_vhe_id_field(void) +{ + return ISOLATE_FIELD(read_id_aa64mmfr1_el1(), ID_AA64MMFR1_EL1_VHE_SHIFT, + ID_AA64MMFR1_EL1_VHE_MASK); +} + +static unsigned int read_feat_sve_id_field(void) +{ + return ISOLATE_FIELD(read_id_aa64pfr0_el1(), ID_AA64PFR0_SVE_SHIFT, + ID_AA64PFR0_SVE_MASK); +} + +static unsigned int read_feat_ras_id_field(void) +{ + return ISOLATE_FIELD(read_id_aa64pfr0_el1(), ID_AA64PFR0_RAS_SHIFT, + ID_AA64PFR0_RAS_MASK); +} + +static unsigned int read_feat_dit_id_field(void) +{ + return ISOLATE_FIELD(read_id_aa64pfr0_el1(), ID_AA64PFR0_DIT_SHIFT, + ID_AA64PFR0_DIT_MASK); +} + +static unsigned int read_feat_amu_id_field(void) +{ + return ISOLATE_FIELD(read_id_aa64pfr0_el1(), ID_AA64PFR0_AMU_SHIFT, + ID_AA64PFR0_AMU_MASK); +} + +static unsigned int read_feat_mpam_version(void) +{ + return (unsigned int)((((read_id_aa64pfr0_el1() >> + ID_AA64PFR0_MPAM_SHIFT) & ID_AA64PFR0_MPAM_MASK) << 4) | + ((read_id_aa64pfr1_el1() >> + ID_AA64PFR1_MPAM_FRAC_SHIFT) & ID_AA64PFR1_MPAM_FRAC_MASK)); +} + +static unsigned int read_feat_nv_id_field(void) +{ + return ISOLATE_FIELD(read_id_aa64mmfr2_el1(), ID_AA64MMFR2_EL1_NV_SHIFT, + ID_AA64MMFR2_EL1_NV_MASK); +} + +static unsigned int read_feat_sel2_id_field(void) +{ + return ISOLATE_FIELD(read_id_aa64pfr0_el1(), ID_AA64PFR0_SEL2_SHIFT, + ID_AA64PFR0_SEL2_MASK); +} + +static unsigned int read_feat_trf_id_field(void) +{ + return ISOLATE_FIELD(read_id_aa64dfr0_el1(), ID_AA64DFR0_TRACEFILT_SHIFT, + ID_AA64DFR0_TRACEFILT_MASK); +} +static unsigned int get_armv8_5_mte_support(void) +{ + return ISOLATE_FIELD(read_id_aa64pfr1_el1(), ID_AA64PFR1_EL1_MTE_SHIFT, + ID_AA64PFR1_EL1_MTE_MASK); +} +static unsigned int read_feat_rng_id_field(void) +{ + return ISOLATE_FIELD(read_id_aa64isar0_el1(), ID_AA64ISAR0_RNDR_SHIFT, + ID_AA64ISAR0_RNDR_MASK); +} +static unsigned int read_feat_fgt_id_field(void) +{ + return ISOLATE_FIELD(read_id_aa64mmfr0_el1(), ID_AA64MMFR0_EL1_FGT_SHIFT, + ID_AA64MMFR0_EL1_FGT_MASK); +} +static unsigned int read_feat_ecv_id_field(void) +{ + return ISOLATE_FIELD(read_id_aa64mmfr0_el1(), ID_AA64MMFR0_EL1_ECV_SHIFT, + ID_AA64MMFR0_EL1_ECV_MASK); +} +static unsigned int read_feat_twed_id_field(void) +{ + return ISOLATE_FIELD(read_id_aa64mmfr1_el1(), ID_AA64MMFR1_EL1_TWED_SHIFT, + ID_AA64MMFR1_EL1_TWED_MASK); +} + +static unsigned int read_feat_hcx_id_field(void) +{ + return ISOLATE_FIELD(read_id_aa64mmfr1_el1(), ID_AA64MMFR1_EL1_HCX_SHIFT, + ID_AA64MMFR1_EL1_HCX_MASK); +} +static unsigned int read_feat_ls64_id_field(void) +{ + return ISOLATE_FIELD(read_id_aa64isar1_el1(), ID_AA64ISAR1_LS64_SHIFT, + ID_AA64ISAR1_LS64_MASK); +} +static unsigned int read_feat_tcr2_id_field(void) +{ + return ISOLATE_FIELD(read_id_aa64mmfr3_el1(), ID_AA64MMFR3_EL1_TCRX_SHIFT, + ID_AA64MMFR3_EL1_TCRX_MASK); +} +static unsigned int read_feat_s2pie_id_field(void) +{ + return ISOLATE_FIELD(read_id_aa64mmfr3_el1(), ID_AA64MMFR3_EL1_S2PIE_SHIFT, + ID_AA64MMFR3_EL1_S2PIE_MASK); +} +static unsigned int read_feat_s1pie_id_field(void) +{ + return ISOLATE_FIELD(read_id_aa64mmfr3_el1(), ID_AA64MMFR3_EL1_S1PIE_SHIFT, + ID_AA64MMFR3_EL1_S1PIE_MASK); +} +static unsigned int read_feat_s2poe_id_field(void) +{ + return ISOLATE_FIELD(read_id_aa64mmfr3_el1(), ID_AA64MMFR3_EL1_S2POE_SHIFT, + ID_AA64MMFR3_EL1_S2POE_MASK); +} +static unsigned int read_feat_s1poe_id_field(void) +{ + return ISOLATE_FIELD(read_id_aa64mmfr3_el1(), ID_AA64MMFR3_EL1_S1POE_SHIFT, + ID_AA64MMFR3_EL1_S1POE_MASK); +} +static unsigned int read_feat_brbe_id_field(void) +{ + return ISOLATE_FIELD(read_id_aa64dfr0_el1(), ID_AA64DFR0_BRBE_SHIFT, + ID_AA64DFR0_BRBE_MASK); +} +static unsigned int read_feat_trbe_id_field(void) +{ + return ISOLATE_FIELD(read_id_aa64dfr0_el1(), ID_AA64DFR0_TRACEBUFFER_SHIFT, + ID_AA64DFR0_TRACEBUFFER_MASK); +} +static unsigned int read_feat_sme_id_field(void) +{ + return ISOLATE_FIELD(read_id_aa64pfr1_el1(), ID_AA64PFR1_EL1_SME_SHIFT, + ID_AA64PFR1_EL1_SME_MASK); +} +static unsigned int read_feat_gcs_id_field(void) +{ + return ISOLATE_FIELD(read_id_aa64pfr1_el1(), ID_AA64PFR1_EL1_GCS_SHIFT, + ID_AA64PFR1_EL1_GCS_MASK); +} + +static unsigned int read_feat_rme_id_field(void) +{ + return ISOLATE_FIELD(read_id_aa64pfr0_el1(), ID_AA64PFR0_FEAT_RME_SHIFT, + ID_AA64PFR0_FEAT_RME_MASK); +} + +static unsigned int read_feat_pan_id_field(void) +{ + return ISOLATE_FIELD(read_id_aa64mmfr1_el1(), ID_AA64MMFR1_EL1_PAN_SHIFT, + ID_AA64MMFR1_EL1_PAN_MASK); +} + +static unsigned int read_feat_mtpmu_id_field(void) +{ + return ISOLATE_FIELD(read_id_aa64dfr0_el1(), ID_AA64DFR0_MTPMU_SHIFT, + ID_AA64DFR0_MTPMU_MASK); + +} + +static unsigned int read_feat_the_id_field(void) +{ + return ISOLATE_FIELD(read_id_aa64pfr1_el1(), ID_AA64PFR1_EL1_THE_SHIFT, + ID_AA64PFR1_EL1_THE_MASK); +} + +static unsigned int read_feat_sctlr2_id_field(void) +{ + return ISOLATE_FIELD(read_id_aa64mmfr3_el1(), ID_AA64MMFR3_EL1_SCTLR2_SHIFT, + ID_AA64MMFR3_EL1_SCTLR2_MASK); +} + +static unsigned int read_feat_d128_id_field(void) +{ + return ISOLATE_FIELD(read_id_aa64mmfr3_el1(), ID_AA64MMFR3_EL1_D128_SHIFT, + ID_AA64MMFR3_EL1_D128_MASK); } /*********************************************************************************** @@ -151,7 +318,7 @@ void detect_arch_features(void) * revisions so that we catch them as they come along */ check_feature(FEAT_STATE_ALWAYS, read_feat_pmuv3_id_field(), - "PMUv3", 1, ID_AA64DFR0_PMUVER_PMUV3P7); + "PMUv3", 1, ID_AA64DFR0_PMUVER_PMUV3P8); /* v8.1 features */ check_feature(ENABLE_FEAT_PAN, read_feat_pan_id_field(), "PAN", 1, 3); @@ -163,6 +330,7 @@ void detect_arch_features(void) check_feature(ENABLE_FEAT_RAS, read_feat_ras_id_field(), "RAS", 1, 2); /* v8.3 features */ + /* TODO: Pauth yet to convert to tri-state feat detect logic */ read_feat_pauth(); /* v8.4 features */ @@ -179,15 +347,18 @@ void detect_arch_features(void) "TRF", 1, 1); /* v8.5 features */ - read_feat_mte(); + check_feature(ENABLE_FEAT_MTE2, get_armv8_5_mte_support(), "MTE2", + MTE_IMPLEMENTED_ELX, MTE_IMPLEMENTED_ASY); check_feature(ENABLE_FEAT_RNG, read_feat_rng_id_field(), "RNG", 1, 1); - read_feat_bti(); - read_feat_rng_trap(); + check_feature(ENABLE_BTI, read_feat_bti_id_field(), "BTI", 1, 1); + check_feature(ENABLE_FEAT_RNG_TRAP, read_feat_rng_trap_id_field(), + "RNG_TRAP", 1, 1); /* v8.6 features */ check_feature(ENABLE_FEAT_AMUv1p1, read_feat_amu_id_field(), "AMUv1p1", 2, 2); - check_feature(ENABLE_FEAT_FGT, read_feat_fgt_id_field(), "FGT", 1, 1); + check_feature(ENABLE_FEAT_FGT, read_feat_fgt_id_field(), "FGT", 1, 2); + check_feature(ENABLE_FEAT_FGT2, read_feat_fgt_id_field(), "FGT2", 2, 2); check_feature(ENABLE_FEAT_ECV, read_feat_ecv_id_field(), "ECV", 1, 2); check_feature(ENABLE_FEAT_TWED, read_feat_twed_id_field(), "TWED", 1, 1); @@ -201,6 +372,7 @@ void detect_arch_features(void) /* v8.7 features */ check_feature(ENABLE_FEAT_HCX, read_feat_hcx_id_field(), "HCX", 1, 1); + check_feature(ENABLE_FEAT_LS64_ACCDATA, read_feat_ls64_id_field(), "LS64", 1, 3); /* v8.9 features */ check_feature(ENABLE_FEAT_TCR2, read_feat_tcr2_id_field(), @@ -213,8 +385,14 @@ void detect_arch_features(void) "S2POE", 1, 1); check_feature(ENABLE_FEAT_S1POE, read_feat_s1poe_id_field(), "S1POE", 1, 1); - check_feature(ENABLE_FEAT_MTE_PERM, read_feat_mte_perm_id_field(), - "MTE_PERM", 1, 1); + check_feature(ENABLE_FEAT_CSV2_3, read_feat_csv2_id_field(), + "CSV2_3", 3, 3); + check_feature(ENABLE_FEAT_DEBUGV8P9, read_feat_debugv8p9_id_field(), + "DEBUGV8P9", 11, 11); + check_feature(ENABLE_FEAT_THE, read_feat_the_id_field(), + "THE", 1, 1); + check_feature(ENABLE_FEAT_SCTLR2, read_feat_sctlr2_id_field(), + "SCTLR2", 1, 1); /* v9.0 features */ check_feature(ENABLE_BRBE_FOR_NS, read_feat_brbe_id_field(), @@ -228,10 +406,13 @@ void detect_arch_features(void) check_feature(ENABLE_SME2_FOR_NS, read_feat_sme_id_field(), "SME2", 2, 2); + /* v9.3 features */ + check_feature(ENABLE_FEAT_D128, read_feat_d128_id_field(), + "D128", 1, 1); + /* v9.4 features */ check_feature(ENABLE_FEAT_GCS, read_feat_gcs_id_field(), "GCS", 1, 1); - - read_feat_rme(); + check_feature(ENABLE_RME, read_feat_rme_id_field(), "RME", 1, 1); if (tainted) { panic(); diff --git a/docs/Makefile b/docs/Makefile index 5bc24db7..68c09581 100644 --- a/docs/Makefile +++ b/docs/Makefile @@ -1,11 +1,13 @@ # -# Copyright (c) 2019-2023, ARM Limited. All rights reserved. +# Copyright (c) 2019-2024, Arm Limited. All rights reserved. # # SPDX-License-Identifier: BSD-3-Clause # # Minimal makefile for Sphinx documentation # +include ../make_helpers/common.mk + # You can set these variables from the command line. SPHINXOPTS = -W SPHINXBUILD = sphinx-build @@ -13,20 +15,14 @@ SPHINXPROJ = TrustedFirmware-A SOURCEDIR = . BUILDDIR = build -V ?= 0 -ifeq ($(V),0) - Q := @ -else - Q := -endif - # Put it first so that "make" without argument is like "make help". help: - ${Q}$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) + $(q)$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) .PHONY: help Makefile # Catch-all target: route all unknown targets to Sphinx using the new # "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). -%: Makefile - ${Q}$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) +.DEFAULT: Makefile + $(if $(host-poetry),$(q)poetry -q install --with=docs) + $(q)$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) diff --git a/docs/about/contact.rst b/docs/about/contact.rst index 4f482bd3..bb73dfe0 100644 --- a/docs/about/contact.rst +++ b/docs/about/contact.rst @@ -36,9 +36,8 @@ topic within the community. More details can be found `here`_. Issue Tracker ^^^^^^^^^^^^^ -Bug reports may be filed on the `issue tracker`_ on the TrustedFirmware.org -website. Using this tracker gives everyone visibility of the known issues in -TF-A. +Bug reports may be filed on the `issue tracker`_ on Github. Using this tracker +gives everyone visibility of the known issues in TF-A. Arm Licensees ^^^^^^^^^^^^^ @@ -46,7 +45,7 @@ Arm Licensees Arm licensees have an additional support conduit - they may contact Arm directly via their partner managers. -.. _`issue tracker`: https://developer.trustedfirmware.org +.. _`issue tracker`: https://github.com/TrustedFirmware-A/trusted-firmware-a/issues .. _`TF-A development`: https://lists.trustedfirmware.org/mailman3/lists/tf-a.lists.trustedfirmware.org/ .. _`TF-A-Tests development`: https://lists.trustedfirmware.org/mailman3/lists/tf-a-tests.lists.trustedfirmware.org/ .. _`summary of all the lists`: https://lists.trustedfirmware.org/mailman3/lists/ diff --git a/docs/about/features.rst b/docs/about/features.rst index c12509d9..9b7bdf9d 100644 --- a/docs/about/features.rst +++ b/docs/about/features.rst @@ -128,7 +128,7 @@ Additionally the following libraries are marked experimental when included in a platform: - MPU translation library ``lib/xlat_mpu`` -- RSS comms driver ``drivers/arm/rss`` +- RSE comms driver ``drivers/arm/rse`` Still to come ------------- diff --git a/docs/about/maintainers.rst b/docs/about/maintainers.rst index 4531a03e..03526a62 100644 --- a/docs/about/maintainers.rst +++ b/docs/about/maintainers.rst @@ -49,12 +49,14 @@ Maintainers :|G|: `laurenw-arm`_ :|M|: Madhukar Pappireddy <Madhukar.Pappireddy@arm.com> :|G|: `madhukar-Arm`_ -:|M|: Raghu Krishnamurthy <raghu.ncstate@icloud.com> +:|M|: Raghu Krishnamurthy <raghuoss@raghushome.com> :|G|: `raghuncstate`_ :|M|: Manish Badarkhe <manish.badarkhe@arm.com> :|G|: `ManishVB-Arm`_ :|M|: Yann Gautier <yann.gautier@st.com> :|G|: `Yann-lms`_ +:|M|: Govindraj Raja <govindraj.raja@arm.com> +:|G|: `govindraj-arm`_ LTS Maintainers --------------- @@ -63,8 +65,8 @@ LTS Maintainers :|G|: `bipinravi-arm`_ :|M|: Joanna Farley <joanna.farley@arm.com> :|G|: `joannafarley-arm`_ -:|M|: Okash Khawaja <okash@google.com> -:|G|: `bytefire`_ +:|M|: Jidong Sun <jidong@google.com> +:|G|: `jidongsun`_ :|M|: Varun Wadekar <vwadekar@nvidia.com> :|G|: `vwadekar`_ :|M|: Yann Gautier <yann.gautier@st.com> @@ -114,6 +116,8 @@ Secure Partition Manager Core (EL3 FF-A SPMC) :|M|: Marc Bonnici <marc.bonnici@arm.com> :|G|: `marcbonnici`_ :|F|: services/std_svc/spm/el3_spmc/\* +:|F|: include/services/el3_spmc\_\* +:|F|: include/services/spmc_svc.h Secure Partition Manager Dispatcher (SPMD) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -121,7 +125,13 @@ Secure Partition Manager Dispatcher (SPMD) :|G|: `odeprez`_ :|M|: Joao Alves <Joao.Alves@arm.com> :|G|: `J-Alves`_ +:|M|: Madhukar Pappireddy <Madhukar.Pappireddy@arm.com> +:|G|: `madhukar-Arm`_ :|F|: services/std_svc/spmd/\* +:|F|: plat/common/plat_spmd_manifest.c +:|F|: include/services/ffa_svc.h +:|F|: include/services/el3_spmd_logical_sp.h +:|F|: include/services/spmd_svc.h Exception Handling Framework (EHF) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -131,6 +141,16 @@ Exception Handling Framework (EHF) :|G|: `manish-pandey-arm`_ :|F|: bl31/ehf.c +Runtime Exceptions and Interrupt Management +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +:|M|: Manish Pandey <manish.pandey2@arm.com> +:|G|: `manish-pandey-arm`_ +:|M|: Madhukar Pappireddy <Madhukar.Pappireddy@arm.com> +:|G|: `madhukar-Arm`_ +:|F|: bl31/aarch64/ +:|F|: bl31/interrupt_mgmt.c +:|F|: include/bl31/interrupt_mgmt.h + Realm Management Monitor Dispatcher (RMMD) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ :|M|: Javier Almansa Sobrino <javier.almansasobrino@arm.com> @@ -216,12 +236,14 @@ Power State Coordination Interface (PSCI) :|M|: Lauren Wehrmeister <Lauren.Wehrmeister@arm.com> :|G|: `laurenw-arm`_ :|F|: lib/psci/ +:|F|: include/lib/psci/ DebugFS ^^^^^^^ :|M|: Olivier Deprez <olivier.deprez@arm.com> :|G|: `odeprez`_ :|F|: lib/debugfs/ +:|F|: include/lib/debugfs.h Firmware Configuration Framework (FCONF) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -232,6 +254,10 @@ Firmware Configuration Framework (FCONF) :|M|: Lauren Wehrmeister <Lauren.Wehrmeister@arm.com> :|G|: `laurenw-arm`_ :|F|: lib/fconf/ +:|F|: plat/arm/common/fconf/ +:|F|: include/lib/fconf/ +:|F|: include/plat/arm/common/arm_fconf\_\* +:|F|: include/plat/arm/common/fconf\_\* Performance Measurement Framework (PMF) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -253,6 +279,7 @@ Arm CPU libraries :|M|: Lauren Wehrmeister <Lauren.Wehrmeister@arm.com> :|G|: `laurenw-arm`_ :|F|: lib/cpus/ +:|F|: include/lib/cpus/ Reliability Availability Serviceabilty (RAS) framework ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -337,12 +364,12 @@ Message Handling Unit (MHU) driver :|F|: include/drivers/arm/mhu.h :|F|: drivers/arm/mhu -Runtime Security Subsystem (RSS) comms driver -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Runtime Security Engine (RSE) comms driver +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ :|M|: David Vincze <david.vincze@arm.com> :|G|: `davidvincze`_ -:|F|: include/drivers/arm/rss_comms.h -:|F|: drivers/arm/rss +:|F|: include/drivers/arm/rse_comms.h +:|F|: drivers/arm/rse Libfdt wrappers ^^^^^^^^^^^^^^^ @@ -380,6 +407,8 @@ DRTM :|M|: Manish Pandey <manish.pandey2@arm.com> :|G|: `manish-pandey-arm`_ :|F|: services/std_svc/drtm +:|F|: include/plat/common/plat_drtm.h +:|F|: include/services/drtm_svc.h PSA Firmware Update ^^^^^^^^^^^^^^^^^^^ @@ -433,6 +462,32 @@ Firmware Handoff Library (Transfer List) :|F|: lib/transfer_list :|F|: include/lib/transfer_list.h +Context Management +^^^^^^^^^^^^^^^^^^ +:|M|: Jayanth Dodderi Chidanand <jayanthdodderi.chidanand@arm.com> +:|G|: `jayanthchidanand-arm`_ +:|M|: Manish Pandey <manish.pandey2@arm.com> +:|G|: `manish-pandey-arm`_ +:|M|: Madhukar Pappireddy <Madhukar.Pappireddy@arm.com> +:|G|: `madhukar-Arm`_ +:|F|: bl1/aarch32/bl1_context_mgmt.c +:|F|: bl1/aarch64/bl1_context_mgmt.c +:|F|: bl31/bl31_context_mgmt.c +:|F|: lib/el3_runtime/ +:|F|: include/lib/el3_runtime/ + +Runtime Services +^^^^^^^^^^^^^^^^ +:|M|: Manish Pandey <manish.pandey2@arm.com> +:|G|: `manish-pandey-arm`_ +:|M|: Madhukar Pappireddy <Madhukar.Pappireddy@arm.com> +:|G|: `madhukar-Arm`_ +:|F|: services/std_svc/std_svc_setup.c +:|F|: common/runtime_svc.c +:|F|: include/common/runtime_svc.h +:|F|: include/services/arm_arch_svc.h +:|F|: include/services/std_svc.h + Platform Ports ~~~~~~~~~~~~~~ @@ -512,8 +567,8 @@ Arm Rich IoT Platform ports ^^^^^^^^^^^^^^^^^^^^^^^^^^^ :|M|: Abdellatif El Khlifi <abdellatif.elkhlifi@arm.com> :|G|: `abdellatif-elkhlifi`_ -:|M|: Xueliang Zhong <xueliang.zhong@arm.com> -:|G|: `xueliang-zhong-arm`_ +:|M|: Hugues Kamba Mpiana <hugues.kambampiana@arm.com> +:|G|: `hugues-kambampiana-arm`_ :|F|: plat/arm/board/corstone700 :|F|: plat/arm/board/a5ds :|F|: plat/arm/board/corstone1000 @@ -524,13 +579,14 @@ Arm Reference Design platform ports :|G|: `thomas-arm`_ :|M|: Vijayenthiran Subramaniam <vijayenthiran.subramaniam@arm.com> :|G|: `vijayenthiran-arm`_ -:|F|: plat/arm/css/sgi/ -:|F|: plat/arm/board/rde1edge/ -:|F|: plat/arm/board/rdn1edge/ -:|F|: plat/arm/board/rdn2/ -:|F|: plat/arm/board/rdv1/ -:|F|: plat/arm/board/rdv1mc/ -:|F|: plat/arm/board/sgi575/ +:|M|: Rohit Mathew <Rohit.Mathew@arm.com> +:|G|: `rohit-arm`_ +:|F|: plat/arm/board/neoverse_rd/common +:|F|: plat/arm/board/neoverse_rd/platform/rdn1edge/ +:|F|: plat/arm/board/neoverse_rd/platform/rdn2/ +:|F|: plat/arm/board/neoverse_rd/platform/rdv1/ +:|F|: plat/arm/board/neoverse_rd/platform/rdv1mc/ +:|F|: plat/arm/board/neoverse_rd/platform/sgi575/ Arm Total Compute platform port ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -540,6 +596,16 @@ Arm Total Compute platform port :|G|: `rupsin01`_ :|F|: plat/arm/board/tc +Arm Automotive RD platform port +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +:|M|: Diego Sueiro <diego.sueiro@arm.com> +:|G|: `diego-sueiro`_ +:|M|: Peter Hoyes <peter.hoyes@arm.com> +:|G|: `hoyes`_ +:|M|: Divin Raj <divin.raj@arm.com> +:|G|: `divin-raj`_ +:|F|: plat/arm/board/automotive_rd + Aspeed platform port ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ :|M|: Chia-Wei Wang <chiawei_wang@aspeedtech.com> @@ -576,8 +642,6 @@ Intel SocFPGA platform ports MediaTek platform ports ^^^^^^^^^^^^^^^^^^^^^^^ -:|M|: Rex-BC Chen <rex-bc.chen@mediatek.com> -:|G|: `mtk-rex-bc-chen`_ :|M|: Leon Chen <leon.chen@mediatek.com> :|G|: `leon-chen-mtk`_ :|M|: Jason-CH Chen <jason-ch.chen@mediatek.com> @@ -605,7 +669,6 @@ Nuvoton npcm845x platform port :|M|: Avi Fishman <avi.fishman@nuvoton.com> :|G|: `avifishman`_ :|F|: docs/plat/npcm845x.rst -:|F|: drivers/nuvoton/ :|F|: include/drivers/nuvoton/ :|F|: include/plat/nuvoton/ :|F|: plat/nuvoton/ @@ -646,6 +709,13 @@ NXP i.MX8M platform port :|F|: docs/plat/imx8m.rst :|F|: plat/imx/imx8m/ +NXP i.MX8ULP platform port +^^^^^^^^^^^^^^^^^^^^^^^^^^ +:|M|: Jacky Bai <ping.bai@nxp.com> +:|G|: `JackyBai`_ +:|F|: docs/plat/imx8ulp.rst +:|F|: plat/imx/imx8ulp/ + NXP i.MX9 platform port ^^^^^^^^^^^^^^^^^^^^^^^^ :|M|: Jacky Bai <ping.bai@nxp.com> @@ -704,6 +774,16 @@ NXP SoC Part LS1088A and its platform port :|F|: plat/nxp/soc-ls1088a/ls1088ardb :|F|: plat/nxp/soc-ls1088a/ls1088aqds +NXP SoC Part S32G274A and its platform port +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +:|M|: Ghennadi Procopciuc <ghennadi.procopciuc@nxp.com> +:|G|: `gprocopciucnxp`_ +:|F|: docs/plat/s32g274a.rst +:|F|: drivers/nxp/clk/s32cc +:|F|: drivers/nxp/console/linflex_console.S +:|F|: include/drivers/nxp/console/linflex.h +:|F|: plat/nxp/s32 + QEMU platform port ^^^^^^^^^^^^^^^^^^ :|M|: Jens Wiklander <jens.wiklander@linaro.org> @@ -790,14 +870,14 @@ RockChip platform port :|G|: `rockchip-linux`_ :|M|: Heiko Stuebner <heiko@sntech.de> :|G|: `mmind`_ -:|M|: Julius Werner <jwerner@chromium.org> -:|G|: `jwerner-chromium`_ :|F|: plat/rockchip/ -STM32MP1 platform port -^^^^^^^^^^^^^^^^^^^^^^ +STMicroelectronics platform ports +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ :|M|: Yann Gautier <yann.gautier@st.com> :|G|: `Yann-lms`_ +:|M|: Maxime Méré <maxime.mere@foss.st.com> +:|G|: `meremST`_ :|F|: docs/plat/st/* :|F|: docs/plat/stm32mp1.rst :|F|: drivers/st/ @@ -805,12 +885,15 @@ STM32MP1 platform port :|F|: include/drivers/st/ :|F|: include/dt-bindings/\*/stm32\* :|F|: plat/st/ +:|F|: tools/fiptool/plat_fiptool/st/ :|F|: tools/stm32image/ Synquacer platform port ^^^^^^^^^^^^^^^^^^^^^^^ :|M|: Sumit Garg <sumit.garg@linaro.org> :|G|: `b49020`_ +:|M|: Masahisa Kojima <kojima.masahisa@socionext.com> +:|G|: `masahisak`_ :|F|: docs/plat/synquacer.rst :|F|: plat/socionext/synquacer/ @@ -935,7 +1018,7 @@ Threat Model :|G|: `sandrine-bailleux-arm`_ :|M|: Joanna Farley <joanna.farley@arm.com> :|G|: `joannafarley-arm`_ -:|M|: Raghu Krishnamurthy <raghu.ncstate@icloud.com> +:|M|: Raghu Krishnamurthy <raghuoss@raghushome.com> :|G|: `raghuncstate`_ :|M|: Varun Wadekar <vwadekar@nvidia.com> :|G|: `vwadekar`_ @@ -947,94 +1030,105 @@ Conventional Changelog Extensions :|G|: `CJKay`_ :|F|: tools/conventional-changelog-tf-a +.. _abdellatif-elkhlifi: https://github.com/abdellatif-elkhlifi +.. _Akshay-Belsare: https://github.com/Akshay-Belsare .. _AlexeiFedorov: https://github.com/AlexeiFedorov +.. _amit-nagal: https://github.com/amit-nagal .. _andersdellien-arm: https://github.com/andersdellien-arm .. _Andre-ARM: https://github.com/Andre-ARM .. _Anson-Huang: https://github.com/Anson-Huang +.. _anukou: https://github.com/anukou +.. _arugan02: https://github.com/arugan02 +.. _arve-android: https://github.com/arve-android +.. _avifishman: https://github.com/avifishman +.. _b49020: https://github.com/b49020 +.. _BenjaminLimJL: https://github.com/BenjaminLimJL .. _bijucdas: https://github.com/bijucdas +.. _bipinravi-arm: https://github.com/bipinravi-arm .. _bryanodonoghue: https://github.com/bryanodonoghue -.. _b49020: https://github.com/b49020 +.. _jidongsun: https://github.com/jidongsun .. _carlocaione: https://github.com/carlocaione +.. _chandnich: https://github.com/chandnich +.. _ChiaweiW: https://github.com/chiaweiw +.. _CJKay: https://github.com/cjkay .. _danh-arm: https://github.com/danh-arm .. _davidvincze: https://github.com/davidvincze +.. _diego-sueiro: https://github.com/diego-sueiro +.. _divin-raj: https://github.com/divin-raj .. _etienne-lms: https://github.com/etienne-lms .. _glneo: https://github.com/glneo +.. _govindraj-arm: https://github.com/govindraj-arm +.. _gprocopciucnxp: https://github.com/gprocopciucnxp .. _grandpaul: https://github.com/grandpaul +.. _harrisonmutai-arm: https://github.com/harrisonmutai-arm +.. _hilamirandakuzi1: https://github.com/hilamirandakuzi1 +.. _hoyes: https://github.com/hoyes .. _hzhuang1: https://github.com/hzhuang1 +.. _hugues-kambampiana-arm: https://github.com/hugueskamba .. _JackyBai: https://github.com/JackyBai +.. _J-Alves: https://github.com/J-Alves +.. _jason-ch-chen: https://github.com/jason-ch-chen +.. _javieralso-arm: https://github.com/javieralso-arm +.. _jayanthchidanand-arm: https://github.com/jayanthchidanand-arm .. _jcorbier: https://github.com/jcorbier .. _jenswi-linaro: https://github.com/jenswi-linaro +.. _JiafeiPan: https://github.com/JiafeiPan +.. _jimmy-brisson: https://github.com/theotherjimmy +.. _joannafarley-arm: https://github.com/joannafarley-arm .. _jslater8: https://github.com/jslater8 .. _jwerner-chromium: https://github.com/jwerner-chromium .. _kostapr: https://github.com/kostapr .. _lachitp: https://github.com/lachitp +.. _laurenw-arm: https://github.com/laurenw-arm +.. _leon-chen-mtk: https://github.com/leon-chen-mtk +.. _linyidi: https://github.com/linyidi +.. _madhukar-Arm: https://github.com/madhukar-Arm +.. _manish-pandey-arm: https://github.com/manish-pandey-arm +.. _ManishVB-Arm: https://github.com/ManishVB-Arm +.. _marcbonnici: https://github.com/marcbonnici +.. _marcone: https://github.com/marcone +.. _mardyk01: https://github.com/mardyk01 .. _marex: https://github.com/marex .. _masahir0y: https://github.com/masahir0y +.. _masahisak: https://github.com/masahisak +.. _max-shvetsov: https://github.com/max-shvetsov +.. _meremST: https://github.com/meremST .. _michalsimek: https://github.com/michalsimek .. _mmind: https://github.com/mmind .. _MrVan: https://github.com/MrVan -.. _mtk-rex-bc-chen: https://github.com/mtk-rex-bc-chen -.. _leon-chen-mtk: https://github.com/leon-chen-mtk -.. _jason-ch-chen: https://github.com/jason-ch-chen -.. _linyidi: https://github.com/linyidi +.. _Neal-liu: https://github.com/neal-liu .. _niej: https://github.com/niej +.. _nmenon: https://github.com/nmenon .. _npoushin: https://github.com/npoushin +.. _odeprez: https://github.com/odeprez +.. _pangupta: https://github.com/pangupta .. _prabhakarlad: https://github.com/prabhakarlad .. _quic_mkf: https://github.com/quicmkf +.. _raghuncstate: https://github.com/raghuncstate +.. _raymo200915: https://github.com/raymo200915 .. _remi-triplefault: https://github.com/repk .. _rockchip-linux: https://github.com/rockchip-linux +.. _rohit-arm: https://github.com/rohit-arm +.. _rupsin01: https://github.com/rupsin01 +.. _rutigl: https://github.com/rutigl .. _sandrine-bailleux-arm: https://github.com/sandrine-bailleux-arm .. _sgorecha: https://github.com/sgorecha .. _shawnguo2: https://github.com/shawnguo2 +.. _sieumunt: https://github.com/sieumunt .. _smaeul: https://github.com/smaeul .. _soby-mathew: https://github.com/soby-mathew .. _sreekare: https://github.com/sreekare .. _stefanasimion: https://github.com/stefanasimion .. _stephan-gh: https://github.com/stephan-gh -.. _sieumunt: https://github.com/sieumunt -.. _BenjaminLimJL: https://github.com/BenjaminLimJL .. _thomas-arm: https://github.com/thomas-arm .. _TonyXie06: https://github.com/TonyXie06 .. _TravMurav: https://github.com/TravMurav +.. _uarif1: https://github.com/uarif1 +.. _vijayenthiran-arm: https://github.com/vijayenthiran-arm +.. _vishnu-banavath: https://github.com/vishnu-banavath .. _vwadekar: https://github.com/vwadekar .. _Yann-lms: https://github.com/Yann-lms -.. _manish-pandey-arm: https://github.com/manish-pandey-arm -.. _mardyk01: https://github.com/mardyk01 -.. _odeprez: https://github.com/odeprez -.. _bipinravi-arm: https://github.com/bipinravi-arm -.. _joannafarley-arm: https://github.com/joannafarley-arm -.. _ManishVB-Arm: https://github.com/ManishVB-Arm -.. _max-shvetsov: https://github.com/max-shvetsov -.. _javieralso-arm: https://github.com/javieralso-arm -.. _laurenw-arm: https://github.com/laurenw-arm -.. _J-Alves: https://github.com/J-Alves -.. _madhukar-Arm: https://github.com/madhukar-Arm -.. _raghuncstate: https://github.com/raghuncstate -.. _CJKay: https://github.com/cjkay -.. _nmenon: https://github.com/nmenon -.. _anukou: https://github.com/anukou -.. _chandnich: https://github.com/chandnich -.. _abdellatif-elkhlifi: https://github.com/abdellatif-elkhlifi -.. _vishnu-banavath: https://github.com/vishnu-banavath -.. _vijayenthiran-arm: https://github.com/vijayenthiran-arm -.. _arugan02: https://github.com/arugan02 -.. _uarif1: https://github.com/uarif1 -.. _pangupta: https://github.com/pangupta -.. _JiafeiPan: https://github.com/JiafeiPan -.. _arve-android: https://github.com/arve-android -.. _marcone: https://github.com/marcone -.. _marcbonnici: https://github.com/marcbonnici -.. _jayanthchidanand-arm: https://github.com/jayanthchidanand-arm -.. _bytefire: https://github.com/bytefire -.. _rupsin01: https://github.com/rupsin01 -.. _jimmy-brisson: https://github.com/theotherjimmy -.. _ChiaweiW: https://github.com/chiaweiw -.. _Neal-liu: https://github.com/neal-liu -.. _amit-nagal: https://github.com/amit-nagal -.. _Akshay-Belsare: https://github.com/Akshay-Belsare -.. _hilamirandakuzi1: https://github.com/hilamirandakuzi1 -.. _rutigl: https://github.com/rutigl -.. _avifishman: https://github.com/avifishman -.. _xueliang-zhong-arm: https://github.com/xueliang-zhong-arm -.. _raymo200915: https://github.com/raymo200915 -.. _harrisonmutai-arm: https://github.com/harrisonmutai-arm + +-------------- + +*Copyright (c) 2019-2024, Arm Limited and Contributors. All rights reserved.* diff --git a/docs/about/release-information.rst b/docs/about/release-information.rst index 654d65fd..9d6bbf6a 100644 --- a/docs/about/release-information.rst +++ b/docs/about/release-information.rst @@ -68,6 +68,12 @@ depending on project requirement and partner feedback. +-----------------+---------------------------+------------------------------+ | v2.10 | 4th week of Nov '23 | 2nd week of Nov '23 | +-----------------+---------------------------+------------------------------+ +| v2.11 | 4th week of May '24 | 2nd week of May '24 | ++-----------------+---------------------------+------------------------------+ +| v2.12 | 4th week of Nov '24 | 2nd week of Nov '24 | ++-----------------+---------------------------+------------------------------+ +| v2.13 | 4th week of May '25 | 2nd week of May '25 | ++-----------------+---------------------------+------------------------------+ Removal of Deprecated Interfaces -------------------------------- @@ -81,9 +87,7 @@ after which it will be removed. | | Date | after | | | | | Release | | +================================+=============+=========+=========================================================+ -| Mbedtls-2.x | 2.10 | 2.10 | Support for TF-A builds with Mbedtls-2.x will be removed| -+--------------------------------+-------------+---------+---------------------------------------------------------+ -| STM32MP15_OPTEE_RSV_SHM | 2.10 | 3.0 | OP-TEE manages its own memory on STM32MP15 | +| | | | | +--------------------------------+-------------+---------+---------------------------------------------------------+ Removal of Deprecated Drivers @@ -101,6 +105,19 @@ after which it will be removed. | None at this time. | | | | +--------------------------------+-------------+---------+---------------------------------------------------------+ +Build Options deprecated/removed +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Populated table provides details about build options that were removed or deprecated. + ++-----------------------+--------------------------------+ +| Build Option | Deprecated from TF-A Version | ++=======================+================================+ +| | | ++-----------------------+--------------------------------+ +| | | ++-----------------------+--------------------------------+ + -------------- -*Copyright (c) 2018-2023, Arm Limited and Contributors. All rights reserved.* +*Copyright (c) 2018-2024, Arm Limited and Contributors. All rights reserved.* diff --git a/docs/change-log.md b/docs/change-log.md index cfc8c564..721e0f32 100644 --- a/docs/change-log.md +++ b/docs/change-log.md @@ -3,6 +3,1958 @@ This document contains a summary of the new features, changes, fixes and known issues in each release of Trusted Firmware-A. +## [2.12.0](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/refs/tags/v2.11.0..refs/tags/v2.12.0) (2024-11-19) + +The threat model for context management and the asymmetric CPU extension support +feature is not available in the release. + +### ⚠BREAKING CHANGES + +- **Bootloader Images** + + - remove unused plat_try_next_boot_source + + **See:** remove unused plat_try_next_boot_source ([2c303e3](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/2c303e393befcd063df60806e5208ff09958d573)) + +### Resolved Issues + +- **Architecture** + + - **Branch Record Buffer Extension (FEAT_BRBE)** + + - allow RME builds with BRBE ([9890eab](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/9890eab5743629c10a3d7432cdb89b65e11c83b8)) + + - **Memory Tagging Extension2** + + - improve ENABLE_FEAT_MTE deprecation warning ([ba65e2d](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/ba65e2d1574954cead8b474e692eef608deff4b3)) + - remove deprecated CTX_INCLUDE_MTE_REGS/FEAT_MTE ([6f2b881](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/6f2b8810f6d48bde930d4384df4b6894effcd14f)) + +- **Platforms** + + - **Allwinner** + + - dtb: check for correct error condition ([7300a4d](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/7300a4d1676f0c929f6a41810f9bc43d4e5334eb)) + - enable dtb modifications for CPU idle states to the rich OS ([188a988](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/188a9888e7b541299133a75b7632fdda2584833d)) + - remove unneeded header inclusion ([8bb8f02](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/8bb8f02d44d1620de6c410f9091c2dd53814479e)) + + - **Arm** + + - **FPGA** + + - avoid stripping kernel trampoline ([8292f24](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/8292f240e5d3fc1391cb463d068a69803b72a9e7)) + + - **FVP** + + - add DRAM memory regions that linux kernel can share ([18ec9bd](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/18ec9bdc2d51f0b58d24e4a6520b2922e74e7dd8)) + - add optee specific mem-size attribute ([75265a1](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/75265a16c978c75c9737e03101fb4616b0aedf7e)) + - add secure uart interrupt in device region ([fc3a01a](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/fc3a01aac3a8c4ba2d491e77681567a2727935e3)) + - enable FEAT_MTE2 ([d081c61](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/d081c6116e455732b579304268027b9cd98e50ff)) + - fix the FF-A optee manifest by adding the boot info node ([bf36351](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/bf36351acaa5ecef6243513d68afb083d7aba07e)) + - update the memory size allocated to optee at EL1 ([4739372](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/47393722783c4cc636244388dccd9987ecf97fa9)) + + - **Neoverse-RD** + + - **RD-V3** + + - remove NEED_* from RD-V3 makefile ([a3eef39](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/a3eef39f45d8e82bb306045eaf4a1f3ad37592c7)) + + - **TC** + + - add SCP_BL2 to RSE measured boot ([7984154](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/79841546a2782c400751bdc5a4d5f8c0263b3812)) + - add stubs for soc_css_init functions ([f5ae5dc](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/f5ae5dcd89497d4c5e5187137a8392d4216a5aaa)) + - correct CPU PMU binding ([7aca660](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/7aca660c4e77477d81623df00fc7ffab2700dcb9)) + - correct NS timer frame ID for TC ([034cc80](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/034cc8087b249f87bfd42b99ac8553756274ee5a)) + - don't enable TZC on TC3 ([8ce29a7](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/8ce29a74a44523ce3e56da09a7b64f415c08a20f)) + - enable MTE2 unconditionally ([be8eaa5](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/be8eaa5e62d2a916c6521e1d9c17ec4698bbbb27)) + - fix the MHUv3 interrupt name in DT ([1bf3325](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/1bf33251a8fe774674205df9ea0f49d55233820c)) + - retain NS timer frame ID for TC2 as 0 ([1ba0880](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/1ba08807a58d977e2cbf0fec5ec49f29652ff997)) + + - **Corstone-1000** + + - fix Makefile error reporting ([09bf366](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/09bf366bef9bcbf10267ec036b8de7b5b35fd58e)) + - clean cache and disable interrupt before system reset ([335c4f8](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/335c4f8b301ffe0fd323a25e9995c3e0b1b8aa1d)) + - include platform header file ([783e5ab](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/783e5abe94a10c9aa5c7c750ec1590f0529702fa)) + - pass spsr value explicitly ([32690ba](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/32690bacb9564263f4ed23e27a1f22ba0a22bc9e)) + - remove unused NS_SHARED_RAM region ([83c11c0](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/83c11c0bd119ffe8f2673aa09e17e1432b226415)) + - update memory layout comments ([d7417ad](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/d7417adc218c1386b30658e83ea8d4f3b7b72697)) + + - **Aspeed** + + - **AST2700** + + - fix mpll calculate statement ([aa09622](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/aa09622233a891cb04c65a5db816e0dc76110e21)) + + - **HiSilicon** + + - **Poplar** + + - shutdown wdt0 before powering off ([88bc65d](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/88bc65d745c0c29f4d2d9a75abe3ea45a235a719)) + - use sysctrl module to reset ([c961e68](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/c961e68e7990eb802d6638bc881afa3b7068e60d)) + + - **Intel** + + - add cache invalidation during BL31 initialization ([3c640c1](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/3c640c124ec02f3f0e6bbc5b6d364a0b851ba1ad)) + - add in JTAG ID for Linux FCS ([ea906b9](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/ea906b9bb97fa6011ad974838266d5f82efc134d)) + - add in missing ECC register ([4683946](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/4683946015365e1a6e8a7fd8c8c2c72cc6043b02)) + - add in watchdog for QSPI driver ([6704cba](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/6704cba25d6386469832fe82e8ec6e0fed79b0ce)) + - bridge ack timing issue causing fpga config hung ([9a402d2](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/9a402d2f0f7e4c62c26903af1482d2f67cfa48c5)) + - correct macro naming ([815245e](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/815245e4deafc375dd62aa26821059a07e7ad2b5)) + - f2sdram bridge quick write thru failed ([64cf9de](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/64cf9deb770ea7eccd5f92a013b67b492978aea0)) + - fix bridge enable and disable function ([90f5283](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/90f5283ec052f622285ef35210d4bc452e4b905a)) + - fix CCU for cache maintenance ([f06fdb1](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/f06fdb1469e8855e0b711ba86fde98b44f1d7736)) + - flush L1/L2/L3/Sys cache before HPS cold reset ([7ac7dad](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/7ac7dadb551ee602299aef91043dc4adbd234a3e)) + - implement soc and lwsoc bridge control for burst speed ([a8d81d6](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/a8d81d61e120f2e5958f996cd59ab5219a8a3cce)) + - refactor SDMMC driver for Altera products ([beba204](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/beba20403e23ab128711c2c8c9d480a3a40b804c)) + - remove redundant BIT_32 macro ([7985ade](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/7985aded701cc715bff2dd247680b9d0d2ffb42c)) + - software workaround for bridge timeout ([e08039d](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/e08039d0e2b3ed69bf2b10592006be8008dcb398)) + - update Agilex5 BL2 init flow and other misc changes ([b3d2850](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/b3d28508427225f41d55fa3b10fe4f1f1dfbd238)) + - update Agilex5 warm reset subroutines ([c1253b2](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/c1253b2445d6b57851118fb9cb4ee1eac9e122be)) + - update all the platforms hand-off data offset value ([1838a39](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/1838a39a44a058c6fc14e045fabe433c93e609c4)) + - update CCU configuration for Agilex5 platform ([09330a4](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/09330a49376306031cf92e26bbd6955ebfe87597)) + - update mailbox SDM printout message ([569a03c](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/569a03c7114f4a5c005a8cf4fa1dcae2b54bec56)) + - update memcpy to memcpy_s ([e264b55](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/e264b5573952c72805a14e69e438168c00163e9a)) + - update outdated code for Linux direct boot ([21a01da](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/21a01dac879daaded762f2feccccbdf6c07cf451)) + - update preloaded_bl33_base for legacy product ([f29765f](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/f29765fd337cc0a405b1ffee945bc6a5db2d7e8b)) + - update sip smc config addr for agilex5 ([7c72dfa](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/7c72dfac962ce1e1f95be4c974b691d667a8eae4)) + - update the size with addition 0x8000 0000 base ([9978a3f](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/9978a3fd8b97f024a28be798494b608f43ef5e79)) + + - **Marvell** + + - **Armada** + + - **A3K** + + - reset GIC before resetting via CM3 secure coprocessor ([5993af4](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/5993af454fca84d1401d12eabc3c714b6b5dd953)) + + - **MediaTek** + + - **MT8188** + + - remove BL32 region protection if SPD sets to none ([207c447](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/207c4470492ea5b9554051b9abaf6cc9c1a78f35)) + + - **NXP** + + - **i.MX** + + - disable DRAM retention by default on i.MX8MQ ([108146c](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/108146ce73573ca761fb2072efef0e0c4e4d50bb)) + + - **i.MX 8M** + + - 8mq: enable imx_hab_handler ([af79981](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/af799814e2639a03b3453744f06a73e77cb66e86)) + - ensure domain permissions for the console ([f7434fa](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/f7434fa13507b8879922bcf0c55947e9b9606404)) + + - **S32G274A** + + - avoid overwriting const fields ([bf01296](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/bf012960d4f1490897b6a243eb89c70d6e03161f)) + - workaround for ERR051700 erratum ([b47d085](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/b47d085a3bc918d51dae48fa7bb13678f3ae14ba)) + + - **QEMU** + + - allocate space for GPT bitlock ([e9bcbd7](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/e9bcbd7b2ee43b3abc89f8e505b9fd5689f91aae)) + - exclude GPT reserve from BL32_MEM_SIZE ([7604288](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/7604288577bab9a1ff02fd69e07a803b808bbfae)) + - fix build error with spmd ([1b1b40a](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/1b1b40a941b62a845e57ca8d2bf754396b1b5dcb)) + - fix EL3-SPMC data store alignment ([eee52da](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/eee52dac2c3e6b7c9ac51624c6200d2201e65bc2)) + - fix L0 GPT page table mapping ([147b1a6](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/147b1a6f068bc3db73d0f945137054af83c486f5)) + - remove validate_ns_entrypoint ([e5362e2](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/e5362e29d556df2e4238e798513f670ca3f85aad)) + - update rmmd_attest_get_platform_token() ([9248ee0](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/9248ee0cc413a209f93ee330a04890f873fec1ee)) + + - **Raspberry Pi** + + - **Raspberry Pi 3** + + - manually populate CNTFRQ reg ([11dff59](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/11dff5994671bf3ec4f26b7ea930bd4749658aa2)) + - use correct define for GPIO reg_clr ([9876baf](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/9876baf180d307fe36ec846c03c05dd8a1b08d53)) + + - **Rockchip** + + - add parenthesis for BITS_SHIFT macro ([901e94e](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/901e94ed1a0d5e381d857e062c8b8289cfa80a48)) + - fix "unexpected token" error with clang ([52cdebb](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/52cdebbcc5d1fffea7af837178a712c8d02bcdde)) + - xlat: fix compatibility between v1 and v2 ([d43a2e8](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/d43a2e8bf4b4434cf30296cc56fdaf15321e5e8b)) + + - **ST** + + - set no-pie option when building ST elf file ([6d26d75](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/6d26d75c374bc9c7aa03d8c745b9f5f9082b18c2)) + - support device tree DDR sizes higher than 16Gbits for aarch64 ([cd9c92c](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/cd9c92cd16b1beb6199ae7a7c01effb0d49ab448)) + + - **STM32MP1** + + - remove unnecessary assert on GPIO_BANK_A value ([5c45768](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/5c457689b283437cbf1ba87c48bae9e03a579aa8)) + - skip OP-TEE header check if image base is NULL ([b452e7a](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/b452e7a8246533a4923d54cc916bdf805f9543da)) + + - **STM32MP2** + + - enable timer earlier in BL31 ([16a659d](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/16a659d73a70ce16662c0e2df4097f3496d65f63)) + - remove mapping of BL2 DT area ([60d0758](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/60d0758411064ac67df22ade6dba460d31d00c81)) + - set PLAT_MAX_PWR_LVL to one ([747d85e](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/747d85ee77d8d8b2e04a4988f98cb2fc426103a3)) + - use TOOL_ADD_IMG_PAYLOAD for BL31 DT ([f15f1c6](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/f15f1c6270d50e06eafb4202dd32326d516960f3)) + + - **Xilinx** + + - avoid altering function parameters ([b21e287](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/b21e2874f81633892e914f7d53b5bf0fe3b41a18)) + - dcc to support runtime console scope ([238eb54](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/238eb542bb746a776de82236dd25b7ae5876b743)) + - declare unused parameters as void ([d3bb350](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/d3bb350c40d202bec31dde04911f1c50d3e71634)) + - explicitly check operators precedence ([8e9a5a5](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/8e9a5a5150c631dec09b9fea610ca3846e0dce9c)) + - fix comment about MEM_BASE/SIZE ([1e2a5e2](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/1e2a5e2851072803a78a8e998dee1ff4ad5b7f9b)) + - fix logic to read ipi response ([03fa6f4](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/03fa6f42502a3b6b318a9a73a228a6c751329a8f)) + - fix OVERRUN coverity violation ([e27b949](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/e27b9491f39c4657727d3b1641680a7e5c09a3b4)) + - handle power down event if SGI not registered ([c3ffa4c](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/c3ffa4c5bae5c2be313faa015bfffdb7b46c4122)) + - map PMC_GPIO device node to interrupt for wakeup source ([692d32b](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/692d32b5733b4520093ac059578b2e6c2429b80d)) + - modify conditions to have boolean type ([e223037](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/e223037525ef7b2e3794733ba417cbb848907dda)) + - optimize logic to read IPI response ([02943d0](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/02943d0d8d05e8a647a72eb11ac9159c6a257aa3)) + - register for idle callback ([a3b0a34](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/a3b0a3422c3f2b2718a7f8b337d019f470101d4d)) + - rename variable to avoid conflict ([aba5bf9](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/aba5bf901d775ffbf77a5034eb91f3667758a4c1)) + - warn if reserved memory pre-exists in DT ([729477f](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/729477fd86fc7c471fe44f81ed58e94d1656571f)) + + - **Versal** + + - add const qualifier ([0f9f557](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/0f9f5575cc2c5de913e4222c149146c149378728)) + - add external declaration ([16c611f](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/16c611f8a6f6a6669265fda95115a0ade56078e7)) + - declare unused parameters as void ([ab9aab3](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/ab9aab38d13a0905804ab5a8480dd31828d5b3ab)) + - evaluate condition for boolean ([b39c82e](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/b39c82e9201255f6a396ff9a80cb2c2ec038b588)) + - explicitly check operators precedence ([0ed8b4b](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/0ed8b4bffc31e52facf27445503ea668e7ba3dc2)) + - kernel QEMU boot is failing on versal platform ([8e5252f](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/8e5252f3c08d25575fbbcbb8cb4ed3a4b0c9d506)) + - modify conditions to have boolean type ([1247566](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/12475663b53f6e5ffe18343470d653cc092aca48)) + - remove check for bl32 load address ([4c9ae8a](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/4c9ae8ae1f266f7558c5bcc98491a4fbb69967f5)) + - variable conflicting with external linkage ([e452826](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/e452826ad3aa595f720be2c2500ada2f27d3eaea)) + + - **Versal NET** + + - evaluate condition for boolean ([37c46d8](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/37c46d85d14021fa89186d3221621658410e8720)) + - declare unused parameters as void ([06f63f4](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/06f63f4b566c86209fbd13142d6c5453a6fd9c8e)) + - explicitly check operators precedence ([a4ddd24](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/a4ddd24f97953b6c8ad6b9dfddc240067807c502)) + - ignore the unused function return value ([aa6df8e](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/aa6df8ec32a48d8e57205b6bb93d4bc283d353f2)) + - modify conditions to have boolean type ([83c3c36](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/83c3c36b1b2869ade53f36cfd9052e6b6a17797b)) + - remove check for bl32 load address ([c38ced2](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/c38ced2d279a40298cab6a4c99b046146c3a1917)) + - variable conflicting with external linkage ([4d2b4e4](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/4d2b4e4dd7ed22a41c0569f9b2b2fd5c419a8261)) + + - **ZynqMP** + + - add const qualifier ([bb145c9](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/bb145c9d9b543d9440b3b4fc48b8210df4b35ce9)) + - add external declaration ([6c08d1d](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/6c08d1df0ccb14fb66ba081bbe57ea17b8b3bb1c)) + - declare unused parameters as void ([1c43e36](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/1c43e36ac18aeaa6816a0474655d699909d616b1)) + - evaluate condition for boolean ([aaf6e76](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/aaf6e7627e11b1b8616d798975e40d71d1e03c8c)) + - explicitly check operators precedence ([5b54231](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/5b542313f8af2373549e71266307b8fbbb8788cd)) + - handle secure SGI at EL1 for OP-TEE ([f5b2fa9](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/f5b2fa90e0c0324f31e72429e7a7382f49a25912)) + - ignore the unused function return value ([355ccf8](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/355ccf895e5106d0f7a9b5932f73759277d1ab2a)) + - modify conditions to have boolean type ([a42e6e4](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/a42e6e44b89fb1be1d3e97e5adc4f7288bb7e69b)) + - variable conflicting with external linkage ([eda23fa](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/eda23fa5aa065216d9cf86176fbb916b4841c874)) + + - **AMD** + + - **Versal Gen 2** + + - add const qualifier ([a0745f2](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/a0745f21aa0c5c869a3788e8f2c590bace11ef0b)) + - add external declaration ([17a8f41](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/17a8f41e458e662c878fc8549d7a04a49e88abac)) + - add ufs specific features support ([b9c20e5](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/b9c20e5d144347ca28e17df080b7ee9bf0dd9377)) + - correct the UFS clock rates ([b048601](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/b048601eeeeb34fb1e7642d1ed7f18f9a51d6ae9)) + - declare unused parameters as void ([851df3c](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/851df3c8915d5832d9ac1d58dc3420847cacb0a0)) + - explicitly check operators precedence ([15a9e38](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/15a9e381cdfc607e516f86adc118d036ce78aa86)) + - ospi data integrity cases are failing ([a147362](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/a14736268bd5156f657286b535af5d27959dec99)) + - update check for TRANSFER_LIST macro ([7d09198](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/7d09198f58cefd10a9ca19305782785632ffa72a)) + - variable conflicting with external linkage ([ca39fd4](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/ca39fd46c1ce0203df7f797fa6bd8a4fc5336c38)) + + - **Nuvoton** + + - fix MMU mapping settings ([0a1df64](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/0a1df6411734d1793e06e508f27bcf95f01c703f)) + +- **Services** + + - **RME** + + - **RMMD** + + - continue boot if rmmd_setup fails ([fdd8a24](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/fdd8a24b9892fa0e67580dc25f7e7ca0b54c870e)) + - fail gracefully if RME is not enabled ([eacbef4](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/eacbef4c643a5ee69828a7004abf0097b3d3f728)) + - handle RMMD manifest loading failure ([0c70781](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/0c707813e9e734d9a62d5cdc592e68e245f4f557)) + - ignore SMC FID when RMM image is not present ([adcd74c](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/adcd74ca05fe4d7c3c047c0108cb9f136b67be49)) + - remove the assert check for RMM_BASE ([8cb9c63](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/8cb9c635775b2f1c413c28ea8610dc81b6e8928f)) + + - **SPM** + + - **EL3 SPMC** + + - use write_el1_ctx_timer() macro to set cntkctl_el1 value ([19082c2](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/19082c20d98456d147816d8ebf01f4e6721c7b12)) + + - **SPMD** + + - remove spmd_handle_spmc_message ([6c378c2](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/6c378c2feffd8826542322e8d2cc53fd7f0d8252)) + + - **SPM MM** + + - carve out NS buffer TZC400 region ([1922875](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/192287523350dfdc06b794ae2fbc1827ff69ab72)) + + - **DRTM** + + - do cache maintenance before launching DLME ([23378ae](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/23378ae0bdcdaee5764af9ebf5faed7cdb8b2737)) + - return proper values for DRTM get and set error SMCs ([5e1fa57](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/5e1fa57459aa27a28bb21be5496fb471350b6046)) + +- **Libraries** + + - **CPU Support** + + - modify the fix for Cortex-A75 erratum 764081 ([7f152ea](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/7f152ea6856c7780424ec3e92b181d805a314f43)) + - workaround for Cortex-A720 erratum 2792132 ([b1bde25](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/b1bde25ed9b302a2203a928457c91693ed7f91a7)) + - workaround for Cortex-A720 erratum 2844092 ([1214090](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/12140908a52230081f85069f0f0a400ddabf44ef)) + - workaround for Cortex-X4 erratum 2816013 ([1e4480b](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/1e4480bb54b0f567688cfbea2119aa703fcbb7b8)) + - workaround for Cortex-X4 erratum 2897503 ([609d08a](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/609d08a86db2ddf09f98105b999d57b8e2eecc8b)) + - workaround for Cortex-X4 erratum 3076789 ([db7eb68](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/db7eb68817dad1a429a2f6518926791c47091b1c)) + - workaround for Cortex-A520(2938996) and Cortex-X4(2726228) ([4a97ff5](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/4a97ff5111204a18b4f72d1e1cd3d8285f16289d)) + + - **EL3 Runtime** + + - correct CASSERT for cpu data size ([483dc2e](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/483dc2e43e550cf5d4541a7b164b49edbaa467e6)) + + - **PSCI** + + - fix parent parsing in psci_is_last_cpu_to_idle_at_pwrlvl ([01959a1](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/01959a1656a08dacd1d036d0441165d52bf7563e)) + + - **ROMlib** + + - prevent race condition on the build directory ([25cde5f](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/25cde5f810422867bf03b2c0e8354dcee2493e8a)) + - wrap indirectly included functions ([d95d56b](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/d95d56bd2bfc87951f35d2badde9db336c0a6489)) + + - **GPT** + + - fix GPT library fill_l1_tbl() function ([d024cce](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/d024cce376f01652b91ebdef286dceffc9ffb063)) + - fix RME GPT library bug ([6350aea](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/6350aea2f186c593ef46737f573de5e4833a9433)) + + - **Translation Tables** + + - correct attribute retrieval in a RME enabled system ([e3c0869](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/e3c0869f6fbd8008b556738384e3f3a22cf981c3)) + + - **Authentication** + + - check the presence of the policy check function ([491832f](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/491832fedf979b6b0c00c5c5411780047f106804)) + - correct RSE_CRYPTO_EXPORT_PUBLIC_KEY_SID ([759994a](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/759994aa3b1ad1e54ef3a998d0685108fec6d27c)) + - remove the bl2 static c file ([ac106f2](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/ac106f208fad311e691b69e116632239c635a81f)) + + - **mbedTLS** + + - fix error return code for calc_hash ([885bd91](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/885bd91f27fd31d46f33861b94a814fa4537ab5f)) + - sign verification issue with invalid Key/Signature ([7731465](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/7731465252bd82ce97620a327f3b5d8905f8bdb1)) + - add extra hash config to validate ROTPK ([014975c](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/014975cea46261d84a934644be2ad53bbdc0dc79)) + + - **mbedTLS-PSA** + + - fix P-384 PSA key signature verification ([12a8e95](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/12a8e95303c051dc5671441a6419741db3b0964e)) + + - **GUID Partition Tables Support** + + - fix unaligned access in load_mbr_header() ([21a77e0](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/21a77e08921a13ac4adc523a136d829333a854f1)) + + - **Arm** + + - **GIC** + + - **GICv3** + + - fix GITS_CTLR.Quiescent bit definition ([2da29d2](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/2da29d2d07cdd8c52a1c1d6f26d7d45ac11ef2be)) + - incorrect impdef power down sequence ([b1925dc](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/b1925dcfd97a5d77a796bee8164519b4e8254d8c)) + - wait rwp when gicr_ctrl.enablelpis from 1 to 0 ([66668c7](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/66668c77cb140c3af1a801b8f56b0c0ec65c4c21)) + + - **MHU** + + - fix compilation error with ENABLE_ASSERTIONS=0 option ([e2e8a39](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/e2e8a397f88eaedb9d3f16b6b4560eec51aee7e0)) + + - **RSE** + + - include lib-psa to resolve build ([654ae70](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/654ae705c35baa1fbd13a0cd8558a64c8454347c)) + + - **NXP** + + - **SFP** + + - shift gpio register offsets by 2 ([d30312a](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/d30312a2dcdbe7aa651f8770d9b00e6ae83baacc)) + + - **Clock** + + - broken UART clock initalization ([f8490b8](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/f8490b85b49c92799a792587658eca4cf36fd4f6)) + - function parameter should not be modified ([8ee0fc3](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/8ee0fc31992538823177e764e4522293ea829957)) + + - **ST** + + - **Clock** + + - adapt order of CSS on LSE and HSE ([eca5103](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/eca510346d9ae7d14eea53ec01554bbde6cb2e69)) + - display proper PLL number for STM32MP13 ([039b7d4](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/039b7d4673e5b39056a6c0c40204aad2b0258581)) + - do not reconfigure LSE ([f4a2bb9](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/f4a2bb986b43fcb1c0c8c45b5d9a93798f655453)) + + - **DDR** + + - fix coverity issue in ddrphyinit ([5dd1d54](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/5dd1d5447750e1be9377ae8d1c4fce2608a53a63)) + - move skipddc_dat definition ([13cc1a5](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/13cc1a506428398cc8cc142015dca10d24840f96)) + + - **GPIO** + + - configure each GPIO mux as secure for STM32MP2 ([179a130](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/179a130aea4876c7fc89606c65b55f143724eb38)) + +- **Miscellaneous** + + - **DT Bindings** + + - update STM32MP2 clock and reset bindings ([8522909](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/85229098ab70dfb65905f9ad7229db6478335a00)) + + - **FDTs** + + - reserved memory: detect existing region ([4248806](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/42488064e10383247d0c321fe1e7fc13eec0752c)) + + - **SDEI** + + - fix a crash when attempting to bind more events than are available ([4096bd6](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/4096bd66c7af0a5661c7926460f2a2ca4162388d)) + +- **Documentation** + + - fix CPU type for mt8195 ([65ada75](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/65ada7571781317f16240ee3694bd684fd3bdaf5)) + - fix the example command for doc build ([9db2b05](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/9db2b059eb76eaf51af8e434904caf277b998c99)) + - point poetry readthedocs virtual env ([5383a88](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/5383a88b93abead45ab3479536d1b1516d9be3f8)) + - refactor poetry dependency group ([4a29299](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/4a29299f2e1640dc9f3136682b914c39930562eb)) + - replace "ARM-TF" with "TF-A" in diagrams ([c4067a9](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/c4067a9df6e9c478a824bd5b0ac44b84d48c9b40)) + +- **Build System** + + - correct feature assignment for ARM v8.8 compliance ([94ff1d9](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/94ff1d98c95db491137177c2160ef1afe944ff5f)) + - ensure `$(ROT_KEY)` depends on correct directory rules ([7a95759](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/7a95759f935202c1f25df10eb32c67bbd69db3c8)) + - fix incorrectly-escaped armlink preprocessor definitions ([df52e26](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/df52e2600deef3fff250d337d06f55863d1dfd76)) + - pass the PLAT option during FIP tool compilation ([40469bf](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/40469bf977a615400424cdcd78c350b3310ebd2f)) + - string split into two lines causing error ([4f32179](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/4f321794ffaacad74258082272163a61f3db8477)) + + +- **Tools** + - **fiptool** + + - update the fiptool and certtool to fix POSIX build ([ccbfd01](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/ccbfd01d95b9b35acb3e2ca5f25379ce8fa0ed1c)) + +- **Dependencies** + + - **checkpatch** + + - detect issues in commit message ([1a72174](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/1a721748605bc753089bc34c6010aa236c9d0ab7)) +### New Features + +- **Architecture** + + - **Fine-grained Traps 2 (FEAT_FGT2).** + + - add support for FEAT_FGT2 ([33e6aaa](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/33e6aaacf1e8f327b33fe2db1f5e964b0adb41c7)) + + - **CPU feature / ID register handling in general** + + - add ENABLE_FEAT_LS64_ACCDATA ([19d52a8](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/19d52a83b755cdf6d9b7defc7eb821eb62e80310)) + - add new feature state for asymmetric features ([43d1d95](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/43d1d951ddb3b725d372884f314babb6594fcd47)) + - upgrade PMU to v8 (FEATURE_DETECTION) ([515d2d4](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/515d2d46a318fa3c4c172491c6408c032e6a6b15)) + + - **Debug Extension (FEAT_Debugv8p9)** + + - add support for FEAT_Debugv8p9 ([83271d5](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/83271d5a5aae06c23c59a32c30a0fe83fb82e79f)) + + - **Statistical profiling Extension (FEAT_SPE)** + + - introduce spe_disable() function ([651fe50](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/651fe5073c790647305363a4de05cf050e0851de)) + + - **Trace Buffer Extension (FEAT_TRBE)** + + - introduce trbe_disable() function ([b36e975](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/b36e975ea374589270fc4010aa247e1e56432bda)) + + - **Extension to SCTLR_ELx (FEAT_SCTLR2)** + + - enable FEAT_SCTLR2 for Realm world ([b17fecd](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/b17fecd6cf23f50346d70ec84f5708c95a2db5f8)) + - add support for FEAT_SCTLR2 ([4ec4e54](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/4ec4e545c66cb888bfbedcea4030a234421457d7)) + + - **128-bit Translation Tables (FEAT_D128)** + + - add support for FEAT_D128 ([3065513](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/306551362c15c3be7d118b549c7c99290716d5d6)) + + - **Translation Hardening Extension (FEAT_THE)** + + - add support for FEAT_THE ([6d0433f](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/6d0433f04045f52856ecb837efc873a5504d9fa2)) + +- **Platforms** + + - **Allwinner** + + - adjust H616 L2 cache size in DTB ([ee5b26f](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/ee5b26fd0058d5e696cdf83bf389351eab296bf7)) + - h616: add I2C PMIC support ([0444589](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/044458981f986b03445185b646bebbea1d90f11f)) + - h616: add support for AXP313 PMIC ([0385136](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/03851367dbd46f73708fa35da2b501489e44afa4)) + - h616: add support for AXP717 PMIC ([646d06b](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/646d06b2378b39b8dfa713b74f936a2b02782e96)) + + - **Arm** + + - **Common** + + - add support for loading CONFIG from BL2 ([973e0b7](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/973e0b7f2cc9ac64132b2179295c424a88b690ea)) + - add fw handoff support for RESET_TO_BL31 ([1a0ebff](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/1a0ebff784c11f0b11f203b56eeb3180f994c0b9)) + - correct the RESET_TO_BL31 x1 handoff arg ([5da68cc](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/5da68cc477adf0f686eeb9b6c8c53c1104805f24)) + - load dt before updating entry point ([c1c406a](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/c1c406a4de90b859a2e534304e33331ecd3dcef8)) + - move HW_CONFIG relocation into BL31 ([fe94a21](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/fe94a21a6815fc8623074e7184d87583f2f58940)) + - remove critical handoff code from assert ([cca1b72](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/cca1b72b3bf25dab03d3527c9fbe0f5d368382cc)) + - makefile invoke CoT dt2c ([0e0fab0](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/0e0fab0ca2190d75dd12b655e043ed8b6053221f)) + - generate tbbr c file CoT dt2c ([479c833](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/479c833afcfce3afebefdc8eecefea71c09f0bf1)) + - add COT_DESC_IN_DTB option for Dualroot ([731ac5e](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/731ac5ea043efb333ea74c8443c10989acce5d94)) + + - **FPGA** + + - enable new CPU features ([1920a32](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/1920a32b7fd32c22f4cef6d948c1d0be4efce0e5)) + + - **FVP** + + - change UART0-1 to NS device region ([cd656a5](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/cd656a5612e6f6942fd8fb768b5dd948efbc37ac)) + - add Cactus partition manifest for EL3 SPMC ([5134623](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/51346236c3f07fd86bf14f4743517ab1d15bd56c)) + - add cpu power control ([d38c64d](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/d38c64d2466006104142ae23a673a9cf2b4170e2)) + - add Dualroot CoT in DTB support ([0af86f0](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/0af86f08ce5c39e3d53ccd9daa77084acef09fa7)) + - add flash areas for secure partition ([9fb7676](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/9fb767630dbb3a54eff17b9b9b83078a7b3e77b7)) + - add SPM manifest for OP-TEE at S-EL1 without S-EL2/Hafnium ([41d73bf](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/41d73bffe1cac198ef1f21149ac64f784f5ae8db)) + - allow SIMD context to be put in TZC DRAM ([b4c23ad](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/b4c23adf58dce011ce5119cfc79f4312cea855f7)) + - fdts: add stdout-path to the Foundation FVPs ([2faccab](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/2faccaba80318b48e7ae738a909a38a989ed3c5e)) + - replace managed-exit with ns-interrupts-action ([887cec9](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/887cec9caedb87f824f8f35adbf058e1e83b250e)) + - scale SP_MIN max size based on SRAM size ([3b5eca9](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/3b5eca9e7a96f7a6f3c764fb981a3b2bfe67e514)) + - update FF-A version to v1.1 supported by optee ([4f37e1e](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/4f37e1e8b233a2968dd32708eef0a4a44d093b7a)) + - remove duplicate jumptable entry ([180a3a9](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/180a3a9ed3e0ee80f4ed4d02d671a7b0fb28db6d)) + + - **Neoverse-RD** + + - add a routine to update NT_FW_CONFIG in BL31 ([c6b27c4](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/c6b27c4916d41db9a8f6be089970fa5f79634f7c)) + - add CSS definitions for third gen platforms ([6d52713](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/6d5271346d38ac9899bc2f8c9fe96b32bcef05c8)) + - add DRAM layout for third gen platforms ([10eb4c4](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/10eb4c4bee31786800a8d61ef54d68d22db97221)) + - add firmware definitions for third gen platforms ([e517ccf](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/e517ccf52cf9f2578d980b5340900fafe3e9a6e6)) + - add MHUv3 channels on third gen multichip platforms ([47348b1](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/47348b1c53c1000f7b36593aa1641240d0509947)) + - add MHUv3 doorbell channels on third gen platforms ([46d474f](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/46d474fc9fc99b1d8c9e8b66514cc380ec10aa9a)) + - add multichip pas entries ([c72e9dc](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/c72e9dcdd872f3922eb093afbfded0dd78533cc7)) + - add pas definitions for third gen platforms ([896e9aa](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/896e9aa98b5cf25a4b5e9d11a58265fdb43dca1e)) + - add RoS definitions for third gen platforms ([fad5a20](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/fad5a209a03ae7a893b8e93197ed6e795fe370a6)) + - add scope for RD-Fremont variants ([84973bb](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/84973bb3cafeb21f7c706335570fbef41ab62179)) + - add SRAM layout for third gen platforms ([5a37d68](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/5a37d68c78b0c1fcd527e2d6fbc40ecf84dc0f15)) + - allow RESET_TO_BL31 for third gen platforms ([4abcfd8](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/4abcfd8b2ce2fd8aad9f4de652a11a0b6a28e8dd)) + - enable RESET_TO_BL31 for RD-V3 ([527fc46](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/527fc46541b85371b01dc55e5ebc1ba92c1b6b47)) + + - **RD-V3** + + - add DRAM pas entries in pas table for multichip ([6a9cf0e](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/6a9cf0e5aaf6bc97b433e79c74cf4ba435c877b2)) + - add implementation for GPT setup ([0876c74](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/0876c74285377857d34701f9279cc15b60f6ac50)) + - add support for measured boot at BL1 and BL2 ([6182950](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/61829505d2d40a1b5a3065fda53df7f6b833cdb3)) + - add support for RD-Fremont ([c0513e0](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/c0513e0f8500d8552646f57b2a2e68113c48ad2e)) + - add support for RD-Fremont-Cfg1 ([6a0cb48](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/6a0cb487fd61e0c583465338bb502833803b8a5a)) + - add support for RD-Fremont-Cfg2 ([eedb2d8](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/eedb2d820a26300314ac81773fe597938e67698e)) + - enable AMU if present on the platform ([faf98b3](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/faf98b3fe24926bd556b175ce07c97a63b058b45)) + - enable MPAM if present on the platform ([e951985](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/e9519857d36517624f954b85b7f24f677fdc6765)) + - enable MTE2 if present on the platform ([f801377](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/f80137720cbe08c2de1b130b1a4ba44af037fa1d)) + - enable SVE for SWD and NS ([7e2736b](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/7e2736b0c1fbe5a41cd815da0b625a90f0142a57)) + - fetch attestation key and token from RSE ([0e323ec](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/0e323ec5c4e824c113394f87d1c77103471e8123)) + - helper to initialize rse-comms with AP-RSE MHUv3 ([2a35fcd](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/2a35fcdd9faa056e182a43ea6e53dc529bfc4186)) + - initialize GPT on GPC SMMU block ([ba35fac](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/ba35fac174ae4a9d52625e709863b6c565608538)) + - initialize the rse comms driver ([f546113](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/f54611376113d7c0cfdfd0eb89752040deb99aff)) + - integrate DTS files for RD-Fremont variants ([1b96641](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/1b966414c1a2a38a931eb4499bc209c37c4f39db)) + - update Root registers page offset for SMMUv3 ([859355f](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/859355f27598da4f9ac76c0d12d1f8db4499e131)) + - set CTX_INCLUDE_SVE_REGS build flag for RD-V3 variants ([1551834](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/155183432afffa8dad4260b0dc4eeef60a8385cd)) + + - **TC** + + - add default SLC policy for the gpu ([bebefe0](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/bebefe0f33411245325c9a25db4eb9d7cbec69fc)) + - add device tree binding for SPE ([77080f6](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/77080f6aaf7e1cde46a4d48a9e8eb673119dd3ff)) + - add device tree binding for TC4 ([3cedc47](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/3cedc47b1d4cf46622b4b5413fab01d3224dc872)) + - add DSU PMU node for tc3 ([d3ae677](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/d3ae67771d14e7ffa06793661833654681934d39)) + - add dts entries for MCN PMU nodes ([1401a42](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/1401a42c950751170c5cf14106d1872160d7ecea)) + - add MHUv3 addresses between RSS and AP ([5ab7a2f](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/5ab7a2f2eac2b9e398d83ca2a16738f38a18baf6)) + - add MHUv3 doorbell support on TC3 ([4f65c0b](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/4f65c0beaad1a73e45919eb0b450a86c4f58de27)) + - add MHUv3 DT binding for TC3 ([6c069e7](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/6c069e7168445d5fa1e1a49dbfc269faa65bfa62)) + - add MHUv3 register addresses for TC4 ([36ffe3e](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/36ffe3e1be3fe91e2b709b769eb4f17545f6ce04)) + - add new TC4 RoS definitions ([e9e83e9](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/e9e83e96bb0f7d83dd7e8eae3a3a82f391922bd9)) + - add NI-Tower PMU node for TC3 ([169eb7d](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/169eb7daf248e75d40cd72a434aedc70a3d9ebdb)) + - add PPI partitions in DT binding ([ebc991b](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/ebc991b3a11a01142d8e4d71263c5a9a5f40db1b)) + - add system generic timer register definition for TC4 ([d6b6a8b](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/d6b6a8b7cc9fa872f752640a52b9a752fa50e3a8)) + - add uart node in spmc manifest ([880dcd0](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/880dcd0d791288dab34f9e6668f9491796ef687a)) + - allow TARGET_VERSION=4 ([e8e1b60](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/e8e1b60820dcba1f2be151d296a8e81de9bed8ba)) + - bind DPU SMMU on TC4 ([e365479](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/e365479d0d89999f815ea71b1511ff7952b479e2)) + - bind GPU SMMU on TC4 ([11ec5de](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/11ec5de6957206c9b1ec84b78cccf4e876688a84)) + - bind SCMI over MHUv3 for TC3 ([f2596ff](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/f2596ff1a8c0c3daddcd406a18224fce9af0f1fc)) + - bind SMMU-600 with the DPU on TC3 FPGA ([4c6960c](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/4c6960ca4040e5628874f48576170b6f8f3904a9)) + - bind SMMU-700 with DPU on TC3 ([0458d3a](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/0458d3acae25aa98f28bc0e0aa578fdce7ae92fa)) + - change GIC DT property 'interrupt-cells' to 4 ([1300bbc](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/1300bbce15308868fefda1be9ee7b4fccedde951)) + - configure MCN rdalloc and wralloc mode ([bb04d02](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/bb04d0232e8eeb593028aa730618be35d32a4f22)) + - enable el1 access to DSU PMU registers ([de8b9ce](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/de8b9cedccd652c357aff5311f8d7cb9d663514b)) + - enable Last-level cache (LLC) ([e1b76cb](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/e1b76cb06a70b5c3d9b46a71c26e7e889dcee91b)) + - enable MCN non-secure access to pmu counters on TC3 ([adc91a3](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/adc91a3440af73e2799023117764c6e1b1fd26fb)) + - enable SME and SME2 options for TC4 ([9face21](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/9face2123a5925619d54070d0a9e4e628084eff3)) + - enable trbe errata flags for Cortex-A520 and X4 ([74dc801](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/74dc801d4b284e0b3829ab8ec741e0f2c311a7c2)) + - make SPE feature asymmetric ([7754b77](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/7754b770cff6fb956e0384150c1f84a1a6abc620)) + - make TCR2 feature asymmetric ([3e8a82a](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/3e8a82a030735c14eab0d15fa6f65d7c3f90042d)) + - move flash device to own node ([62269d4](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/62269d47439e34c161f2c4990f9fdc536d82943a)) + - provide target_locality info of AP FW components ([3201faf](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/3201faf3563930d90a0eb2fa6fad92f65b01101e)) + - remove static memory used for fwu ([25a2fe3](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/25a2fe3b74689614f73138d130ab0cae14269b51)) + - setup ni-tower non-secure access for TC3 ([89c58a5](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/89c58a5087f12f0e965ce8fdf946038d5799d07d)) + - specify MHU version based on platform ([04085d6](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/04085d6eb47b67833d0a5444c92c9856b38459f6)) + - support full-HD resolution for the FVP model ([dd5bf9c](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/dd5bf9c5e26ea47988cde76f916495031ecc85c9)) + - update DT for Drage GPU ([b3a4f8c](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/b3a4f8cfcfad1df90273d0e131c2016068c57f61)) + + - **Corstone-1000** + + - add multicore support for fvp ([16f4862](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/16f48623d8d398ec588a958accb037c6debb7f7b)) + + - **Automotive RD** + + - **RD-1 AE** + + - add device tree files ([bb7c7e7](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/bb7c7e713074e6254955e9e64386493a7ad810f1)) + - enabling Trusted Board Boot(TBB) for RD-1 AE ([2638496](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/2638496965edd80e43af71a5952e7005d1fd3e8c)) + - introduce Arm RD-1 AE platform ([f661c74](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/f661c74b528f3aee6f30a28a82e8c76ab26f35f7)) + - introduce BL31 for RD-1 AE platform ([daf934c](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/daf934ca918057b13fecfe949315e097ca358329)) + + - **Aspeed** + + - **AST2700** + + - set up CPU clock frequency by SCU ([e3d1bbd](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/e3d1bbdb08f643ad54e79c678d9f8cadaf63d4ce)) + + - **Intel** + + - add build option for boot source ([ef8b05f](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/ef8b05f559a698cdeca43b3ad287d720f0c22a8a)) + - add in SHA384 authentication ([cab83c3](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/cab83c34871aa3d20bab81d3fca34c3d746c3db4)) + - add QSPI get devinfo mailbox cmd ([8fb1b48](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/8fb1b484ac74f945eb483453b3f7e776c13b7b90)) + - clock manager PLL configuration for Agilex5 platform ([e60bedd](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/e60bedd5e134e2ad996a0d21a8170caec12c2dd2)) + - direct boot from TF-A to Linux for Agilex ([b5c3a3f](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/b5c3a3fc94b43f273332518024d4955e2c54a995)) + - enable VAB support for Intel products ([3eb5640](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/3eb5640a7d9277eee80b5b31bb30230a374e0fb0)) + - pinmux and power manager config for Agilex5 platform ([94a546a](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/94a546acc4d6e659f64266d93d9e74b0a2b86f4f)) + - update Agilex5 DDR and IOSSM driver ([ce21a1a](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/ce21a1a909f2ec98f83c25dd2ed3b7fedd46c46b)) + - update BL2 platform specific functions ([fa1e92c](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/fa1e92c6360280447a63422b3844df5abf186577)) + - update hand-off data to include agilex5 params ([6875d82](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/6875d823ede6f3668e3c176e97083dea97ab236d)) + + - **MediaTek** + + - change log level from INFO to VERBOSE ([5f2f384](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/5f2f384890c44756c6b6d946ae675d72bdadc904)) + - configure DEV_IRQ as G1S interrupt ([240a1ec](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/240a1ecd1818e3098d641bd3304acda8b1744809)) + - move plat_helpers.h to the common folder ([b741293](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/b741293f34e394dc544250b3bad39a148e206f6d)) + + - **MT8186** + + - add common and MT8186 TRNG driver ([8c1740e](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/8c1740e2f260e662ed13fc04e1702c20b66d459f)) + + - **MT8188** + + - add MT8188 TRNG driver ([b88d1f5](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/b88d1f527baa5e2666df465acb85e09a2f8c9f8b)) + - update SVP region ID and permission ([fc77c69](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/fc77c69a17c6228c29113c695efc6aac1a8f6b18)) + - update SVP region ID protection flow ([e66c4ea](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/e66c4ea8ae2c586e648e85370c1f04c0b67bbfcb)) + - update the memory usage for SCP core0 and core1 ([83112aa](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/83112aa24f408fda256c536b0880df46726db593)) + + - **MT8192** + + - update memory protect region ([7587cfd](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/7587cfdd96029247145d992ac042bf3af0c2f20d)) + + - **MT8195** + + - update memory protect region ([4224783](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/4224783f8403031fc12c340efdc87e3cda30fb22)) + + - **NXP** + + - **i.MX** + + - add helper to take params from BL2 ([7eae1db](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/7eae1db027149e361c84395a14115324d430aa52)) + + - **i.MX 8M** + + - **i.MX 8M Nano** + + - optionally take params from BL2 ([c37a877](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/c37a877e563fd3953e3ea0dc29570cbd5e13aa36)) + + - **i.MX 8M Mini** + + - optionally take params from BL2 ([11d32b3](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/11d32b33ea3331adf31fac7fe499176a739178b1)) + + - **i.MX 8M Plus** + + - optionally take params from BL2 ([3d9fea9](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/3d9fea941a3be346ea5382c69b06d05ca470903a)) + + - **i.MX 9** + + - **i.MX93** + + - optionally take params from BL2 ([02d1813](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/02d1813e8701752ec6bb23ad0c1e68be2f4b38e4)) + + - **S32G274A** + + - add ncore support ([5071f7c](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/5071f7c7ee0c1ef1498d71f6ac65e71014044498)) + - enable BL2 early clocks ([66af542](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/66af5425a6c28af7f426a82af4ec7ea4049aa6f2)) + - enable workaround for ERR051700 ([cc6e9b0](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/cc6e9b01900b0f4101e012889b19ff225ff55001)) + - use s32cc clock driver ([f1e4ac5](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/f1e4ac56b53029e67b2cb626b637a4bfe4904866)) + + - **QEMU** + + - **SBSA** + + - handle the information of CPU topology ([c891b4d](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/c891b4d83578db25d24d2a8e3e7e419e65773ac8)) + + - **Raspberry Pi** + + - **Raspberry Pi 5** + + - add PCI SMCCC support ([682607f](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/682607fbd775e37fb5631508434dab9e60220c9a)) + + - **Renesas** + + - **R-Car** + + - **R-Car 3** + + - populate kaslr-seed in next stage DT ([b9e34d1](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/b9e34d14c954a9af21deb70acc4579b4494824fb)) + + - **Rockchip** + + - add RK3566/RK3568 Socs support ([9fd9f1d](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/9fd9f1d024872b440e3906eded28037330b6f422)) + + - **RK3588** + + - enable crypto function ([b833bbe](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/b833bbe6f088e3ee78037515d6c7c5ebb6d9a0cc)) + - support rk3588 ([e3ec6ff](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/e3ec6ff4b24c7daa4dfa82709c23a22829947160)) + - support SCMI for clock/reset domain ([04150fe](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/04150fee44cc0dec5bbe4cce42e2b626695d6f52)) + + - **ST** + + - add FWU with boot from NAND ([795a559](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/795a559bc59887543afa76f05397382befd14fb8)) + - add stm32mp_is_wakeup_from_standby() ([87cd847](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/87cd847ce5640039068993868d6f853e9035c01a)) + - manage backup partitions for NAND devices ([ae81d48](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/ae81d48d8366bf2d7e890741bb92262b3d3a1aaa)) + - manage BL31 FCONF load_info struct ([aa7f6cd](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/aa7f6cd8b363fb97efd232991eb9ccedc2316a9d)) + + - **STM32MP1** + + - always boot at 650MHz ([f655922](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/f65592278869951330325085cf373c3306ccab57)) + - handle DDR power supplies ([47e6231](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/47e62314b6baee0e5647c903b0feeba47f804df0)) + + - **STM32MP15** + + - remove OP-TEE shared mem ([8dd2a64](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/8dd2a64a12b3ee47507aab4fb0294d366a5a5159)) + + - **STM32MP2** + + - add BL2 boot first steps ([db77f8b](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/db77f8bf227b1ffc6b282408aeccc4737cb1fc78)) + - add BL31 device tree support ([27dd11d](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/27dd11dbf5a7dc3d9894e6bae9630b4e5aa36d59)) + - add defines for the PWR peripheral ([6add715](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/6add715405bd92e5f5ad59da79c3a23031162544)) + - add fixed regulators support ([c3a7534](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/c3a7534167b22d6a14fb0ee224bbb7b49478a479)) + - add fw-config compilation ([5af9369](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/5af9369c6ce0beff681ce1548bb5d614c3a6a85e)) + - add helper to get DDRDBG base address ([2fd7b23](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/2fd7b230ee8605d109167e1a6f76d87c7fb132f7)) + - add minimal support for BL31 ([03020b6](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/03020b6688b459da84bdb2a3fb58c99916bfd7f7)) + - add RETRAM map/unmap capability ([52f530d](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/52f530d3ab9d27db653670511b238d54e212cf0f)) + - add RISAB registers description ([631c5f8](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/631c5f86d5438e92e1d64e7dfdab58e92ad3e24f)) + - boot BL33 at EL1 or EL2 ([c900760](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/c900760d47d9fa9833610f5b831712cec1ba2ef2)) + - disable unsupported features ([128df96](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/128df96579f4837ed9571a1843a5b842de52ed3c)) + - display CPU info ([381b2a6](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/381b2a6b02ef5b0245f200b8c2d42a4a58cf88be)) + - enable DDR driver ([213a08e](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/213a08eb422a69bc7c95579fadf076f5af152f49)) + - enable DDR sub-system clock ([5e0be8c](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/5e0be8c0241e5075b34bd5b14df2df9f048715d3)) + - get chip ID ([154e6e6](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/154e6e62fe851b95cd17087a8cdd53bfbb39613b)) + - handle DDR power supplies ([e2d6e5e](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/e2d6e5e21adcf9e41a335c31d5c337c65ad0a133)) + - improve BL31 size management ([64e5a6d](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/64e5a6df4638af5a5c308c9ebd4aee5a839f7e3e)) + - initialize gic and delay timer in bl31_plat_arch_setup ([77847f0](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/77847f037df3e28ac221396f118e9fd4189b1894)) + - introduce DDR type compilation flags ([d07e946](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/d07e9467d375bd414fefc86dead4a833572a166a)) + - load FW binaries to DDR ([9a0cad3](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/9a0cad3917e6bb76694e02fd2e099ccb564a6431)) + - load fw-config file ([a846a23](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/a846a23596d97b90f203dc39aeef00c0ccd88b9d)) + - manage DDR FW via FIP ([ae84525](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/ae84525f44ddfe8abd66644475899fdc19893481)) + - print board info ([cdaced3](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/cdaced366844b80024a8871adcbc94fbe31f6f1b)) + + - **Texas Instruments** + + - implement DM_MANAGED suspend ([9b7550f](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/9b7550f1f0caaa20acb6140211ac298e74894f22)) + + - **Xilinx** + + - add feature check function for TF-A specific APIs ([9a0f5d1](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/9a0f5d128ac70da64bc33731c4e4b29007692cc3)) + - add none console ([6d41398](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/6d41398382430134308a513c027b77ec70b03ae4)) + - remove PM_IOCTL and PM_QUERY_DATA APIs ([924f8ce](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/924f8ce2e966d2ffdb2c0f29c72cb3a68d293b45)) + - update SiP SVC version number ([c26aa08](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/c26aa08bee58e81710ee9d884247fdf9b23c0022)) + - update TF-A to passthrough all PLM commands ([4661c8f](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/4661c8f508d3ecdb7a258c71a26f489ea1bffc21)) + + - **Versal** + + - add DTB console to platform.mk ([d629db2](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/d629db247648acdb703d841b4d3d303506af6ff0)) + - add support for QEMU COSIM platform ([db827f9](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/db827f99a0132389ab18836b9419406b45ccd11c)) + - dedicate console for boot and runtime ([d533f58](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/d533f58d556e729a5705b9f1aaeac467291dc686)) + - deprecate build time arg VERSAL_PLATFORM ([09ac1ca](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/09ac1ca27c6497cd1e04e108d4d927500d737991)) + + - **Versal NET** + + - add DTB console to platform.mk ([d61ba95](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/d61ba95eecf61b660cc5161a7e4fd68948775e39)) + - dedicate console for boot and runtime ([28ad0e0](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/28ad0e0209ac38711d69384da9f706f43e4cc681)) + - set lower cluster bus qos value ([c6f6202](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/c6f62027afb2e888b0c5f1eccc42c23bab0885ef)) + + - **ZynqMP** + + - add DTB console to platform.mk ([09a02ce](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/09a02ce0bd37585a85f5b3e7f8dd6d7dc82e5f14)) + - dedicate console for boot and runtime ([4557ab6](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/4557ab69fe371137d44f8a0ee6bb2129886ab6cd)) + - enable ENABLE_LTO flag ([19d8756](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/19d875677e368e96ca0e96ec59e0c60a092114b4)) + - move zynqmp platform to xlat tables v2 ([fdda980](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/fdda980af4b8c8d59374785681a153afda8f71e2)) + + - **AMD** + + - populate handoff from TL ([1fbe81f](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/1fbe81febd4fc69813188ceefb4cbe95a3410ed9)) + + - **Versal Gen 2** + + - add dtb & runtime console ([1196474](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/11964742d6557c314b6106a8630a3317666c708f)) + - add dummy implementation for SCMI PD ([095a20a](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/095a20a70ce55a08752214fc9eb46bffe4a44a21)) + - add support for AMD Versal Gen 2 platform ([c97857d](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/c97857dba2588ce44dd1d9907797f9f4e952fea7)) + - implement USB_SET_STATE dummy IOCTL ([282bce1](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/282bce19bbdb3a95a5365a0385aecfbfa4293ae6)) + - support dynamic XLAT tables ([9aa71f4](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/9aa71f48bcf98c047e920a8c671b8f5c58b57b74)) + +- **Bootloader Images** + + - add plat handler for image loading ([a03dafe](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/a03dafe5164fd3ec81915c49f4e50f0f927726ea)) + + - **BL32** + + - setup GPT in BL31 in RESET_TO_BL31 boot flow ([1547e5e](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/1547e5e66675ec11bf6dc5958d2d5cff1948cd1f)) + +- **Services** + + - **RME** + + - **RMMD** + + - el3 token sign during attestation ([6a88ec8](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/6a88ec8b300ca88ba7b6ba8d9626b66a7ee87116)) + + - **SPM** + + - **EL3 SPMC** + + - support simd context management upon world switch ([59bdcc5](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/59bdcc58c3948cd24428c0aef7c478128b2a0bde)) + + - **SPM MM** + + - switch to simd_ctx_save/restore APIs ([e6e3486](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/e6e348689a4b25089145abb798fc2b2aabf6f90b)) + + - **Secure Payload Dispatcher** + + - **ProvenCore** + + - switch to simd_ctx_save/restore apis ([a9b64ed](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/a9b64ed969edffe020e2096b5006b27373218ff6)) + + - **Trusty** + + - switch to simd_ctx_save/restore apis ([7461025](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/74610259856a1df5ca7b9516e74478bb16490a95)) + +- **Libraries** + + - **CPU Support** + + - add support for arcadia cpu ([8fa5460](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/8fa54607088314aa8e3db1da5649276f2544c75a)) + - add support for cortex-a720ae ([8118078](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/8118078b71583e01a486da01f1bf369b4fde3c59)) + - add sysreg_bitfield_insert_from_gpr macro ([ad8b514](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/ad8b51418e3c9e19ddc957424ab19386711ba7ee)) + + - **EL3 Runtime** + + - **Context Management** + - context switch MDCR_EL3 register ([123002f](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/123002f9171384d976d95935b7f566740d69cc68)) + - introduce EL3/root context ([40e5f7a](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/40e5f7a58f906beef74587a06f7fc35efe20537d)) + - add Root-Context documentation([0f3cd51](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/0f3cd5150c8f530bb96b84b0ae8129f749835ba3)) + - enhance the cpu_context memory report ([781e1a4](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/781e1a44e0cdbd1fd8bbd978a60dcc947eecf29e)) + - move mpam registers into el2 context ([7d930c7](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/7d930c7e599de10bf2418cc93a176122211e7bbb)) + - convert el1-ctx assembly offset entries to c structure ([42e35d2](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/42e35d2f8c0ec3b931a0da90cb0111369aecea1f)) + - add explicit context entries for ERRATA_SPECULATIVE_AT ([59b7c0a](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/59b7c0a03fa8adfc9272f959bd8b4228ddd2607a)) + - remove el1 context when SPMD_SPM_AT_SEL2=1 ([a0674ab](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/a0674ab08192e2175afe919f929c9985adc32174)) + - support for asymmetric feature among cores ([2f41c9a](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/2f41c9a7be46b148d557d3d933547c6e9ad1fd40)) + - asymmetric feature support for trbe ([721249b](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/721249b0c0cce9fbe60175af6ee895e2bb7a6d10)) + - handle asymmetry for FEAT_TCR2 ([f4303d0](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/f4303d05ead1026ce5f97f83558f15159e7d6476)) + - handle asymmetry for SPE feature ([188f8c4](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/188f8c4b6040a35adce6f6c15670f2af436df0c3)) + - test integrity of el1_ctx registers ([7623e08](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/7623e085cb5396054b72f1ea3f02e8c7a34568b5)) + - keep actlr_el2 value in the init context ([0aa3284](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/0aa3284a45ccf4405cda0bb76f6b16a33e87f222)) + + - **SIMD** + + - add data struct for simd ctxt management ([841533d](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/841533dd5345dfd7ab78effe1544dc72b6ec840d)) + - add routines to save, restore sve state ([6d5319a](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/6d5319afecf62f931fe03c12f2dbc398e959c7f0)) + - add rules to rationalize simd ctxt mgmt ([3524d07](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/3524d0742e6dd4e8ed9e7a11d8268a9ea2f42c6a)) + - add sve state to simd ctxt struct ([4242262](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/42422622f924b0cf636864e045e38110e97ac126)) + - introduce simd context helper APIs ([308ebfa](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/308ebfa18859c89c8b630c1c130e7002095e875f)) + + - **GPT** + + - change the default max GPT block size to 512MB ([01faa99](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/01faa994ceb2635a175f1d299d3b2cd7afd036c0)) + - add support for large GPT mappings ([ec0088b](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/ec0088bbab9335c5273e57a84b81adf2201a51db)) + - configure memory size protected by bitlock ([d766084](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/d766084fc48ed83890c63a7ef773b8fff9e4ea86)) + + - **C Standard Library** + + - avoid CWE-190 for GENMASK macros ([1f0b6e7](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/1f0b6e756a6d1894f7ec8423fac18671b55c51af)) + - fix MISRA 12.2 violations for BIT32 and BIT64 macros ([0605b7e](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/0605b7e8af4980d4e26afc6720dcbf2644633c53)) + + - **PSA** + + - introduce generic library for CCA attestation ([98d36e5](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/98d36e5b02f859866da6782a8ad73b0d26d781e8)) + + - **Firmware Handoff** + + - fix register convention r1/x1 value on transfer list ([7475815](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/7475815f4b3697f6c61868e4ae6680baee8b93e2)) + - make tl generation flexible ([2329e22](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/2329e22b8bec6fdbb1b5531f3d29569519782a63)) + +- **Drivers** + + - **Generic Clock** + + - add set_parent callback ([a2c6016](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/a2c6016f927e4b9a23499005c63f3e46f48ff8a2)) + - add set_rate callback ([19f9e2e](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/19f9e2e657918d023c9836f8330a967e97a45d7e)) + + - **NXP** + + - add clock skeleton for s32cc ([3a580e9](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/3a580e9e472a5506da82227e809e0bd472dea1b1)) + - add Linflex flush callback ([95ac568](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/95ac568b6137ee8d3a53d3ec911a7116c90e8d5d)) + + - **Clock** + + - add A53 clock objects ([44e2130](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/44e2130ab9948530cd5eb3fbd1d6d8ead6336845)) + - add ARM PLL enablement ([b5101c4](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/b5101c452e3fefdf4fe13d944372e5ad5d2ea5c4)) + - add ARM PLL ODIV enablement ([84e8208](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/84e82085a1d59624ab7dc14256a152d6d7dd15f2)) + - add CGM0 instance ([9dbca85](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/9dbca85ddf0c9a7c64e4207b74c25a09fd923aba)) + - add clock objects for ARM DFS ([44ae54a](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/44ae54af5cadb499cb72cc0edd71711d7a2d019e)) + - add clock objects for ARM PLL ([a8be748](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/a8be748a2821355734f603342b2d2cf7105f6a30)) + - add dependencies for the XBAR clock ([5692f88](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/5692f881f5064f612719a4f6e7aa3a4abb827439)) + - add DFS module enablement ([4cd04c5](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/4cd04c50eb4de7dfd65f8811331f0ed3f9f4037c)) + - add FXOSC clock enablement ([8ab3435](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/8ab34357497b454b2f5e505d06ce9437da7772e4)) + - add get_parent callback ([96e069c](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/96e069cb8ec72b6ac3cac0e7708749cb3fe13abb)) + - add MC_CGM clock objects ([3fa91a9](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/3fa91a94501ed13587132f6e2aec66a6c054c61e)) + - add MC_ME utilities ([b8c68ad](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/b8c68ad799523229ed7c0a9d025b22f74ffe9eed)) + - add minimal set of S32CC clock ids ([086ee20](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/086ee20fe7ccb9dcbf6e9ee1ce529ae98e6cf977)) + - add objects needed for DDR clock ([4a2ca71](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/4a2ca718571b3b46cd091cac50c83e9f76c5927b)) + - add oscillator clock objects ([7c36209](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/7c36209b29da152cc5e98b6a141fe85d78fca84b)) + - add partition reset utilities ([11a7c54](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/11a7c54072f651512948446e432421ba7ee57469)) + - add partitions objects ([af3020e](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/af3020e2ae86b71a87d936bb5e7181393874d708)) + - add PERIPH PLL enablement ([8653352](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/8653352ad72e0f95dfd44f2ef9d1b2406dd8dca5)) + - add set_parent callback ([12e7a2c](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/12e7a2cd2f8f535dfd63834ce78e3fc248ff39f2)) + - enable the A53 clock ([7004f67](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/7004f6782e0c9c7c5875b294af049cd022695cbb)) + - enable the DDR clock ([8a4f840](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/8a4f840b1e13b0187b373e014ea314c3dabb122d)) + - enable the XBAR clock ([b8ad880](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/b8ad8800b2b13d40a6ea1e997e6feb573744665b)) + - enable UART clock ([e4462da](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/e4462dae81d0674eaf07ad8fa61b25b28a209d0b)) + - implement set_rate for oscillators ([d937351](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/d9373519873b11cf7d9cad57742272c80d8967e7)) + - refactor clock enablement ([5300040](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/5300040bfd0acf0e839a9828a1a5341afc936e36)) + - set parent for ARM PLL and MC_CGM muxes ([83af450](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/83af45042debcaf76f2f898984f1b74dedc477e1)) + - set rate for clock fixed divider ([65739db](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/65739db28bf0c0d5d4daa8735a2935681f835634)) + - set rate for clock muxes ([64e0c22](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/64e0c2260fa385bdf91d7e3471e10ab251c96644)) + - set rate for PLL divider objects ([de950ef](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/de950ef04f2bf71924d7ac65e86cfc0cfd97aae3)) + - set rate for PLL objects ([7ad4e23](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/7ad4e2312f58606ee74ac7c655a655bd85148582)) + - setup the DDR PLL ([18c2b13](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/18c2b137f84fed5929ee5f21cbec9260670814a2)) + + - **ST** + + - **Clock** + + - add function to restore generic timer rate ([bfe8a12](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/bfe8a12eea3d51c07570cce65ea7a290db0ab9ce)) + - add STM32MP2 clock driver ([615f31f](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/615f31fe40e5ebf9ecef81eb01abbe52984e093a)) + - don't gate/ungate an oscillator if it is not wired ([f2aebab](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/f2aebab8591ef9370159fc9ddf976599bdef6349)) + - update with new bindings ([ae1e503](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/ae1e503763c8bc52eba1a38e320539d61ebe2043)) + - use early traces ([1a25db1](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/1a25db196d8fb4da379ecea43d0d004470806ee6)) + + - **DDR** + + - add STM32MP2 driver ([79629b1](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/79629b1a79bd1ee254077d4e76fea05ba73b9bab)) + + - **GPIO** + + - add set GPIO config API ([bfa5f61](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/bfa5f61b579f9eaeead1278efc5997ddd4b5543a)) + + - **ST PMIC** + + - add STPMIC2 driver ([817f42f](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/817f42f07ede5ef55dab857cde4e9601e349ad75)) + + - **Regulator** + + - add enable ramp-delay ([6897ae8](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/6897ae8d0f4bba1b147f572306782b1aa6b18666)) + - support regulator_set_voltage for fixed regulator ([156ed97](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/156ed9724f95643dd749b5ed00a7a4b92bab1c71)) + + - **Reset** + + - add stm32mp2_reset driver ([f829d7d](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/f829d7df7e261fb8f68e21dbceab8c77ce65aedd)) + - add system reset management ([d91d10a](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/d91d10ab39b29339f1c98d95745ba98476fd7e46)) + +- **Miscellaneous** + + - **DT Bindings** + + - add missing SPIx bus clocks ([c6d50c9](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/c6d50c9f933a0e11c419848d30ff018d404c9a42)) + - describe ST GPIO banks and config ([deb9c86](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/deb9c864eac86b4c7a57ec5bf90d301f7f741bd0)) + - introduce Dualroot CoT DTB ([703df3a](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/703df3a3ef4aafe30a3522b80ec305a9833f732d)) + - new RCC DT bindings ([52b253b](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/52b253bfa2b1788d30339f75cfe39bce387496f3)) + + - **FDT Wrappers** + + - add function to read uint64 with default value ([bc8dfca](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/bc8dfca64d07185304a5acfe87a039c8a6649a4c)) + + - **FDTs** + + - add DDR4 files for STM32MP2 ([178aef6](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/178aef6989395f956b0e149b2b33cdfc0ac2e854)) + + - **STM32MP1** + + - move RNG1 to CSI to improve random generation ([d594239](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/d594239d4ebf2d44521bc30ec4b59b23f08c5a36)) + - new RCC DT bindings for STM32MP1 ([4391e5e](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/4391e5edea930810e68d087ddeb02d06886d891d)) + - remove PLL1 settings ([66d7c8b](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/66d7c8bf8ef12f3424fc6da214f9fc65d4cf82b5)) + - remove RTC clock configuration ([703a581](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/703a581e2522bffe21b421c98994dc02aed2934c)) + + - **STM32MP2** + + - add BL31 info in fw-config ([a370c85](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/a370c856f1f7655384f8e06f7fd84ded63838c02)) + - add clock tree for STM32MP257F-EV1 ([293a4f3](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/293a4f3defe95eddaccd671783e4ff855f1d6f8b)) + - add fw-config file ([513b5cc](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/513b5cc83add907f2faa8587e1d24195294c03a5)) + - add fw-config files for STM32MP257F-EV1 ([83f571e](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/83f571edb49e35855fa1ab277b3788354d6e707b)) + - add I2C7 pin muxing ([0a08208](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/0a0820885d341cc26620c37f6c10ca478955d11f)) + - add io_policies ([53e8982](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/53e89824aa2b4107a583150d1b14b855f25cd63c)) + - add memory node ([e34839b](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/e34839b9a275ec9d8487875fc8ef1949a1c41665)) + - add SD-card and eMMC support on STM32MP257F-EV1 ([1dafb40](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/1dafb409ba94b3b5c8caba08f691c099e5a7433d)) + - add sdmmc nodes in SoC DT file ([3879761](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/3879761fc206d8b3c04f0fb48d811efc267c025f)) + - add sdmmc pins definition ([6a85f67](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/6a85f6710fb03474d3724667e806ab7deff84814)) + - add UART and I2C nodes for STM32MP2 ([c7cfe27](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/c7cfe27a2412cceef6e1e217798d2f3fc43abded)) + - describe stpmic2 power supplies ([e974670](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/e97467068a2defaea92ec6acaf76b9f416de02a1)) + - remove pins-are-numbered ([a1a50ef](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/a1a50ef1e2f7c5aac89c65b8a7bc67b1f502f21d)) + - update STM32MP257F-EV1 DT ([f0d6dcb](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/f0d6dcb2bf5e3d382c908a28d1dc670b4914d366)) + + - **STM32MP25** + + - add DDR power supplies ([7323c7f](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/7323c7f9a30391f14dca7ae0627e1a3ce32b3515)) + - add DDRCTRL and DDRPHY settings in DDR node ([56ac99a](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/56ac99a04cac9f29e75153c6bf84e37d2f746f0b)) + +- **Documentation** + + - add DPE to RSE design doc ([e4582e4](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/e4582e424799c6072e03d1c6244109eb069ac4bd)) + - add RMM option in build-options.rst ([1b7f51e](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/1b7f51ea1662810dea4112a543f2309fe44fdca6)) + - add RSE provided mboot backends to the threat model ([3849d27](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/3849d272e3b1317ad660df37f1501cb11827e600)) + - add STM32MP2 docs links ([21b6260](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/21b6260ec8d83fc9dbbfca22ef3addcf2018da9f)) + - update mboot threat model ([07c2d18](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/07c2d18f4ef6cd1ce61326e0e85d93abe8f2f4ed)) + +- **Build System** + + - add ability to define platform specific defaults ([1b2fb6a](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/1b2fb6adb53de652d3fe69984731a62da122e0da)) + - add ctags recipes for indexing assembly files ([54b773e](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/54b773e18336b2b01b52686799192808b5aa2751)) + +- **Tools** + + - **Transfer List Compiler** + + - add command gen-header ([9b05c37](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/9b05c3739c44418f47c2b50980fe24651a1eed1f)) + - add host tool for static TL generation ([6ac31f3](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/6ac31f3e76021fed1951d8b62105e6708123f8e3)) + - add support for tox ([38487c7](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/38487c7fd3f337298ceb60657a6bca5f11816b56)) + - add creating transfer lists from yaml files ([3112099](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/311209934e78b1d7005ae48c95b0d45c08c1c728)) + - add option to input attr as string of flag names ([4dcbba9](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/4dcbba98cee2260e4c4f680f6a7fda5a98fdc7d5)) + - add option to input text instead of tag id number ([792e8e8](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/792e8e896f81fff3e0d75dca5f633903fa18f55e)) + + - **Chain of Trust device tree to C source file** + + - standalone CoT dt2c tool ([4274d6f](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/4274d6f885f9df1845d5a6a0b4145cd2f289f4bb)) + - fix various breakages ([73f7b7d](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/73f7b7ddbe9c86520c47a9ceb9dc95f224aa0bc6)) + - use processed Device Tree source file as input ([e19977d](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/e19977d664027bb16324b1b5e1aaa0ca097e637b)) + - update documentation for cot-dt2c ([b95f398](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/b95f398ebd58785f29b96d94d14aec1301f42355)) + + +## [2.11.0](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/refs/tags/v2.10.0..refs/tags/v2.11.0) (2024-05-17) + +### ⚠BREAKING CHANGES + +- **Architecture** + + - **Memory Tagging Extension2** + + - Any platform or downstream code trying to use + SCR_EL3.ATA bit(26) will see failures as this is now moved to be + used only with FEAT_MTE2 with + commit@ef0d0e5478a3f19cbe70a378b9b184036db38fe2 + + **See:** remove mte, mte_perm ([c282384](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/c282384dbb45b6185b4aba14efebbad110d18e49)) + +- **Services** + + - **SPM** + + - **SPMD** + + - Given the optimizations made in TF-A SPMD to simplify NS EL1 context + management, platform integrators must use SPMC binaries built by + picking commits after 2fc6dcfa97e05159f95859fcf68db3031586f8c7 from + hafnium repository. + + **See:** skip NS EL1 context save & restore operations ([2d960a1](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/2d960a11601be6e7f24c38d84b2a4fdbb52efb9b)) + +- **Drivers** + + - **Arm** + + - **RSE** + + - remove PLAT_RSS_NOT_SUPPORTED build option + + **See:** remove PLAT_RSS_NOT_SUPPORTED build option ([878354a](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/878354a845cbc51c198b879d3d92ed472e21889c)) + + - **FWU** + + - add a config flag for including image info in the FWU metadata ([11d05a7](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/11d05a77295885f27530cf07029ebc2b36f49918)) + - add a function to obtain an alternate FWU bank to boot ([26aab79](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/26aab79560a2281c4207b01102495459c2bddefc)) + - add some sanity checks for the FWU metadata ([d2566cf](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/d2566cfb896672ea07c31c37e7acd9ef77abc4fb)) + - document the config flag for including image info in the FWU metadata ([7ae1619](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/7ae16196cc73a580f298734bb98f2ccb210e3ba9)) + - migrate FWU metadata structure to version 2 ([a89d58b](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/a89d58bb204c00db260225859bce0b55aa5e2385)) + +### New Features + +- **Architecture** + + - **CPU feature / ID register handling in general** + + - add cortex-a35 l2 extended control register ([a727d59](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/a727d59d9c1ef5ecf2f221ce289506da2011dda1)) + - add feature detection for FEAT_CSV2_3 ([30019d8](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/30019d8698b219d4a642dc59e7178006f59654ff)) + - added few helper functions ([30f05b4](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/30f05b4f5db605ddc1a3ca0ae0cbd13ed0e728b6)) + + - **DynamIQ Shared Unit (DSU)** + + - save/restore DSU PMU register ([f99a69c](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/f99a69c386ce5448edfc47eaf146d1a20ac8216e)) + + - **Memory Tagging Extension2** + + - add mte2 feat ([8e39788](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/8e3978899a481484d8c60bf276be503aebd43afb)) + +- **Platforms** + + - update SZ_* macros ([6d511a8](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/6d511a8c31f0d792695566ae75c8f7b08b3b7236)) + + - **Arm** + + - add COT_DESC_IN_DTB option for CCA CoT ([b76a43c](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/b76a43c9382e85969cac896cd4d5d6774d0d1553)) + - add trusty_sp_fw_config build option ([0686a01](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/0686a01b0cacb9aab840a5c334409b5739a95a97)) + - move GPT setup to common BL source ([341df6a](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/341df6af6eb911ffd175e129f61fc59efcf9fcea)) + - retrieve GPT related data from platform ([86e4859](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/86e4859a05614b40ff3cf38f8bd4efc856c546fe)) + - support FW handoff b/w BL1 & BL2 ([9c11ed7](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/9c11ed7e3e5536ad1fcb9190560e0368da9c5ab5)) + - support FW handoff b/w BL2 & BL31 ([a5566f6](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/a5566f65fd1be689ca5c63baa1f5b61b40960c8d)) + - add platform API that gets cluster ID ([e6ae019](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/e6ae019a84c4d2ad2d2825b32fbcbe304752e3ae)) + + - **CSS** + + - initialise generic timer early in the boot ([3447ba1](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/3447ba1f0405a8590ec31e4b79737efe151c3d5b)) + + - **FVP** + + - add CCA CoT in DTB support ([4c79b86](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/4c79b86ed6a36b572cf9e96f0269eb5dd0b46d5f)) + - add stdout-path ([8c30a0c](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/8c30a0c7fe0162de0618b26fb34cc91ea582e5f7)) + - add support for virto-net, virtio-9p and virtio-rng ([51b8b9c](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/51b8b9c3c46cec87ebb7b484727c80ff29d73057)) + - added calls to unprotect/protect memory ([6873088](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/6873088c2cd6983025b6777d4c3bde912eade571)) + - delegate FFH RAS handling to SP ([d07d4d6](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/d07d4d63374b0d155b9281f9fcaf6b44f18117c8)) + - remove left-over RSS usage ([a1726fa](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/a1726fa7ffecdcc8f8f4d09bd0bdc97ef3b72f11)) + + - **Neoverse-RD** + + - add scope for RD-V1 ([86a4949](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/86a4949fd012a9912c8bf909d14e20657bba2240)) + - add scope for RD-V1-MC ([6fb16da](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/6fb16dac6e6672040ec80f85f2f337f52cf3f3d3)) + - add scope for SGI-575 ([18b5070](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/18b50707f7732a8b3deb46d8d011566199711c0b)) + - disable SPMD_SPM_AT_SEL2 for A75/V1/N1 platforms ([b9c3273](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/b9c32730e5b7efe5170ed3c0dda7ab9db397c478)) + - disable SPMD_SPM_AT_SEL2 for N2/V2 platforms ([301c017](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/301c01748ea717d0f2cf3ba1f0a2fe389b6fb155)) + - enable AMU if supported by the platform ([fed9368](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/fed9368529e5bc2c9111ac5a743688166661fd8f)) + - remove unused SGI_PLAT build-option ([2d32517](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/2d32517ce64886f154c6d509f80d0fcde05dc498)) + + - **SGI-575** + + - remove SGI-575 from deprecated list ([f104eec](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/f104eecdea209af87de43c62811a0a9456f2838c)) + + - **RD-E1-Edge** + + - remove support for RD-E1-Edge ([c69253c](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/c69253cc3ad3063380c8f905125fe85f6d942d09)) + + - **RD-N1-Edge** + + - remove RD-N1-Edge from deprecated list ([78b7939](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/78b793956f3a86a3dd62394c858ae9ee41379b8b)) + + - **RD-N2** + + - enable NEOVERSE_Nx_EXTERNAL_LLC flag ([ab2b363](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/ab2b3632171dd5488952ba3f68693e490857e9dc)) + - add dts for secure partition ([49df726](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/49df7261be44d5199a930c95667edb6b878355d1)) + - enable AMU if present on the platform ([2cfedfa](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/2cfedfad9c2c59316adf17d4f0ee561b50a041b6)) + - enable MTE2 if present on the platform ([3a5b375](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/3a5b3753033561cb5d7cd7aace634cc66eab0fa7)) + - update power message value to 0 ([08f6398](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/08f6398b2b9566812cd110498e3135dfc2e3e494)) + + - **TC** + + - add arm_ffa node in dts ([4fc4e9c](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/4fc4e9c969930d83f1144441199301d3b4b34a5a)) + - add DPE backend to the measured boot framework ([e7f1181](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/e7f1181f8a7729acb07ebac86944e36932bcd09e)) + - add DPE context handle node to device tree ([1f47a71](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/1f47a7133f7fe7fb038aca97fc93533964b2b429)) + - add dummy TRNG support to be able to boot pVMs ([7be391d](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/7be391d1ce5683c717fcf2be584f3d294ebc2bf3)) + - add firmware update secure partition ([d062872](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/d0628728a627ee11c97839640d404221a74c3a65)) + - add memory node in the device tree ([5ee4deb](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/5ee4deb8e69175f57fa51519ef37e3674aa6b9a0)) + - add PMU entry ([553b06b](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/553b06b5d4f7ec8e49796e0ffdf081bf5cf30d53)) + - add RSS SDS region right after SCMI payload ([6f503e0](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/6f503e0eea23a2663ed5cbfe9b925e1e0d65c236)) + - add save/restore DSU PMU register support ([b87d7ab](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/b87d7ab13f4b03f872c3c4a3dd7c755baf3a38d3)) + - add SCMI power domain and IOMMU toggles ([a658b46](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/a658b46dc74ceaa51d119bd7bd9eccdefb0cc455)) + - add spmc manifest with trusty sp ([ba197f5](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/ba197f5f708fe8e033971c6f4d5b25f6783aaa45)) + - add TC3 platform definitions ([62320dc](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/62320dc4fd2c13d9f4b227fe73cad2a79bdba42c)) + - allow booting from DRAM ([18f754a](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/18f754a275083ea66823b1c9f39e234cf430140e)) + - choose the DPU address and irq based on the target ([8e94163](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/8e94163ec041f2d7df41c2dfd8625c06655ba08e)) + - enable gpu/dpu scmi power domain and also gpu perf domain ([127eabe](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/127eabeddfc4fb596a1b499fe68ee6f7e5b5b6d5)) + - factor in FVP/FPGA differences ([1b8ed09](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/1b8ed0993fc5c04f76d949df7e2851e67040bbf9)) + - get the parent component provided DPE context_handle ([467bdf2](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/467bdf26b64a38cfbfb3bf8ab915eb97eb6b3037)) + - group components into certificates ([6df8d76](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/6df8d7647dad5c347d363554d25e590d24eb05e5)) + - interrupt numbers for `smmu_700` ([2c406dd](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/2c406ddaf700e0f1c80535e309a2245b9e0bee92)) + - introduce an FPGA subvariant and TC3 CPUs ([a02bb36](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/a02bb36caa521259ae57a904dedb7fd4e6a51340)) + - pass the DTB address to BL33 in R0 ([638e4a9](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/638e4a92d80346b4d46ef2cc5fbb7941d1b7fd31)) + - provide a mock mbedtls-random generation function ([a877818](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/a8778185d2fd2b80cee8af7879ecb92be1aa3898)) + - share DPE context handle with child component ([03d388d](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/03d388d8e3eb5c6cce65afba060a16fae83d4d12)) + + - **Intel** + + - add in QSPI ECC for Linux ([4d122e5](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/4d122e5f199ad1531650ae11de5121057cfc0855)) + - enable query of fip offset on RSU ([6cbe2c5](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/6cbe2c5d19c4af0ba6bbba049962bf55454da8bb)) + - enable SDMMC frontdoor load for ATF->Linux ([32a87d4](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/32a87d440087e0a71765a61ec341af7cfcfbda97)) + - increase bl2 size limit ([2d46b2e](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/2d46b2e46189120b6779cd27ec6bd6ec9901f72c)) + - restructure watchdog ([47ca43b](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/47ca43bcb4565a992bf527f68e1ff60fc036fd12)) + - support QSPI ECC Linux for Agilex ([d6ae69c](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/d6ae69c8c69016d05d64752538aad53f319b88a2)) + - support QSPI ECC Linux for N5X ([6cf16b3](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/6cf16b36821b9f2a60ed9abbaa593ef62b8b9f2b)) + - support QSPI ECC Linux for Stratix10 ([8be16e4](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/8be16e44cf0143e8651090d80bd14194aa78b1f2)) + - support query of fip offset using RSU ([62be2a1](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/62be2a1ae3efcba0bb8b7ec8ef73b2a0f5a437e3)) + - support SDM mailbox safe inject seu error for Linux ([fffcb25](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/fffcb25c3c2171624c582d92173154f570708a9a)) + - support wipe DDR after calibration ([68bb3e8](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/68bb3e836e93b271f9f1c05787025dd3f04dd788)) + + - **MediaTek** + + - remove bl32 flag for mtk_bl ([9c41cc1](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/9c41cc182dd7acf541565ab3df7a4261fb7eaf1b)) + + - **MT8188** + + - add secure iommu support ([5fb5ff5](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/5fb5ff5694c1bcf0ddfc972600b69d7494ca6645)) + - remove apusys kernel handler usage constraints ([0c77651](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/0c77651fb47c7ffd4b1b37a74aea77373179ab5d)) + + - **NXP** + + - **i.MX** + + - **i.MX 8M** + + - add 3600 MTps DDR PLL rate ([f1bb459](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/f1bb459c3192eb6b3fc6b9b77658d82227eae2d5)) + - add defines for csu_sa access security ([81de503](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/81de50372c9192098118fc8bddaf086a620add87)) + - add imx csu_sa enum type defines for imx8m ([2ac4909](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/2ac4909a5ec0a50a75cab9bb587fb1b8e592794d)) + - make bl33 start configurable via PRELOADED_BL33_BASE ([9260a8c](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/9260a8c818aadbf513b2744cad978c18d0f65a8e)) + - obtain boot image set for imx8mn/mp ([6d2c502](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/6d2c502afb845e7af94c610ab5a375b868c885ba)) + + - **i.MX 8M Mini** + + - restrict peripheral access to secure world ([1156c76](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/1156c76361c170c83c6b9a9dd7c22aa401a4ce2e)) + - set and lock almost all peripherals as non-secure ([f4b11e5](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/f4b11e59b81af3e485e6992b10b50b362902eee1)) + + - **i.MX 8M Plus** + + - restrict peripheral access to secure world ([0324081](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/0324081af0105af536992c8ced2caa5a1928010f)) + - set and lock almost all peripherals as non-secure ([cba7daa](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/cba7daa10576684670e06d05ff02888a5b4f16bf)) + + - **i.MX 8Q** + + - detect console base address during runtime ([52ee817](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/52ee8173041c46aafcfa43f004029dddbfa9f9b5)) + + - **i.MX 8ULP** + + - add a flag check for the ddr status ([4fafccb](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/4fafccb9a8f7b35406b08743f6d9c9b519b01c61)) + - add APD power down mode(PD) support in system suspend ([478af8d](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/478af8d3c34576793a820733ddba6449c2cf2fac)) + - add i.MX8ULP basic support ([fcd41e8](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/fcd41e8692ce8e8fc98d069bc131820cbf83c55c)) + - add memory region policy ([5fd0642](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/5fd06421f8bf9f5b67e73828281534f14f302630)) + - add OPTEE support ([e7b82a7](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/e7b82a7d2fa1fc3f32724e6836b8f6078d20c103)) + - add some delay before cmc1 access ([c514d3c](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/c514d3cfa7640313c4d78674df9d7cbe9227420b)) + - add system power off support ([891c547](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/891c547e9658c1827559d8da5e3b87de5a2e9f6a)) + - add the basic support for idle & system suspned ([daa4478](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/daa4478a3cb2f86501c37e5a301cd4d6a6e60ee6)) + - add the initial XRDC support ([ac5d69b](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/ac5d69b628736f66f72e99532656105fdc07a3fe)) + - add trusty support ([e853041](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/e853041920b15b77839027ab802d0cd9a08c7c35)) + - adjust the dram mapped region ([8d50c91](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/8d50c91b476474cc403c30eb6de6af28cb246e5a)) + - adjust the voltage when sys dvfs enabled ([416c443](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/416c4433f0047a86165e450e60f93020c561151b)) + - allocated caam did for the non secure world ([7c5eedc](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/7c5eedca4c7f176448e6b92eb5c22ee2ea45e70a)) + - allow RTD to reset APD through MU ([ea1f7a2](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/ea1f7a2e109181f19f5bdeb71533e7dfda753df7)) + - ddrc switch auto low power and software interface ([ee25e6a](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/ee25e6a51bf20c92471e737ccba98af4a74d1383)) + - enable 512KB cache after resume on imx8ulp ([bcca70b](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/bcca70b9688c5effa0731f39e2b209071f54be2c)) + - enable the DDR frequency scaling support ([caee273](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/caee2733ba4e7a09ea656b0be85f150a275cc57c)) + - give HIFI4 DSP access to more resources ([351976b](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/351976bb063cca7866e214a6bda9302f9ab018b3)) + - not power off LPAV PD when LPAV owner is RTD ([ab787db](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/ab787dba7726bdf58c15626e5cc9a3525aade8a3)) + - protect TEE region for secure access only ([ff5e179](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/ff5e1793b95ed4297deae72cdb665178e6e72e44)) + - update the upower config for power optimization ([36af80c](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/36af80c2b420cb32ff57273eda0d7d0e93b49153)) + - update XRDC for ELE to access DDR with CA35 DID ([d159c00](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/d159c00532afe50686dd92215de9b420d60502f6)) + + - **S32G274A** + + - add S32G274ARDB2 board support ([8b81a39](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/8b81a39e28a087e1123271a42c04a7ce3b496a58)) + - enable BL31 stage ([e73c3c3](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/e73c3c3a6cbc1e81de4c9d73a5d713e6b37ae3b2)) + + - **QEMU** + + - allow ARM_ARCH_MAJOR/MINOR override ([e769f83](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/e769f830d3116f49ed82769d9d731c4dca8f6188)) + - enable FEAT_ECV when present ([1b694c7](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/1b694c77c497cb8272c97417ef1fa4f5f9c869c1)) + - enable transfer list to BL31/32 ([305825b](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/305825b490a77e5b0ee816ea29c53bc6444a1d63)) + - load and run RMM image ([8ffe0b2](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/8ffe0b2edea6b00c9fe7d9ecaeca43c734d3764d)) + - setup Granule Protection Table ([6cd113f](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/6cd113fe06fdaa67a8457391eb6bcffd295f87fd)) + - setup memory map for RME ([cd75693](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/cd75693f5ed303c1366fdff9b392d766848b6b67)) + - support TRP for RME ([ebe82a3](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/ebe82a392f06aa0adddf9cc5caa7af8f561b2fb4)) + - update mapping types for RME ([a5ab1ef](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/a5ab1ef7febb2dc931cd8f7fcd76caac04d628cd)) + - update to manifest v0.3 ([762a1c4](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/762a1c44b985b71495a90bc3484b576d28c8511a)) + - use mock attestation functions for RME ([c69e95e](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/c69e95eed0491b481971b48f5df855402ed5392a)) + + - **SBSA** + + - handle CPU information ([42925c1](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/42925c15bee09162c6dfc8c2204843ffac6201c1)) + - handle memory information ([8b7dd83](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/8b7dd8397dd017b61ecda8447e8956a1d9d6d5d3)) + - mpidr needs to be present ([4fc54c9](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/4fc54c99d08926c2d42173902c8aaf3862722c84)) + + - **Raspberry Pi** + + - add Raspberry Pi 5 support ([f834b64](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/f834b64f889c1c4e03e590d44a6a52e3ac79cf42)) + + - **Renesas** + + - **R-Car** + + - **R-Car 3** + + - add cache operations to boot process ([7e06b06](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/7e06b06753b12d567b6f48b6e60d6d0a56cf72e5)) + - change CAM setting to improve bus latency of R-Car Gen3 ([e366f8c](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/e366f8cf3349189daafb7ac2ab74d98931757a60)) + - change MMU configurations ([5e8c2d8](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/5e8c2d8e23ca0760bca7e5b692ee95dd2871ec89)) + - enable the stack protection ([cfa466a](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/cfa466ab733ff021771b94b4a98d22bfdd246139)) + - update IPL and Secure Monitor Rev.4.0.0 ([516a98e](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/516a98ef277626aa1858d9a4018d13ab2aeb39e7)) + + - **ST** + + - add a function to clear the FWU trial state counter ([6e99fee](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/6e99fee43efa256bdac3b38864206c94bd9ae3c8)) + - add logic to boot the platform from an alternate bank ([6166051](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/6166051426638087b5433eff1739d26478313dff)) + - do not directly call BSEC functions in common code ([3007c72](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/3007c72844c72e0911721e499dbab37b3eca1cdc)) + - get the state of the active bank directly ([588b01b](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/588b01b5e4726cd4a6d235e9f566a546ef17f631)) + - use stm32_get_otp_value_from_idx() in BL31 ([189db94](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/189db9486ddd949f279faa970bfc1dd9cc0e3623)) + + - **STM32MP1** + + - only fuse monotonic counter on closed devices ([d6bb94f](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/d6bb94f3a14ddbcf44c667134ed302eff054954c)) + + - **STM32MP2** + + - add BSEC and OTP support ([197ac78](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/197ac780d73c3421c4643e0bc02d112ceffd248f)) + - add ddr-fw parameter for fiptool ([e494afc](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/e494afc05f8562455e09b4f131f2699990a744f8)) + - add plat_my_core_pos ([d1c85da](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/d1c85da8ef23a99387823272b03399a07e3a00da)) + - add STM32MP_USB_PROGRAMMER compilation ([2e905c0](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/2e905c0682b4e6d2cfdbd42e41f6097b16967ff5)) + - put back core 1 in wfi after debugger's halt ([2331a34](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/2331a34f783b29a9a1fe86f5142d0a359cacb259)) + - use early traces ([47ea303](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/47ea303389f6d0ac81617366973ece9d93dc49c9)) + + - **Xilinx** + + - add handler for power down req sgi irq ([ade92a6](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/ade92a64e4d2fbb5f246e6ad891465d10e0d9b26)) + - add new state to identify cpu power down ([5949701](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/5949701600c7f3c3a6589d0efd743615156c34b6)) + - add wrapper to handle cpu power down req ([3dd118c](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/3dd118cf9d60e1eab97af505eb63a2cdc044d747)) + - power down all cores on receiving cpu pwrdwn req ([c3280df](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/c3280df1bb95ed09b5d5f91f8977bbe99c6a923b)) + - request cpu power down from reset ([88ee081](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/88ee0816a7429689890659f69b895ac84e48f141)) + - send SGI to mailbox driver ([9a7f892](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/9a7f892e29ea81c67f6f6b1342a367234e125b63)) + + - **Versal** + + - enable errata management feature ([d766f99](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/d766f994d2bd00c538f66e95686fc47b45ccbdb9)) + - extend platform address space sizes ([663f024](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/663f024f207bddb7b80167e661c094d77955e292)) + + - **Versal NET** + + - add bufferless IPI Support ([511e4a4](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/511e4a48ccd5e74af338041be238f5df12fffe3e)) + + - **ZynqMP** + + - remove unused pm_get_proc_by_node() ([b03ba48](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/b03ba4801d39da1d5acc7a58d9c7736e57efc099)) + +- **Bootloader Images** + + - **BL32** + + - create an sp_min_setup function ([a1255c7](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/a1255c758593f9f6fb85b70165fad21de7491e1e)) + +- **Services** + + - **FF-A** + + - update FF-A version to v1.2 ([e830e4c](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/e830e4cdee3d2238314326ef8c259b35d1c4f167)) + + - **RME** + + - build TF-A with ENABLE_RME for Armv9.2 ([7d5fc98](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/7d5fc98f5483efb942f7cbe4c04bf546a9a8598c)) + - pass console info via RMM-EL3 ifc ([3290447](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/32904472cc55a4bc9d8181a389ce3419033e0101)) + + - **SPM** + + - **EL3 SPMC** + + - add support for FFA_CONSOLE_LOG ([638a6f8](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/638a6f8e04c543649369374492524f2952f8d6b6)) + - add support for FFA_MEM_PERM_GET and SET ABIs ([1f6b2b2](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/1f6b2b26535d5254d998239f232d997972d0475b)) + - add support to handle power mgmt calls for s-el0 sp ([5917379](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/59173793f47e27a66c871a0e8237e0f0d462080d)) + - add support to map S-EL0 SP device regions ([727ab1c](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/727ab1c4ab1e5ce1559fa6efec510114ce51fdf8)) + - add support to map S-EL0 SP memory regions ([83c3da7](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/83c3da7711a246e04f4d0a64593fc0ab46f08bad)) + - add support to setup S-EL0 context ([48db2b0](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/48db2b0120d1726208ff38a0edf6962f55a988bf)) + - synchronize access to the s-el0 sp context ([5ed8e25](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/5ed8e255096bd34d12bc6621e48cf9139bf414b2)) + + - **SPMD** + + - add FFA_MSG_SEND_DIR_REQ2 ([cc6047b](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/cc6047b3de52e412988f321723f67077a409e27d)) + - add FFA_MSG_SEND_DIR_RESP2 ([0651b7b](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/0651b7beb7e08a01c6e28be61026b053d53308fa)) + - initialize SCR_EL3.EEL2 bit at RESET ([8815cda](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/8815cdaf57806901cfd388b8ee8c7979a8a2fe15)) + - pass SMCCCv1.3 SVE hint to lower EL ([c925867](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/c925867ec1be039abb72a7d65bff1b6a85b3d67a)) + + - **DRTM** + + - add ACPI table region size to the DLME header ([5dde96b](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/5dde96b02490829d023b37931737c2ba2a6ed431)) + - add additional return codes ([89f5c75](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/89f5c753af8e5b8091543e8b1cae4d37e345ed7f)) + - for TPM features fw hash algorithm should be 16-bits ([c86cfa3](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/c86cfa35975542d25d2192b81908074195aafe96)) + - update DRTM version to 1.0 ([9c36b90](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/9c36b900f904642f41e201024df584c0eaef9fc5)) + - update references to DRTM beta0 ([b94d590](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/b94d59099f0addb32389952dc6ecf35136a23859)) + - update return code if secondary PE is not off ([bc9064a](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/bc9064ae5c983aaca56102c2c0d3513ed022fd46)) + + - **ChromeOS** + + - add ChromeOS widevine SMC handler ([b22e689](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/b22e6898e1493eb00d0f0de6d48655d744264cb6)) + +- **Libraries** + + - **CPU Support** + + - add support for Poseidon V CPU ([b77f55d](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/b77f55d6c7e51025d6c7ada1b4aa9506a046cf0f)) + - support to update External LLC presence in Neoverse N3 ([6fbc98b](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/6fbc98b15d92d881c4fbb74fd1344f0ef3f128ad)) + - support to update External LLC presence in Neoverse V2 ([6aa5d1b](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/6aa5d1b3ab7b29c85ffe05942f2991da869e7fed)) + + - **EL3 Runtime** + + - introduce UNDEF injection to lower EL ([3c789bf](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/3c789bfccca548ebcbdafbc7ecb07461d9368bea)) + + - **FCONF** + + - support signing-key in root cert node ([04ac0b3](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/04ac0b3c2711a4cb2f35983e91ff0ee842b52bbd)) + + - **OP-TEE** + + - enable transfer list in opteed ([0e8def9](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/0e8def996e73673d3e2c3d755a84e2b759ab3052)) + + - **PSCI** + + - add psci_do_manage_extensions API ([160e843](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/160e8434baa48cc19d69913b00d2a643c788caec)) + + - **GPT** + + - validate CRC of GPT partition entries ([7a9e9f6](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/7a9e9f6e96a93617abd33ef48734b65ad792ec13)) + + - **SMCCC** + + - add vendor specific el3 id ([be5b1e2](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/be5b1e22346c6d8ce4b0c56604c99f7a9d3676cc)) + - add vendor-specific el3 service ([de6b79d](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/de6b79d8b5e15262b328051095e15ad4c67518eb)) + - add version FID for PMF ([42cbefc](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/42cbefc72721a9cbf68a70d81cbcb141a2d085f1)) + + - **C Standard Library** + + - add printf support for space padding ([0926d2d](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/0926d2df7a5606c2b7c341d51f04a396084c39f2)) + + - **Locks** + + - add bitlock ([222f885](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/222f885df38c3abd34ee239a721654155609631b)) + + - **DICE Protection Environment (Experimental)** + + - add cert_id argument to dpe_derive_context() ([6a415bd](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/6a415bd1e71ac944c0ac67507b01f251e63361c3)) + - add client API for DICE Protection Environment ([b03fe8c](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/b03fe8c025f1c8025e70e7289339ecbc6cf83aae)) + - add DPE driver to measured boot ([0ae9c63](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/0ae9c631eaa32a30df3ff10cb4f0abafccb6c409)) + - add QCBOR library as a dependency of DPE ([c19977b](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/c19977be0c3654e12accd51d4aef7059411106a6)) + - add typedefs from the Open DICE repo ([584052c](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/584052c7f80b406666b9597447eeccef4d6deca4)) + + - **Context Management** + + - report context memory usage ([bfef8b9](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/bfef8b908e3a3cc29656c1d30a6b53490c79539b)) + - add documentation for context management library ([4efd219](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/4efd2193621ab7b933f4edfa28888379f3e03cbd)) + + - **Firmware Handoff** + + - add additional TE tags ([a312bfb](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/a312bfb34487774a0e3244266ee45f63af86e2e8)) + - add support for RESET_TO_BL2 ([f019c80](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/f019c8013e9c5efeb85eec7792fe901543a5832c)) + - add TE's for BL1 handoff interface ([0646c9b](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/0646c9b293a2d8cdfd4626d15395385b5c1c2a6c)) + - add TL source files to BL1 ([469b1d8](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/469b1d8412a748819f8c1bf51f695f2cb9f20489)) + - enhance transfer list library ([40fd755](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/40fd755bad9411d1e9e55984107186dde4137635)) + +- **Drivers** + + - **Authentication** + + - add explicit entries for key OIDs ([2b53106](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/2b53106a0e91e0865bf855935de04b24ef1cfa02)) + + - **mbedTLS** + + - update config for 3.6.0 ([55aed7d](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/55aed7d798f3d48d6aa08d58eb46c4cda318bcfb)) + + - **Console** + + - introduce EARLY_CONSOLE ([ae770fe](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/ae770fedf459d5643125d29f48659e3e936ebd2d)) + + - **FWU** + + - modify the check for getting the FWU bank's state ([56724d0](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/56724d09c2c55ee2b8486b7c706f5fb9d980df88)) + - update the URL links for the FWU specification ([e106a78](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/e106a78ef00df4c70a1594a89520af07b939cd92)) + + - **SCMI** + + - add scmi sensor support ([e63819f](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/e63819f2bc307e7a42d43151242009f91ceeb06b)) + + - **Arm** + + - **SMMU** + + - fix to perform INV_ALL before enabling GPC ([70d849c](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/70d849c14de99e7320cc381b441af8bfe2a38375)) + - separate out smmuv3_security_init from smmuv3_init ([a23710b](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/a23710b4b943a15a418a5d41236b2b57bd071de6)) + + - **MHU** + + - add MHUv3 doorbell driver ([bc17476](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/bc174764f0daa82128bf60163653fc20db9a7e87)) + - add MHUv3 wrapper APIs for RSS comm driver ([4b4f850](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/4b4f8505e7c58ba80a00c47a11f5feaf6d6f44f2)) + - use compile flag to choose mhu version ([996b3af](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/996b3af84cc6aeca90bc0dd3559abffd8bdc0ed7)) + + - **RSE** + + - add defines for 'type' range and use them in psa_call() ([002b106](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/002b10604ba0b90ac6e85d445ce2184cab52e39b)) + - adjust parameter packing to match TF-M changes ([5abcc83](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/5abcc83991770a2fdbcb57dfc01000c6354da915)) + + - **NXP** + + - add Linflex driver ([306946b](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/306946b01490cfe0675300412cf738840bd099ef)) + + - **ST** + + - **BSEC** + + - add driver for the new IP version BSEC3 ([ae6542f](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/ae6542f6c7ac9224843448424d3a539733bd651b)) + - use early traces ([cf237f8](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/cf237f8d55255da1aad4f8dccb3110bab6060eba)) + + - **Clock** + + - add function to control MCU subsystem ([77b4ca0](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/77b4ca0b2fd2c35e3bcb516078e1d9e3573172b3)) + + - **SDMMC2** + + - set FIFO size to 1024 on STM32MP25 ([d5b4d5d](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/d5b4d5d2e62e57acdcb2dbbcd4fe208bde92dc4c)) + +- **Miscellaneous** + + - **AArch64** + + - add functions for TLBI RPALOS ([8754cc5](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/8754cc5d1c1b33d645b321f465bcfe61bc3915d6)) + + - **DT Bindings** + + - introduce CCA CoT, rename TBBR ([c4b35ce](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/c4b35cebffb0d034aa7bdba7cfdb65ba93939e35)) + + - **FDTs** + + - **STM32MP2** + + - add board ID OTP in STM32MP257F-EV1 ([88528f5](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/88528f55771fdc0a94b2ddd7f49f495a83044a24)) + - add OTP nodes in STM32MP251 SoC DT file ([c238a46](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/c238a46a76660cbfa9ed40da4b1d0e5d477c3dd7)) + + - **Security** + + - add support for SLS mitigation ([538516f](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/538516f5d3db6e2c30dfa9f0b82859389f529e78)) + +- **Documentation** + + - update maintainer list for neoverse_rd ([2d7902d](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/2d7902d9bf0bafceee9f571225862c476de0cdce)) + +- **Build System** + + - check that .text section starts at page boundary ([3d6edc3](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/3d6edc325c52082ab63ffd003c55a4ed875a52c5)) + - redirect stdin to nul during toolchain detection ([b9014f8](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/b9014f858d1fd963a466228ec15572b0892a8490)) + +- **Tools** + + - **Memory Mapping Tool** + + - add RELA section display ([a6462e0](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/a6462e05cf1cd55da44002cdede04053a928cf0a)) + +### Resolved Issues + +- **Architecture** + + - **Memory Tagging Extension2** + + - remove CTX_INCLUDE_MTE_REGS usage ([30788a8](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/30788a8455779b70aebd38d53afc8aa19d776c6c)) + - use ATA bit with FEAT_MTE2 ([ef0d0e5](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/ef0d0e5478a3f19cbe70a378b9b184036db38fe2)) + + - **Performance Monitors Extension (FEAT_PMUv3)** + + - fix breakage on ARMv7 CPUs with SP_min as BL32 ([e6f8fc7](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/e6f8fc7437f6b9483ea0463315809d7ff6d5c0ec)) + + - **Statistical profiling Extension (FEAT_SPE)** + + - invoke spe_disable during power domain off/suspend ([777f1f6](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/777f1f6897b57fe98c70d17c0d318aab3b86e119)) + +- **Platforms** + + - **Arm** + + - move console flush/switch in common function ([6bdc856](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/6bdc856bc9135db420196683501b4f201b30ae3a)) + - only expose `arm_bl2_dyn_cfg_init` to BL2 ([3b48ca1](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/3b48ca17f350d8b0999e89e8d9215993701e16a0)) + + - **FVP** + + - added ranges for linux ([b7491c7](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/b7491c77d7ad2991b8c7c01f0311ebb3b0eca397)) + - don't check MPIDRs with the power controller in BL1 ([6d8546f](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/6d8546f9fc49a03a817b15b20a9d62fadda74b9c)) + - permit enabling SME for SPD=spmd ([0b0fd0b](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/0b0fd0b47616b706e2f07c6da548cdc913fecd17)) + + - **FPGA** + + - halve number of PEs per core ([70b9204](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/70b9204e6f98f1ec4f0529e8c1c88e8ece490d22)) + + - **Neoverse-RD** + + - **SGI** + + - align to misra rule for braces ([cacee06](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/cacee0605684a75bbe8783c74fddba97b9abcffa)) + - apply workarounds for N2 CPU erratum ([7934b68](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/7934b68af6b446783823a114f25c3be06244c0e4)) + - increase BL31 carveout size ([0737bd3](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/0737bd33faba5c9e6a0e98969e015430e2782332)) + - reduce cper buffer carveout size ([f10d3e4](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/f10d3e4953741eb3be1f9e4c09e7420554a0f050)) + - update spi_id max for sgi multichip platforms ([89d8577](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/89d857780c50bddf94db26f158c008b4cc846edf)) + + - **RD-N1-Edge** + + - update RD-N1-Edge's changelog title ([d239ede](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/d239edea5644657ac72458cc13e3ce6bb5754ff8)) + + - **RD-N2** + + - populate TOS_CONFIG only when SPMC_AT_EL3 is enabled ([10dcffe](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/10dcffedb36a658cf8a3389fbdeb499d4e7e4446)) + + - **TC** + + - correct interrupts ([d2e44e7](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/d2e44e7d71863e3b302b5e72c8262bb0f3964fe6)) + - do not enable MPMM and Aux AMU counters always ([fc42f84](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/fc42f84560d33c53b248e14913bbd6a69a8d310a)) + - do not use r0 for HW_CONFIG ([a5a966b](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/a5a966b12d9fe51a337db3204e7463ad95ba99c6)) + - enable FEAT_MTE2 ([154eb0a](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/154eb0a22fa0a88d1f46e3674e3979626a83e063)) + - guard PSA crypto headers under TF-M test-suite define ([d2ce6aa](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/d2ce6aa066ce1539908726de0d94a59c16634c4a)) + - increase BL2 maximum size limit ([19258a5](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/19258a5839cae9a81fb7256fbea34ff118220161)) + - increase stack size when TRUSTED_BOARD_BOOT=0 ([44ddee6](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/44ddee6f0a993ed5b3409e6626c0c70b7ed7d7a2)) + - missing device regions in spmc manifest ([5e47112](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/5e4711208db622ff6150e69c87962b506742a544)) + - remove timer interrupt from G1S ([9bf31a5](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/9bf31a59d187f6537066f05677972d9767e96c82)) + + - **Intel** + + - add HPS remapper to remap base address for SDM ([b727664](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/b727664e0dcf62be39552521c451ecde02091917)) + - bl31 overwrite OCRAM configuration ([cfbac59](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/cfbac59590056e6b639aed56a1da480cd46f6f3e)) + - fix hardcoded mpu frequency ticks ([150d2be](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/150d2be0d2d440011c91c9bf8013a1ab602b464c)) + - read QSPI bank buffer data in bytes ([2f17ac0](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/2f17ac01adf28edb90a5ec8f446be1be76971b5c)) + - revert back to use L4 clock ([d0e400b](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/d0e400b3c626be647b9a20bc4f4869e20cc15dde)) + - revert sys counter to 400MHz ([460692a](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/460692afb5b934720b69c410e3b02c540a3b1ddf)) + - temporarily workaround for Zephyr SMP ([68820f6](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/68820f642191cef67df38516ef1c2ed1411c579f)) + - update DDR range checking for Agilex5 ([f4aaa9f](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/f4aaa9fd6e6b4edd03976680b94e1c24aa582a68)) + - update fcs crypto init code to check for mode ([b0f4478](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/b0f447897d3e2ddd72b291cb450165f4d220663e)) + - update fcs functions to check ddr range ([e8a3454](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/e8a3454cb74a9b55c0cb678d47a8553ece660439)) + - update from INFO to VERBOSE when print debug message ([56c8d02](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/56c8d022b00ba212f3e21dcfab20c14f3a44eec4)) + - update HPS bridges for Agilex5 SoC FPGA ([2973054](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/2973054d9b4ba4fbcad7e04303ce8e0838b2f2b3)) + - update individual return result for hps and fpga bridges ([82752c4](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/82752c412362607549068d1c10cf7688f309d249)) + - update nand driver to match GHRD design ([a773f41](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/a773f4121b3064fba24631e980c6226f23378e06)) + - update stream id to non-secure for SDM ([8fbd307](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/8fbd3073cacfc7a23efdfda4eecfaf6607515306)) + - update system counter back to 400MHz ([a72f86a](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/a72f86ac4208e2aae5da83229cdd9ac97f651e36)) + + - **NXP** + + - **i.MX** + + - **i.MX 8M** + + - align 3200 MTps rate with U-Boot ([060fe63](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/060fe63337097c6cadea76ef5d2d383f0d90ef01)) + - fix CSU_SA_REG to work with all sa registers ([c13016b](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/c13016bac6a6960acbbfb3e0176e1894a7e9fa3a)) + - handle 3734 in addition to 3733 and 3732 MTps rates ([cb60a87](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/cb60a876efc156c87afcd5ec53b9cf356f30211d)) + + - **i.MX 8M Plus** + + - uncondtionally enable only the USB power domain ([ae6ce19](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/ae6ce196df5b932f38c543cd8c6d8d86ee600009)) + + - **i.MX 8ULP** + + - add sw workaround for csi/hotplug test hang ([e1d5c3c](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/e1d5c3c8f435424394367e2ff19240b1b8a3073c)) + - fix suspend/resume issue when DBD owner is s400 only ([68f132b](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/68f132b88bb24277ee34d5c3c94d16c26d7d4545)) + - increase the mmap region num ([047d7d1](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/047d7d1ba2fc84d8377156f7f45d2d69c3cb5f84)) + + - **QEMU** + + - disable FEAT_SB ([59bdb42](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/59bdb426d300a6350334523a8dbc3fa6ae9f3bfc)) + - increase max FIP size ([f465ac2](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/f465ac221001f82bed907be356917675645d92eb)) + + - **Raspberry Pi** + + - consider MT when calculating core index from MPIDR ([6744d07](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/6744d07d9475adb49352fa57aa72fce17a95d757)) + + - **Renesas** + + - **R-Car** + + - fix implicit rule invocations in tools ([e068a7c](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/e068a7ca860f35a171f608d55fb8a2a00ebd7561)) + + - **R-Car 3** + + - change RAM protection configurations ([e9afde1](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/e9afde1a2e311df0197a8e9102ef535382aef228)) + - fix load address range check ([4f7e0fa](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/4f7e0fa38fdb6a25b07afafff492985bcc4e63a0)) + + - **Rockchip** + + - add support for building with LTO enabled ([e5e9ccd](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/e5e9ccdb0c070d3066e7d778e5e2b563acd7ba98)) + - fix documentation in how build bl31 in AARCH64 ([6611e81](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/6611e81e14ed4aa16844e3865fd8a9f6fa99a074)) + + - **RK3328** + + - apply ERRATA_A53_1530924 erratum ([dd2c888](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/dd2c888606dcdd638354c6345e08d4415d9d09fd)) + + - **ST** + + - **STM32MP2** + + - add missing include ([cb0d6b5](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/cb0d6b5b5f7530335eac3c387bbb82d86608b0ea)) + - correct early/crash console init ([4da462d](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/4da462dcdc2e435c8b732f3ceff4c94ca28b4c43)) + + - **Texas Instruments** + + - do not stop non-secure timer on world switch ([d2e1f6a](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/d2e1f6a8811e52505556f7b91156499d82488751)) + + - **K3** + + - increment while reading trail bytes ([0bdaf5c](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/0bdaf5c804f852fe21f6172e436524157c9f6919)) + + - **Xilinx** + + - add console_flush() before shutdown ([7ec53af](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/7ec53afaade308b35f546480990dbc9304e06e7d)) + - add FIT image check in DT console ([e2d9dfe](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/e2d9dfe2bffe4fde28f2714058c8c882ea90102a)) + - add FIT image check in prepare_dtb ([046e130](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/046e1304721e8bbf3d304dac22aa290bcbb0d10c)) + - check proc variable before use ([652c1ab](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/652c1ab1526877d3505218f87ea96e6a9b2ccc11)) + - deprecate SiP service count query ([6a80c20](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/6a80c20eff74054c28273b42f3fe8e1a8fc5add4)) + - fix sending sgi to linux ([427e46d](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/427e46ddea1e528d4c57b1d8215482055bd79c3e)) + - follow MISRA-C standards for condition check ([655e62a](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/655e62aa5bede7ace8f8c6df571707aca9d6e14f)) + - rename macros to align with ARM ([7995319](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/79953190bc856ac3f47281029a80e5129bb4437d)) + - update correct return types ([8eb6a1d](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/8eb6a1da1229b8f0bff33293cbb86ce20d09259d)) + + - **Versal** + + - initialize cntfrq_el0 register ([f000744](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/f000744e0f501c89fb2240b47e91c261e3082249)) + + - **Versal NET** + + - setup counter frequency ([07625d9](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/07625d9dd42d81c0e15f101fc0b6efa1c784b6f4)) + - use arm common GIC handlers ([b225926](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/b2259261815961042d2a994401929bc76a0d3ee9)) + + - **ZynqMP** + + - resolve null pointer dereferencing ([20fa9fc](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/20fa9fc82334c67834eb22e20a3f4a07bcbe069d)) + + - **Nuvoton** + + - gfx frame buffer memory corruption during secondary boot ([ae2b4a5](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/ae2b4a5494f9b4985fc2434e543ab0921e3b5a34)) + - prevent changing clock frequency ([fe8cc55](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/fe8cc55a0cb5e47a0c0e28b147ee3e8dfdae07b2)) + +- **Bootloader Images** + + - **BL1** + + - add missing `__RW_{START,END}__` symbols ([d701b48](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/d701b48eef4bb4b4b13ce5ef4091a37047e49a0b)) + - add missing spinlock dependency ([e40b563](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/e40b563e87fd4ff58474a289909a1827c8d2bca7)) + + - **BL2** + + - make BL2 SRAM footprint flexible ([e0e03a8](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/e0e03a8d8b7eac45606812d1f2a9685b51e44515)) + +- **Services** + + - **FF-A** + + - add NS memory node to fvp_spmc_optee_sp manifest ([92bba3e](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/92bba3e711a21f2d31842bee64a1bd87e4b65414)) + + - **RME** + + - **RMMD** + + - avoid TRP when external RMM is defined ([57bc3c4](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/57bc3c40560285e6029742b7360f8a0d0ac2346c)) + - fix bug, raised by coverity, when zeroing manifest struct ([83a4e8e](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/83a4e8e0c69c64219e4d9de6c7f51fb10e3adc5a)) + + - **SPM** + + - add device-regions used in tf-a-tests ([45716e3](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/45716e377ecb30c17aa3b375ce1e232d15492b9c)) + - not defining load-address in SP config ([04e7f80](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/04e7f80823e8a083138dd25963a5509bacd93257)) + - reduce verbosity on passing tf-a-tests ([29872eb](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/29872eb330201334fcb8e418b7dc7ae8ff0dc192)) + - silence warning in sp_mk_generator ([6a3225e](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/6a3225e2277df18e5c3aceb6173579cccefece51)) + + - **EL3 SPMC** + + - add datastore linker script markers ([ba33528](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/ba33528a00bb83f5562918131cb37574fc287193)) + - fix dangling pointer in FFA_CONSOLE_LOG ([83129bc](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/83129bcd8e75f1ffbfc9a3bae3d60749b1d22fe3)) + + - **SPMD** + + - register group0 handler only if supported ([fca5f0e](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/fca5f0ebe5c2b5cf1c9d5096db6001a60ff7e089)) + - skip NS EL1 context save & restore operations ([2d960a1](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/2d960a11601be6e7f24c38d84b2a4fdbb52efb9b)) + +- **Libraries** + + - **CPU Support** + + - workaround for Cortex-A520 erratum 2630792 ([f03bfc3](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/f03bfc304599540d859c4a07ac85d1bd9ae2c4f0)) + - workaround for Cortex-A520 erratum 2858100 ([34db353](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/34db3531ba085f111274b3b8e18476c4a392c245)) + - workaround for Cortex-A710 erratum 2778471 ([c9508d6](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/c9508d6a1062ec3de4baaa3bd79ceed13eb972ad)) + - workaround for Cortex-A715 erratum 2331818 ([53b3cd2](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/53b3cd2532dbdb794ddfedcc8a3985d2404eb6f7)) + - workaround for Cortex-A715 erratum 2344187 ([33c665a](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/33c665ae955fe5f5ae255f56ef6cdf073a9f601f)) + - workaround for Cortex-A715 erratum 2413290 ([15a0461](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/15a04615bb6834d93ab0077b89726dc17e3ba8b0)) + - workaround for Cortex-A715 erratum 2420947 ([1f73247](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/1f732471320cee7b4f355ecff7dcfab7018e48ae)) + - workaround for Cortex-A715 erratum 2429384 ([262dc9f](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/262dc9f76086970dab3dc43815890bed0ea29c79)) + - workaround for Cortex-A715 erratum 2561034 ([6a6b282](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/6a6b282378340dc61cf088ff5a06770cf68f44d8)) + - workaround for Cortex-A715 erratum 2728106 ([10134e3](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/10134e3556ca61e670017e681eb637889b1bd4f8)) + - workaround for Cortex-A720 erratum 2926083 ([152f4cf](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/152f4cfa16bc3d2786f598390450af38f4b2d0be)) + - workaround for Cortex-A720 erratum 2940794 ([7385213](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/7385213e602465d27530015a9b28ebc36a77b1c1)) + - workaround for Cortex-A78C erratum 2683027 ([68cac6a](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/68cac6a0f273dbe4f44563b467c996fafef07016)) + - workaround for Cortex-A78C erratum 2743232 ([81d4094](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/81d4094d637871ff34ddd7c2e2b3e842915f30f5)) + - workaround for Cortex-X2 erratum 2778471 ([b01a93d](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/b01a93d7789a794ef0635e0a7b0e7e53cc8519e5)) + - workaround for Cortex-X3 erratum 2266875 ([a65c5ba](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/a65c5ba351178e6119299fa935a3576453cf900b)) + - workaround for Cortex-X3 erratum 2302506 ([3f9df2c](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/3f9df2c6ad053172c5dab74cd12d82a5b2c93c34)) + - workaround for Cortex-X3 erratum 2372204 ([7f69a40](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/7f69a40697c3cc64e3fc553f6b50c72b97238dc9)) + - workaround for Cortex X3 erratum 2641945 ([c1aa3fa](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/c1aa3fa5555250dfbcae99fb6944ad24c4ee6a0b)) + - workaround for Cortex X3 erratum 2743088 ([f43e9f5](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/f43e9f57dc37a806bcd5e25a46b9f9bb1f365a64)) + - workaround for Cortex-X3 erratum 2779509 ([355ce0a](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/355ce0a43abc1559b072b9cd9905f5194a6f0b86)) + - workaround for Cortex-X4 erratum 2701112 ([cc41b56](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/cc41b56f41af14b00ce9f5c802e2f883786cef38)) + - workaround for Cortex-X4 erratum 2740089 ([c833ca6](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/c833ca66a6fecbc54e038164e466be677559ec4e)) + - workaround for Cortex-X4 erratum 2763018 ([4731211](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/47312115dea140dd7ba26cf0512856a41f3e3067)) + - workaround for Neoverse V1 erratum 2348377 ([71ed917](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/71ed91733140c82a392161c81869fcadb445c01a)) + - workaround for Neoverse V2 erratum 2618597 ([c0f8ce5](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/c0f8ce5379a77e61e89d91e225784801e5bbd3e0)) + - workaround for Neoverse V2 erratum 2662553 ([912c409](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/912c4090fff207b445dde4bff72cc9b6e057e8b7)) + - workaround for Neoverse V2 erratum 3099206 ([8815cda](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/8815cdaf57806901cfd388b8ee8c7979a8a2fe15)) + - add Cortex-A520 definitions ([ae19093](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/ae19093f2aa6dd95cc7819accb0d05c0ebe4eeb3)) + - workaround for Cortex-A715 erratum 2413290 re-factored with ENABLE_SPE_FOR_NS=1 ([bd2f7d3](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/bd2f7d325826f75acd729d4ee2719fd6130a7c5e)) + - fix a defect in Cortex-A715 erratum 2561034 ([57ab6d8](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/57ab6d897656f71d229268d80e41b26e62179400)) + - add erratum 2701951 to Cortex-X3's list ([106c428](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/106c4283a564e4f37976ebc7dd8bc7d35f6592e4)) + - update status of Cortex-X3 erratum 2615812 ([f589a2a](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/f589a2a5f1b032ff3a09a419e49db0b97ccd8595)) + - fix incorrect AMU trap settings for N2 CPU ([54b86d4](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/54b86d47eb05f09330df57519b7d04b9968890e5)) + - correct variant name for default Poseidon CPU ([61a2968](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/61a29682c66d0437806f81fb8ab0e3ff321dfe04)) + - check for SCU before accessing DSU ([5b5562b](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/5b5562b2e5855f949f1fc0579d7aff15e6b274ef)) + + - **EL3 Runtime** + + - **Context Management** + + - add more feature registers to EL1 context mgmt ([d6c76e6](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/d6c76e6c65429326e7572e10f521dd9108a3a1e3)) + - add more system registers to EL1 context mgmt ([ed9bb82](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/ed9bb824e4a3815e60acaa69ed66796279f4afbf)) + - hide `cm_init_context_by_index` from BL1 ([a6b3643](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/a6b3643c2a1a95146e93c8b6f07c2e491a1230d6)) + - remove ENABLE_FEAT_MTE usage ([a796d5a](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/a796d5aa11b25622841cd2283630ff9348eed699)) + - save guarded control stack registers ([6aae3ac](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/6aae3acfd0d48e49e2367e6cd883dda7dca974c8)) + - update gic el2 sysregs save/restore mechanism ([937d6fd](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/937d6fdb70cd24602fd2638a5dbd5c46d32559c1)) + - couple el2 registers with dependent feature flags ([d6af234](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/d6af23443179f6d2239c7f5f190f0d8828bd68cf)) + - move EL1 save/restore routines into C ([59f8882](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/59f8882b44845ab865e354eeda8ce653f5d5fcf3)) + + - **FCONF** + + - boot fails using ARM_ARCH_MINOR=8 ([0c86a84](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/0c86a846d9149ee5af7e1ee4bb185c532ed9d0f8)) + + - **OP-TEE** + + - set interrupt handler before kernel boot ([0ec69a5](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/0ec69a5bfbfcdf4566db8e96adaf29ad847d3d58)) + + - **PSCI** + + - fix parent_idx in psci_validate_state_coordination ([412d92f](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/412d92fdfd28d2f850a48e5f0aee95faa894a556)) + - mask the Last in Level nibble in StateId ([0a9c244](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/0a9c244b05ef2d2d4b946ba81bb9b9584b479b48)) + + - **GPT** + + - declare gpt_tlbi_by_pa_ll() ([832e4ed](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/832e4ed520d5ed7e64249fe98c1ffb4550db5eca)) + - unify logging messages ([b99926e](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/b99926ef7b287738c4b4a87ee7ab4eaed1e4038f)) + - use DC CIGDPAPA when MTE2 is implemented ([62d6465](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/62d64652134ca1d3ea68da65ea9e4ae136f6c44e)) + + - **C Standard Library** + + - add memcpy_s source file to libc_asm mk ([99db13b](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/99db13bfaa5b11345730937c2e0e56cb670c01a5)) + - memset inclusion to libc makefiles ([84eb3ef](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/84eb3ef6c9f596e968b4f9b83a3a01deda2a8a9d)) + + - **PSA** + + - fix static check failure ([bc0ff02](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/bc0ff02cbb046388eff1a95efd0043757d6ac317)) + + - **Context Management** + + - align the memory address of EL2 context registers ([8c56a78](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/8c56a78894ddc69167bc093fe19f173feced720c)) + + - **Firmware Handoff** + + - correct representation of tag_id ([d594ace](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/d594ace68d4fa62cf2f1d5d13503b737b85924e5)) + + - **Exception Handling Framework (EHF)** + + - restrict secure world FIQ routing model to SPM_MM ([7671008](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/7671008fcfc826dbc3166ff1bdbb9cd7fbc7f68b)) + + - **SMCCC** + + - correctly find pmf version ([62865b4](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/62865b4ee455806e37a9c5bd52255b8c09cf1a1a)) + +- **Drivers** + + - **Measured Boot** + + - add missing image identifier string ([a8a09e3](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/a8a09e3141354b159e7699d7c9c325bdd817b1f5)) + + - **SCMI** + + - induce a delay in monitoring SCMI channel status ([af1ac2d](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/af1ac2d7db47717bc69afd69b56f398aa34b2fb6)) + + - **Arm** + + - **GIC** + + - **GICv3** + + - **GIC-600** + + - workaround for Part 1 of GIC600 erratum 2384374 ([24a4a0a](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/24a4a0a5ec25e179f2e567a6e13a9b5c87db1b81)) + + - **GICv2** + + - fix SGIR_NSATT bitshift ([eef240c](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/eef240cfdedcc59f09dd5cd942448c5dcecc75d6)) + + - **MHU** + + - use MHUv2 if PLAT_MHU_VERSION undefined ([c34dd06](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/c34dd06a843d71cdba2fa1c3c9067f6f130a0c73)) + - provide only the usable size of memory ([5cd1084](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/5cd10848be4f6ac19daa66803c3d512e3eea4266)) + + - **RSE** + + - fix bound check during protocol selection ([f754bd4](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/f754bd466749a9338561f991bfb85140dd034e03)) + + - **Renesas** + + - **R-Car3** + + - add integer overflow check ([ef38fb1](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/ef38fb1f5a5f2bdb897158e4244a1eddd2396eeb)) + - add integer overflow check ([93b8952](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/93b8952eefa14141c142070a71fc017736c8910c)) + - check "rcar_image_number" variable before use ([b469880](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/b469880e3b6b26849c3d43d3fe88a755a25249bc)) + - check for length underflow ([9778b27](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/9778b270e29bac3e16f57f9557098c45858c05de)) + - check loaded NS image area ([ae4860b](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/ae4860b0f5c283aeca4def1449f0293ef22ff508)) + + - **USB** + + - add missing include ([f84f21f](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/f84f21fa8d17662dcdc6b0b8b0caca4a45cd9ccd)) + +- **Miscellaneous** + + - **TBBR** + + - move rotpk definitions out of arm_def.h ([0f0fd49](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/0f0fd499dedd799e19279f0aa1f4f686085a944a)) + + - code coverage optimization fix ([152ad11](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/152ad112d73402523302f3cb252aee0efc145736)) + - fix MISRA defects ([c42d0d8](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/c42d0d8754ae8818a7e7a63e873ca7699a7f102b)) + - static checks on spmc dts ([c35299d](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/c35299d6b4e8b2757e47dc4c5a3b2e0836f89a7d)) + +- **Documentation** + + - revise the description of REGISTER_CRYPTO_LIB ([5710229](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/5710229f9e837f28e4bafee6b51e828f901bf3f1)) + - typo in the romlib design ([3b57ae2](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/3b57ae23e0891e44d5b648575b80cbad4fc10405)) + +- **Build System** + + - add forgotten BL_LDFLAGS to lto command line ([49ba1df](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/49ba1df52204e721f06a6da76ef0f8692ce1b2f8)) + - don't generate build-id ([304ad94](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/304ad94b34c2117823169a199558e7484139caa1)) + - don't rely on that gcc-ar is in the same directory as gcc ([7ef0b83](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/7ef0b8377fa7fb3697dda5adfa44dafd7e14150f)) + - enforce single partition for LTO build ([31f80ef](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/31f80efeefaee2c59db50a46cabe2b5fdf20e4ae)) + - march handling with arch-features ([7275ac2](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/7275ac2af86277e2442ef4b0fee6c35cbe830056)) + - move comment for VERSION_PATCH ([c25d1cc](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/c25d1ccf1e205b2781ecd0de91e91d35e57b79bc)) + - mute sp_mk_generator from build log ([fbd32ac](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/fbd32ac081c421929728f454427b7839235d2075)) + - properly manage versions in .versionrc.js ([7f74030](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/7f74030b89136a1673e2a949564403709bc48f5d)) + - wrap toolchain paths in double quotes ([4731c00](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/4731c00bb60915c0d4b29c082a752e9925a244b4)) + +- **Tools** + + - **Certificate Creation Tool** + + - add guardrails around brainpool usage ([c0c280d](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/c0c280dfda7322dcaebb5c6341c0880bdf524e13)) + - use a salt length equal to digest length for RSA-PSS ([e639ad2](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/e639ad23c8c7a1b320af9ebd519420ae7d431531)) + + - **Memory Mapping Tool** + + - fix footprint free space calculation ([9e72d01](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/9e72d01ed29c350dfc0567c59bc482901211634b)) + - fix memory map dump when SEPARATE_CODE_AND_RODATA=0 ([6dc8ee6](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/6dc8ee61ffeee8ea5aafdbef3121fa4e82b57932)) + + - **Marvell Tools** + + - include mbedtls/version.h before use ([8eb4efe](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/8eb4efe70bd5b03917e2063ab8ff5646de88922a)) + ## [2.10.0](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/refs/tags/v2.9.0..refs/tags/v2.10.0) (2023-11-21) ### ⚠BREAKING CHANGES @@ -2667,11 +4619,11 @@ issues in each release of Trusted Firmware-A. - route GIC IPI interrupts during setup ([04cc91b](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/04cc91b43c1d10fcba563e18f06336987e6e3a24)) - use only one space for indentation ([dee5885](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/dee588591328b96d9b9ef908869c8b42bd2632f2)) - - **Versal NET** + - **Versal NET** - - Enable a78 errata workarounds ([bcc6e4a](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/bcc6e4a02a88056b9c45ff28f405e09444433528)) - - add default values for silicon ([faa22d4](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/faa22d48d9929d57975b84ab76cb595afdcf57f4)) - - use api_id directly without FUNCID_MASK ([b0eb6d1](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/b0eb6d124b1764264778d17b1519bfe62b7b9337)) + - Enable a78 errata workarounds ([bcc6e4a](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/bcc6e4a02a88056b9c45ff28f405e09444433528)) + - add default values for silicon ([faa22d4](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/faa22d48d9929d57975b84ab76cb595afdcf57f4)) + - use api_id directly without FUNCID_MASK ([b0eb6d1](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/b0eb6d124b1764264778d17b1519bfe62b7b9337)) - **ZynqMP** @@ -4642,6 +6594,7 @@ issues in each release of Trusted Firmware-A. - bump BL2 stack size ([d22f1d3](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/d22f1d358731f0f55f2f392fa587f0fa8d315aa5)) - provide boot files via semihosting ([749d0fa](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/749d0fa80d1c7ca30b4092a381a06deeeaf1747f)) - OP-TEE SP manifest per latest SPMC changes ([b7bc51a](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/b7bc51a7a747bf40d219b2041e5b3ce56737a71b)) + - mock support for CCA NV ctr ([7423e5e](https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/7423e5e893179d37061a67f8eafda24e649a79ea)) - **FVP-R** @@ -8839,7 +10792,7 @@ releases of TF-A. ______________________________________________________________________ -*Copyright (c) 2013-2023, Arm Limited and Contributors. All rights reserved.* +*Copyright (c) 2013-2024, Arm Limited and Contributors. All rights reserved.* [mbed tls releases]: https://tls.mbed.org/tech-updates/releases [pr#1002]: https://github.com/ARM-software/arm-trusted-firmware/pull/1002#issuecomment-312650193 diff --git a/docs/components/arm-sip-service.rst b/docs/components/arm-sip-service.rst index b51a94dc..74a40a3f 100644 --- a/docs/components/arm-sip-service.rst +++ b/docs/components/arm-sip-service.rst @@ -15,19 +15,20 @@ services: The Arm SiP implementation offers the following services: -- Performance Measurement Framework (PMF) - Execution State Switching service -- DebugFS interface Source definitions for Arm SiP service are located in the ``arm_sip_svc.h`` header file. -Performance Measurement Framework (PMF) ---------------------------------------- ++----------------------------+----------------------------+---------------------------------------+ +| ARM_SIP_SVC_VERSION_MAJOR | ARM_SIP_SVC_VERSION_MINOR | Changes | ++============================+============================+=======================================+ +| 1 | 0 | Move DebugFS and PMF to the new vendor| +| | | specific FID range. The old FID range | +| | | for these services are deprecated | ++----------------------------+----------------------------+---------------------------------------+ -The :ref:`Performance Measurement Framework <firmware_design_pmf>` -allows callers to retrieve timestamps captured at various paths in TF-A -execution. +*Table 1: Showing different versions of arm-sip-service and changes done with each version* Execution State Switching service --------------------------------- @@ -88,348 +89,8 @@ Instead, execution starts at the supplied entry point, with the CPU registers 0 and 1 populated with the supplied *Cookie hi* and *Cookie lo* values, respectively. -DebugFS interface ------------------ - -The optional DebugFS interface is accessed through an SMC SiP service. Refer -to the component documentation for details. - -String parameters are passed through a shared buffer using a specific union: - -.. code:: c - - union debugfs_parms { - struct { - char fname[MAX_PATH_LEN]; - } open; - - struct mount { - char srv[MAX_PATH_LEN]; - char where[MAX_PATH_LEN]; - char spec[MAX_PATH_LEN]; - } mount; - - struct { - char path[MAX_PATH_LEN]; - dir_t dir; - } stat; - - struct { - char oldpath[MAX_PATH_LEN]; - char newpath[MAX_PATH_LEN]; - } bind; - }; - -Format of the dir_t structure as such: - -.. code:: c - - typedef struct { - char name[NAMELEN]; - long length; - unsigned char mode; - unsigned char index; - unsigned char dev; - qid_t qid; - } dir_t; - - -* Identifiers - -======================== ============================================= -SMC_OK 0 -SMC_UNK -1 -DEBUGFS_E_INVALID_PARAMS -2 -======================== ============================================= - -======================== ============================================= -MOUNT 0 -CREATE 1 -OPEN 2 -CLOSE 3 -READ 4 -WRITE 5 -SEEK 6 -BIND 7 -STAT 8 -INIT 10 -VERSION 11 -======================== ============================================= - -MOUNT -~~~~~ - -Description -^^^^^^^^^^^ -This operation mounts a blob of data pointed to by path stored in `src`, at -filesystem location pointed to by path stored in `where`, using driver pointed -to by path in `spec`. - -Parameters -^^^^^^^^^^ -======== ============================================================ -uint32_t FunctionID (0x82000030 / 0xC2000030) -uint32_t ``MOUNT`` -======== ============================================================ - -Return values -^^^^^^^^^^^^^ - -=============== ========================================================== -int32_t w0 == SMC_OK on success - - w0 == DEBUGFS_E_INVALID_PARAMS if mount operation failed -=============== ========================================================== - -OPEN -~~~~ - -Description -^^^^^^^^^^^ -This operation opens the file path pointed to by `fname`. - -Parameters -^^^^^^^^^^ - -======== ============================================================ -uint32_t FunctionID (0x82000030 / 0xC2000030) -uint32_t ``OPEN`` -uint32_t mode -======== ============================================================ - -mode can be one of: - -.. code:: c - - enum mode { - O_READ = 1 << 0, - O_WRITE = 1 << 1, - O_RDWR = 1 << 2, - O_BIND = 1 << 3, - O_DIR = 1 << 4, - O_STAT = 1 << 5 - }; - -Return values -^^^^^^^^^^^^^ - -=============== ========================================================== -int32_t w0 == SMC_OK on success - - w0 == DEBUGFS_E_INVALID_PARAMS if open operation failed - -uint32_t w1: file descriptor id on success. -=============== ========================================================== - -CLOSE -~~~~~ - -Description -^^^^^^^^^^^ - -This operation closes a file described by a file descriptor obtained by a -previous call to OPEN. - -Parameters -^^^^^^^^^^ - -======== ============================================================ -uint32_t FunctionID (0x82000030 / 0xC2000030) -uint32_t ``CLOSE`` -uint32_t File descriptor id returned by OPEN -======== ============================================================ - -Return values -^^^^^^^^^^^^^ -=============== ========================================================== -int32_t w0 == SMC_OK on success - - w0 == DEBUGFS_E_INVALID_PARAMS if close operation failed -=============== ========================================================== - -READ -~~~~ - -Description -^^^^^^^^^^^ - -This operation reads a number of bytes from a file descriptor obtained by -a previous call to OPEN. - -Parameters -^^^^^^^^^^ - -======== ============================================================ -uint32_t FunctionID (0x82000030 / 0xC2000030) -uint32_t ``READ`` -uint32_t File descriptor id returned by OPEN -uint32_t Number of bytes to read -======== ============================================================ - -Return values -^^^^^^^^^^^^^ - -On success, the read data is retrieved from the shared buffer after the -operation. - -=============== ========================================================== -int32_t w0 == SMC_OK on success - - w0 == DEBUGFS_E_INVALID_PARAMS if read operation failed - -uint32_t w1: number of bytes read on success. -=============== ========================================================== - -SEEK -~~~~ - -Description -^^^^^^^^^^^ - -Move file pointer for file described by given `file descriptor` of given -`offset` related to `whence`. - -Parameters -^^^^^^^^^^ - -======== ============================================================ -uint32_t FunctionID (0x82000030 / 0xC2000030) -uint32_t ``SEEK`` -uint32_t File descriptor id returned by OPEN -sint32_t offset in the file relative to whence -uint32_t whence -======== ============================================================ - -whence can be one of: - -========= ============================================================ -KSEEK_SET 0 -KSEEK_CUR 1 -KSEEK_END 2 -========= ============================================================ - -Return values -^^^^^^^^^^^^^ - -=============== ========================================================== -int32_t w0 == SMC_OK on success - - w0 == DEBUGFS_E_INVALID_PARAMS if seek operation failed -=============== ========================================================== - -BIND -~~~~ - -Description -^^^^^^^^^^^ - -Create a link from `oldpath` to `newpath`. - -Parameters -^^^^^^^^^^ - -======== ============================================================ -uint32_t FunctionID (0x82000030 / 0xC2000030) -uint32_t ``BIND`` -======== ============================================================ - -Return values -^^^^^^^^^^^^^ - -=============== ========================================================== -int32_t w0 == SMC_OK on success - - w0 == DEBUGFS_E_INVALID_PARAMS if bind operation failed -=============== ========================================================== - -STAT -~~~~ - -Description -^^^^^^^^^^^ - -Perform a stat operation on provided file `name` and returns the directory -entry statistics into `dir`. - -Parameters -^^^^^^^^^^ - -======== ============================================================ -uint32_t FunctionID (0x82000030 / 0xC2000030) -uint32_t ``STAT`` -======== ============================================================ - -Return values -^^^^^^^^^^^^^ - -=============== ========================================================== -int32_t w0 == SMC_OK on success - - w0 == DEBUGFS_E_INVALID_PARAMS if stat operation failed -=============== ========================================================== - -INIT -~~~~ - -Description -^^^^^^^^^^^ -Initial call to setup the shared exchange buffer. Notice if successful once, -subsequent calls fail after a first initialization. The caller maps the same -page frame in its virtual space and uses this buffer to exchange string -parameters with filesystem primitives. - -Parameters -^^^^^^^^^^ - -======== ============================================================ -uint32_t FunctionID (0x82000030 / 0xC2000030) -uint32_t ``INIT`` -uint64_t Physical address of the shared buffer. -======== ============================================================ - -Return values -^^^^^^^^^^^^^ - -=============== ====================================================== -int32_t w0 == SMC_OK on success - - w0 == DEBUGFS_E_INVALID_PARAMS if already initialized, - or internal error occurred. -=============== ====================================================== - -VERSION -~~~~~~~ - -Description -^^^^^^^^^^^ -Returns the debugfs interface version if implemented in TF-A. - -Parameters -^^^^^^^^^^ - -======== ============================================================ -uint32_t FunctionID (0x82000030 / 0xC2000030) -uint32_t ``VERSION`` -======== ============================================================ - -Return values -^^^^^^^^^^^^^ - -=============== ====================================================== -int32_t w0 == SMC_OK on success - - w0 == SMC_UNK if interface is not implemented - -uint32_t w1: On success, debugfs interface version, 32 bits - value with major version number in upper 16 bits and - minor version in lower 16 bits. -=============== ====================================================== - -* CREATE(1) and WRITE (5) command identifiers are unimplemented and - return `SMC_UNK`. - -------------- -*Copyright (c) 2017-2020, Arm Limited and Contributors. All rights reserved.* +*Copyright (c) 2017-2024, Arm Limited and Contributors. All rights reserved.* .. _SMC Calling Convention: https://developer.arm.com/docs/den0028/latest diff --git a/docs/components/context-management-library.rst b/docs/components/context-management-library.rst new file mode 100644 index 00000000..6a76ada3 --- /dev/null +++ b/docs/components/context-management-library.rst @@ -0,0 +1,568 @@ +Context Management Library +************************** + +This document provides an overview of the Context Management library implementation +in Trusted Firmware-A (TF-A). It enumerates and describes the APIs implemented +and their accessibility from other components at EL3. + +Overview +======== + +Arm TrustZone architecture facilitates hardware-enforced isolation between +software running in various security states (Secure/Non-Secure/Realm). +The general-purpose registers, most of the system registers and vector registers +are not banked per world. When moving between the security states it is the +responsibility of the secure monitor software (BL31(AArch64) / BL32(Aarch32)) +in TF-A, not the hardware, to save and restore register state. +Refer to `Trustzone for AArch64`_ for more details. + +EL3 Runtime Firmware, also termed as secure monitor firmware, is integrated +with a context management library to handle the context of the CPU, managing the +saving and restoring of register states across the worlds. + +TF-A Context +============ + +In TF-A, the context is represented as a data structure used by the EL3 firmware +to preserve the state of the CPU at the next lower exception level (EL) in a given +security state and save enough EL3 metadata to be able to return to that exception +level and security state. The memory for the context data structures are allocated +in BSS section of EL3 firmware. + +In a trusted system at any instance, a given CPU could be executing in one of the +security states (Non-Secure, Secure, Realm). Each world must have its +configuration of system registers independent of other security states to access +and execute any of the architectural features. + +If the CPU switches across security states (for example: from Non-secure to Secure +or vice versa), the register contents, especially the ones that are not banked +(EL2/EL1, vector, general-purpose registers), will be overwritten, as the software +running in either state has the privileges to access them. Additionally, some of +the architectural features enabled in the former security state will be unconditionally +accessible in the latter security state as well. This can be a major concern when +dealing with security-specific bits, as they need to be explicitly enabled or +disabled in each state to prevent data leakage across the worlds. + +In general, an ideal trusted system should have Secure world-specific configurations +that are not influenced by Normal World operations. Therefore, for each CPU, we +need to maintain world-specific context to ensure that register entries from one +world do not leak or impact the execution of the CPU in other worlds. +This will help ensure the integrity and security of the system, preventing any +unauthorized access or data corruption between the different security states. + +Design +====== + +The Context Management library in TF-A is designed to cover all the requirements +for maintaining world-specific context essential for a trusted system. +This includes implementing CPU context initialization and management routines, +as well as other helper APIs that are required by dispatcher components in EL3 +firmware, which are collectively referred to as CPU Context Management. +The APIs and their usecases are listed in detail under the :ref:`Library APIs` +section. + +Originally, the Context Management library in TF-A was designed to cater for a +two-world system, comprising of Non-Secure and Secure Worlds. In this case, the +EL3 Firmware is assumed to be running in Secure World. +With introduction of Realm Management Extension (RME), from Armv9.2 a system +can have four distinct worlds (Non-Secure, Secure, Realm, Root). +RME isolates EL3 from all other Security states and moves it into its own security +state called root. EL3 firmware now runs at Root World and thereby is +trusted from software in Non-secure, Secure, and Realm states. +Refer to `Security States with RME`_ for more details. + +Key principles followed in designing the context management library : + +1. **EL3 should only initialize immediate used lower EL** + +Context Management library running at EL3 should only initialize and monitor the +immediate used lower EL. This implies that, when S-EL2 is present in the system, +EL3 should initialise and monitor S-EL2 registers only. S-EL1 registers should +not be the concern of EL3 while S-EL2 is in place. In systems where S-EL2 is +absent, S-EL1 registers should be initialised from EL3. + +2. **Decentralized model for context management** + +Each world (Non-Secure, Secure, and Realm) should have their separate component +in EL3 responsible for their respective world context management. +Both the Secure and Realm world have associated dispatcher components in EL3 +firmware to allow management of the respective worlds. For the Non-Secure world, +PSCI Library (BL31)/context management library provides routines to help +initialize the Non-Secure world context. + +3. **Flexibility for Dispatchers to select desired feature set to save and restore** + +Each feature is supported with a helper function ``is_feature_supported(void)``, +to detect its presence at runtime. This helps dispatchers to select the desired +feature set, and thereby save and restore the configuration associated with them. + +4. **Dynamic discovery of Feature enablement by EL3** + +TF-A supports four states for feature enablement at EL3, to make them available +for lower exception levels. + +.. code:: c + + #define FEAT_STATE_DISABLED 0 + #define FEAT_STATE_ENABLED 1 + #define FEAT_STATE_CHECK 2 + #define FEAT_STATE_CHECK_ASYMMETRIC 3 + +A pattern is established for feature enablement behavior. +Each feature must support the 3 possible values with rigid semantics. + +- **FEAT_STATE_DISABLED** - all code relating to this feature is always skipped. + Firmware is unaware of this feature. + +- **FEAT_STATE_ALWAYS** - all code relating to this feature is always executed. + Firmware expects this feature to be present in hardware. + +- **FEAT_STATE_CHECK** - same as ``FEAT_STATE_ALWAYS`` except that the feature's + existence will be checked at runtime. Default on dynamic platforms (example: FVP). + +- **FEAT_STATE_CHECK_ASYMMETRIC** - same as ``FEAT_STATE_CHECK`` except that the feature's + existence is asymmetric across cores, which requires the feature existence is checked + during warmboot path also. Note that only limited number of features can be asymmetric. + + .. note:: + Only limited number of features can be ``FEAT_STATE_CHECK_ASYMMETRIC`` this is due to + the fact that Operating systems are designed for SMP systems. + There are no clear guidelines what kind of mismatch is allowed but following pointers + can help making a decision + + - All mandatory features must be symmetric. + - Any feature that impacts the generation of page tables must be symmetric. + - Any feature access which does not trap to EL3 should be symmetric. + - Features related with profiling, debug and trace could be asymmetric + - Migration of vCPU/tasks between CPUs should not cause an error + + Whenever there is asymmetric feature support is added for a feature TF-A need to add + feature specific code in context management code. + + .. note:: + ``FEAT_RAS`` is an exception here, as it impacts the execution of EL3 and + it is essential to know its presence at compile time. Refer to ``ENABLE_FEAT`` + macro under :ref:`Build Options` section for more details. + +Code Structure +============== + +`lib/el3_runtime/(aarch32/aarch64)`_ - Context library code directory. + +Source Files +~~~~~~~~~~~~ + +#. ``context_mgmt.c`` : consists of core functions that setup, save and restore + context for different security states alongside high level feature enablement + APIs for individual worlds. + +#. ``cpu_data_array.c`` : contains per_cpu_data structure instantiation. + +#. ``context.S`` : consists of functions that save and restore some of the context + structure members in assembly code. + +#. ``cpu_data.S`` : consists of helper functions to initialise per_cpu_data pointers. + +#. ``el3_common_macros.S`` : consists of macros to facilitate actions to be performed + during cold and warmboot and el3 registers initialisation in assembly code. + +Header Files +~~~~~~~~~~~~ + +#. ``context_mgmt.h`` : contains the public interface to Context Management Library. + +#. ``context.h`` : contains the helper macros and definitions for context entries. + +#. ``cpu_data.h`` : contains the public interface to Per CPU data structure. + +#. ``context_debug.h`` : contains public interface to report context memory + utilisation across the security states. + +#. ``context_el2.h`` : internal header consisting of helper macros to access EL2 + context entries. Used by ``context.h``. + +Apart from these files, we have some context related source files under ``BL1`` +and ``BL31`` directory. ``bl1_context_mgmt.c`` ``bl31_context_mgmt.c`` + +Bootloader Images utilizing Context Management Library +====================================================== + ++-------------------------------------------+-----------------------------+ +| Bootloader | Context Management Library | ++-------------------------------------------+-----------------------------+ +| BL1 | Yes | ++-------------------------------------------+-----------------------------+ +| BL2 | No | ++-------------------------------------------+-----------------------------+ +| BL31 (Aarch64- EL3runtime firmware) | Yes | ++-------------------------------------------+-----------------------------+ +| BL32 (Aarch32- EL3runtime firmware) | Yes | ++-------------------------------------------+-----------------------------+ + +CPU Data Structure +================== +For a given system, depending on the CPU count, the platform statically +allocates memory for the CPU data structure. + +.. code:: c + + /* The per_cpu_ptr_cache_t space allocation */ + cpu_data_t percpu_data[PLATFORM_CORE_COUNT]; + +This CPU data structure has a member element with an array of pointers to hold +the Non-Secure, Realm and Secure security state context structures as listed below. + +.. code:: c + + typedef struct cpu_data { + #ifdef __aarch64__ + void *cpu_context[CPU_DATA_CONTEXT_NUM]; + #endif + + .... + .... + + }cpu_data_t; + +|CPU Data Structure| + +At runtime, ``cpu_context[CPU_DATA_CONTEXT_NUM]`` array will be intitialised with +the Secure, Non-Secure and Realm context structure addresses to ensure proper +handling of the register state. +See :ref:`Library APIs` section for more details. + +CPU Context and Memory allocation +================================= + +CPU Context +~~~~~~~~~~~ +The members of the context structure used by the EL3 firmware to preserve the +state of CPU across exception levels for a given security state are listed below. + +.. code:: c + + typedef struct cpu_context { + gp_regs_t gpregs_ctx; + el3_state_t el3state_ctx; + + cve_2018_3639_t cve_2018_3639_ctx; + + #if ERRATA_SPECULATIVE_AT + errata_speculative_at_t errata_speculative_at_ctx; + #endif + + #if CTX_INCLUDE_PAUTH_REGS + pauth_t pauth_ctx; + #endif + + #if (CTX_INCLUDE_EL2_REGS && IMAGE_BL31) + el2_sysregs_t el2_sysregs_ctx; + #else + el1_sysregs_t el1_sysregs_ctx; + #endif + } cpu_context_t; + +Context Memory Allocation +~~~~~~~~~~~~~~~~~~~~~~~~~ + +CPUs maintain their context per world. The individual context memory allocation +for each CPU per world is allocated by the world-specific dispatcher components +at compile time as shown below. + +|Context memory allocation| + +NS-Context Memory +~~~~~~~~~~~~~~~~~ +It's important to note that the Normal world doesn't possess the dispatcher +component found in the Secure and Realm worlds. Instead, the PSCI library at EL3 +handles memory allocation for ``Non-Secure`` world context for all CPUs. + +.. code:: c + + static cpu_context_t psci_ns_context[PLATFORM_CORE_COUNT]; + +Secure-Context Memory +~~~~~~~~~~~~~~~~~~~~~ +Secure World dispatcher (such as SPMD) at EL3 allocates the memory for ``Secure`` +world context of all CPUs. + +.. code:: c + + static spmd_spm_core_context_t spm_core_context[PLATFORM_CORE_COUNT]; + +Realm-Context Memory +~~~~~~~~~~~~~~~~~~~~ +Realm World dispatcher (RMMD) at EL3 allocates the memory for ``Realm`` world +context of all CPUs. + +.. code:: c + + rmmd_rmm_context_t rmm_context[PLATFORM_CORE_COUNT]; + +To summarize, the world-specific context structures are synchronized with +per-CPU data structures, which means that each CPU will have an array of pointers +to individual worlds. The figure below illustrates the same. + +|CPU Context Memory Configuration| + +Context Setup/Initialization +============================ + +The CPU has been assigned context structures for every security state, which include +Non-Secure, Secure and Realm. It is crucial to initialize each of these structures +during the bootup of every CPU before they enter any security state for the +first time. This section explains the specifics of how the initialization of +every CPU context takes place during both cold and warm boot paths. + +Context Setup during Cold boot +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +The cold boot path is mainly executed by the primary CPU, other than essential +CPU initialization executed by all CPUs. After executing BL1 and BL2, the Primary +CPU jumps to the BL31 image for runtime services initialization. +During this process, the per_cpu_data structure gets initialized with statically +allocated world-specific context memory. + +Later in the cold boot sequence, the BL31 image at EL3 checks for the presence +of a Secure world image at S-EL2. If detected, it invokes the secure context +initialization sequence under SPMD. Additionally, based on RME enablement, +the Realm context gets initialized from the RMMD at EL3. Finally, before exiting +to the normal world, the Non-Secure context gets initialized via the context +management library. At this stage, all Primary CPU contexts are initialized +and the CPU exits EL3 to enter the Normal world. + +|Context Init ColdBoot| + +.. note:: + The figure above illustrates a scenario on FVP for one of the build + configurations with TFTF component at NS-EL2. + +Context Setup during Warmboot +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +During a warm boot sequence, the primary CPU is responsible for powering on the +secondary CPUs. Refer to :ref:`CPU Reset` and :ref:`Firmware Design` sections for +more details on the warm boot. + +|Context Init WarmBoot| + +The primary CPU initializes the Non-Secure context for the secondary CPU while +restoring re-entry information for the Non-Secure world. +It initialises via ``cm_init_context_by_index(target_idx, ep )``. + +``psci_warmboot_entrypoint()`` is the warm boot entrypoint procedure. +During the warm bootup process, secondary CPUs have their secure context +initialized through SPMD at EL3. Upon successful SP initialization, the SPD +power management operations become shared with the PSCI library. During this +process, the SPMD duly registers its handlers with the PSCI library. + +.. code:: c + + file: psci_common.c + const spd_pm_ops_t *psci_spd_pm; + + file: spmd_pm.c + const spd_pm_ops_t spmd_pm = { + .svc_on_finish = spmd_cpu_on_finish_handler, + .svc_off = spmd_cpu_off_handler + } + +Secondary CPUs during their bootup in the ``psci_cpu_on_finish()`` routine get +their secure context initialised via the registered SPMD handler +``spmd_cpu_on_finish_handler()`` at EL3. +The figure above illustrates the same with reference of Primary CPU running at +NS-EL2. + +.. _Library APIs: + +Library APIs +============ + +The public APIs and types can be found in ``include/lib/el3_runtime/context_management.h`` +and this section is intended to provide additional details and clarifications. + +Context Initialization for Individual Worlds +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +The library implements high level APIs for the CPUs in setting up their individual +context for each world (Non-Secure, Secure and Realm). + +.. c:function:: static void setup_context_common(cpu_context_t *ctx, const entry_point_info_t *ep); + +This function is responsible for the general context initialization that applies +to all worlds. It will be invoked first, before calling the individual +world-specific context setup APIs. + +.. c:function:: static void setup_ns_context(cpu_context_t *ctx, const struct entry_point_info *ep); +.. c:function:: static void setup_realm_context(cpu_context_t *ctx, const struct entry_point_info *ep); +.. c:function:: static void setup_secure_context(cpu_context_t *ctx, const struct entry_point_info *ep); + +Depending on the security state that the CPU needs to enter, the respective +world-specific context setup handlers listed above will be invoked once per-CPU +to set up the context for their execution. + +.. c:function:: void cm_manage_extensions_el3(void) + +This function initializes all EL3 registers whose values do not change during the +lifetime of EL3 runtime firmware. It is invoked from each CPU via the cold boot +path ``bl31_main()`` and in the WarmBoot entry path ``void psci_warmboot_entrypoint()``. + +Runtime Save and Restore of Registers +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +EL1 Registers +------------- + +.. c:function:: void cm_el1_sysregs_context_save(uint32_t security_state); +.. c:function:: void cm_el1_sysregs_context_restore(uint32_t security_state); + +These functions are utilized by the world-specific dispatcher components running +at EL3 to facilitate the saving and restoration of the EL1 system registers +during a world switch. + +EL2 Registers +------------- + +.. c:function:: void cm_el2_sysregs_context_save(uint32_t security_state); +.. c:function:: void cm_el2_sysregs_context_restore(uint32_t security_state); + +These functions are utilized by the world-specific dispatcher components running +at EL3 to facilitate the saving and restoration of the EL2 system registers +during a world switch. + +Pauth Registers +--------------- + +Pointer Authentication feature is enabled by default for Non-Secure world and +disabled for Secure and Realm worlds. In this case, we don't need to explicitly +save and restore the Pauth registers during world switch. +However, ``CTX_INCLUDE_PAUTH_REGS`` flag is explicitly used to enable Pauth for +lower exception levels of Secure and Realm worlds. In this scenario, we save the +general purpose and Pauth registers while we enter EL3 from lower ELs via +``prepare_el3_entry`` and restore them back while we exit EL3 to lower ELs +via ``el3_exit``. + +.. code:: c + + .macro save_gp_pmcr_pauth_regs + func restore_gp_pmcr_pauth_regs + +Feature Enablement for Individual Worlds +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. c:function:: static void manage_extensions_nonsecure(cpu_context_t *ctx); +.. c:function:: static void manage_extensions_secure(cpu_context_t *ctx); +.. c:function:: static void manage_extensions_realm(cpu_context_t *ctx) + +Functions that allow the enabling and disabling of architectural features for +each security state. These functions are invoked from the top-level setup APIs +during context initialization. + +Further, a pattern is established for feature enablement code (AArch64). +Each feature implements following APIs as applicable: +Note: (``xxx`` is the name of the feature in the APIs) + +- ``is_feat_xxx_supported()`` and ``is_feat_xxx_present()`` - mandatory for all features. + +- ``xxx_enable(cpu_context * )`` and ``xxx_disable(cpu_context * )`` - optional + functions to enable the feature for the passed context only. To be called in + the respective world's setup_context to select behaviour. + +- ``xxx_init_el3()`` - optional function to enable the feature in-place in any EL3 + registers that are never context switched. The values they write must never + change, otherwise the functions mentioned in previous point should be used. + Invoked from ``cm_manage_extensions_el3()``. + +- ``xxx_init_el2_unused()`` - optional function to enable the feature in-place + in any EL2 registers that are necessary for execution in EL1 with no EL2 present. + +The above mentioned rules, followed for ``FEAT_SME`` is shown below: + +.. code:: c + + void sme_enable(cpu_context_t *context); + void sme_init_el3(void); + void sme_init_el2_unused(void); + void sme_disable(cpu_context_t *context); + +Per-world Context +================= + +Apart from the CPU context structure, we have another structure to manage some +of the EL3 system registers whose values are identical across all the CPUs +referred to as ``per_world_context_t``. +The Per-world context structure is intended for managing EL3 system registers with +identical values across all CPUs, requiring only a singular context entry for each +individual world. This structure operates independently of the CPU context +structure and is intended to manage specific EL3 registers. + +.. code-block:: c + + typedef struct per_world_context { + uint64_t ctx_cptr_el3; + uint64_t ctx_zcr_el3; + uint64_t ctx_mpam3_el3; + } per_world_context_t; + +These functions facilitate the activation of architectural extensions that possess +identical values across all cores for the individual Non-secure, Secure, and +Realm worlds. + +Root-Context (EL3-Execution-Context) +==================================== + +EL3/Root Context is the execution environment while the CPU is running at EL3. + +Previously, while the CPU is in execution at EL3, the system registers persist +with the values of the incoming world. This implies that if the CPU is entering +EL3 from NS world, the EL1 and EL2 system registers which might be modified in +lower exception levels NS(EL2/EL1) will carry forward those values to EL3. +Further the EL3 registers also hold on to the values configured for Non-secure +world, written during the previous ERET from EL3 to NS(EL2/EL1). +Same policy is followed with respect to other worlds (Secure/Realm) depending on +the system configuration. + +The firmware at EL3 has traditionally operated within the context of the incoming +world (Secure/Non-Secure/Realm). This becomes problematic in scenarios where the +EL3/Root world must explicitly use architectural features that depend on system +registers configured for lower exception levels. +A good example of this is the PAuth regs. The Root world would need to program +its own PAuth Keys while executing in EL3 and this needs to be restored in entry +to EL3 from any world. +Therefore, Root world should maintain its own distinct settings to access +features for its own execution at EL3. + +Register values which are currently known to be of importance during EL3 execution, +is referred to as the EL3/Root context. +This includes ( MDCR_EL3.SDD, SCR_EL3.{EA, SIF}, PMCR_EL0.DP, PSTATE.DIT) +EL3 Context ensures, CPU executes under fixed EL3 system register settings +which is not affected by settings of other worlds. + +Root Context needs to be setup as early as possible before we try and access/modify +architectural features at EL3. Its a simple restore operation ``setup_el3_execution_context`` +that overwrites the selected bits listed above. EL3 never changes its mind about +what those values should be, sets it as required for EL3. Henceforth, a Root +context save operation is not required. + +The figure below illustrates the same with NS-world as a reference while entering +EL3. + +|Root Context Sequence| + +.. code:: c + + # EL3/Root_Context routine + .macro setup_el3_execution_context + +EL3 execution context needs to setup at both boot time (cold and warm boot) +entrypaths and at all the possible exception handlers routing to EL3 at runtime. + +*Copyright (c) 2024, Arm Limited and Contributors. All rights reserved.* + +.. |Context Memory Allocation| image:: ../resources/diagrams/context_memory_allocation.png +.. |CPU Context Memory Configuration| image:: ../resources/diagrams/cpu_data_config_context_memory.png +.. |CPU Data Structure| image:: ../resources/diagrams/percpu-data-struct.png +.. |Context Init ColdBoot| image:: ../resources/diagrams/context_init_coldboot.png +.. |Context Init WarmBoot| image:: ../resources/diagrams/context_init_warmboot.png +.. |Root Context Sequence| image:: ../resources/diagrams/root_context_sequence.png +.. _Trustzone for AArch64: https://developer.arm.com/documentation/102418/0101/TrustZone-in-the-processor/Switching-between-Security-states +.. _Security States with RME: https://developer.arm.com/documentation/den0126/0100/Security-states +.. _lib/el3_runtime/(aarch32/aarch64): https://git.trustedfirmware.org/TF-A/trusted-firmware-a.git/tree/lib/el3_runtime diff --git a/docs/components/cot-binding.rst b/docs/components/cot-binding.rst index 4f8c8b72..5d9acdff 100644 --- a/docs/components/cot-binding.rst +++ b/docs/components/cot-binding.rst @@ -67,14 +67,16 @@ Manifests and Certificate node bindings definition - signing-key Usage: - This property is used to refer public key node present in - parent certificate node and it is required property for all - non-root certificates which are authenticated using public-key - present in parent certificate. + For non-root certificates, this property is used to refer + public key node present in parent certificate node and it is + required property for all non-root certificates which are + authenticated using public-key present in parent certificate. - This property is not required for root-certificates - as root-certificates are validated using root of trust - public key provided by platform. + This property is not required for all root-certificates. If + omitted, the root certificate will be validated using the + default platform ROTPK. If instead the root certificate needs + validating using a different ROTPK, the signing-key property + should provide a reference to the ROTPK node to use. Value type: <phandle> @@ -106,7 +108,7 @@ Manifests and Certificate node bindings definition Usage: This property provides the Object ID of public key - provided in the certificate which the help of which + provided in the certificate with the help of which public key information can be extracted. Value type: <string> @@ -120,7 +122,7 @@ Manifests and Certificate node bindings definition Usage: This property provides the Object ID of hash provided in - the certificate which the help of which hash information + the certificate with the help of which hash information can be extracted. Value type: <string> @@ -136,7 +138,7 @@ Example: trusted-key-cert: trusted-key-cert { root-certificate; image-id = <TRUSTED_KEY_CERT_ID>; - antirollback-counter = <&trusted_nv_counter>; + antirollback-counter = <&trusted_nv_ctr>; trusted-world-pk: trusted-world-pk { oid = TRUSTED_WORLD_PK_OID; @@ -150,7 +152,7 @@ Example: image-id = <SCP_FW_KEY_CERT_ID>; parent = <&trusted-key-cert>; signing-key = <&trusted_world_pk>; - antirollback-counter = <&trusted_nv_counter>; + antirollback-counter = <&trusted_nv_ctr>; scp_fw_content_pk: scp_fw_content_pk { oid = SCP_FW_CONTENT_CERT_PK_OID; @@ -310,23 +312,63 @@ Below is non-volatile counters example for ARM platform #address-cells = <1>; #size-cells = <0>; - trusted-nv-counter: trusted_nv_counter { + trusted_nv_ctr: trusted_nv_ctr { id = <TRUSTED_NV_CTR_ID>; reg = <TFW_NVCTR_BASE>; oid = TRUSTED_FW_NVCOUNTER_OID; }; - non_trusted_nv_counter: non_trusted_nv_counter { + non_trusted_nv_ctr: non_trusted_nv_ctr { id = <NON_TRUSTED_NV_CTR_ID>; reg = <NTFW_CTR_BASE>; oid = NON_TRUSTED_FW_NVCOUNTER_OID; }; }; +rot_keys node binding definition +--------------------------------- + +- rot_keys node + Description: Contains root-of-trust keys for the root certificates. + + SUBNODES + - Description: + + Root of trust key information present in the root certificates + are shown by these nodes. + + - rot key node + Description: Provide ROT key information in the certificate. + + PROPERTIES + + - oid + Usage: + + This property provides the Object ID of ROT key provided + in the certificate. + + Value type: <string> + +Example: +Below is rot_keys example for CCA platform + +.. code:: c + + rot_keys { + swd_rot_pk: swd_rot_pk { + oid = SWD_ROT_PK_OID; + }; + + prot_pk: prot_pk { + oid = PROT_PK_OID; + }; + }; + Future update to chain of trust binding --------------------------------------- This binding document needs to be revisited to generalise some terminologies which are currently specific to X.509 certificates for e.g. Object IDs. -*Copyright (c) 2020, Arm Limited. All rights reserved.* +*Copyright (c) 2020-2024, Arm Limited. All rights reserved.* diff --git a/docs/components/fconf/index.rst b/docs/components/fconf/index.rst index 029f324d..b8b4519e 100644 --- a/docs/components/fconf/index.rst +++ b/docs/components/fconf/index.rst @@ -147,3 +147,4 @@ Properties binding information fconf_properties amu-bindings mpmm-bindings + tb_fw_bindings diff --git a/docs/components/fconf/tb_fw_bindings.rst b/docs/components/fconf/tb_fw_bindings.rst new file mode 100644 index 00000000..aee3b8da --- /dev/null +++ b/docs/components/fconf/tb_fw_bindings.rst @@ -0,0 +1,159 @@ +Trusted Boot Firmware Configuration bindings +============================================ + +This document defines the nodes and properties used to define the Trusted-Boot +firmware configuration. Platform owners are advised to define shared bindings +here. If a binding does not generalize, they should be documented +alongside platform documentation. There is no guarantee of backward +compatibility with the nodes and properties outlined in this context. + +Trusted Boot Firmware Configuration +----------------------------------- + +- compatible [mandatory] + - value type: <string> + - Should be the string ``"<plat>,tb_fw"``, where ``<plat>`` is the name of the + platform (i.e. ``"arm,tb_fw"``). + +- disable_auth [mandatory] + - value type: <u32> + - Flag used to dynamically disable authentication for development purposes. + Has two possible values: 0 or 1. Setting the flag to 1 disables + authentication. + +- mbedtls_heap_addr [mandatory] + - value type: <u64> + - Base address of the dynamically allocated Mbed TLS heap. This is given as a placeholder. + +- mbedtls_heap_size [mandatory] + - value type: <u32> + - Size of the Mbed TLS heap. + +IO FIP Handles +-------------- + +- compatible [mandatory] + - value type: <string> + - Should be the string ``"<plat>,io-fip-handle"``, where ``<plat>`` is the name of the + platform (i.e. ``"arm,io-fip-handle"``). + +- scp_bl2_uuid [mandatory] + - value type: <string> + - SCP Firmware SCP_BL2 UUID + +- bl31_uuid [mandatory] + - value type: <string> + - EL3 Runtime Firmware BL31 UUID + +- bl32_uuid [mandatory] + - value type: <string> + - Secure Payload BL32 (Trusted OS) UUID + +- bl32_extra1_uuid [mandatory] + - value type: <string> + - Secure Payload BL32_EXTRA1 (Trusted OS Extra1) UUID + +- bl32_extra2_uuid [mandatory] + - value type: <string> + - Secure Payload BL32_EXTRA2 (Trusted OS Extra2) UUID + +- bl33_uuid [mandatory] + - value type: <string> + - Non-Trusted Firmware BL33 UUID + +- hw_cfg_uuid [mandatory] + - value type: <string> + - HW_CONFIG (e.g. Kernel DT) UUID + +- soc_fw_cfg_uuid [mandatory] + - value type: <string> + - SOC Firmware Configuration SOC_FW_CONFIG UUID + +- tos_fw_cfg_uuid [mandatory] + - value type: <string> + - Trusted OS Firmware Configuration TOS_FW_CONFIG UUID + +- nt_fw_cfg_uuid [mandatory] + - value type: <string> + - Non-Trusted Firmware Configuration NT_FW_CONFIG UUID + +- cca_cert_uuid [optional] + - value type: <string> + - CCA Content Certificate UUID + +- core_swd_cert_uuid [optional] + - value type: <string> + - Core SWD Key Certificate UUID + +- plat_cert_uuid [optional] + - value type: <string> + - Core SWD Key Certificate UUID + +- t_key_cert_uuid [optional] + - value type: <string> + - Trusted Key Certificate UUID + +- scp_fw_key_uuid [optional] + - value type: <string> + - SCP Firmware Key UUID + +- soc_fw_key_uuid [optional] + - value type: <string> + - SOC Firmware Key UUID + +- tos_fw_key_cert_uuid [optional] + - value type: <string> + - TOS Firmware Key UUID + +- nt_fw_key_cert_uuid [optional] + - value type: <string> + - Non-Trusted Firmware Key UUID + +- scp_fw_content_cert_uuid [optional] + - value type: <string> + - SCP Firmware Content Certificate UUID + +- soc_fw_content_cert_uuid [optional] + - value type: <string> + - SOC Firmware Content Certificate UUID + +- tos_fw_content_cert_uuid [optional] + - value type: <string> + - TOS Firmware Content Certificate UUID + +- nt_fw_content_cert_uuid [optional] + - value type: <string> + - Non-Trusted Firmware Content Certificate UUID + +- plat_sp_content_cert_uuid [optional] + - value type: <string> + - Platform Secure Partition Content Certificate UUID + + +Secure Partitions +----------------- + +- compatible [mandatory] + - value type: <string> + - Should be the string ``"<plat>,sp"``, where ``<plat>`` is the name of the + platform (i.e. ``"arm,sp"``). + +- uuid [mandatory] + - value type: <string> + - A string identifying the UUID of the service implemented by this partition. + The UUID format is described in RFC 4122. + +- load-address [mandatory] + - value type: <u32> + - Physical base address of the partition in memory. Absence of this field + indicates that the partition is position independent and can be loaded at + any address chosen at boot time. + +- owner [optional] + - value type: <string> + - A string property representing the name of the owner of the secure + partition, which may be the silicon or platform provider. + +-------------- + +*Copyright (c) 2024, Arm Limited and Contributors. All rights reserved.* diff --git a/docs/components/ffa-manifest-binding.rst b/docs/components/ffa-manifest-binding.rst index ee322ac6..2b6382b3 100644 --- a/docs/components/ffa-manifest-binding.rst +++ b/docs/components/ffa-manifest-binding.rst @@ -1,5 +1,5 @@ FF-A manifest binding to device tree -======================================== +==================================== This document defines the nodes and properties used to define a partition, according to the FF-A specification. @@ -82,7 +82,7 @@ Partition Properties the partition. Absence of this field indicates that the entry point is at offset 0x0 from the base of the partition's binary. -- xlat-granule [mandatory] +- xlat-granule - value type: <u32> - Translation granule used with the partition: @@ -91,10 +91,10 @@ Partition Properties - 0x2: 64k - boot-order - - value type: <u16> + - value type: <u32> - A unique number amongst all partitions that specifies if this partition must be booted before others. The partition with the smaller number will be - booted first. + booted first. Highest vlue allowed for this field is 0xFFFF. - rx-tx-buffer - value type: "memory-regions" node @@ -103,13 +103,15 @@ Partition Properties The "compatible" must be the string "arm,ffa-manifest-rx_tx-buffer". - messaging-method [mandatory] - - value type: <u8> + - value type: <u32> - Specifies which messaging methods are supported by the partition, set bit means the feature is supported, clear bit - not supported: - - Bit[0]: partition can receive direct requests if set - - Bit[1]: partition can send direct requests if set + - Bit[0]: partition can receive direct requests via FFA_MSG_SEND_DIRECT_REQ ABI if set + - Bit[1]: partition can send direct requests via FFA_MSG_SEND_DIRECT_REQ ABI if set - Bit[2]: partition can send and receive indirect messages + - Bit[9]: partition can receive direct requests via FFA_MSG_SEND_DIRECT_REQ2 ABI if set + - Bit[10]: partition can send direct requests via FFA_MSG_SEND_DIRECT_REQ2 ABI if set - managed-exit - value type: <empty> @@ -117,6 +119,11 @@ Partition Properties - This field is deprecated in favor of ns-interrupts-action field in the FF-A v1.1 EAC0 spec. +- managed-exit-virq + - value type: <empty> + - Indicates if the partition needs managed exit, if supported, to be signaled + through vIRQ signal. + - ns-interrupts-action [mandatory] - value type: <u32> - Specifies the action that the SPMC must take in response to a Non-secure @@ -157,11 +164,6 @@ Partition Properties the FF-A boot information blob to be passed in the specified general purpose register. -- stream-endpoint-ids - - value type: <prop-encoded-array> - - List of <u32> tuples, identifying the IDs this partition is acting as - proxy for. - - power-management-messages - value type: <u32> - Specifies which power management messages a partition subscribes to. @@ -172,6 +174,17 @@ Partition Properties - Bit[1]: CPU_SUSPEND - Bit[2]: CPU_SUSPEND_RESUME +- vm-availability-messages + - value type: <u32> + - Specifies which VM availability messages a partition subscribes to. A set + bit means the partition should be informed of the event, clear bit - should + not be informed of event: + + - Bit[0]: VM created + - Bit[1]: VM destroyed + +.. _memory_region_node: + Memory Regions -------------- @@ -209,6 +222,33 @@ Memory Regions then communicate the region properties (including the base address chosen by the partition manager) to the partition. +- load-address-relative-offset + - value type: <u64> + - Offset relative to the load address of the partition. + When this is provided in the partition manifest, it should be added to the + load address to get the base address of the region. The secure partition + manifest can have either "base-address" or "load-address-relative-offset". + It cannot have both. + +- stream-ids + - value type: <prop-encoded-array> + - List of IDs belonging to a DMA capable peripheral device that has access to + the memory region represented by current node. + - Each ID must have been declared in exactly one device region node. + +- smmu-id + - value type: <u32> + - Identifies the SMMU IP that enforces the access control for the DMA device + that owns the above stream-ids. + +- stream-ids-access-permissions + - value type: <prop-encoded-array> + - List of attributes representing the instruction and data access permissions + used by the DMA device streams to access the memory region represented by + current node. + +.. _device_region_node: + Device Regions -------------- @@ -251,11 +291,10 @@ Device Regions - stream-ids - value type: <prop-encoded-array> - - A list of (id, mem-manage) pair, where: - - - id: A unique <u32> value amongst all devices assigned to the partition. + - List of IDs where an ID is a unique <u32> value amongst all devices assigned + to the partition. -- interrupts [mandatory] +- interrupts - value type: <prop-encoded-array> - A list of (id, attributes) pair describing the device interrupts, where: @@ -306,4 +345,4 @@ Device Regions -------------- -*Copyright (c) 2019-2022, Arm Limited and Contributors. All rights reserved.* +*Copyright (c) 2019-2024, Arm Limited and Contributors. All rights reserved.* diff --git a/docs/components/firmware-update.rst b/docs/components/firmware-update.rst index 1ba1e1c6..eda78525 100644 --- a/docs/components/firmware-update.rst +++ b/docs/components/firmware-update.rst @@ -494,4 +494,4 @@ This is only allowed if the image is not being executed. .. _Universally Unique Identifier: https://tools.ietf.org/rfc/rfc4122.txt .. |Flow Diagram| image:: ../resources/diagrams/fwu_flow.png .. |FWU state machine| image:: ../resources/diagrams/fwu_states.png -.. _PSA FW update specification: https://developer.arm.com/documentation/den0118/a/ +.. _PSA FW update specification: https://developer.arm.com/documentation/den0118/latest/ diff --git a/docs/components/granule-protection-tables-design.rst b/docs/components/granule-protection-tables-design.rst index 07637dd5..78d2f12a 100644 --- a/docs/components/granule-protection-tables-design.rst +++ b/docs/components/granule-protection-tables-design.rst @@ -1,17 +1,17 @@ Granule Protection Tables Library ================================= -This document describes the design of the granule protection tables (GPT) +This document describes the design of the Granule Protection Tables (GPT) library used by Trusted Firmware-A (TF-A). This library provides the APIs needed to initialize the GPTs based on a data structure containing information about the systems memory layout, configure the system registers to enable granule protection checks based on these tables, and transition granules between different PAS (physical address spaces) at runtime. -Arm CCA adds two new security states for a total of four: root, realm, secure, and -non-secure. In addition to new security states, corresponding physical address -spaces have been added to control memory access for each state. The PAS access -allowed to each security state can be seen in the table below. +Arm CCA adds two new security states for a total of four: root, realm, secure, +and non-secure. In addition to new security states, corresponding physical +address spaces have been added to control memory access for each state. The PAS +access allowed to each security state can be seen in the table below. .. list-table:: Security states and PAS access rights :widths: 25 25 25 25 25 @@ -45,12 +45,15 @@ allowed to each security state can be seen in the table below. The GPT can function as either a 1 level or 2 level lookup depending on how a PAS region is configured. The first step is the level 0 table, each entry in the -level 0 table controls access to a relatively large region in memory (block +level 0 table controls access to a relatively large region in memory (GPT Block descriptor), and the entire region can belong to a single PAS when a one step -mapping is used, or a level 0 entry can link to a level 1 table where relatively -small regions (granules) of memory can be assigned to different PAS with a 2 -step mapping. The type of mapping used for each PAS is determined by the user -when setting up the configuration structure. +mapping is used. Level 0 entry can also link to a level 1 table (GPT Table +descriptor) with a 2 step mapping. To change PAS of a region dynamically, the +region must be mapped in Level 1 table. + +The Level 1 tables entries with the same PAS can be combined to form a +contiguous block entry using GPT Contiguous descriptor. More details about this +is explained in the following section. Design Concepts and Interfaces ------------------------------ @@ -73,18 +76,23 @@ coded in the firmware. GPT setup is split into two parts: table creation and runtime initialization. In the table creation step, a data structure containing information about the desired PAS regions is passed into the library which validates the mappings, -creates the tables in memory, and enables granule protection checks. In the +creates the tables in memory, and enables granule protection checks. It also +allocates memory for fine-grained locks adjacent to the L0 tables. In the runtime initialization step, the runtime firmware locates the existing tables in memory using the GPT register configuration and saves important data to a structure used by the granule transition service which will be covered more below. In the reference implementation for FVP models, you can find an example of PAS -region definitions in the file ``include/plat/arm/common/arm_pas_def.h``. Table -creation API calls can be found in ``plat/arm/common/arm_bl2_setup.c`` and +region definitions in the file ``plat/arm/board/fvp/include/fvp_pas_def.h``. +Table creation API calls can be found in ``plat/arm/common/arm_common.c`` and runtime initialization API calls can be seen in ``plat/arm/common/arm_bl31_setup.c``. +During the table creation time, the GPT lib opportunistically fuses contiguous +GPT L1 entries having the same PAS. The maximum size of +supported contiguous blocks is defined by ``RME_GPT_MAX_BLOCK`` build option. + Defining PAS regions ~~~~~~~~~~~~~~~~~~~~ @@ -115,8 +123,13 @@ Level 0 and Level 1 Tables ~~~~~~~~~~~~~~~~~~~~~~~~~~ The GPT initialization APIs require memory to be passed in for the tables to be -constructed, ``gpt_init_l0_tables`` takes a memory address and size for building -the level 0 tables and ``gpt_init_pas_l1_tables`` takes an address and size for +constructed. The ``gpt_init_l0_tables`` API takes a memory address and size for +building the level 0 tables and also memory for allocating the fine-grained bitlock +data structure. The amount of memory needed for bitlock structure is controlled via +``RME_GPT_BITLOCK_BLOCK`` config which defines the block size for each bit of the +the bitlock. + +The ``gpt_init_pas_l1_tables`` API takes an address and size for building the level 1 tables which are linked from level 0 descriptors. The tables should have PAS type ``GPT_GPI_ROOT`` and a typical system might place its level 0 table in SRAM and its level 1 table(s) in DRAM. @@ -124,12 +137,28 @@ its level 0 table in SRAM and its level 1 table(s) in DRAM. Granule Transition Service ~~~~~~~~~~~~~~~~~~~~~~~~~~ -The Granule Transition Service allows memory mapped with GPT_MAP_REGION_GRANULE -ownership to be changed using SMC calls. Non-secure granules can be transitioned -to either realm or secure space, and realm and secure granules can be -transitioned back to non-secure. This library only allows memory mapped as -granules to be transitioned, memory mapped as blocks have their GPIs fixed after -table creation. +The Granule Transition Service allows memory mapped with +``GPT_MAP_REGION_GRANULE`` ownership to be changed using SMC calls. Non-secure +granules can be transitioned to either realm or secure space, and realm and +secure granules can be transitioned back to non-secure. This library only +allows Level 1 entries to be transitioned. The lib may either shatter +contiguous blocks or fuse adjacent GPT entries to form a contiguous block +opportunistically. Depending on the maximum block size, the fuse operation may +propogate to higher block sizes as allowed by RME Architecture. Thus a higher +maximum block size may have a higher runtime cost due to software operations +that need to be performed for fuse to bigger block sizes. This cost may +be offset by better TLB performance due to the higher block size and platforms +need to make the trade-off decision based on their particular workload. + +Locking Scheme +~~~~~~~~~~~~~~ + +During Granule Transition access to L1 tables is controlled by a lock to ensure +that no more than one CPU is allowed to make changes at any given time. +The granularity of the lock is defined by ``RME_GPT_BITLOCK_BLOCK`` build option +which defines the size of the memory block protected by one bit of ``bitlock`` +structure. Setting this option to 0 chooses a single spinlock for all GPT L1 +table entries. Library APIs ------------ @@ -196,7 +225,9 @@ The L0 table memory has some constraints that must be taken into account. is greater. L0 table size is the total protected space (PPS) divided by the size of each L0 region (L0GPTSZ) multiplied by the size of each L0 descriptor (8 bytes). ((PPS / L0GPTSZ) * 8) -* The L0 memory size must be greater than or equal to the table size. +* The L0 memory size must be greater than the table size and have enough space + to allocate array of ``bitlock`` structures at the end of L0 table if + required (``RME_GPT_BITLOCK_BLOCK`` is not 0). * The L0 memory must fall within a PAS of type GPT_GPI_ROOT. The L1 memory also has some constraints. @@ -223,6 +254,24 @@ Substitute values to get this: ((0x100000000 / 0x40000000) * 8) And solve to get 32 bytes. In this case, 4096 is greater than 32, so the L0 tables must be aligned to 4096 bytes. +Sample calculation for bitlock array size +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Let PGS=GPCCR_PPS_256TB and RME_GPT_BITLOCK_BLOCK=1 + +The size of bit lock array in bits is the total protected space (PPS) divided +by the size of memory block per bit. The size of memory block +is ``RME_GPT_BITLOCK_BLOCK`` (number of 512MB blocks per bit) times +512MB (0x20000000). This is then divided by the number of bits in ``bitlock`` +structure (8) to get the size of bit array in bytes. + +In other words, we can find the total size of ``bitlock`` array +in bytes with PPS / (RME_GPT_BITLOCK_BLOCK * 0x20000000 * 8). + +Substitute values to get this: 0x1000000000000 / (1 * 0x20000000 * 8) + +And solve to get 0x10000 bytes. + Sample calculation for L1 table size and alignment ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/docs/components/index.rst b/docs/components/index.rst index 30d80fcd..36970260 100644 --- a/docs/components/index.rst +++ b/docs/components/index.rst @@ -26,3 +26,6 @@ Components realm-management-extension rmm-el3-comms-spec granule-protection-tables-design + ven-el3-service + ven-el3-debugfs + context-management-library diff --git a/docs/components/platform-interrupt-controller-API.rst b/docs/components/platform-interrupt-controller-API.rst index 4de39d1e..8cd4bae9 100644 --- a/docs/components/platform-interrupt-controller-API.rst +++ b/docs/components/platform-interrupt-controller-API.rst @@ -282,9 +282,28 @@ may be signalled to the PE. The API should return the current priority value that it's overwriting. In case of Arm standard platforms using GIC, the implementation of the API -inserts to order memory updates before updating mask, then writes to the GIC -*Priority Mask Register*, and make sure memory updates are visible before -potential trigger due to mask update. +inserts barriers to order memory updates before updating mask, +then writes to the GIC *Priority Mask Register*, and make sure memory updates +are visible before potential trigger due to mask update. + +Function: unsigned int plat_ic_deactivate_priority(unsigned int id); [optional] +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +:: + + Argument : unsigned int + Return : int + +This API performs the operations of plat_ic_set_priority_mask along with +calling the errata workaround gicv3_apply_errata_wa_2384374(). This is +performed when priority mask is restored to it's older value. This API returns +the current priority value that it's overwriting. + +In case of Arm standard platforms using GIC, the implementation of the API +inserts barriers to order memory updates before updating mask, then writes +to the GIC *Priority Mask Register*, and make sure memory updates +are visible before potential trigger due to mask update, and +applies 2384374 GIC errata workaround to process pending interrupt packets. .. _plat_ic_get_interrupt_id: diff --git a/docs/components/realm-management-extension.rst b/docs/components/realm-management-extension.rst index f228e6b5..39186b42 100644 --- a/docs/components/realm-management-extension.rst +++ b/docs/components/realm-management-extension.rst @@ -237,7 +237,7 @@ Use the following command to run the tests on FVP. -C bp.ve_sysregs.exit_on_shutdown=1 \ -C cache_state_modelled=1 \ -C bp.dram_size=4 \ - -C bp.secure_memory=1 \ + -C bp.secure_memory=0 \ -C pci.pci_smmuv3.mmu.SMMU_ROOT_IDR0=3 \ -C pci.pci_smmuv3.mmu.SMMU_ROOT_IIDR=0x43B \ -C pci.pci_smmuv3.mmu.root_register_page_offset=0x20000 \ diff --git a/docs/components/rmm-el3-comms-spec.rst b/docs/components/rmm-el3-comms-spec.rst index 009ac28c..79e1d2cd 100644 --- a/docs/components/rmm-el3-comms-spec.rst +++ b/docs/components/rmm-el3-comms-spec.rst @@ -52,8 +52,8 @@ are explained below: - ``RES0``: Bit 31 of the version number is reserved 0 as to maintain consistency with the versioning schemes used in other parts of RMM. -This document specifies the 0.2 version of Boot Interface ABI and RMM-EL3 -services specification and the 0.2 version of the Boot Manifest. +This document specifies the 0.4 version of Boot Interface ABI and RMM-EL3 +services specification and the 0.3 version of the Boot Manifest. .. _rmm_el3_boot_interface: @@ -159,8 +159,8 @@ as per the following table: ``E_RMM_BOOT_SUCCESS``,Boot successful,0 ``E_RMM_BOOT_ERR_UNKNOWN``,Unknown error,-1 ``E_RMM_BOOT_VERSION_NOT_VALID``,Boot Interface version reported by EL3 is not supported by RMM,-2 - ``E_RMM_BOOT_CPUS_OUT_OF_RAGE``,Number of CPUs reported by EL3 larger than maximum supported by RMM,-3 - ``E_RMM_BOOT_CPU_ID_OUT_OF_RAGE``,Current CPU Id is higher or equal than the number of CPUs supported by RMM,-4 + ``E_RMM_BOOT_CPUS_OUT_OF_RANGE``,Number of CPUs reported by EL3 larger than maximum supported by RMM,-3 + ``E_RMM_BOOT_CPU_ID_OUT_OF_RANGE``,Current CPU Id is higher or equal than the number of CPUs supported by RMM,-4 ``E_RMM_BOOT_INVALID_SHARED_BUFFER``,Invalid pointer to shared memory area,-5 ``E_RMM_BOOT_MANIFEST_VERSION_NOT_SUPPORTED``,Version reported by the Boot Manifest not supported by RMM,-6 ``E_RMM_BOOT_MANIFEST_DATA_ERROR``,Error parsing core Boot Manifest,-7 @@ -182,17 +182,20 @@ platform information. This Boot Manifest is versioned independently of the Boot Interface, to help evolve the former independent of the latter. -The current version for the Boot Manifest is ``v0.2`` and the rules explained +The current version for the Boot Manifest is ``v0.3`` and the rules explained in :ref:`rmm_el3_ifc_versioning` apply on this version as well. -The Boot Manifest v0.2 has the following fields: +The Boot Manifest v0.3 has the following fields: - - version : Version of the Manifest (v0.2) + - version : Version of the Manifest (v0.3) - plat_data : Pointer to the platform specific data and not specified by this document. These data are optional and can be NULL. - plat_dram : Structure encoding the NS DRAM information on the platform. This - field is also optional and platform can choose to zero out this structure if + field is optional and platform can choose to zero out this structure if RMM does not need EL3 to send this information during the boot. + - plat_console : Structure encoding the list of consoles for RMM use on the + platform. This field is optional and platform can choose to not populate + the console list if this is not needed by the RMM for this platform. For the current version of the Boot Manifest, the core manifest contains a pointer to the platform data. EL3 must ensure that the whole Boot Manifest, including @@ -235,6 +238,7 @@ error condition as described in the following table: ``E_RMM_BAD_PAS``,Incorrect PAS,-3 ``E_RMM_NOMEM``,Not enough memory to perform an operation,-4 ``E_RMM_INVAL``,The value of an argument was invalid,-5 + ``E_RMM_AGAIN``,The resource is busy. Try again.,-6 If multiple failure conditions are detected in an RMM to EL3 command, then EL3 is allowed to return an error code corresponding to any of the failure @@ -255,6 +259,8 @@ implemented by EL3 Firmware. 0xC40001B1,``RMM_GTSI_UNDELEGATE`` 0xC40001B2,``RMM_ATTEST_GET_REALM_KEY`` 0xC40001B3,``RMM_ATTEST_GET_PLAT_TOKEN`` + 0xC40001B4,``RMM_EL3_FEATURES`` + 0xC40001B5,``RMM_EL3_TOKEN_SIGN`` RMM_RMI_REQ_COMPLETE command ============================ @@ -439,7 +445,21 @@ Supported ECC Curves RMM_ATTEST_GET_PLAT_TOKEN command ================================= -Retrieve the Platform Token from EL3. +Retrieve the Platform Token from EL3. If the entire token does not fit in the +buffer, EL3 returns a hunk of the token (via ``tokenHunkSize`` parameter) and +indicates the remaining bytes that are pending retrieval (via ``remainingSize`` +parameter). The challenge object for the platform token must be populated in +the buffer for the first call of this command and the size of the object is +indicated by ``c_size`` parameter. Subsequent calls to retrieve remaining hunks of +the token must be made with ``c_size`` as 0. + +If ``c_size`` is not 0, this command could cause regeneration of platform token +and will return token hunk corresponding to beginning of the token. + +It is valid for the calls of this command to return ``E_RMM_AGAIN`` error, +which is an indication to the caller to retry this command again. Depending on the +platform, this mechanism can be used to implement queuing to HES, if HES is +involved in platform token generation. FID --- @@ -454,9 +474,9 @@ Input values :widths: 1 1 1 1 5 fid,x0,[63:0],UInt64,Command FID - buf_pa,x1,[63:0],Address,PA of the platform attestation token. The challenge object is passed in this buffer. The PA must belong to the shared buffer + buf_pa,x1,[63:0],Address,"PA of the platform attestation token. The challenge object must be passed in this buffer for the first call of this command. Any subsequent calls, if required to retrieve the full token, should not have this object. The PA must belong to the shared buffer." buf_size,x2,[63:0],Size,Size in bytes of the platform attestation token buffer. ``bufPa + bufSize`` must lie within the shared buffer - c_size,x3,[63:0],Size,Size in bytes of the challenge object. It corresponds to the size of one of the defined SHA algorithms + c_size,x3,[63:0],Size,"Size in bytes of the challenge object. It corresponds to the size of one of the defined SHA algorithms. Any subsequent calls, if required to retrieve the full token, should set this size to 0." Output values ------------- @@ -466,7 +486,8 @@ Output values :widths: 1 1 1 1 5 Result,x0,[63:0],Error Code,Command return status - tokenSize,x1,[63:0],Size,Size of the platform token + tokenHunkSize,x1,[63:0],Size,Size of the platform token hunk retrieved + remainingSize,x2,[63:0],Size,Remaining bytes of the token that are pending retrieval Failure conditions ------------------ @@ -478,12 +499,178 @@ a failure. The errors are ordered by condition check. :header: "ID", "Condition" :widths: 1 5 + ``E_RMM_AGAIN``,Resource for Platform token retrieval is busy. Try again. ``E_RMM_BAD_ADDR``,``PA`` is outside the shared buffer ``E_RMM_INVAL``,``PA + BSize`` is outside the shared buffer - ``E_RMM_INVAL``,``CSize`` does not represent the size of a supported SHA algorithm + ``E_RMM_INVAL``,``CSize`` does not represent the size of a supported SHA algorithm for the first call to this command + ``E_RMM_INVAL``,``CSize`` is not 0 for subsequent calls to retrieve remaining hunks of the token ``E_RMM_UNK``,An unknown error occurred whilst processing the command ``E_RMM_OK``,No errors detected +RMM_EL3_FEATURES command +======================== + +This command provides a mechanism to discover features and ABIs supported by the +RMM-EL3 interface, for a given version. This command is helpful when there are +platform specific optional RMM-EL3 interfaces and features exposed by vendor +specific EL3 firmware, and a generic RMM that can modify its behavior based on +discovery of EL3 features. + +The features can be discovered by specifying the feature register index that +has fields defined to indicate presence or absence of features and other +relevant information. The feature register index is specified in the +``feat_reg_idx`` parameter. Each feature register is a 64 bit register. + +This command is available from v0.4 of the RMM-EL3 interface. + +The following is the register definition for feature register index 0 for +v0.4 of the interface: + +RMM-EL3 Feature Resister 0 +-------------------------- + +.. code-block:: none + + 63 32 31 16 15 8 7 1 0 + +-------+-------+-------+-------+-------+-------+-------+-------+ + | | | | | | | | | + | | | | | | | | | + +-------+-------+-------+-------+-------+-------+-------+-------+ + ^ + | + RMMD_EL3_TOKEN_SIGN + +**Bit Fields:** + +- **Bit 0**: `RMMD_EL3_TOKEN_SIGN` + - When set to 1, the `RMMD_EL3_TOKEN_SIGN` feature is enabled. + - When cleared (0), the feature is disabled. +- **Bits [1:63]**: Reserved (must be zero) + +FID +--- + +``0xC40001B4`` + + +Input values +------------ + +.. csv-table:: Input values for RMM_EL3_FEATURES + :header: "Name", "Register", "Field", "Type", "Description" + :widths: 1 1 1 1 5 + + fid,x0,[63:0],UInt64,Command FID + feat_reg_idx,x1,[63:0],UInt64, "Feature register index. For v0.4, a value of 0 is the only + acceptable value" + + +Output values +------------- + +.. csv-table:: Output values for RMM_EL3_FEATURES + :header: "Name", "Register", "Field", "Type", "Description" + :widths: 1 1 1 1 5 + + Result,x0,[63:0],Error Code,Command return status + feat_reg,x1,[63:0],Value,Value of the register as defined above + +Failure conditions +------------------ + +The table below shows all the possible error codes returned in ``Result`` upon +a failure. The errors are ordered by condition check. + +.. csv-table:: Failure conditions for RMM_EL3_FEATURES + :header: "ID", "Condition" + :widths: 1 5 + + ``E_RMM_INVAL``,``feat_reg_idx`` is out of valid range + ``E_RMM_UNK``,"if the SMC is not present, if interface version is <0.4" + ``E_RMM_OK``,No errors detected + +RMM_EL3_TOKEN_SIGN command +========================== + +This command is an optional command that can be discovered using the RMM_EL3_FEATURES command. +This command is used to send requests related to realm attestation token signing requests to EL3. +The command supports 3 opcodes: + + - RMM_EL3_TOKEN_SIGN_PUSH_REQ_OP + - RMM_EL3_TOKEN_SIGN_PULL_RESP_OP + - RMM_EL3_TOKEN_SIGN_GET_RAK_PUB_OP + +The above opcodes can be used to send realm attestation token signing requests to EL3 and get their +response, so that the realm attestation token can be constructed. + +This command is useful when the RMM may not have access to the private portion of the realm +attestation key and needs signing services from EL3 or CCA HES, or other platform specific +mechanisms to perform signing. + +The RMM-EL3 interface for this command is modeled as two separate queues, one for signing requests +and one for retrieving the signed responses. It is possible that the queue in EL3 is full or EL3 is busy and +unable to service the RMM requests, in which case the RMM is expected to retry the push operation +for requests and pop operation for responses. + +FID +--- + +``0xC40001B5`` + +Input values +------------ + +.. csv-table:: Input values for RMM_EL3_TOKEN_SIGN + :header: "Name", "Register", "Field", "Type", "Description" + :widths: 1 1 1 1 5 + + fid,x0,[63:0],UInt64,Command FID + opcode,x1,[63:0],UInt64," + Opcode that is one of: + + - RMM_EL3_TOKEN_SIGN_PUSH_REQ_OP: 0x1 - + Opcode to push a token signing request to EL3 using struct el3_token_sign_request as described above + - RMM_EL3_TOKEN_SIGN_PULL_RESP_OP: 0x2 - + Opcode to pull a token signing response from EL3 using struct el3_token_sign_response as described above + - RMM_EL3_TOKEN_SIGN_GET_RAK_PUB_OP: 0x3 - + Opcode to get the realm attestation public key + + " + buf_pa,x2,[63:0],Address,"PA where the request structure is stored for the opcode RMM_EL3_TOKEN_SIGN_PUSH_REQ_OP, the response structure needs to be populated for the opcode RMM_EL3_TOKEN_SIGN_PULL_RESP_OP, or where the public key must be populated for the opcode RMM_EL3_TOKEN_SIGN_GET_RAK_PUB_OP. The PA must belong to the RMM-EL3 shared buffer" + buf_size,x3,[63:0],Size,Size in bytes of the input buffer in ``buf_pa``. ``buf_pa + buf_size`` must lie within the shared buffer + ecc_curve,x4,[63:0],Enum,Type of the elliptic curve to which the requested attestation key belongs to. See :ref:`ecc_curves`. This parameter is valid on for the opcode RMM_EL3_TOKEN_SIGN_GET_RAK_PUB_OP + +Output values +------------- + +.. csv-table:: Output values for RMM_EL3_TOKEN_SIGN + :header: "Name", "Register", "Field", "Type", "Description" + :widths: 1 1 1 1 5 + + Result,x0,[63:0],Error Code,Command return status. Valid for all opcodes listed in input values + retval1,x1,[63:0],Value, "If opcode is RMM_EL3_TOKEN_SIGN_GET_RAK_PUB_OP, then returns length of + public key returned. Otherwise, reserved" + + +Failure conditions +------------------ + +The table below shows all the possible error codes returned in ``Result`` upon +a failure. The errors are ordered by condition check. + +.. csv-table:: Failure conditions for RMM_EL3_TOKEN_SIGN + :header: "ID", "Condition" + :widths: 1 5 + + ``E_RMM_INVAL``,"if opcode is invalid or buffer address and length passed to the EL3 are not in valid range + corresponding to the RMM-EL3 shared buffer, or if the curve used for opcode + RMM_EL3_TOKEN_SIGN_GET_RAK_PUB_OP is not the ECC P384 curve" + ``E_RMM_UNK``,"if the SMC is not present, if interface version is <0.4" + ``E_RMM_AGAIN``,"For opcode RMM_EL3_TOKEN_SIGN_PUSH_REQ_OP, if the request is not queued since + the EL3 queue is full, or if the response is not ready yet, for other opcodes" + ``E_RMM_OK``,No errors detected + + RMM-EL3 world switch register save restore convention _____________________________________________________ @@ -533,23 +720,25 @@ _____ RMM-EL3 Boot Manifest structure ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -The RMM-EL3 Boot Manifest v0.2 structure contains platform boot information passed -from EL3 to RMM. The size of the Boot Manifest is 40 bytes. +The RMM-EL3 Boot Manifest v0.3 structure contains platform boot information passed +from EL3 to RMM. The size of the Boot Manifest is 64 bytes. The members of the RMM-EL3 Boot Manifest structure are shown in the following table: -+-----------+--------+----------------+----------------------------------------+ -| Name | Offset | Type | Description | -+===========+========+================+========================================+ -| version | 0 | uint32_t | Boot Manifest version | -+-----------+--------+----------------+----------------------------------------+ -| padding | 4 | uint32_t | Reserved, set to 0 | -+-----------+--------+----------------+----------------------------------------+ -| plat_data | 8 | uintptr_t | Pointer to Platform Data section | -+-----------+--------+----------------+----------------------------------------+ -| plat_dram | 16 | ns_dram_info | NS DRAM Layout Info structure | -+-----------+--------+----------------+----------------------------------------+ ++--------------+--------+----------------+----------------------------------------+ +| Name | Offset | Type | Description | ++==============+========+================+========================================+ +| version | 0 | uint32_t | Boot Manifest version | ++--------------+--------+----------------+----------------------------------------+ +| padding | 4 | uint32_t | Reserved, set to 0 | ++--------------+--------+----------------+----------------------------------------+ +| plat_data | 8 | uintptr_t | Pointer to Platform Data section | ++--------------+--------+----------------+----------------------------------------+ +| plat_dram | 16 | ns_dram_info | NS DRAM Layout Info structure | ++--------------+--------+----------------+----------------------------------------+ +| plat_console | 40 | console_list | List of consoles available to RMM | ++--------------+--------+----------------+----------------------------------------+ .. _ns_dram_info_struct: @@ -587,5 +776,99 @@ NS DRAM Bank structure contains information about each Non-secure DRAM bank: | size | 8 | uint64_t | Size of bank in bytes | +-----------+--------+----------------+----------------------------------------+ +.. _console_list_struct: + +Console List structure +~~~~~~~~~~~~~~~~~~~~~~ + +Console List structure contains information about the available consoles for RMM. +The members of this structure are shown in the table below: + ++--------------+--------+----------------+----------------------------------------+ +| Name | Offset | Type | Description | ++==============+========+================+========================================+ +| num_consoles | 0 | uint64_t | Number of consoles | ++--------------+--------+----------------+----------------------------------------+ +| consoles | 8 | console_info * | Pointer to 'console_info'[] array | ++--------------+--------+----------------+----------------------------------------+ +| checksum | 16 | uint64_t | Checksum | ++--------------+--------+----------------+----------------------------------------+ +Checksum is calculated as two's complement sum of 'num_consoles', 'consoles' +pointer and the consoles array pointed by it. + +.. _console_info_struct: + +Console Info structure +~~~~~~~~~~~~~~~~~~~~~~ +Console Info structure contains information about each Console available to RMM. + ++-----------+--------+---------------+----------------------------------------+ +| Name | Offset | Type | Description | ++===========+========+===============+========================================+ +| base | 0 | uintptr_t | Console Base address | ++-----------+--------+---------------+----------------------------------------+ +| map_pages | 8 | uint64_t | Num of pages to map for console MMIO | ++-----------+--------+---------------+----------------------------------------+ +| name | 16 | char[] | Name of console | ++-----------+--------+---------------+----------------------------------------+ +| clk_in_hz | 24 | uint64_t | UART clock (in hz) for console | ++-----------+--------+---------------+----------------------------------------+ +| baud_rate | 32 | uint64_t | Baud rate | ++-----------+--------+---------------+----------------------------------------+ +| flags | 40 | uint64_t | Additional flags (RES0) | ++-----------+--------+---------------+----------------------------------------+ + +.. _el3_token_sign_request_struct: + +EL3 Token Sign Request structure +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +This structure represents a realm attestation toekn signing request. + ++-------------+--------+---------------+-----------------------------------------+ +| Name | Offset | Type | Description | ++=============+========+===============+=========================================+ +| sig_alg_id | 0 | uint32_t | Algorithm idenfier for the sign request.| +| | | | - 0x0: ECC SECP384R1 (ECDSA) | +| | | | - Other values reserved | ++-------------+--------+---------------+-----------------------------------------+ +| rec_granule | 8 | uint64_t | Identifier used by RMM to associate | +| | | | a signing request to a realm. Must not | +| | | | be interpreted or modified. | ++-------------+--------+---------------+-----------------------------------------+ +| req_ticket | 16 | uint64_t | Value used by RMM to associate request | +| | | | and responses. Must not be interpreted | +| | | | or modified. | ++-------------+--------+---------------+-----------------------------------------+ +| hash_alg_id | 24 | uint32_t | Hash algorithm for data in `hash_buf` | +| | | | - 0x1: SHA2-384 | +| | | | - All other values reserved. | ++-------------+--------+---------------+-----------------------------------------+ +| hash_buf | 32 | uint8_t[] | TBS (to-be-signed) Hash of length | +| | | | defined by hash algorithm `hash_alg_id` | ++-------------+--------+---------------+-----------------------------------------+ + +.. _el3_token_sign_response_struct: + +EL3 Token Sign Response structure +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +This structure represents a realm attestation token signing response. + ++---------------+--------+---------------+-----------------------------------------+ +| Name | Offset | Type | Description | ++===============+========+===============+=========================================+ +| rec_granule | 0 | uint64_t | Identifier used by RMM to associate | +| | | | a signing request to a realm. Must not | +| | | | be interpreted or modified. | ++---------------+--------+---------------+-----------------------------------------+ +| req_ticket | 8 | uint64_t | Value used by RMM to associate request | +| | | | and responses. Must not be interpreted | +| | | | or modified. | ++---------------+--------+---------------+-----------------------------------------+ +| sig_len | 16 | uint16_t | Length of the `signature_buf` field | ++---------------+--------+---------------+-----------------------------------------+ +| signature_buf | 18 | uint8_t[] | Signature | ++---------------+--------+---------------+-----------------------------------------+ diff --git a/docs/components/romlib-design.rst b/docs/components/romlib-design.rst index d34b3cc5..c0f3ed34 100644 --- a/docs/components/romlib-design.rst +++ b/docs/components/romlib-design.rst @@ -71,13 +71,22 @@ image(s) is replaced with the wrapper function. The "library at ROM" contains a necessary init function that initialises the global variables defined by the functions inside "library at ROM". +Wrapper functions are specified at the link stage of compilation and cannot +interpose uppon functions within the same translation unit. For example, if +function ``fn_a`` calls ``fn_b`` within translation unit ``functions.c`` and +the romlib jump table includes an entry for ``fn_b``, ``fn_a`` will include +a reference to ``fn_b``'s original program text instead of the wrapper. Thus +the jumptable author must take care to include public entry points into +translation units to avoid paying the program text cost twice, once in the +original executable and once in romlib. + Script ~~~~~~ -There is a ``romlib_generate.py`` Python script that generates the necessary +There is a ``romlib_generator.py`` Python script that generates the necessary files for the "library at ROM" to work. It implements multiple functions: -1. ``romlib_generate.py gentbl [args]`` - Generates the jump table by parsing +1. ``romlib_generator.py gentbl [args]`` - Generates the jump table by parsing the index file. 2. ``romlib_generator.py genvar [args]`` - Generates the jump table global @@ -86,17 +95,17 @@ files for the "library at ROM" to work. It implements multiple functions: 3. ``romlib_generator.py genwrappers [args]`` - Generates a wrapper function for each entry in the index file except for the ones that contain the keyword - ``patch``. The generated wrapper file is called ``<fn_name>.s``. + ``patch``. The generated wrapper file is called ``wrappers.s``. 4. ``romlib_generator.py pre [args]`` - Preprocesses the index file which means it resolves all the include commands in the file recursively. It can also generate a dependency file of the included index files which can be directly used in makefiles. -Each ``romlib_generate.py`` function has its own manual which is accessible by +Each ``romlib_generator.py`` function has its own manual which is accessible by runing ``romlib_generator.py [function] --help``. -``romlib_generate.py`` requires Python 3 environment. +``romlib_generator.py`` requires Python 3 environment. Patching of functions in library at ROM diff --git a/docs/components/sdei.rst b/docs/components/sdei.rst index 60259c83..309375f9 100644 --- a/docs/components/sdei.rst +++ b/docs/components/sdei.rst @@ -354,7 +354,51 @@ implemented in assembly, following a similar pattern as below: -------------- -*Copyright (c) 2017-2019, Arm Limited and Contributors. All rights reserved.* +Security Considerations +----------------------- + +SDEI introduces concept of providing software based non-maskable interrupts to +Hypervisor/OS. In doing so, it modifies the priority scheme defined by Interrupt +controllers and relies on Non-Secure clients, Hypervisor or OS, to create/manage +high priority events. + +Considering a Non-secure client is involved in SDEI state management, there exists +some security considerations which needs to be taken care of in both client and EL3 +when using SDEI. Few of them are mentioned below. + +Bound events +~~~~~~~~~~~~ + +A bound event is an SDEI event that corresponds to a client interrupt. +The binding of event is done using ``SDEI_INTERRUPT_BIND`` SMC call to associate +an SDEI event with a client interrupt. There is a possibility that a rogue +client can request an invalid interrupt to be bound. This may potentially +cause out-of-bound memory read. + +Even though TF-A implementation has checks to ensure that interrupt ID passed +by client is architecturally valid, Non-secure client should also ensure the +validity of interrupts. + +Recurring events +~~~~~~~~~~~~~~~~ + +For a given event source, if the events are generated continuously, then NS client +may be unusable. To mitigate against this, the Non-secure client must have +mechanism in place to remove such interrupt source from the system. + +One of the examples is a memory region which continuously generates RAS errors. +This may result in unusable Non-secure client. + +Dispatched events +~~~~~~~~~~~~~~~~~ + +For a dispatched event, it is the client's responsibility to ensure that the +handling finishes in finite time and notify the dispatcher through +``SDEI_EVENT_COMPLETE`` or ``SDEI_EVENT_COMPLETE_AND_RESUME``. If the client +fails to complete the event handling, it might result in ``UNPREDICTABLE`` behavior +in the client and potentially end up in unusable PE. + +*Copyright (c) 2017-2024, Arm Limited and Contributors. All rights reserved.* .. rubric:: Footnotes diff --git a/docs/components/secure-partition-manager.rst b/docs/components/secure-partition-manager.rst index 5d3adec8..a181204b 100644 --- a/docs/components/secure-partition-manager.rst +++ b/docs/components/secure-partition-manager.rst @@ -9,43 +9,13 @@ Secure Partition Manager Acronyms ======== -+--------+--------------------------------------+ -| CoT | Chain of Trust | -+--------+--------------------------------------+ -| DMA | Direct Memory Access | -+--------+--------------------------------------+ -| DTB | Device Tree Blob | +--------+--------------------------------------+ | DTS | Device Tree Source | +--------+--------------------------------------+ -| EC | Execution Context | -+--------+--------------------------------------+ -| FIP | Firmware Image Package | -+--------+--------------------------------------+ | FF-A | Firmware Framework for Arm A-profile | +--------+--------------------------------------+ -| IPA | Intermediate Physical Address | -+--------+--------------------------------------+ -| JOP | Jump-Oriented Programming | -+--------+--------------------------------------+ | NWd | Normal World | +--------+--------------------------------------+ -| ODM | Original Design Manufacturer | -+--------+--------------------------------------+ -| OEM | Original Equipment Manufacturer | -+--------+--------------------------------------+ -| PA | Physical Address | -+--------+--------------------------------------+ -| PE | Processing Element | -+--------+--------------------------------------+ -| PM | Power Management | -+--------+--------------------------------------+ -| PVM | Primary VM | -+--------+--------------------------------------+ -| ROP | Return-Oriented Programming | -+--------+--------------------------------------+ -| SMMU | System Memory Management Unit | -+--------+--------------------------------------+ | SP | Secure Partition | +--------+--------------------------------------+ | SPD | Secure Payload Dispatcher | @@ -56,16 +26,8 @@ Acronyms +--------+--------------------------------------+ | SPMD | SPM Dispatcher | +--------+--------------------------------------+ -| SiP | Silicon Provider | -+--------+--------------------------------------+ | SWd | Secure World | +--------+--------------------------------------+ -| TLV | Tag-Length-Value | -+--------+--------------------------------------+ -| TOS | Trusted Operating System | -+--------+--------------------------------------+ -| VM | Virtual Machine | -+--------+--------------------------------------+ Foreword ======== @@ -74,34 +36,14 @@ Three implementations of a Secure Partition Manager co-exist in the TF-A codebase: #. S-EL2 SPMC based on the FF-A specification `[1]`_, enabling virtualization in - the secure world, managing multiple S-EL1 or S-EL0 partitions. + the secure world, managing multiple S-EL1 or S-EL0 partitions `[5]`_. #. EL3 SPMC based on the FF-A specification, managing a single S-EL1 partition - without virtualization in the secure world. + without virtualization in the secure world `[6]`_. #. EL3 SPM based on the MM specification, legacy implementation managing a single S-EL0 partition `[2]`_. These implementations differ in their respective SW architecture and only one -can be selected at build time. This document: - -- describes the implementation from bullet 1. when the SPMC resides at S-EL2. -- is not an architecture specification and it might provide assumptions - on sections mandated as implementation-defined in the specification. -- covers the implications to TF-A used as a bootloader, and Hafnium used as a - reference code base for an S-EL2/SPMC secure firmware on platforms - implementing the FEAT_SEL2 architecture extension. - -Terminology ------------ - -- The term Hypervisor refers to the NS-EL2 component managing Virtual Machines - (or partitions) in the normal world. -- The term SPMC refers to the S-EL2 component managing secure partitions in - the secure world when the FEAT_SEL2 architecture extension is implemented. -- Alternatively, SPMC can refer to an S-EL1 component, itself being a secure - partition and implementing the FF-A ABI on platforms not implementing the - FEAT_SEL2 architecture extension. -- The term VM refers to a normal world Virtual Machine managed by an Hypervisor. -- The term SP refers to a secure world "Virtual Machine" managed by an SPMC. +can be selected at build time. Support for legacy platforms ---------------------------- @@ -123,16 +65,6 @@ TF-A supports both cases: - S-EL2 SPMC for platforms implementing the FEAT_SEL2 architecture extension. The SPMD relays the FF-A protocol from EL3 to S-EL2. -Sample reference stack -====================== - -The following diagram illustrates a possible configuration when the -FEAT_SEL2 architecture extension is implemented, showing the SPMD -and SPMC, one or multiple secure partitions, with an optional -Hypervisor: - -.. image:: ../resources/diagrams/ff-a-spm-sel2.png - TF-A build options ================== @@ -147,16 +79,15 @@ SPMC located at S-EL1, S-EL2 or EL3: level to being at S-EL2. It defaults to enabled (value 1) when SPD=spmd is chosen. - **SPMC_AT_EL3**: this option adjusts the SPMC exception level to being - at EL3. -- If neither ``SPMD_SPM_AT_SEL2`` or ``SPMC_AT_EL3`` are enabled the SPMC - exception level is set to S-EL1. + at EL3. If neither ``SPMD_SPM_AT_SEL2`` or ``SPMC_AT_EL3`` are enabled the + SPMC exception level is set to S-EL1. ``SPMD_SPM_AT_SEL2`` is enabled. The context save/restore routine and exhaustive list of registers is visible at `[4]`_. - **SPMC_AT_EL3_SEL0_SP**: this option enables the support to load SEL0 SP when SPMC at EL3 support is enabled. - **SP_LAYOUT_FILE**: this option specifies a text description file providing paths to SP binary images and manifests in DTS format - (see `Describing secure partitions`_). It + (see `[3]`_). It is required when ``SPMD_SPM_AT_SEL2`` is enabled hence when multiple secure partitions are to be loaded by BL2 on behalf of the SPMC. @@ -179,7 +110,7 @@ Notes: - Only Arm's FVP platform is supported to use with the TF-A reference software stack. - When ``SPMD_SPM_AT_SEL2=1``, the reference software stack assumes enablement - of FEAT_PAuth, FEAT_BTI and FEAT_MTE architecture extensions. + of FEAT_PAuth, FEAT_BTI and FEAT_MTE2 architecture extensions. - ``(*) CTX_INCLUDE_EL2_REGS``, this flag is |TF-A| internal and informational in this table. When set, it provides the generic support for saving/restoring EL2 registers required when S-EL2 firmware is present. @@ -215,7 +146,7 @@ implemented and the SPMC is located at S-EL2: ARM_ARCH_MINOR=5 \ BRANCH_PROTECTION=1 \ CTX_INCLUDE_PAUTH_REGS=1 \ - CTX_INCLUDE_MTE_REGS=1 \ + ENABLE_FEAT_MTE2=1 \ BL32=<path-to-hafnium-binary> \ BL33=<path-to-bl33-binary> \ SP_LAYOUT_FILE=sp_layout.json \ @@ -233,7 +164,7 @@ implemented, the SPMC is located at S-EL2, and enabling secure boot: ARM_ARCH_MINOR=5 \ BRANCH_PROTECTION=1 \ CTX_INCLUDE_PAUTH_REGS=1 \ - CTX_INCLUDE_MTE_REGS=1 \ + ENABLE_FEAT_MTE2=1 \ BL32=<path-to-hafnium-binary> \ BL33=<path-to-bl33-binary> \ SP_LAYOUT_FILE=sp_layout.json \ @@ -275,1358 +206,28 @@ enabled: PLAT=fvp \ all fip -FVP model invocation -==================== - -The FVP command line needs the following options to exercise the S-EL2 SPMC: - -+---------------------------------------------------+------------------------------------+ -| - cluster0.has_arm_v8-5=1 | Implements FEAT_SEL2, FEAT_PAuth, | -| - cluster1.has_arm_v8-5=1 | and FEAT_BTI. | -+---------------------------------------------------+------------------------------------+ -| - pci.pci_smmuv3.mmu.SMMU_AIDR=2 | Parameters required for the | -| - pci.pci_smmuv3.mmu.SMMU_IDR0=0x0046123B | SMMUv3.2 modeling. | -| - pci.pci_smmuv3.mmu.SMMU_IDR1=0x00600002 | | -| - pci.pci_smmuv3.mmu.SMMU_IDR3=0x1714 | | -| - pci.pci_smmuv3.mmu.SMMU_IDR5=0xFFFF0472 | | -| - pci.pci_smmuv3.mmu.SMMU_S_IDR1=0xA0000002 | | -| - pci.pci_smmuv3.mmu.SMMU_S_IDR2=0 | | -| - pci.pci_smmuv3.mmu.SMMU_S_IDR3=0 | | -+---------------------------------------------------+------------------------------------+ -| - cluster0.has_branch_target_exception=1 | Implements FEAT_BTI. | -| - cluster1.has_branch_target_exception=1 | | -+---------------------------------------------------+------------------------------------+ -| - cluster0.has_pointer_authentication=2 | Implements FEAT_PAuth | -| - cluster1.has_pointer_authentication=2 | | -+---------------------------------------------------+------------------------------------+ -| - cluster0.memory_tagging_support_level=2 | Implements FEAT_MTE2 | -| - cluster1.memory_tagging_support_level=2 | | -| - bp.dram_metadata.is_enabled=1 | | -+---------------------------------------------------+------------------------------------+ - -Sample FVP command line invocation: - -.. code:: shell - - <path-to-fvp-model>/FVP_Base_RevC-2xAEMvA -C pctl.startup=0.0.0.0 \ - -C cluster0.NUM_CORES=4 -C cluster1.NUM_CORES=4 -C bp.secure_memory=1 \ - -C bp.secureflashloader.fname=trusted-firmware-a/build/fvp/debug/bl1.bin \ - -C bp.flashloader0.fname=trusted-firmware-a/build/fvp/debug/fip.bin \ - -C bp.pl011_uart0.out_file=fvp-uart0.log -C bp.pl011_uart1.out_file=fvp-uart1.log \ - -C bp.pl011_uart2.out_file=fvp-uart2.log \ - -C cluster0.has_arm_v8-5=1 -C cluster1.has_arm_v8-5=1 \ - -C cluster0.has_pointer_authentication=2 -C cluster1.has_pointer_authentication=2 \ - -C cluster0.has_branch_target_exception=1 -C cluster1.has_branch_target_exception=1 \ - -C cluster0.memory_tagging_support_level=2 -C cluster1.memory_tagging_support_level=2 \ - -C bp.dram_metadata.is_enabled=1 \ - -C pci.pci_smmuv3.mmu.SMMU_AIDR=2 -C pci.pci_smmuv3.mmu.SMMU_IDR0=0x0046123B \ - -C pci.pci_smmuv3.mmu.SMMU_IDR1=0x00600002 -C pci.pci_smmuv3.mmu.SMMU_IDR3=0x1714 \ - -C pci.pci_smmuv3.mmu.SMMU_IDR5=0xFFFF0472 -C pci.pci_smmuv3.mmu.SMMU_S_IDR1=0xA0000002 \ - -C pci.pci_smmuv3.mmu.SMMU_S_IDR2=0 -C pci.pci_smmuv3.mmu.SMMU_S_IDR3=0 - Boot process ============ -Loading Hafnium and secure partitions in the secure world ---------------------------------------------------------- - -TF-A BL2 is the bootlader for the SPMC and SPs in the secure world. - -SPs may be signed by different parties (SiP, OEM/ODM, TOS vendor, etc.). -Thus they are supplied as distinct signed entities within the FIP flash -image. The FIP image itself is not signed hence this provides the ability -to upgrade SPs in the field. - -Booting through TF-A --------------------- - -SP manifests -~~~~~~~~~~~~ - -An SP manifest describes SP attributes as defined in `[1]`_ -(partition manifest at virtual FF-A instance) in DTS format. It is -represented as a single file associated with the SP. A sample is -provided by `[5]`_. A binding document is provided by `[6]`_. - -Secure Partition packages -~~~~~~~~~~~~~~~~~~~~~~~~~ - -Secure partitions are bundled as independent package files consisting -of: - -- a header -- a DTB -- an image payload - -The header starts with a magic value and offset values to SP DTB and -image payload. Each SP package is loaded independently by BL2 loader -and verified for authenticity and integrity. - -The SP package identified by its UUID (matching FF-A uuid property) is -inserted as a single entry into the FIP at end of the TF-A build flow -as shown: - -.. code:: shell - - Trusted Boot Firmware BL2: offset=0x1F0, size=0x8AE1, cmdline="--tb-fw" - EL3 Runtime Firmware BL31: offset=0x8CD1, size=0x13000, cmdline="--soc-fw" - Secure Payload BL32 (Trusted OS): offset=0x1BCD1, size=0x15270, cmdline="--tos-fw" - Non-Trusted Firmware BL33: offset=0x30F41, size=0x92E0, cmdline="--nt-fw" - HW_CONFIG: offset=0x3A221, size=0x2348, cmdline="--hw-config" - TB_FW_CONFIG: offset=0x3C569, size=0x37A, cmdline="--tb-fw-config" - SOC_FW_CONFIG: offset=0x3C8E3, size=0x48, cmdline="--soc-fw-config" - TOS_FW_CONFIG: offset=0x3C92B, size=0x427, cmdline="--tos-fw-config" - NT_FW_CONFIG: offset=0x3CD52, size=0x48, cmdline="--nt-fw-config" - B4B5671E-4A90-4FE1-B81F-FB13DAE1DACB: offset=0x3CD9A, size=0xC168, cmdline="--blob" - D1582309-F023-47B9-827C-4464F5578FC8: offset=0x48F02, size=0xC168, cmdline="--blob" - -.. uml:: ../resources/diagrams/plantuml/fip-secure-partitions.puml - -Describing secure partitions -~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -A json-formatted description file is passed to the build flow specifying paths -to the SP binary image and associated DTS partition manifest file. The latter -is processed by the dtc compiler to generate a DTB fed into the SP package. -Optionally, the partition's json description can contain offsets for both -the image and partition manifest within the SP package. Both offsets need to be -4KB aligned, because it is the translation granule supported by Hafnium SPMC. -These fields can be leveraged to support SPs with S1 translation granules that -differ from 4KB, and to configure the regions allocated within the SP package, -as well as to comply with the requirements for the implementation of the boot -information protocol (see `Passing boot data to the SP`_ for more details). In -case the offsets are absent in their json node, they default to 0x1000 and -0x4000 for the manifest offset and image offset respectively. -This file also specifies the SP owner (as an optional field) identifying the -signing domain in case of dual root CoT. -The SP owner can either be the silicon or the platform provider. The -corresponding "owner" field value can either take the value of "SiP" or "Plat". -In absence of "owner" field, it defaults to "SiP" owner. -The UUID of the partition can be specified as a field in the description file or -if it does not exist there the UUID is extracted from the DTS partition -manifest. - -.. code:: shell - - { - "tee1" : { - "image": "tee1.bin", - "pm": "tee1.dts", - "owner": "SiP", - "uuid": "1b1820fe-48f7-4175-8999-d51da00b7c9f" - }, - - "tee2" : { - "image": "tee2.bin", - "pm": "tee2.dts", - "owner": "Plat" - }, - - "tee3" : { - "image": { - "file": "tee3.bin", - "offset":"0x2000" - }, - "pm": { - "file": "tee3.dts", - "offset":"0x6000" - }, - "owner": "Plat" - }, - } - -SPMC manifest -~~~~~~~~~~~~~ - -This manifest contains the SPMC *attribute* node consumed by the SPMD at boot -time. It implements `[1]`_ (SP manifest at physical FF-A instance) and serves -two different cases: - -- The SPMC resides at S-EL1: the SPMC manifest is used by the SPMD to setup a - SP that co-resides with the SPMC and executes at S-EL1 or Secure Supervisor - mode. -- The SPMC resides at S-EL2: the SPMC manifest is used by the SPMD to setup - the environment required by the SPMC to run at S-EL2. SPs run at S-EL1 or - S-EL0. - -.. code:: shell - - attribute { - spmc_id = <0x8000>; - maj_ver = <0x1>; - min_ver = <0x1>; - exec_state = <0x0>; - load_address = <0x0 0x6000000>; - entrypoint = <0x0 0x6000000>; - binary_size = <0x60000>; - }; - -- *spmc_id* defines the endpoint ID value that SPMC can query through - ``FFA_ID_GET``. -- *maj_ver/min_ver*. SPMD checks provided version versus its internal - version and aborts if not matching. -- *exec_state* defines the SPMC execution state (AArch64 or AArch32). - Notice Hafnium used as a SPMC only supports AArch64. -- *load_address* and *binary_size* are mostly used to verify secondary - entry points fit into the loaded binary image. -- *entrypoint* defines the cold boot primary core entry point used by - SPMD (currently matches ``BL32_BASE``) to enter the SPMC. - -Other nodes in the manifest are consumed by Hafnium in the secure world. -A sample can be found at `[7]`_: - -- The *hypervisor* node describes SPs. *is_ffa_partition* boolean attribute - indicates a FF-A compliant SP. The *load_address* field specifies the load - address at which BL2 loaded the SP package. -- *cpus* node provide the platform topology and allows MPIDR to VMPIDR mapping. - Note the primary core is declared first, then secondary cores are declared - in reverse order. -- The *memory* nodes provide platform information on the ranges of memory - available for use by SPs at runtime. These ranges relate to either - secure or non-secure memory, depending on the *device_type* field. - If the field specifies "memory" the range is secure, else if it specifies - "ns-memory" the memory is non-secure. The system integrator must exclude - the memory used by other components that are not SPs, such as the monitor, - or the SPMC itself, the OS Kernel/Hypervisor, or other NWd VMs. The SPMC - limits the SP's address space such that they do not access memory outside - of those ranges. +The boot process involving SPMC is highly dependent on the SPMC implementation. +It is recommended to refer to corresponding SPMC documentation for further +details. Some aspects of boot process are described here in the greater interest +of the project. SPMC boot -~~~~~~~~~ +--------- -The SPMC is loaded by BL2 as the BL32 image. +When SPMC resides at a lower EL i.e., S-EL1 or S-EL2, it is loaded by BL2 as the +BL32 image. The SPMC manifest is loaded by BL2 as the ``TOS_FW_CONFIG`` image `[7]`_. -The SPMC manifest is loaded by BL2 as the ``TOS_FW_CONFIG`` image `[9]`_. - -BL2 passes the SPMC manifest address to BL31 through a register. - -At boot time, the SPMD in BL31 runs from the primary core, initializes the core -contexts and launches the SPMC (BL32) passing the following information through -registers: +BL2 passes the SPMC manifest address to BL31 through a register. At boot time, +the SPMD in BL31 runs from the primary core, initializes the core contexts and +launches the SPMC (BL32) passing the following information through registers: - X0 holds the ``TOS_FW_CONFIG`` physical address (or SPMC manifest blob). - X1 holds the ``HW_CONFIG`` physical address. - X4 holds the currently running core linear id. -Loading of SPs -~~~~~~~~~~~~~~ - -At boot time, BL2 loads SPs sequentially in addition to the SPMC as depicted -below: - -.. uml:: ../resources/diagrams/plantuml/bl2-loading-sp.puml - -Note this boot flow is an implementation sample on Arm's FVP platform. -Platforms not using TF-A's *Firmware CONFiguration* framework would adjust to a -different boot flow. The flow restricts to a maximum of 8 secure partitions. - -Secure boot -~~~~~~~~~~~ - -The SP content certificate is inserted as a separate FIP item so that BL2 loads SPMC, -SPMC manifest, secure partitions and verifies them for authenticity and integrity. -Refer to TBBR specification `[3]`_. - -The multiple-signing domain feature (in current state dual signing domain `[8]`_) allows -the use of two root keys namely S-ROTPK and NS-ROTPK: - -- SPMC (BL32) and SPMC manifest are signed by the SiP using the S-ROTPK. -- BL33 may be signed by the OEM using NS-ROTPK. -- An SP may be signed either by SiP (using S-ROTPK) or by OEM (using NS-ROTPK). -- A maximum of 4 partitions can be signed with the S-ROTPK key and 4 partitions - signed with the NS-ROTPK key. - -Also refer to `Describing secure partitions`_ and `TF-A build options`_ sections. - -Hafnium in the secure world -=========================== - -General considerations ----------------------- - -Build platform for the secure world -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -In the Hafnium reference implementation specific code parts are only relevant to -the secure world. Such portions are isolated in architecture specific files -and/or enclosed by a ``SECURE_WORLD`` macro. - -Secure partitions scheduling -~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -The FF-A specification `[1]`_ provides two ways to relinquinsh CPU time to -secure partitions. For this a VM (Hypervisor or OS kernel), or SP invokes one of: - -- the FFA_MSG_SEND_DIRECT_REQ interface. -- the FFA_RUN interface. - -Additionally a secure interrupt can pre-empt the normal world execution and give -CPU cycles by transitioning to EL3 and S-EL2. - -Platform topology -~~~~~~~~~~~~~~~~~ - -The *execution-ctx-count* SP manifest field can take the value of one or the -total number of PEs. The FF-A specification `[1]`_ recommends the -following SP types: - -- Pinned MP SPs: an execution context matches a physical PE. MP SPs must - implement the same number of ECs as the number of PEs in the platform. -- Migratable UP SPs: a single execution context can run and be migrated on any - physical PE. Such SP declares a single EC in its SP manifest. An UP SP can - receive a direct message request originating from any physical core targeting - the single execution context. - -Parsing SP partition manifests ------------------------------- - -Hafnium consumes SP manifests as defined in `[1]`_ and `SP manifests`_. -Note the current implementation may not implement all optional fields. - -The SP manifest may contain memory and device regions nodes. In case of -an S-EL2 SPMC: - -- Memory regions are mapped in the SP EL1&0 Stage-2 translation regime at - load time (or EL1&0 Stage-1 for an S-EL1 SPMC). A memory region node can - specify RX/TX buffer regions in which case it is not necessary for an SP - to explicitly invoke the ``FFA_RXTX_MAP`` interface. The memory referred - shall be contained within the memory ranges defined in SPMC manifest. The - NS bit in the attributes field should be consistent with the security - state of the range that it relates to. I.e. non-secure memory shall be - part of a non-secure memory range, and secure memory shall be contained - in a secure memory range of a given platform. -- Device regions are mapped in the SP EL1&0 Stage-2 translation regime (or - EL1&0 Stage-1 for an S-EL1 SPMC) as peripherals and possibly allocate - additional resources (e.g. interrupts). - -For the S-EL2 SPMC, base addresses for memory and device region nodes are IPAs -provided the SPMC identity maps IPAs to PAs within SP EL1&0 Stage-2 translation -regime. - -Note: in the current implementation both VTTBR_EL2 and VSTTBR_EL2 point to the -same set of page tables. It is still open whether two sets of page tables shall -be provided per SP. The memory region node as defined in the specification -provides a memory security attribute hinting to map either to the secure or -non-secure EL1&0 Stage-2 table if it exists. - -Passing boot data to the SP ---------------------------- - -In `[1]`_ , the section "Boot information protocol" defines a method for passing -data to the SPs at boot time. It specifies the format for the boot information -descriptor and boot information header structures, which describe the data to be -exchanged between SPMC and SP. -The specification also defines the types of data that can be passed. -The aggregate of both the boot info structures and the data itself is designated -the boot information blob, and is passed to a Partition as a contiguous memory -region. - -Currently, the SPM implementation supports the FDT type which is used to pass the -partition's DTB manifest. - -The region for the boot information blob is allocated through the SP package. - -.. image:: ../resources/diagrams/partition-package.png - -To adjust the space allocated for the boot information blob, the json description -of the SP (see section `Describing secure partitions`_) shall be updated to contain -the manifest offset. If no offset is provided the manifest offset defaults to 0x1000, -which is the page size in the Hafnium SPMC. - -The configuration of the boot protocol is done in the SPs manifest. As defined by -the specification, the manifest field 'gp-register-num' configures the GP register -which shall be used to pass the address to the partitions boot information blob when -booting the partition. -In addition, the Hafnium SPMC implementation requires the boot information arguments -to be listed in a designated DT node: - -.. code:: shell - - boot-info { - compatible = "arm,ffa-manifest-boot-info"; - ffa_manifest; - }; - -The whole secure partition package image (see `Secure Partition packages`_) is -mapped to the SP secure EL1&0 Stage-2 translation regime. As such, the SP can -retrieve the address for the boot information blob in the designated GP register, -process the boot information header and descriptors, access its own manifest -DTB blob and extract its partition manifest properties. - -SP Boot order -------------- - -SP manifests provide an optional boot order attribute meant to resolve -dependencies such as an SP providing a service required to properly boot -another SP. SPMC boots the SPs in accordance to the boot order attribute, -lowest to the highest value. If the boot order attribute is absent from the FF-A -manifest, the SP is treated as if it had the highest boot order value -(i.e. lowest booting priority). - -It is possible for an SP to call into another SP through a direct request -provided the latter SP has already been booted. - -Boot phases ------------ - -Primary core boot-up -~~~~~~~~~~~~~~~~~~~~ - -Upon boot-up, BL31 hands over to the SPMC (BL32) on the primary boot physical -core. The SPMC performs its platform initializations and registers the SPMC -secondary physical core entry point physical address by the use of the -`FFA_SECONDARY_EP_REGISTER`_ interface (SMC invocation from the SPMC to the SPMD -at secure physical FF-A instance). - -The SPMC then creates secure partitions based on SP packages and manifests. Each -secure partition is launched in sequence (`SP Boot order`_) on their "primary" -execution context. If the primary boot physical core linear id is N, an MP SP is -started using EC[N] on PE[N] (see `Platform topology`_). If the partition is a -UP SP, it is started using its unique EC0 on PE[N]. - -The SP primary EC (or the EC used when the partition is booted as described -above): - -- Performs the overall SP boot time initialization, and in case of a MP SP, - prepares the SP environment for other execution contexts. -- In the case of a MP SP, it invokes the FFA_SECONDARY_EP_REGISTER at secure - virtual FF-A instance (SMC invocation from SP to SPMC) to provide the IPA - entry point for other execution contexts. -- Exits through ``FFA_MSG_WAIT`` to indicate successful initialization or - ``FFA_ERROR`` in case of failure. - -Secondary cores boot-up -~~~~~~~~~~~~~~~~~~~~~~~ - -Once the system is started and NWd brought up, a secondary physical core is -woken up by the ``PSCI_CPU_ON`` service invocation. The TF-A SPD hook mechanism -calls into the SPMD on the newly woken up physical core. Then the SPMC is -entered at the secondary physical core entry point. - -In the current implementation, the first SP is resumed on the coresponding EC -(the virtual CPU which matches the physical core). The implication is that the -first SP must be a MP SP. - -In a linux based system, once secure and normal worlds are booted but prior to -a NWd FF-A driver has been loaded: - -- The first SP has initialized all its ECs in response to primary core boot up - (at system initialization) and secondary core boot up (as a result of linux - invoking PSCI_CPU_ON for all secondary cores). -- Other SPs have their first execution context initialized as a result of secure - world initialization on the primary boot core. Other ECs for those SPs have to - be run first through ffa_run to complete their initialization (which results - in the EC completing with FFA_MSG_WAIT). - -Refer to `Power management`_ for further details. - -Notifications -------------- - -The FF-A v1.1 specification `[1]`_ defines notifications as an asynchronous -communication mechanism with non-blocking semantics. It allows for one FF-A -endpoint to signal another for service provision, without hindering its current -progress. - -Hafnium currently supports 64 notifications. The IDs of each notification define -a position in a 64-bit bitmap. - -The signaling of notifications can interchangeably happen between NWd and SWd -FF-A endpoints. - -The SPMC is in charge of managing notifications from SPs to SPs, from SPs to -VMs, and from VMs to SPs. An hypervisor component would only manage -notifications from VMs to VMs. Given the SPMC has no visibility of the endpoints -deployed in NWd, the Hypervisor or OS kernel must invoke the interface -FFA_NOTIFICATION_BITMAP_CREATE to allocate the notifications bitmap per FF-A -endpoint in the NWd that supports it. - -A sender can signal notifications once the receiver has provided it with -permissions. Permissions are provided by invoking the interface -FFA_NOTIFICATION_BIND. - -Notifications are signaled by invoking FFA_NOTIFICATION_SET. Henceforth -they are considered to be in a pending sate. The receiver can retrieve its -pending notifications invoking FFA_NOTIFICATION_GET, which, from that moment, -are considered to be handled. - -Per the FF-A v1.1 spec, each FF-A endpoint must be associated with a scheduler -that is in charge of donating CPU cycles for notifications handling. The -FF-A driver calls FFA_NOTIFICATION_INFO_GET to retrieve the information about -which FF-A endpoints have pending notifications. The receiver scheduler is -called and informed by the FF-A driver, and it should allocate CPU cycles to the -receiver. - -There are two types of notifications supported: - -- Global, which are targeted to a FF-A endpoint and can be handled within any of - its execution contexts, as determined by the scheduler of the system. -- Per-vCPU, which are targeted to a FF-A endpoint and to be handled within a - a specific execution context, as determined by the sender. - -The type of a notification is set when invoking FFA_NOTIFICATION_BIND to give -permissions to the sender. - -Notification signaling resorts to two interrupts: - -- Schedule Receiver Interrupt: non-secure physical interrupt to be handled by - the FF-A driver within the receiver scheduler. At initialization the SPMC - donates a SGI ID chosen from the secure SGI IDs range and configures it as - non-secure. The SPMC triggers this SGI on the currently running core when - there are pending notifications, and the respective receivers need CPU cycles - to handle them. -- Notifications Pending Interrupt: virtual interrupt to be handled by the - receiver of the notification. Set when there are pending notifications for the - given secure partition. The NPI is pended when the NWd relinquishes CPU cycles - to an SP. - -The notifications receipt support is enabled in the partition FF-A manifest. - -Mandatory interfaces --------------------- - -The following interfaces are exposed to SPs: - -- ``FFA_VERSION`` -- ``FFA_FEATURES`` -- ``FFA_RX_RELEASE`` -- ``FFA_RXTX_MAP`` -- ``FFA_RXTX_UNMAP`` -- ``FFA_PARTITION_INFO_GET`` -- ``FFA_ID_GET`` -- ``FFA_MSG_WAIT`` -- ``FFA_MSG_SEND_DIRECT_REQ`` -- ``FFA_MSG_SEND_DIRECT_RESP`` -- ``FFA_MEM_DONATE`` -- ``FFA_MEM_LEND`` -- ``FFA_MEM_SHARE`` -- ``FFA_MEM_RETRIEVE_REQ`` -- ``FFA_MEM_RETRIEVE_RESP`` -- ``FFA_MEM_RELINQUISH`` -- ``FFA_MEM_FRAG_RX`` -- ``FFA_MEM_FRAG_TX`` -- ``FFA_MEM_RECLAIM`` -- ``FFA_RUN`` - -As part of the FF-A v1.1 support, the following interfaces were added: - - - ``FFA_NOTIFICATION_BITMAP_CREATE`` - - ``FFA_NOTIFICATION_BITMAP_DESTROY`` - - ``FFA_NOTIFICATION_BIND`` - - ``FFA_NOTIFICATION_UNBIND`` - - ``FFA_NOTIFICATION_SET`` - - ``FFA_NOTIFICATION_GET`` - - ``FFA_NOTIFICATION_INFO_GET`` - - ``FFA_SPM_ID_GET`` - - ``FFA_SECONDARY_EP_REGISTER`` - - ``FFA_MEM_PERM_GET`` - - ``FFA_MEM_PERM_SET`` - - ``FFA_MSG_SEND2`` - - ``FFA_RX_ACQUIRE`` - -FFA_VERSION -~~~~~~~~~~~ - -``FFA_VERSION`` requires a *requested_version* parameter from the caller. -The returned value depends on the caller: - -- Hypervisor or OS kernel in NS-EL1/EL2: the SPMD returns the SPMC version - specified in the SPMC manifest. -- SP: the SPMC returns its own implemented version. -- SPMC at S-EL1/S-EL2: the SPMD returns its own implemented version. - -FFA_FEATURES -~~~~~~~~~~~~ - -FF-A features supported by the SPMC may be discovered by secure partitions at -boot (that is prior to NWd is booted) or run-time. - -The SPMC calling FFA_FEATURES at secure physical FF-A instance always get -FFA_SUCCESS from the SPMD. - -The request made by an Hypervisor or OS kernel is forwarded to the SPMC and -the response relayed back to the NWd. - -FFA_RXTX_MAP/FFA_RXTX_UNMAP -~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -When invoked from a secure partition FFA_RXTX_MAP maps the provided send and -receive buffers described by their IPAs to the SP EL1&0 Stage-2 translation -regime as secure buffers in the MMU descriptors. - -When invoked from the Hypervisor or OS kernel, the buffers are mapped into the -SPMC EL2 Stage-1 translation regime and marked as NS buffers in the MMU -descriptors. The provided addresses may be owned by a VM in the normal world, -which is expected to receive messages from the secure world. The SPMC will in -this case allocate internal state structures to facilitate RX buffer access -synchronization (through FFA_RX_ACQUIRE interface), and to permit SPs to send -messages. - -The FFA_RXTX_UNMAP unmaps the RX/TX pair from the translation regime of the -caller, either it being the Hypervisor or OS kernel, as well as a secure -partition. - -FFA_PARTITION_INFO_GET -~~~~~~~~~~~~~~~~~~~~~~ - -Partition info get call can originate: - -- from SP to SPMC -- from Hypervisor or OS kernel to SPMC. The request is relayed by the SPMD. - -FFA_ID_GET -~~~~~~~~~~ - -The FF-A id space is split into a non-secure space and secure space: - -- FF-A ID with bit 15 clear relates to VMs. -- FF-A ID with bit 15 set related to SPs. -- FF-A IDs 0, 0xffff, 0x8000 are assigned respectively to the Hypervisor, SPMD - and SPMC. - -The SPMD returns: - -- The default zero value on invocation from the Hypervisor. -- The ``spmc_id`` value specified in the SPMC manifest on invocation from - the SPMC (see `SPMC manifest`_) - -This convention helps the SPMC to determine the origin and destination worlds in -an FF-A ABI invocation. In particular the SPMC shall filter unauthorized -transactions in its world switch routine. It must not be permitted for a VM to -use a secure FF-A ID as origin world by spoofing: - -- A VM-to-SP direct request/response shall set the origin world to be non-secure - (FF-A ID bit 15 clear) and destination world to be secure (FF-A ID bit 15 - set). -- Similarly, an SP-to-SP direct request/response shall set the FF-A ID bit 15 - for both origin and destination IDs. - -An incoming direct message request arriving at SPMD from NWd is forwarded to -SPMC without a specific check. The SPMC is resumed through eret and "knows" the -message is coming from normal world in this specific code path. Thus the origin -endpoint ID must be checked by SPMC for being a normal world ID. - -An SP sending a direct message request must have bit 15 set in its origin -endpoint ID and this can be checked by the SPMC when the SP invokes the ABI. - -The SPMC shall reject the direct message if the claimed world in origin endpoint -ID is not consistent: - -- It is either forwarded by SPMD and thus origin endpoint ID must be a "normal - world ID", -- or initiated by an SP and thus origin endpoint ID must be a "secure world ID". - - -FFA_MSG_SEND_DIRECT_REQ/FFA_MSG_SEND_DIRECT_RESP -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -This is a mandatory interface for secure partitions consisting in direct request -and responses with the following rules: - -- An SP can send a direct request to another SP. -- An SP can receive a direct request from another SP. -- An SP can send a direct response to another SP. -- An SP cannot send a direct request to an Hypervisor or OS kernel. -- An Hypervisor or OS kernel can send a direct request to an SP. -- An SP can send a direct response to an Hypervisor or OS kernel. - -FFA_NOTIFICATION_BITMAP_CREATE/FFA_NOTIFICATION_BITMAP_DESTROY -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -The secure partitions notifications bitmap are statically allocated by the SPMC. -Hence, this interface is not to be issued by secure partitions. - -At initialization, the SPMC is not aware of VMs/partitions deployed in the -normal world. Hence, the Hypervisor or OS kernel must use both ABIs for SPMC -to be prepared to handle notifications for the provided VM ID. - -FFA_NOTIFICATION_BIND/FFA_NOTIFICATION_UNBIND -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Pair of interfaces to manage permissions to signal notifications. Prior to -handling notifications, an FF-A endpoint must allow a given sender to signal a -bitmap of notifications. - -If the receiver doesn't have notification support enabled in its FF-A manifest, -it won't be able to bind notifications, hence forbidding it to receive any -notifications. - -FFA_NOTIFICATION_SET/FFA_NOTIFICATION_GET -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -FFA_NOTIFICATION_GET retrieves all pending global notifications and -per-vCPU notifications targeted to the current vCPU. - -Hafnium maintains a global count of pending notifications which gets incremented -and decremented when handling FFA_NOTIFICATION_SET and FFA_NOTIFICATION_GET -respectively. A delayed SRI is triggered if the counter is non-zero when the -SPMC returns to normal world. - -FFA_NOTIFICATION_INFO_GET -~~~~~~~~~~~~~~~~~~~~~~~~~ - -Hafnium maintains a global count of pending notifications whose information -has been retrieved by this interface. The count is incremented and decremented -when handling FFA_NOTIFICATION_INFO_GET and FFA_NOTIFICATION_GET respectively. -It also tracks notifications whose information has been retrieved individually, -such that it avoids duplicating returned information for subsequent calls to -FFA_NOTIFICATION_INFO_GET. For each notification, this state information is -reset when receiver called FFA_NOTIFICATION_GET to retrieve them. - -FFA_SPM_ID_GET -~~~~~~~~~~~~~~ - -Returns the FF-A ID allocated to an SPM component which can be one of SPMD -or SPMC. - -At initialization, the SPMC queries the SPMD for the SPMC ID, using the -FFA_ID_GET interface, and records it. The SPMC can also query the SPMD ID using -the FFA_SPM_ID_GET interface at the secure physical FF-A instance. - -Secure partitions call this interface at the virtual FF-A instance, to which -the SPMC returns the priorly retrieved SPMC ID. - -The Hypervisor or OS kernel can issue the FFA_SPM_ID_GET call handled by the -SPMD, which returns the SPMC ID. - -FFA_SECONDARY_EP_REGISTER -~~~~~~~~~~~~~~~~~~~~~~~~~ - -When the SPMC boots, all secure partitions are initialized on their primary -Execution Context. - -The FFA_SECONDARY_EP_REGISTER interface is to be used by a secure partition -from its first execution context, to provide the entry point address for -secondary execution contexts. - -A secondary EC is first resumed either upon invocation of PSCI_CPU_ON from -the NWd or by invocation of FFA_RUN. - -FFA_RX_ACQUIRE/FFA_RX_RELEASE -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -The RX buffers can be used to pass information to an FF-A endpoint in the -following scenarios: - - - When it was targetted by a FFA_MSG_SEND2 invokation from another endpoint. - - Return the result of calling ``FFA_PARTITION_INFO_GET``. - - In a memory share operation, as part of the ``FFA_MEM_RETRIEVE_RESP``, - with the memory descriptor of the shared memory. - -If a normal world VM is expected to exchange messages with secure world, -its RX/TX buffer addresses are forwarded to the SPMC via FFA_RXTX_MAP ABI, -and are from this moment owned by the SPMC. -The hypervisor must call the FFA_RX_ACQUIRE interface before attempting -to use the RX buffer, in any of the aforementioned scenarios. A successful -call to FFA_RX_ACQUIRE transfers ownership of RX buffer to hypervisor, such -that it can be safely used. - -The FFA_RX_RELEASE interface is used after the FF-A endpoint is done with -processing the data received in its RX buffer. If the RX buffer has been -acquired by the hypervisor, the FFA_RX_RELEASE call must be forwarded to -the SPMC to reestablish SPMC's RX ownership. - -An attempt from an SP to send a message to a normal world VM whose RX buffer -was acquired by the hypervisor fails with error code FFA_BUSY, to preserve -the RX buffer integrity. -The operation could then be conducted after FFA_RX_RELEASE. - -FFA_MSG_SEND2 -~~~~~~~~~~~~~ - -Hafnium copies a message from the sender TX buffer into receiver's RX buffer. -For messages from SPs to VMs, operation is only possible if the SPMC owns -the receiver's RX buffer. - -Both receiver and sender need to enable support for indirect messaging, -in their respective partition manifest. The discovery of support -of such feature can be done via FFA_PARTITION_INFO_GET. - -On a successful message send, Hafnium pends an RX buffer full framework -notification for the receiver, to inform it about a message in the RX buffer. - -The handling of framework notifications is similar to that of -global notifications. Binding of these is not necessary, as these are -reserved to be used by the hypervisor or SPMC. - -SPMC-SPMD direct requests/responses ------------------------------------ - -Implementation-defined FF-A IDs are allocated to the SPMC and SPMD. -Using those IDs in source/destination fields of a direct request/response -permits SPMD to SPMC communication and either way. - -- SPMC to SPMD direct request/response uses SMC conduit. -- SPMD to SPMC direct request/response uses ERET conduit. - -This is used in particular to convey power management messages. - -Memory Sharing --------------- - -Hafnium implements the following memory sharing interfaces: - - - ``FFA_MEM_SHARE`` - for shared access between lender and borrower. - - ``FFA_MEM_LEND`` - borrower to obtain exclusive access, though lender - retains ownership of the memory. - - ``FFA_MEM_DONATE`` - lender permanently relinquishes ownership of memory - to the borrower. - -The ``FFA_MEM_RETRIEVE_REQ`` interface is for the borrower to request the -memory to be mapped into its address space: for S-EL1 partitions the SPM updates -their stage 2 translation regime; for S-EL0 partitions the SPM updates their -stage 1 translation regime. On a successful call, the SPMC responds back with -``FFA_MEM_RETRIEVE_RESP``. - -The ``FFA_MEM_RELINQUISH`` interface is for when the borrower is done with using -a memory region. - -The ``FFA_MEM_RECLAIM`` interface is for the owner of the memory to reestablish -its ownership and exclusive access to the memory shared. - -The memory transaction descriptors are transmitted via RX/TX buffers. In -situations where the size of the memory transaction descriptor exceeds the -size of the RX/TX buffers, Hafnium provides support for fragmented transmission -of the full transaction descriptor. The ``FFA_MEM_FRAG_RX`` and ``FFA_MEM_FRAG_TX`` -interfaces are for receiving and transmitting the next fragment, respectively. - -If lender and borrower(s) are SPs, all memory sharing operations are supported. - -Hafnium also supports memory sharing operations between the normal world and the -secure world. If there is an SP involved, the SPMC allocates data to track the -state of the operation. - -The SPMC is also the designated allocator for the memory handle. The hypervisor -or OS kernel has the possibility to rely on the SPMC to maintain the state -of the operation, thus saving memory. -A lender SP can only donate NS memory to a borrower from the normal world. - -The SPMC supports the hypervisor retrieve request, as defined by the FF-A -v1.1 EAC0 specification, in section 16.4.3. The intent is to aid with operations -that the hypervisor must do for a VM retriever. For example, when handling -an FFA_MEM_RECLAIM, if the hypervisor relies on SPMC to keep the state -of the operation, the hypervisor retrieve request can be used to obtain -that state information, do the necessary validations, and update stage 2 -memory translation. - -Hafnium also supports memory lend and share targetting multiple borrowers. -This is the case for a lender SP to multiple SPs, and for a lender VM to -multiple endpoints (from both secure world and normal world). If there is -at least one borrower VM, the hypervisor is in charge of managing its -stage 2 translation on a successful memory retrieve. -The semantics of ``FFA_MEM_DONATE`` implies ownership transmission, -which should target only one partition. - -The memory share interfaces are backwards compatible with memory transaction -descriptors from FF-A v1.0. These get translated to FF-A v1.1 descriptors for -Hafnium's internal processing of the operation. If the FF-A version of a -borrower is v1.0, Hafnium provides FF-A v1.0 compliant memory transaction -descriptors on memory retrieve response. - -PE MMU configuration --------------------- - -With secure virtualization enabled (``HCR_EL2.VM = 1``) and for S-EL1 -partitions, two IPA spaces (secure and non-secure) are output from the -secure EL1&0 Stage-1 translation. -The EL1&0 Stage-2 translation hardware is fed by: - -- A secure IPA when the SP EL1&0 Stage-1 MMU is disabled. -- One of secure or non-secure IPA when the secure EL1&0 Stage-1 MMU is enabled. - -``VTCR_EL2`` and ``VSTCR_EL2`` provide configuration bits for controlling the -NS/S IPA translations. The following controls are set up: -``VSTCR_EL2.SW = 0`` , ``VSTCR_EL2.SA = 0``, ``VTCR_EL2.NSW = 0``, -``VTCR_EL2.NSA = 1``: - -- Stage-2 translations for the NS IPA space access the NS PA space. -- Stage-2 translation table walks for the NS IPA space are to the secure PA space. - -Secure and non-secure IPA regions (rooted to by ``VTTBR_EL2`` and ``VSTTBR_EL2``) -use the same set of Stage-2 page tables within a SP. - -The ``VTCR_EL2/VSTCR_EL2/VTTBR_EL2/VSTTBR_EL2`` virtual address space -configuration is made part of a vCPU context. - -For S-EL0 partitions with VHE enabled, a single secure EL2&0 Stage-1 translation -regime is used for both Hafnium and the partition. - -Schedule modes and SP Call chains ---------------------------------- - -An SP execution context is said to be in SPMC scheduled mode if CPU cycles are -allocated to it by SPMC. Correspondingly, an SP execution context is said to be -in Normal world scheduled mode if CPU cycles are allocated by the normal world. - -A call chain represents all SPs in a sequence of invocations of a direct message -request. When execution on a PE is in the secure state, only a single call chain -that runs in the Normal World scheduled mode can exist. FF-A v1.1 spec allows -any number of call chains to run in the SPMC scheduled mode but the Hafnium -SPMC restricts the number of call chains in SPMC scheduled mode to only one for -keeping the implementation simple. - -Partition runtime models ------------------------- - -The runtime model of an endpoint describes the transitions permitted for an -execution context between various states. These are the four partition runtime -models supported (refer to `[1]`_ section 7): - - - RTM_FFA_RUN: runtime model presented to an execution context that is - allocated CPU cycles through FFA_RUN interface. - - RTM_FFA_DIR_REQ: runtime model presented to an execution context that is - allocated CPU cycles through FFA_MSG_SEND_DIRECT_REQ interface. - - RTM_SEC_INTERRUPT: runtime model presented to an execution context that is - allocated CPU cycles by SPMC to handle a secure interrupt. - - RTM_SP_INIT: runtime model presented to an execution context that is - allocated CPU cycles by SPMC to initialize its state. - -If an endpoint execution context attempts to make an invalid transition or a -valid transition that could lead to a loop in the call chain, SPMC denies the -transition with the help of above runtime models. - -Interrupt management --------------------- - -GIC ownership -~~~~~~~~~~~~~ - -The SPMC owns the GIC configuration. Secure and non-secure interrupts are -trapped at S-EL2. The SPMC manages interrupt resources and allocates interrupt -IDs based on SP manifests. The SPMC acknowledges physical interrupts and injects -virtual interrupts by setting the use of vIRQ/vFIQ bits before resuming a SP. - -Abbreviations: - - - NS-Int: A non-secure physical interrupt. It requires a switch to the normal - world to be handled if it triggers while execution is in secure world. - - Other S-Int: A secure physical interrupt targeted to an SP different from - the one that is currently running. - - Self S-Int: A secure physical interrupt targeted to the SP that is currently - running. - -Non-secure interrupt handling -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -This section documents the actions supported in SPMC in response to a non-secure -interrupt as per the guidance provided by FF-A v1.1 EAC0 specification. -An SP specifies one of the following actions in its partition manifest: - - - Non-secure interrupt is signaled. - - Non-secure interrupt is signaled after a managed exit. - - Non-secure interrupt is queued. - -An SP execution context in a call chain could specify a less permissive action -than subsequent SP execution contexts in the same call chain. The less -permissive action takes precedence over the more permissive actions specified -by the subsequent execution contexts. Please refer to FF-A v1.1 EAC0 section -8.3.1 for further explanation. - -Secure interrupt handling -~~~~~~~~~~~~~~~~~~~~~~~~~ - -This section documents the support implemented for secure interrupt handling in -SPMC as per the guidance provided by FF-A v1.1 EAC0 specification. -The following assumptions are made about the system configuration: - - - In the current implementation, S-EL1 SPs are expected to use the para - virtualized ABIs for interrupt management rather than accessing the virtual - GIC interface. - - Unless explicitly stated otherwise, this support is applicable only for - S-EL1 SPs managed by SPMC. - - Secure interrupts are configured as G1S or G0 interrupts. - - All physical interrupts are routed to SPMC when running a secure partition - execution context. - - All endpoints with multiple execution contexts have their contexts pinned - to corresponding CPUs. Hence, a secure virtual interrupt cannot be signaled - to a target vCPU that is currently running or blocked on a different - physical CPU. - -A physical secure interrupt could trigger while CPU is executing in normal world -or secure world. -The action of SPMC for a secure interrupt depends on: the state of the target -execution context of the SP that is responsible for handling the interrupt; -whether the interrupt triggered while execution was in normal world or secure -world. - -Secure interrupt signaling mechanisms -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Signaling refers to the mechanisms used by SPMC to indicate to the SP execution -context that it has a pending virtual interrupt and to further run the SP -execution context, such that it can handle the virtual interrupt. SPMC uses -either the FFA_INTERRUPT interface with ERET conduit or vIRQ signal for signaling -to S-EL1 SPs. When normal world execution is preempted by a secure interrupt, -the SPMD uses the FFA_INTERRUPT ABI with ERET conduit to signal interrupt to SPMC -running in S-EL2. - -+-----------+---------+---------------+---------------------------------------+ -| SP State | Conduit | Interface and | Description | -| | | parameters | | -+-----------+---------+---------------+---------------------------------------+ -| WAITING | ERET, | FFA_INTERRUPT,| SPMC signals to SP the ID of pending | -| | vIRQ | Interrupt ID | interrupt. It pends vIRQ signal and | -| | | | resumes execution context of SP | -| | | | through ERET. | -+-----------+---------+---------------+---------------------------------------+ -| BLOCKED | ERET, | FFA_INTERRUPT | SPMC signals to SP that an interrupt | -| | vIRQ | | is pending. It pends vIRQ signal and | -| | | | resumes execution context of SP | -| | | | through ERET. | -+-----------+---------+---------------+---------------------------------------+ -| PREEMPTED | vIRQ | NA | SPMC pends the vIRQ signal but does | -| | | | not resume execution context of SP. | -+-----------+---------+---------------+---------------------------------------+ -| RUNNING | ERET, | NA | SPMC pends the vIRQ signal and resumes| -| | vIRQ | | execution context of SP through ERET. | -+-----------+---------+---------------+---------------------------------------+ - -Secure interrupt completion mechanisms -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -A SP signals secure interrupt handling completion to the SPMC through the -following mechanisms: - - - ``FFA_MSG_WAIT`` ABI if it was in WAITING state. - - ``FFA_RUN`` ABI if its was in BLOCKED state. - -This is a remnant of SPMC implementation based on the FF-A v1.0 specification. -In the current implementation, S-EL1 SPs use the para-virtualized HVC interface -implemented by SPMC to perform priority drop and interrupt deactivation (SPMC -configures EOImode = 0, i.e. priority drop and deactivation are done together). -The SPMC performs checks to deny the state transition upon invocation of -either FFA_MSG_WAIT or FFA_RUN interface if the SP didn't perform the -deactivation of the secure virtual interrupt. - -If the current SP execution context was preempted by a secure interrupt to be -handled by execution context of target SP, SPMC resumes current SP after signal -completion by target SP execution context. - -Actions for a secure interrupt triggered while execution is in normal world -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -+-------------------+----------+-----------------------------------------------+ -| State of target | Action | Description | -| execution context | | | -+-------------------+----------+-----------------------------------------------+ -| WAITING | Signaled | This starts a new call chain in SPMC scheduled| -| | | mode. | -+-------------------+----------+-----------------------------------------------+ -| PREEMPTED | Queued | The target execution must have been preempted | -| | | by a non-secure interrupt. SPMC queues the | -| | | secure virtual interrupt now. It is signaled | -| | | when the target execution context next enters | -| | | the RUNNING state. | -+-------------------+----------+-----------------------------------------------+ -| BLOCKED, RUNNING | NA | The target execution context is blocked or | -| | | running on a different CPU. This is not | -| | | supported by current SPMC implementation and | -| | | execution hits panic. | -+-------------------+----------+-----------------------------------------------+ - -If normal world execution was preempted by a secure interrupt, SPMC uses -FFA_NORMAL_WORLD_RESUME ABI to indicate completion of secure interrupt handling -and further returns execution to normal world. - -The following figure describes interrupt handling flow when a secure interrupt -triggers while execution is in normal world: - -.. image:: ../resources/diagrams/ffa-secure-interrupt-handling-nwd.png - -A brief description of the events: - - - 1) Secure interrupt triggers while normal world is running. - - 2) FIQ gets trapped to EL3. - - 3) SPMD signals secure interrupt to SPMC at S-EL2 using FFA_INTERRUPT ABI. - - 4) SPMC identifies target vCPU of SP and injects virtual interrupt (pends - vIRQ). - - 5) Assuming SP1 vCPU is in WAITING state, SPMC signals virtual interrupt - using FFA_INTERRUPT with interrupt id as an argument and resumes the SP1 - vCPU using ERET in SPMC scheduled mode. - - 6) Execution traps to vIRQ handler in SP1 provided that the virtual - interrupt is not masked i.e., PSTATE.I = 0 - - 7) SP1 queries for the pending virtual interrupt id using a paravirtualized - HVC call. SPMC clears the pending virtual interrupt state management - and returns the pending virtual interrupt id. - - 8) SP1 services the virtual interrupt and invokes the paravirtualized - de-activation HVC call. SPMC de-activates the physical interrupt, - clears the fields tracking the secure interrupt and resumes SP1 vCPU. - - 9) SP1 performs secure interrupt completion through FFA_MSG_WAIT ABI. - - 10) SPMC returns control to EL3 using FFA_NORMAL_WORLD_RESUME. - - 11) EL3 resumes normal world execution. - -Actions for a secure interrupt triggered while execution is in secure world -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -+-------------------+----------+------------------------------------------------+ -| State of target | Action | Description | -| execution context | | | -+-------------------+----------+------------------------------------------------+ -| WAITING | Signaled | This starts a new call chain in SPMC scheduled | -| | | mode. | -+-------------------+----------+------------------------------------------------+ -| PREEMPTED by Self | Signaled | The target execution context reenters the | -| S-Int | | RUNNING state to handle the secure virtual | -| | | interrupt. | -+-------------------+----------+------------------------------------------------+ -| PREEMPTED by | Queued | SPMC queues the secure virtual interrupt now. | -| NS-Int | | It is signaled when the target execution | -| | | context next enters the RUNNING state. | -+-------------------+----------+------------------------------------------------+ -| BLOCKED | Signaled | Both preempted and target execution contexts | -| | | must have been part of the Normal world | -| | | scheduled call chain. Refer scenario 1 of | -| | | Table 8.4 in the FF-A v1.1 EAC0 spec. | -+-------------------+----------+------------------------------------------------+ -| RUNNING | NA | The target execution context is running on a | -| | | different CPU. This scenario is not supported | -| | | by current SPMC implementation and execution | -| | | hits panic. | -+-------------------+----------+------------------------------------------------+ - -The following figure describes interrupt handling flow when a secure interrupt -triggers while execution is in secure world. We assume OS kernel sends a direct -request message to SP1. Further, SP1 sends a direct request message to SP2. SP1 -enters BLOCKED state and SPMC resumes SP2. - -.. image:: ../resources/diagrams/ffa-secure-interrupt-handling-swd.png - -A brief description of the events: - - - 1) Secure interrupt triggers while SP2 is running. - - 2) SP2 gets preempted and execution traps to SPMC as IRQ. - - 3) SPMC finds the target vCPU of secure partition responsible for handling - this secure interrupt. In this scenario, it is SP1. - - 4) SPMC pends vIRQ for SP1 and signals through FFA_INTERRUPT interface. - SPMC further resumes SP1 through ERET conduit. Note that SP1 remains in - Normal world schedule mode. - - 6) Execution traps to vIRQ handler in SP1 provided that the virtual - interrupt is not masked i.e., PSTATE.I = 0 - - 7) SP1 queries for the pending virtual interrupt id using a paravirtualized - HVC call. SPMC clears the pending virtual interrupt state management - and returns the pending virtual interrupt id. - - 8) SP1 services the virtual interrupt and invokes the paravirtualized - de-activation HVC call. SPMC de-activates the physical interrupt and - clears the fields tracking the secure interrupt and resumes SP1 vCPU. - - 9) Since SP1 direct request completed with FFA_INTERRUPT, it resumes the - direct request to SP2 by invoking FFA_RUN. - - 9) SPMC resumes the pre-empted vCPU of SP2. - -EL3 interrupt handling -~~~~~~~~~~~~~~~~~~~~~~ - -In GICv3 based systems, EL3 interrupts are configured as Group0 secure -interrupts. Execution traps to SPMC when a Group0 interrupt triggers while an -SP is running. Further, SPMC running at S-EL2 uses FFA_EL3_INTR_HANDLE ABI to -request EL3 platform firmware to handle a pending Group0 interrupt. -Similarly, SPMD registers a handler with interrupt management framework to -delegate handling of Group0 interrupt to the platform if the interrupt triggers -in normal world. - - - Platform hook - - - plat_spmd_handle_group0_interrupt - - SPMD provides platform hook to handle Group0 secure interrupts. In the - current design, SPMD expects the platform not to delegate handling to the - NWd (such as through SDEI) while processing Group0 interrupts. - -Power management ----------------- - -In platforms with or without secure virtualization: - -- The NWd owns the platform PM policy. -- The Hypervisor or OS kernel is the component initiating PSCI service calls. -- The EL3 PSCI library is in charge of the PM coordination and control - (eventually writing to platform registers). -- While coordinating PM events, the PSCI library calls backs into the Secure - Payload Dispatcher for events the latter has statically registered to. - -When using the SPMD as a Secure Payload Dispatcher: - -- A power management event is relayed through the SPD hook to the SPMC. -- In the current implementation only cpu on (svc_on_finish) and cpu off - (svc_off) hooks are registered. -- The behavior for the cpu on event is described in `Secondary cores boot-up`_. - The SPMC is entered through its secondary physical core entry point. -- The cpu off event occurs when the NWd calls PSCI_CPU_OFF. The PM event is - signaled to the SPMC through a power management framework message. - It consists in a SPMD-to-SPMC direct request/response (`SPMC-SPMD direct - requests/responses`_) conveying the event details and SPMC response. - The SPMD performs a synchronous entry into the SPMC. The SPMC is entered and - updates its internal state to reflect the physical core is being turned off. - In the current implementation no SP is resumed as a consequence. This behavior - ensures a minimal support for CPU hotplug e.g. when initiated by the NWd linux - userspace. - -Arm architecture extensions for security hardening -================================================== - -Hafnium supports the following architecture extensions for security hardening: - -- Pointer authentication (FEAT_PAuth): the extension permits detection of forged - pointers used by ROP type of attacks through the signing of the pointer - value. Hafnium is built with the compiler branch protection option to permit - generation of a pointer authentication code for return addresses (pointer - authentication for instructions). The APIA key is used while Hafnium runs. - A random key is generated at boot time and restored upon entry into Hafnium - at run-time. APIA and other keys (APIB, APDA, APDB, APGA) are saved/restored - in vCPU contexts permitting to enable pointer authentication in VMs/SPs. -- Branch Target Identification (FEAT_BTI): the extension permits detection of - unexpected indirect branches used by JOP type of attacks. Hafnium is built - with the compiler branch protection option, inserting land pads at function - prologues that are reached by indirect branch instructions (BR/BLR). - Hafnium code pages are marked as guarded in the EL2 Stage-1 MMU descriptors - such that an indirect branch must always target a landpad. A fault is - triggered otherwise. VMs/SPs can (independently) mark their code pages as - guarded in the EL1&0 Stage-1 translation regime. -- Memory Tagging Extension (FEAT_MTE): the option permits detection of out of - bound memory array accesses or re-use of an already freed memory region. - Hafnium enables the compiler option permitting to leverage MTE stack tagging - applied to core stacks. Core stacks are marked as normal tagged memory in the - EL2 Stage-1 translation regime. A synchronous data abort is generated upon tag - check failure on load/stores. A random seed is generated at boot time and - restored upon entry into Hafnium. MTE system registers are saved/restored in - vCPU contexts permitting MTE usage from VMs/SPs. - -SMMUv3 support in Hafnium -========================= - -An SMMU is analogous to an MMU in a CPU. It performs address translations for -Direct Memory Access (DMA) requests from system I/O devices. -The responsibilities of an SMMU include: - -- Translation: Incoming DMA requests are translated from bus address space to - system physical address space using translation tables compliant to - Armv8/Armv7 VMSA descriptor format. -- Protection: An I/O device can be prohibited from read, write access to a - memory region or allowed. -- Isolation: Traffic from each individial device can be independently managed. - The devices are differentiated from each other using unique translation - tables. - -The following diagram illustrates a typical SMMU IP integrated in a SoC with -several I/O devices along with Interconnect and Memory system. - -.. image:: ../resources/diagrams/MMU-600.png - -SMMU has several versions including SMMUv1, SMMUv2 and SMMUv3. Hafnium provides -support for SMMUv3 driver in both normal and secure world. A brief introduction -of SMMUv3 functionality and the corresponding software support in Hafnium is -provided here. - -SMMUv3 features ---------------- - -- SMMUv3 provides Stage1, Stage2 translation as well as nested (Stage1 + Stage2) - translation support. It can either bypass or abort incoming translations as - well. -- Traffic (memory transactions) from each upstream I/O peripheral device, - referred to as Stream, can be independently managed using a combination of - several memory based configuration structures. This allows the SMMUv3 to - support a large number of streams with each stream assigned to a unique - translation context. -- Support for Armv8.1 VMSA where the SMMU shares the translation tables with - a Processing Element. AArch32(LPAE) and AArch64 translation table format - are supported by SMMUv3. -- SMMUv3 offers non-secure stream support with secure stream support being - optional. Logically, SMMUv3 behaves as if there is an indepdendent SMMU - instance for secure and non-secure stream support. -- It also supports sub-streams to differentiate traffic from a virtualized - peripheral associated with a VM/SP. -- Additionally, SMMUv3.2 provides support for PEs implementing Armv8.4-A - extensions. Consequently, SPM depends on Secure EL2 support in SMMUv3.2 - for providing Secure Stage2 translation support to upstream peripheral - devices. - -SMMUv3 Programming Interfaces ------------------------------ - -SMMUv3 has three software interfaces that are used by the Hafnium driver to -configure the behaviour of SMMUv3 and manage the streams. - -- Memory based data strutures that provide unique translation context for - each stream. -- Memory based circular buffers for command queue and event queue. -- A large number of SMMU configuration registers that are memory mapped during - boot time by Hafnium driver. Except a few registers, all configuration - registers have independent secure and non-secure versions to configure the - behaviour of SMMUv3 for translation of secure and non-secure streams - respectively. - -Peripheral device manifest --------------------------- - -Currently, SMMUv3 driver in Hafnium only supports dependent peripheral devices. -These devices are dependent on PE endpoint to initiate and receive memory -management transactions on their behalf. The acccess to the MMIO regions of -any such device is assigned to the endpoint during boot. Moreover, SMMUv3 driver -uses the same stage 2 translations for the device as those used by partition -manager on behalf of the PE endpoint. This ensures that the peripheral device -has the same visibility of the physical address space as the endpoint. The -device node of the corresponding partition manifest (refer to `[1]`_ section 3.2 -) must specify these additional properties for each peripheral device in the -system : - -- smmu-id: This field helps to identify the SMMU instance that this device is - upstream of. -- stream-ids: List of stream IDs assigned to this device. - -.. code:: shell - - smmuv3-testengine { - base-address = <0x00000000 0x2bfe0000>; - pages-count = <32>; - attributes = <0x3>; - smmu-id = <0>; - stream-ids = <0x0 0x1>; - interrupts = <0x2 0x3>, <0x4 0x5>; - exclusive-access; - }; - -SMMUv3 driver limitations -------------------------- - -The primary design goal for the Hafnium SMMU driver is to support secure -streams. - -- Currently, the driver only supports Stage2 translations. No support for - Stage1 or nested translations. -- Supports only AArch64 translation format. -- No support for features such as PCI Express (PASIDs, ATS, PRI), MSI, RAS, - Fault handling, Performance Monitor Extensions, Event Handling, MPAM. -- No support for independent peripheral devices. - -S-EL0 Partition support -======================= -The SPMC (Hafnium) has limited capability to run S-EL0 FF-A partitions using -FEAT_VHE (mandatory with ARMv8.1 in non-secure state, and in secure world -with ARMv8.4 and FEAT_SEL2). - -S-EL0 partitions are useful for simple partitions that don't require full -Trusted OS functionality. It is also useful to reduce jitter and cycle -stealing from normal world since they are more lightweight than VMs. - -S-EL0 partitions are presented, loaded and initialized the same as S-EL1 VMs by -the SPMC. They are differentiated primarily by the 'exception-level' property -and the 'execution-ctx-count' property in the SP manifest. They are host apps -under the single EL2&0 Stage-1 translation regime controlled by the SPMC and -call into the SPMC through SVCs as opposed to HVCs and SMCs. These partitions -can use FF-A defined services (FFA_MEM_PERM_*) to update or change permissions -for memory regions. - -S-EL0 partitions are required by the FF-A specification to be UP endpoints, -capable of migrating, and the SPMC enforces this requirement. The SPMC allows -a S-EL0 partition to accept a direct message from secure world and normal world, -and generate direct responses to them. -All S-EL0 partitions must use AArch64. AArch32 S-EL0 partitions are not supported. - -Memory sharing, indirect messaging, and notifications functionality with S-EL0 -partitions is supported. - -Interrupt handling is not supported with S-EL0 partitions and is work in -progress. References ========== @@ -1641,8 +242,7 @@ References .. _[3]: -[3] `Trusted Boot Board Requirements -Client <https://developer.arm.com/documentation/den0006/d/>`__ +[3] https://hafnium.readthedocs.io/en/latest/secure-partition-manager/secure-partition-manager.html#secure-partitions-layout-file .. _[4]: @@ -1650,24 +250,16 @@ Client <https://developer.arm.com/documentation/den0006/d/>`__ .. _[5]: -[5] https://git.trustedfirmware.org/TF-A/tf-a-tests.git/tree/spm/cactus/plat/arm/fvp/fdts/cactus.dts +[5] https://hafnium.readthedocs.io/en/latest/secure-partition-manager/index.html .. _[6]: -[6] https://trustedfirmware-a.readthedocs.io/en/latest/components/ffa-manifest-binding.html +[6] :ref:`EL3 Secure Partition Manager<EL3 Secure Partition Manager>` .. _[7]: -[7] https://git.trustedfirmware.org/TF-A/trusted-firmware-a.git/tree/plat/arm/board/fvp/fdts/fvp_spmc_manifest.dts - -.. _[8]: - -[8] https://lists.trustedfirmware.org/archives/list/tf-a@lists.trustedfirmware.org/thread/CFQFGU6H2D5GZYMUYGTGUSXIU3OYZP6U/ - -.. _[9]: - -[9] https://trustedfirmware-a.readthedocs.io/en/latest/design/firmware-design.html#dynamic-configuration-during-cold-boot +[7] https://trustedfirmware-a.readthedocs.io/en/latest/design/firmware-design.html#dynamic-configuration-during-cold-boot -------------- -*Copyright (c) 2020-2023, Arm Limited and Contributors. All rights reserved.* +*Copyright (c) 2020-2024, Arm Limited and Contributors. All rights reserved.* diff --git a/docs/components/ven-el3-debugfs.rst b/docs/components/ven-el3-debugfs.rst new file mode 100644 index 00000000..8629d701 --- /dev/null +++ b/docs/components/ven-el3-debugfs.rst @@ -0,0 +1,343 @@ +DebugFS interface +================= + +The optional DebugFS interface is accessed through a Vendor specific EL3 service. Refer +to the component documentation for details. + +String parameters are passed through a shared buffer using a specific union: + +.. code:: c + + union debugfs_parms { + struct { + char fname[MAX_PATH_LEN]; + } open; + + struct mount { + char srv[MAX_PATH_LEN]; + char where[MAX_PATH_LEN]; + char spec[MAX_PATH_LEN]; + } mount; + + struct { + char path[MAX_PATH_LEN]; + dir_t dir; + } stat; + + struct { + char oldpath[MAX_PATH_LEN]; + char newpath[MAX_PATH_LEN]; + } bind; + }; + +Format of the dir_t structure as such: + +.. code:: c + + typedef struct { + char name[NAMELEN]; + long length; + unsigned char mode; + unsigned char index; + unsigned char dev; + qid_t qid; + } dir_t; + + +* Identifiers + +======================== ============================================= +SMC_OK 0 +SMC_UNK -1 +DEBUGFS_E_INVALID_PARAMS -2 +======================== ============================================= + +======================== ============================================= +MOUNT 0 +CREATE 1 +OPEN 2 +CLOSE 3 +READ 4 +WRITE 5 +SEEK 6 +BIND 7 +STAT 8 +INIT 10 +VERSION 11 +======================== ============================================= + +MOUNT +~~~~~ + +Description +^^^^^^^^^^^ +This operation mounts a blob of data pointed to by path stored in `src`, at +filesystem location pointed to by path stored in `where`, using driver pointed +to by path in `spec`. + +Parameters +^^^^^^^^^^ +======== ============================================================ +uint32_t FunctionID (0x87000010 / 0xC7000010) +uint32_t ``MOUNT`` +======== ============================================================ + +Return values +^^^^^^^^^^^^^ + +=============== ========================================================== +int32_t w0 == SMC_OK on success + + w0 == DEBUGFS_E_INVALID_PARAMS if mount operation failed +=============== ========================================================== + +OPEN +~~~~ + +Description +^^^^^^^^^^^ +This operation opens the file path pointed to by `fname`. + +Parameters +^^^^^^^^^^ + +======== ============================================================ +uint32_t FunctionID (0x87000010 / 0xC7000010) +uint32_t ``OPEN`` +uint32_t mode +======== ============================================================ + +mode can be one of: + +.. code:: c + + enum mode { + O_READ = 1 << 0, + O_WRITE = 1 << 1, + O_RDWR = 1 << 2, + O_BIND = 1 << 3, + O_DIR = 1 << 4, + O_STAT = 1 << 5 + }; + +Return values +^^^^^^^^^^^^^ + +=============== ========================================================== +int32_t w0 == SMC_OK on success + + w0 == DEBUGFS_E_INVALID_PARAMS if open operation failed + +uint32_t w1: file descriptor id on success. +=============== ========================================================== + +CLOSE +~~~~~ + +Description +^^^^^^^^^^^ + +This operation closes a file described by a file descriptor obtained by a +previous call to OPEN. + +Parameters +^^^^^^^^^^ + +======== ============================================================ +uint32_t FunctionID (0x87000010 / 0xC7000010) +uint32_t ``CLOSE`` +uint32_t File descriptor id returned by OPEN +======== ============================================================ + +Return values +^^^^^^^^^^^^^ +=============== ========================================================== +int32_t w0 == SMC_OK on success + + w0 == DEBUGFS_E_INVALID_PARAMS if close operation failed +=============== ========================================================== + +READ +~~~~ + +Description +^^^^^^^^^^^ + +This operation reads a number of bytes from a file descriptor obtained by +a previous call to OPEN. + +Parameters +^^^^^^^^^^ + +======== ============================================================ +uint32_t FunctionID (0x87000010 / 0xC7000010) +uint32_t ``READ`` +uint32_t File descriptor id returned by OPEN +uint32_t Number of bytes to read +======== ============================================================ + +Return values +^^^^^^^^^^^^^ + +On success, the read data is retrieved from the shared buffer after the +operation. + +=============== ========================================================== +int32_t w0 == SMC_OK on success + + w0 == DEBUGFS_E_INVALID_PARAMS if read operation failed + +uint32_t w1: number of bytes read on success. +=============== ========================================================== + +SEEK +~~~~ + +Description +^^^^^^^^^^^ + +Move file pointer for file described by given `file descriptor` of given +`offset` related to `whence`. + +Parameters +^^^^^^^^^^ + +======== ============================================================ +uint32_t FunctionID (0x87000010 / 0xC7000010) +uint32_t ``SEEK`` +uint32_t File descriptor id returned by OPEN +sint32_t offset in the file relative to whence +uint32_t whence +======== ============================================================ + +whence can be one of: + +========= ============================================================ +KSEEK_SET 0 +KSEEK_CUR 1 +KSEEK_END 2 +========= ============================================================ + +Return values +^^^^^^^^^^^^^ + +=============== ========================================================== +int32_t w0 == SMC_OK on success + + w0 == DEBUGFS_E_INVALID_PARAMS if seek operation failed +=============== ========================================================== + +BIND +~~~~ + +Description +^^^^^^^^^^^ + +Create a link from `oldpath` to `newpath`. + +Parameters +^^^^^^^^^^ + +======== ============================================================ +uint32_t FunctionID (0x87000010 / 0xC7000010) +uint32_t ``BIND`` +======== ============================================================ + +Return values +^^^^^^^^^^^^^ + +=============== ========================================================== +int32_t w0 == SMC_OK on success + + w0 == DEBUGFS_E_INVALID_PARAMS if bind operation failed +=============== ========================================================== + +STAT +~~~~ + +Description +^^^^^^^^^^^ + +Perform a stat operation on provided file `name` and returns the directory +entry statistics into `dir`. + +Parameters +^^^^^^^^^^ + +======== ============================================================ +uint32_t FunctionID (0x87000010 / 0xC7000010) +uint32_t ``STAT`` +======== ============================================================ + +Return values +^^^^^^^^^^^^^ + +=============== ========================================================== +int32_t w0 == SMC_OK on success + + w0 == DEBUGFS_E_INVALID_PARAMS if stat operation failed +=============== ========================================================== + +INIT +~~~~ + +Description +^^^^^^^^^^^ +Initial call to setup the shared exchange buffer. Notice if successful once, +subsequent calls fail after a first initialization. The caller maps the same +page frame in its virtual space and uses this buffer to exchange string +parameters with filesystem primitives. + +Parameters +^^^^^^^^^^ + +======== ============================================================ +uint32_t FunctionID (0x87000010 / 0xC7000010) +uint32_t ``INIT`` +uint64_t Physical address of the shared buffer. +======== ============================================================ + +Return values +^^^^^^^^^^^^^ + +=============== ====================================================== +int32_t w0 == SMC_OK on success + + w0 == DEBUGFS_E_INVALID_PARAMS if already initialized, + or internal error occurred. +=============== ====================================================== + +VERSION +~~~~~~~ + +Description +^^^^^^^^^^^ +Returns the debugfs interface version if implemented in TF-A. + +Parameters +^^^^^^^^^^ + +======== ============================================================ +uint32_t FunctionID (0x87000010 / 0xC7000010) +uint32_t ``VERSION`` +======== ============================================================ + +Return values +^^^^^^^^^^^^^ + +=============== ====================================================== +int32_t w0 == SMC_OK on success + + w0 == SMC_UNK if interface is not implemented + +uint32_t w1: On success, debugfs interface version, 32 bits + value with major version number in upper 16 bits and + minor version in lower 16 bits. +=============== ====================================================== + +* CREATE(1) and WRITE (5) command identifiers are unimplemented and + return `SMC_UNK`. + +-------------- + +*Copyright (c) 2024, Arm Limited and Contributors. All rights reserved.* diff --git a/docs/components/ven-el3-service.rst b/docs/components/ven-el3-service.rst new file mode 100644 index 00000000..13449ba6 --- /dev/null +++ b/docs/components/ven-el3-service.rst @@ -0,0 +1,78 @@ +Vendor Specific EL3 Monitor Service Calls +========================================= + +This document enumerates and describes the Vendor Specific EL3 Monitor Service +Calls. + +These are Service Calls defined by the vendor of the EL3 Monitor. +They are accessed via ``SMC`` ("SMC calls") instruction executed from Exception +Levels below EL3. SMC calls for Vendor Specific EL3 Monitor Services: + +- Follow `SMC Calling Convention`_; +- Use SMC function IDs that fall in the vendor-specific EL3 range, which are + ++---------------------------+--------------------------------------------------+ +| SMC Function Identifier | Service Type | ++===========================+==================================================+ +| 0x87000000 - 0x8700FFFF | SMC32: Vendor Specific EL3 Monitor Service Calls | ++---------------------------+--------------------------------------------------+ +| 0xC7000000 - 0xC700FFFF | SMC64: Vendor Specific EL3 Monitor Service Calls | ++---------------------------+--------------------------------------------------+ + +Vendor-specific EL3 monitor services are as follows: + ++-----------------------------------+-----------------------+---------------------------------------------+ +| SMC Function Identifier | Service Type | FID's Usage | ++===================================+=======================+=============================================+ +| 0x87000010 - 0x8700001F (SMC32) | DebugFS Interface | | 0 - 11 are in use. | ++-----------------------------------+ | | 12 - 15 are reserved for future expansion.| +| 0xC7000010 - 0xC700001F (SMC64) | | | ++-----------------------------------+-----------------------+---------------------------------------------+ +| 0x87000020 - 0x8700002F (SMC32) | Performance | | 0,1 is in use. | ++-----------------------------------+ Measurement Framework | | 2 - 15 are reserved for future expansion. | +| 0xC7000020 - 0xC700002F (SMC64) | (PMF) | | ++-----------------------------------+-----------------------+---------------------------------------------+ +| 0x87000030 - 0x8700FFFF (SMC32) | Reserved | | reserved for future expansion | ++-----------------------------------+ | | +| 0xC7000030 - 0xC700FFFF (SMC64) | | | ++-----------------------------------+-----------------------+---------------------------------------------+ + +Source definitions for vendor-specific EL3 Monitor Service Calls used by TF-A are located in +the ``ven_el3_svc.h`` header file. + ++----------------------------+----------------------------+--------------------------------+ +| VEN_EL3_SVC_VERSION_MAJOR | VEN_EL3_SVC_VERSION_MINOR | Changes | ++============================+============================+================================+ +| 1 | 0 | Added Debugfs and PMF services.| ++----------------------------+----------------------------+--------------------------------+ + +*Table 1: Showing different versions of Vendor-specific service and changes done with each version* + +Each sub service will have its own version, one FID allocated for sub service version. + +Some ground rules when one should update top level version. + - VEN_EL3_SVC_VERSION_MAJOR is incremented when any of the sub service version discovery + FID changes or the FID that was allocated for discovery changes. So any breaking subfeature + discovery changes will lead to major version update. + - VEN_EL3_SVC_VERSION_MINOR is incremented when we add a new FID or a new sub service. + For example adding an new monitor service at 0x30, Debugfs starts at 0x10 and PMF + starts at 0x20 next one will start at 0x30, this will need a update to minor version. + +Performance Measurement Framework (PMF) +--------------------------------------- + +The :ref:`Performance Measurement Framework <firmware_design_pmf>` +allows callers to retrieve timestamps captured at various paths in TF-A +execution. + +DebugFS interface +----------------- + +The optional DebugFS interface is accessed through Vendor specific EL3 service. Refer +to :ref:`DebugFS interface` documentation for further details and usage. + +-------------- + +*Copyright (c) 2024, Arm Limited and Contributors. All rights reserved.* + +.. _SMC Calling Convention: https://developer.arm.com/docs/den0028/latest diff --git a/docs/conf.py b/docs/conf.py index d4e54239..3a7264fd 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- # -# Copyright (c) 2019-2023, Arm Limited. All rights reserved. +# Copyright (c) 2019-2024, Arm Limited. All rights reserved. # # SPDX-License-Identifier: BSD-3-Clause # @@ -14,8 +14,8 @@ project = "Trusted Firmware-A" author = "Trusted Firmware-A contributors" -version = "2.10.0" -release = "2.10.0" +version = "2.12.0" +release = "2.12.0" # -- General configuration --------------------------------------------------- diff --git a/docs/design/auth-framework.rst b/docs/design/auth-framework.rst index 597f955e..6dc2245c 100644 --- a/docs/design/auth-framework.rst +++ b/docs/design/auth-framework.rst @@ -254,8 +254,8 @@ These functions are registered in the CM using the macro: REGISTER_CRYPTO_LIB(_name, _init, _verify_signature, - _calc_hash, _verify_hash, + _calc_hash, _auth_decrypt, _convert_pk); @@ -505,11 +505,12 @@ uses this information to: typedef enum { AUTH_PARAM_NONE, - AUTH_PARAM_RAW_DATA, /* Raw image data */ + AUTH_PARAM_RAW_DATA, /* Raw image data */ AUTH_PARAM_SIG, /* The image signature */ AUTH_PARAM_SIG_ALG, /* The image signature algorithm */ AUTH_PARAM_HASH, /* A hash (including the algorithm) */ AUTH_PARAM_PUB_KEY, /* A public key */ + AUTH_PARAM_NV_CTR, /* A non-volatile counter */ } auth_param_type_t; The AM defines the following structure to identify an authentication parameter @@ -1018,4 +1019,4 @@ The mbedTLS library algorithm support is configured by both the *Copyright (c) 2017-2023, Arm Limited and Contributors. All rights reserved.* -.. _TBBR-Client specification: https://developer.arm.com/docs/den0006/latest/trusted-board-boot-requirements-client-tbbr-client-armv8-a +.. _TBBR-Client specification: https://developer.arm.com/docs/den0006/latest diff --git a/docs/design/cpu-specific-build-macros.rst b/docs/design/cpu-specific-build-macros.rst index d03daf89..fda43dc9 100644 --- a/docs/design/cpu-specific-build-macros.rst +++ b/docs/design/cpu-specific-build-macros.rst @@ -384,11 +384,19 @@ For Cortex-A78C, the following errata build flags are defined : Cortex-A78C CPU. This needs to be enabled for revisions r0p1 and r0p2. This erratum is still open. +- ``ERRATA_A78C_2683027`` : This applies errata 2683027 workaround to + Cortex-A78C CPU. This needs to be enabled for revisions r0p1 and r0p2. This + erratum is still open. + - ``ERRATA_A78C_2712575`` : This applies erratum 2712575 workaround to Cortex-A78C CPU, this erratum affects system configurations that do not use an ARM interconnect IP. This needs to be enabled for revisions r0p1 and r0p2 and is still open. +- ``ERRATA_A78C_2743232`` : This applies erratum 2743232 workaround to + Cortex-A78C CPU. This needs to be enabled for revisions r0p1 and r0p2. + This erratum is still open. + - ``ERRATA_A78C_2772121`` : This applies errata 2772121 workaround to Cortex-A78C CPU. This needs to be enabled for revisions r0p0, r0p1 and r0p2. This erratum is still open. @@ -501,6 +509,10 @@ For Neoverse V1, the following errata build flags are defined : CPU. This needs to be enabled for revisions r0p0, r1p0, and r1p1 and r1p2 of the CPU. +- ``ERRATA_V1_2348377``: This applies errata 2348377 workaroud to Neoverse-V1 + CPU. This needs to be enabled for revisions r0p0, r1p0 and r1p1 of the CPU. + It has been fixed in r1p2. + - ``ERRATA_V1_2372203``: This applies errata 2372203 workaround to Neoverse-V1 CPU. This needs to be enabled for revisions r0p0, r1p0 and r1p1 of the CPU. It is still open. @@ -528,6 +540,14 @@ For Neoverse V2, the following errata build flags are defined : CPU. This needs to be enabled for revisions r0p0, r0p1 and r0p2. It is still open. +- ``ERRATA_V2_2618597``: This applies errata 2618597 workaround to Neoverse-V2 + CPU. This needs to be enabled for revisions r0p0 and r0p1. It is fixed in + r0p2. + +- ``ERRATA_V2_2662553``: This applies errata 2662553 workaround to Neoverse-V2 + CPU. This needs to be enabled for revisions r0p0 and r0p1. It is fixed in + r0p2. + - ``ERRATA_V2_2719103``: This applies errata 2719103 workaround to Neoverse-V2 CPU, this affects system configurations that do not use and ARM interconnect IP. This needs to be enabled for revisions r0p0 and r0p1. It has been fixed @@ -620,6 +640,10 @@ For Cortex-A710, the following errata build flags are defined : Cortex-A710 CPU. This needs to be enabled for revisions r0p0, r1p0, r2p0 and r2p1 of the CPU and is still open. +- ``ERRATA_A710_2778471``: This applies errata 2778471 workaround to Cortex-A710 + CPU. This needs to be enabled for revisions r0p0, r1p0, r2p0 and r2p1 of the + CPU and is still open. + For Neoverse N2, the following errata build flags are defined : - ``ERRATA_N2_2002655``: This applies errata 2002655 workaround to Neoverse-N2 @@ -741,24 +765,87 @@ For Cortex-X2, the following errata build flags are defined : CPU. This needs to be enabled for revisions r0p0, r1p0, r2p0 and r2p1 of the CPU and is still open. +- ``ERRATA_X2_2778471``: This applies errata 2778471 workaround to Cortex-X2 + CPU. This needs to be enabled for revisions r0p0, r1p0, r2p0 and r2p1 of the + CPU and it is still open. + For Cortex-X3, the following errata build flags are defined : - ``ERRATA_X3_2070301``: This applies errata 2070301 workaround to the Cortex-X3 CPU. This needs to be enabled only for revisions r0p0, r1p0, r1p1 and r1p2 of the CPU and is still open. +- ``ERRATA_X3_2266875``: This applies errata 2266875 workaround to the Cortex-X3 + CPU. This needs to be enabled only for revisions r0p0 and r1p0 of the CPU, it + is fixed in r1p1. + +- ``ERRATA_X3_2302506``: This applies errata 2302506 workaround to the Cortex-X3 + CPU. This needs to be enabled only for revisions r0p0, r1p0 and r1p1, it is + fixed in r1p2. + - ``ERRATA_X3_2313909``: This applies errata 2313909 workaround to Cortex-X3 CPU. This needs to be enabled only for revisions r0p0 and r1p0 of the CPU, it is fixed in r1p1. +- ``ERRATA_X3_2372204``: This applies errata 2372204 workaround to + Cortex-X3 CPU. This needs to be enabled only for revisions r0p0 and r1p0 + of the CPU, it is fixed in r1p1. + - ``ERRATA_X3_2615812``: This applies errata 2615812 workaround to Cortex-X3 CPU. This needs to be enabled only for revisions r0p0, r1p0 and r1p1 of the - CPU, it is still open. + CPU, it is fixed in r1p2. + +- ``ERRATA_X3_2641945``: This applies errata 2641945 workaround to Cortex-X3 + CPU. This needs to be enabled only for revisions r0p0 and r1p0 of the CPU. + It is fixed in r1p1. + +- ``ERRATA_X3_2701951``: This applies erratum 2701951 workaround to Cortex-X3 + CPU and affects system configurations that do not use an ARM interconnect + IP. This needs to be applied to revisions r0p0, r1p0 and r1p1. It is fixed + in r1p2. - ``ERRATA_X3_2742421``: This applies errata 2742421 workaround to Cortex-X3 CPU. This needs to be enabled only for revisions r0p0, r1p0 and r1p1. It is fixed in r1p2. +- ``ERRATA_X3_2743088``: This applies errata 2743088 workaround to Cortex-X3 + CPU. This needs to be enabled only for revisions r0p0, r1p0 and r1p1. It is + fixed in r1p2. + +- ``ERRATA_X3_2779509``: This applies errata 2779509 workaround to Cortex-X3 + CPU. This needs to be enabled only for revisions r0p0, r1p0 and r1p1 of the + CPU. It is fixed in r1p2. + +For Cortex-X4, the following errata build flags are defined : + +- ``ERRATA_X4_2701112``: This applies erratum 2701112 workaround to Cortex-X4 + CPU and affects system configurations that do not use an Arm interconnect IP. + This needs to be enabled for revisions r0p0 and is fixed in r0p1. + The workaround for this erratum is not implemented in EL3, but the flag can + be enabled/disabled at the platform level. The flag is used when the errata ABI + feature is enabled and can assist the Kernel in the process of + mitigation of the erratum. + +- ``ERRATA_X4_2726228``: This applies erratum 2726228 workaround to Cortex-X4 + CPU. This needs to be enabled for revisions r0p0 and r0p1. It is fixed in + r0p2. + +- ``ERRATA_X4_2740089``: This applies errata 2740089 workaround to Cortex-X4 + CPU. This needs to be enabled for revisions r0p0 and r0p1. It is fixed + in r0p2. + +- ``ERRATA_X4_2763018``: This applies errata 2763018 workaround to Cortex-X4 + CPU. This needs to be enabled for revisions r0p0 and r0p1. It is fixed in r0p2. + +- ``ERRATA_X4_2816013``: This applies errata 2816013 workaround to Cortex-X4 + CPU. This needs to be enabled for revisions r0p0 and r0p1. It is fixed in r0p2. + +- ``ERRATA_X4_2897503``: This applies errata 2897503 workaround to Cortex-X4 + CPU. This needs to be enabled for revisions r0p0 and r0p1. It is fixed in r0p2. + +- ``ERRATA_X4_3076789``: This applies errata 3076789 workaround to Cortex-X4 + CPU. This needs to be enabled for revisions r0p0 and r0p1. It is fixed in r0p2. + For Cortex-A510, the following errata build flags are defined : - ``ERRATA_A510_1922240``: This applies errata 1922240 workaround to @@ -812,12 +899,68 @@ For Cortex-A510, the following errata build flags are defined : Cortex-A510 CPU. This needs to be applied to revision r0p0, r0p1, r0p2, r0p3, r1p0, r1p1 and r1p2. It is fixed in r1p3. +For Cortex-A520, the following errata build flags are defined : + +- ``ERRATA_A520_2630792``: This applies errata 2630792 workaround to + Cortex-A520 CPU. This needs to applied for revisions r0p0, r0p1 of the + CPU and is still open. + +- ``ERRATA_A520_2858100``: This applies errata 2858100 workaround to + Cortex-A520 CPU. This needs to be enabled for revisions r0p0 and r0p1. + It is still open. + +- ``ERRATA_A520_2938996``: This applies errata 2938996 workaround to + Cortex-A520 CPU. This needs to be enabled for revisions r0p0 and r0p1. + It is fixed in r0p2. + For Cortex-A715, the following errata build flags are defined : -- ``ERRATA_A715_2701951``: This applies erratum 2701951 workaround to Cortex-A715 - CPU and affects system configurations that do not use an ARM interconnect - IP. This needs to be applied to revisions r0p0, r1p0 and r1p1. It is fixed - in r1p2. +- ``ERRATA_A715_2331818``: This applies errata 2331818 workaround to + Cortex-A715 CPU. This needs to be enabled for revisions r0p0 and r1p0. + It is fixed in r1p1. + +- ``ERRATA_A715_2344187``: This applies errata 2344187 workaround to + Cortex-A715 CPU. This needs to be enabled for revisions r0p0 and r1p0. It is + fixed in r1p1. + +- ``ERRATA_A715_2413290``: This applies errata 2413290 workaround to + Cortex-A715 CPU. This needs to be enabled only for revision r1p0 and + when SPE(Statistical profiling extension)=True. The errata is fixed + in r1p1. + +- ``ERRATA_A715_2420947``: This applies errata 2420947 workaround to + Cortex-A715 CPU. This needs to be enabled only for revision r1p0. + It is fixed in r1p1. + +- ``ERRATA_A715_2429384``: This applies errata 2429384 workaround to + Cortex-A715 CPU. This needs to be enabled for revision r1p0. There is no + workaround for revision r0p0. It is fixed in r1p1. + +- ``ERRATA_A715_2561034``: This applies errata 2561034 workaround to + Cortex-A715 CPU. This needs to be enabled only for revision r1p0. + It is fixed in r1p1. + +- ``ERRATA_A715_2728106``: This applies errata 2728106 workaround to + Cortex-A715 CPU. This needs to be enabled for revisions r0p0, r1p0 + and r1p1. It is fixed in r1p2. + +For Cortex-A720, the following errata build flags are defined : + +- ``ERRATA_A720_2792132``: This applies errata 2792132 workaround to + Cortex-A720 CPU. This needs to be enabled for revisions r0p0 and r0p1. + It is fixed in r0p2. + +- ``ERRATA_A720_2844092``: This applies errata 2844092 workaround to + Cortex-A720 CPU. This needs to be enabled for revisions r0p0 and r0p1. + It is fixed in r0p2. + +- ``ERRATA_A720_2926083``: This applies errata 2926083 workaround to + Cortex-A720 CPU. This needs to be enabled for revisions r0p0 and r0p1. + It is fixed in r0p2. + +- ``ERRATA_A720_2940794``: This applies errata 2940794 workaround to + Cortex-A720 CPU. This needs to be enabled for revisions r0p0 and r0p1. + It is fixed in r0p2. DSU Errata Workarounds ---------------------- @@ -904,7 +1047,7 @@ GIC Errata Workarounds -------------- -*Copyright (c) 2014-2023, Arm Limited and Contributors. All rights reserved.* +*Copyright (c) 2014-2024, Arm Limited and Contributors. All rights reserved.* .. _CVE-2017-5715: http://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2017-5715 .. _CVE-2018-3639: http://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2018-3639 diff --git a/docs/design/firmware-design.rst b/docs/design/firmware-design.rst index 3fce3939..2ba54ea8 100644 --- a/docs/design/firmware-design.rst +++ b/docs/design/firmware-design.rst @@ -645,6 +645,35 @@ on entry, these should be enabled during ``bl31_plat_arch_setup()``. Data structures used in the BL31 cold boot interface '''''''''''''''''''''''''''''''''''''''''''''''''''' +In the cold boot flow, ``entry_point_info`` is used to represent the execution +state of an image; that is, the state of general purpose registers, PC, and +SPSR. + +There are two variants of this structure, for AArch64: + +.. code:: c + + typedef struct entry_point_info { + param_header_t h; + uintptr_t pc; + uint32_t spsr; + + aapcs64_params_t args; + } + +and, AArch32: + +.. code:: c + + typedef struct entry_point_info { + param_header_t h; + uintptr_t pc; + uint32_t spsr; + + uintptr_t lr_svc; + aapcs32_params_t args; + } entry_point_info_t; + These structures are designed to support compatibility and independent evolution of the structures and the firmware images. For example, a version of BL31 that can interpret the BL3x image information from different versions of @@ -662,13 +691,17 @@ BL31 to detect which information is present and respond appropriately. The uint8_t type; /* type of the structure */ uint8_t version; /* version of this structure */ uint16_t size; /* size of this structure in bytes */ - uint32_t attr; /* attributes: unused bits SBZ */ + uint32_t attr; /* attributes */ } param_header_t; -The structures using this format are ``entry_point_info``, ``image_info`` and -``bl31_params``. The code that allocates and populates these structures must set -the header fields appropriately, and the ``SET_PARAM_HEAD()`` a macro is defined -to simplify this action. +In `entry_point_info`, Bits 0 and 5 of ``attr`` field are used to encode the +security state; in other words, whether the image is to be executed in Secure, +Non-Secure, or Realm mode. + +Other structures using this format are ``image_info`` and ``bl31_params``. The +code that allocates and populates these structures must set the header fields +appropriately, the ``SET_PARAM_HEAD()`` macro is defined to simplify this +action. Required CPU state for BL31 Warm boot initialization ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -2767,13 +2800,11 @@ Armv8.5-A - Branch Target Identification feature is selected by ``BRANCH_PROTECTION`` option set to 1. This option defaults to 0. -- Memory Tagging Extension feature is unconditionally enabled for both worlds - (at EL0 and S-EL0) if it is only supported at EL0. If instead it is - implemented at all ELs, it is unconditionally enabled for only the normal - world. To enable it for the secure world as well, the build option - ``CTX_INCLUDE_MTE_REGS`` is required. If the hardware does not implement - MTE support at all, it is always disabled, no matter what build options - are used. +- Memory Tagging Extension feature has few variants but not all of them require + enablement from EL3 to be used at lower EL. e.g. Memory tagging only at + EL0(MTE) does not require EL3 configuration however memory tagging at + EL2/EL1 (MTE2) does require EL3 enablement and we need to set this option + ``ENABLE_FEAT_MTE2`` to 1. This option defaults to 0. Armv7-A ~~~~~~~ @@ -2860,13 +2891,13 @@ kernel at boot time. These can be found in the ``fdts`` directory. -------------- -*Copyright (c) 2013-2023, Arm Limited and Contributors. All rights reserved.* +*Copyright (c) 2013-2024, Arm Limited and Contributors. All rights reserved.* .. _SMCCC: https://developer.arm.com/docs/den0028/latest .. _PSCI: https://developer.arm.com/documentation/den0022/latest/ .. _Arm ARM: https://developer.arm.com/docs/ddi0487/latest .. _SMC Calling Convention: https://developer.arm.com/docs/den0028/latest -.. _Trusted Board Boot Requirements CLIENT (TBBR-CLIENT) Armv8-A (ARM DEN0006D): https://developer.arm.com/docs/den0006/latest/trusted-board-boot-requirements-client-tbbr-client-armv8-a +.. _Trusted Board Boot Requirements CLIENT (TBBR-CLIENT) Armv8-A (ARM DEN0006D): https://developer.arm.com/docs/den0006/latest .. _Arm Confidential Compute Architecture (Arm CCA): https://www.arm.com/why-arm/architecture/security-features/arm-confidential-compute-architecture .. _AArch64 exception vector table: https://developer.arm.com/documentation/100933/0100/AArch64-exception-vector-table diff --git a/docs/design/trusted-board-boot.rst b/docs/design/trusted-board-boot.rst index fed202ad..f10d2e72 100644 --- a/docs/design/trusted-board-boot.rst +++ b/docs/design/trusted-board-boot.rst @@ -1,24 +1,45 @@ Trusted Board Boot ================== -The Trusted Board Boot (TBB) feature prevents malicious firmware from running on -the platform by authenticating all firmware images up to and including the -normal world bootloader. It does this by establishing a Chain of Trust using +The `Trusted Board Boot` (TBB) feature prevents malicious firmware from running +on the platform by authenticating all firmware images up to and including the +normal world bootloader. It does this by establishing a `Chain of Trust` using Public-Key-Cryptography Standards (PKCS). This document describes the design of Trusted Firmware-A (TF-A) TBB, which is an implementation of the `Trusted Board Boot Requirements (TBBR)`_ specification, -Arm DEN0006D. It should be used in conjunction with the -:ref:`Firmware Update (FWU)` design document, which implements a specific aspect -of the TBBR. +Arm DEN0006D. It should be used in conjunction with the :ref:`Firmware Update +(FWU)` design document, which implements a specific aspect of the TBBR. Chain of Trust -------------- -A Chain of Trust (CoT) starts with a set of implicitly trusted components. On -the Arm development platforms, these components are: +A Chain of Trust (CoT) starts with a set of implicitly trusted components, which +are used to establish trust in the next layer of components, and so on, in a +`chained` manner. -- A SHA-256 hash of the Root of Trust Public Key (ROTPK). It is stored in the +The chain of trust depends on several factors, including: + +- The set of firmware images in use on this platform. + Typically, most platforms share a common set of firmware images (BL1, BL2, + BL31, BL33) but extra platform-specific images might be required. + +- The key provisioning scheme: which keys need to programmed into the device + and at which stage during the platform's manufacturing lifecycle. + +- The key ownership model: who owns which key. + +As these vary across platforms, chains of trust also vary across +platforms. Although each platform is free to define its own CoT based on its +needs, TF-A provides a set of "default" CoTs fitting some typical trust models, +which platforms may reuse. The rest of this section presents general concepts +which apply to all these default CoTs. + +The implicitly trusted components forming the trust anchor are: + +- A Root of Trust Public Key (ROTPK), or a hash of it. + + On Arm development platforms, a SHA-256 hash of the ROTPK is stored in the trusted root-key storage registers. Alternatively, a development ROTPK might be used and its hash embedded into the BL1 and BL2 images (only for development purposes). @@ -31,11 +52,11 @@ images. The certificates follow the `X.509 v3`_ standard. This standard enables adding custom extensions to the certificates, which are used to store essential information to establish the CoT. -In the TBB CoT all certificates are self-signed. There is no need for a -Certificate Authority (CA) because the CoT is not established by verifying the -validity of a certificate's issuer but by the content of the certificate -extensions. To sign the certificates, different signature schemes are available, -please refer to the :ref:`Build Options` for more details. +All certificates are self-signed. There is no need for a Certificate Authority +(CA) because the CoT is not established by verifying the validity of a +certificate's issuer but by the content of the certificate extensions. To sign +the certificates, different signature schemes are available, please refer to the +:ref:`Build Options` for more details. The certificates are categorised as "Key" and "Content" certificates. Key certificates are used to verify public keys which have been used to sign content @@ -43,27 +64,40 @@ certificates. Content certificates are used to store the hash of a boot loader image. An image can be authenticated by calculating its hash and matching it with the hash extracted from the content certificate. Various hash algorithms are supported to calculate all hashes, please refer to the :ref:`Build Options` -for more details.. The public keys and hashes are included as non-standard +for more details. The public keys and hashes are included as non-standard extension fields in the `X.509 v3`_ certificates. -The keys used to establish the CoT are: +The next sections now present specificities of each default CoT provided in +TF-A. + +Default CoT #1: TBBR +~~~~~~~~~~~~~~~~~~~~ + +The `TBBR` CoT is named after the specification it follows to the letter. + +In the TBBR CoT, all firmware binaries and certificates are (directly or +indirectly) linked to the Root of Trust Public Key (ROTPK). Typically, the same +vendor owns the ROTPK, the Trusted key and the Non-Trusted Key. Thus, this vendor +is involved in signing every BL3x Key Certificate. + +The keys used to establish this CoT are: - **Root of trust key** - The private part of this key is used to sign the BL2 content certificate and - the trusted key certificate. The public part is the ROTPK. + The private part of this key is used to sign the trusted boot firmware + certificate and the trusted key certificate. The public part is the ROTPK. - **Trusted world key** The private part is used to sign the key certificates corresponding to the secure world images (SCP_BL2, BL31 and BL32). The public part is stored in - one of the extension fields in the trusted world certificate. + one of the extension fields in the trusted key certificate. - **Non-trusted world key** The private part is used to sign the key certificate corresponding to the - non secure world image (BL33). The public part is stored in one of the - extension fields in the trusted world certificate. + non-secure world image (BL33). The public part is stored in one of the + extension fields in the trusted key certificate. - **BL3X keys** @@ -82,10 +116,11 @@ The following images are included in the CoT: The following certificates are used to authenticate the images. -- **BL2 content certificate** +- **Trusted boot firmware certificate** - It is self-signed with the private part of the ROT key. It contains a hash - of the BL2 image. + It is self-signed with the private part of the ROT key. It contains a hash of + the BL2 image and hashes of various firmware configuration files + (TB_FW_CONFIG, HW_CONFIG, FW_CONFIG). - **Trusted key certificate** @@ -93,45 +128,82 @@ The following certificates are used to authenticate the images. public part of the trusted world key and the public part of the non-trusted world key. -- **SCP_BL2 key certificate** +- **SCP firmware key certificate** It is self-signed with the trusted world key. It contains the public part of the SCP_BL2 key. -- **SCP_BL2 content certificate** +- **SCP firmware content certificate** It is self-signed with the SCP_BL2 key. It contains a hash of the SCP_BL2 image. -- **BL31 key certificate** +- **SoC firmware key certificate** It is self-signed with the trusted world key. It contains the public part of the BL31 key. -- **BL31 content certificate** +- **SoC firmware content certificate** - It is self-signed with the BL31 key. It contains a hash of the BL31 image. + It is self-signed with the BL31 key. It contains hashes of the BL31 image and + its configuration file (SOC_FW_CONFIG). -- **BL32 key certificate** +- **Trusted OS key certificate** It is self-signed with the trusted world key. It contains the public part of the BL32 key. -- **BL32 content certificate** +- **Trusted OS content certificate** - It is self-signed with the BL32 key. It contains a hash of the BL32 image. + It is self-signed with the BL32 key. It contains hashes of the BL32 image(s) + and its configuration file(s) (TOS_FW_CONFIG). -- **BL33 key certificate** +- **Non-trusted firmware key certificate** It is self-signed with the non-trusted world key. It contains the public part of the BL33 key. -- **BL33 content certificate** +- **Non-trusted firmware content certificate** + + It is self-signed with the BL33 key. It contains hashes of the BL33 image and + its configuration file (NT_FW_CONFIG). + +The SCP firmware and Trusted OS certificates are optional, but they must be +present if the corresponding SCP_BL2 or BL32 images are present. + +The following diagram summarizes the part of the TBBR CoT enforced by BL2. Some +images (SCP, debug certificates, secure partitions, configuration files) are not +shown here for conciseness: + +.. image:: ../resources/diagrams/cot-tbbr.jpg + +Default CoT #2: Dualroot +~~~~~~~~~~~~~~~~~~~~~~~~ + +The `dualroot` CoT is targeted at systems where the Normal World firmware is +owned by a different entity than the Secure World Firmware, and those 2 entities +do not wish to share any keys or have any dependency between each other when it +comes to signing their respective images. It establishes 2 separate signing +domains, each with its own Root of Trust key. In that sense, this CoT has 2 +roots of trust, hence the `dualroot` name. + +Although the dualroot CoT reuses some of the TBBR CoT components and concepts, +it differs on the BL33 image's chain of trust, which is rooted into a new key, +called `Platform ROTPK`, or `PROTPK` for short. + +The following diagram summarizes the part of the dualroot CoT enforced by +BL2. Some images (SCP, debug certificates, secure partitions, configuration +files) are not shown here for conciseness: + +.. image:: ../resources/diagrams/cot-dualroot.jpg - It is self-signed with the BL33 key. It contains a hash of the BL33 image. +Default CoT #3: CCA +~~~~~~~~~~~~~~~~~~~ -The SCP_BL2 and BL32 certificates are optional, but they must be present if the -corresponding SCP_BL2 or BL32 images are present. +This CoT is targeted at Arm CCA systems. The Arm CCA security model recommends +making supply chains for the Arm CCA firmware, the secure world firmware and the +platform owner firmware, independent. Hence, this CoT has 3 roots of trust, one +for each supply chain. Trusted Board Boot Sequence --------------------------- @@ -261,4 +333,4 @@ Instructions for building and using the tool can be found in the *Copyright (c) 2015-2020, Arm Limited and Contributors. All rights reserved.* .. _X.509 v3: https://tools.ietf.org/rfc/rfc5280.txt -.. _Trusted Board Boot Requirements (TBBR): https://developer.arm.com/docs/den0006/latest/trusted-board-boot-requirements-client-tbbr-client-armv8-a +.. _Trusted Board Boot Requirements (TBBR): https://developer.arm.com/docs/den0006/latest diff --git a/docs/design_documents/cmake_framework.rst b/docs/design_documents/cmake_framework.rst index d88942e8..f946b2e0 100644 --- a/docs/design_documents/cmake_framework.rst +++ b/docs/design_documents/cmake_framework.rst @@ -11,11 +11,7 @@ TF-A CMake buildsystem Abstract -------- This document presents a proposal for a new buildsystem for TF-A using CMake, -and as part of this a reusable CMake framework for embedded projects. For a -summary about the proposal, please see the `Phabricator wiki page -<https://developer.trustedfirmware.org/w/tf_a/cmake-buildsystem-proposal/>`_. As -mentioned there, the proposal consists of two phases. The subject of this -document is the first phase only. +and as part of this a reusable CMake framework for embedded projects. Introduction ------------ @@ -162,4 +158,4 @@ the settings group, we can use it for conditionally adding source files. E.g. -------------- -*Copyright (c) 2019-2020, Arm Limited and Contributors. All rights reserved.* +*Copyright (c) 2019-2024, Arm Limited and Contributors. All rights reserved.* diff --git a/docs/design_documents/context_mgmt_rework.rst b/docs/design_documents/context_mgmt_rework.rst index 59f9d4ea..b086e3c8 100644 --- a/docs/design_documents/context_mgmt_rework.rst +++ b/docs/design_documents/context_mgmt_rework.rst @@ -4,7 +4,7 @@ Enhance Context Management library for EL3 firmware :Authors: Soby Mathew & Zelalem Aweke :Organization: Arm Limited :Contact: Soby Mathew <soby.mathew@arm.com> & Zelalem Aweke <zelalem.aweke@arm.com> -:Status: RFC +:Status: Implementation is ongoing. Refer to :ref:`Context Management Library` for more details. .. contents:: Table of Contents @@ -194,4 +194,4 @@ improvements which are thought to have negligible impact on EL3 performance. -------------- -*Copyright (c) 2022, Arm Limited and Contributors. All rights reserved.* +*Copyright (c) 2022-2024, Arm Limited and Contributors. All rights reserved.* diff --git a/docs/design_documents/index.rst b/docs/design_documents/index.rst index ecc68b23..ac982e09 100644 --- a/docs/design_documents/index.rst +++ b/docs/design_documents/index.rst @@ -9,7 +9,7 @@ Design Documents context_mgmt_rework measured_boot_poc drtm_poc - rss + rse psci_osi_mode measured_boot diff --git a/docs/design_documents/measured_boot.rst b/docs/design_documents/measured_boot.rst index c4e52135..005903ed 100644 --- a/docs/design_documents/measured_boot.rst +++ b/docs/design_documents/measured_boot.rst @@ -91,10 +91,10 @@ The Measured Boot implementation in TF-A supports: and the variable length crypto agile structure called TCG_PCR_EVENT2. Event Log driver implemented in TF-A covers later part. -#. RSS +#. |RSE| - It is one of physical backend to extend the measurements. Please refer this - document :ref:`Runtime Security Subsystem (RSS)` for more details. + It is one of the physical backends to extend the measurements. Please refer + this document :ref:`Runtime Security Engine (RSE)` for more details. Platform Interface ------------------ @@ -121,7 +121,7 @@ Responsibilities of these platform interfaces are - void bl2_plat_mboot_init(void); Initialise all Measured Boot backends supported by the platform - (e.g. Event Log buffer, RSS). As these functions do not return any value, + (e.g. Event Log buffer, |RSE|). As these functions do not return any value, the platform should deal with error management, such as logging the error somewhere, or panicking the system if this is considered a fatal error. @@ -147,8 +147,9 @@ Responsibilities of these platform interfaces are - - If it is Event Log backend, then record the measurement in TCG Event Log format. - - If it is a secure crypto-processor (like RSS), then extend the designated - PCR (or slot) with the given measurement. + - If it is a secure crypto-processor (like |RSE|), then extend the + designated PCR (or store it in secure on-chip memory) with the given + measurement. - This function must return 0 on success, a signed integer error code otherwise. - On the Arm FVP port, this function measures the given image and then @@ -222,8 +223,8 @@ Responsibilities of these platform interfaces are - - Public key data size is passed as the third argument to this function. - This function must return 0 on success, a signed integer error code otherwise. - - In FVP platform, this function is used to calculate the hash of the given - key and forward this hash to RSS alongside the measurement of the image + - In TC2 platform, this function is used to calculate the hash of the given + key and forward this hash to |RSE| alongside the measurement of the image which the key signs. -------------- diff --git a/docs/design_documents/rss.rst b/docs/design_documents/rse.rst similarity index 52% rename from docs/design_documents/rss.rst rename to docs/design_documents/rse.rst index 18d54368..dd110ca8 100644 --- a/docs/design_documents/rss.rst +++ b/docs/design_documents/rse.rst @@ -1,45 +1,45 @@ -Runtime Security Subsystem (RSS) -================================ +Runtime Security Engine (RSE) +============================= -This document focuses on the relationship between the Runtime Security Subsystem -(RSS) and the application processor (AP). According to the ARM reference design -the RSS is an independent core next to the AP and the SCP on the same die. It +This document focuses on the relationship between the Runtime Security Engine +(RSE) and the application processor (AP). According to the ARM reference design +the RSE is an independent core next to the AP and the SCP on the same die. It provides fundamental security guarantees and runtime services for the rest of the system (e.g.: trusted boot, measured boot, platform attestation, key management, and key derivation). -At power up RSS boots first from its private ROM code. It validates and loads +At power up RSE boots first from its private ROM code. It validates and loads its own images and the initial images of SCP and AP. When AP and SCP are released from reset and their initial code is loaded then they continue their -own boot process, which is the same as on non-RSS systems. Please refer to the -``RSS documentation`` [1]_ for more details about the RSS boot flow. +own boot process, which is the same as on non-RSE systems. Please refer to the +``RSE documentation`` [1]_ for more details about the RSE boot flow. -The last stage of the RSS firmware is a persistent, runtime component. Much +The last stage of the RSE firmware is a persistent, runtime component. Much like AP_BL31, this is a passive entity which has no periodical task to do and -just waits for external requests from other subsystems. RSS and other -subsystems can communicate with each other over message exchange. RSS waits +just waits for external requests from other subsystems. RSE and other +subsystems can communicate with each other over message exchange. RSE waits in idle for the incoming request, handles them, and sends a response then goes back to idle. -RSS communication layer +RSE communication layer ----------------------- -The communication between RSS and other subsystems are primarily relying on the -Message Handling Unit (MHU) module. The number of MHU interfaces between RSS +The communication between RSE and other subsystems are primarily relying on the +Message Handling Unit (MHU) module. The number of MHU interfaces between RSE and other cores is IMPDEF. Besides MHU other modules also could take part in -the communication. RSS is capable of mapping the AP memory to its address space. -Thereby either RSS core itself or a DMA engine if it is present, can move the -data between memory belonging to RSS or AP. In this way, a bigger amount of data +the communication. RSE is capable of mapping the AP memory to its address space. +Thereby either RSE core itself or a DMA engine if it is present, can move the +data between memory belonging to RSE or AP. In this way, a bigger amount of data can be transferred in a short time. The MHU comes in pairs. There is a sender and receiver side. They are connected to each other. An MHU interface consists of two pairs of MHUs, one sender and one receiver on both sides. Bidirectional communication is possible over an -interface. One pair provides message sending from AP to RSS and the other pair -from RSS to AP. The sender and receiver are connected via channels. There is an +interface. One pair provides message sending from AP to RSE and the other pair +from RSE to AP. The sender and receiver are connected via channels. There is an IMPDEF number of channels (e.g: 4-16) between a sender and a receiver module. -The RSS communication layer provides two ways for message exchange: +The RSE communication layer provides two ways for message exchange: - ``Embedded messaging``: The full message, including header and payload, are exchanged over the MHU channels. A channel is capable of delivering a single @@ -55,16 +55,16 @@ The RSS communication layer provides two ways for message exchange: - ``Pointer-access messaging``: The message header and the payload are separated and they are conveyed in different ways. The header is sent over the channels, similar to the embedded messaging but the payload is - copied over by RSS core (or by DMA) between the sender and the receiver. This + copied over by RSE core (or by DMA) between the sender and the receiver. This could be useful in the case of long messages because transaction time is less - compared to the embedded messaging mode. Small payloads are copied by the RSS + compared to the embedded messaging mode. Small payloads are copied by the RSE core because setting up DMA would require more CPU cycles. The payload is - either copied into an internal buffer or directly read-written by RSS. Actual - behavior depends on RSS setup, whether the partition supports memory-mapped + either copied into an internal buffer or directly read-written by RSE. Actual + behavior depends on RSE setup, whether the partition supports memory-mapped ``iovec``. Therefore, the sender must handle both cases and prevent access to - the memory, where payload data lives, while the RSS handles the request. + the memory, where payload data lives, while the RSE handles the request. -The RSS communication layer supports both ways of messaging in parallel. It is +The RSE communication layer supports both ways of messaging in parallel. It is decided at runtime based on the message size which way to transfer the message. .. code-block:: bash @@ -93,25 +93,25 @@ decided at runtime based on the message size which way to transfer the message. V | | | V V +----------------------------------------------+ | | +-------------------+ | |--+-+ | | - | RSS | | SRAM | + | RSE | | SRAM | | | | | +----------------------------------------------+ +-------------------+ .. Note:: - The RSS communication layer is not prepared for concurrent execution. The + The RSE communication layer is not prepared for concurrent execution. The current use case only requires message exchange during the boot phase. In the boot phase, only a single core is running and the rest of the cores are in reset. Message structure ^^^^^^^^^^^^^^^^^ -A description of the message format can be found in the ``RSS communication +A description of the message format can be found in the ``RSE communication design`` [2]_ document. Source files ^^^^^^^^^^^^ -- RSS comms: ``drivers/arm/rss`` +- RSE comms: ``drivers/arm/rse`` - MHU driver: ``drivers/arm/mhu`` @@ -119,29 +119,34 @@ API for communication over MHU ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ The API is defined in these header files: -- ``include/drivers/arm/rss_comms.h`` +- ``include/drivers/arm/rse_comms.h`` - ``include/drivers/arm/mhu.h`` -RSS provided runtime services +RSE provided runtime services ----------------------------- -RSS provides the following runtime services: +RSE provides the following runtime services: - ``Measured boot``: Securely store the firmware measurements which were computed during the boot process and the associated metadata (image description, measurement algorithm, etc.). More info on measured boot service - in RSS can be found in the ``measured_boot_integration_guide`` [3]_ . + in RSE can be found in the ``measured_boot_integration_guide`` [3]_ . - ``Delegated attestation``: Query the platform attestation token and derive a delegated attestation key. More info on the delegated attestation service - in RSS can be found in the ``delegated_attestation_integration_guide`` [4]_ . + in RSE can be found in the ``delegated_attestation_integration_guide`` [4]_ . - ``OTP assets management``: Public keys used by AP during the trusted boot - process can be requested from RSS. Furthermore, AP can request RSS to + process can be requested from RSE. Furthermore, AP can request RSE to increase a non-volatile counter. Please refer to the - ``RSS key management`` [5]_ document for more details. + ``RSE key management`` [5]_ document for more details. +- ``DICE Protection Environment``: Securely store the firmware measurements + which were computed during the boot process and the associated metadata. It is + also capable of representing the boot measurements in the form of a + certificate chain, which is queriable. Please refer to the + ``DICE Protection Environment (DPE)`` [8]_ document for more details. Runtime service API ^^^^^^^^^^^^^^^^^^^ -The RSS provided runtime services implement a PSA aligned API. The parameter +The RSE provided runtime services implement a PSA aligned API. The parameter encoding follows the PSA client protocol described in the ``Firmware Framework for M`` [6]_ document in chapter 4.4. The implementation is restricted to the static handle use case therefore only the ``psa_call`` API is @@ -168,7 +173,7 @@ Software and API layers | | V V +------------------------------------------------+ - | RSS communication protocol | + | RSE communication protocol | +------------------------------------------------+ | ^ | mhu_send_data() | mhu_receive_data() @@ -188,7 +193,7 @@ Software and API layers | V +------------------------------------------------+ - | MHU HW on RSS side | + | MHU HW on RSE side | +------------------------------------------------+ | ^ | IRQ | Register access @@ -204,17 +209,17 @@ Software and API layers +---------------+ +------------------------+ -RSS based Measured Boot +RSE based Measured Boot ----------------------- Measured Boot is the process of cryptographically measuring (computing the hash value of a binary) the code and critical data used at boot time. The measurement must be stored in a tamper-resistant way, so the security state -of the device can be attested later to an external party. RSS provides a runtime +of the device can be attested later to an external party. RSE provides a runtime service which is meant to store measurements and associated metadata alongside. Data is stored in internal SRAM which is only accessible by the secure runtime -firmware of RSS. Data is stored in so-called measurement slots. A platform has +firmware of RSE. Data is stored in so-called measurement slots. A platform has IMPDEF number of measurement slots. The measurement storage follows extend semantics. This means that measurements are not stored directly (as it was taken) instead they contribute to the current value of the measurement slot. @@ -236,7 +241,7 @@ Defined here: .. code-block:: c psa_status_t - rss_measured_boot_extend_measurement(uint8_t index, + rse_measured_boot_extend_measurement(uint8_t index, const uint8_t *signer_id, size_t signer_id_size, const uint8_t *version, @@ -291,27 +296,27 @@ multiple times: .. Note:: Extending multiple measurements in the same slot leads to some metadata - information loss. Since RSS is not constrained on special HW resources to + information loss. Since RSE is not constrained on special HW resources to store the measurements and metadata, therefore it is worth considering to store all of them one by one in distinct slots. However, they are one-by-one included in the platform attestation token. So, the number of distinct firmware image measurements has an impact on the size of the attestation token. -The allocation of the measurement slot among RSS, Root and Realm worlds is +The allocation of the measurement slot among RSE, Root and Realm worlds is platform dependent. The platform must provide an allocation of the measurement slot at build time. An example can be found in ``tf-a/plat/arm/board/tc/tc_bl1_measured_boot.c`` Furthermore, the memory, which holds the metadata is also statically allocated -in RSS memory. Some of the fields have a static value (measurement algorithm), +in RSE memory. Some of the fields have a static value (measurement algorithm), and some of the values have a dynamic value (measurement value) which is updated by the bootloaders when the firmware image is loaded and measured. The metadata structure is defined in -``include/drivers/measured_boot/rss/rss_measured_boot.h``. +``include/drivers/measured_boot/rse/rse_measured_boot.h``. .. code-block:: c - struct rss_mboot_metadata { + struct rse_mboot_metadata { unsigned int id; uint8_t slot; uint8_t signer_id[SIGNER_ID_MAX_SIZE]; @@ -328,24 +333,24 @@ Signer-ID API ^^^^^^^^^^^^^ This function calculates the hash of a public key (signer-ID) using the -``Measurement algorithm`` and stores it in the ``rss_mboot_metadata`` field +``Measurement algorithm`` and stores it in the ``rse_mboot_metadata`` field named ``signer_id``. Prior to calling this function, the caller must ensure that the ``signer_id`` field points to the zero-filled buffer. Defined here: -- ``include/drivers/measured_boot/rss/rss_measured_boot.h`` +- ``include/drivers/measured_boot/rse/rse_measured_boot.h`` .. code-block:: c - int rss_mboot_set_signer_id(struct rss_mboot_metadata *metadata_ptr, + int rse_mboot_set_signer_id(struct rse_mboot_metadata *metadata_ptr, const void *pk_oid, const void *pk_ptr, size_t pk_len) -- First parameter is the pointer to the ``rss_mboot_metadata`` structure. +- First parameter is the pointer to the ``rse_mboot_metadata`` structure. - Second parameter is the pointer to the key-OID of the public key. - Third parameter is the pointer to the public key buffer. - Fourth parameter is the size of public key buffer. @@ -355,16 +360,14 @@ Defined here: Build time config options ^^^^^^^^^^^^^^^^^^^^^^^^^ -- ``MEASURED_BOOT``: Enable measured boot. It depends on the platform - implementation whether RSS or TPM (or both) backend based measured boot is - enabled. -- ``MBOOT_RSS_HASH_ALG``: Determine the hash algorithm to measure the images. +- ``MEASURED_BOOT``: Enable measured boot. +- ``MBOOT_RSE_HASH_ALG``: Determine the hash algorithm to measure the images. The default value is sha-256. Measured boot flow ^^^^^^^^^^^^^^^^^^ -.. figure:: ../resources/diagrams/rss_measured_boot_flow.svg +.. figure:: ../resources/diagrams/rse_measured_boot_flow.svg :align: center Sample console log @@ -425,17 +428,13 @@ The detailed description of the delegated attestation service can be found in the ``Delegated Attestation Service Integration Guide`` [4]_ document. In the CCA use case, the Realm Management Monitor (RMM) relies on the delegated -attestation service of the RSS to get a realm attestation key and the CCA +attestation service of the RSE to get a realm attestation key and the CCA platform token. BL31 does not use the service for its own purpose, only calls -it on behalf of RMM. The access to MHU interface and thereby to RSS is +it on behalf of RMM. The access to MHU interface and thereby to RSE is restricted to BL31 only. Therefore, RMM does not have direct access, all calls need to go through BL31. The RMM dispatcher module of the BL31 is responsible for delivering the calls between the two parties. -.. Note:: - Currently the connection between the RMM dispatcher and the PSA/RSS layer - is not yet implemented. RMM dispatcher just returns hard coded data. - Delegated Attestation API ^^^^^^^^^^^^^^^^^^^^^^^^^ Defined here: @@ -445,7 +444,7 @@ Defined here: .. code-block:: c psa_status_t - rss_delegated_attest_get_delegated_key(uint8_t ecc_curve, + rse_delegated_attest_get_delegated_key(uint8_t ecc_curve, uint32_t key_bits, uint8_t *key_buf, size_t key_buf_size, @@ -453,7 +452,7 @@ Defined here: uint32_t hash_algo); psa_status_t - rss_delegated_attest_get_token(const uint8_t *dak_pub_hash, + rse_delegated_attest_get_token(const uint8_t *dak_pub_hash, size_t dak_pub_hash_size, uint8_t *token_buf, size_t token_buf_size, @@ -462,7 +461,7 @@ Defined here: Attestation flow ^^^^^^^^^^^^^^^^ -.. figure:: ../resources/diagrams/rss_attestation_flow.svg +.. figure:: ../resources/diagrams/rse_attestation_flow.svg :align: center Sample attestation token @@ -482,74 +481,101 @@ Binary format: INFO: Get platform token start INFO: Get platform token succeeds, len: 1086 INFO: Platform attestation token: - INFO: d2 84 44 a1 01 38 22 a0 59 03 d1 a9 0a 58 20 00 - INFO: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - INFO: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 19 - INFO: 01 00 58 21 01 cb 8c 79 f7 a0 0a 6c ce 12 66 f8 - INFO: 64 45 48 42 0e c5 10 bf 84 ee 22 18 b9 8f 11 04 - INFO: c7 22 31 9d fb 19 09 5c 58 20 aa aa aa aa aa aa - INFO: aa aa bb bb bb bb bb bb bb bb cc cc cc cc cc cc - INFO: cc cc dd dd dd dd dd dd dd dd 19 09 5b 19 30 00 - INFO: 19 09 5f 89 a4 05 58 20 bf e6 d8 6f 88 26 f4 ff - INFO: 97 fb 96 c4 e6 fb c4 99 3e 46 19 fc 56 5d a2 6a - INFO: df 34 c3 29 48 9a dc 38 04 67 31 2e 36 2e 30 2b - INFO: 30 01 64 52 54 5f 30 02 58 20 90 27 f2 46 ab 31 - INFO: 85 36 46 c4 d7 c6 60 ed 31 0d 3c f0 14 de f0 6c - INFO: 24 0b de b6 7a 84 fc 3f 5b b7 a4 05 58 20 b3 60 - INFO: ca f5 c9 8c 6b 94 2a 48 82 fa 9d 48 23 ef b1 66 - INFO: a9 ef 6a 6e 4a a3 7c 19 19 ed 1f cc c0 49 04 67 - INFO: 30 2e 30 2e 30 2b 30 01 64 52 54 5f 31 02 58 20 - INFO: 52 13 15 d4 9d b2 cf 54 e4 99 37 44 40 68 f0 70 - INFO: 7d 73 64 ae f7 08 14 b0 f7 82 ad c6 17 db a3 91 - INFO: a4 05 58 20 bf e6 d8 6f 88 26 f4 ff 97 fb 96 c4 - INFO: e6 fb c4 99 3e 46 19 fc 56 5d a2 6a df 34 c3 29 - INFO: 48 9a dc 38 04 67 31 2e 35 2e 30 2b 30 01 64 52 - INFO: 54 5f 32 02 58 20 8e 5d 64 7e 6f 6c c6 6f d4 4f - INFO: 54 b6 06 e5 47 9a cc 1b f3 7f ce 87 38 49 c5 92 - INFO: d8 2f 85 2e 85 42 a4 05 58 20 bf e6 d8 6f 88 26 - INFO: f4 ff 97 fb 96 c4 e6 fb c4 99 3e 46 19 fc 56 5d - INFO: a2 6a df 34 c3 29 48 9a dc 38 04 67 31 2e 35 2e - INFO: 30 2b 30 01 60 02 58 20 b8 01 65 a7 78 8b c6 59 - INFO: 42 8d 33 10 85 d1 49 0a dc 9e c3 ee df 85 1b d2 - INFO: f0 73 73 6a 0c 07 11 b8 a4 05 58 20 b0 f3 82 09 - INFO: 12 97 d8 3a 37 7a 72 47 1b ec 32 73 e9 92 32 e2 - INFO: 49 59 f6 5e 8b 4a 4a 46 d8 22 9a da 04 60 01 6a - INFO: 46 57 5f 43 4f 4e 46 49 47 00 02 58 20 21 9e a0 - INFO: 13 82 e6 d7 97 5a 11 13 a3 5f 45 39 68 b1 d9 a3 - INFO: ea 6a ab 84 23 3b 8c 06 16 98 20 ba b9 a4 05 58 - INFO: 20 b0 f3 82 09 12 97 d8 3a 37 7a 72 47 1b ec 32 - INFO: 73 e9 92 32 e2 49 59 f6 5e 8b 4a 4a 46 d8 22 9a - INFO: da 04 60 01 6d 54 42 5f 46 57 5f 43 4f 4e 46 49 - INFO: 47 00 02 58 20 41 39 f6 c2 10 84 53 c5 17 ae 9a - INFO: e5 be c1 20 7b cc 24 24 f3 9d 20 a8 fb c7 b3 10 - INFO: e3 ee af 1b 05 a4 05 58 20 b0 f3 82 09 12 97 d8 - INFO: 3a 37 7a 72 47 1b ec 32 73 e9 92 32 e2 49 59 f6 - INFO: 5e 8b 4a 4a 46 d8 22 9a da 04 60 01 65 42 4c 5f - INFO: 32 00 02 58 20 5c 96 20 e1 e3 3b 0f 2c eb c1 8e - INFO: 1a 02 a6 65 86 dd 34 97 a7 4c 98 13 bf 74 14 45 - INFO: 2d 30 28 05 c3 a4 05 58 20 b0 f3 82 09 12 97 d8 - INFO: 3a 37 7a 72 47 1b ec 32 73 e9 92 32 e2 49 59 f6 - INFO: 5e 8b 4a 4a 46 d8 22 9a da 04 60 01 6e 53 45 43 - INFO: 55 52 45 5f 52 54 5f 45 4c 33 00 02 58 20 f6 fb - INFO: 62 99 a5 0c df db 02 0b 72 5b 1c 0b 63 6e 94 ee - INFO: 66 50 56 3a 29 9c cb 38 f0 ec 59 99 d4 2e a4 05 - INFO: 58 20 b0 f3 82 09 12 97 d8 3a 37 7a 72 47 1b ec - INFO: 32 73 e9 92 32 e2 49 59 f6 5e 8b 4a 4a 46 d8 22 - INFO: 9a da 04 60 01 6a 48 57 5f 43 4f 4e 46 49 47 00 - INFO: 02 58 20 98 5d 87 21 84 06 33 9d c3 1f 91 f5 68 - INFO: 8d a0 5a f0 d7 7e 20 51 ce 3b f2 a5 c3 05 2e 3c - INFO: 8b 52 31 19 01 09 78 1c 68 74 74 70 3a 2f 2f 61 - INFO: 72 6d 2e 63 6f 6d 2f 43 43 41 2d 53 53 44 2f 31 - INFO: 2e 30 2e 30 19 09 62 71 6e 6f 74 2d 68 61 73 68 - INFO: 2d 65 78 74 65 6e 64 65 64 19 09 61 44 ef be ad - INFO: de 19 09 60 77 77 77 77 2e 74 72 75 73 74 65 64 - INFO: 66 69 72 6d 77 61 72 65 2e 6f 72 67 58 60 29 4e - INFO: 4a d3 98 1e 3b 70 9f b6 66 ed 47 33 0e 99 f0 b1 - INFO: c3 f2 bc b2 1d b0 ae 90 0c c4 82 ff a2 6f ae 45 - INFO: f6 87 09 4a 09 21 77 ec 36 1c 53 b8 a7 9b 8e f7 - INFO: 27 eb 7a 09 da 6f fb bf cb fd b3 e5 e9 36 91 b1 - INFO: 92 13 c1 30 16 b4 5c 49 5e c0 c1 b9 01 5c 88 2c - INFO: f8 2f 3e a4 a2 6d e4 9d 31 6a 06 f7 a7 73 + INFO: d2 84 44 a1 01 38 22 a0 59 05 81 a9 19 01 09 78 + INFO: 23 74 61 67 3a 61 72 6d 2e 63 6f 6d 2c 32 30 32 + INFO: 33 3a 63 63 61 5f 70 6c 61 74 66 6f 72 6d 23 31 + INFO: 2e 30 2e 30 0a 58 20 0d 22 e0 8a 98 46 90 58 48 + INFO: 63 18 28 34 89 bd b3 6f 09 db ef eb 18 64 df 43 + INFO: 3f a6 e5 4e a2 d7 11 19 09 5c 58 20 7f 45 4c 46 + INFO: 02 01 01 00 00 00 00 00 00 00 00 00 03 00 3e 00 + INFO: 01 00 00 00 50 58 00 00 00 00 00 00 19 01 00 58 + INFO: 21 01 07 06 05 04 03 02 01 00 0f 0e 0d 0c 0b 0a + INFO: 09 08 17 16 15 14 13 12 11 10 1f 1e 1d 1c 1b 1a + INFO: 19 18 19 09 61 44 cf cf cf cf 19 09 5b 19 30 03 + INFO: 19 09 62 67 73 68 61 2d 32 35 36 19 09 60 78 3a + INFO: 68 74 74 70 73 3a 2f 2f 76 65 72 61 69 73 6f 6e + INFO: 2e 65 78 61 6d 70 6c 65 2f 2e 77 65 6c 6c 2d 6b + INFO: 6e 6f 77 6e 2f 76 65 72 61 69 73 6f 6e 2f 76 65 + INFO: 72 69 66 69 63 61 74 69 6f 6e 19 09 5f 8d a4 01 + INFO: 69 52 53 45 5f 42 4c 31 5f 32 05 58 20 53 78 79 + INFO: 63 07 53 5d f3 ec 8d 8b 15 a2 e2 dc 56 41 41 9c + INFO: 3d 30 60 cf e3 22 38 c0 fa 97 3f 7a a3 02 58 20 + INFO: 9a 27 1f 2a 91 6b 0b 6e e6 ce cb 24 26 f0 b3 20 + INFO: 6e f0 74 57 8b e5 5d 9b c9 4f 6f 3f e3 ab 86 aa + INFO: 06 67 73 68 61 2d 32 35 36 a4 01 67 52 53 45 5f + INFO: 42 4c 32 05 58 20 53 78 79 63 07 53 5d f3 ec 8d + INFO: 8b 15 a2 e2 dc 56 41 41 9c 3d 30 60 cf e3 22 38 + INFO: c0 fa 97 3f 7a a3 02 58 20 53 c2 34 e5 e8 47 2b + INFO: 6a c5 1c 1a e1 ca b3 fe 06 fa d0 53 be b8 eb fd + INFO: 89 77 b0 10 65 5b fd d3 c3 06 67 73 68 61 2d 32 + INFO: 35 36 a4 01 65 52 53 45 5f 53 05 58 20 53 78 79 + INFO: 63 07 53 5d f3 ec 8d 8b 15 a2 e2 dc 56 41 41 9c + INFO: 3d 30 60 cf e3 22 38 c0 fa 97 3f 7a a3 02 58 20 + INFO: 11 21 cf cc d5 91 3f 0a 63 fe c4 0a 6f fd 44 ea + INFO: 64 f9 dc 13 5c 66 63 4b a0 01 d1 0b cf 43 02 a2 + INFO: 06 67 73 68 61 2d 32 35 36 a4 01 66 41 50 5f 42 + INFO: 4c 31 05 58 20 53 78 79 63 07 53 5d f3 ec 8d 8b + INFO: 15 a2 e2 dc 56 41 41 9c 3d 30 60 cf e3 22 38 c0 + INFO: fa 97 3f 7a a3 02 58 20 15 71 b5 ec 78 bd 68 51 + INFO: 2b f7 83 0b b6 a2 a4 4b 20 47 c7 df 57 bc e7 9e + INFO: b8 a1 c0 e5 be a0 a5 01 06 67 73 68 61 2d 32 35 + INFO: 36 a4 01 66 41 50 5f 42 4c 32 05 58 20 53 78 79 + INFO: 63 07 53 5d f3 ec 8d 8b 15 a2 e2 dc 56 41 41 9c + INFO: 3d 30 60 cf e3 22 38 c0 fa 97 3f 7a a3 02 58 20 + INFO: 10 15 9b af 26 2b 43 a9 2d 95 db 59 da e1 f7 2c + INFO: 64 51 27 30 16 61 e0 a3 ce 4e 38 b2 95 a9 7c 58 + INFO: 06 67 73 68 61 2d 32 35 36 a4 01 67 53 43 50 5f + INFO: 42 4c 31 05 58 20 53 78 79 63 07 53 5d f3 ec 8d + INFO: 8b 15 a2 e2 dc 56 41 41 9c 3d 30 60 cf e3 22 38 + INFO: c0 fa 97 3f 7a a3 02 58 20 10 12 2e 85 6b 3f cd + INFO: 49 f0 63 63 63 17 47 61 49 cb 73 0a 1a a1 cf aa + INFO: d8 18 55 2b 72 f5 6d 6f 68 06 67 73 68 61 2d 32 + INFO: 35 36 a4 01 67 53 43 50 5f 42 4c 32 05 58 20 f1 + INFO: 4b 49 87 90 4b cb 58 14 e4 45 9a 05 7e d4 d2 0f + INFO: 58 a6 33 15 22 88 a7 61 21 4d cd 28 78 0b 56 02 + INFO: 58 20 aa 67 a1 69 b0 bb a2 17 aa 0a a8 8a 65 34 + INFO: 69 20 c8 4c 42 44 7c 36 ba 5f 7e a6 5f 42 2c 1f + INFO: e5 d8 06 67 73 68 61 2d 32 35 36 a4 01 67 41 50 + INFO: 5f 42 4c 33 31 05 58 20 53 78 79 63 07 53 5d f3 + INFO: ec 8d 8b 15 a2 e2 dc 56 41 41 9c 3d 30 60 cf e3 + INFO: 22 38 c0 fa 97 3f 7a a3 02 58 20 2e 6d 31 a5 98 + INFO: 3a 91 25 1b fa e5 ae fa 1c 0a 19 d8 ba 3c f6 01 + INFO: d0 e8 a7 06 b4 cf a9 66 1a 6b 8a 06 67 73 68 61 + INFO: 2d 32 35 36 a4 01 63 52 4d 4d 05 58 20 53 78 79 + INFO: 63 07 53 5d f3 ec 8d 8b 15 a2 e2 dc 56 41 41 9c + INFO: 3d 30 60 cf e3 22 38 c0 fa 97 3f 7a a3 02 58 20 + INFO: a1 fb 50 e6 c8 6f ae 16 79 ef 33 51 29 6f d6 71 + INFO: 34 11 a0 8c f8 dd 17 90 a4 fd 05 fa e8 68 81 64 + INFO: 06 67 73 68 61 2d 32 35 36 a4 01 69 48 57 5f 43 + INFO: 4f 4e 46 49 47 05 58 20 53 78 79 63 07 53 5d f3 + INFO: ec 8d 8b 15 a2 e2 dc 56 41 41 9c 3d 30 60 cf e3 + INFO: 22 38 c0 fa 97 3f 7a a3 02 58 20 1a 25 24 02 97 + INFO: 2f 60 57 fa 53 cc 17 2b 52 b9 ff ca 69 8e 18 31 + INFO: 1f ac d0 f3 b0 6e ca ae f7 9e 17 06 67 73 68 61 + INFO: 2d 32 35 36 a4 01 69 46 57 5f 43 4f 4e 46 49 47 + INFO: 05 58 20 53 78 79 63 07 53 5d f3 ec 8d 8b 15 a2 + INFO: e2 dc 56 41 41 9c 3d 30 60 cf e3 22 38 c0 fa 97 + INFO: 3f 7a a3 02 58 20 9a 92 ad bc 0c ee 38 ef 65 8c + INFO: 71 ce 1b 1b f8 c6 56 68 f1 66 bf b2 13 64 4c 89 + INFO: 5c cb 1a d0 7a 25 06 67 73 68 61 2d 32 35 36 a4 + INFO: 01 6c 54 42 5f 46 57 5f 43 4f 4e 46 49 47 05 58 + INFO: 20 53 78 79 63 07 53 5d f3 ec 8d 8b 15 a2 e2 dc + INFO: 56 41 41 9c 3d 30 60 cf e3 22 38 c0 fa 97 3f 7a + INFO: a3 02 58 20 23 89 03 18 0c c1 04 ec 2c 5d 8b 3f + INFO: 20 c5 bc 61 b3 89 ec 0a 96 7d f8 cc 20 8c dc 7c + INFO: d4 54 17 4f 06 67 73 68 61 2d 32 35 36 a4 01 6d + INFO: 53 4f 43 5f 46 57 5f 43 4f 4e 46 49 47 05 58 20 + INFO: 53 78 79 63 07 53 5d f3 ec 8d 8b 15 a2 e2 dc 56 + INFO: 41 41 9c 3d 30 60 cf e3 22 38 c0 fa 97 3f 7a a3 + INFO: 02 58 20 e6 c2 1e 8d 26 0f e7 18 82 de bd b3 39 + INFO: d2 40 2a 2c a7 64 85 29 bc 23 03 f4 86 49 bc e0 + INFO: 38 00 17 06 67 73 68 61 2d 32 35 36 58 60 31 d0 + INFO: 4d 52 cc de 95 2c 1e 32 cb a1 81 88 5a 40 b8 cc + INFO: 38 e0 52 8c 1e 89 58 98 07 64 2a a5 e3 f2 bc 37 + INFO: f9 53 74 50 6b ff 4d 2e 4b e7 06 3c 4d 72 41 92 + INFO: 70 c7 22 e8 d4 d9 3e e8 b6 c9 fa ce 3b 43 c9 76 + INFO: 1a 49 94 1a b6 f3 8f fd ff 49 6a d4 63 b4 cb fa + INFO: 11 d8 3e 23 e3 1f 7f 62 32 9d e3 0c 1c c8 INFO: DELEGATED ATTEST TEST END JSON format: @@ -557,93 +583,174 @@ JSON format: .. code-block:: JSON { - "CCA_PLATFORM_CHALLENGE": "b'0000000000000000000000000000000000000000000000000000000000000000'", - "CCA_PLATFORM_INSTANCE_ID": "b'01CB8C79F7A00A6CCE1266F8644548420EC510BF84EE2218B98F1104C722319DFB'", - "CCA_PLATFORM_IMPLEMENTATION_ID": "b'AAAAAAAAAAAAAAAABBBBBBBBBBBBBBBBCCCCCCCCCCCCCCCCDDDDDDDDDDDDDDDD'", - "CCA_PLATFORM_LIFECYCLE": "secured_3000", + "CCA_ATTESTATION_PROFILE": "tag:arm.com,2023:cca_platform#1.0.0", + "CCA_PLATFORM_CHALLENGE": "b'0D22E08A98469058486318283489BDB36F09DBEFEB1864DF433FA6E54EA2D711'", + "CCA_PLATFORM_IMPLEMENTATION_ID": "b'7F454C4602010100000000000000000003003E00010000005058000000000000'", + "CCA_PLATFORM_INSTANCE_ID": "b'0107060504030201000F0E0D0C0B0A090817161514131211101F1E1D1C1B1A1918'", + "CCA_PLATFORM_CONFIG": "b'CFCFCFCF'", + "CCA_PLATFORM_LIFECYCLE": "secured_3003", + "CCA_PLATFORM_HASH_ALGO_ID": "sha-256", + "CCA_PLATFORM_VERIFICATION_SERVICE": "https://veraison.example/.well-known/veraison/verification", "CCA_PLATFORM_SW_COMPONENTS": [ { - "SIGNER_ID": "b'BFE6D86F8826F4FF97FB96C4E6FBC4993E4619FC565DA26ADF34C329489ADC38'", - "SW_COMPONENT_VERSION": "1.6.0+0", - "SW_COMPONENT_TYPE": "RT_0", - "MEASUREMENT_VALUE": "b'9027F246AB31853646C4D7C660ED310D3CF014DEF06C240BDEB67A84FC3F5BB7'" + "SW_COMPONENT_TYPE": "RSE_BL1_2", + "SIGNER_ID": "b'5378796307535DF3EC8D8B15A2E2DC5641419C3D3060CFE32238C0FA973F7AA3'", + "MEASUREMENT_VALUE": "b'9A271F2A916B0B6EE6CECB2426F0B3206EF074578BE55D9BC94F6F3FE3AB86AA'", + "CCA_SW_COMPONENT_HASH_ID": "sha-256" + }, + { + "SW_COMPONENT_TYPE": "RSE_BL2", + "SIGNER_ID": "b'5378796307535DF3EC8D8B15A2E2DC5641419C3D3060CFE32238C0FA973F7AA3'", + "MEASUREMENT_VALUE": "b'53C234E5E8472B6AC51C1AE1CAB3FE06FAD053BEB8EBFD8977B010655BFDD3C3'", + "CCA_SW_COMPONENT_HASH_ID": "sha-256" }, { - "SIGNER_ID": "b'B360CAF5C98C6B942A4882FA9D4823EFB166A9EF6A6E4AA37C1919ED1FCCC049'", - "SW_COMPONENT_VERSION": "0.0.0+0", - "SW_COMPONENT_TYPE": "RT_1", - "MEASUREMENT_VALUE": "b'521315D49DB2CF54E49937444068F0707D7364AEF70814B0F782ADC617DBA391'" + "SW_COMPONENT_TYPE": "RSE_S", + "SIGNER_ID": "b'5378796307535DF3EC8D8B15A2E2DC5641419C3D3060CFE32238C0FA973F7AA3'", + "MEASUREMENT_VALUE": "b'1121CFCCD5913F0A63FEC40A6FFD44EA64F9DC135C66634BA001D10BCF4302A2'", + "CCA_SW_COMPONENT_HASH_ID": "sha-256" }, { - "SIGNER_ID": "b'BFE6D86F8826F4FF97FB96C4E6FBC4993E4619FC565DA26ADF34C329489ADC38'", - "SW_COMPONENT_VERSION": "1.5.0+0", - "SW_COMPONENT_TYPE": "RT_2", - "MEASUREMENT_VALUE": "b'8E5D647E6F6CC66FD44F54B606E5479ACC1BF37FCE873849C592D82F852E8542'" + "SW_COMPONENT_TYPE": "AP_BL1", + "SIGNER_ID": "b'5378796307535DF3EC8D8B15A2E2DC5641419C3D3060CFE32238C0FA973F7AA3'", + "MEASUREMENT_VALUE": "b'1571B5EC78BD68512BF7830BB6A2A44B2047C7DF57BCE79EB8A1C0E5BEA0A501'", + "CCA_SW_COMPONENT_HASH_ID": "sha-256" }, { - "SIGNER_ID": "b'BFE6D86F8826F4FF97FB96C4E6FBC4993E4619FC565DA26ADF34C329489ADC38'", - "SW_COMPONENT_VERSION": "1.5.0+0", - "SW_COMPONENT_TYPE": "", - "MEASUREMENT_VALUE": "b'B80165A7788BC659428D331085D1490ADC9EC3EEDF851BD2F073736A0C0711B8'" + "SW_COMPONENT_TYPE": "AP_BL2", + "SIGNER_ID": "b'5378796307535DF3EC8D8B15A2E2DC5641419C3D3060CFE32238C0FA973F7AA3'", + "MEASUREMENT_VALUE": "b'10159BAF262B43A92D95DB59DAE1F72C645127301661E0A3CE4E38B295A97C58'", + "CCA_SW_COMPONENT_HASH_ID": "sha-256" }, { - "SIGNER_ID": "b'b0f382091297d83a377a72471bec3273e99232e24959f65e8b4a4a46d8229ada'", - "SW_COMPONENT_VERSION": "", - "SW_COMPONENT_TYPE": "FW_CONFIG\u0000", - "MEASUREMENT_VALUE": "b'219EA01382E6D7975A1113A35F453968B1D9A3EA6AAB84233B8C06169820BAB9'" + "SW_COMPONENT_TYPE": "SCP_BL1", + "SIGNER_ID": "b'5378796307535DF3EC8D8B15A2E2DC5641419C3D3060CFE32238C0FA973F7AA3'", + "MEASUREMENT_VALUE": "b'10122E856B3FCD49F063636317476149CB730A1AA1CFAAD818552B72F56D6F68'", + "CCA_SW_COMPONENT_HASH_ID": "sha-256" }, { - "SIGNER_ID": "b'b0f382091297d83a377a72471bec3273e99232e24959f65e8b4a4a46d8229ada'", - "SW_COMPONENT_VERSION": "", - "SW_COMPONENT_TYPE": "TB_FW_CONFIG\u0000", - "MEASUREMENT_VALUE": "b'4139F6C2108453C517AE9AE5BEC1207BCC2424F39D20A8FBC7B310E3EEAF1B05'" + "SW_COMPONENT_TYPE": "SCP_BL2", + "SIGNER_ID": "b'F14B4987904BCB5814E4459A057ED4D20F58A633152288A761214DCD28780B56'", + "MEASUREMENT_VALUE": "b'AA67A169B0BBA217AA0AA88A65346920C84C42447C36BA5F7EA65F422C1FE5D8'", + "CCA_SW_COMPONENT_HASH_ID": "sha-256" }, { - "SIGNER_ID": "b'b0f382091297d83a377a72471bec3273e99232e24959f65e8b4a4a46d8229ada'", - "SW_COMPONENT_VERSION": "", - "SW_COMPONENT_TYPE": "BL_2\u0000", - "MEASUREMENT_VALUE": "b'5C9620E1E33B0F2CEBC18E1A02A66586DD3497A74C9813BF7414452D302805C3'" + "SW_COMPONENT_TYPE": "AP_BL31", + "SIGNER_ID": "b'5378796307535DF3EC8D8B15A2E2DC5641419C3D3060CFE32238C0FA973F7AA3'", + "MEASUREMENT_VALUE": "b'2E6D31A5983A91251BFAE5AEFA1C0A19D8BA3CF601D0E8A706B4CFA9661A6B8A'", + "CCA_SW_COMPONENT_HASH_ID": "sha-256" }, { - "SIGNER_ID": "b'b0f382091297d83a377a72471bec3273e99232e24959f65e8b4a4a46d8229ada'", - "SW_COMPONENT_VERSION": "", - "SW_COMPONENT_TYPE": "SECURE_RT_EL3\u0000", - "MEASUREMENT_VALUE": "b'F6FB6299A50CDFDB020B725B1C0B636E94EE6650563A299CCB38F0EC5999D42E'" + "SW_COMPONENT_TYPE": "RMM", + "SIGNER_ID": "b'5378796307535DF3EC8D8B15A2E2DC5641419C3D3060CFE32238C0FA973F7AA3'", + "MEASUREMENT_VALUE": "b'A1FB50E6C86FAE1679EF3351296FD6713411A08CF8DD1790A4FD05FAE8688164'", + "CCA_SW_COMPONENT_HASH_ID": "sha-256" }, { - "SIGNER_ID": "b'b0f382091297d83a377a72471bec3273e99232e24959f65e8b4a4a46d8229ada'", - "SW_COMPONENT_VERSION": "", - "SW_COMPONENT_TYPE": "HW_CONFIG\u0000", - "MEASUREMENT_VALUE": "b'985D87218406339DC31F91F5688DA05AF0D77E2051CE3BF2A5C3052E3C8B5231'" + "SW_COMPONENT_TYPE": "HW_CONFIG", + "SIGNER_ID": "b'5378796307535DF3EC8D8B15A2E2DC5641419C3D3060CFE32238C0FA973F7AA3'", + "MEASUREMENT_VALUE": "b'1A252402972F6057FA53CC172B52B9FFCA698E18311FACD0F3B06ECAAEF79E17'", + "CCA_SW_COMPONENT_HASH_ID": "sha-256" + }, + { + "SW_COMPONENT_TYPE": "FW_CONFIG", + "SIGNER_ID": "b'5378796307535DF3EC8D8B15A2E2DC5641419C3D3060CFE32238C0FA973F7AA3'", + "MEASUREMENT_VALUE": "b'9A92ADBC0CEE38EF658C71CE1B1BF8C65668F166BFB213644C895CCB1AD07A25'", + "CCA_SW_COMPONENT_HASH_ID": "sha-256" + }, + { + "SW_COMPONENT_TYPE": "TB_FW_CONFIG", + "SIGNER_ID": "b'5378796307535DF3EC8D8B15A2E2DC5641419C3D3060CFE32238C0FA973F7AA3'", + "MEASUREMENT_VALUE": "b'238903180CC104EC2C5D8B3F20C5BC61B389EC0A967DF8CC208CDC7CD454174F'", + "CCA_SW_COMPONENT_HASH_ID": "sha-256" + }, + { + "SW_COMPONENT_TYPE": "SOC_FW_CONFIG", + "SIGNER_ID": "b'5378796307535DF3EC8D8B15A2E2DC5641419C3D3060CFE32238C0FA973F7AA3'", + "MEASUREMENT_VALUE": "b'E6C21E8D260FE71882DEBDB339D2402A2CA7648529BC2303F48649BCE0380017'", + "CCA_SW_COMPONENT_HASH_ID": "sha-256" } - ], - "CCA_ATTESTATION_PROFILE": "http://arm.com/CCA-SSD/1.0.0", - "CCA_PLATFORM_HASH_ALGO_ID": "not-hash-extended", - "CCA_PLATFORM_CONFIG": "b'EFBEADDE'", - "CCA_PLATFORM_VERIFICATION_SERVICE": "www.trustedfirmware.org" + ] } -RSS OTP Assets Management +RSE based DICE Protection Environment +------------------------------------- + +The ``DICE Protection Environment (DPE)`` [8]_ service makes it possible to +execute |DICE| commands within an isolated execution environment. It provides +clients with an interface to send DICE commands, encoded as CBOR objects, +that act on opaque context handles. The |DPE| service performs |DICE| +derivations and certification on its internal contexts, without exposing the +|DICE| secrets (private keys and CDIs) outside of the isolated execution +environment. + +|DPE| API +^^^^^^^^^ + +Defined here: + +- ``include/lib/psa/dice_protection_environment.h`` + +.. code-block:: c + + dpe_error_t + dpe_derive_context(int context_handle, + uint32_t cert_id, + bool retain_parent_context, + bool allow_new_context_to_derive, + bool create_certificate, + const DiceInputValues *dice_inputs, + int32_t target_locality, + bool return_certificate, + bool allow_new_context_to_export, + bool export_cdi, + int *new_context_handle, + int *new_parent_context_handle, + uint8_t *new_certificate_buf, + size_t new_certificate_buf_size, + size_t *new_certificate_actual_size, + uint8_t *exported_cdi_buf, + size_t exported_cdi_buf_size, + size_t *exported_cdi_actual_size); + +Build time config options +^^^^^^^^^^^^^^^^^^^^^^^^^ + +- ``MEASURED_BOOT``: Enable measured boot. +- ``DICE_PROTECTION_ENVIRONMENT``: Boolean flag to specify the measured boot + backend when |RSE| based ``MEASURED_BOOT`` is enabled. The default value is + ``0``. When set to ``1`` then measurements and additional metadata collected + during the measured boot process are sent to the |DPE| for storage and + processing. +- ``DPE_ALG_ID``: Determine the hash algorithm to measure the images. The + default value is sha-256. + +Example certificate chain +^^^^^^^^^^^^^^^^^^^^^^^^^ + +``plat/arm/board/tc/tc_dpe.h`` + +RSE OTP Assets Management ------------------------- -RSS provides access for AP to assets in OTP, which include keys for image +RSE provides access for AP to assets in OTP, which include keys for image signature verification and non-volatile counters for anti-rollback protection. Non-Volatile Counter API ^^^^^^^^^^^^^^^^^^^^^^^^ -AP/RSS interface for retrieving and incrementing non-volatile counters API is +AP/RSE interface for retrieving and incrementing non-volatile counters API is as follows. Defined here: -- ``include/lib/psa/rss_platform_api.h`` +- ``include/lib/psa/rse_platform_api.h`` .. code-block:: c - psa_status_t rss_platform_nv_counter_increment(uint32_t counter_id) + psa_status_t rse_platform_nv_counter_increment(uint32_t counter_id) - psa_status_t rss_platform_nv_counter_read(uint32_t counter_id, + psa_status_t rse_platform_nv_counter_read(uint32_t counter_id, uint32_t size, uint8_t *val) Through this service, we can read/increment any of the 3 non-volatile @@ -656,15 +763,15 @@ counters used on an Arm CCA platform: Public Key API ^^^^^^^^^^^^^^ -AP/RSS interface for reading the ROTPK is as follows. +AP/RSE interface for reading the ROTPK is as follows. Defined here: -- ``include/lib/psa/rss_platform_api.h`` +- ``include/lib/psa/rse_platform_api.h`` .. code-block:: c - psa_status_t rss_platform_key_read(enum rss_key_id_builtin_t key, + psa_status_t rse_platform_key_read(enum rse_key_id_builtin_t key, uint8_t *data, size_t data_size, size_t *data_length) Through this service, we can read any of the 3 ROTPKs used on an @@ -677,14 +784,16 @@ Arm CCA platform: References ---------- -.. [1] https://tf-m-user-guide.trustedfirmware.org/platform/arm/rss/readme.html -.. [2] https://tf-m-user-guide.trustedfirmware.org/platform/arm/rss/rss_comms.html -.. [3] https://git.trustedfirmware.org/TF-M/tf-m-extras.git/tree/partitions/measured_boot/measured_boot_integration_guide.rst -.. [4] https://git.trustedfirmware.org/TF-M/tf-m-extras.git/tree/partitions/delegated_attestation/delegated_attest_integration_guide.rst -.. [5] https://tf-m-user-guide.trustedfirmware.org/platform/arm/rss/rss_key_management.html +.. [1] https://trustedfirmware-m.readthedocs.io/en/latest/platform/arm/rse/index.html +.. [2] https://trustedfirmware-m.readthedocs.io/en/latest/platform/arm/rse/rse_comms.html +.. [3] https://trustedfirmware-m.readthedocs.io/projects/tf-m-extras/en/latest/partitions/measured_boot_integration_guide.html +.. [4] https://trustedfirmware-m.readthedocs.io/projects/tf-m-extras/en/latest/partitions/delegated_attestation/delegated_attest_integration_guide.html +.. [5] https://trustedfirmware-m.readthedocs.io/en/latest/platform/arm/rse/rse_key_management.html .. [6] https://developer.arm.com/-/media/Files/pdf/PlatformSecurityArchitecture/Architect/DEN0063-PSA_Firmware_Framework-1.0.0-2.pdf?revision=2d1429fa-4b5b-461a-a60e-4ef3d8f7f4b4&hash=3BFD6F3E687F324672F18E5BE9F08EDC48087C93 .. [7] https://developer.arm.com/documentation/DEN0096/A_a/?lang=en +.. [8] https://trustedfirmware-m.readthedocs.io/projects/tf-m-extras/en/latest/partitions/dice_protection_environment/dice_protection_environment.html -------------- -*Copyright (c) 2023, Arm Limited. All rights reserved.* +*Copyright (c) 2023-2024, Arm Limited. All rights reserved.* +*Copyright (c) 2024, Linaro Limited. All rights reserved.* diff --git a/docs/getting_started/build-internals.rst b/docs/getting_started/build-internals.rst index 390c3671..c43f4e98 100644 --- a/docs/getting_started/build-internals.rst +++ b/docs/getting_started/build-internals.rst @@ -19,3 +19,11 @@ depends on certain options to be enabled or disabled. ``HANDLE_EA_EL3_FIRST_NS`` is set. Currently only NS world routes EA to EL3 but in future when Secure/Realm wants to use FFH then they can introduce new macros which will enable this option implicitly. + +- ``OPTEE_SP_FW_CONFIG``: DTC build flag to include OP-TEE as SP in + tb_fw_config device tree. This flag is defined only when + ``ARM_SPMC_MANIFEST_DTS`` manifest file name contains pattern optee_sp. + +- ``TRUSTY_SP_FW_CONFIG``: DTC build flag to include Trusty as SP in + tb_fw_config device tree. This flag is defined only when + ``ARM_SPMC_MANIFEST_DTS`` manifest file name contains pattern trusty_sp. diff --git a/docs/getting_started/build-options.rst b/docs/getting_started/build-options.rst index 80baf9cd..ab0b94d5 100644 --- a/docs/getting_started/build-options.rst +++ b/docs/getting_started/build-options.rst @@ -23,8 +23,9 @@ Common build options is expected to contain a makefile called ``<aarch32_sp-value>.mk``. - ``AMU_RESTRICT_COUNTERS``: Register reads to the group 1 counters will return - zero at all but the highest implemented exception level. Reads from the - memory mapped view are unaffected by this control. + zero at all but the highest implemented exception level. External + memory-mapped debug accesses are unaffected by this control. + The default value is 1 for all platforms. - ``ARCH`` : Choose the target build architecture for TF-A. It can take either ``aarch64`` or ``aarch32`` as values. By default, it is defined to @@ -98,6 +99,10 @@ Common build options file that contains the BL32 private key in PEM format or a PKCS11 URI. If ``SAVE_KEYS=1``, only a file is accepted and it will be used to save the key. +- ``RMM``: This is an optional build option used when ``ENABLE_RME`` is set. + It specifies the path to RMM binary for the ``fip`` target. If the RMM option + is not specified, TF-A builds the TRP to load and run at R-EL2. + - ``BL33``: Path to BL33 image in the host file system. This is mandatory for ``fip`` target in case TF-A BL2 is used. @@ -180,26 +185,32 @@ Common build options registers to be included when saving and restoring the CPU context. Default is 0. -- ``CTX_INCLUDE_MTE_REGS``: Numeric value to include Memory Tagging Extension - registers in cpu context. This must be enabled, if the platform wants to use - this feature in the Secure world and MTE is enabled at ELX. This flag can - take values 0 to 2, to align with the ``FEATURE_DETECTION`` mechanism. - Default value is 0. +- ``CTX_INCLUDE_MPAM_REGS``: Boolean option that, when set to 1, will cause the + Memory System Resource Partitioning and Monitoring (MPAM) + registers to be included when saving and restoring the CPU context. + Default is '0'. - ``CTX_INCLUDE_NEVE_REGS``: Numeric value, when set will cause the Armv8.4-NV registers to be saved/restored when entering/exiting an EL2 execution context. This flag can take values 0 to 2, to align with the - ``FEATURE_DETECTION`` mechanism. Default value is 0. + ``ENABLE_FEAT`` mechanism. Default value is 0. - ``CTX_INCLUDE_PAUTH_REGS``: Numeric value to enable the Pointer Authentication for Secure world. This will cause the ARMv8.3-PAuth registers to be included when saving and restoring the CPU context as part of world - switch. This flag can take values 0 to 2, to align with ``FEATURE_DETECTION`` + switch. This flag can take values 0 to 2, to align with ``ENABLE_FEAT`` mechanism. Default value is 0. Note that Pointer Authentication is enabled for Non-secure world irrespective of the value of this flag if the CPU supports it. +- ``CTX_INCLUDE_SVE_REGS``: Boolean option that, when set to 1, will cause the + SVE registers to be included when saving and restoring the CPU context. Note + that this build option requires ``ENABLE_SVE_FOR_SWD`` to be enabled. In + general, it is recommended to perform SVE context management in lower ELs + and skip in EL3 due to the additional cost of maintaining large data + structures to track the SVE state. Hence, the default value is 0. + - ``DEBUG``: Chooses between a debug and release build. It can take either 0 (release) or 1 (debug) as values. 0 is the default. @@ -215,7 +226,7 @@ Common build options - ``DISABLE_MTPMU``: Numeric option to disable ``FEAT_MTPMU`` (Multi Threaded PMU). ``FEAT_MTPMU`` is an optional feature available on Armv8.6 onwards. - This flag can take values 0 to 2, to align with the ``FEATURE_DETECTION`` + This flag can take values 0 to 2, to align with the ``ENABLE_FEAT`` mechanism. Default is ``0``. - ``DYN_DISABLE_AUTH``: Provides the capability to dynamically disable Trusted @@ -231,6 +242,13 @@ Common build options contributions are still expected to build with ``W=0`` and ``E=1`` (the default). +- ``EARLY_CONSOLE``: This option is used to enable early traces before default + console is properly setup. It introduces EARLY_* traces macros, that will + use the non-EARLY traces macros if the flag is enabled, or do nothing + otherwise. To use this feature, platforms will have to create the function + plat_setup_early_console(). + Default is 0 (disabled) + - ``EL3_PAYLOAD_BASE``: This option enables booting an EL3 payload instead of the normal boot flow. It must specify the entry point address of the EL3 payload. Please refer to the "Booting an EL3 payload" section for more @@ -262,9 +280,35 @@ Common build options builds, but this behaviour can be overridden in each platform's Makefile or in the build command line. +- ``ENABLE_FEAT`` + The Arm architecture defines several architecture extension features, + named FEAT_xxx in the architecure manual. Some of those features require + setup code in higher exception levels, other features might be used by TF-A + code itself. + Most of the feature flags defined in the TF-A build system permit to take + the values 0, 1 or 2, with the following meaning: + + :: + + ENABLE_FEAT_* = 0: Feature is disabled statically at compile time. + ENABLE_FEAT_* = 1: Feature is enabled unconditionally at compile time. + ENABLE_FEAT_* = 2: Feature is enabled, but checked at runtime. + + When setting the flag to 0, the feature is disabled during compilation, + and the compiler's optimisation stage and the linker will try to remove + as much of this code as possible. + If it is defined to 1, the code will use the feature unconditionally, so the + CPU is expected to support that feature. The FEATURE_DETECTION debug + feature, if enabled, will verify this. + If the feature flag is set to 2, support for the feature will be compiled + in, but its existence will be checked at runtime, so it works on CPUs with + or without the feature. This is mostly useful for platforms which either + support multiple different CPUs, or where the CPU is configured at runtime, + like in emulators. + - ``ENABLE_FEAT_AMU``: Numeric value to enable Activity Monitor Unit extensions. This flag can take the values 0 to 2, to align with the - ``FEATURE_DETECTION`` mechanism. This is an optional architectural feature + ``ENABLE_FEAT`` mechanism. This is an optional architectural feature available on v8.4 onwards. Some v8.2 implementations also implement an AMU and this option can be used to enable this feature on those systems as well. This flag can take the values 0 to 2, the default is 0. @@ -272,65 +316,83 @@ Common build options - ``ENABLE_FEAT_AMUv1p1``: Numeric value to enable the ``FEAT_AMUv1p1`` extension. ``FEAT_AMUv1p1`` is an optional feature available on Arm v8.6 onwards. This flag can take the values 0 to 2, to align with the - ``FEATURE_DETECTION`` mechanism. Default value is ``0``. + ``ENABLE_FEAT`` mechanism. Default value is ``0``. - ``ENABLE_FEAT_CSV2_2``: Numeric value to enable the ``FEAT_CSV2_2`` extension. It allows access to the SCXTNUM_EL2 (Software Context Number) register during EL2 context save/restore operations. ``FEAT_CSV2_2`` is an optional feature available on Arm v8.0 onwards. This flag can take values - 0 to 2, to align with the ``FEATURE_DETECTION`` mechanism. + 0 to 2, to align with the ``ENABLE_FEAT`` mechanism. Default value is ``0``. +- ``ENABLE_FEAT_CSV2_3``: Numeric value to enable support for ``FEAT_CSV2_3`` + extension. This feature is supported in AArch64 state only and is an optional + feature available in Arm v8.0 implementations. + ``FEAT_CSV2_3`` implies the implementation of ``FEAT_CSV2_2``. + The flag can take values 0 to 2, to align with the ``ENABLE_FEAT`` + mechanism. Default value is ``0``. + +- ``ENABLE_FEAT_DEBUGV8P9``: Numeric value to enable ``FEAT_DEBUGV8P9`` + extension which allows the ability to implement more than 16 breakpoints + and/or watchpoints. This feature is mandatory from v8.9 and is optional + from v8.8. This flag can take the values of 0 to 2, to align with the + ``ENABLE_FEAT`` mechanism. Default value is ``0``. + - ``ENABLE_FEAT_DIT``: Numeric value to enable ``FEAT_DIT`` (Data Independent Timing) extension. It allows setting the ``DIT`` bit of PSTATE in EL3. ``FEAT_DIT`` is a mandatory architectural feature and is enabled from v8.4 and upwards. This flag can take the values 0 to 2, to align with the - ``FEATURE_DETECTION`` mechanism. Default value is ``0``. + ``ENABLE_FEAT`` mechanism. Default value is ``0``. - ``ENABLE_FEAT_ECV``: Numeric value to enable support for the Enhanced Counter Virtualization feature, allowing for access to the CNTPOFF_EL2 (Counter-timer Physical Offset register) during EL2 to EL3 context save/restore operations. Its a mandatory architectural feature and is enabled from v8.6 and upwards. - This flag can take the values 0 to 2, to align with the ``FEATURE_DETECTION`` + This flag can take the values 0 to 2, to align with the ``ENABLE_FEAT`` mechanism. Default value is ``0``. - ``ENABLE_FEAT_FGT``: Numeric value to enable support for FGT (Fine Grain Traps) feature allowing for access to the HDFGRTR_EL2 (Hypervisor Debug Fine-Grained Read Trap Register) during EL2 to EL3 context save/restore operations. Its a mandatory architectural feature and is enabled from v8.6 and upwards. - This flag can take the values 0 to 2, to align with the ``FEATURE_DETECTION`` + This flag can take the values 0 to 2, to align with the ``ENABLE_FEAT`` + mechanism. Default value is ``0``. + +- ``ENABLE_FEAT_FGT2``: Numeric value to enable support for FGT2 + (Fine Grain Traps 2) feature allowing for access to Fine-grained trap 2 registers + during EL2 to EL3 context save/restore operations. + Its an optional architectural feature and is available from v8.8 and upwards. + This flag can take the values 0 to 2, to align with the ``ENABLE_FEAT`` mechanism. Default value is ``0``. - ``ENABLE_FEAT_HCX``: Numeric value to set the bit SCR_EL3.HXEn in EL3 to allow access to HCRX_EL2 (extended hypervisor control register) from EL2 as well as adding HCRX_EL2 to the EL2 context save/restore operations. Its a mandatory architectural feature and is enabled from v8.7 and upwards. This - flag can take the values 0 to 2, to align with the ``FEATURE_DETECTION`` + flag can take the values 0 to 2, to align with the ``ENABLE_FEAT`` mechanism. Default value is ``0``. -- ``ENABLE_FEAT_MTE_PERM``: Numeric value to enable support for - ``FEAT_MTE_PERM``, which introduces Allocation tag access permission to - memory region attributes. ``FEAT_MTE_PERM`` is a optional architectural - feature available from v8.9 and upwards. This flag can take the values 0 to - 2, to align with the ``FEATURE_DETECTION`` mechanism. Default value is - ``0``. +- ``ENABLE_FEAT_MTE2``: Numeric value to enable Memory Tagging Extension2 + if the platform wants to use this feature and MTE2 is enabled at ELX. + This flag can take values 0 to 2, to align with the ``ENABLE_FEAT`` + mechanism. Default value is ``0``. - ``ENABLE_FEAT_PAN``: Numeric value to enable the ``FEAT_PAN`` (Privileged Access Never) extension. ``FEAT_PAN`` adds a bit to PSTATE, generating a permission fault for any privileged data access from EL1/EL2 to virtual memory address, accessible at EL0, provided (HCR_EL2.E2H=1). It is a mandatory architectural feature and is enabled from v8.1 and upwards. This - flag can take values 0 to 2, to align with the ``FEATURE_DETECTION`` + flag can take values 0 to 2, to align with the ``ENABLE_FEAT`` mechanism. Default value is ``0``. - ``ENABLE_FEAT_RNG``: Numeric value to enable the ``FEAT_RNG`` extension. ``FEAT_RNG`` is an optional feature available on Arm v8.5 onwards. This - flag can take the values 0 to 2, to align with the ``FEATURE_DETECTION`` + flag can take the values 0 to 2, to align with the ``ENABLE_FEAT`` mechanism. Default value is ``0``. - ``ENABLE_FEAT_RNG_TRAP``: Numeric value to enable the ``FEAT_RNG_TRAP`` extension. This feature is only supported in AArch64 state. This flag can - take values 0 to 2, to align with the ``FEATURE_DETECTION`` mechanism. + take values 0 to 2, to align with the ``ENABLE_FEAT`` mechanism. Default value is ``0``. ``FEAT_RNG_TRAP`` is an optional feature from Armv8.5 onwards. @@ -342,13 +404,13 @@ Common build options - ``ENABLE_FEAT_SEL2``: Numeric value to enable the ``FEAT_SEL2`` (Secure EL2) extension. ``FEAT_SEL2`` is a mandatory feature available on Arm v8.4. - This flag can take values 0 to 2, to align with the ``FEATURE_DETECTION`` + This flag can take values 0 to 2, to align with the ``ENABLE_FEAT`` mechanism. Default is ``0``. - ``ENABLE_FEAT_TWED``: Numeric value to enable the ``FEAT_TWED`` (Delayed trapping of WFE Instruction) extension. ``FEAT_TWED`` is a optional feature available on Arm v8.6. This flag can take values 0 to 2, to align with the - ``FEATURE_DETECTION`` mechanism. Default is ``0``. + ``ENABLE_FEAT`` mechanism. Default is ``0``. When ``ENABLE_FEAT_TWED`` is set to ``1``, WFE instruction trapping gets delayed by the amount of value in ``TWED_DELAY``. @@ -357,42 +419,67 @@ Common build options Host Extensions) extension. It allows access to CONTEXTIDR_EL2 register during EL2 context save/restore operations.``FEAT_VHE`` is a mandatory architectural feature and is enabled from v8.1 and upwards. It can take - values 0 to 2, to align with the ``FEATURE_DETECTION`` mechanism. + values 0 to 2, to align with the ``ENABLE_FEAT`` mechanism. Default value is ``0``. - ``ENABLE_FEAT_TCR2``: Numeric value to set the bit SCR_EL3.ENTCR2 in EL3 to allow access to TCR2_EL2 (extended translation control) from EL2 as well as adding TCR2_EL2 to the EL2 context save/restore operations. Its a mandatory architectural feature and is enabled from v8.9 and upwards. This - flag can take the values 0 to 2, to align with the ``FEATURE_DETECTION`` + flag can take the values 0 to 2, to align with the ``ENABLE_FEAT`` mechanism. Default value is ``0``. - ``ENABLE_FEAT_S2PIE``: Numeric value to enable support for FEAT_S2PIE at EL2 and below, and context switch relevant registers. This flag - can take the values 0 to 2, to align with the ``FEATURE_DETECTION`` + can take the values 0 to 2, to align with the ``ENABLE_FEAT`` mechanism. Default value is ``0``. - ``ENABLE_FEAT_S1PIE``: Numeric value to enable support for FEAT_S1PIE at EL2 and below, and context switch relevant registers. This flag - can take the values 0 to 2, to align with the ``FEATURE_DETECTION`` + can take the values 0 to 2, to align with the ``ENABLE_FEAT`` mechanism. Default value is ``0``. - ``ENABLE_FEAT_S2POE``: Numeric value to enable support for FEAT_S2POE at EL2 and below, and context switch relevant registers. This flag - can take the values 0 to 2, to align with the ``FEATURE_DETECTION`` + can take the values 0 to 2, to align with the ``ENABLE_FEAT`` mechanism. Default value is ``0``. - ``ENABLE_FEAT_S1POE``: Numeric value to enable support for FEAT_S1POE at EL2 and below, and context switch relevant registers. This flag - can take the values 0 to 2, to align with the ``FEATURE_DETECTION`` + can take the values 0 to 2, to align with the ``ENABLE_FEAT`` mechanism. Default value is ``0``. - ``ENABLE_FEAT_GCS``: Numeric value to set the bit SCR_EL3.GCSEn in EL3 to allow use of Guarded Control Stack from EL2 as well as adding the GCS registers to the EL2 context save/restore operations. This flag can take - the values 0 to 2, to align with the ``FEATURE_DETECTION`` mechanism. + the values 0 to 2, to align with the ``ENABLE_FEAT`` mechanism. Default value is ``0``. +- ``ENABLE_FEAT_THE``: Numeric value to enable support for FEAT_THE + (Translation Hardening Extension) at EL2 and below, setting the bit + SCR_EL3.RCWMASKEn in EL3 to allow access to RCWMASK_EL1 and RCWSMASK_EL1 + registers and context switch them. + Its an optional architectural feature and is available from v8.8 and upwards. + This flag can take the values 0 to 2, to align with the ``ENABLE_FEAT`` + mechanism. Default value is ``0``. + +- ``ENABLE_FEAT_SCTLR2``: Numeric value to enable support for FEAT_SCTLR2 + (Extension to SCTLR_ELx) at EL2 and below, setting the bit + SCR_EL3.SCTLR2En in EL3 to allow access to SCTLR2_ELx registers and + context switch them. This feature is OPTIONAL from Armv8.0 implementations + and mandatory in Armv8.9 implementations. + This flag can take the values 0 to 2, to align with the ``ENABLE_FEAT`` + mechanism. Default value is ``0``. + +- ``ENABLE_FEAT_D128``: Numeric value to enable support for FEAT_D128 + at EL2 and below, setting the bit SCT_EL3.D128En in EL3 to allow access to + 128 bit version of system registers like PAR_EL1, TTBR0_EL1, TTBR1_EL1, + TTBR0_EL2, TTBR1_EL2, TTBR0_EL12, TTBR1_EL12 , VTTBR_EL2, RCWMASK_EL1, and + RCWSMASK_EL1. Its an optional architectural feature and is available from + 9.3 and upwards. + This flag can take the values 0 to 2, to align with the ``ENABLE_FEAT`` + mechanism. Default value is ``0``. + - ``ENABLE_LTO``: Boolean option to enable Link Time Optimization (LTO) support in GCC for TF-A. This option is currently only supported for AArch64. Default is 0. @@ -403,7 +490,7 @@ Common build options various ELs can assign themselves to desired partition to control their performance aspects. - This flag can take values 0 to 2, to align with the ``FEATURE_DETECTION`` + This flag can take values 0 to 2, to align with the ``ENABLE_FEAT`` mechanism. When this option is set to ``1`` or ``2``, EL3 allows lower ELs to access their own MPAM registers without trapping into EL3. This option doesn't make use of partitioning in EL3, however. Platform initialisation @@ -412,6 +499,11 @@ Common build options The flag is automatically disabled when the target architecture is AArch32. +- ``ENABLE_FEAT_LS64_ACCDATA``: Numeric value to enable access and save and + restore the ACCDATA_EL1 system register, at EL2 and below. This flag can + take the values 0 to 2, to align with the ``ENABLE_FEAT`` mechanism. + Default value is ``0``. + - ``ENABLE_MPMM``: Boolean option to enable support for the Maximum Power Mitigation Mechanism supported by certain Arm cores, which allows the SoC firmware to detect and limit high activity events to assist in SoC processor @@ -444,27 +536,32 @@ Common build options - ``ENABLE_SPE_FOR_NS`` : Numeric value to enable Statistical Profiling extensions. This is an optional architectural feature for AArch64. - This flag can take the values 0 to 2, to align with the ``FEATURE_DETECTION`` + This flag can take the values 0 to 2, to align with the ``ENABLE_FEAT`` mechanism. The default is 2 but is automatically disabled when the target architecture is AArch32. - ``ENABLE_SVE_FOR_NS``: Numeric value to enable Scalable Vector Extension (SVE) for the Non-secure world only. SVE is an optional architectural feature - for AArch64. Note that when SVE is enabled for the Non-secure world, access - to SIMD and floating-point functionality from the Secure world is disabled by - default and controlled with ENABLE_SVE_FOR_SWD. - This is to avoid corruption of the Non-secure world data in the Z-registers - which are aliased by the SIMD and FP registers. The build option is not - compatible with the ``CTX_INCLUDE_FPREGS`` build option, and will raise an - assert on platforms where SVE is implemented and ``ENABLE_SVE_FOR_NS`` - enabled. This flag can take the values 0 to 2, to align with the - ``FEATURE_DETECTION`` mechanism. At this time, this build option cannot be - used on systems that have SPM_MM enabled. The default is 1. - -- ``ENABLE_SVE_FOR_SWD``: Boolean option to enable SVE for the Secure world. - SVE is an optional architectural feature for AArch64. Note that this option - requires ENABLE_SVE_FOR_NS to be enabled. The default is 0 and it is - automatically disabled when the target architecture is AArch32. + for AArch64. This flag can take the values 0 to 2, to align with the + ``ENABLE_FEAT`` mechanism. At this time, this build option cannot be used on + systems that have SPM_MM enabled. The default value is 2. + + Note that when SVE is enabled for the Non-secure world, access + to SVE, SIMD and floating-point functionality from the Secure world is + independently controlled by build option ``ENABLE_SVE_FOR_SWD``. When enabling + ``CTX_INCLUDE_FPREGS`` and ``ENABLE_SVE_FOR_NS`` together, it is mandatory to + enable ``CTX_INCLUDE_SVE_REGS``. This is to avoid corruption of the Non-secure + world data in the Z-registers which are aliased by the SIMD and FP registers. + +- ``ENABLE_SVE_FOR_SWD``: Boolean option to enable SVE and FPU/SIMD functionality + for the Secure world. SVE is an optional architectural feature for AArch64. + The default is 0 and it is automatically disabled when the target architecture + is AArch32. + + .. note:: + This build flag requires ``ENABLE_SVE_FOR_NS`` to be enabled. When enabling + ``ENABLE_SVE_FOR_SWD``, a developer must carefully consider whether + ``CTX_INCLUDE_SVE_REGS`` is also needed. - ``ENABLE_STACK_PROTECTOR``: String option to enable the stack protection checks in GCC. Allowed values are "all", "strong", "default" and "none". The @@ -671,6 +768,19 @@ Common build options MARCH_DIRECTIVE := -march=armv8.5-a +- ``HARDEN_SLS``: used to pass -mharden-sls=all from the TF-A build + options to the compiler currently supporting only of the options. + GCC documentation: + https://gcc.gnu.org/onlinedocs/gcc/AArch64-Options.html#index-mharden-sls + + An example usage: + + .. code:: make + + HARDEN_SLS := 1 + + This option defaults to 0. + - ``NON_TRUSTED_WORLD_KEY``: This option is used when ``GENERATE_COT=1``. It specifies a file that contains the Non-Trusted World private key in PEM format or a PKCS11 URI. If ``SAVE_KEYS=1``, only a file is accepted and it @@ -685,10 +795,6 @@ Common build options 1 (do save and restore). 0 is the default. An SPD may set this to 1 if it wants the timer registers to be saved and restored. -- ``OPTEE_SP_FW_CONFIG``: DTC build flag to include OP-TEE as SP in - tb_fw_config device tree. This flag is defined only when - ``ARM_SPMC_MANIFEST_DTS`` manifest file name contains pattern optee_sp. - - ``OVERRIDE_LIBC``: This option allows platforms to override the default libc for the BL image. It can be either 0 (include) or 1 (remove). The default value is 0. @@ -704,12 +810,22 @@ Common build options platform makefile named ``platform.mk``. For example, to build TF-A for the Arm Juno board, select PLAT=juno. +- ``PLATFORM_REPORT_CTX_MEM_USE``: Reports the context memory allocated for + each core as well as the global context. The data includes the memory used + by each world and each privileged exception level. This build option is + applicable only for ``ARCH=aarch64`` builds. The default value is 0. + - ``PRELOADED_BL33_BASE``: This option enables booting a preloaded BL33 image instead of the normal boot flow. When defined, it must specify the entry point address for the preloaded BL33 image. This option is incompatible with ``EL3_PAYLOAD_BASE``. If both are defined, ``EL3_PAYLOAD_BASE`` has priority over ``PRELOADED_BL33_BASE``. +- ``PRESERVE_DSU_PMU_REGS``: This options when enabled allows the platform to + save/restore the DynamIQ Shared Unit's(DSU) Performance Monitoring Unit(PMU) + registers when the cluster goes through a power cycle. This is disabled by + default and platforms that require this feature have to enable them. + - ``PROGRAMMABLE_RESET_ADDRESS``: This option indicates whether the reset vector address can be programmed or is fixed on the platform. It can take either 0 (fixed) or 1 (programmable). Default is 0. If the platform has a @@ -749,6 +865,21 @@ Common build options instead of the BL1 entrypoint. It can take the value 0 (CPU reset to BL1 entrypoint) or 1 (CPU reset to SP_MIN entrypoint). The default value is 0. +- ``RME_GPT_BITLOCK_BLOCK``: This defines the block size (in number of 512MB +- blocks) covered by a single bit of the bitlock structure during RME GPT +- operations. The lower the block size, the better opportunity for +- parallelising GPT operations but at the cost of more bits being needed +- for the bitlock structure. This numeric parameter can take the values +- from 0 to 512 and must be a power of 2. The value of 0 is special and +- and it chooses a single spinlock for all GPT L1 table entries. Default +- value is 1 which corresponds to block size of 512MB per bit of bitlock +- structure. + +- ``RME_GPT_MAX_BLOCK``: Numeric value in MB to define the maximum size of + supported contiguous blocks in GPT Library. This parameter can take the + values 0, 2, 32 and 512. Setting this value to 0 disables use of Contigious + descriptors. Default value is 512. + - ``ROT_KEY``: This option is used when ``GENERATE_COT=1``. It specifies a file that contains the ROT private key in PEM format or a PKCS11 URI and enforces public key hash generation. If ``SAVE_KEYS=1``, only a file is @@ -796,6 +927,11 @@ Common build options flag is disabled by default and NOLOAD sections are placed in RAM immediately following the loaded firmware image. +- ``SEPARATE_SIMD_SECTION``: Setting this option to ``1`` allows the SIMD context + data structures to be put in a dedicated memory region as decided by platform + integrator. Default value is ``0`` which means the SIMD context is put in BSS + section of EL3 firmware. + - ``SMC_PCI_SUPPORT``: This option allows platforms to handle PCI configuration access requests via a standard SMCCC defined in `DEN0115`_. When combined with UEFI+ACPI this can provide a certain amount of OS forward compatibility @@ -1056,31 +1192,26 @@ Common build options - ``ENABLE_BRBE_FOR_NS``: Numeric value to enable access to the branch record buffer registers from NS ELs when FEAT_BRBE is implemented. BRBE is an optional architectural feature for AArch64. This flag can take the values - 0 to 2, to align with the ``FEATURE_DETECTION`` mechanism. The default is 0 + 0 to 2, to align with the ``ENABLE_FEAT`` mechanism. The default is 0 and it is automatically disabled when the target architecture is AArch32. - ``ENABLE_TRBE_FOR_NS``: Numeric value to enable access of trace buffer control registers from NS ELs, NS-EL2 or NS-EL1(when NS-EL2 is implemented but unused) when FEAT_TRBE is implemented. TRBE is an optional architectural feature for AArch64. This flag can take the values 0 to 2, to align with the - ``FEATURE_DETECTION`` mechanism. The default is 0 and it is automatically + ``ENABLE_FEAT`` mechanism. The default is 0 and it is automatically disabled when the target architecture is AArch32. - ``ENABLE_SYS_REG_TRACE_FOR_NS``: Numeric value to enable trace system registers access from NS ELs, NS-EL2 or NS-EL1 (when NS-EL2 is implemented but unused). This feature is available if trace unit such as ETMv4.x, and ETE(extending ETM feature) is implemented. This flag can take the values - 0 to 2, to align with the ``FEATURE_DETECTION`` mechanism. The default is 0. + 0 to 2, to align with the ``ENABLE_FEAT`` mechanism. The default is 0. - ``ENABLE_TRF_FOR_NS``: Numeric value to enable trace filter control registers access from NS ELs, NS-EL2 or NS-EL1 (when NS-EL2 is implemented but unused), if FEAT_TRF is implemented. This flag can take the values 0 to 2, to align - with the ``FEATURE_DETECTION`` mechanism. This flag is disabled by default. - -- ``PLAT_RSS_NOT_SUPPORTED``: Boolean option to enable the usage of the PSA - APIs on platforms that doesn't support RSS (providing Arm CCA HES - functionalities). When enabled (``1``), a mocked version of the APIs are used. - The default value is 0. + with the ``ENABLE_FEAT`` mechanism. This flag is disabled by default. - ``CONDITIONAL_CMO``: Boolean option to enable call to platform-defined routine ``plat_can_cmo`` which will return zero if cache management operations should @@ -1199,6 +1330,13 @@ Experimental build options Common build options ~~~~~~~~~~~~~~~~~~~~ +- ``DICE_PROTECTION_ENVIRONMENT``: Boolean flag to specify the measured boot + backend when ``MEASURED_BOOT`` is enabled. The default value is ``0``. When + set to ``1`` then measurements and additional metadata collected during the + measured boot process are sent to the DICE Protection Environment for storage + and processing. A certificate chain, which represents the boot state of the + device, can be queried from the DPE. + - ``DRTM_SUPPORT``: Boolean flag to enable support for Dynamic Root of Trust for Measurement (DRTM). This feature has trust dependency on BL31 for taking the measurements and recording them as per `PSA DRTM specification`_. For @@ -1208,7 +1346,14 @@ Common build options - ``ENABLE_RME``: Numeric value to enable support for the ARMv9 Realm Management Extension. This flag can take the values 0 to 2, to align with - the ``FEATURE_DETECTION`` mechanism. Default value is 0. + the ``ENABLE_FEAT`` mechanism. Default value is 0. + +- ``RMMD_ENABLE_EL3_TOKEN_SIGN``: Numeric value to enable support for singing + realm attestation token signing requests in EL3. This flag can take the + values 0 and 1. The default value is ``0``. When set to ``1``, this option + enables additional RMMD SMCs to push and pop requests for signing to + EL3 along with platform hooks that must be implemented to service those + requests and responses. - ``ENABLE_SME_FOR_NS``: Numeric value to enable Scalable Matrix Extension (SME), SVE, and FPU/SIMD for the non-secure world only. These features share @@ -1218,7 +1363,7 @@ Common build options superset of SVE. SME is an optional architectural feature for AArch64. At this time, this build option cannot be used on systems that have SPD=spmd/SPM_MM and atempting to build with this option will fail. - This flag can take the values 0 to 2, to align with the ``FEATURE_DETECTION`` + This flag can take the values 0 to 2, to align with the ``ENABLE_FEAT`` mechanism. Default is 0. - ``ENABLE_SME2_FOR_NS``: Numeric value to enable Scalable Matrix Extension @@ -1226,7 +1371,7 @@ Common build options architectural feature for AArch64. This should be set along with ENABLE_SME_FOR_NS=1, if not, the default SME accesses will still be trapped. This flag can take the values 0 to 2, to - align with the ``FEATURE_DETECTION`` mechanism. Default is 0. + align with the ``ENABLE_FEAT`` mechanism. Default is 0. - ``ENABLE_SME_FOR_SWD``: Boolean option to enable the Scalable Matrix Extension for secure world. Used along with SVE and FPU/SIMD. @@ -1240,41 +1385,16 @@ Common build options must not be used if ``SPMC_AT_EL3`` is enabled. - ``FEATURE_DETECTION``: Boolean option to enable the architectural features - detection mechanism. It detects whether the Architectural features enabled - through feature specific build flags are supported by the PE or not by - validating them either at boot phase or at runtime based on the value - possessed by the feature flag (0 to 2) and report error messages at an early - stage. This flag will also enable errata ordering checking for ``DEBUG`` - builds. - - This prevents and benefits us from EL3 runtime exceptions during context save - and restore routines guarded by these build flags. Henceforth validating them - before their usage provides more control on the actions taken under them. - - The mechanism permits the build flags to take values 0, 1 or 2 and - evaluates them accordingly. - - Lets consider ``ENABLE_FEAT_HCX``, build flag for ``FEAT_HCX`` as an example: - - :: - - ENABLE_FEAT_HCX = 0: Feature disabled statically at compile time. - ENABLE_FEAT_HCX = 1: Feature Enabled and the flag is validated at boottime. - ENABLE_FEAT_HCX = 2: Feature Enabled and the flag is validated at runtime. - - In the above example, if the feature build flag, ``ENABLE_FEAT_HCX`` set to - 0, feature is disabled statically during compilation. If it is defined as 1, - feature is validated, wherein FEAT_HCX is detected at boot time. In case not - implemented by the PE, a hard panic is generated. Finally, if the flag is set - to 2, feature is validated at runtime. + verification mechanism. This is a debug feature that compares the + architectural features enabled through the feature specific build flags + (ENABLE_FEAT_xxx) with the features actually available on the CPU running, + and reports any discrepancies. + This flag will also enable errata ordering checking for ``DEBUG`` builds. - Note that the entire implementation is divided into two phases, wherein as - as part of phase-1 we are supporting the values 0,1. Value 2 is currently not - supported and is planned to be handled explicilty in phase-2 implementation. - - ``FEATURE_DETECTION`` macro is disabled by default. Platforms can explicitly - make use of this by mechanism, by enabling it to validate whether they have - set their build flags properly at an early phase. + It is expected that this feature is only used for flexible platforms like + software emulators, or for hardware platforms at bringup time, to verify + that the configured feature set matches the CPU. + The ``FEATURE_DETECTION`` macro is disabled by default. - ``PSA_CRYPTO``: Boolean option for enabling MbedTLS PSA crypto APIs support. The platform will use PSA compliant Crypto APIs during authentication and @@ -1314,12 +1434,21 @@ Firmware update options This flag is used in defining the firmware update metadata structure. This flag is by default set to '1'. +- ``PSA_FWU_METADATA_FW_STORE_DESC``: To be enabled when the FWU + metadata contains image description. The default value is 1. + + The version 2 of the FWU metadata allows for an opaque metadata + structure where a platform can choose to not include the firmware + store description in the metadata structure. This option indicates + if the firmware store description, which provides information on + the updatable images is part of the structure. + -------------- -*Copyright (c) 2019-2023, Arm Limited. All rights reserved.* +*Copyright (c) 2019-2024, Arm Limited. All rights reserved.* .. _DEN0115: https://developer.arm.com/docs/den0115/latest -.. _PSA FW update specification: https://developer.arm.com/documentation/den0118/a/ +.. _PSA FW update specification: https://developer.arm.com/documentation/den0118/latest/ .. _PSA DRTM specification: https://developer.arm.com/documentation/den0113/a .. _GCC: https://gcc.gnu.org/onlinedocs/gcc/Warning-Options.html .. _Clang: https://clang.llvm.org/docs/DiagnosticsReference.html diff --git a/docs/getting_started/docs-build.rst b/docs/getting_started/docs-build.rst index 50fff575..54e29dd5 100644 --- a/docs/getting_started/docs-build.rst +++ b/docs/getting_started/docs-build.rst @@ -37,25 +37,8 @@ Ubuntu): Building rendered documentation ------------------------------- -To install Python dependencies using Poetry: - -.. code:: shell - - poetry install - -Poetry will create a new virtual environment and install all dependencies listed -in ``pyproject.toml``. You can get information about this environment, such as -its location and the Python version, with the command: - -.. code:: shell - - poetry env info - -If you have already sourced a virtual environment, Poetry will respect this and -install dependencies there. - -Once all dependencies are installed, the documentation can be compiled into -HTML-formatted pages from the project root directory by running: +The documentation can be compiled into HTML-formatted pages from the project +root directory by running: .. code:: shell @@ -129,7 +112,7 @@ from project root directory bash -c 'cd /tf-a && apt-get update && apt-get install -y curl plantuml && curl -sSL https://install.python-poetry.org | python3 - && - ~/.local/bin/poetry install && ~/.local/bin/poetry run make doc' + ~/.local/bin/poetry run make doc' The above command fetches the ``sphinxdoc/sphinx`` container from `docker hub`_, launches the container, installs documentation requirements and finally @@ -138,7 +121,7 @@ build process will be placed in: ``docs/build/html``. -------------- -*Copyright (c) 2019-2023, Arm Limited. All rights reserved.* +*Copyright (c) 2019-2024, Arm Limited. All rights reserved.* .. _Sphinx: http://www.sphinx-doc.org/en/master/ .. _Poetry: https://python-poetry.org/docs/ diff --git a/docs/getting_started/prerequisites.rst b/docs/getting_started/prerequisites.rst index 573abdfe..c414b1f2 100644 --- a/docs/getting_started/prerequisites.rst +++ b/docs/getting_started/prerequisites.rst @@ -8,33 +8,53 @@ It may possible to build |TF-A| with combinations of software packages that are different from those listed below, however only the software described in this document can be officially supported. -Build Host ----------- +Getting the TF-A Source +----------------------- -|TF-A| can be built using either a Linux or a Windows machine as the build host. +Source code for |TF-A| is maintained in a Git repository hosted on +`TrustedFirmware.org`_. To clone this repository from the server, run the following +in your shell: -A relatively recent Linux distribution is recommended for building |TF-A|. We -have performed tests using Ubuntu 22.04 LTS (64-bit) but other distributions -should also work fine as a base, provided that the necessary tools and libraries -can be installed. +.. code:: shell -.. _prerequisites_toolchain: + git clone "https://review.trustedfirmware.org/TF-A/trusted-firmware-a" -Toolchain ---------- -|TF-A| can be built with any of the following *cross-compiler* toolchains that -target the Armv7-A or Armv8-A architectures: +Requirements +------------ + +======================== ===================== + Program Min supported version +======================== ===================== +Arm Compiler 6.18 +Arm GNU Compiler 13.3 +Clang/LLVM 18.1.8 +Device Tree Compiler 1.6.1 +GNU make 3.81 +mbed TLS\ [#f1]_ 3.6.1 +Node.js [#f2]_ 16 +OpenSSL 1.0.0 +Poetry 1.3.2 +QCBOR\ [#f3]_ 1.2 +Sphinx\ [#f2]_ 5.3.0 +======================== ===================== + +.. [#f1] Required for Trusted Board Boot and Measured Boot. +.. [#f2] Required only for building TF-A documentation. +.. [#f3] Required only when enabling DICE Protection Environment support. -- TF-A has been tested with version 12.3.Rel1 (gcc 12.3) from the `Arm Developer website`_ +Toolchain +^^^^^^^^^ - You will need the targets ``arm-none-eabi`` and ``aarch64-none-elf`` for - AArch32 and AArch64 builds respectively. +|TF-A| can be compiled using any cross-compiler toolchain specified in the +preceding table that target Armv7-A or Armv8-A. For AArch32 and +AArch64 builds, the respective targets required are ``arm-none-eabi`` and +``aarch64-none-elf``. -- Clang == 14.0.0 -- Arm Compiler == 6.18 +Testing has been performed with version 13.3.Rel1 (gcc 13.3) of the Arm +GNU compiler, which can be installed from the `Arm Developer website`_. -In addition, a native compiler is required to build the supporting tools. +In addition, a native compiler is required to build supporting tools. .. note:: Versions greater than the ones specified are likely but not guaranteed to @@ -42,78 +62,69 @@ In addition, a native compiler is required to build the supporting tools. which may be older than the version expected by the compiler. Fixes and bug reports are always welcome. -.. note:: - The software has also been built on Windows 7 Enterprise SP1, using CMD.EXE, - Cygwin, and Msys (MinGW) shells, using version 5.3.1 of the GNU toolchain. - .. note:: For instructions on how to select the cross compiler refer to :ref:`Performing an Initial Build`. -.. _prerequisites_software_and_libraries: - -Software and Libraries ----------------------- - -The following tools are required to obtain and build |TF-A|: - -- An appropriate toolchain (see :ref:`prerequisites_toolchain`) -- GNU Make -- Git +OpenSSL +^^^^^^^ -The following libraries must be available to build one or more components or -supporting tools: +OpenSSL is required to build the cert_create, encrypt_fw, and fiptool tools. -- OpenSSL >= 1.1.1 (v3.0.0 to v3.0.6 highly discouraged due to security issues) +If using OpenSSL 3, older Linux versions may require it to be built from +source code, as it may not be available in the default package repositories. +Please refer to the OpenSSL project documentation for more information. - Required to build the cert_create, encrypt_fw, and fiptool tools. +.. warning:: + Versions 1.0.x and from v3.0.0 up to v3.0.6 are strongly advised against due + to concerns regarding security vulnerabilities! - .. note:: +Device Tree Compiler (DTC) +^^^^^^^^^^^^^^^^^^^^^^^^^^ - If using OpenSSL 3, older Linux versions may require it to be built from - source code, as it may not be available in the default package repositories. - Please refer to the OpenSSL project documentation for more information. +Needed if you want to rebuild the provided Flattened Device Tree (FDT) +source files (``.dts`` files). DTC is available for Linux through the package +repositories of most distributions. -The following libraries are required for Trusted Board Boot and Measured Boot -support: +Arm Development Studio (`Arm-DS`_) +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -- mbed TLS == 3.4.1 (tag: ``mbedtls-3.4.1``) +The standard software package used for debugging software on Arm development +platforms and |FVP| models. -These tools are optional: +Node.js +^^^^^^^ -- Device Tree Compiler (DTC) >= 1.4.7 +Highly recommended, and necessary in order to install and use the packaged +Git hooks and helper tools. Without these tools you will need to rely on the +CI for feedback on commit message conformance. - Needed if you want to rebuild the provided Flattened Device Tree (FDT) - source files (``.dts`` files). DTC is available for Linux through the package - repositories of most distributions. +Poetry +^^^^^^ -- Arm `Development Studio (Arm-DS)`_ +Required for managing Python dependencies, this will allow you to reliably +reproduce a Python environment to build documentation and run some of the +integrated Python tools. Most importantly, it ensures your system environment +will not be affected by dependencies in the Python scripts. - The standard software package used for debugging software on Arm development - platforms and |FVP| models. +For installation instructions, see the `official Poetry documentation`_. -- Node.js >= 16 - - Highly recommended, and necessary in order to install and use the packaged - Git hooks and helper tools. Without these tools you will need to rely on the - CI for feedback on commit message conformance. +.. _prerequisites_software_and_libraries: -- Poetry >= 1.3.2 +Package Installation (Linux) +---------------------------- - Required for managing Python dependencies, this will allow you to reliably - reproduce a Python environment to build documentation and run analysis tools. - Most importantly, it ensures your system environment will not be affected by - dependencies in the Python scripts. +|TF-A| can be compiled on both Linux and Windows-based machines. +However, we strongly recommend using a UNIX-compatible build environment. -Package Installation (Linux) -^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Testing is performed using Ubuntu 22.04 LTS (64-bit), but other distributions +should also work, provided the necessary tools and libraries are installed. -If you are using the recommended Ubuntu distribution then you can install the -required packages with the following command: +The following are steps to install the required packages: .. code:: shell - sudo apt install build-essential git + sudo apt install build-essential The optional packages can be installed using: @@ -142,17 +153,6 @@ instructions in :ref:`Performing an Initial Build`. .. _prerequisites_get_source: -Getting the TF-A Source ------------------------ - -Source code for |TF-A| is maintained in a Git repository hosted on -TrustedFirmware.org. To clone this repository from the server, run the following -in your shell: - -.. code:: shell - - git clone "https://review.trustedfirmware.org/TF-A/trusted-firmware-a" - Additional Steps for Contributors ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -184,11 +184,13 @@ documentation, available `here <https://git-scm.com/docs/githooks>`_. -------------- -*Copyright (c) 2021-2023, Arm Limited. All rights reserved.* +*Copyright (c) 2021-2024, Arm Limited. All rights reserved.* .. _Arm Developer website: https://developer.arm.com/tools-and-software/open-source-software/developer-tools/gnu-toolchain/downloads .. _Gerrit Code Review: https://www.gerritcodereview.com/ .. _Linaro Release Notes: https://community.arm.com/dev-platforms/w/docs/226/old-release-notes .. _Linaro instructions: https://community.arm.com/dev-platforms/w/docs/304/arm-reference-platforms-deliverables -.. _Development Studio (Arm-DS): https://developer.arm.com/Tools%20and%20Software/Arm%20Development%20Studio +.. _Arm-DS: https://developer.arm.com/Tools%20and%20Software/Arm%20Development%20Studio .. _Linaro Release 20.01: http://releases.linaro.org/members/arm/platforms/20.01 +.. _TrustedFirmware.org: https://www.trustedfirmware.org/ +.. _official Poetry documentation: https://python-poetry.org/docs/#installation diff --git a/docs/getting_started/rt-svc-writers-guide.rst b/docs/getting_started/rt-svc-writers-guide.rst index fe645588..4d4ec22f 100644 --- a/docs/getting_started/rt-svc-writers-guide.rst +++ b/docs/getting_started/rt-svc-writers-guide.rst @@ -49,8 +49,11 @@ legacy 32-bit software that predates the `SMCCC`_. Fast 1 CPU Service calls Fast 2 SiP Service calls Fast 3 OEM Service calls - Fast 4 Standard Service calls - Fast 5-47 Reserved for future use + Fast 4 Standard Secure Service calls + Fast 5 Standard Hypervisor Service Calls + Fast 6 Vendor Specific Hypervisor Service Calls + Fast 7 Vendor Specific EL3 Monitor Calls + Fast 8-47 Reserved for future use Fast 48-49 Trusted Application calls Fast 50-63 Trusted OS calls @@ -312,9 +315,17 @@ TODO: Provide details of the additional work required to implement a SPD and the BL31 support for these services. Or a reference to the document that will provide this information.... +Additional References: +---------------------- + +#. :ref:`ARM SiP Services <arm sip services>` +#. :ref:`Vendor Specific EL3 Monitor Service Calls` + -------------- -*Copyright (c) 2014-2023, Arm Limited and Contributors. All rights reserved.* +*Copyright (c) 2014-2024, Arm Limited and Contributors. All rights reserved.* .. _SMCCC: https://developer.arm.com/docs/den0028/latest .. _PSCI: https://developer.arm.com/documentation/den0022/latest/ +.. _ARM SiP Services: arm-sip-service.rst +.. _Vendor Specific EL3 Monitor Service Calls: ven-el3-service.rst diff --git a/docs/global_substitutions.txt b/docs/global_substitutions.txt index 80012e7b..23a91cdc 100644 --- a/docs/global_substitutions.txt +++ b/docs/global_substitutions.txt @@ -8,6 +8,8 @@ .. |COT| replace:: :term:`COT` .. |CSS| replace:: :term:`CSS` .. |CVE| replace:: :term:`CVE` +.. |DICE| replace:: :term:`DICE` +.. |DPE| replace:: :term:`DPE` .. |DTB| replace:: :term:`DTB` .. |DS-5| replace:: :term:`DS-5` .. |DSU| replace:: :term:`DSU` @@ -21,6 +23,7 @@ .. |FVP| replace:: :term:`FVP` .. |FWU| replace:: :term:`FWU` .. |GIC| replace:: :term:`GIC` +.. |HES| replace:: :term:`HES` .. |ISA| replace:: :term:`ISA` .. |Linaro| replace:: :term:`Linaro` .. |MMU| replace:: :term:`MMU` @@ -31,12 +34,14 @@ .. |OEN| replace:: :term:`OEN` .. |OP-TEE| replace:: :term:`OP-TEE` .. |OTE| replace:: :term:`OTE` +.. |PCR| replace:: :term:`PCR` .. |PDD| replace:: :term:`PDD` .. |PAUTH| replace:: :term:`PAUTH` .. |PMF| replace:: :term:`PMF` .. |PSCI| replace:: :term:`PSCI` .. |RAS| replace:: :term:`RAS` .. |ROT| replace:: :term:`ROT` +.. |RSE| replace:: :term:`RSE` .. |SCMI| replace:: :term:`SCMI` .. |SCP| replace:: :term:`SCP` .. |SDEI| replace:: :term:`SDEI` @@ -55,6 +60,7 @@ .. |SVE| replace:: :term:`SVE` .. |TBB| replace:: :term:`TBB` .. |TBBR| replace:: :term:`TBBR` +.. |TCB| replace:: :term:`TCB` .. |TCG| replace:: :term:`TCG` .. |TEE| replace:: :term:`TEE` .. |TF-A| replace:: :term:`TF-A` diff --git a/docs/glossary.rst b/docs/glossary.rst index 58b7d999..f19897c7 100644 --- a/docs/glossary.rst +++ b/docs/glossary.rst @@ -40,6 +40,9 @@ You can find additional definitions in the `Arm Glossary`_. Common Vulnerabilities and Exposures. A CVE document is commonly used to describe a publicly-known security vulnerability. + DICE + Device Identifier Composition Engine + DCE DRTM Configuration Environment @@ -52,6 +55,9 @@ You can find additional definitions in the `Arm Glossary`_. DRTM Dynamic Root of Trust for Measurement + DPE + DICE Protection Environment + DS-5 Arm Development Studio 5 @@ -94,6 +100,9 @@ You can find additional definitions in the `Arm Glossary`_. GIC Generic Interrupt Controller + HES + Arm CCA Hardware Enforced Security + ISA Instruction Set Architecture @@ -130,6 +139,9 @@ You can find additional definitions in the `Arm Glossary`_. OTE Open-source Trusted Execution Environment + PCR + Platform Configuration Register + PDD Platform Design Document @@ -142,6 +154,9 @@ You can find additional definitions in the `Arm Glossary`_. PSA Platform Security Architecture + PSR + Platform Security Requirements + PSCI Power State Coordination Interface @@ -153,6 +168,9 @@ You can find additional definitions in the `Arm Glossary`_. ROT Root of Trust + RSE + Runtime Security Engine + SCMI System Control and Management Interface diff --git a/docs/index.rst b/docs/index.rst index a7a59935..c05c0a50 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -91,8 +91,8 @@ have previously been raised against the software. .. _Armv7-A and Armv8-A: https://developer.arm.com/products/architecture/a-profile .. _Secure Monitor: http://www.arm.com/products/processors/technologies/trustzone/tee-smc.php .. _Power State Coordination Interface (PSCI): https://developer.arm.com/documentation/den0022/latest/ -.. _Trusted Board Boot Requirements CLIENT (TBBR-CLIENT): https://developer.arm.com/docs/den0006/latest/trusted-board-boot-requirements-client-tbbr-client-armv8-a +.. _Trusted Board Boot Requirements CLIENT (TBBR-CLIENT): https://developer.arm.com/docs/den0006/latest .. _System Control and Management Interface (SCMI): http://infocenter.arm.com/help/topic/com.arm.doc.den0056a/DEN0056A_System_Control_and_Management_Interface.pdf .. _Software Delegated Exception Interface (SDEI): http://infocenter.arm.com/help/topic/com.arm.doc.den0054a/ARM_DEN0054A_Software_Delegated_Exception_Interface.pdf .. _SMC Calling Convention: https://developer.arm.com/docs/den0028/latest -.. _PSA FW update specification: https://developer.arm.com/documentation/den0118/a/ +.. _PSA FW update specification: https://developer.arm.com/documentation/den0118/latest/ diff --git a/docs/license.rst b/docs/license.rst index 80f11186..9e0298b7 100644 --- a/docs/license.rst +++ b/docs/license.rst @@ -85,6 +85,35 @@ license text is included in those source files. See the original `Linux MIT license`_. +- Some source files originating from the `Open Profile for DICE`_ project. + These files are licensed under the Apache License, Version 2.0, which is a + permissive license compatible with BSD-3-Clause. Any contributions to this + code must also be made under the terms of `Apache License 2.0`_. + These files are: + + - ``include/lib/dice/dice.h`` + +- Some source files originating from the `pydevicetree`_ project. + These files are licensed under the Apache License, Version 2.0, which is a + permissive license compatible with BSD-3-Clause. Any contributions to this + code must also be made under the terms of `Apache License 2.0`_. + These files are: + + - ``tools/cot_dt2c/cot_dt2c/pydevicetree/ast/__init__.py`` + - ``tools/cot_dt2c/cot_dt2c/pydevicetree/ast/directive.py`` + - ``tools/cot_dt2c/cot_dt2c/pydevicetree/ast/helpers.py`` + - ``tools/cot_dt2c/cot_dt2c/pydevicetree/ast/node.py`` + - ``tools/cot_dt2c/cot_dt2c/pydevicetree/ast/property.py`` + - ``tools/cot_dt2c/cot_dt2c/pydevicetree/ast/reference.py`` + - ``tools/cot_dt2c/cot_dt2c/pydevicetree/source/__init__.py`` + - ``tools/cot_dt2c/cot_dt2c/pydevicetree/source/grammar.py`` + - ``tools/cot_dt2c/cot_dt2c/pydevicetree/source/parser.py`` + - ``tools/cot_dt2c/cot_dt2c/pydevicetree/__init__.py`` + + .. _FreeBSD: http://www.freebsd.org .. _Linux MIT license: https://raw.githubusercontent.com/torvalds/linux/master/LICENSES/preferred/MIT .. _SCC: http://www.simple-cc.org/ +.. _Open Profile for DICE: https://pigweed.googlesource.com/open-dice/ +.. _Apache License 2.0: https://www.apache.org/licenses/LICENSE-2.0.txt +.. _pydevicetree: https://pypi.org/project/pydevicetree/ diff --git a/docs/perf/psci-performance-juno.rst b/docs/perf/psci-performance-juno.rst index bab10862..9640a242 100644 --- a/docs/perf/psci-performance-juno.rst +++ b/docs/perf/psci-performance-juno.rst @@ -31,8 +31,8 @@ timestamps, which runs at 50MHz on Juno. The following source trees and binaries were used: -- TF-A [`v2.9-rc0`_] -- TFTF [`v2.9-rc0`_] +- `TF-A v2.12-rc0`_ +- `TFTF v2.12-rc0`_ Please see the Runtime Instrumentation :ref:`Testing Methodology <Runtime Instrumentation Methodology>` @@ -73,156 +73,158 @@ Results ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .. table:: ``CPU_SUSPEND`` latencies (µs) to deepest power level in - parallel (v2.9) - - +---------+------+-----------+--------+-------------+ - | Cluster | Core | Powerdown | Wakeup | Cache Flush | - +---------+------+-----------+--------+-------------+ - | 0 | 0 | 104.58 | 241.20 | 5.26 | - +---------+------+-----------+--------+-------------+ - | 0 | 1 | 384.24 | 22.50 | 138.76 | - +---------+------+-----------+--------+-------------+ - | 1 | 0 | 244.56 | 22.18 | 5.16 | - +---------+------+-----------+--------+-------------+ - | 1 | 1 | 670.56 | 18.58 | 4.44 | - +---------+------+-----------+--------+-------------+ - | 1 | 2 | 809.36 | 269.28 | 4.44 | - +---------+------+-----------+--------+-------------+ - | 1 | 3 | 984.96 | 219.70 | 79.62 | - +---------+------+-----------+--------+-------------+ + parallel (v2.12) + + +---------+------+-------------------+------------------+--------------------+ + | Cluster | Core | Powerdown | Wakeup | Cache Flush | + +---------+------+-------------------+------------------+--------------------+ + | 0 | 0 | 244.52 (-65.43%) | 26.92 (-32.60%) | 5.54 (-96.70%) | + +---------+------+-------------------+------------------+--------------------+ + | 0 | 1 | 526.18 (+105.12%) | 416.1 | 138.52 (+2011.59%) | + +---------+------+-------------------+------------------+--------------------+ + | 1 | 0 | 104.34 | 27.02 (-94.62%) | 5.32 | + +---------+------+-------------------+------------------+--------------------+ + | 1 | 1 | 384.98 | 23.06 (-85.40%) | 4.48 | + +---------+------+-------------------+------------------+--------------------+ + | 1 | 2 | 812.44 (+45.94%) | 126.78 | 4.54 | + +---------+------+-------------------+------------------+--------------------+ + | 1 | 3 | 986.84 | 77.22 (+176.58%) | 79.76 | + +---------+------+-------------------+------------------+--------------------+ .. table:: ``CPU_SUSPEND`` latencies (µs) to deepest power level in - parallel (v2.10) - - +---------+------+-------------------+--------+-------------+ - | Cluster | Core | Powerdown | Wakeup | Cache Flush | - +---------+------+-------------------+--------+-------------+ - | 0 | 0 | 242.66 (+132.03%) | 245.1 | 5.4 | - +---------+------+-------------------+--------+-------------+ - | 0 | 1 | 522.08 (+35.87%) | 26.24 | 138.32 | - +---------+------+-------------------+--------+-------------+ - | 1 | 0 | 104.36 (-57.33%) | 27.1 | 5.32 | - +---------+------+-------------------+--------+-------------+ - | 1 | 1 | 382.56 (-42.95%) | 23.34 | 4.42 | - +---------+------+-------------------+--------+-------------+ - | 1 | 2 | 807.74 | 271.54 | 4.64 | - +---------+------+-------------------+--------+-------------+ - | 1 | 3 | 981.36 | 221.8 | 79.48 | - +---------+------+-------------------+--------+-------------+ + parallel (v2.11) + + +---------+------+-------------------+--------------------+-------------+ + | Cluster | Core | Powerdown | Wakeup | Cache Flush | + +---------+------+-------------------+--------------------+-------------+ + | 0 | 0 | 112.98 (-53.44%) | 26.16 (-89.33%) | 5.48 | + +---------+------+-------------------+--------------------+-------------+ + | 0 | 1 | 411.18 | 438.88 (+1572.56%) | 138.54 | + +---------+------+-------------------+--------------------+-------------+ + | 1 | 0 | 261.82 (+150.88%) | 474.06 (+1649.30%) | 5.6 | + +---------+------+-------------------+--------------------+-------------+ + | 1 | 1 | 714.76 (+86.84%) | 26.44 | 4.48 | + +---------+------+-------------------+--------------------+-------------+ + | 1 | 2 | 862.66 | 149.34 (-45.00%) | 4.38 | + +---------+------+-------------------+--------------------+-------------+ + | 1 | 3 | 1045.12 | 98.12 (-55.76%) | 79.74 | + +---------+------+-------------------+--------------------+-------------+ .. table:: ``CPU_SUSPEND`` latencies (µs) to deepest power level in - serial (v2.9) - - +---------+------+-----------+--------+-------------+ - | Cluster | Core | Powerdown | Wakeup | Cache Flush | - +---------+------+-----------+--------+-------------+ - | 0 | 0 | 236.56 | 23.24 | 138.18 | - +---------+------+-----------+--------+-------------+ - | 0 | 1 | 236.86 | 23.28 | 138.10 | - +---------+------+-----------+--------+-------------+ - | 1 | 0 | 281.04 | 22.80 | 77.24 | - +---------+------+-----------+--------+-------------+ - | 1 | 1 | 100.28 | 18.52 | 4.54 | - +---------+------+-----------+--------+-------------+ - | 1 | 2 | 100.12 | 18.78 | 4.50 | - +---------+------+-----------+--------+-------------+ - | 1 | 3 | 100.36 | 18.94 | 4.44 | - +---------+------+-----------+--------+-------------+ + serial (v2.12) + + +---------+------+-----------+-----------------+-------------+ + | Cluster | Core | Powerdown | Wakeup | Cache Flush | + +---------+------+-----------+-----------------+-------------+ + | 0 | 0 | 236.36 | 27.94 (-31.52%) | 138.0 | + +---------+------+-----------+-----------------+-------------+ + | 0 | 1 | 236.58 | 27.86 (-31.72%) | 138.2 | + +---------+------+-----------+-----------------+-------------+ + | 1 | 0 | 280.68 | 27.02 | 77.6 | + +---------+------+-----------+-----------------+-------------+ + | 1 | 1 | 101.4 | 22.52 | 4.42 | + +---------+------+-----------+-----------------+-------------+ + | 1 | 2 | 100.92 | 22.68 | 4.4 | + +---------+------+-----------+-----------------+-------------+ + | 1 | 3 | 100.96 | 22.54 | 4.38 | + +---------+------+-----------+-----------------+-------------+ .. table:: ``CPU_SUSPEND`` latencies (µs) to deepest power level in - serial (v2.10) + serial (v2.11) +---------+------+-----------+--------+-------------+ | Cluster | Core | Powerdown | Wakeup | Cache Flush | +---------+------+-----------+--------+-------------+ - | 0 | 0 | 236.84 | 27.1 | 138.36 | + | 0 | 0 | 244.42 | 27.42 | 138.12 | +---------+------+-----------+--------+-------------+ - | 0 | 1 | 236.96 | 27.1 | 138.32 | + | 0 | 1 | 245.02 | 27.34 | 138.08 | +---------+------+-----------+--------+-------------+ - | 1 | 0 | 280.06 | 26.94 | 77.5 | + | 1 | 0 | 297.66 | 26.2 | 77.68 | +---------+------+-----------+--------+-------------+ - | 1 | 1 | 100.76 | 23.42 | 4.36 | + | 1 | 1 | 108.02 | 21.94 | 4.52 | +---------+------+-----------+--------+-------------+ - | 1 | 2 | 100.02 | 23.42 | 4.44 | + | 1 | 2 | 107.48 | 21.88 | 4.46 | +---------+------+-----------+--------+-------------+ - | 1 | 3 | 100.08 | 23.2 | 4.4 | + | 1 | 3 | 107.52 | 21.86 | 4.46 | +---------+------+-----------+--------+-------------+ ``CPU_SUSPEND`` to power level 0 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .. table:: ``CPU_SUSPEND`` latencies (µs) to power level 0 in - parallel (v2.9) - - +---------+------+-----------+--------+-------------+ - | Cluster | Core | Powerdown | Wakeup | Cache Flush | - +---------+------+-----------+--------+-------------+ - | 0 | 0 | 662.34 | 15.22 | 8.08 | - +---------+------+-----------+--------+-------------+ - | 0 | 1 | 802.00 | 15.50 | 8.16 | - +---------+------+-----------+--------+-------------+ - | 1 | 0 | 385.22 | 15.74 | 7.88 | - +---------+------+-----------+--------+-------------+ - | 1 | 1 | 106.16 | 16.06 | 7.44 | - +---------+------+-----------+--------+-------------+ - | 1 | 2 | 524.38 | 15.64 | 7.34 | - +---------+------+-----------+--------+-------------+ - | 1 | 3 | 246.00 | 15.78 | 7.72 | - +---------+------+-----------+--------+-------------+ + parallel (v2.12) + + +--------------------------------------------------------------------+ + | test_rt_instr_cpu_susp_parallel | + +---------+------+-------------------+-----------------+-------------+ + | Cluster | Core | Powerdown | Wakeup | Cache Flush | + +---------+------+-------------------+-----------------+-------------+ + | 0 | 0 | 663.12 | 19.66 (-39.21%) | 8.26 | + +---------+------+-------------------+-----------------+-------------+ + | 0 | 1 | 804.18 | 19.24 (-40.65%) | 8.1 | + +---------+------+-------------------+-----------------+-------------+ + | 1 | 0 | 105.58 (-58.80%) | 19.68 | 7.42 | + +---------+------+-------------------+-----------------+-------------+ + | 1 | 1 | 245.02 (-39.67%) | 19.8 | 6.82 | + +---------+------+-------------------+-----------------+-------------+ + | 1 | 2 | 383.82 (-30.83%) | 18.84 | 7.06 | + +---------+------+-------------------+-----------------+-------------+ + | 1 | 3 | 523.36 (+391.23%) | 19.0 | 7.3 | + +---------+------+-------------------+-----------------+-------------+ .. table:: ``CPU_SUSPEND`` latencies (µs) to power level 0 in - parallel (v2.10) + parallel (v2.11) +---------+------+-------------------+--------+-------------+ | Cluster | Core | Powerdown | Wakeup | Cache Flush | +---------+------+-------------------+--------+-------------+ - | 0 | 0 | 801.04 | 18.66 | 8.22 | + | 0 | 0 | 704.46 | 19.28 | 7.86 | +---------+------+-------------------+--------+-------------+ - | 0 | 1 | 661.28 | 19.08 | 7.88 | + | 0 | 1 | 853.66 | 18.78 | 7.82 | +---------+------+-------------------+--------+-------------+ - | 1 | 0 | 105.9 (-72.51%) | 20.3 | 7.58 | + | 1 | 0 | 556.52 (+425.51%) | 19.06 | 7.82 | +---------+------+-------------------+--------+-------------+ - | 1 | 1 | 383.58 (+261.32%) | 20.4 | 7.42 | + | 1 | 1 | 113.28 (-70.47%) | 19.28 | 7.48 | +---------+------+-------------------+--------+-------------+ - | 1 | 2 | 523.52 | 20.1 | 7.74 | + | 1 | 2 | 260.62 (-50.22%) | 19.8 | 7.26 | +---------+------+-------------------+--------+-------------+ - | 1 | 3 | 244.5 | 20.16 | 7.56 | + | 1 | 3 | 408.16 (+66.94%) | 19.82 | 7.38 | +---------+------+-------------------+--------+-------------+ -.. table:: ``CPU_SUSPEND`` latencies (µs) to power level 0 in serial (v2.9) +.. table:: ``CPU_SUSPEND`` latencies (µs) to power level 0 in serial (v2.12) - +---------+------+-----------+--------+-------------+ - | Cluster | Core | Powerdown | Wakeup | Cache Flush | - +---------+------+-----------+--------+-------------+ - | 0 | 0 | 99.80 | 15.94 | 5.42 | - +---------+------+-----------+--------+-------------+ - | 0 | 1 | 99.76 | 15.80 | 5.24 | - +---------+------+-----------+--------+-------------+ - | 1 | 0 | 278.26 | 16.16 | 4.58 | - +---------+------+-----------+--------+-------------+ - | 1 | 1 | 96.88 | 16.00 | 4.52 | - +---------+------+-----------+--------+-------------+ - | 1 | 2 | 96.80 | 16.12 | 4.54 | - +---------+------+-----------+--------+-------------+ - | 1 | 3 | 96.88 | 16.12 | 4.54 | - +---------+------+-----------+--------+-------------+ + +---------+------+-----------+-----------------+-------------+ + | Cluster | Core | Powerdown | Wakeup | Cache Flush | + +---------+------+-----------+-----------------+-------------+ + | 0 | 0 | 100.04 | 20.32 (-38.50%) | 5.62 | + +---------+------+-----------+-----------------+-------------+ + | 0 | 1 | 99.78 | 20.6 (-36.10%) | 5.42 | + +---------+------+-----------+-----------------+-------------+ + | 1 | 0 | 278.28 | 19.52 | 4.32 | + +---------+------+-----------+-----------------+-------------+ + | 1 | 1 | 97.3 | 19.44 | 4.26 | + +---------+------+-----------+-----------------+-------------+ + | 1 | 2 | 97.56 | 19.52 | 4.32 | + +---------+------+-----------+-----------------+-------------+ + | 1 | 3 | 97.52 | 19.46 | 4.26 | + +---------+------+-----------+-----------------+-------------+ -.. table:: ``CPU_SUSPEND`` latencies (µs) to power level 0 in serial (v2.10) +.. table:: ``CPU_SUSPEND`` latencies (µs) to power level 0 in serial (v2.11) +---------+------+-----------+--------+-------------+ | Cluster | Core | Powerdown | Wakeup | Cache Flush | +---------+------+-----------+--------+-------------+ - | 0 | 0 | 99.84 | 18.86 | 5.54 | + | 0 | 0 | 106.78 | 19.2 | 5.32 | +---------+------+-----------+--------+-------------+ - | 0 | 1 | 100.2 | 18.82 | 5.66 | + | 0 | 1 | 107.44 | 19.64 | 5.44 | +---------+------+-----------+--------+-------------+ - | 1 | 0 | 278.12 | 20.56 | 4.48 | + | 1 | 0 | 295.82 | 19.14 | 4.34 | +---------+------+-----------+--------+-------------+ - | 1 | 1 | 96.68 | 20.62 | 4.3 | + | 1 | 1 | 104.34 | 19.18 | 4.28 | +---------+------+-----------+--------+-------------+ - | 1 | 2 | 96.94 | 20.14 | 4.42 | + | 1 | 2 | 103.96 | 19.34 | 4.4 | +---------+------+-----------+--------+-------------+ - | 1 | 3 | 96.68 | 20.46 | 4.32 | + | 1 | 3 | 104.32 | 19.18 | 4.34 | +---------+------+-----------+--------+-------------+ ``CPU_OFF`` on all non-lead CPUs @@ -231,82 +233,80 @@ Results ``CPU_OFF`` on all non-lead CPUs in sequence then, ``CPU_SUSPEND`` on the lead core to the deepest power level. -.. table:: ``CPU_OFF`` latencies (µs) on all non-lead CPUs (v2.9) +.. table:: ``CPU_OFF`` latencies (µs) on all non-lead CPUs (v2.12) - +---------+------+-----------+--------+-------------+ - | Cluster | Core | Powerdown | Wakeup | Cache Flush | - +---------+------+-----------+--------+-------------+ - | 0 | 0 | 235.76 | 26.14 | 137.80 | - +---------+------+-----------+--------+-------------+ - | 0 | 1 | 235.40 | 25.72 | 137.62 | - +---------+------+-----------+--------+-------------+ - | 1 | 0 | 174.70 | 22.40 | 77.26 | - +---------+------+-----------+--------+-------------+ - | 1 | 1 | 100.92 | 24.04 | 4.52 | - +---------+------+-----------+--------+-------------+ - | 1 | 2 | 100.68 | 22.44 | 4.36 | - +---------+------+-----------+--------+-------------+ - | 1 | 3 | 101.36 | 22.70 | 4.52 | - +---------+------+-----------+--------+-------------+ + +---------+------+-----------+-----------------+-------------+ + | Cluster | Core | Powerdown | Wakeup | Cache Flush | + +---------+------+-----------+-----------------+-------------+ + | 0 | 0 | 236.3 | 30.88 (-29.30%) | 137.76 | + +---------+------+-----------+-----------------+-------------+ + | 0 | 1 | 236.66 | 30.5 (-29.23%) | 138.02 | + +---------+------+-----------+-----------------+-------------+ + | 1 | 0 | 175.9 | 27.0 | 77.86 | + +---------+------+-----------+-----------------+-------------+ + | 1 | 1 | 100.96 | 27.56 | 4.26 | + +---------+------+-----------+-----------------+-------------+ + | 1 | 2 | 101.04 | 26.48 | 4.38 | + +---------+------+-----------+-----------------+-------------+ + | 1 | 3 | 101.08 | 26.74 | 4.4 | + +---------+------+-----------+-----------------+-------------+ -.. table:: ``CPU_OFF`` latencies (µs) on all non-lead CPUs (v2.10) +.. table:: ``CPU_OFF`` latencies (µs) on all non-lead CPUs (v2.11) - +---------------------------------------------------+ - | test_rt_instr_cpu_off_serial (latest) | +---------+------+-----------+--------+-------------+ | Cluster | Core | Powerdown | Wakeup | Cache Flush | +---------+------+-----------+--------+-------------+ - | 0 | 0 | 236.04 | 30.02 | 137.9 | + | 0 | 0 | 243.62 | 29.84 | 137.66 | +---------+------+-----------+--------+-------------+ - | 0 | 1 | 235.38 | 29.7 | 137.72 | + | 0 | 1 | 243.88 | 29.54 | 137.8 | +---------+------+-----------+--------+-------------+ - | 1 | 0 | 175.18 | 26.96 | 77.26 | + | 1 | 0 | 183.26 | 26.22 | 77.76 | +---------+------+-----------+--------+-------------+ - | 1 | 1 | 100.56 | 28.34 | 4.32 | + | 1 | 1 | 107.64 | 26.74 | 4.34 | +---------+------+-----------+--------+-------------+ - | 1 | 2 | 100.38 | 26.82 | 4.3 | + | 1 | 2 | 107.52 | 25.9 | 4.32 | +---------+------+-----------+--------+-------------+ - | 1 | 3 | 100.86 | 26.98 | 4.42 | + | 1 | 3 | 107.74 | 25.8 | 4.34 | +---------+------+-----------+--------+-------------+ ``CPU_VERSION`` in parallel ~~~~~~~~~~~~~~~~~~~~~~~~~~~ -.. table:: ``CPU_VERSION`` latency (µs) in parallel on all cores (2.9) - - +-------------+--------+-------------+ - | Cluster | Core | Latency | - +-------------+--------+-------------+ - | 0 | 0 | 1.48 | - +-------------+--------+-------------+ - | 0 | 1 | 1.04 | - +-------------+--------+-------------+ - | 1 | 0 | 0.56 | - +-------------+--------+-------------+ - | 1 | 1 | 0.92 | - +-------------+--------+-------------+ - | 1 | 2 | 0.96 | - +-------------+--------+-------------+ - | 1 | 3 | 0.96 | - +-------------+--------+-------------+ - -.. table:: ``CPU_VERSION`` latency (µs) in parallel on all cores (2.10) - - +-------------+--------+----------------------+ - | Cluster | Core | Latency | - +-------------+--------+----------------------+ - | 0 | 0 | 1.1 (-25.68%) | - +-------------+--------+----------------------+ - | 0 | 1 | 1.06 | - +-------------+--------+----------------------+ - | 1 | 0 | 0.58 | - +-------------+--------+----------------------+ - | 1 | 1 | 0.88 | - +-------------+--------+----------------------+ - | 1 | 2 | 0.92 | - +-------------+--------+----------------------+ - | 1 | 3 | 0.9 | - +-------------+--------+----------------------+ +.. table:: ``CPU_VERSION`` latency (µs) in parallel on all cores (2.12) + + +-------------+--------+--------------+ + | Cluster | Core | Latency | + +-------------+--------+--------------+ + | 0 | 0 | 1.0 | + +-------------+--------+--------------+ + | 0 | 1 | 1.02 | + +-------------+--------+--------------+ + | 1 | 0 | 0.52 | + +-------------+--------+--------------+ + | 1 | 1 | 0.94 | + +-------------+--------+--------------+ + | 1 | 2 | 0.94 | + +-------------+--------+--------------+ + | 1 | 3 | 0.92 | + +-------------+--------+--------------+ + +.. table:: ``CPU_VERSION`` latency (µs) in parallel on all cores (2.11) + + +-------------+--------+--------------+ + | Cluster | Core | Latency | + +-------------+--------+--------------+ + | 0 | 0 | 1.26 | + +-------------+--------+--------------+ + | 0 | 1 | 0.96 | + +-------------+--------+--------------+ + | 1 | 0 | 0.54 | + +-------------+--------+--------------+ + | 1 | 1 | 0.94 | + +-------------+--------+--------------+ + | 1 | 2 | 0.92 | + +-------------+--------+--------------+ + | 1 | 3 | 1.02 | + +-------------+--------+--------------+ Annotated Historic Results -------------------------- @@ -526,8 +526,9 @@ effects, given that these measurements are at the nano-second level. -------------- -*Copyright (c) 2019-2023, Arm Limited and Contributors. All rights reserved.* +*Copyright (c) 2019-2024, Arm Limited and Contributors. All rights reserved.* .. _Juno R1 platform: https://developer.arm.com/documentation/100122/latest/ .. _TF master as of 31/01/2017: https://git.trustedfirmware.org/TF-A/trusted-firmware-a.git/tree/?id=c38b36d -.. _v2.9-rc0: https://git.trustedfirmware.org/TF-A/trusted-firmware-a.git/tree/?h=v2.9-rc0 +.. _TF-A v2.12-rc0: https://git.trustedfirmware.org/TF-A/trusted-firmware-a.git/tree/?h=v2.12-rc0 +.. _TFTF v2.12-rc0: https://git.trustedfirmware.org/TF-A/tf-a-tests.git/tree/?h=v2.12-rc0 diff --git a/docs/perf/psci-performance-n1sdp.rst b/docs/perf/psci-performance-n1sdp.rst index fd3c9c94..178d8e64 100644 --- a/docs/perf/psci-performance-n1sdp.rst +++ b/docs/perf/psci-performance-n1sdp.rst @@ -6,8 +6,8 @@ contains an SoC consisting of two dual-core Arm N1 clusters. The following source trees and binaries were used: -- TF-A [`v2.9-rc0-16-g666aec401`_] -- TFTF [`v2.9-rc0`_] +- `TF-A v2.12-rc0`_ +- `TFTF v2.12-rc0`_ - SCP/MCP `Prebuilt Images`_ Please see the Runtime Instrumentation :ref:`Testing Methodology @@ -92,206 +92,192 @@ Results ``CPU_SUSPEND`` to deepest power level ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -.. table:: ``CPU_SUSPEND`` latencies (µs) to deepest power level in - parallel (v2.9) +.. table:: ``CPU_SUSPEND`` latencies (µs) to deepest power level in parallel (v2.12) + + +---------+------+----------------+--------+----------------+ + | Cluster | Core | Powerdown | Wakeup | Cache Flush | + +---------+------+----------------+--------+----------------+ + | 0 | 0 | 2.58 | 24.14 | 0.28 (-69.57%) | + +---------+------+----------------+--------+----------------+ + | 0 | 0 | 4.24 (-32.27%) | 40.1 | 0.3 | + +---------+------+----------------+--------+----------------+ + | 1 | 0 | 3.58 | 35.54 | 0.28 | + +---------+------+----------------+--------+----------------+ + | 1 | 0 | 3.28 | 42.36 | 0.3 | + +---------+------+----------------+--------+----------------+ + +.. table:: ``CPU_SUSPEND`` latencies (µs) to deepest power level in parallel (v2.11) + + +---------+------+----------------+--------+----------------+ + | Cluster | Core | Powerdown | Wakeup | Cache Flush | + +---------+------+----------------+--------+----------------+ + | 0 | 0 | 3.0 (+41.51%) | 23.14 | 1.2 (+185.71%) | + +---------+------+----------------+--------+----------------+ + | 0 | 0 | 4.6 | 35.86 | 0.3 | + +---------+------+----------------+--------+----------------+ + | 1 | 0 | 3.68 (+33.33%) | 33.36 | 0.3 | + +---------+------+----------------+--------+----------------+ + | 1 | 0 | 3.7 (+40.15%) | 38.1 | 0.28 | + +---------+------+----------------+--------+----------------+ + +.. table:: ``CPU_SUSPEND`` latencies (µs) to deepest power level in serial (v2.12) +---------+------+-----------+--------+-------------+ | Cluster | Core | Powerdown | Wakeup | Cache Flush | +---------+------+-----------+--------+-------------+ - | 0 | 0 | 2.80 | 10.08 | 0.80 | + | 0 | 0 | 1.9 | 23.8 | 0.36 | +---------+------+-----------+--------+-------------+ - | 0 | 0 | 4.14 | 15.92 | 0.16 | + | 0 | 0 | 2.26 | 23.86 | 0.34 | +---------+------+-----------+--------+-------------+ - | 1 | 0 | 3.68 | 12.96 | 0.16 | + | 1 | 0 | 2.02 | 23.4 | 0.36 | +---------+------+-----------+--------+-------------+ - | 1 | 0 | 3.36 | 18.58 | 0.18 | + | 1 | 0 | 2.24 | 23.84 | 0.36 | +---------+------+-----------+--------+-------------+ -.. table:: ``CPU_SUSPEND`` latencies (µs) to deepest power level in - parallel (v2.10) - - +---------+------+----------------+------------------+-----------------+ - | Cluster | Core | Powerdown | Wakeup | Cache Flush | - +---------+------+----------------+------------------+-----------------+ - | 0 | 0 | 2.12 | 23.94 (+137.50%) | 0.42 (-47.50%) | - +---------+------+----------------+------------------+-----------------+ - | 0 | 0 | 3.52 | 42.08 (+164.32%) | 0.26 (+62.50%) | - +---------+------+----------------+------------------+-----------------+ - | 1 | 0 | 2.76 (-25.00%) | 38.3 (+195.52%) | 0.26 (+62.50%) | - +---------+------+----------------+------------------+-----------------+ - | 1 | 0 | 2.64 | 44.56 (+139.83%) | 0.36 (+100.00%) | - +---------+------+----------------+------------------+-----------------+ - -.. table:: ``CPU_SUSPEND`` latencies (µs) to deepest power level in - serial (v2.9) +.. table:: ``CPU_SUSPEND`` latencies (µs) to deepest power level in serial (v2.11) +---------+------+-----------+--------+-------------+ | Cluster | Core | Powerdown | Wakeup | Cache Flush | +---------+------+-----------+--------+-------------+ - | 0 | 0 | 1.86 | 9.92 | 0.32 | + | 0 | 0 | 1.7 | 22.46 | 0.3 | +---------+------+-----------+--------+-------------+ - | 0 | 0 | 2.70 | 10.48 | 0.36 | + | 0 | 0 | 2.28 | 22.5 | 0.3 | +---------+------+-----------+--------+-------------+ - | 1 | 0 | 1.78 | 9.72 | 0.16 | + | 1 | 0 | 2.14 | 21.5 | 0.32 | +---------+------+-----------+--------+-------------+ - | 1 | 0 | 1.94 | 10.44 | 0.16 | + | 1 | 0 | 2.24 | 22.66 | 0.3 | +---------+------+-----------+--------+-------------+ -.. table:: ``CPU_SUSPEND`` latencies (µs) to deepest power level in - serial (v2.10) - - +---------+------+-----------+------------------+----------------+ - | Cluster | Core | Powerdown | Wakeup | Cache Flush | - +---------+------+-----------+------------------+----------------+ - | 0 | 0 | 1.74 | 23.7 (+138.91%) | 0.3 | - +---------+------+-----------+------------------+----------------+ - | 0 | 0 | 2.08 | 23.96 (+128.63%) | 0.26 (-27.78%) | - +---------+------+-----------+------------------+----------------+ - | 1 | 0 | 1.9 | 23.62 (+143.00%) | 0.28 (+75.00%) | - +---------+------+-----------+------------------+----------------+ - | 1 | 0 | 2.06 | 23.92 (+129.12%) | 0.26 (+62.50%) | - +---------+------+-----------+------------------+----------------+ - ``CPU_SUSPEND`` to power level 0 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -.. table:: ``CPU_SUSPEND`` latencies (µs) to power level 0 in - parallel (v2.9) - - +---------------------------------------------------+ - | test_rt_instr_cpu_susp_parallel | - +---------+------+-----------+--------+-------------+ - | Cluster | Core | Powerdown | Wakeup | Cache Flush | - +---------+------+-----------+--------+-------------+ - | 0 | 0 | 0.88 | 12.32 | 0.26 | - +---------+------+-----------+--------+-------------+ - | 0 | 0 | 2.12 | 14.62 | 0.26 | - +---------+------+-----------+--------+-------------+ - | 1 | 0 | 1.86 | 14.14 | 0.16 | - +---------+------+-----------+--------+-------------+ - | 1 | 0 | 1.92 | 9.44 | 0.18 | - +---------+------+-----------+--------+-------------+ +.. table:: ``CPU_SUSPEND`` latencies (µs) to power level 0 in parallel (v2.12) + + +---------+------+-----------+--------+----------------+ + | Cluster | Core | Powerdown | Wakeup | Cache Flush | + +---------+------+-----------+--------+----------------+ + | 0 | 0 | 1.46 | 31.7 | 0.32 | + +---------+------+-----------+--------+----------------+ + | 0 | 0 | 2.06 | 35.5 | 0.48 (+60.00%) | + +---------+------+-----------+--------+----------------+ + | 1 | 0 | 1.96 | 35.7 | 0.32 | + +---------+------+-----------+--------+----------------+ + | 1 | 0 | 2.08 | 23.38 | 0.28 | + +---------+------+-----------+--------+----------------+ + +.. table:: ``CPU_SUSPEND`` latencies (µs) to power level 0 in parallel (v2.11) + + +---------+------+----------------+--------+-------------+ + | Cluster | Core | Powerdown | Wakeup | Cache Flush | + +---------+------+----------------+--------+-------------+ + | 0 | 0 | 0.94 (-37.33%) | 30.36 | 0.3 | + +---------+------+----------------+--------+-------------+ + | 0 | 0 | 2.12 | 33.12 | 0.28 | + +---------+------+----------------+--------+-------------+ + | 1 | 0 | 2.08 | 32.56 | 0.3 | + +---------+------+----------------+--------+-------------+ + | 1 | 0 | 2.14 | 21.92 | 0.28 | + +---------+------+----------------+--------+-------------+ + +.. table:: ``CPU_SUSPEND`` latencies (µs) to power level 0 in serial (v2.12) + + +---------+------+-----------+--------+----------------+ + | Cluster | Core | Powerdown | Wakeup | Cache Flush | + +---------+------+-----------+--------+----------------+ + | 0 | 0 | 1.66 | 23.22 | 0.36 | + +---------+------+-----------+--------+----------------+ + | 0 | 0 | 2.58 | 23.72 | 0.78 (+85.71%) | + +---------+------+-----------+--------+----------------+ + | 1 | 0 | 2.02 | 23.84 | 0.38 | + +---------+------+-----------+--------+----------------+ + | 1 | 0 | 2.16 | 23.92 | 0.34 | + +---------+------+-----------+--------+----------------+ + +.. table:: ``CPU_SUSPEND`` latencies (µs) to power level 0 in serial (v2.11) -.. table:: ``CPU_SUSPEND`` latencies (µs) to power level 0 in - parallel (v2.10) - - +---------+------+---------------+------------------+----------------+ - | Cluster | Core | Powerdown | Wakeup | Cache Flush | - +---------+------+---------------+------------------+----------------+ - | 0 | 0 | 1.5 (+70.45%) | 35.02 (+184.25%) | 0.24 | - +---------+------+---------------+------------------+----------------+ - | 0 | 0 | 1.92 | 38.12 (+160.74%) | 0.28 | - +---------+------+---------------+------------------+----------------+ - | 1 | 0 | 1.88 | 38.1 (+169.45%) | 0.26 (+62.50%) | - +---------+------+---------------+------------------+----------------+ - | 1 | 0 | 2.04 | 23.1 (+144.70%) | 0.24 | - +---------+------+---------------+------------------+----------------+ - -.. table:: ``CPU_SUSPEND`` latencies (µs) to power level 0 in serial (v2.9) - - +---------------------------------------------------+ - | test_rt_instr_cpu_susp_serial | +---------+------+-----------+--------+-------------+ | Cluster | Core | Powerdown | Wakeup | Cache Flush | +---------+------+-----------+--------+-------------+ - | 0 | 0 | 1.52 | 9.40 | 0.30 | + | 0 | 0 | 1.64 | 21.88 | 0.34 | +---------+------+-----------+--------+-------------+ - | 0 | 0 | 1.92 | 9.80 | 0.18 | + | 0 | 0 | 2.42 | 21.76 | 0.34 | +---------+------+-----------+--------+-------------+ - | 1 | 0 | 2.20 | 9.60 | 0.14 | + | 1 | 0 | 2.02 | 21.14 | 0.32 | +---------+------+-----------+--------+-------------+ - | 1 | 0 | 1.82 | 9.78 | 0.18 | + | 1 | 0 | 2.18 | 22.3 | 0.34 | +---------+------+-----------+--------+-------------+ -.. table:: ``CPU_SUSPEND`` latencies (µs) to power level 0 in serial (v2.10) - - +---------+------+-----------+------------------+-----------------+ - | Cluster | Core | Powerdown | Wakeup | Cache Flush | - +---------+------+-----------+------------------+-----------------+ - | 0 | 0 | 1.52 | 23.08 (+145.53%) | 0.3 | - +---------+------+-----------+------------------+-----------------+ - | 0 | 0 | 1.98 | 23.68 (+141.63%) | 0.28 (+55.56%) | - +---------+------+-----------+------------------+-----------------+ - | 1 | 0 | 1.84 | 23.86 (+148.54%) | 0.28 (+100.00%) | - +---------+------+-----------+------------------+-----------------+ - | 1 | 0 | 1.98 | 23.68 (+142.13%) | 0.28 (+55.56%) | - +---------+------+-----------+------------------+-----------------+ - ``CPU_OFF`` on all non-lead CPUs ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ``CPU_OFF`` on all non-lead CPUs in sequence then, ``CPU_SUSPEND`` on the lead core to the deepest power level. -.. table:: ``CPU_OFF`` latencies (µs) on all non-lead CPUs (v2.9) - - +---------+------+-----------+--------+-------------+ - | Cluster | Core | Powerdown | Wakeup | Cache Flush | - +---------+------+-----------+--------+-------------+ - | 0 | 0 | 1.84 | 9.94 | 0.32 | - +---------+------+-----------+--------+-------------+ - | 0 | 0 | 14.20 | 13.10 | 0.50 | - +---------+------+-----------+--------+-------------+ - | 1 | 0 | 13.88 | 12.36 | 0.42 | - +---------+------+-----------+--------+-------------+ - | 1 | 0 | 14.40 | 13.26 | 0.52 | - +---------+------+-----------+--------+-------------+ - -.. table:: ``CPU_OFF`` latencies (µs) on all non-lead CPUs (v2.10) - - +---------+------+-----------+------------------+----------------+ - | Cluster | Core | Powerdown | Wakeup | Cache Flush | - +---------+------+-----------+------------------+----------------+ - | 0 | 0 | 1.78 | 23.7 (+138.43%) | 0.3 | - +---------+------+-----------+------------------+----------------+ - | 0 | 0 | 13.96 | 31.16 (+137.86%) | 0.34 (-32.00%) | - +---------+------+-----------+------------------+----------------+ - | 1 | 0 | 13.54 | 30.24 (+144.66%) | 0.26 (-38.10%) | - +---------+------+-----------+------------------+----------------+ - | 1 | 0 | 14.46 | 31.12 (+134.69%) | 0.7 (+34.62%) | - +---------+------+-----------+------------------+----------------+ +.. table:: ``CPU_OFF`` latencies (µs) on all non-lead CPUs (v2.12) + + +---------+------+-----------+--------+----------------+ + | Cluster | Core | Powerdown | Wakeup | Cache Flush | + +---------+------+-----------+--------+----------------+ + | 0 | 0 | 1.84 | 23.82 | 0.36 | + +---------+------+-----------+--------+----------------+ + | 0 | 0 | 14.18 | 31.78 | 0.56 (+86.67%) | + +---------+------+-----------+--------+----------------+ + | 1 | 0 | 13.64 | 30.54 | 0.36 | + +---------+------+-----------+--------+----------------+ + | 1 | 0 | 14.18 | 31.82 | 0.68 | + +---------+------+-----------+--------+----------------+ + +.. table:: ``CPU_OFF`` latencies (µs) on all non-lead CPUs (v2.11) + + +---------+------+-----------+--------+----------------+ + | Cluster | Core | Powerdown | Wakeup | Cache Flush | + +---------+------+-----------+--------+----------------+ + | 0 | 0 | 1.96 | 22.44 | 0.38 | + +---------+------+-----------+--------+----------------+ + | 0 | 0 | 13.76 | 30.34 | 0.26 | + +---------+------+-----------+--------+----------------+ + | 1 | 0 | 13.46 | 28.28 | 0.24 | + +---------+------+-----------+--------+----------------+ + | 1 | 0 | 13.84 | 30.06 | 0.28 (-60.00%) | + +---------+------+-----------+--------+----------------+ ``CPU_VERSION`` in parallel ~~~~~~~~~~~~~~~~~~~~~~~~~~~ -.. table:: ``CPU_VERSION`` latency (µs) in parallel on all cores (v2.9) - - +------------------------------------+ - | test_rt_instr_psci_version_parallel| - +-------------+--------+-------------+ - | Cluster | Core | Latency | - +-------------+--------+-------------+ - | 0 | 0 | 0.08 | - +-------------+--------+-------------+ - | 0 | 0 | 0.26 | - +-------------+--------+-------------+ - | 1 | 0 | 0.20 | - +-------------+--------+-------------+ - | 1 | 0 | 0.26 | - +-------------+--------+-------------+ - -.. table:: ``CPU_VERSION`` latency (µs) in parallel on all cores (v2.10) - - +----------------------------------------------+ - | test_rt_instr_psci_version_parallel (latest) | - +-------------+--------+-----------------------+ - | Cluster | Core | Latency | - +-------------+--------+-----------------------+ - | 0 | 0 | 0.14 (+75.00%) | - +-------------+--------+-----------------------+ - | 0 | 0 | 0.22 | - +-------------+--------+-----------------------+ - | 1 | 0 | 0.2 | - +-------------+--------+-----------------------+ - | 1 | 0 | 0.26 | - +-------------+--------+-----------------------+ +.. table:: ``CPU_VERSION`` latency (µs) in parallel on all cores (v2.12) + + +----------+------+-------------------+ + | Cluster | Core | Latency | + +----------+------+-------------------+ + | 0 | 0 | 0.14 | + +----------+------+-------------------+ + | 0 | 0 | 0.2 (-28.57%) | + +----------+------+-------------------+ + | 1 | 0 | 0.2 | + +----------+------+-------------------+ + | 1 | 0 | 0.26 | + +----------+------+-------------------+ + +.. table:: ``CPU_VERSION`` latency (µs) in parallel on all cores (v2.11) + + +-------------+--------+--------------+ + | Cluster | Core | Latency | + +-------------+--------+--------------+ + | 0 | 0 | 0.12 | + +-------------+--------+--------------+ + | 0 | 0 | 0.24 | + +-------------+--------+--------------+ + | 1 | 0 | 0.2 | + +-------------+--------+--------------+ + | 1 | 0 | 0.26 | + +-------------+--------+--------------+ -------------- -*Copyright (c) 2023, Arm Limited. All rights reserved.* +*Copyright (c) 2023-2024, Arm Limited. All rights reserved.* -.. _v2.9-rc0-16-g666aec401: https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/refs/heads/v2.9-rc0-16-g666aec401 -.. _v2.9-rc0: https://review.trustedfirmware.org/plugins/gitiles/TF-A/tf-a-tests/+/refs/tags/v2.9-rc0 +.. _TF-A v2.12-rc0: https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/refs/tags/v2.12-rc0 +.. _TFTF v2.12-rc0: https://review.trustedfirmware.org/plugins/gitiles/TF-A/tf-a-tests/+/refs/tags/v2.12-rc0 .. _user guide: https://gitlab.arm.com/arm-reference-solutions/arm-reference-solutions-docs/-/blob/master/docs/n1sdp/user-guide.rst -.. _Prebuilt Images: https://downloads.trustedfirmware.org/tf-a/css_scp_2.11.0/n1sdp/release/ +.. _Prebuilt Images: https://downloads.trustedfirmware.org/tf-a/css_scp_2.12.0/n1sdp/release/ .. _N1SDP: https://developer.arm.com/documentation/101489/latest diff --git a/docs/plat/amd-versal2.rst b/docs/plat/amd-versal2.rst new file mode 100644 index 00000000..876ab3c7 --- /dev/null +++ b/docs/plat/amd-versal2.rst @@ -0,0 +1,87 @@ +AMD Versal Gen 2 +================ + +Trusted Firmware-A implements the EL3 firmware layer for AMD Versal Gen 2. +The platform only uses the runtime part of TF-A as AMD Versal Gen 2 already +has a BootROM (BL1) and PMC FW (BL2). + +BL31 is TF-A. +BL32 is an optional Secure Payload. +BL33 is the non-secure world software (U-Boot, Linux etc). + +To build: +```bash +make RESET_TO_BL31=1 CROSS_COMPILE=aarch64-none-elf- PLAT=versal2 bl31 +``` + +To build TF-A for JTAG DCC console: +```bash +make RESET_TO_BL31=1 CROSS_COMPILE=aarch64-none-elf- PLAT=versal2 CONSOLE=dcc bl31 +``` + +To build TF-A with Errata management interface +```bash +make RESET_TO_BL31=1 CROSS_COMPILE=aarch64-none-elf- PLAT=versal2 bl31 ERRATA_ABI_SUPPORT=1 +``` + +To build TF-A with IPI CRC check: +```bash +make RESET_TO_BL31=1 CROSS_COMPILE=aarch64-none-elf- PLAT=versal2 bl31 IPI_CRC_CHECK=1 +``` + +AMD Versal Gen 2 platform specific build options +------------------------------------------------- + +* `MEM_BASE`: Specifies the base address of the bl31 binary. +* `MEM_SIZE`: Specifies the size of the memory region of the bl31 binary. +* `BL32_MEM_BASE`: Specifies the base address of the bl32 binary. +* `BL32_MEM_SIZE`: Specifies the size of the memory region of the bl32 binary. + +* `CONSOLE`: Select the console driver. Options: + - `pl011`, `pl011_0`: ARM pl011 UART 0 (default) + - `pl011_1` : ARM pl011 UART 1 + - `dcc` : JTAG Debug Communication Channel(DCC) + + +Reference DEN0028E SMC calling convention +------------------------------------------ + +Allocated subranges of Function Identifier to SIP services +------------------------------------------------------------ + ++-----------------------+-------------------------------------------------------+ +| SMC Function | Identifier Service type | ++-----------------------+-------------------------------------------------------+ +| 0xC2000000-0xC200FFFF | Fast SMC64 SiP Service Calls as per SMCCC Section 6.1 | ++-----------------------+-------------------------------------------------------+ + +IPI SMC call ranges +------------------- + ++---------------------------+-----------------------------------------------------------+ +| SMC Function Identifier | Service type | ++---------------------------+-----------------------------------------------------------+ +| 0xc2001000-0xc2001FFF | Fast SMC64 SiP Service call range used for AMD-Xilinx IPI | ++---------------------------+-----------------------------------------------------------+ + +PM SMC call ranges +------------------ + ++---------------------------+---------------------------------------------------------------------------+ +| SMC Function Identifier | Service type | ++---------------------------+---------------------------------------------------------------------------+ +| 0xc2000000-0xc2000FFF | Fast SMC64 SiP Service call range used for AMD-Xilinx Platform Management | ++---------------------------+---------------------------------------------------------------------------+ + +SMC function IDs for SiP Service queries +---------------------------------------- + ++--------------+--------------+--------------+ +| Service | Call UID | Revision | ++--------------+--------------+--------------+ +| SiP Service | 0x8200_FF01 | 0x8200_FF03 | ++--------------+--------------+--------------+ + +Call UID Query – Returns a unique identifier of the service provider. + +Revision Query – Returns revision details of the service implementor. diff --git a/docs/plat/arm/arm-build-options.rst b/docs/plat/arm/arm-build-options.rst index 3301067e..afbb1576 100644 --- a/docs/plat/arm/arm-build-options.rst +++ b/docs/plat/arm/arm-build-options.rst @@ -16,6 +16,12 @@ Arm Platform Build Options should match the frame used by the Non-Secure image (normally the Linux kernel). Default is true (access to the frame is allowed). +- ``ARM_FW_CONFIG_LOAD_ENABLE``: Boolean option to enable the loading of + FW_CONFIG device trees from the Firmware Image Package (FIP). When enabled, + BL2 calls the platform specific function `arm_bl2_el3_plat_config_load`. + This function is responsible for loading, parsing, and validating the + FW_CONFIG device trees from the FIP. The option depends on RESET_TO_BL2. + - ``ARM_DISABLE_TRUSTED_WDOG``: boolean option to disable the Trusted Watchdog. By default, Arm platforms use a watchdog to trigger a system reset in case an error is encountered during the boot process (for example, when an image @@ -121,17 +127,6 @@ Arm CSS Platform-Specific Build Options management operations and for SCP RAM Firmware transfer. If this option is set to 1, then SCMI/SDS drivers will be used. Default is 0. - - ``CSS_SGI_CHIP_COUNT``: Configures the number of chips on a SGI/RD platform - which supports multi-chip operation. If ``CSS_SGI_CHIP_COUNT`` is set to any - valid value greater than 1, the platform code performs required configuration - to support multi-chip operation. - -- ``CSS_SGI_PLATFORM_VARIANT``: Selects the variant of a SGI/RD platform. A - particular SGI/RD platform may have multiple variants which may differ in - core count, cluster count or other peripherals. This build option is used - to select the appropriate platform variant for the build. The range of - valid values is platform specific. - - ``CSS_SYSTEM_GRACEFUL_RESET``: Build option to enable graceful powerdown of CPU core on reset. This build option can be used on CSS platforms that require all the CPUs to execute the CPU specific power down sequence to @@ -152,8 +147,22 @@ Arm Juno Build Options AArch64 and facilitates the loading of ``SP_MIN`` and BL33 as AArch32 executable images. +Arm Neoverse RD Platform Build Options +-------------------------------------- + + - ``NRD_CHIP_COUNT``: Configures the number of chips on a Neoverse RD platform + which supports multi-chip operation. If ``NRD_CHIP_COUNT`` is set to any + valid value greater than 1, the platform code performs required configuration + to support multi-chip operation. + +- ``NRD_PLATFORM_VARIANT``: Selects the variant of a Neoverse RD platform. A + particular Neoverse RD platform may have multiple variants which may differ in + core count, cluster count or other peripherals. This build option is used to + select the appropriate platform variant for the build. The range of valid + values is platform specific. + -------------- .. |FIP in a GPT image| image:: ../../resources/diagrams/FIP_in_a_GPT_image.png -*Copyright (c) 2019-2023, Arm Limited. All rights reserved.* +*Copyright (c) 2019-2024, Arm Limited. All rights reserved.* diff --git a/docs/plat/arm/automotive_rd/index.rst b/docs/plat/arm/automotive_rd/index.rst new file mode 100644 index 00000000..d0db6ac5 --- /dev/null +++ b/docs/plat/arm/automotive_rd/index.rst @@ -0,0 +1,50 @@ +RD-1 AE (Kronos) Platform +========================= + +Some of the features of the RD-1 AE platform referenced in TF-A include: + +- Neoverse-V3AE, Arm9.2-A application processor (64-bit mode) +- A GICv4-compatible GIC-720AE + +Further information on RD1-AE is available at `rd1ae`_ + +Boot Sequence +------------- + +BL2 –> BL31 –> BL33 + +The boot process starts from RSE (Runtime Security Engine) that loads the BL2 image +and signals the System Control Processor (SCP) to power up the Application Processor (AP). +The AP then runs BL2, which loads the rest of the images, including the runtime firmware +BL31, and proceeds to execute it. Finally, it passes control to the non-secure world +BL33 (u-boot). + +BL2 performs the actions described in the `Trusted Board Boot (TBB)`_ document. + +Build Procedure (TF-A only) +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +- Obtain `Arm toolchain`_ and set the CROSS_COMPILE environment variable to + point to the toolchain folder. + +- Build TF-A: + + .. code:: shell + + make \ + PLAT=rd1ae \ + MBEDTLS_DIR=<mbedtls_dir> \ + ARCH=aarch64 \ + CREATE_KEYS=1 \ + GENERATE_COT=1 \ + TRUSTED_BOARD_BOOT=1 \ + COT=tbbr \ + ARM_ROTPK_LOCATION=devel_rsa \ + ROT_KEY=plat/arm/board/common/rotpk/arm_rotprivk_rsa.pem \ + BL33=<path to u-boot binary> \ + +*Copyright (c) 2024, Arm Limited. All rights reserved.* + +.. _Arm Toolchain: https://developer.arm.com/tools-and-software/open-source-software/developer-tools/gnu-toolchain/downloads +.. _rd1ae: https://developer.arm.com/Tools%20and%20Software/Arm%20Reference%20Design-1%20AE +.. _Trusted Board Boot (TBB): https://trustedfirmware-a.readthedocs.io/en/latest/design/trusted-board-boot.html diff --git a/docs/plat/arm/fvp/fvp-aemv8-base.rst b/docs/plat/arm/fvp/fvp-aemv8-base.rst new file mode 100644 index 00000000..6dd35e53 --- /dev/null +++ b/docs/plat/arm/fvp/fvp-aemv8-base.rst @@ -0,0 +1,154 @@ +Running on the AEMv8 Base FVP +============================= + +AArch64 with reset to BL1 entrypoint +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +The following ``FVP_Base_RevC-2xAEMv8A`` parameters should be used to boot Linux +with 8 CPUs using the AArch64 build of TF-A. + +.. code:: shell + + <path-to>/FVP_Base_RevC-2xAEMv8A \ + -C pctl.startup=0.0.0.0 \ + -C bp.secure_memory=1 \ + -C bp.tzc_400.diagnostics=1 \ + -C cluster0.NUM_CORES=4 \ + -C cluster1.NUM_CORES=4 \ + -C cache_state_modelled=1 \ + -C bp.secureflashloader.fname="<path-to>/<bl1-binary>" \ + -C bp.flashloader0.fname="<path-to>/<FIP-binary>" \ + --data cluster0.cpu0="<path-to>/<kernel-binary>"@0x80080000 \ + --data cluster0.cpu0="<path-to>/<ramdisk>"@0x84000000 + +.. note:: + The ``FVP_Base_RevC-2xAEMv8A`` has shifted affinities and requires + a specific DTS for all the CPUs to be loaded. + +AArch32 with reset to BL1 entrypoint +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +The following ``FVP_Base_AEMv8A-AEMv8A`` parameters should be used to boot Linux +with 8 CPUs using the AArch32 build of TF-A. + +.. code:: shell + + <path-to>/FVP_Base_AEMv8A-AEMv8A \ + -C pctl.startup=0.0.0.0 \ + -C bp.secure_memory=1 \ + -C bp.tzc_400.diagnostics=1 \ + -C cluster0.NUM_CORES=4 \ + -C cluster1.NUM_CORES=4 \ + -C cache_state_modelled=1 \ + -C cluster0.cpu0.CONFIG64=0 \ + -C cluster0.cpu1.CONFIG64=0 \ + -C cluster0.cpu2.CONFIG64=0 \ + -C cluster0.cpu3.CONFIG64=0 \ + -C cluster1.cpu0.CONFIG64=0 \ + -C cluster1.cpu1.CONFIG64=0 \ + -C cluster1.cpu2.CONFIG64=0 \ + -C cluster1.cpu3.CONFIG64=0 \ + -C bp.secureflashloader.fname="<path-to>/<bl1-binary>" \ + -C bp.flashloader0.fname="<path-to>/<FIP-binary>" \ + --data cluster0.cpu0="<path-to>/<kernel-binary>"@0x80080000 \ + --data cluster0.cpu0="<path-to>/<ramdisk>"@0x84000000 + +AArch64 with reset to BL31 entrypoint +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +The following ``FVP_Base_RevC-2xAEMv8A`` parameters should be used to boot Linux +with 8 CPUs using the AArch64 build of TF-A. + +.. code:: shell + + <path-to>/FVP_Base_RevC-2xAEMv8A \ + -C pctl.startup=0.0.0.0 \ + -C bp.secure_memory=1 \ + -C bp.tzc_400.diagnostics=1 \ + -C cluster0.NUM_CORES=4 \ + -C cluster1.NUM_CORES=4 \ + -C cache_state_modelled=1 \ + -C cluster0.cpu0.RVBAR=0x04010000 \ + -C cluster0.cpu1.RVBAR=0x04010000 \ + -C cluster0.cpu2.RVBAR=0x04010000 \ + -C cluster0.cpu3.RVBAR=0x04010000 \ + -C cluster1.cpu0.RVBAR=0x04010000 \ + -C cluster1.cpu1.RVBAR=0x04010000 \ + -C cluster1.cpu2.RVBAR=0x04010000 \ + -C cluster1.cpu3.RVBAR=0x04010000 \ + --data cluster0.cpu0="<path-to>/<bl31-binary>"@0x04010000 \ + --data cluster0.cpu0="<path-to>/<bl32-binary>"@0xff000000 \ + --data cluster0.cpu0="<path-to>/<bl33-binary>"@0x88000000 \ + --data cluster0.cpu0="<path-to>/<fdt>"@0x82000000 \ + --data cluster0.cpu0="<path-to>/<kernel-binary>"@0x80080000 \ + --data cluster0.cpu0="<path-to>/<ramdisk>"@0x84000000 + +Notes: + +- Position Independent Executable (PIE) support is enabled in this + config allowing BL31 to be loaded at any valid address for execution. + +- Since a FIP is not loaded when using BL31 as reset entrypoint, the + ``--data="<path-to><bl31|bl32|bl33-binary>"@<base-address-of-binary>`` + parameter is needed to load the individual bootloader images in memory. + BL32 image is only needed if BL31 has been built to expect a Secure-EL1 + Payload. For the same reason, the FDT needs to be compiled from the DT source + and loaded via the ``--data cluster0.cpu0="<path-to>/<fdt>"@0x82000000`` + parameter. + +- The ``FVP_Base_RevC-2xAEMv8A`` has shifted affinities and requires a + specific DTS for all the CPUs to be loaded. + +- The ``-C cluster<X>.cpu<Y>.RVBAR=@<base-address-of-bl31>`` parameter, where + X and Y are the cluster and CPU numbers respectively, is used to set the + reset vector for each core. + +- Changing the default value of ``ARM_TSP_RAM_LOCATION`` will also require + changing the value of + ``--data="<path-to><bl32-binary>"@<base-address-of-bl32>`` to the new value of + ``BL32_BASE``. + +AArch32 with reset to SP_MIN entrypoint +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +The following ``FVP_Base_AEMv8A-AEMv8A`` parameters should be used to boot Linux +with 8 CPUs using the AArch32 build of TF-A. + +.. code:: shell + + <path-to>/FVP_Base_AEMv8A-AEMv8A \ + -C pctl.startup=0.0.0.0 \ + -C bp.secure_memory=1 \ + -C bp.tzc_400.diagnostics=1 \ + -C cluster0.NUM_CORES=4 \ + -C cluster1.NUM_CORES=4 \ + -C cache_state_modelled=1 \ + -C cluster0.cpu0.CONFIG64=0 \ + -C cluster0.cpu1.CONFIG64=0 \ + -C cluster0.cpu2.CONFIG64=0 \ + -C cluster0.cpu3.CONFIG64=0 \ + -C cluster1.cpu0.CONFIG64=0 \ + -C cluster1.cpu1.CONFIG64=0 \ + -C cluster1.cpu2.CONFIG64=0 \ + -C cluster1.cpu3.CONFIG64=0 \ + -C cluster0.cpu0.RVBAR=0x04002000 \ + -C cluster0.cpu1.RVBAR=0x04002000 \ + -C cluster0.cpu2.RVBAR=0x04002000 \ + -C cluster0.cpu3.RVBAR=0x04002000 \ + -C cluster1.cpu0.RVBAR=0x04002000 \ + -C cluster1.cpu1.RVBAR=0x04002000 \ + -C cluster1.cpu2.RVBAR=0x04002000 \ + -C cluster1.cpu3.RVBAR=0x04002000 \ + --data cluster0.cpu0="<path-to>/<bl32-binary>"@0x04002000 \ + --data cluster0.cpu0="<path-to>/<bl33-binary>"@0x88000000 \ + --data cluster0.cpu0="<path-to>/<fdt>"@0x82000000 \ + --data cluster0.cpu0="<path-to>/<kernel-binary>"@0x80080000 \ + --data cluster0.cpu0="<path-to>/<ramdisk>"@0x84000000 + +.. note:: + Position Independent Executable (PIE) support is enabled in this + config allowing SP_MIN to be loaded at any valid address for execution. + +-------------- + +*Copyright (c) 2019-2024, Arm Limited. All rights reserved.* diff --git a/docs/plat/arm/fvp/fvp-build-options.rst b/docs/plat/arm/fvp/fvp-build-options.rst new file mode 100644 index 00000000..b0359fa9 --- /dev/null +++ b/docs/plat/arm/fvp/fvp-build-options.rst @@ -0,0 +1,51 @@ +.. _build_options_arm_fvp_platform: + +Arm FVP Platform Specific Build Options +--------------------------------------- + +- ``FVP_CLUSTER_COUNT`` : Configures the cluster count to be used to + build the topology tree within TF-A. By default TF-A is configured for dual + cluster topology and this option can be used to override the default value. + +- ``FVP_INTERCONNECT_DRIVER``: Selects the interconnect driver to be built. The + default interconnect driver depends on the value of ``FVP_CLUSTER_COUNT`` as + explained in the options below: + + - ``FVP_CCI`` : The CCI driver is selected. This is the default + if 0 < ``FVP_CLUSTER_COUNT`` <= 2. + - ``FVP_CCN`` : The CCN driver is selected. This is the default + if ``FVP_CLUSTER_COUNT`` > 2. + +- ``FVP_MAX_CPUS_PER_CLUSTER``: Sets the maximum number of CPUs implemented in + a single cluster. This option defaults to 4. + +- ``FVP_MAX_PE_PER_CPU``: Sets the maximum number of PEs implemented on any CPU + in the system. This option defaults to 1. Note that the build option + ``ARM_PLAT_MT`` doesn't have any effect on FVP platforms. + +- ``FVP_USE_GIC_DRIVER`` : Selects the GIC driver to be built. Options: + + - ``FVP_GICV2`` : The GICv2 only driver is selected + - ``FVP_GICV3`` : The GICv3 only driver is selected (default option) + +- ``FVP_HW_CONFIG_DTS`` : Specify the path to the DTS file to be compiled + to DTB and packaged in FIP as the HW_CONFIG. See :ref:`Firmware Design` for + details on HW_CONFIG. By default, this is initialized to a sensible DTS + file in ``fdts/`` folder depending on other build options. But some cases, + like shifted affinity format for MPIDR, cannot be detected at build time + and this option is needed to specify the appropriate DTS file. + +- ``FVP_HW_CONFIG`` : Specify the path to the HW_CONFIG blob to be packaged in + FIP. See :ref:`Firmware Design` for details on HW_CONFIG. This option is + similar to the ``FVP_HW_CONFIG_DTS`` option, but it directly specifies the + HW_CONFIG blob instead of the DTS file. This option is useful to override + the default HW_CONFIG selected by the build system. + +- ``FVP_GICR_REGION_PROTECTION``: Mark the redistributor pages of + inactive/fused CPU cores as read-only. The default value of this option + is ``0``, which means the redistributor pages of all CPU cores are marked + as read and write. + +-------------- + +*Copyright (c) 2019-2024, Arm Limited. All rights reserved.* diff --git a/docs/plat/arm/fvp/fvp-cortex-a32.rst b/docs/plat/arm/fvp/fvp-cortex-a32.rst new file mode 100644 index 00000000..df17eed6 --- /dev/null +++ b/docs/plat/arm/fvp/fvp-cortex-a32.rst @@ -0,0 +1,47 @@ +Running on the Cortex-A32 Base FVP (AArch32) +============================================ + +With reset to BL1 entrypoint +^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +The following ``FVP_Base_Cortex-A32x4`` model parameters should be used to +boot Linux with 4 CPUs using the AArch32 build of TF-A. + +.. code:: shell + + <path-to>/FVP_Base_Cortex-A32x4 \ + -C pctl.startup=0.0.0.0 \ + -C bp.secure_memory=1 \ + -C bp.tzc_400.diagnostics=1 \ + -C cache_state_modelled=1 \ + -C bp.secureflashloader.fname="<path-to>/<bl1-binary>" \ + -C bp.flashloader0.fname="<path-to>/<FIP-binary>" \ + --data cluster0.cpu0="<path-to>/<kernel-binary>"@0x80080000 \ + --data cluster0.cpu0="<path-to>/<ramdisk>"@0x84000000 + +With reset to SP_MIN entrypoint +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +The following ``FVP_Base_Cortex-A32x4`` model parameters should be used to +boot Linux with 4 CPUs using the AArch32 build of TF-A. + +.. code:: shell + + <path-to>/FVP_Base_Cortex-A32x4 \ + -C pctl.startup=0.0.0.0 \ + -C bp.secure_memory=1 \ + -C bp.tzc_400.diagnostics=1 \ + -C cache_state_modelled=1 \ + -C cluster0.cpu0.RVBARADDR=0x04002000 \ + -C cluster0.cpu1.RVBARADDR=0x04002000 \ + -C cluster0.cpu2.RVBARADDR=0x04002000 \ + -C cluster0.cpu3.RVBARADDR=0x04002000 \ + --data cluster0.cpu0="<path-to>/<bl32-binary>"@0x04002000 \ + --data cluster0.cpu0="<path-to>/<bl33-binary>"@0x88000000 \ + --data cluster0.cpu0="<path-to>/<fdt>"@0x82000000 \ + --data cluster0.cpu0="<path-to>/<kernel-binary>"@0x80080000 \ + --data cluster0.cpu0="<path-to>/<ramdisk>"@0x84000000 + +-------------- + +*Copyright (c) 2019-2024, Arm Limited. All rights reserved.* diff --git a/docs/plat/arm/fvp/fvp-cortex-a57-a53.rst b/docs/plat/arm/fvp/fvp-cortex-a57-a53.rst new file mode 100644 index 00000000..8f541140 --- /dev/null +++ b/docs/plat/arm/fvp/fvp-cortex-a57-a53.rst @@ -0,0 +1,52 @@ +Running on the Cortex-A57-A53 Base FVP +====================================== + +With reset to BL1 entrypoint +^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +The following ``FVP_Base_Cortex-A57x4-A53x4`` model parameters should be used to +boot Linux with 8 CPUs using the AArch64 build of TF-A. + +.. code:: shell + + <path-to>/FVP_Base_Cortex-A57x4-A53x4 \ + -C pctl.startup=0.0.0.0 \ + -C bp.secure_memory=1 \ + -C bp.tzc_400.diagnostics=1 \ + -C cache_state_modelled=1 \ + -C bp.secureflashloader.fname="<path-to>/<bl1-binary>" \ + -C bp.flashloader0.fname="<path-to>/<FIP-binary>" \ + --data cluster0.cpu0="<path-to>/<kernel-binary>"@0x80080000 \ + --data cluster0.cpu0="<path-to>/<ramdisk>"@0x84000000 + +With reset to BL31 entrypoint +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +The following ``FVP_Base_Cortex-A57x4-A53x4`` model parameters should be used to +boot Linux with 8 CPUs using the AArch64 build of TF-A. + +.. code:: shell + + <path-to>/FVP_Base_Cortex-A57x4-A53x4 \ + -C pctl.startup=0.0.0.0 \ + -C bp.secure_memory=1 \ + -C bp.tzc_400.diagnostics=1 \ + -C cache_state_modelled=1 \ + -C cluster0.cpu0.RVBARADDR=0x04010000 \ + -C cluster0.cpu1.RVBARADDR=0x04010000 \ + -C cluster0.cpu2.RVBARADDR=0x04010000 \ + -C cluster0.cpu3.RVBARADDR=0x04010000 \ + -C cluster1.cpu0.RVBARADDR=0x04010000 \ + -C cluster1.cpu1.RVBARADDR=0x04010000 \ + -C cluster1.cpu2.RVBARADDR=0x04010000 \ + -C cluster1.cpu3.RVBARADDR=0x04010000 \ + --data cluster0.cpu0="<path-to>/<bl31-binary>"@0x04010000 \ + --data cluster0.cpu0="<path-to>/<bl32-binary>"@0xff000000 \ + --data cluster0.cpu0="<path-to>/<bl33-binary>"@0x88000000 \ + --data cluster0.cpu0="<path-to>/<fdt>"@0x82000000 \ + --data cluster0.cpu0="<path-to>/<kernel-binary>"@0x80080000 \ + --data cluster0.cpu0="<path-to>/<ramdisk>"@0x84000000 + +-------------- + +*Copyright (c) 2019-2024, Arm Limited. All rights reserved.* diff --git a/docs/plat/arm/fvp/fvp-foundation.rst b/docs/plat/arm/fvp/fvp-foundation.rst new file mode 100644 index 00000000..dd6f9dce --- /dev/null +++ b/docs/plat/arm/fvp/fvp-foundation.rst @@ -0,0 +1,42 @@ +Running on the Foundation FVP +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +The following ``Foundation_Platform`` parameters should be used to boot Linux with +4 CPUs using the AArch64 build of TF-A. + +.. code:: shell + + <path-to>/Foundation_Platform \ + --cores=4 \ + --arm-v8.0 \ + --secure-memory \ + --visualization \ + --gicv3 \ + --data="<path-to>/<bl1-binary>"@0x0 \ + --data="<path-to>/<FIP-binary>"@0x08000000 \ + --data="<path-to>/<kernel-binary>"@0x80080000 \ + --data="<path-to>/<ramdisk-binary>"@0x84000000 + +Notes: + +- BL1 is loaded at the start of the Trusted ROM. +- The Firmware Image Package is loaded at the start of NOR FLASH0. +- The firmware loads the FDT packaged in FIP to the DRAM. The FDT load address + is specified via the ``load-address`` property in the ``hw-config`` node of + `FW_CONFIG for FVP`_. +- The default use-case for the Foundation FVP is to use the ``--gicv3`` option + and enable the GICv3 device in the model. Note that without this option, + the Foundation FVP defaults to legacy (Versatile Express) memory map which + is not supported by TF-A. +- In order for TF-A to run correctly on the Foundation FVP, the architecture + versions must match. The Foundation FVP defaults to the highest v8.x + version it supports but the default build for TF-A is for v8.0. To avoid + issues either start the Foundation FVP to use v8.0 architecture using the + ``--arm-v8.0`` option, or build TF-A with an appropriate value for + ``ARM_ARCH_MINOR``. + +-------------- + +*Copyright (c) 2019-2024, Arm Limited. All rights reserved.* + +.. _FW_CONFIG for FVP: https://git.trustedfirmware.org/TF-A/trusted-firmware-a.git/tree/plat/arm/board/fvp/fdts/fvp_fw_config.dts diff --git a/docs/plat/arm/fvp/fvp-specific-configs.rst b/docs/plat/arm/fvp/fvp-specific-configs.rst new file mode 100644 index 00000000..63b3c31c --- /dev/null +++ b/docs/plat/arm/fvp/fvp-specific-configs.rst @@ -0,0 +1,209 @@ +Booting Firmware Update images +------------------------------ + +When Firmware Update (FWU) is enabled there are at least 2 new images +that have to be loaded, the Non-Secure FWU ROM (NS-BL1U), and the +FWU FIP. + +The additional fip images must be loaded with: + +:: + + --data cluster0.cpu0="<path_to>/ns_bl1u.bin"@0x0beb8000 [ns_bl1u_base_address] + --data cluster0.cpu0="<path_to>/fwu_fip.bin"@0x08400000 [ns_bl2u_base_address] + +The address ns_bl1u_base_address is the value of NS_BL1U_BASE. +In the same way, the address ns_bl2u_base_address is the value of +NS_BL2U_BASE. + +Booting an EL3 payload +---------------------- + +The EL3 payloads boot flow requires the CPU's mailbox to be cleared at reset for +the secondary CPUs holding pen to work properly. Unfortunately, its reset value +is undefined on the FVP platform and the FVP platform code doesn't clear it. +Therefore, one must modify the way the model is normally invoked in order to +clear the mailbox at start-up. + +One way to do that is to create an 8-byte file containing all zero bytes using +the following command: + +.. code:: shell + + dd if=/dev/zero of=mailbox.dat bs=1 count=8 + +and pre-load it into the FVP memory at the mailbox address (i.e. ``0x04000000``) +using the following model parameters: + +:: + + --data cluster0.cpu0=mailbox.dat@0x04000000 [Base FVPs] + --data=mailbox.dat@0x04000000 [Foundation FVP] + +To provide the model with the EL3 payload image, the following methods may be +used: + +#. If the EL3 payload is able to execute in place, it may be programmed into + flash memory. On Base Cortex and AEM FVPs, the following model parameter + loads it at the base address of the NOR FLASH1 (the NOR FLASH0 is already + used for the FIP): + + :: + + -C bp.flashloader1.fname="<path-to>/<el3-payload>" + + On Foundation FVP, there is no flash loader component and the EL3 payload + may be programmed anywhere in flash using method 3 below. + +#. When using the ``SPIN_ON_BL1_EXIT=1`` loading method, the following DS-5 + command may be used to load the EL3 payload ELF image over JTAG: + + :: + + load <path-to>/el3-payload.elf + +#. The EL3 payload may be pre-loaded in volatile memory using the following + model parameters: + + :: + + --data cluster0.cpu0="<path-to>/el3-payload>"@address [Base FVPs] + --data="<path-to>/<el3-payload>"@address [Foundation FVP] + + The address provided to the FVP must match the ``EL3_PAYLOAD_BASE`` address + used when building TF-A. + +Booting a preloaded kernel image (Base FVP) +------------------------------------------- + +The following example uses a simplified boot flow by directly jumping from the +TF-A to the Linux kernel, which will use a ramdisk as filesystem. This can be +useful if both the kernel and the device tree blob (DTB) are already present in +memory (like in FVP). + +For example, if the kernel is loaded at ``0x80080000`` and the DTB is loaded at +address ``0x82000000``, the firmware can be built like this: + +.. code:: shell + + CROSS_COMPILE=aarch64-none-elf- \ + make PLAT=fvp DEBUG=1 \ + RESET_TO_BL31=1 \ + ARM_LINUX_KERNEL_AS_BL33=1 \ + PRELOADED_BL33_BASE=0x80080000 \ + ARM_PRELOADED_DTB_BASE=0x82000000 \ + all fip + +Now, it is needed to modify the DTB so that the kernel knows the address of the +ramdisk. The following script generates a patched DTB from the provided one, +assuming that the ramdisk is loaded at address ``0x84000000``. Note that this +script assumes that the user is using a ramdisk image prepared for U-Boot, like +the ones provided by Linaro. If using a ramdisk without this header,the ``0x40`` +offset in ``INITRD_START`` has to be removed. + +.. code:: bash + + #!/bin/bash + + # Path to the input DTB + KERNEL_DTB=<path-to>/<fdt> + # Path to the output DTB + PATCHED_KERNEL_DTB=<path-to>/<patched-fdt> + # Base address of the ramdisk + INITRD_BASE=0x84000000 + # Path to the ramdisk + INITRD=<path-to>/<ramdisk.img> + + # Skip uboot header (64 bytes) + INITRD_START=$(printf "0x%x" $((${INITRD_BASE} + 0x40)) ) + INITRD_SIZE=$(stat -Lc %s ${INITRD}) + INITRD_END=$(printf "0x%x" $((${INITRD_BASE} + ${INITRD_SIZE})) ) + + CHOSEN_NODE=$(echo \ + "/ { \ + chosen { \ + linux,initrd-start = <${INITRD_START}>; \ + linux,initrd-end = <${INITRD_END}>; \ + }; \ + };") + + echo $(dtc -O dts -I dtb ${KERNEL_DTB}) ${CHOSEN_NODE} | \ + dtc -O dtb -o ${PATCHED_KERNEL_DTB} - + +And the FVP binary can be run with the following command: + +.. code:: shell + + <path-to>/FVP_Base_AEMv8A-AEMv8A \ + -C pctl.startup=0.0.0.0 \ + -C bp.secure_memory=1 \ + -C cluster0.NUM_CORES=4 \ + -C cluster1.NUM_CORES=4 \ + -C cache_state_modelled=1 \ + -C cluster0.cpu0.RVBAR=0x04001000 \ + -C cluster0.cpu1.RVBAR=0x04001000 \ + -C cluster0.cpu2.RVBAR=0x04001000 \ + -C cluster0.cpu3.RVBAR=0x04001000 \ + -C cluster1.cpu0.RVBAR=0x04001000 \ + -C cluster1.cpu1.RVBAR=0x04001000 \ + -C cluster1.cpu2.RVBAR=0x04001000 \ + -C cluster1.cpu3.RVBAR=0x04001000 \ + --data cluster0.cpu0="<path-to>/bl31.bin"@0x04001000 \ + --data cluster0.cpu0="<path-to>/<patched-fdt>"@0x82000000 \ + --data cluster0.cpu0="<path-to>/<kernel-binary>"@0x80080000 \ + --data cluster0.cpu0="<path-to>/<ramdisk.img>"@0x84000000 + +Obtaining the Flattened Device Trees +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Depending on the FVP configuration and Linux configuration used, different +FDT files are required. FDT source files for the Foundation and Base FVPs can +be found in the TF-A source directory under ``fdts/``. The Foundation FVP has +a subset of the Base FVP components. For example, the Foundation FVP lacks +CLCD and MMC support, and has only one CPU cluster. + +.. note:: + It is not recommended to use the FDTs built along the kernel because not + all FDTs are available from there. + +The dynamic configuration capability is enabled in the firmware for FVPs. +This means that the firmware can authenticate and load the FDT if present in +FIP. A default FDT is packaged into FIP during the build based on +the build configuration. This can be overridden by using the ``FVP_HW_CONFIG`` +or ``FVP_HW_CONFIG_DTS`` build options (refer to +:ref:`build_options_arm_fvp_platform` for details on the options). + +- ``fvp-base-gicv2-psci.dts`` + + For use with models such as the Cortex-A57-A53 or Cortex-A32 Base FVPs + without shifted affinities and with Base memory map configuration. + +- ``fvp-base-gicv3-psci.dts`` + + For use with models such as the Cortex-A57-A53 or Cortex-A32 Base FVPs + without shifted affinities and with Base memory map configuration and + Linux GICv3 support. + +- ``fvp-base-gicv3-psci-1t.dts`` + + For use with models such as the AEMv8-RevC Base FVP with shifted affinities, + single threaded CPUs, Base memory map configuration and Linux GICv3 support. + +- ``fvp-base-gicv3-psci-dynamiq.dts`` + + For use with models as the Cortex-A55-A75 Base FVPs with shifted affinities, + single cluster, single threaded CPUs, Base memory map configuration and Linux + GICv3 support. + +- ``fvp-foundation-gicv2-psci.dts`` + + For use with Foundation FVP with Base memory map configuration. + +- ``fvp-foundation-gicv3-psci.dts`` + + (Default) For use with Foundation FVP with Base memory map configuration + and Linux GICv3 support. + +-------------- + +*Copyright (c) 2019-2024, Arm Limited. All rights reserved.* diff --git a/docs/plat/arm/fvp/fvp-support.rst b/docs/plat/arm/fvp/fvp-support.rst new file mode 100644 index 00000000..ad76cf16 --- /dev/null +++ b/docs/plat/arm/fvp/fvp-support.rst @@ -0,0 +1,102 @@ +Fixed Virtual Platform (FVP) Support +------------------------------------ + +This section lists the supported Arm |FVP| platforms. Please refer to the FVP +documentation for a detailed description of the model parameter options. + +The latest version of the AArch64 build of TF-A has been tested on the following +Arm FVPs without shifted affinities, and that do not support threaded CPU cores +(64-bit host machine only). + +.. note:: + The FVP models used are Version 11.26 Build 11, unless otherwise stated. + +- ``FVP_Base_AEMvA-AEMvA`` +- ``FVP_Base_RevC-2xAEMvA`` +- ``FVP_Base_Cortex-A32x4`` +- ``FVP_Base_Cortex-A35x4`` +- ``FVP_Base_Cortex-A53x4`` +- ``FVP_Base_Cortex-A55`` +- ``FVP_Base_Cortex-A57x1-A53x1`` +- ``FVP_Base_Cortex-A57x2-A53x4`` +- ``FVP_Base_Cortex-A57x4`` +- ``FVP_Base_Cortex-A57x4-A53x4`` +- ``FVP_Base_Cortex-A65`` (Version 11.24/24) +- ``FVP_Base_Cortex-A65AE`` (Version 11.24/24) +- ``FVP_Base_Cortex-A710`` +- ``FVP_Base_Cortex-A72x4`` +- ``FVP_Base_Cortex-A72x4-A53x4`` +- ``FVP_Base_Cortex-A73x4`` +- ``FVP_Base_Cortex-A73x4-A53x4`` +- ``FVP_Base_Cortex-A75`` +- ``FVP_Base_Cortex-A76`` +- ``FVP_Base_Cortex-A76AE`` +- ``FVP_Base_Cortex-A77`` +- ``FVP_Base_Cortex-A78`` +- ``FVP_Base_Cortex-A78AE`` +- ``FVP_Base_Cortex-A78C`` +- ``FVP_Base_Cortex-X2`` +- ``FVP_Base_Neoverse-E1`` (Version 11.24/24) +- ``FVP_Base_Neoverse-N1`` +- ``FVP_Base_Neoverse-N2`` +- ``FVP_Base_Neoverse-V1`` +- ``FVP_BaseR_AEMv8R`` +- ``FVP_Morello`` (Version 0.11/33) +- ``FVP_RD_V1`` +- ``FVP_RD_1_AE`` (Version 11.27/20) +- ``FVP_TC3`` (Version 11.26/16) +- ``FVP_TC4`` (Version 0.0/8404) + +The latest version of the AArch32 build of TF-A has been tested on the +following Arm FVPs without shifted affinities, and that do not support threaded +CPU cores (64-bit host machine only). + +- ``FVP_Base_AEMvA`` +- ``FVP_Base_AEMvA-AEMvA`` +- ``FVP_Base_Cortex-A32x4`` + +.. note:: + The ``FVP_Base_RevC-2xAEMv8A`` FVP only supports shifted affinities, which + is not compatible with legacy GIC configurations. Therefore this FVP does not + support these legacy GIC configurations. + +The *Foundation* and *Base* FVPs can be downloaded free of charge. See the `Arm +FVP website`_. The Cortex-A models listed above are also available to download +from `Arm's website`_. + +.. note:: + The build numbers quoted above are those reported by launching the FVP + with the ``--version`` parameter. + +.. note:: + Linaro provides a ramdisk image in prebuilt FVP configurations and full + file systems that can be downloaded separately. To run an FVP with a virtio + file system image an additional FVP configuration option + ``-C bp.virtioblockdevice.image_path="<path-to>/<file-system-image>`` can be + used. + +.. note:: + The software will not work on Version 1.0 of the Foundation FVP. + The commands below would report an ``unhandled argument`` error in this case. + +.. note:: + FVPs can be launched with ``--cadi-server`` option such that a + CADI-compliant debugger (for example, Arm DS-5) can connect to and control + its execution. + +.. warning:: + Since FVP model Version 11.0 Build 11.0.34 and Version 8.5 Build 0.8.5202 + the internal synchronisation timings changed compared to older versions of + the models. The models can be launched with ``-Q 100`` option if they are + required to match the run time characteristics of the older versions. + +All the above platforms have been tested with `Linaro Release 20.01`_. + +-------------- + +*Copyright (c) 2019-2024, Arm Limited. All rights reserved.* + +.. _Arm's website: `FVP models`_ +.. _FVP models: https://developer.arm.com/products/system-design/fixed-virtual-platforms +.. _Linaro Release 20.01: http://releases.linaro.org/members/arm/platforms/20.01 +.. _Arm FVP website: https://developer.arm.com/products/system-design/fixed-virtual-platforms diff --git a/docs/plat/arm/fvp/index.rst b/docs/plat/arm/fvp/index.rst index 700020f5..088beec5 100644 --- a/docs/plat/arm/fvp/index.rst +++ b/docs/plat/arm/fvp/index.rst @@ -1,639 +1,31 @@ Arm Fixed Virtual Platforms (FVP) ================================= -Fixed Virtual Platform (FVP) Support ------------------------------------- +Arm |FVP|\s are complete simulations of an Arm system, including processor, +memory and peripherals. They enable software development without the need for +real hardware. -This section lists the supported Arm |FVP| platforms. Please refer to the FVP -documentation for a detailed description of the model parameter options. +There exists many types of FVPs. This page provides details on how to build and +run TF-A on some of these FVPs. -The latest version of the AArch64 build of TF-A has been tested on the following -Arm FVPs without shifted affinities, and that do not support threaded CPU cores -(64-bit host machine only). +Please also refer to the TF-A CI scripts under the `model/`_ directory for an +exhaustive list of |FVP|\s which TF-A is regularly tested on as part of our +continuous integration strategy. -.. note:: - The FVP models used are Version 11.22 Build 14, unless otherwise stated. +.. toctree:: + :maxdepth: 1 + :caption: Contents -- ``Foundation_Platform`` -- ``FVP_Base_AEMv8A-AEMv8A-AEMv8A-AEMv8A-CCN502`` (Version 11.17/21) -- ``FVP_Base_AEMv8A-GIC600AE`` (Version 11.17/21) -- ``FVP_Base_AEMvA`` -- ``FVP_Base_AEMvA-AEMvA`` -- ``FVP_Base_Cortex-A32x4`` (Version 11.12/38) -- ``FVP_Base_Cortex-A35x4`` -- ``FVP_Base_Cortex-A53x4`` -- ``FVP_Base_Cortex-A55`` -- ``FVP_Base_Cortex-A55x4+Cortex-A75x4`` -- ``FVP_Base_Cortex-A55x4+Cortex-A76x2`` -- ``FVP_Base_Cortex-A57x1-A53x1`` -- ``FVP_Base_Cortex-A57x2-A53x4`` -- ``FVP_Base_Cortex-A57x4`` -- ``FVP_Base_Cortex-A57x4-A53x4`` -- ``FVP_Base_Cortex-A65`` -- ``FVP_Base_Cortex-A65AE`` -- ``FVP_Base_Cortex-A710x4`` (Version 11.17/21) -- ``FVP_Base_Cortex-A72x4`` -- ``FVP_Base_Cortex-A72x4-A53x4`` -- ``FVP_Base_Cortex-A73x4`` -- ``FVP_Base_Cortex-A73x4-A53x4`` -- ``FVP_Base_Cortex-A75`` -- ``FVP_Base_Cortex-A76`` -- ``FVP_Base_Cortex-A76AE`` -- ``FVP_Base_Cortex-A77`` -- ``FVP_Base_Cortex-A78`` -- ``FVP_Base_Cortex-A78AE`` -- ``FVP_Base_Cortex-A78C`` -- ``FVP_Base_Cortex-X2x4`` (Version 11.17/21) -- ``FVP_Base_Neoverse-E1`` -- ``FVP_Base_Neoverse-N1`` -- ``FVP_Base_Neoverse-V1`` -- ``FVP_Base_RevC-2xAEMvA`` -- ``FVP_BaseR_AEMv8R`` -- ``FVP_Morello`` (Version 0.11/33) -- ``FVP_RD_V1`` -- ``FVP_TC1`` -- ``FVP_TC2`` (Version 11.20/24) - -The latest version of the AArch32 build of TF-A has been tested on the -following Arm FVPs without shifted affinities, and that do not support threaded -CPU cores (64-bit host machine only). - -- ``FVP_Base_AEMvA`` -- ``FVP_Base_AEMvA-AEMvA`` -- ``FVP_Base_Cortex-A32x4`` - -.. note:: - The ``FVP_Base_RevC-2xAEMvA`` FVP only supports shifted affinities, which - is not compatible with legacy GIC configurations. Therefore this FVP does not - support these legacy GIC configurations. - -The *Foundation* and *Base* FVPs can be downloaded free of charge. See the `Arm -FVP website`_. The Cortex-A models listed above are also available to download -from `Arm's website`_. - -.. note:: - The build numbers quoted above are those reported by launching the FVP - with the ``--version`` parameter. - -.. note:: - Linaro provides a ramdisk image in prebuilt FVP configurations and full - file systems that can be downloaded separately. To run an FVP with a virtio - file system image an additional FVP configuration option - ``-C bp.virtioblockdevice.image_path="<path-to>/<file-system-image>`` can be - used. - -.. note:: - The software will not work on Version 1.0 of the Foundation FVP. - The commands below would report an ``unhandled argument`` error in this case. - -.. note:: - FVPs can be launched with ``--cadi-server`` option such that a - CADI-compliant debugger (for example, Arm DS-5) can connect to and control - its execution. - -.. warning:: - Since FVP model Version 11.0 Build 11.0.34 and Version 8.5 Build 0.8.5202 - the internal synchronisation timings changed compared to older versions of - the models. The models can be launched with ``-Q 100`` option if they are - required to match the run time characteristics of the older versions. - -All the above platforms have been tested with `Linaro Release 20.01`_. - -.. _build_options_arm_fvp_platform: - -Arm FVP Platform Specific Build Options ---------------------------------------- - -- ``FVP_CLUSTER_COUNT`` : Configures the cluster count to be used to - build the topology tree within TF-A. By default TF-A is configured for dual - cluster topology and this option can be used to override the default value. - -- ``FVP_INTERCONNECT_DRIVER``: Selects the interconnect driver to be built. The - default interconnect driver depends on the value of ``FVP_CLUSTER_COUNT`` as - explained in the options below: - - - ``FVP_CCI`` : The CCI driver is selected. This is the default - if 0 < ``FVP_CLUSTER_COUNT`` <= 2. - - ``FVP_CCN`` : The CCN driver is selected. This is the default - if ``FVP_CLUSTER_COUNT`` > 2. - -- ``FVP_MAX_CPUS_PER_CLUSTER``: Sets the maximum number of CPUs implemented in - a single cluster. This option defaults to 4. - -- ``FVP_MAX_PE_PER_CPU``: Sets the maximum number of PEs implemented on any CPU - in the system. This option defaults to 1. Note that the build option - ``ARM_PLAT_MT`` doesn't have any effect on FVP platforms. - -- ``FVP_USE_GIC_DRIVER`` : Selects the GIC driver to be built. Options: - - - ``FVP_GICV2`` : The GICv2 only driver is selected - - ``FVP_GICV3`` : The GICv3 only driver is selected (default option) - -- ``FVP_HW_CONFIG_DTS`` : Specify the path to the DTS file to be compiled - to DTB and packaged in FIP as the HW_CONFIG. See :ref:`Firmware Design` for - details on HW_CONFIG. By default, this is initialized to a sensible DTS - file in ``fdts/`` folder depending on other build options. But some cases, - like shifted affinity format for MPIDR, cannot be detected at build time - and this option is needed to specify the appropriate DTS file. - -- ``FVP_HW_CONFIG`` : Specify the path to the HW_CONFIG blob to be packaged in - FIP. See :ref:`Firmware Design` for details on HW_CONFIG. This option is - similar to the ``FVP_HW_CONFIG_DTS`` option, but it directly specifies the - HW_CONFIG blob instead of the DTS file. This option is useful to override - the default HW_CONFIG selected by the build system. - -- ``FVP_GICR_REGION_PROTECTION``: Mark the redistributor pages of - inactive/fused CPU cores as read-only. The default value of this option - is ``0``, which means the redistributor pages of all CPU cores are marked - as read and write. - -Booting Firmware Update images ------------------------------- - -When Firmware Update (FWU) is enabled there are at least 2 new images -that have to be loaded, the Non-Secure FWU ROM (NS-BL1U), and the -FWU FIP. - -The additional fip images must be loaded with: - -:: - - --data cluster0.cpu0="<path_to>/ns_bl1u.bin"@0x0beb8000 [ns_bl1u_base_address] - --data cluster0.cpu0="<path_to>/fwu_fip.bin"@0x08400000 [ns_bl2u_base_address] - -The address ns_bl1u_base_address is the value of NS_BL1U_BASE. -In the same way, the address ns_bl2u_base_address is the value of -NS_BL2U_BASE. - -Booting an EL3 payload ----------------------- - -The EL3 payloads boot flow requires the CPU's mailbox to be cleared at reset for -the secondary CPUs holding pen to work properly. Unfortunately, its reset value -is undefined on the FVP platform and the FVP platform code doesn't clear it. -Therefore, one must modify the way the model is normally invoked in order to -clear the mailbox at start-up. - -One way to do that is to create an 8-byte file containing all zero bytes using -the following command: - -.. code:: shell - - dd if=/dev/zero of=mailbox.dat bs=1 count=8 - -and pre-load it into the FVP memory at the mailbox address (i.e. ``0x04000000``) -using the following model parameters: - -:: - - --data cluster0.cpu0=mailbox.dat@0x04000000 [Base FVPs] - --data=mailbox.dat@0x04000000 [Foundation FVP] - -To provide the model with the EL3 payload image, the following methods may be -used: - -#. If the EL3 payload is able to execute in place, it may be programmed into - flash memory. On Base Cortex and AEM FVPs, the following model parameter - loads it at the base address of the NOR FLASH1 (the NOR FLASH0 is already - used for the FIP): - - :: - - -C bp.flashloader1.fname="<path-to>/<el3-payload>" - - On Foundation FVP, there is no flash loader component and the EL3 payload - may be programmed anywhere in flash using method 3 below. - -#. When using the ``SPIN_ON_BL1_EXIT=1`` loading method, the following DS-5 - command may be used to load the EL3 payload ELF image over JTAG: - - :: - - load <path-to>/el3-payload.elf - -#. The EL3 payload may be pre-loaded in volatile memory using the following - model parameters: - - :: - - --data cluster0.cpu0="<path-to>/el3-payload>"@address [Base FVPs] - --data="<path-to>/<el3-payload>"@address [Foundation FVP] - - The address provided to the FVP must match the ``EL3_PAYLOAD_BASE`` address - used when building TF-A. - -Booting a preloaded kernel image (Base FVP) -------------------------------------------- - -The following example uses a simplified boot flow by directly jumping from the -TF-A to the Linux kernel, which will use a ramdisk as filesystem. This can be -useful if both the kernel and the device tree blob (DTB) are already present in -memory (like in FVP). - -For example, if the kernel is loaded at ``0x80080000`` and the DTB is loaded at -address ``0x82000000``, the firmware can be built like this: - -.. code:: shell - - CROSS_COMPILE=aarch64-none-elf- \ - make PLAT=fvp DEBUG=1 \ - RESET_TO_BL31=1 \ - ARM_LINUX_KERNEL_AS_BL33=1 \ - PRELOADED_BL33_BASE=0x80080000 \ - ARM_PRELOADED_DTB_BASE=0x82000000 \ - all fip - -Now, it is needed to modify the DTB so that the kernel knows the address of the -ramdisk. The following script generates a patched DTB from the provided one, -assuming that the ramdisk is loaded at address ``0x84000000``. Note that this -script assumes that the user is using a ramdisk image prepared for U-Boot, like -the ones provided by Linaro. If using a ramdisk without this header,the ``0x40`` -offset in ``INITRD_START`` has to be removed. - -.. code:: bash - - #!/bin/bash - - # Path to the input DTB - KERNEL_DTB=<path-to>/<fdt> - # Path to the output DTB - PATCHED_KERNEL_DTB=<path-to>/<patched-fdt> - # Base address of the ramdisk - INITRD_BASE=0x84000000 - # Path to the ramdisk - INITRD=<path-to>/<ramdisk.img> - - # Skip uboot header (64 bytes) - INITRD_START=$(printf "0x%x" $((${INITRD_BASE} + 0x40)) ) - INITRD_SIZE=$(stat -Lc %s ${INITRD}) - INITRD_END=$(printf "0x%x" $((${INITRD_BASE} + ${INITRD_SIZE})) ) - - CHOSEN_NODE=$(echo \ - "/ { \ - chosen { \ - linux,initrd-start = <${INITRD_START}>; \ - linux,initrd-end = <${INITRD_END}>; \ - }; \ - };") - - echo $(dtc -O dts -I dtb ${KERNEL_DTB}) ${CHOSEN_NODE} | \ - dtc -O dtb -o ${PATCHED_KERNEL_DTB} - - -And the FVP binary can be run with the following command: - -.. code:: shell - - <path-to>/FVP_Base_AEMv8A-AEMv8A \ - -C pctl.startup=0.0.0.0 \ - -C bp.secure_memory=1 \ - -C cluster0.NUM_CORES=4 \ - -C cluster1.NUM_CORES=4 \ - -C cache_state_modelled=1 \ - -C cluster0.cpu0.RVBAR=0x04001000 \ - -C cluster0.cpu1.RVBAR=0x04001000 \ - -C cluster0.cpu2.RVBAR=0x04001000 \ - -C cluster0.cpu3.RVBAR=0x04001000 \ - -C cluster1.cpu0.RVBAR=0x04001000 \ - -C cluster1.cpu1.RVBAR=0x04001000 \ - -C cluster1.cpu2.RVBAR=0x04001000 \ - -C cluster1.cpu3.RVBAR=0x04001000 \ - --data cluster0.cpu0="<path-to>/bl31.bin"@0x04001000 \ - --data cluster0.cpu0="<path-to>/<patched-fdt>"@0x82000000 \ - --data cluster0.cpu0="<path-to>/<kernel-binary>"@0x80080000 \ - --data cluster0.cpu0="<path-to>/<ramdisk.img>"@0x84000000 - -Obtaining the Flattened Device Trees -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -Depending on the FVP configuration and Linux configuration used, different -FDT files are required. FDT source files for the Foundation and Base FVPs can -be found in the TF-A source directory under ``fdts/``. The Foundation FVP has -a subset of the Base FVP components. For example, the Foundation FVP lacks -CLCD and MMC support, and has only one CPU cluster. - -.. note:: - It is not recommended to use the FDTs built along the kernel because not - all FDTs are available from there. - -The dynamic configuration capability is enabled in the firmware for FVPs. -This means that the firmware can authenticate and load the FDT if present in -FIP. A default FDT is packaged into FIP during the build based on -the build configuration. This can be overridden by using the ``FVP_HW_CONFIG`` -or ``FVP_HW_CONFIG_DTS`` build options (refer to -:ref:`build_options_arm_fvp_platform` for details on the options). - -- ``fvp-base-gicv2-psci.dts`` - - For use with models such as the Cortex-A57-A53 or Cortex-A32 Base FVPs - without shifted affinities and with Base memory map configuration. - -- ``fvp-base-gicv3-psci.dts`` - - For use with models such as the Cortex-A57-A53 or Cortex-A32 Base FVPs - without shifted affinities and with Base memory map configuration and - Linux GICv3 support. - -- ``fvp-base-gicv3-psci-1t.dts`` - - For use with models such as the AEMv8-RevC Base FVP with shifted affinities, - single threaded CPUs, Base memory map configuration and Linux GICv3 support. - -- ``fvp-base-gicv3-psci-dynamiq.dts`` - - For use with models as the Cortex-A55-A75 Base FVPs with shifted affinities, - single cluster, single threaded CPUs, Base memory map configuration and Linux - GICv3 support. - -- ``fvp-foundation-gicv2-psci.dts`` - - For use with Foundation FVP with Base memory map configuration. - -- ``fvp-foundation-gicv3-psci.dts`` - - (Default) For use with Foundation FVP with Base memory map configuration - and Linux GICv3 support. - - -Running on the Foundation FVP with reset to BL1 entrypoint -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -The following ``Foundation_Platform`` parameters should be used to boot Linux with -4 CPUs using the AArch64 build of TF-A. - -.. code:: shell - - <path-to>/Foundation_Platform \ - --cores=4 \ - --arm-v8.0 \ - --secure-memory \ - --visualization \ - --gicv3 \ - --data="<path-to>/<bl1-binary>"@0x0 \ - --data="<path-to>/<FIP-binary>"@0x08000000 \ - --data="<path-to>/<kernel-binary>"@0x80080000 \ - --data="<path-to>/<ramdisk-binary>"@0x84000000 - -Notes: - -- BL1 is loaded at the start of the Trusted ROM. -- The Firmware Image Package is loaded at the start of NOR FLASH0. -- The firmware loads the FDT packaged in FIP to the DRAM. The FDT load address - is specified via the ``load-address`` property in the ``hw-config`` node of - `FW_CONFIG for FVP`_. -- The default use-case for the Foundation FVP is to use the ``--gicv3`` option - and enable the GICv3 device in the model. Note that without this option, - the Foundation FVP defaults to legacy (Versatile Express) memory map which - is not supported by TF-A. -- In order for TF-A to run correctly on the Foundation FVP, the architecture - versions must match. The Foundation FVP defaults to the highest v8.x - version it supports but the default build for TF-A is for v8.0. To avoid - issues either start the Foundation FVP to use v8.0 architecture using the - ``--arm-v8.0`` option, or build TF-A with an appropriate value for - ``ARM_ARCH_MINOR``. - -Running on the AEMv8 Base FVP with reset to BL1 entrypoint -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -The following ``FVP_Base_RevC-2xAEMv8A`` parameters should be used to boot Linux -with 8 CPUs using the AArch64 build of TF-A. - -.. code:: shell - - <path-to>/FVP_Base_RevC-2xAEMv8A \ - -C pctl.startup=0.0.0.0 \ - -C bp.secure_memory=1 \ - -C bp.tzc_400.diagnostics=1 \ - -C cluster0.NUM_CORES=4 \ - -C cluster1.NUM_CORES=4 \ - -C cache_state_modelled=1 \ - -C bp.secureflashloader.fname="<path-to>/<bl1-binary>" \ - -C bp.flashloader0.fname="<path-to>/<FIP-binary>" \ - --data cluster0.cpu0="<path-to>/<kernel-binary>"@0x80080000 \ - --data cluster0.cpu0="<path-to>/<ramdisk>"@0x84000000 - -.. note:: - The ``FVP_Base_RevC-2xAEMv8A`` has shifted affinities and requires - a specific DTS for all the CPUs to be loaded. - -Running on the AEMv8 Base FVP (AArch32) with reset to BL1 entrypoint -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -The following ``FVP_Base_AEMv8A-AEMv8A`` parameters should be used to boot Linux -with 8 CPUs using the AArch32 build of TF-A. - -.. code:: shell - - <path-to>/FVP_Base_AEMv8A-AEMv8A \ - -C pctl.startup=0.0.0.0 \ - -C bp.secure_memory=1 \ - -C bp.tzc_400.diagnostics=1 \ - -C cluster0.NUM_CORES=4 \ - -C cluster1.NUM_CORES=4 \ - -C cache_state_modelled=1 \ - -C cluster0.cpu0.CONFIG64=0 \ - -C cluster0.cpu1.CONFIG64=0 \ - -C cluster0.cpu2.CONFIG64=0 \ - -C cluster0.cpu3.CONFIG64=0 \ - -C cluster1.cpu0.CONFIG64=0 \ - -C cluster1.cpu1.CONFIG64=0 \ - -C cluster1.cpu2.CONFIG64=0 \ - -C cluster1.cpu3.CONFIG64=0 \ - -C bp.secureflashloader.fname="<path-to>/<bl1-binary>" \ - -C bp.flashloader0.fname="<path-to>/<FIP-binary>" \ - --data cluster0.cpu0="<path-to>/<kernel-binary>"@0x80080000 \ - --data cluster0.cpu0="<path-to>/<ramdisk>"@0x84000000 - -Running on the Cortex-A57-A53 Base FVP with reset to BL1 entrypoint -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -The following ``FVP_Base_Cortex-A57x4-A53x4`` model parameters should be used to -boot Linux with 8 CPUs using the AArch64 build of TF-A. - -.. code:: shell - - <path-to>/FVP_Base_Cortex-A57x4-A53x4 \ - -C pctl.startup=0.0.0.0 \ - -C bp.secure_memory=1 \ - -C bp.tzc_400.diagnostics=1 \ - -C cache_state_modelled=1 \ - -C bp.secureflashloader.fname="<path-to>/<bl1-binary>" \ - -C bp.flashloader0.fname="<path-to>/<FIP-binary>" \ - --data cluster0.cpu0="<path-to>/<kernel-binary>"@0x80080000 \ - --data cluster0.cpu0="<path-to>/<ramdisk>"@0x84000000 - -Running on the Cortex-A32 Base FVP (AArch32) with reset to BL1 entrypoint -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -The following ``FVP_Base_Cortex-A32x4`` model parameters should be used to -boot Linux with 4 CPUs using the AArch32 build of TF-A. - -.. code:: shell - - <path-to>/FVP_Base_Cortex-A32x4 \ - -C pctl.startup=0.0.0.0 \ - -C bp.secure_memory=1 \ - -C bp.tzc_400.diagnostics=1 \ - -C cache_state_modelled=1 \ - -C bp.secureflashloader.fname="<path-to>/<bl1-binary>" \ - -C bp.flashloader0.fname="<path-to>/<FIP-binary>" \ - --data cluster0.cpu0="<path-to>/<kernel-binary>"@0x80080000 \ - --data cluster0.cpu0="<path-to>/<ramdisk>"@0x84000000 - - -Running on the AEMv8 Base FVP with reset to BL31 entrypoint -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -The following ``FVP_Base_RevC-2xAEMv8A`` parameters should be used to boot Linux -with 8 CPUs using the AArch64 build of TF-A. - -.. code:: shell - - <path-to>/FVP_Base_RevC-2xAEMv8A \ - -C pctl.startup=0.0.0.0 \ - -C bp.secure_memory=1 \ - -C bp.tzc_400.diagnostics=1 \ - -C cluster0.NUM_CORES=4 \ - -C cluster1.NUM_CORES=4 \ - -C cache_state_modelled=1 \ - -C cluster0.cpu0.RVBAR=0x04010000 \ - -C cluster0.cpu1.RVBAR=0x04010000 \ - -C cluster0.cpu2.RVBAR=0x04010000 \ - -C cluster0.cpu3.RVBAR=0x04010000 \ - -C cluster1.cpu0.RVBAR=0x04010000 \ - -C cluster1.cpu1.RVBAR=0x04010000 \ - -C cluster1.cpu2.RVBAR=0x04010000 \ - -C cluster1.cpu3.RVBAR=0x04010000 \ - --data cluster0.cpu0="<path-to>/<bl31-binary>"@0x04010000 \ - --data cluster0.cpu0="<path-to>/<bl32-binary>"@0xff000000 \ - --data cluster0.cpu0="<path-to>/<bl33-binary>"@0x88000000 \ - --data cluster0.cpu0="<path-to>/<fdt>"@0x82000000 \ - --data cluster0.cpu0="<path-to>/<kernel-binary>"@0x80080000 \ - --data cluster0.cpu0="<path-to>/<ramdisk>"@0x84000000 - -Notes: - -- Position Independent Executable (PIE) support is enabled in this - config allowing BL31 to be loaded at any valid address for execution. - -- Since a FIP is not loaded when using BL31 as reset entrypoint, the - ``--data="<path-to><bl31|bl32|bl33-binary>"@<base-address-of-binary>`` - parameter is needed to load the individual bootloader images in memory. - BL32 image is only needed if BL31 has been built to expect a Secure-EL1 - Payload. For the same reason, the FDT needs to be compiled from the DT source - and loaded via the ``--data cluster0.cpu0="<path-to>/<fdt>"@0x82000000`` - parameter. - -- The ``FVP_Base_RevC-2xAEMv8A`` has shifted affinities and requires a - specific DTS for all the CPUs to be loaded. - -- The ``-C cluster<X>.cpu<Y>.RVBAR=@<base-address-of-bl31>`` parameter, where - X and Y are the cluster and CPU numbers respectively, is used to set the - reset vector for each core. - -- Changing the default value of ``ARM_TSP_RAM_LOCATION`` will also require - changing the value of - ``--data="<path-to><bl32-binary>"@<base-address-of-bl32>`` to the new value of - ``BL32_BASE``. - - -Running on the AEMv8 Base FVP (AArch32) with reset to SP_MIN entrypoint -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -The following ``FVP_Base_AEMv8A-AEMv8A`` parameters should be used to boot Linux -with 8 CPUs using the AArch32 build of TF-A. - -.. code:: shell - - <path-to>/FVP_Base_AEMv8A-AEMv8A \ - -C pctl.startup=0.0.0.0 \ - -C bp.secure_memory=1 \ - -C bp.tzc_400.diagnostics=1 \ - -C cluster0.NUM_CORES=4 \ - -C cluster1.NUM_CORES=4 \ - -C cache_state_modelled=1 \ - -C cluster0.cpu0.CONFIG64=0 \ - -C cluster0.cpu1.CONFIG64=0 \ - -C cluster0.cpu2.CONFIG64=0 \ - -C cluster0.cpu3.CONFIG64=0 \ - -C cluster1.cpu0.CONFIG64=0 \ - -C cluster1.cpu1.CONFIG64=0 \ - -C cluster1.cpu2.CONFIG64=0 \ - -C cluster1.cpu3.CONFIG64=0 \ - -C cluster0.cpu0.RVBAR=0x04002000 \ - -C cluster0.cpu1.RVBAR=0x04002000 \ - -C cluster0.cpu2.RVBAR=0x04002000 \ - -C cluster0.cpu3.RVBAR=0x04002000 \ - -C cluster1.cpu0.RVBAR=0x04002000 \ - -C cluster1.cpu1.RVBAR=0x04002000 \ - -C cluster1.cpu2.RVBAR=0x04002000 \ - -C cluster1.cpu3.RVBAR=0x04002000 \ - --data cluster0.cpu0="<path-to>/<bl32-binary>"@0x04002000 \ - --data cluster0.cpu0="<path-to>/<bl33-binary>"@0x88000000 \ - --data cluster0.cpu0="<path-to>/<fdt>"@0x82000000 \ - --data cluster0.cpu0="<path-to>/<kernel-binary>"@0x80080000 \ - --data cluster0.cpu0="<path-to>/<ramdisk>"@0x84000000 - -.. note:: - Position Independent Executable (PIE) support is enabled in this - config allowing SP_MIN to be loaded at any valid address for execution. - -Running on the Cortex-A57-A53 Base FVP with reset to BL31 entrypoint -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -The following ``FVP_Base_Cortex-A57x4-A53x4`` model parameters should be used to -boot Linux with 8 CPUs using the AArch64 build of TF-A. - -.. code:: shell - - <path-to>/FVP_Base_Cortex-A57x4-A53x4 \ - -C pctl.startup=0.0.0.0 \ - -C bp.secure_memory=1 \ - -C bp.tzc_400.diagnostics=1 \ - -C cache_state_modelled=1 \ - -C cluster0.cpu0.RVBARADDR=0x04010000 \ - -C cluster0.cpu1.RVBARADDR=0x04010000 \ - -C cluster0.cpu2.RVBARADDR=0x04010000 \ - -C cluster0.cpu3.RVBARADDR=0x04010000 \ - -C cluster1.cpu0.RVBARADDR=0x04010000 \ - -C cluster1.cpu1.RVBARADDR=0x04010000 \ - -C cluster1.cpu2.RVBARADDR=0x04010000 \ - -C cluster1.cpu3.RVBARADDR=0x04010000 \ - --data cluster0.cpu0="<path-to>/<bl31-binary>"@0x04010000 \ - --data cluster0.cpu0="<path-to>/<bl32-binary>"@0xff000000 \ - --data cluster0.cpu0="<path-to>/<bl33-binary>"@0x88000000 \ - --data cluster0.cpu0="<path-to>/<fdt>"@0x82000000 \ - --data cluster0.cpu0="<path-to>/<kernel-binary>"@0x80080000 \ - --data cluster0.cpu0="<path-to>/<ramdisk>"@0x84000000 - -Running on the Cortex-A32 Base FVP (AArch32) with reset to SP_MIN entrypoint -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -The following ``FVP_Base_Cortex-A32x4`` model parameters should be used to -boot Linux with 4 CPUs using the AArch32 build of TF-A. - -.. code:: shell - - <path-to>/FVP_Base_Cortex-A32x4 \ - -C pctl.startup=0.0.0.0 \ - -C bp.secure_memory=1 \ - -C bp.tzc_400.diagnostics=1 \ - -C cache_state_modelled=1 \ - -C cluster0.cpu0.RVBARADDR=0x04002000 \ - -C cluster0.cpu1.RVBARADDR=0x04002000 \ - -C cluster0.cpu2.RVBARADDR=0x04002000 \ - -C cluster0.cpu3.RVBARADDR=0x04002000 \ - --data cluster0.cpu0="<path-to>/<bl32-binary>"@0x04002000 \ - --data cluster0.cpu0="<path-to>/<bl33-binary>"@0x88000000 \ - --data cluster0.cpu0="<path-to>/<fdt>"@0x82000000 \ - --data cluster0.cpu0="<path-to>/<kernel-binary>"@0x80080000 \ - --data cluster0.cpu0="<path-to>/<ramdisk>"@0x84000000 + fvp-support + fvp-build-options + fvp-foundation + fvp-aemv8-base + fvp-cortex-a57-a53 + fvp-cortex-a32 + fvp-specific-configs -------------- -*Copyright (c) 2019-2023, Arm Limited. All rights reserved.* +*Copyright (c) 2019-2024, Arm Limited. All rights reserved.* -.. _FW_CONFIG for FVP: https://git.trustedfirmware.org/TF-A/trusted-firmware-a.git/tree/plat/arm/board/fvp/fdts/fvp_fw_config.dts -.. _Arm's website: `FVP models`_ -.. _FVP models: https://developer.arm.com/products/system-design/fixed-virtual-platforms -.. _Linaro Release 20.01: http://releases.linaro.org/members/arm/platforms/20.01 -.. _Arm FVP website: https://developer.arm.com/products/system-design/fixed-virtual-platforms +.. _model/: https://git.trustedfirmware.org/ci/tf-a-ci-scripts.git/tree/model diff --git a/docs/plat/arm/index.rst b/docs/plat/arm/index.rst index 2f685222..35c0c598 100644 --- a/docs/plat/arm/index.rst +++ b/docs/plat/arm/index.rst @@ -14,6 +14,7 @@ Arm Development Platforms arm-build-options morello/index corstone1000/index + automotive_rd/index This chapter holds documentation related to Arm's development platforms, including both software models (FVPs) and hardware development boards @@ -21,4 +22,4 @@ such as Juno. -------------- -*Copyright (c) 2019-2021, Arm Limited. All rights reserved.* +*Copyright (c) 2019-2024, Arm Limited. All rights reserved.* diff --git a/docs/plat/arm/juno/index.rst b/docs/plat/arm/juno/index.rst index 5320a3b3..1e376960 100644 --- a/docs/plat/arm/juno/index.rst +++ b/docs/plat/arm/juno/index.rst @@ -56,7 +56,7 @@ installed. #. Obtain SCP binaries (Juno) - This version of TF-A is tested with SCP version 2.12.0 on Juno. You can + This version of TF-A is tested with SCP version 2.15.0 on Juno. You can download pre-built SCP binaries (``scp_bl1.bin`` and ``scp_bl2.bin``) from `TF-A downloads page`_. Alternatively, you can `build the binaries from source`_. @@ -241,11 +241,11 @@ configure it. -------------- -*Copyright (c) 2019-2023, Arm Limited. All rights reserved.* +*Copyright (c) 2019-2024, Arm Limited. All rights reserved.* .. _Linaro release software stack: http://releases.linaro.org/members/arm/platforms/ .. _Juno platform software user guide: https://git.linaro.org/landing-teams/working/arm/arm-reference-platforms.git/about/docs/juno/user-guide.rst -.. _TF-A downloads page: https://downloads.trustedfirmware.org/tf-a/css_scp_2.12.0/juno/ +.. _TF-A downloads page: https://downloads.trustedfirmware.org/tf-a/css_scp_2.15.0/juno/ .. _build the binaries from source: https://github.com/ARM-software/SCP-firmware/blob/master/user_guide.md#scp-firmware-user-guide .. _Arm Platforms Portal: https://community.arm.com/dev-platforms/ .. _Juno Getting Started Guide: https://developer.arm.com/documentation/den0928/f/?lang=en diff --git a/docs/plat/arm/tc/index.rst b/docs/plat/arm/tc/index.rst index 9469e9ad..467738cd 100644 --- a/docs/plat/arm/tc/index.rst +++ b/docs/plat/arm/tc/index.rst @@ -13,13 +13,15 @@ Some of the features of TC platform referenced in TF-A include: - SCMI - MHUv2 -Currently, the main difference between TC0 (TARGET_PLATFORM=0), TC1 -(TARGET_PLATFORM=1), TC2 (TARGET_PLATFORM=2) platforms w.r.t to TF-A -is the CPUs supported as below: +The TF-A build is specified by the option `TARGET_PLATFORM` which represents +the Total Compute platform number. The platforms support the CPU variants +listed as below: - TC0 has support for Cortex A510, Cortex A710 and Cortex X2. (Note TC0 is now deprecated) - TC1 has support for Cortex A510, Cortex A715 and Cortex X3. (Note TC1 is now deprecated) -- TC2 has support for Cortex A520, Cortex A720 and Cortex x4. +- TC2 has support for Cortex A520, Cortex A720 and Cortex x4. (Note TC2 is now deprecated) +- TC3 has support for Cortex A520, Cortex A725 and Cortex x925. + Boot Sequence ------------- @@ -43,7 +45,7 @@ Build Procedure (TF-A only) .. code:: shell make PLAT=tc BL33=<path_to_uboot.bin> \ - SCP_BL2=<path_to_scp_ramfw.bin> TARGET_PLATFORM={0,1,2} all fip + SCP_BL2=<path_to_scp_ramfw.bin> TARGET_PLATFORM={3} all fip Enable TBBR by adding the following options to the make command: diff --git a/docs/plat/imx8ulp.rst b/docs/plat/imx8ulp.rst new file mode 100644 index 00000000..b6b13e26 --- /dev/null +++ b/docs/plat/imx8ulp.rst @@ -0,0 +1,69 @@ +NXP i.MX 8ULP +================== + +i.MX 8ULP is part of the ULP family with emphasis on extreme low-power techniques +using the 28 nm fully depleted silicon on insulator process. Like i.MX 7ULP, +i.MX 8ULP continues to be based on asymmetric architecture. + +The i.MX 8ULP family of processors features NXP’s advanced implementation of the +dual Arm Cortex-A35 cores alongside an Arm Cortex-M33. This combined architecture +enables the device to run a rich operating system (such as Linux) on the Cortex-A35 +core and an RTOS (such as FreeRTOS) on the Cortex-M33 core. It also includes a Cadence +Tensilica Fusion DSP for low-power audio and a HiFi4 DSP for advanced audio and machine +learning applications. + +The design enables clean separation between two processing domains, where each has +separate power, clocking and peripheral islands, but the bus fabric of each domain +is tightly integrated for efficient communication. The part is streamlined to minimize +pin count, enabling small packages and simple system integration. This microprocessor +is intended for applications where efficiency and simple system integration is important. +`i.MX8ULP Applications Processors`_. + +Boot Sequence +------------- + +BootROM --> SPL --> BL31 --> BL33(u-boot) --> Linux kernel + +How to build +------------ + +Build Procedure +~~~~~~~~~~~~~~~ + +- Prepare AARCH64 toolchain. + +- Get the ELE FW image from NXP linux SDK package + +- Build SPL and u-boot firstly, and get binary images: u-boot-spl.bin, + u-boot.bin and dtb + +- Build TF-A + + Build bl31: + + .. code:: shell + + CROSS_COMPILE=aarch64-linux-gnu- make PLAT=<Target_SoC> bl31 + + Target_SoC should be "imx8ulp" for i.MX8ULP SoC. + +Deploy TF-A Images +~~~~~~~~~~~~~~~~~~ + +TF-A binary(bl31.bin), u-boot-spl.bin u-boot.bin, ELE FW image are combined +together to generate a binary file called flash.bin, the imx-mkimage tool is +used to generate flash.bin, and flash.bin needs to be flashed into SD card +with certain offset for BOOT ROM. + +Reference Documentation +~~~~~~~~~~~~~~~~~~~~~~~ + +Details on how to prepare, generate & deploy the boot image be found in following documents: + +- i.MX Linux User's Guide + `link <https://www.nxp.com/design/software/embedded-software/i-mx-software/embedded-linux-for-i-mx-applications-processors:IMXLINUX>`__ +- i.MX Linux Reference Manual + `link <https://www.nxp.com/design/software/embedded-software/i-mx-software/embedded-linux-for-i-mx-applications-processors:IMXLINUX>`__ + +.. _i.MX8ULP Applications Processors: https://www.nxp.com/products/processors-and-microcontrollers/arm-processors/i-mx-applications-processors/i-mx-8-applications-processors/i-mx-8ulp-applications-processor-family:i.MX8ULP + diff --git a/docs/plat/index.rst b/docs/plat/index.rst index b1ccaa51..a8e0c8dc 100644 --- a/docs/plat/index.rst +++ b/docs/plat/index.rst @@ -7,6 +7,7 @@ Platform Ports :hidden: allwinner + amd-versal2 arm/index ast2700 meson-axg @@ -27,7 +28,9 @@ Platform Ports warp7 imx8 imx8m + imx8ulp imx9 + s32g274a npcm845x nxp/index poplar @@ -37,6 +40,7 @@ Platform Ports qti-msm8916 rpi3 rpi4 + rpi5 rcar-gen3 rz-g2 rockchip @@ -59,7 +63,6 @@ documentation associated with them. - Arm Neoverse N1 System Development Platform (N1SDP) - Arm Neoverse Reference Design N1 Edge (RD-N1-Edge) FVP - - Arm Neoverse Reference Design E1 Edge (RD-E1-Edge) FVP - Arm SGI-575 - MediaTek MT8173 SoCs @@ -69,21 +72,10 @@ Deprecated platforms +----------------+----------------+--------------------+--------------------+ | Platform | Vendor | Deprecated version | Deleted version | +================+================+====================+====================+ -| sgm775 | Arm | 2.5 | 2.7 | -+----------------+----------------+--------------------+--------------------+ -| mt6795 | MTK | 2.5 | 2.7 | -+----------------+----------------+--------------------+--------------------+ -| sgi575 | Arm | 2.8 | TBD | -+----------------+----------------+--------------------+--------------------+ -| rdn1edge | Arm | 2.8 | TBD | -+----------------+----------------+--------------------+--------------------+ -| tc0 | Arm | 2.8 | 2.10 | -+----------------+----------------+--------------------+--------------------+ -| tc1 | Arm | 2.10 | TBD | -+----------------+----------------+--------------------+--------------------+ -| rde1edge | Arm | 2.9 | 3.0 | +| TC2 | Arm | 2.12 | TBD | +| | | | | +----------------+----------------+--------------------+--------------------+ -------------- -*Copyright (c) 2019-2023, Arm Limited. All rights reserved.* +*Copyright (c) 2019-2024, Arm Limited. All rights reserved.* diff --git a/docs/plat/mt8195.rst b/docs/plat/mt8195.rst index b2aeea20..9810f9e4 100644 --- a/docs/plat/mt8195.rst +++ b/docs/plat/mt8195.rst @@ -2,8 +2,8 @@ MediaTek 8195 ============= MediaTek 8195 (MT8195) is a 64-bit ARM SoC introduced by MediaTek in 2021. -The chip incorporates eight cores - four Cortex-A55 little cores and Cortex-A76. -Cortex-A76 can operate at up to 2.2 GHz. +The chip incorporates eight cores - four Cortex-A55 little cores and Cortex-A78. +Cortex-A78 can operate at up to 2.6 GHz. Cortex-A55 can operate at up to 2.0 GHz. Boot Sequence diff --git a/docs/plat/rockchip.rst b/docs/plat/rockchip.rst index b7c43fbe..384cd73d 100644 --- a/docs/plat/rockchip.rst +++ b/docs/plat/rockchip.rst @@ -10,6 +10,8 @@ This includes right now: - rk3328: Quad-Core Cortex-A53 - rk3368: Octa-Core Cortex-A53 - rk3399: Hexa-Core Cortex-A53/A72 +- rk3566/rk3568: Quad-Core Cortex-A55 +- rk3588: Octa-Core Cortex-A55/A76 Boot Sequence @@ -35,7 +37,7 @@ these images need to get build from the TF-A repository. For AARCH64 architectures the build command looks like - make CROSS_COMPILE=aarch64-linux-gnu- PLAT=rk3399 bl32 + make CROSS_COMPILE=aarch64-linux-gnu- PLAT=rk3399 bl31 while AARCH32 needs a slightly different command diff --git a/docs/plat/rpi5.rst b/docs/plat/rpi5.rst new file mode 100644 index 00000000..f2e1b9f2 --- /dev/null +++ b/docs/plat/rpi5.rst @@ -0,0 +1,78 @@ +Raspberry Pi 5 +============== + +The `Raspberry Pi 5`_ is a single-board computer that contains four +Arm Cortex-A76 cores. + +This port is a minimal BL31 implementation capable of booting 64-bit EL2 +payloads such as Linux and EDK2. + +**IMPORTANT NOTE**: This port isn't secure. All of the memory used is DRAM, +which is available from both the Non-secure and Secure worlds. The SoC does +not seem to feature a secure memory controller of any kind, so portions of +DRAM can't be protected properly from the Non-secure world. + +Build +------------------ + +To build this platform, run: + +.. code:: shell + + CROSS_COMPILE=aarch64-linux-gnu- make PLAT=rpi5 DEBUG=1 + +The firmware will be generated at ``build/rpi5/debug/bl31.bin``. + +The following build options are supported: + +- ``RPI3_DIRECT_LINUX_BOOT``: Enabled by default. Allows direct boot of the Linux + kernel from the firmware. + +- ``PRELOADED_BL33_BASE``: Used to specify the fixed address of a BL33 binary + that has been preloaded by earlier boot stages (VPU). Useful for bundling + BL31 and BL33 in the same ``armstub`` image (e.g. TF-A + EDK2). + +- ``RPI3_PRELOADED_DTB_BASE``: This option allows to specify the fixed address of + a DTB in memory. Can only be used if ``device_tree_address=`` is present in + config.txt. + +- ``RPI3_RUNTIME_UART``: Indicates whether TF-A should use the debug UART for + runtime messages or not. ``-1`` (default) disables the option, any other value + enables it. + +Usage +------------------ + +Copy the firmware binary to the first FAT32 partition of a supported boot media +(SD, USB) and append ``armstub=bl31.bin`` to config.txt, or just rename the +file to ``armstub8-2712.bin``. + +No other config options or files are required by the firmware alone, this will +depend on the payload you intend to run. + +For Linux, you must also place an appropriate DTB and kernel in the boot +partition. This has been validated with a copy of Raspberry Pi OS. + +The VPU will preload a BL33 AArch64 image named either ``kernel_2712.img`` or +``kernel8.img``, which can be overridden by adding a ``kernel=filename`` option +to config.txt. + +Kernel and DTB load addresses are also chosen by the VPU and can be changed with +``kernel_address=`` and ``device_tree_address=`` in config.txt. If TF-A was built +with ``PRELOADED_BL33_BASE`` or ``RPI3_PRELOADED_DTB_BASE``, setting those config +options may be necessary. + +By default, all boot stages print messages to the dedicated UART debug port. +Configuration is ``115200 8n1``. + +Design +------------------ + +This port is largely based on the RPi 4 one. + +The boot process is essentially the same, the only notable difference being that +all VPU blobs have been moved into EEPROM (former start4.elf & fixup4.dat). There's +also a custom BL31 TF-A armstub included for PSCI, which can be replaced with this +port. + +.. _Raspberry Pi 5: https://www.raspberrypi.com/products/raspberry-pi-5/ diff --git a/docs/plat/s32g274a.rst b/docs/plat/s32g274a.rst new file mode 100644 index 00000000..d3f31cab --- /dev/null +++ b/docs/plat/s32g274a.rst @@ -0,0 +1,111 @@ +NXP S32G274A +============ + +S32G2 is an NXP vehicle network processor combining ASIL D safety, hardware +security, high-performance real-time and application processing and network +acceleration. S32G2 supports the needs of new vehicle architectures: +service-oriented gateways, domain controllers, zonal processors, safety +processors and more. It is equipped with 4 Cortex-A53 cores operating at +1.0GHz. + +The TF-A includes support for one single S32G2-based board called S32G274ARDB2. +The S32G-VNP-RDB2 is a compact, highly optimized and integrated board +engineering for vehicle service-oriented gateway (SoG), domain control +applications, high-performance processing, safety and security applications. +More details about this board can be found at `s32g274ardb2`_. + +Boot Flow +--------- + +:: + + BootROM -> BL2 (SRAM) -> BL31 (SRAM) -> BL33 (DDR - TODO) + +.. warning:: + This boot flow is a preliminary version that will serve as a foundation for + upcoming S32G2 contributions. The execution will hang after the BL31 stage + due to U-Boot being deployed in SRAM instead of DDR. This issue will be + resolved with the addition of the DDR driver. + +Code Locations +-------------- + +- Downstream TF-A: + `link: <https://github.com/nxp-auto-linux/arm-trusted-firmware>`__ + +- Downstream U-Boot: + `link <https://github.com/nxp-auto-linux/u-boot>`__ + +- Downstream Linux: + `link <https://github.com/nxp-auto-linux/linux>`__ + +How to build +------------ + +The port currently available on the S32G274ARDB2 platform is in its initial +stage. This means that important drivers like DDR and storage are not yet +available. Consequently, the boot process depends on BootROM to load all TF-A +stages in SRAM. To create a bootable image, the script below should be used. +This script makes use of the ``mkimage`` tool, which is part of the U-Boot drop +for S32G274A SoCs. + +.. code:: bash + + #!/bin/bash -xe + TF_A="${TF_A:-`pwd`}" + UBOOT="${UBOOT:-${TF_A}/../u-boot}" + DEBUG="${DEBUG:-1}" + + FIP_BASE="0x34100000" + + if [ "${DEBUG}" -eq "1" ]; then + BUILD="debug" + else + BUILD="release" + fi + + BOOT_IMAGE="build/s32g274ardb2/${BUILD}/BOOT_IMAGE.bin" + BL2_BIN="build/s32g274ardb2/${BUILD}/bl2.bin" + FIP_BIN="build/s32g274ardb2/${BUILD}/fip.bin" + + # Generate bl2, bl31 and fip image + make -C "${TF_A}" -j9 'PLAT=s32g274ardb2' \ + BL33="${UBOOT}/u-boot-nodtb.bin" DEBUG="${DEBUG}" clean + make -C "${TF_A}" -j9 'PLAT=s32g274ardb2' \ + BL33="${UBOOT}/u-boot-nodtb.bin" DEBUG="${DEBUG}" bl2 + make -C "${TF_A}" -j9 'PLAT=s32g274ardb2' \ + BL33="${UBOOT}/u-boot-nodtb.bin" DEBUG="${DEBUG}" fip + + # Extract BL2 entry + BL2_START="0x$(poetry run memory -p s32g274ardb2 -b debug -f | \ + grep BL2 | awk -F'|' '{print $3}' | xargs)" + # BL2 bin file size in bytes + BL2_SIZE="$(stat -c "%s" "${BL2_BIN}")" + + # Pack bl2.bin and fip.bin by ensuring that the FIP image will start at FIP_BASE + cp -vf "${BL2_BIN}" "${BOOT_IMAGE}" + dd if="${FIP_BIN}" of="${BOOT_IMAGE}" seek="$((FIP_BASE - BL2_START))" bs=1 + + # Build a bootable image by appending the IVT + "${UBOOT}/tools/mkimage" \ + -a "${BL2_START}" \ + -e "${BL2_START}" \ + -T s32ccimage \ + -n "${UBOOT}/u-boot-s32.cfgout" \ + -d "${BOOT_IMAGE}" \ + fip.s32 + +SoC Errata Workarounds +---------------------- + +The S32G274A port of the TF-A includes compilation flags that can be used to +control the workaround for the SoC. These flags are used similarly to how the +:ref:`arm_cpu_macros_errata_workarounds` are used. The list of workarounds +includes the following switches: + +- ``ERRATA_S32_051700``: This applies erratum ERR051700 workaround to + SoCs part of the S32 Common Chassis family, and therefore it needs to + be enabled for the S32G and S32R devices. + +.. _s32g2: https://www.nxp.com/products/processors-and-microcontrollers/s32-automotive-platform/s32g-vehicle-network-processors/s32g2-processors-for-vehicle-networking:S32G2 +.. _s32g274ardb2: https://www.nxp.com/design/design-center/designs/s32g2-vehicle-networking-reference-design:S32G-VNP-RDB2 diff --git a/docs/plat/st/stm32mp1.rst b/docs/plat/st/stm32mp1.rst index b6e4b0d8..39a43eeb 100644 --- a/docs/plat/st/stm32mp1.rst +++ b/docs/plat/st/stm32mp1.rst @@ -115,8 +115,9 @@ ______ make stm32mp15_trusted_defconfig make DEVICE_TREE=stm32mp157c-ev1 all -OP-TEE (optional) -_________________ +OP-TEE (recommended) +____________________ +OP-TEE is the default BL32 supported for STMicroelectronics platforms. .. code:: bash @@ -125,9 +126,10 @@ _________________ CFG_EMBED_DTB_SOURCE_FILE=stm32mp157c-ev1.dts -TF-A BL32 (SP_min) -__________________ +TF-A BL32 (SP_min) (not recommended) +____________________________________ If you choose not to use OP-TEE, you can use TF-A SP_min. +This is not the recommended BL32 to use, and will have very limited support. To build TF-A BL32, and its device tree file: .. code:: bash @@ -217,4 +219,4 @@ __________________ .. _STM32MP1 Series: https://www.st.com/en/microcontrollers-microprocessors/stm32mp1-series.html .. _STM32MP1 part number codification: https://wiki.st.com/stm32mpu/wiki/STM32MP15_microprocessor#Part_number_codification -*Copyright (c) 2023, STMicroelectronics - All Rights Reserved* +*Copyright (c) 2023-2024, STMicroelectronics - All Rights Reserved* diff --git a/docs/plat/st/stm32mp2.rst b/docs/plat/st/stm32mp2.rst index 43e131d7..87bb6a53 100644 --- a/docs/plat/st/stm32mp2.rst +++ b/docs/plat/st/stm32mp2.rst @@ -4,6 +4,8 @@ STM32MP2 STM32MP2 is a microprocessor designed by STMicroelectronics based on Arm Cortex-A35. +More information can be found on `STM32MP2 Series`_ page. + For TF-A common configuration of STM32 MPUs, please check :ref:`STM32 MPUs` page. @@ -19,11 +21,13 @@ The STM32MP25 series is available in 4 different lines which are pin-to-pin comp Each line comes with a security option (cryptography & secure boot) and a Cortex-A frequency option: -- A Basic + Cortex-A35 @ 1GHz -- C Secure Boot + HW Crypto + Cortex-A35 @ 1GHz +- A Basic + Cortex-A35 @ 1.2GHz +- C Secure Boot + HW Crypto + Cortex-A35 @ 1.2GHz - D Basic + Cortex-A35 @ 1.5GHz - F Secure Boot + HW Crypto + Cortex-A35 @ 1.5GHz +The `STM32MP2 part number codification`_ page gives more information about part numbers. + Memory mapping -------------- @@ -81,7 +85,8 @@ To compile the correct DDR driver, one flag must be set among: Boot with FIP ~~~~~~~~~~~~~ -You need to build BL2, BL31, BL32 (OP-TEE) and BL33 (U-Boot) before building FIP binary. +You need to build BL2, BL31, BL32 (OP-TEE) and BL33 (U-Boot) and retrieve +DDR PHY firmware before building FIP binary. U-Boot ______ @@ -102,9 +107,24 @@ ______ ARCH=arm PLATFORM=stm32mp2 \ CFG_EMBED_DTB_SOURCE_FILE=stm32mp257f-ev1.dts -TF-A BL2 & BL31 -_______________ -To build TF-A BL2 with its STM32 header and BL31 for SD-card boot: +DDR PHY firmware +________________ +DDR PHY firmware files may not be delivered inside TF-A repository, especially +if you build directly from trustedfirmware.org repository. It then needs to be +retrieved from `STMicroelectronics DDR PHY github`_. + +You can either clone the repository to the default directory: + +.. code:: bash + + git clone https://github.com/STMicroelectronics/stm32-ddr-phy-binary.git drivers/st/ddr/phy/firmware/bin + +Or clone it somewhere else, and add ``STM32MP_DDR_FW_PATH=`` in your make command +line when building FIP. + +TF-A BL2 +________ +To build TF-A BL2 with its STM32 header for SD-card boot: .. code:: bash @@ -130,4 +150,8 @@ ___ BL32_EXTRA1=<optee_directory>/tee-pager_v2.bin fip -*Copyright (c) 2023, STMicroelectronics - All Rights Reserved* +.. _STM32MP2 Series: https://www.st.com/en/microcontrollers-microprocessors/stm32mp2-series.html +.. _STM32MP2 part number codification: https://wiki.st.com/stm32mpu/wiki/STM32MP25_microprocessor#Part_number_codification +.. _STMicroelectronics DDR PHY github: https://github.com/STMicroelectronics/stm32-ddr-phy-binary + +*Copyright (c) 2023-2024, STMicroelectronics - All Rights Reserved* diff --git a/docs/plat/st/stm32mpus.rst b/docs/plat/st/stm32mpus.rst index 931dd57f..7b471127 100644 --- a/docs/plat/st/stm32mpus.rst +++ b/docs/plat/st/stm32mpus.rst @@ -45,6 +45,8 @@ Serial boot devices: - ``STM32MP_UART_PROGRAMMER`` - ``STM32MP_USB_PROGRAMMER`` +Only one storage or serial device should be selected in the build command line, +to save space and not overflow SYSRAM size, or else the platform won't build or boot. Other configuration flags: @@ -52,8 +54,6 @@ Other configuration flags: | Default: stm32mp157c-ev1.dtb - | ``DWL_BUFFER_BASE``: the 'serial boot' load address of FIP, | default location (end of the first 128MB) is used when absent -- | ``STM32MP_EARLY_CONSOLE``: to enable early traces before clock driver is setup. - | Default: 0 (disabled) - | ``STM32MP_RECONFIGURE_CONSOLE``: to re-configure crash console (especially after BL2). | Default: 0 (disabled) - | ``STM32MP_UART_BAUDRATE``: to select UART baud rate. @@ -75,4 +75,4 @@ Usually, two copies of fsbl are used (fsbl1 and fsbl2) instead of one partition -------------- -*Copyright (c) 2023, STMicroelectronics - All Rights Reserved* +*Copyright (c) 2023-2024, STMicroelectronics - All Rights Reserved* diff --git a/docs/plat/xilinx-versal-net.rst b/docs/plat/xilinx-versal-net.rst index 1db7695b..d22a46d1 100644 --- a/docs/plat/xilinx-versal-net.rst +++ b/docs/plat/xilinx-versal-net.rst @@ -40,3 +40,72 @@ Xilinx Versal NET platform specific build options * `TFA_NO_PM` : Platform Management support. - 0 : Enable Platform Management (Default) - 1 : Disable Platform Management + +* `CPU_PWRDWN_SGI`: Select the SGI for triggering CPU power down request to + secondary cores on receiving power down callback from + firmware. Options: + + - `0` : SGI 0 + - `1` : SGI 1 + - `2` : SGI 2 + - `3` : SGI 3 + - `4` : SGI 4 + - `5` : SGI 5 + - `6` : SGI 6 (Default) + - `7` : SGI 7 + +Reference DEN0028E SMC calling convention +------------------------------------------ + +Allocated subranges of Function Identifier to SIP services +----------------------------------------------------------- + ++-----------------------+-------------------------------------------------------+ +| SMC Function | Identifier Service type | ++-----------------------+------------------------------+------------------------+ +| 0xC2000000-0xC200FFFF | Fast SMC64 SiP Service Calls as per SMCCC Section 6.1 | ++-----------------------+-------------------------------------------------------+ + +IPI SMC call ranges +------------------------------------------------------------- + ++---------------------------+-----------------------------------------------------------+ +| SMC Function Identifier | Service type | ++---------------------------+-----------------------------------------------------------+ +| 0xc2001000-0xc2001FFF | Fast SMC64 SiP Service call range used for AMD-Xilinx IPI | ++---------------------------+-----------------------------------------------------------+ + +PM SMC call ranges for SiP SVC version 0.1 +-------------------------------------------------------- + ++---------------------------+---------------------------------------------------------------------------+ +| SMC Function Identifier | Service type | ++---------------------------+---------------------------------------------------------------------------+ +| 0xc2000000-0xc2000FFF | Fast SMC64 SiP Service call range used for AMD-Xilinx Platform Management | ++---------------------------+---------------------------------------------------------------------------+ + +PM SMC call ranges for SiP SVC version 0.2 +-------------------------------------------------------- + ++---------------------------+---------------------------------------------------------------------------+ +| SMC Function Identifier | Service type | ++---------------------------+---------------------------------------------------------------------------+ +| 0xc2000FFF | Fast SMC64 SiP Service call used for pass-through of AMD-Xilinx Platform | +| | Management APIs to firmware | ++---------------------------+---------------------------------------------------------------------------+ +| 0xc2000A00-0xc2000AFF | Fast SMC64 SiP Service call range used for AMD-Xilinx Platform Management | +| | specific TF-A APIs | ++---------------------------+---------------------------------------------------------------------------+ + +SMC function IDs for SiP Service queries +---------------------------------------------- + ++--------------+--------------+--------------+ +| Service | Call UID | Revision | ++--------------+--------------+--------------+ +| SiP Service | 0x8200_FF01 | 0x8200_FF03 | ++--------------+--------------+--------------+ + +Call UID Query – Returns a unique identifier of the service provider. + +Revision Query – Returns revision details of the service implementor. diff --git a/docs/plat/xilinx-versal.rst b/docs/plat/xilinx-versal.rst index b71776d2..7185d910 100644 --- a/docs/plat/xilinx-versal.rst +++ b/docs/plat/xilinx-versal.rst @@ -14,11 +14,6 @@ To build: make RESET_TO_BL31=1 CROSS_COMPILE=aarch64-none-elf- PLAT=versal bl31 ``` -To build ATF for different platform (supported are "silicon"(default) and "versal_virt") -```bash -make RESET_TO_BL31=1 CROSS_COMPILE=aarch64-none-elf- PLAT=versal VERSAL_PLATFORM=versal_virt bl31 -``` - To build bl32 TSP you have to rebuild bl31 too ```bash make CROSS_COMPILE=aarch64-none-elf- PLAT=versal SPD=tspd RESET_TO_BL31=1 bl31 bl32 @@ -29,6 +24,11 @@ To build TF-A for JTAG DCC console make RESET_TO_BL31=1 CROSS_COMPILE=aarch64-none-elf- PLAT=versal bl31 VERSAL_CONSOLE=dcc ``` +To build TF-A with Errata management interface +```bash +make RESET_TO_BL31=1 CROSS_COMPILE=aarch64-none-elf- PLAT=versal bl31 ERRATA_ABI_SUPPORT=1 +``` + To build TF-A with Straight-Line Speculation(SLS) ```bash make RESET_TO_BL31=1 CROSS_COMPILE=aarch64-none-elf- PLAT=versal bl31 HARDEN_SLS_ALL=1 @@ -46,10 +46,18 @@ Xilinx Versal platform specific build options - `pl011`, `pl011_0`: ARM pl011 UART 0 - `pl011_1` : ARM pl011 UART 1 -* `VERSAL_PLATFORM`: Select the platform. Options: - - `versal_virt` : Versal Virtual platform - - `spp_itr6` : SPP ITR6 - - `emu_itr6` : EMU ITR6 +* `CPU_PWRDWN_SGI`: Select the SGI for triggering CPU power down request to + secondary cores on receiving power down callback from + firmware. Options: + + - `0` : SGI 0 + - `1` : SGI 1 + - `2` : SGI 2 + - `3` : SGI 3 + - `4` : SGI 4 + - `5` : SGI 5 + - `6` : SGI 6 (Default) + - `7` : SGI 7 # PLM->TF-A Parameter Passing ------------------------------ @@ -58,3 +66,59 @@ uses that data to hand off to the loaded images. The address of the handoff data structure is passed in the ```PMC_GLOBAL_GLOB_GEN_STORAGE4``` register. The register is free to be used by other software once the TF-A is bringing up further firmware images. + +Reference DEN0028E SMC calling convention +------------------------------------------ + +Allocated subranges of Function Identifier to SIP services +---------------------------------------------------------- + ++-----------------------+-------------------------------------------------------+ +| SMC Function | Identifier Service type | ++-----------------------+-------------------------------------------------------+ +| 0xC2000000-0xC200FFFF | Fast SMC64 SiP Service Calls as per SMCCC Section 6.1 | ++-----------------------+-------------------------------------------------------+ + +IPI SMC call ranges +------------------- + ++---------------------------+-----------------------------------------------------------+ +| SMC Function Identifier | Service type | ++---------------------------+-----------------------------------------------------------+ +| 0xc2001000-0xc2001FFF | Fast SMC64 SiP Service call range used for AMD-Xilinx IPI | ++---------------------------+-----------------------------------------------------------+ + +PM SMC call ranges for SiP SVC version 0.1 +-------------------------------------------------------- + ++---------------------------+---------------------------------------------------------------------------+ +| SMC Function Identifier | Service type | ++---------------------------+---------------------------------------------------------------------------+ +| 0xc2000000-0xc2000FFF | Fast SMC64 SiP Service call range used for AMD-Xilinx Platform Management | ++---------------------------+---------------------------------------------------------------------------+ + +PM SMC call ranges for SiP SVC version 0.2 +-------------------------------------------------------- + ++---------------------------+---------------------------------------------------------------------------+ +| SMC Function Identifier | Service type | ++---------------------------+---------------------------------------------------------------------------+ +| 0xc2000FFF | Fast SMC64 SiP Service call used for pass-through of AMD-Xilinx Platform | +| | Management APIs to firmware | ++---------------------------+---------------------------------------------------------------------------+ +| 0xc2000A00-0xc2000AFF | Fast SMC64 SiP Service call range used for AMD-Xilinx Platform Management | +| | specific TF-A APIs | ++---------------------------+---------------------------------------------------------------------------+ + +SMC function IDs for SiP Service queries +---------------------------------------- + ++--------------+--------------+--------------+ +| Service | Call UID | Revision | ++--------------+--------------+--------------+ +| SiP Service | 0x8200_FF01 | 0x8200_FF03 | ++--------------+--------------+--------------+ + +Call UID Query – Returns a unique identifier of the service provider. + +Revision Query – Returns revision details of the service implementor. diff --git a/docs/plat/xilinx-zynqmp.rst b/docs/plat/xilinx-zynqmp.rst index 4fe0d2f8..c8ba27f6 100644 --- a/docs/plat/xilinx-zynqmp.rst +++ b/docs/plat/xilinx-zynqmp.rst @@ -166,3 +166,55 @@ Custom package makefile fragment inclusion in TF-A build - TF-A build command: make CROSS_COMPILE=aarch64-none-elf- PLAT=zynqmp RESET_TO_BL31=1 bl31 CUSTOM_PKG_PATH=<...> + +Reference DEN0028E SMC calling convention +------------------------------------------ + +Allocated subranges of Function Identifier to SIP services +------------------------------------------------------------ + ++-----------------------+-------------------------------------------------------+ +| SMC Function | Identifier Service type | ++-----------------------+-------------------------------------------------------+ +| 0xC2000000-0xC200FFFF | Fast SMC64 SiP Service Calls as per SMCCC Section 6.1 | ++-----------------------+-------------------------------------------------------+ + +IPI SMC call ranges +------------------- + ++---------------------------+-----------------------------------------------------------+ +| SMC Function Identifier | Service type | ++---------------------------+-----------------------------------------------------------+ +| 0xc2001000-0xc2001FFF | Fast SMC64 SiP Service call range used for AMD-Xilinx IPI | ++---------------------------+-----------------------------------------------------------+ + +PM SMC call ranges +------------------ + ++---------------------------+---------------------------------------------------------------------------+ +| SMC Function Identifier | Service type | ++---------------------------+---------------------------------------------------------------------------+ +| 0xc2000000-0xc2000FFF | Fast SMC64 SiP Service call range used for AMD-Xilinx Platform Management | ++---------------------------+---------------------------------------------------------------------------+ + +SMC function IDs for SiP Service queries +---------------------------------------- + ++--------------+--------------+--------------+ +| Service | Call UID | Revision | ++--------------+--------------+--------------+ +| SiP Service | 0x8200_FF01 | 0x8200_FF03 | ++--------------+--------------+--------------+ + +Call UID Query – Returns a unique identifier of the service provider. + +Revision Query – Returns revision details of the service implementor. + +CUSTOM SIP service support +-------------------------- + ++-------------+------------+------------+ +| Service | 32-bit | 64-bit | ++-------------+------------+------------+ +| SiP Service | 0x82002000 | 0xC2002000 | ++-------------+------------+------------+ diff --git a/docs/porting-guide.rst b/docs/porting-guide.rst index 7c66d111..5cb20fd9 100644 --- a/docs/porting-guide.rst +++ b/docs/porting-guide.rst @@ -256,10 +256,10 @@ likely to be suitable for all platform ports. Defines the maximum address in secure RAM that the BL31 image can occupy. -- **#define : PLAT_RSS_COMMS_PAYLOAD_MAX_SIZE** +- **#define : PLAT_RSE_COMMS_PAYLOAD_MAX_SIZE** - Defines the maximum message size between AP and RSS. Need to define if - platform supports RSS. + Defines the maximum message size between AP and RSE. Need to define if + platform supports RSE. For every image, the platform must define individual identifiers that will be used by BL1 or BL2 to load the corresponding image into memory from non-volatile @@ -1518,6 +1518,40 @@ When CONDITIONAL_CMO flag is enabled: - The function must not clobber x1, x2 and x3. It's also not safe to rely on stack. Otherwise obey AAPCS. +Struct: plat_try_images_ops [optional] +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +This optional structure holds platform hooks for alternative images load. +It has to be defined in platform code and registered by calling +plat_setup_try_img_ops() function, passing it the address of the +plat_try_images_ops struct. + +Function : plat_setup_try_img_ops [optional] +............................................ + +:: + + Argument : const struct plat_try_images_ops * + Return : void + +This optional function is called to register platform try images ops, given +as argument. + +Function : plat_try_images_ops.next_instance [optional] +....................................................... + +:: + + Argument : unsigned int image_id + Return : int + +This optional function tries to load images from alternative places. +In case PSA FWU is not used, it can be any instance or media. If PSA FWU is +used, it is mandatory that the backup image is on the same media. +This is required for MTD devices like NAND. +The argument is the ID of the image for which we are looking for an alternative +place. It returns 0 in case of success and a negative errno value otherwise. + Modifications specific to a Boot Loader stage --------------------------------------------- @@ -1607,9 +1641,6 @@ This function executes with the MMU and data caches enabled. It is responsible for performing any remaining platform-specific setup that can occur after the MMU and data cache have been enabled. -if support for multiple boot sources is required, it initializes the boot -sequence used by plat_try_next_boot_source(). - In Arm standard platforms, this function initializes the storage abstraction layer used to load the next bootloader image. @@ -1712,6 +1743,18 @@ This function can be used by the platforms to update/use image information corresponding to ``image_id``. This function is invoked in BL1, both in cold boot and FWU code path, before loading the image. +Function : bl1_plat_calc_bl2_layout() [optional] +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +:: + + Argument : const meminfo_t *bl1_mem_layout, meminfo_t *bl2_mem_layout + Return : void + +This utility function calculates the memory layout of BL2, representing it in a +`meminfo_t` structure. The default implementation derives this layout from the +positioning of BL1’s RW data at the top of the memory layout. + Function : bl1_plat_handle_post_image_load() [optional] ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -1880,25 +1923,7 @@ Function : bl2_plat_preload_setup [optional] This optional function performs any BL2 platform initialization required before image loading, that is not done later in -bl2_platform_setup(). Specifically, if support for multiple -boot sources is required, it initializes the boot sequence used by -plat_try_next_boot_source(). - -Function : plat_try_next_boot_source() [optional] -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -:: - - Argument : void - Return : int - -This optional function passes to the next boot source in the redundancy -sequence. - -This function moves the current boot redundancy source to the next -element in the boot sequence. If there are no more boot sources then it -must return 0, otherwise it must return 1. The default implementation -of this always returns 0. +bl2_platform_setup(). Boot Loader Stage 2 (BL2) at EL3 -------------------------------- @@ -2223,26 +2248,35 @@ Function : plat_rmmd_get_cca_attest_token() [mandatory when ENABLE_RME == 1] :: - Argument : uintptr_t, size_t *, uintptr_t, size_t + Argument : uintptr_t, size_t *, uintptr_t, size_t, size_t * Return : int -This function returns the Platform attestation token. +This function returns the Platform attestation token. If the full token does +not fit in the buffer, the function will return a hunk of the token and +indicate how many bytes were copied and how many are pending. Multiple calls +to this function may be needed to retrieve the entire token. The parameters of the function are: arg0 - A pointer to the buffer where the Platform token should be copied by - this function. The buffer must be big enough to hold the Platform - token. + this function. If the platform token does not completely fit in the + buffer, the function may return a piece of the token only. - arg1 - Contains the size (in bytes) of the buffer passed in arg0. The - function returns the platform token length in this parameter. + arg1 - Contains the size (in bytes) of the buffer passed in arg0. In + addition, this parameter is used by the function to return the size + of the platform token length hunk copied to the buffer. arg2 - A pointer to the buffer where the challenge object is stored. arg3 - The length of the challenge object in bytes. Possible values are 32, - 48 and 64. + 48 and 64. This argument must be zero for subsequent calls to + retrieve the remaining hunks of the token. -The function returns 0 on success, -EINVAL on failure. + arg4 - Returns the remaining length of the token (in bytes) that is yet to + be returned in further calls. + +The function returns 0 on success, -EINVAL on failure and -EAGAIN if the +resource associated with the platform token retrieval is busy. Function : plat_rmmd_get_cca_realm_attest_key() [mandatory when ENABLE_RME == 1] ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -2295,6 +2329,98 @@ RMM image and stores it in the area specified by manifest. When ENABLE_RME is disabled, this function is not used. +Function : plat_rmmd_el3_token_sign_push_req() [mandatory when RMMD_ENABLE_EL3_TOKEN_SIGN == 1] +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +:: + + Arguments : const struct el3_token_sign_request *req + Return : int + +Queue realm attestation token signing request from the RMM in EL3. The interface between +the RMM and EL3 is modeled as a queue but the underlying implementation may be different, +so long as the semantics of queuing and the error codes are used as defined below. + +See :ref:`el3_token_sign_request_struct` for definition of the request structure. + +Optional interface from the RMM-EL3 interface v0.4 onwards. + +The parameters of the functions are: + arg0: Pointer to the token sign request to be pushed to EL3. + The structure must be located in the RMM-EL3 shared + memory buffer and must be locked before use. + +Return codes: + - E_RMM_OK On Success. + - E_RMM_INVAL If the arguments are invalid. + - E_RMM_AGAIN Indicates that the request was not queued since the + queue in EL3 is full. This may also be returned for any reason + or situation in the system, that prevents accepting the request + from the RMM. + - E_RMM_UNK If the SMC is not implemented or if interface + version is < 0.4. + +Function : plat_rmmd_el3_token_sign_pull_resp() [mandatory when RMMD_ENABLE_EL3_TOKEN_SIGN == 1] +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +:: + + Arguments : struct el3_token_sign_response *resp + Return : int + +Populate the attestation signing response in the ``resp`` parameter. The interface between +the RMM and EL3 is modeled as a queue for responses but the underlying implementation may +be different, so long as the semantics of queuing and the error codes are used as defined +below. + +See :ref:`el3_token_sign_response_struct` for definition of the response structure. + +Optional interface from the RMM-EL3 interface v0.4 onwards. + +The parameters of the functions are: + resp: Pointer to the token sign response to get from EL3. + The structure must be located in the RMM-EL3 shared + memory buffer and must be locked before use. + +Return: + - E_RMM_OK On Success. + - E_RMM_INVAL If the arguments are invalid. + - E_RMM_AGAIN Indicates that a response is not ready yet. + - E_RMM_UNK If the SMC is not implemented or if interface + version is < 0.4. + +Function : plat_rmmd_el3_token_sign_get_rak_pub() [mandatory when RMMD_ENABLE_EL3_TOKEN_SIGN == 1] +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +:: + + Argument : uintptr_t, size_t *, unsigned int + Return : int + +This function returns the public portion of the realm attestation key which will be used to +sign Realm attestation token. Typically, with delegated attestation, the private key is +returned, however, there may be platforms where the private key bits are better protected +in a platform specific manner such that the private key is not exposed. In such cases, +the RMM will only cache the public key and forward any requests such as signing, that +uses the private key to EL3. The API currently only supports P-384 ECC curve key. + +This is an optional interface from the RMM-EL3 interface v0.4 onwards. + +The parameters of the function are: + + arg0 - A pointer to the buffer where the public key should be copied + by this function. The buffer must be big enough to hold the + attestation key. + + arg1 - Contains the size (in bytes) of the buffer passed in arg0. The + function returns the attestation key length in this parameter. + + arg2 - The type of the elliptic curve to which the requested attestation key + belongs. + +The function returns E_RMM_OK on success, RMM_E_INVAL if arguments are invalid and +E_RMM_UNK if the SMC is not implemented or if interface version is < 0.4. + Function : bl31_plat_enable_mmu [optional] ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -3274,6 +3400,17 @@ This API is used by the crash reporting mechanism to force write of all buffered data on the designated crash console. It should only use general purpose registers x0 through x5 to do its work. +Function : plat_setup_early_console [optional] +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +:: + + Argument : void + Return : void + +This API is used to setup the early console, it is required only if the flag +``EARLY_CONSOLE`` is enabled. + .. _External Abort handling and RAS Support: External Abort handling and RAS Support @@ -3560,7 +3697,7 @@ to :ref:`Measured Boot Design` for more details. -------------- -*Copyright (c) 2013-2023, Arm Limited and Contributors. All rights reserved.* +*Copyright (c) 2013-2024, Arm Limited and Contributors. All rights reserved.* .. _PSCI: https://developer.arm.com/documentation/den0022/latest/ .. _Arm Generic Interrupt Controller version 2.0 (GICv2): http://infocenter.arm.com/help/topic/com.arm.doc.ihi0048b/index.html diff --git a/docs/process/code-review-guidelines.rst b/docs/process/code-review-guidelines.rst index bd428113..5e9a6678 100644 --- a/docs/process/code-review-guidelines.rst +++ b/docs/process/code-review-guidelines.rst @@ -242,4 +242,4 @@ concerns, questions, or any other type of blocking comment, they should set *Copyright (c) 2020-2023, Arm Limited. All rights reserved.* -.. _Project Maintenance Process: https://developer.trustedfirmware.org/w/collaboration/project-maintenance-process/ +.. _Project Maintenance Process: https://trusted-firmware-docs.readthedocs.io/en/latest/generic_processes/project_maintenance_process.html diff --git a/docs/process/coding-guidelines.rst b/docs/process/coding-guidelines.rst index 97303905..0f207a62 100644 --- a/docs/process/coding-guidelines.rst +++ b/docs/process/coding-guidelines.rst @@ -520,5 +520,3 @@ comply with. .. _`Procedure Call Standard for the Arm 64-bit Architecture`: https://github.com/ARM-software/abi-aa/blob/main/aapcs64/aapcs64.rst .. _`EditorConfig`: http://editorconfig.org/ .. _`Why the “volatile†type class should not be used`: https://www.kernel.org/doc/html/latest/process/volatile-considered-harmful.html -.. _`MISRA C:2012 Guidelines`: https://www.misra.org.uk/Activities/MISRAC/tabid/160/Default.aspx -.. _`a spreadsheet`: https://developer.trustedfirmware.org/file/download/lamajxif3w7c4mpjeoo5/PHID-FILE-fp7c7acszn6vliqomyhn/MISRA-and-TF-Analysis-v1.3.ods diff --git a/docs/process/coding-style.rst b/docs/process/coding-style.rst index 483780b3..4f1976f9 100644 --- a/docs/process/coding-style.rst +++ b/docs/process/coding-style.rst @@ -47,13 +47,13 @@ missing extensions are rarely used, however, and should not pose a problem. MISRA Compliance ---------------- -TF-A attempts to comply with the `MISRA C:2012 Guidelines`_. Coverity -Static Analysis is used to regularly generate a report of current MISRA defects -and to prevent the addition of new ones. +TF-A attempts to comply with the `MISRA C:2012 Guidelines`_. `ECLAIR` static +analysis is used to regularly generate a report of current MISRA defects and to +prevent the addition of new ones. -It is not possible for the project to follow all MISRA guidelines. We maintain -`a spreadsheet`_ that lists all rules and directives and whether we aim to -comply with them or not. A rationale is given for each deviation. +It is not possible for the project to follow all MISRA guidelines. Table 1 +below lists all rules and directives and whether we aim to comply with them or +not. A rationale is given for each deviation. .. note:: Enforcing a rule does not mean that the codebase is free of defects @@ -63,6 +63,9 @@ comply with them or not. A rationale is given for each deviation. Third-party libraries are not considered in our MISRA analysis and we do not intend to modify them to make them MISRA compliant. +.. csv-table:: Table 1: MISRA compliance in TF-A code base + :file: misra-compliance.csv + Indentation ----------- @@ -487,5 +490,4 @@ Existing typedefs will be retained for compatibility. *Copyright (c) 2020-2023, Arm Limited. All rights reserved.* .. _`Linux kernel coding style`: https://www.kernel.org/doc/html/latest/process/coding-style.html -.. _`MISRA C:2012 Guidelines`: https://www.misra.org.uk/Activities/MISRAC/tabid/160/Default.aspx -.. _`a spreadsheet`: https://developer.trustedfirmware.org/file/download/lamajxif3w7c4mpjeoo5/PHID-FILE-fp7c7acszn6vliqomyhn/MISRA-and-TF-Analysis-v1.3.ods +.. _`MISRA C:2012 Guidelines`: https://en.wikipedia.org/wiki/MISRA_C#MISRA_C:2012 diff --git a/docs/process/commit-style.rst b/docs/process/commit-style.rst index d7e937be..c287599e 100644 --- a/docs/process/commit-style.rst +++ b/docs/process/commit-style.rst @@ -149,5 +149,5 @@ More details may be found in the `Gerrit Change-Ids documentation`_. .. _Conventional Commits: https://www.conventionalcommits.org/en/v1.0.0 .. _Gerrit Change-Ids documentation: https://review.trustedfirmware.org/Documentation/user-changeid.html .. _Gerrit Signed-off-by Lines guidelines: https://review.trustedfirmware.org/Documentation/user-signedoffby.html -.. _issue: https://developer.trustedfirmware.org/project/board/1/ +.. _issue: https://github.com/TrustedFirmware-A/trusted-firmware-a/issues .. _quick summary: https://www.conventionalcommits.org/en/v1.0.0/#summary diff --git a/docs/process/contributing.rst b/docs/process/contributing.rst index ef9ebd38..11bec7e4 100644 --- a/docs/process/contributing.rst +++ b/docs/process/contributing.rst @@ -4,11 +4,15 @@ Contributor's Guide Getting Started =============== -- Make sure you have a Github account and you are logged on both - `developer.trustedfirmware.org`_ and `review.trustedfirmware.org`_. +- Make sure you have a Github account and you are logged on to + `review.trustedfirmware.org`_. + + Also make sure that you have registered your full name and email address in + your `review.trustedfirmware.org`_ profile. Otherwise, the Gerrit server + might reject patches you attempt to post for review. - If you plan to contribute a major piece of work, it is usually a good idea to - start a discussion around it on the mailing list. This gives everyone + start a discussion around it on the `TF-A mailing list`_. This gives everyone visibility of what is coming up, you might learn that somebody else is already working on something similar or the community might be able to provide some early input to help shaping the design of the feature. @@ -17,16 +21,16 @@ Getting Started it explicitly in the email thread and ensure that the changes that include Third Party IP are made in a separate patch (or patch series). -- Clone `Trusted Firmware-A`_ on your own machine as described in +- Clone the Trusted Firmware-A source code on your own machine as described in :ref:`prerequisites_get_source`. -- Create a local topic branch based on the `Trusted Firmware-A`_ ``master`` +- Create a local topic branch based on the Trusted Firmware-A ``master`` branch. Making Changes ============== -- Ensure commits adhere to the the project's :ref:`Commit Style`. +- Ensure commits adhere to the project's :ref:`Commit Style`. - Make commits of logical units. See these general `Git guidelines`_ for contributing to a project. @@ -93,13 +97,21 @@ Making Changes Submitting Changes ================== -- Submit your changes for review at https://review.trustedfirmware.org - targeting the ``integration`` branch. +.. note:: + Please follow the `How to Contribute Code`_ section of the OpenCI + documentation for general instructions on setting up Gerrit and posting + patches there. The rest of this section provides details about patch + submission rules specifically for the TF-A project. + +- Submit your changes for review using the ``git review`` command. -- Add reviewers for your patch: + This will automatically rebase them onto the upstream ``integration`` branch, + as required by TF-A's patch submission process. - - At least one code owner for each module modified by the patch. See the list - of modules and their :ref:`code owners`. +- From the Gerrit web UI, add reviewers for your patch: + + - At least one code owner for each module modified by the patch. See the + list of modules and their :ref:`code owners`. - At least one maintainer. See the list of :ref:`maintainers`. @@ -167,26 +179,54 @@ Submitting Changes Add CI Configurations ===================== -- TF-A uses Jenkins tool for Continuous Integration and testing activities. - Various CI Jobs are deployed which run tests on every patch before being - merged. So each of your patches go through a series of checks before they - get merged on to the master branch. Kindly ensure, that everytime you add - new files under your platform, they are covered under the following two sections: +TF-A uses Jenkins for Continuous Integration and testing activities. Various CI +jobs are deployed to run tests on every patch before being merged. Each of your +patches go through a series of checks before they get merged on to the master +branch. Kindly ensure that every time you add new files under your platform, +they are covered by the following two sections. Coverity Scan ------------- -- ``Coverity Scan analysis`` is one of the tests we perform on our source code - at regular intervals. We maintain a build script ``tf-cov-make`` which contains the - build configurations of various platforms in order to cover the entire source - code being analysed by Coverity. +The TF-A project makes use of `Coverity Scan` for static analysis, a service +offered by Synopsys for open-source projects. This tool is able to find defects +and vulnerabilities in a code base, such as dereferences of NULL pointers, use +of uninitialized data, control flow issues and many other things. + +The TF-A source code is submitted daily to this service for analysis. Results of +the latest and previous scans, as well as the complete list of defects it +detected, are accessible online from +https://scan.coverity.com/projects/arm-software-arm-trusted-firmware. + +The `tf-a-ci-scripts repository`_ contains scripts to run the Coverity Scan +tools on the integration branch of the TF-A code base and make them available on +https://scan.coverity.com. These scripts get executed daily by the +`tf-a-coverity Jenkins job`_. + +In order to maintain a high level of coverage, including on newly introduced +code, it is important to maintain the appropriate TF-A CI scripts. Details of +when to update these scripts and how to do so follow. + +We maintain a build script - ``tf-cov-make`` - which contains the build +configurations of various platforms in order to cover the entire source code +being analysed by Coverity. -- When you submit your patches for review containing new source files, please - ensure to include them for the ``Coverity Scan analysis`` by adding the - respective build configurations in the ``tf-cov-make`` build script. +When you submit your patches for review, and if they contain new source files, +`TF-A CI static checks job`_ might report that these files are not covered. In +this case, the job's console output will show the following error message:: -- In this section you find the details on how to append your new build - configurations for Coverity scan analysis illustrated with examples: + ****** Newly added files detection check for Coverity Scan analysis on patch(es) ****** + + Result : FAILURE + + New source files have been identified in your patch.. + some/dir/file.c + + please ensure to include them for the ``Coverity Scan analysis`` by adding + the respective build configurations in the ``tf-cov-make`` build script. + +In this section you find the details on how to append your new build +configurations for Coverity scan analysis illustrated with examples: #. We maintain a separate repository named `tf-a-ci-scripts repository`_ for placing all the test scripts which will be executed by the CI Jobs. @@ -194,9 +234,9 @@ Coverity Scan #. In this repository, ``tf-cov-make`` script is located at ``tf-a-ci-scripts/script/tf-coverity/tf-cov-make`` -#. Edit `tf-cov-make`_ script by appending all the possible build configurations with - the specific ``build-flags`` relevant to your platform, so that newly added - source files get built and analysed by Coverity. +#. Edit the `tf-cov-make`_ script by appending all the possible build + configurations with the specific build flags relevant to your platform, so + that newly added source files get built and analysed by Coverity. #. For better understanding follow the below specified examples listed in the ``tf-cov-make`` script. @@ -220,45 +260,44 @@ Coverity Scan make PLAT=hikey960 $(common_flags) ${TBB_OPTIONS} all make PLAT=poplar $(common_flags) all -- In this case for ``Hikey`` boards additional ``build-flags`` has been included - along with the ``commom_flags`` to cover most of the files relevant to it. +- In this case for ``Hikey`` boards additional build flags have been included + along with the ``common_flags`` to cover most of the files relevant to it. - Similar to this you can still find many other different build configurations of various other platforms listed in the ``tf-cov-make`` script. Kindly refer them and append your build configurations respectively. -Test Build Configuration (``tf-l1-build-plat``) ------------------------------------------------ - -- Coverity Scan analysis, runs on a daily basis and will not be triggered for - every individual trusted-firmware patch. +Test Build Configurations +------------------------- -- Considering this, we have other distinguished CI jobs which run a set of test - configurations on every patch, before they are being passed to ``Coverity scan analysis``. +We have CI jobs which run a set of test configurations on every TF-A patch +before they get merged upstream. -- ``tf-l1-build-plat`` is the test group, which holds the test configurations - to build all the platforms. So be kind enough to verify that your newly added - files are built as part of one of the existing platform configurations present - in ``tf-l1-build-plat`` test group. +At the bare minimum, TF-A code should build without any errors for every +supported platform - and every feature of this platform. To make sure this is +the case, we maintain a set of build tests. ``tf-l1-build-plat`` is the test +group which holds all build tests for all platforms. So be kind enough to +verify that your newly added files are covered by such a build test. -- In this section you find the details on how to add the appropriate files, - needed to build your newly introduced platform as part of ``tf-l1-build-plat`` - test group, illustrated with an example: +If this is not the case, please follow the instructions below to add the +appropriate files. We will illustrate this with an example for the ``Hikey`` +platform. -- Lets consider ``Hikey`` platform: - In the `tf-a-ci-scripts repository`_ we need to add a build configuration file ``hikey-default`` - under tf_config folder, ``tf_config/hikey-default`` listing all the build parameters - relevant to it. +- In the `tf-a-ci-scripts repository`_ we need to add a build configuration file + ``hikey-default`` under ``tf_config/`` folder. ``tf_config/hikey-default`` + must list all the build parameters relevant to it. .. code:: shell - #Hikey Build Parameters + # Hikey Build Parameters CROSS_COMPILE=aarch64-none-elf- PLAT=hikey -- Further a test-configuration file ``hikey-default:nil`` need to be added under the - test group, ``tf-l1-build-plat`` located at ``tf-a-ci-scripts/group/tf-l1-build-plat``, - to allow the platform to be built as part of this group. +- Further another file, ``hikey-default:nil``, needs to be added under + ``group/tf-l1-build-plat/`` folder to allow the platform to be built as part + of this test group. ``group/tf-l1-build-plat/hikey-default:nil`` file just + needs to exist but does not contain anything meaningful, apart from a + mandatory copyright notice: .. code:: shell @@ -268,7 +307,11 @@ Test Build Configuration (``tf-l1-build-plat``) # SPDX-License-Identifier: BSD-3-Clause # -- As illustrated above, you need to add the similar files supporting your platform. +- As illustrated above, you need to add similar files supporting your platform. + +For a more elaborate explanation of the TF-A CI scripts internals, including how +to add more complex tests beyond a simple build test, please refer to the `TF-A +CI scripts overview`_ section of the OpenCI documentation. Binary Components ================= @@ -289,11 +332,9 @@ Binary Components -------------- -*Copyright (c) 2013-2022, Arm Limited and Contributors. All rights reserved.* +*Copyright (c) 2013-2024, Arm Limited and Contributors. All rights reserved.* -.. _developer.trustedfirmware.org: https://developer.trustedfirmware.org .. _review.trustedfirmware.org: https://review.trustedfirmware.org -.. _Trusted Firmware-A: https://git.trustedfirmware.org/TF-A/trusted-firmware-a.git .. _Git guidelines: http://git-scm.com/book/ch5-2.html .. _Gerrit Uploading Changes documentation: https://review.trustedfirmware.org/Documentation/user-upload.html .. _TF-A Tests: https://trustedfirmware-a-tests.readthedocs.io @@ -302,3 +343,7 @@ Binary Components .. _TF-A mailing list: https://lists.trustedfirmware.org/mailman3/lists/tf-a.lists.trustedfirmware.org/ .. _tf-a-ci-scripts repository: https://git.trustedfirmware.org/ci/tf-a-ci-scripts.git/ .. _tf-cov-make: https://git.trustedfirmware.org/ci/tf-a-ci-scripts.git/tree/script/tf-coverity/tf-cov-make +.. _How to Contribute Code: https://tf-ci-users-guide.readthedocs.io/en/latest/#how-to-contribute-code +.. _TF-A CI scripts overview: https://tf-ci-users-guide.readthedocs.io/en/latest/#tf-a-ci-scripts-overview +.. _tf-a-coverity Jenkins job: https://ci.trustedfirmware.org/job/tf-a-coverity/ +.. _TF-A CI static checks job: https://ci.trustedfirmware.org/job/tf-a-static-checks/ diff --git a/docs/process/maintenance.rst b/docs/process/maintenance.rst index 45aada26..5ee435e8 100644 --- a/docs/process/maintenance.rst +++ b/docs/process/maintenance.rst @@ -51,5 +51,5 @@ To put an individual's name up for election, and update the list of maintainers on the :ref:`Project Maintenance<maintainers>` page. -.. _trustedfirmware.org Project Maintenance Process: https://developer.trustedfirmware.org/w/collaboration/project-maintenance-process/ -.. _here: https://developer.trustedfirmware.org/w/collaboration/project-maintenance-process/#how-to-become-a-maintainer +.. _trustedfirmware.org Project Maintenance Process: https://trusted-firmware-docs.readthedocs.io/en/latest/generic_processes/project_maintenance_process.html +.. _here: https://trusted-firmware-docs.readthedocs.io/en/latest/generic_processes/project_maintenance_process.html#how-to-become-a-maintainer diff --git a/docs/process/misra-compliance.csv b/docs/process/misra-compliance.csv new file mode 100644 index 00000000..7b029301 --- /dev/null +++ b/docs/process/misra-compliance.csv @@ -0,0 +1,174 @@ +Seq,Dir / Rule,Number,Source,Category,Checker Enabled,Enforced,Comments +1,D,1.1,MISRA C 2012,Required,N/A,Yes, +2,D,2.1,MISRA C 2012,Required,N/A,Yes, +3,D,3.1,MISRA C 2012,Required,N/A,No,It can’t be done retroactively. +4,D,4.1,MISRA C 2012,Required,N/A,Yes, +5,D,4.2,MISRA C 2012,Advisory,N/A,Yes, +6,D,4.3,MISRA C 2012,Required,Yes,Yes, +7,D,4.4,MISRA C 2012,Advisory,Yes,Yes, +8,D,4.5,MISRA C 2012,Advisory,Yes,Yes, +9,D,4.6,MISRA C 2012,Advisory,No,No,We use a mix of both. It would be too disruptive for the project to change. +10,D,4.7,MISRA C 2012,Required,Yes,Yes, +11,D,4.8,MISRA C 2012,Advisory,No,No,Fixing all instances would involve invasive changes to the codebase for no good reason. +12,D,4.9,MISRA C 2012,Advisory,No,No,"We mustn’t introduce new macros unless strictly needed, but this affects assert(), INFO(), etc. It creates too much noise in the report for little gain." +13,D,4.10,MISRA C 2012,Required,Yes,Yes, +14,D,4.11,MISRA C 2012,Required,Yes,Yes, +15,D,4.12,MISRA C 2012,Required,Yes,Yes, +16,D,4.13,MISRA C 2012,Advisory,Yes,Yes, +17,D,4.14,MISRA C 2012 AMD-1,Required,Yes,Yes, +18,R,1.1,MISRA C 2012,Required,Yes,Yes, +19,R,1.2,MISRA C 2012,Advisory,Yes,Optional,It bans __attribute__(()) and similar helpers. +20,R,1.3,MISRA C 2012,Required,N/A,Yes, +21,R,2.1,MISRA C 2012,Required,Yes,Yes, +22,R,2.2,MISRA C 2012,Required,Yes,Yes, +23,R,2.3,MISRA C 2012,Advisory,Yes,Optional,It prevents the usage of CASSERT(). +24,R,2.4,MISRA C 2012,Advisory,No,No,Header files may use enumerations instead of defines to group sets of values. +25,R,2.5,MISRA C 2012,Advisory,No,No,We define many headers with macros that are unused in the project but may be used by non-upstream code or may be desirable for completeness. +26,R,2.6,MISRA C 2012,Advisory,Yes,Yes, +27,R,2.7,MISRA C 2012,Advisory,No,No,Doesn't allow for simple implementations of porting functions that don't require all parameters. +28,R,3.1,MISRA C 2012,Required,Yes,Yes, +29,R,3.2,MISRA C 2012,Required,Yes,Yes, +30,R,4.1,MISRA C 2012,Required,Yes,Yes, +31,R,4.2,MISRA C 2012,Advisory,Yes,Yes, +32,R,5.1,MISRA C 2012,Required,No,No,We use weak symbols that prevent us from complying with this rule. +33,R,5.2,MISRA C 2012,Required,Yes,Yes, +34,R,5.3,MISRA C 2012,Required,Yes,Yes, +35,R,5.4,MISRA C 2012,Required,Yes,Yes, +36,R,5.5,MISRA C 2012,Required,Yes,Yes, +37,R,5.6,MISRA C 2012,Required,Yes,Yes, +38,R,5.7,MISRA C 2012,Required,Yes,Optional,Fixing all existing defects is problematic because of compatibility issues. +39,R,5.8,MISRA C 2012,Required,No,No,We use weak symbols that prevent us from complying with this rule. +40,R,5.9,MISRA C 2012,Advisory,Yes,Yes, +41,R,6.1,MISRA C 2012,Required,Yes,Yes, +42,R,6.2,MISRA C 2012,Required,Yes,Yes, +43,R,7.1,MISRA C 2012,Required,Yes,Yes, +44,R,7.2,MISRA C 2012,Required,Yes,Yes, +45,R,7.3,MISRA C 2012,Required,Yes,Yes, +46,R,7.4,MISRA C 2012,Required,Yes,Yes, +47,R,8.1,MISRA C 2012,Required,Yes,Yes, +48,R,8.2,MISRA C 2012,Required,Yes,Yes, +49,R,8.3,MISRA C 2012,Required,Yes,Yes, +50,R,8.4,MISRA C 2012,Required,Yes,Yes, +51,R,8.5,MISRA C 2012,Required,Yes,Yes, +52,R,8.6,MISRA C 2012,Required,No,No,We use weak symbols that prevent us from complying with this rule. +53,R,8.7,MISRA C 2012,Advisory,No,No,"Bans pattern of declaring funcs in private header that are used/defined in separate translation units, which seems over the top." +54,R,8.8,MISRA C 2012,Required,Yes,Yes, +55,R,8.9,MISRA C 2012,Advisory,Yes,Yes, +56,R,8.10,MISRA C 2012,Required,Yes,Yes, +57,R,8.11,MISRA C 2012,Advisory,Yes,Optional,This may not be possible in some interfaces. +58,R,8.12,MISRA C 2012,Required,Yes,Yes, +59,R,8.13,MISRA C 2012,Advisory,Yes,Optional,The benefits of fixing existing code aren’t worth the effort. +60,R,8.14,MISRA C 2012,Required,Yes,Yes, +61,R,9.1,MISRA C 2012,Mandatory,Yes,Yes, +62,R,9.2,MISRA C 2012,Required,Yes,Yes, +63,R,9.3,MISRA C 2012,Required,Yes,Yes, +64,R,9.4,MISRA C 2012,Required,Yes,Yes, +65,R,9.5,MISRA C 2012,Required,Yes,Yes, +66,R,10.1,MISRA C 2012,Required,Yes,Optional,Fixing existing code may be counter-productive and introduce bugs. +67,R,10.2,MISRA C 2012,Required,Yes,Yes, +68,R,10.3,MISRA C 2012,Required,Yes,Optional,Fixing existing code may be counter-productive and introduce bugs. +69,R,10.4,MISRA C 2012,Required,Yes,Optional,Fixing existing code may be counter-productive and introduce bugs. +70,R,10.5,MISRA C 2012,Advisory,Yes,Yes, +71,R,10.6,MISRA C 2012,Required,Yes,Yes, +72,R,10.7,MISRA C 2012,Required,Yes,Yes, +73,R,10.8,MISRA C 2012,Required,Yes,Yes, +74,R,11.1,MISRA C 2012,Required,Yes,Yes, +75,R,11.2,MISRA C 2012,Required,Yes,Yes, +76,R,11.3,MISRA C 2012,Required,Yes,Yes, +77,R,11.4,MISRA C 2012,Advisory,No,No,This would be invasive for TF (e.g. in exported linker script macros). Also bans conversion from uintptr_t. +78,R,11.5,MISRA C 2012,Advisory,No,No,"This seems to preclude the pattern of using void * in interfaces to hide the real object, which we use extensively." +79,R,11.6,MISRA C 2012,Required,Yes,Optional,This is needed in several cases. +80,R,11.7,MISRA C 2012,Required,Yes,Yes, +81,R,11.8,MISRA C 2012,Required,Yes,Yes, +82,R,11.9,MISRA C 2012,Required,Yes,Yes, +83,R,12.1,MISRA C 2012,Advisory,Yes,Yes, +84,R,12.2,MISRA C 2012,Required,Yes,Yes,"This rule is fine, but there are lots of false positives in Coverity." +85,R,12.3,MISRA C 2012,Advisory,Yes,Yes, +86,R,12.4,MISRA C 2012,Advisory,Yes,Yes, +87,R,12.5,MISRA C 2012 AMD-1,Mandatory,Yes,Yes, +88,R,13.1,MISRA C 2012,Required,Yes,Yes, +89,R,13.2,MISRA C 2012,Required,Yes,Yes, +90,R,13.3,MISRA C 2012,Advisory,Yes,Yes, +91,R,13.4,MISRA C 2012,Advisory,Yes,Yes, +92,R,13.5,MISRA C 2012,Required,Yes,Yes, +93,R,13.6,MISRA C 2012,Mandatory,Yes,Yes, +94,R,14.1,MISRA C 2012,Required,Yes,Yes, +95,R,14.2,MISRA C 2012,Required,Yes,Yes, +96,R,14.3,MISRA C 2012,Required,Yes,Yes, +97,R,14.4,MISRA C 2012,Required,Yes,Yes, +98,R,15.1,MISRA C 2012,Advisory,No,No,In some cases goto may be useful for readability. +99,R,15.2,MISRA C 2012,Required,Yes,Yes, +100,R,15.3,MISRA C 2012,Required,Yes,Yes, +101,R,15.4,MISRA C 2012,Advisory,Yes,Yes, +102,R,15.5,MISRA C 2012,Advisory,No,No,This has no real value. It may make code less understandable than before. +103,R,15.6,MISRA C 2012,Required,No,No,This directly contradicts the Linux style guidelines and would require many changes. We would have to remove that rule from checkpatch. +104,R,15.7,MISRA C 2012,Required,Yes,Yes, +105,R,16.1,MISRA C 2012,Required,No,No,Cannot comply with this unless we comply with 16.3 +106,R,16.2,MISRA C 2012,Required,Yes,Yes, +107,R,16.3,MISRA C 2012,Required,No,No,Returns within switch statements and fall-throughs can improve readability. +108,R,16.4,MISRA C 2012,Required,Yes,Yes, +109,R,16.5,MISRA C 2012,Required,Yes,Yes, +110,R,16.6,MISRA C 2012,Required,Yes,Yes, +111,R,16.7,MISRA C 2012,Required,Yes,Yes, +112,R,17.1,MISRA C 2012,Required,No,No,This is needed for printf. +113,R,17.2,MISRA C 2012,Required,Yes,Yes,Bans recursion. We consider it acceptable if the max depth is known. +114,R,17.3,MISRA C 2012,Mandatory,Yes,Yes, +115,R,17.4,MISRA C 2012,Mandatory,Yes,Yes, +116,R,17.5,MISRA C 2012,Advisory,Yes,Yes, +117,R,17.6,MISRA C 2012,Mandatory,Yes,Yes, +118,R,17.7,MISRA C 2012,Required,Yes,Optional,In some cases it doesn’t add any value to the code (like with memset() or printf()). +119,R,17.8,MISRA C 2012,Advisory,Yes,Optional,It would make some one-line functions grow in size for no reason. +120,R,18.1,MISRA C 2012,Required,Yes,Yes, +121,R,18.2,MISRA C 2012,Required,Yes,Yes, +122,R,18.3,MISRA C 2012,Required,Yes,Yes, +123,R,18.4,MISRA C 2012,Advisory,Yes,Yes, +124,R,18.5,MISRA C 2012,Advisory,Yes,Yes, +125,R,18.6,MISRA C 2012,Required,Yes,Yes, +126,R,18.7,MISRA C 2012,Required,Yes,Yes, +127,R,18.8,MISRA C 2012,Required,Yes,Yes, +128,R,19.1,MISRA C 2012,Mandatory,Yes,Yes, +129,R,19.2,MISRA C 2012,Advisory,Yes,Optional,"Unions can be useful. We almost don’t use them, so it’s ok." +130,R,20.1,MISRA C 2012,Advisory,Yes,Optional,In some files we have assembly-compatible includes followed by assembly-compatible definitions followed by C includes and C declarations. This is done to not have #ifdef in the include list. +131,R,20.2,MISRA C 2012,Required,Yes,Yes, +132,R,20.3,MISRA C 2012,Required,Yes,Yes, +133,R,20.4,MISRA C 2012,Required,Yes,Yes, +134,R,20.5,MISRA C 2012,Advisory,Yes,Yes, +135,R,20.6,MISRA C 2012,Required,Yes,Yes, +136,R,20.7,MISRA C 2012,Required,Yes,Yes, +137,R,20.8,MISRA C 2012,Required,Yes,Optional,We need a new configuration system to fix all defects. +138,R,20.9,MISRA C 2012,Required,Yes,Optional,"We use a mix of #if and #ifdef for boolean macros, which may raise some failures here. We should consistently use one or the other" +139,R,20.10,MISRA C 2012,Advisory,Yes,Optional,"It’s good to avoid them, but they are sometimes needed." +140,R,20.11,MISRA C 2012,Required,Yes,Yes, +141,R,20.12,MISRA C 2012,Required,Yes,Yes, +142,R,20.13,MISRA C 2012,Required,Yes,Yes, +143,R,20.14,MISRA C 2012,Required,Yes,Yes, +144,R,21.1,MISRA C 2012,Required,Yes,Yes, +145,R,21.2,MISRA C 2012,Required,Yes,Yes, +146,R,21.3,MISRA C 2012,Required,Yes,Yes, +147,R,21.4,MISRA C 2012,Required,Yes,Yes, +148,R,21.5,MISRA C 2012,Required,Yes,Yes, +149,R,21.6,MISRA C 2012,Required,No,No,This bans printf. +150,R,21.7,MISRA C 2012,Required,Yes,Yes, +151,R,21.8,MISRA C 2012,Required,Yes,Yes, +152,R,21.9,MISRA C 2012,Required,Yes,Yes, +153,R,21.10,MISRA C 2012,Required,Yes,Yes, +154,R,21.11,MISRA C 2012,Required,Yes,Yes, +155,R,21.12,MISRA C 2012,Advisory,Yes,Yes, +156,R,21.13,MISRA C 2012 AMD-1,Mandatory,Yes,Yes, +157,R,21.14,MISRA C 2012 AMD-1,Required,Yes,Yes, +158,R,21.15,MISRA C 2012 AMD-1,Required,Yes,Yes, +159,R,21.16,MISRA C 2012 AMD-1,Required,Yes,Yes, +160,R,21.17,MISRA C 2012 AMD-1,Mandatory,Yes,Yes, +161,R,21.18,MISRA C 2012 AMD-1,Mandatory,Yes,Yes, +162,R,21.19,MISRA C 2012 AMD-1,Mandatory,Yes,Yes, +163,R,21.20,MISRA C 2012 AMD-1,Mandatory,Yes,Yes, +164,R,22.1,MISRA C 2012,Required,Yes,Yes, +165,R,22.2,MISRA C 2012,Mandatory,Yes,Yes, +166,R,22.3,MISRA C 2012,Required,Yes,Yes, +167,R,22.4,MISRA C 2012,Mandatory,Yes,Yes, +168,R,22.5,MISRA C 2012,Mandatory,Yes,Yes, +169,R,22.6,MISRA C 2012,Mandatory,Yes,Yes, +170,R,22.7,MISRA C 2012 AMD-1,Required,Yes,Yes, +171,R,22.8,MISRA C 2012 AMD-1,Required,Yes,Yes, +172,R,22.9,MISRA C 2012 AMD-1,Required,Yes,Yes, +173,R,22.10,MISRA C 2012 AMD-1,Required,Yes,Yes, diff --git a/docs/process/security.rst b/docs/process/security.rst index c6429ad5..1e7ac2ec 100644 --- a/docs/process/security.rst +++ b/docs/process/security.rst @@ -73,8 +73,10 @@ Security Advisories | |TFV-10| | Incorrect validation of X.509 certificate extensions can result | | | in an out-of-bounds read | +-----------+------------------------------------------------------------------+ +| |TFV-11| | A Malformed SDEI SMC can cause out of bound memory read | ++-----------+------------------------------------------------------------------+ -.. _issue tracker: https://developer.trustedfirmware.org/project/board/1/ +.. _issue tracker: https://github.com/TrustedFirmware-A/trusted-firmware-a/issues .. _mailing list: https://lists.trustedfirmware.org/mailman3/lists/tf-a.lists.trustedfirmware.org/ .. |TFV-1| replace:: :ref:`Advisory TFV-1 (CVE-2016-10319)` @@ -87,8 +89,9 @@ Security Advisories .. |TFV-8| replace:: :ref:`Advisory TFV-8 (CVE-2018-19440)` .. |TFV-9| replace:: :ref:`Advisory TFV-9 (CVE-2022-23960)` .. |TFV-10| replace:: :ref:`Advisory TFV-10 (CVE-2022-47630)` +.. |TFV-11| replace:: :ref:`Advisory TFV-11 (CVE-2023-49100)` -.. _TrustedFirmware.org security incident process: https://developer.trustedfirmware.org/w/collaboration/security_center/ +.. _TrustedFirmware.org security incident process: https://trusted-firmware-docs.readthedocs.io/en/latest/security_center/ -------------- diff --git a/docs/resources/diagrams/context_init_coldboot.png b/docs/resources/diagrams/context_init_coldboot.png new file mode 100644 index 0000000000000000000000000000000000000000..85606e451d9421da947fa1aa2c46a66d7b5e8dc4 GIT binary patch literal 99885 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\dM*T%&&8K+k$Ls)qZO<6KwK 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 literal 0 HcmV?d00001 diff --git a/docs/resources/diagrams/context_init_warmboot.png b/docs/resources/diagrams/context_init_warmboot.png new file mode 100644 index 0000000000000000000000000000000000000000..19f11e35a05f1e1abd9fd336774338dae99beb32 GIT binary patch literal 97092 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 MtMaBq+Bo3<0OeG@<NyEw literal 0 HcmV?d00001 diff --git a/docs/resources/diagrams/context_memory_allocation.png b/docs/resources/diagrams/context_memory_allocation.png new file mode 100644 index 0000000000000000000000000000000000000000..a2e6a8e5c98e5d06203933a643754ab42843c4d7 GIT binary patch literal 204249 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	hES-a{SCX!t*7@!$!>LoxU~f7I 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%0AyJ6;@r!lhF6i*?-%)u?oMdN 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≤9hc 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 literal 0 HcmV?d00001 diff --git a/docs/resources/diagrams/cot-dualroot.jpg b/docs/resources/diagrams/cot-dualroot.jpg new file mode 100644 index 0000000000000000000000000000000000000000..c56392eb64e08288e1782427cbfa679a66dad707 GIT binary patch literal 134378 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�e^{g)W!w4#XEO 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 AQvd(} literal 0 HcmV?d00001 diff --git a/docs/resources/diagrams/cot-tbbr.jpg b/docs/resources/diagrams/cot-tbbr.jpg new file mode 100644 index 0000000000000000000000000000000000000000..7ecd69d011c01e50eda823bea189c07c62ab02a8 GIT binary patch literal 131981 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%0ACU3MUJw)WUz>@$(@r> zjdvIKVhYA6fseJoHxw$PC;@?mQ8aEMQ7gzL0?NDBzw_t0jjHlKMk7c9MrPBHj|H(l zIQTwH;6i-h+4lKudglCOX<>=kK=02c>s1*=`<3KW5b~U2uvWdMC2heJv6GnsUXfdL zM{^p37$3nFY24^CdCCX8GR=0sF&Zy^bwSJN=q`?`ivbSil&8)S#2mv;^*6IRqrFkm zq1=QU$>AGxfrX>*a8Q$NVDn58QxHQ-jB~h%qh|})R-?t?;`1L%;o$cVMSpS$X$IHs zVmgkyEVU9xw`a6$2P745AR2p_p(>OS96K-xMki(-$RbFft<U-RG}!j#Mrn{nqo4A; z$L+q9*Cr}`21w|3j9;BsZr<F!fl95wln)7Fy&?TZjIDS>kxZ5HxZhsd6f$!@%n#=* zT$;6{Z9UdJTEJvHlYxg>Y>s|U)%M$NEqDbF^*%yV;11Z(Y?+<c&wD)I%e5)v)2q{p ztd8m<AHnP>Y=%tzYR#aA?M`;+S7MW;w&*hzoZgD2kN$630~Eq2S?`;+%B0bA&JJ)3 zD=!2g*BjK--C+)M97F?+d0AO1(DS}HmhGq`a?&Z!b|7z4T6x~!_`O~*e3%|=om7>8 zID$`s9H;1eRcW4$l8RnPddC3PcRgDh<I|~k{`uVV^^{bfKBtCTM3w=0r562AX}f4F zTEh+5(J#PIVSS@LV^?5s$p|D$+UC$amM&Lkk}mI9dFP5xVu;sH=2+y@Lb}rP4;~fS z3TQrKh)knb_egiAp!3!d=g;rIO*ucQumS!3DkFE%Md?Bse07{=O3(ALFR`Hlo>#}} zsEJJVll%g}eON>*G$~U5Rm&KwWqHbZcBZ@rzkcuO;6wn2zoWS`;f{p1ltTGJ`g&ZP zA>w%XB{iGW>newOV7mAY`=xCF)l>3AxS9Swyk#e9yEIkUn2Oqc71QTL%}ewLh`lNe z`__|{qcrY=3uu_+&u=$%m(VkCV1oTFVe$9zFK~CrkR`f@L0e3X=Vd%f&9W&uSv8*# z#@e68(dp@WNT1hh`s_}KYYJD!c7<1Vr<uz;E*Wv|wJhh1H+=>mvs1+nX~{=^GVye2 zmb`=HZZ-!22-ZdKC60C~IDctgOD$r<qn=d>6>F}i`)y}rTCB~n+L`z6n0#a=YfR`R z17RCnm70p%V(fJ;GEO3GRl{w0nu6Sc54aT~ckHF~nu5pk89WK(k`b<>A(82HGH1?P zCO=M2#LmqVUcuYt#V0oJ((oFVUTWcK$UV_}e}?6ja@y8z_!~S{cb%XKBZJtiqKB1f zohI3Z)u?7}R}!@e*eO3_pGdR*YS2*#XQq=(=}nFN>Sfuz_y?$W9q%-M0b<7InX3bh zywd1>!)v)Bw8;aBcY=(;Zm(&T;JW-g_3<Lr(@+Hk6*Iz}rBIDn4TK&%95?9_?$R<! zEK<g?i(}e!A^`0xdtw`vp=UQXi@LZv<}Y}y#cF9ZGw)pFY9iXh1xA(5Y-6UW-@$rg zTDCD%k4w#)ix4j^DkJyYpAF-E5tWFjST%6sd1m6R;aINpk{Ueg6sNX3OkRiN_()tM z;JAv~+KzfB2jer8GcvjD2Ye_>HB(ORe21D{OFK<6)DMqE*Ep7IMOP2iC#OzJkk65u z?_nb?n2Y6oHk8kcIrC*{tzZA+JzzYj@|31yG~L~qJbmhT3wjWF7+=M76lZ8=S(>At zRXD64)5Th)K%6Un`Yv<N`s?^NgWm)qqm;kB<&h-eHqb6ars-8NFz7wKEW@0|f*N|X zpZ{U{hrt`#Uw|rm*)KZ2cYanL#{U8=3jaMt7AC=Uyv_JXsdjBZ?4AZtQIVEbN48(^ zTAID>FlwIpYgLrY@)fIP-@q{U*53foFMsu2{tYPo+4#RBsm^YGkCv{H){oby>2H}X zdn~SoXo#Y<)d~g(`u{qwc>2Tq-1MFyZ}-gT-_YQHz<d7%X8iY5418tB3|Zf|(7eym z@9><v{bepJz|A#yoF6dF<{~<>8p|^@vdylVEU=Ju!qJ(^q|~NwKa#Pp8A$#{J(1PC zQg|Eb$0-xzQ8N5blf+7tnKKv3{h?<1JIVMHiDH?@75|}!n9AXqvUE`g4lq>0z~YY6 zyUyR1qKv01f%JO$7=_m47TWD=h1P$j4EnpO>z^^#$zBv*r~XUU|I~wC3;AHOi<#v@ z=T`sddQOOy+1!t*%R5)vR>5;~dFo^K7^X}WUo-vx1&5^G{-(0~UnsL~M)vV%zESr& z@_3|1ySyJurSHWUhw9S9g=w|v7UlO1k4t+vUc&2#$cn`q*i>9GmPM9&9@Ex_(21I3 zi{19eb(%N;&StNW6^mk`?_i>*Fwsvi(b9j7{%5&cah)dJfU`N6-2cgFs-$rzV_toN z_D*Kxqg7s>Oqx%BFY$lroxii=KSf-QTT*E47ohYPU})!Kg@v=AZW~g)p6vfewK#S! z?Yx@R@xLlw?Cs3u%*N;i&+}(DAIrRsQM=`-Eo32e(3jHDw&yaO5+0BCdzxmM*Re;+ zvE}T&W29!xUB~VCy<jE7wiIu@7Ei#zVKkaF&6u~;W-&(1XSc#ZIP8cWgJ*LGN=X0E zHPb(;Z?9pirsbQQko}4N1;CtqCxAKm4r8Vq4;Kdu3kzeT900%qkimp8hu!ra9YefA zl4n`qg^hB$8#bQkKaR7*l7;;OOyiOIJqHMLXMuDd0X;YRqevJKf7}o^QkBl`Vbb7p zkb8bRl8@6+yN};83g8Feg&jqGlN@kgPYUGrxiA9>^UDu(uTMNL4nj~S3d&oTBln1D zg{(t_&Tr<_E-0^oLJ<X=6xbEfNd$c!+<+wdn&bCaSZX+=y+zqLbGkC8?Ljw*2ZB#U zU`d`Ld_>k8yGq`TxVWIk5FNTcU5x(n(mm3sLOjzWn}8NtxKCt89sc2idgdc^giD(q zss8rBhX!0$SDaK48~h$!Lbbyy-Y_B=fOLUl`VGDUakK(nCYb>K+0AWFxt>6K@|9B^ z8n?cOc?GI%I^vt}yrC3VL-)q3_d2v6Lh$lX50~(9(G)RH@wBke@xf(xe*u6%qiR3H zxD*V$0KxAcrR6@Jwi&CCQWItdk6?HH#I<3)XzzIeSne^RNCbKl3fUF2g3XBhTM~Vx zzH<&eoBUq>qnIow1;{;53|CK5zJ2zIDr9#5oE{g;D!Sc=zh|~A)|CRUEz9}yJ?nEB zh2qjE{6J;m1m%ZHL7W2qT=4IPXR=o;Fz!6gTVWx0jH!a@(I7b4WAOWn&3*uJ_mLeV zl;={n)mD>=xVP{f#Y?Ur?eizc(wj(IS`lu>ity>TLz?_VuD-vA-W&KF^A5qSdAKGO zcn0$fQ3FL_{Q?l&b%7;6K6w6U;7*23h41v0b3Rn}7;Se%S+m~$>Ap7@(34~Zc}Mf* z?7{ZQ-8s7QUjQb||Ju+Ra*JOUt|hp8!t6wo6qY0gzy}1!mx*!9qvcEhdT}pCvI~-) ziaazF=68-Ixbo8B?sUvOu}@xfS<6TtbzaMO@}G1l7P*!630kQ~wi_7l_RV()NT>X# z9jP08lpDuec*EmGIq|vxEX2Uc3moRH2~9ul@I;<7Wjp<4RuC7t*@6-|K7iP?_)6A_ zPT=g_RxHJ#7=EE*k1xx1(mdrsyn3FQEt;_X8Ex}Dac`!d>hN3g--r<Kjs8Ti48m(I z2%2w{GeA;-PHf=jksvxRzT`C}S<c0-pB;)@-0%7JGQe|V=rI#Rz|ls3Tu2(JJ)qEA zt1Q%}nf@1G#|iv6iaqd>H(wxhW~tizOZ#n?at~(~8cLSB+a{3&j;bwn3S?ybAaN_b zbxN|24V-4%i%Wg|Cc3!|n$9d56ot>zqxWzrS`K=6b=yhUnu#<bZ>3l8_1HeFe`*ly z+=n0iRSq9cLzqVF`GtgO^_!wkY#^|CvCAsnQ=LULKl-sT3XEOa_T`R^J26IgjhZ@C ziZ60K4qz26!VSS0apc@&oL4yZ!FCkbuh^sKfqawg6M24f^fq4VwC`m8dtsv%89dWk zrO=5*t|D7gxvB*A5dPad35p^c5YKORD%=wfs|~w<>Y<rxrJa!MywfgH)hbEk6>?u{ zNm55{>OD>29L1Y#!b_!QL8lw_vqo!~=}XUO0E+TLLFuUV0^tk&tg?yk@fxhh>RnB3 z3oM?jZRbVUwsZG&rkBw`g_%1cU=@4E5<|mgb`qOQkYQGM*bMJTK#%XOIO4I;qeQev zERqgy09%h$6e3Iz_w7rAyP5<-W<M1-=yMam34-K;+`OgxN(KfKHa)}6%VV2Q@sw{5 zC56$zpsFkcg�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 literal 0 HcmV?d00001 diff --git a/docs/resources/diagrams/cpu_data_config_context_memory.png b/docs/resources/diagrams/cpu_data_config_context_memory.png new file mode 100644 index 0000000000000000000000000000000000000000..3e64dddf453739b448c0eae771a2dab9eb422857 GIT binary patch literal 680091 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%0AAPeKz+6{G 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 YMQ?@^5)^-b4E(1ouO^ps-N5Vr0Q^gd@Bjb+ literal 0 HcmV?d00001 diff --git a/docs/resources/diagrams/percpu-data-struct.png b/docs/resources/diagrams/percpu-data-struct.png new file mode 100644 index 0000000000000000000000000000000000000000..d8977d53dc2881b546f5e010276ac3396edd29d3 GIT binary patch literal 207339 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­H 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 NIccRcnNkKf{vY9dR~G;P literal 0 HcmV?d00001 diff --git a/docs/resources/diagrams/plantuml/rss_attestation_flow.puml b/docs/resources/diagrams/plantuml/rse_attestation_flow.puml similarity index 99% rename from docs/resources/diagrams/plantuml/rss_attestation_flow.puml rename to docs/resources/diagrams/plantuml/rse_attestation_flow.puml index aca5c01f..9d7d7802 100644 --- a/docs/resources/diagrams/plantuml/rss_attestation_flow.puml +++ b/docs/resources/diagrams/plantuml/rse_attestation_flow.puml @@ -5,7 +5,7 @@ box AP participant RMM participant BL31 endbox -box RSS +box RSE participant DelegAttest participant InitAttest participant MeasuredBoot diff --git a/docs/resources/diagrams/plantuml/rse_measured_boot_flow.puml b/docs/resources/diagrams/plantuml/rse_measured_boot_flow.puml new file mode 100644 index 00000000..97af5627 --- /dev/null +++ b/docs/resources/diagrams/plantuml/rse_measured_boot_flow.puml @@ -0,0 +1,79 @@ +@startuml +skinparam ParticipantPadding 10 +skinparam BoxPadding 10 +box RSE +participant RSE_BL1_1 +participant RSE_BL1_2 +participant RSE_BL2 +participant RSE_S +endbox +box SCP +participant SCP_BL1 +endbox +box AP +participant AP_BL1 +participant AP_BL2 +participant AP_BL31 +endbox + +== RSE Boot phase == +-> RSE_BL1_1: Reset +Rnote over RSE_BL1_1: ROM code, XIP +Rnote over RSE_BL1_2: OTP code, XIP +Rnote over RSE_BL2, AP_BL31: Stored in flash, loaded and executed in RAM +activate RSE_BL1_1 #Green +RSE_BL1_1 -->> RSE_BL1_2: Validate, measure +Rnote over RSE_BL1_1: BL1_2 measurement\n\ saved to a shared buffer +RSE_BL1_1 -> RSE_BL1_2: Pass execution +deactivate RSE_BL1_1 +activate RSE_BL1_2 #Green +RSE_BL1_2 -->> RSE_BL2: Validate, measure, load +Rnote over RSE_BL1_2: RSE_BL2 measurement\n\ saved to a shared buffer +RSE_BL1_2 -> RSE_BL2: Pass execution +deactivate RSE_BL1_2 +activate RSE_BL2 #Green +RSE_BL2 -->> RSE_S: Validate, measure, load +RSE_BL2 -->> SCP_BL1: Validate, measure, load +Rnote over RSE_BL2: RSE_S and SCP_BL1\n\ measurements saved\n\ to a shared buffer +RSE_BL2 -> SCP_BL1: Release from reset +activate SCP_BL1 #Green +Rnote over RSE_BL2, SCP_BL1: MHU init between RSE and SCP +Rnote over SCP_BL1: Configure memory +Rnote over RSE_BL2: Waits for SCP +SCP_BL1 --> RSE_BL2: Done +RSE_BL2 -->> AP_BL1: Validate, measure, load +Rnote over RSE_BL2: AP_BL1 measurement\n\ saved to a shared buffer +RSE_BL2 -> AP_BL1: Release from reset +activate AP_BL1 #Green +RSE_BL2 -> RSE_S: Pass execution +deactivate RSE_BL2 +activate RSE_S #Green +Rnote over RSE_S: Measurements read from\n\ shared buffer and saved by\n\ +Measured Boot service to\n\ measurement slots. + +== RSE Runtime / AP Boot phase == +Rnote over RSE_S, AP_BL1: MHU init between RSE and AP +Rnote over AP_BL1: Measure and load:\n\ FW_CONFIG\n\ TB_FW_CONFIG +AP_BL1 -> RSE_S: Extend measurement +Rnote over RSE_S: Measured Boot:\n\ store measurement +AP_BL1 -->> AP_BL2: Validate, measure,load +AP_BL1 -> RSE_S: Extend measurement +Rnote over RSE_S: Measured Boot:\n\ store measurement +AP_BL1 -> AP_BL2: Pass execution +deactivate AP_BL1 +activate AP_BL2 #Green +Rnote over AP_BL2: Measure and load:\n\ HW_CONFIG +AP_BL2 -> RSE_S: Extend measurement +Rnote over RSE_S: Measured Boot:\n\ store measurement +AP_BL2 -->> AP_BL31: Validate, measure,load +Rnote over AP_BL2: Measure and load:\n\ BL31 +AP_BL2 -> RSE_S: Extend measurement +Rnote over RSE_S: Measured Boot:\n\ store measurement +Rnote over AP_BL2: Measure and load:\n\ RMM +AP_BL2 -> RSE_S: Extend measurement +Rnote over RSE_S: Measured Boot:\n\ store measurement +AP_BL2 -> AP_BL31: Pass execution +deactivate AP_BL2 +activate AP_BL31 #Green +== RSE / AP Runtime == +@enduml diff --git a/docs/resources/diagrams/plantuml/rss_measured_boot_flow.puml b/docs/resources/diagrams/plantuml/rss_measured_boot_flow.puml deleted file mode 100644 index 1aeb1a9f..00000000 --- a/docs/resources/diagrams/plantuml/rss_measured_boot_flow.puml +++ /dev/null @@ -1,79 +0,0 @@ -@startuml -skinparam ParticipantPadding 10 -skinparam BoxPadding 10 -box RSS -participant RSS_BL1_1 -participant RSS_BL1_2 -participant RSS_BL2 -participant RSS_S -endbox -box SCP -participant SCP_BL1 -endbox -box AP -participant AP_BL1 -participant AP_BL2 -participant AP_BL31 -endbox - -== RSS Boot phase == --> RSS_BL1_1: Reset -Rnote over RSS_BL1_1: ROM code, XIP -Rnote over RSS_BL1_2: OTP code, XIP -Rnote over RSS_BL2, AP_BL31: Stored in flash, loaded and executed in RAM -activate RSS_BL1_1 #Green -RSS_BL1_1 -->> RSS_BL1_2: Validate, measure -Rnote over RSS_BL1_1: BL1_2 measurement\n\ saved to a shared buffer -RSS_BL1_1 -> RSS_BL1_2: Pass execution -deactivate RSS_BL1_1 -activate RSS_BL1_2 #Green -RSS_BL1_2 -->> RSS_BL2: Validate, measure, load -Rnote over RSS_BL1_2: RSS_BL2 measurement\n\ saved to a shared buffer -RSS_BL1_2 -> RSS_BL2: Pass execution -deactivate RSS_BL1_2 -activate RSS_BL2 #Green -RSS_BL2 -->> RSS_S: Validate, measure, load -RSS_BL2 -->> SCP_BL1: Validate, measure, load -Rnote over RSS_BL2: RSS_S and SCP_BL1\n\ measurements saved\n\ to a shared buffer -RSS_BL2 -> SCP_BL1: Release from reset -activate SCP_BL1 #Green -Rnote over RSS_BL2, SCP_BL1: MHU init between RSS and SCP -Rnote over SCP_BL1: Configure memory -Rnote over RSS_BL2: Waits for SCP -SCP_BL1 --> RSS_BL2: Done -RSS_BL2 -->> AP_BL1: Validate, measure, load -Rnote over RSS_BL2: AP_BL1 measurement\n\ saved to a shared buffer -RSS_BL2 -> AP_BL1: Release from reset -activate AP_BL1 #Green -RSS_BL2 -> RSS_S: Pass execution -deactivate RSS_BL2 -activate RSS_S #Green -Rnote over RSS_S: Measurements read from\n\ shared buffer and saved by\n\ -Measured Boot service to\n\ measurement slots. - -== RSS Runtime / AP Boot phase == -Rnote over RSS_S, AP_BL1: MHU init between RSS and AP -Rnote over AP_BL1: Measure and load:\n\ FW_CONFIG\n\ TB_FW_CONFIG -AP_BL1 -> RSS_S: Extend measurement -Rnote over RSS_S: Measured Boot:\n\ store measurement -AP_BL1 -->> AP_BL2: Validate, measure,load -AP_BL1 -> RSS_S: Extend measurement -Rnote over RSS_S: Measured Boot:\n\ store measurement -AP_BL1 -> AP_BL2: Pass execution -deactivate AP_BL1 -activate AP_BL2 #Green -Rnote over AP_BL2: Measure and load:\n\ HW_CONFIG -AP_BL2 -> RSS_S: Extend measurement -Rnote over RSS_S: Measured Boot:\n\ store measurement -AP_BL2 -->> AP_BL31: Validate, measure,load -Rnote over AP_BL2: Measure and load:\n\ BL31 -AP_BL2 -> RSS_S: Extend measurement -Rnote over RSS_S: Measured Boot:\n\ store measurement -Rnote over AP_BL2: Measure and load:\n\ RMM -AP_BL2 -> RSS_S: Extend measurement -Rnote over RSS_S: Measured Boot:\n\ store measurement -AP_BL2 -> AP_BL31: Pass execution -deactivate AP_BL2 -activate AP_BL31 #Green -== RSS / AP Runtime == -@enduml diff --git a/docs/resources/diagrams/plantuml/tfa_rss_dfd.puml b/docs/resources/diagrams/plantuml/tfa_rse_dfd.puml similarity index 89% rename from docs/resources/diagrams/plantuml/tfa_rss_dfd.puml rename to docs/resources/diagrams/plantuml/tfa_rse_dfd.puml index a7e0ce57..68a80bfd 100644 --- a/docs/resources/diagrams/plantuml/tfa_rss_dfd.puml +++ b/docs/resources/diagrams/plantuml/tfa_rse_dfd.puml @@ -5,7 +5,7 @@ '/ /' -TF-A Data Flow Diagram including RSS +TF-A Data Flow Diagram including RSE '/ @startuml @@ -54,12 +54,12 @@ digraph tfa_dfd { bl31 [label="TF-A Runtime\n(BL31)" fillcolor="#ddffb3"] } - # RSS cluster - subgraph cluster_rss{ - label ="RSS"; + # RSE cluster + subgraph cluster_rse{ + label ="RSE"; graph [style=filled color="#000000" fillcolor="#faf9cd"] - rss [label="Runtime Security\n\ Subsystem\n\ (RSS)" fillcolor="#ddffb3"] + rse [label="Runtime Security\n\ Subsystem\n\ (RSE)" fillcolor="#ddffb3"] } } @@ -70,7 +70,7 @@ digraph tfa_dfd { sec -> bl2 [dir="both" lhead=cluster_tfa label="DF4"] nsec -> bl1 [dir="both" lhead=cluster_tfa, label="DF5"] bl2 -> tzc [dir="both" ltail=cluster_tfa lhead=cluster_ip label="DF6" minlen=1] - bl31 -> rss [dir="both" ltail=cluster_tfa lhead=cluster_rss label="DF7" minlen=1] + bl31 -> rse [dir="both" ltail=cluster_tfa lhead=cluster_rse label="DF7" minlen=1] } diff --git a/docs/resources/diagrams/root_context_sequence.png b/docs/resources/diagrams/root_context_sequence.png new file mode 100644 index 0000000000000000000000000000000000000000..67783f138cc64f799e09450ef372f8a6d40b14ef GIT binary patch literal 210925 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 Qs6m{0&=U6Xw6QT@`<DGe6 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	h-%~z+{ywpc-*Mw1I-7}E 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%0A9!z3SGB7*@5`<SxY=<xu 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 literal 0 HcmV?d00001 diff --git a/docs/resources/diagrams/rss_attestation_flow.svg b/docs/resources/diagrams/rse_attestation_flow.svg similarity index 99% rename from docs/resources/diagrams/rss_attestation_flow.svg rename to docs/resources/diagrams/rse_attestation_flow.svg index 3728c6fb..7257576c 100644 --- a/docs/resources/diagrams/rss_attestation_flow.svg +++ b/docs/resources/diagrams/rse_attestation_flow.svg @@ -1,4 +1,4 @@ -<?xml version="1.0" encoding="UTF-8" standalone="no"?><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" contentStyleType="text/css" height="1087px" preserveAspectRatio="none" style="width:900px;height:1087px;background:#FFFFFF;" version="1.1" viewBox="0 0 900 1087" width="900px" zoomAndPan="magnify"><defs/><g><rect fill="#DDDDDD" height="1075.1719" style="stroke:#181818;stroke-width:0.5;" width="261.5" x="44" y="6"/><text fill="#000000" font-family="sans-serif" font-size="13" font-weight="bold" lengthAdjust="spacing" textLength="20" x="164.75" y="18.0669">AP</text><rect fill="#DDDDDD" height="1075.1719" style="stroke:#181818;stroke-width:0.5;" width="502" x="364" y="6"/><text fill="#000000" font-family="sans-serif" font-size="13" font-weight="bold" lengthAdjust="spacing" textLength="28" x="601" y="18.0669">RSS</text><line style="stroke:#181818;stroke-width:0.5;stroke-dasharray:5.0,5.0;" x1="82" x2="82" y1="56.4297" y2="1046.875"/><line style="stroke:#181818;stroke-width:0.5;stroke-dasharray:5.0,5.0;" x1="266.5" x2="266.5" y1="56.4297" y2="1046.875"/><line style="stroke:#181818;stroke-width:0.5;stroke-dasharray:5.0,5.0;" x1="426" x2="426" y1="56.4297" y2="1046.875"/><line style="stroke:#181818;stroke-width:0.5;stroke-dasharray:5.0,5.0;" x1="553.5" x2="553.5" y1="56.4297" y2="1046.875"/><line style="stroke:#181818;stroke-width:0.5;stroke-dasharray:5.0,5.0;" x1="705" x2="705" y1="56.4297" y2="1046.875"/><line style="stroke:#181818;stroke-width:0.5;stroke-dasharray:5.0,5.0;" x1="822" x2="822" y1="56.4297" y2="1046.875"/><rect fill="#E2E2F0" height="30.2969" rx="2.5" ry="2.5" style="stroke:#181818;stroke-width:0.5;" width="48" x="58" y="25.1328"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="34" x="65" y="45.1279">RMM</text><rect fill="#E2E2F0" height="30.2969" rx="2.5" ry="2.5" style="stroke:#181818;stroke-width:0.5;" width="48" x="58" y="1045.875"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="34" x="65" y="1065.8701">RMM</text><rect fill="#E2E2F0" height="30.2969" rx="2.5" ry="2.5" style="stroke:#181818;stroke-width:0.5;" width="49" x="242.5" y="25.1328"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="35" x="249.5" y="45.1279">BL31</text><rect fill="#E2E2F0" height="30.2969" rx="2.5" ry="2.5" style="stroke:#181818;stroke-width:0.5;" width="49" x="242.5" y="1045.875"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="35" x="249.5" y="1065.8701">BL31</text><rect fill="#E2E2F0" height="30.2969" rx="2.5" ry="2.5" style="stroke:#181818;stroke-width:0.5;" width="96" x="378" y="25.1328"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="82" x="385" y="45.1279">DelegAttest</text><rect fill="#E2E2F0" height="30.2969" rx="2.5" ry="2.5" style="stroke:#181818;stroke-width:0.5;" width="96" x="378" y="1045.875"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="82" x="385" y="1065.8701">DelegAttest</text><rect fill="#E2E2F0" height="30.2969" rx="2.5" ry="2.5" style="stroke:#181818;stroke-width:0.5;" width="75" x="516.5" y="25.1328"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="61" x="523.5" y="45.1279">InitAttest</text><rect fill="#E2E2F0" height="30.2969" rx="2.5" ry="2.5" style="stroke:#181818;stroke-width:0.5;" width="75" x="516.5" y="1045.875"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="61" x="523.5" y="1065.8701">InitAttest</text><rect fill="#E2E2F0" height="30.2969" rx="2.5" ry="2.5" style="stroke:#181818;stroke-width:0.5;" width="116" x="647" y="25.1328"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="102" x="654" y="45.1279">MeasuredBoot</text><rect fill="#E2E2F0" height="30.2969" rx="2.5" ry="2.5" style="stroke:#181818;stroke-width:0.5;" width="116" x="647" y="1045.875"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="102" x="654" y="1065.8701">MeasuredBoot</text><rect fill="#E2E2F0" height="30.2969" rx="2.5" ry="2.5" style="stroke:#181818;stroke-width:0.5;" width="59" x="793" y="25.1328"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="45" x="800" y="45.1279">Crypto</text><rect fill="#E2E2F0" height="30.2969" rx="2.5" ry="2.5" style="stroke:#181818;stroke-width:0.5;" width="59" x="793" y="1045.875"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="45" x="800" y="1065.8701">Crypto</text><rect fill="#EEEEEE" height="3" style="stroke:#EEEEEE;stroke-width:1.0;" width="893" x="0" y="86.9961"/><line style="stroke:#000000;stroke-width:1.0;" x1="0" x2="893" y1="86.9961" y2="86.9961"/><line style="stroke:#000000;stroke-width:1.0;" x1="0" x2="893" y1="89.9961" y2="89.9961"/><rect fill="#EEEEEE" height="23.1328" style="stroke:#000000;stroke-width:2.0;" width="144" x="374.5" y="76.4297"/><text fill="#000000" font-family="sans-serif" font-size="13" font-weight="bold" lengthAdjust="spacing" textLength="125" x="380.5" y="92.4966">RMM Boot phase</text><polygon fill="#181818" points="255,141.8281,265,145.8281,255,149.8281,259,145.8281" style="stroke:#181818;stroke-width:1.0;"/><line style="stroke:#181818;stroke-width:1.0;" x1="82" x2="261" y1="145.8281" y2="145.8281"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="99" x="89" y="125.6294">get_realm_key(</text><text fill="#000000" font-family="sans-serif" font-size="13" font-weight="bold" lengthAdjust="spacing" textLength="74" x="121" y="140.7622">hash_algo</text><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="25" x="195" y="140.7622">, ...)</text><polygon fill="#181818" points="414,170.9609,424,174.9609,414,178.9609,418,174.9609" style="stroke:#181818;stroke-width:1.0;"/><line style="stroke:#181818;stroke-width:1.0;" x1="267" x2="420" y1="174.9609" y2="174.9609"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="121" x="274" y="169.895">get_delegated_key</text><polygon fill="#181818" points="693,200.0938,703,204.0938,693,208.0938,697,204.0938" style="stroke:#181818;stroke-width:1.0;"/><line style="stroke:#181818;stroke-width:1.0;" x1="426" x2="699" y1="204.0938" y2="204.0938"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="127" x="433" y="199.0278">read_measurement</text><rect fill="#FEFFDD" height="53" style="stroke:#181818;stroke-width:0.5;" width="167" x="342" y="217.0938"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="95" x="346" y="233.1606">Compute input</text><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="110" x="346" y="248.2935">for key derivation</text><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="159" x="346" y="263.4263">(hash of measurements)</text><polygon fill="#181818" points="810.5,292.625,820.5,296.625,810.5,300.625,814.5,296.625" style="stroke:#181818;stroke-width:1.0;"/><line style="stroke:#181818;stroke-width:1.0;" x1="426" x2="816.5" y1="296.625" y2="296.625"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="68" x="433" y="291.5591">derive_key</text><rect fill="#FEFFDD" height="38" style="stroke:#181818;stroke-width:0.5;" width="150" x="351" y="309.625"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="126" x="355" y="325.6919">Compute public key</text><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="60" x="355" y="340.8247">hash with</text><text fill="#000000" font-family="sans-serif" font-size="13" font-weight="bold" lengthAdjust="spacing" textLength="74" x="419" y="340.8247">hash_algo</text><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="4" x="493" y="340.8247">.</text><rect fill="#FEFFDD" height="38" style="stroke:#181818;stroke-width:0.5;" width="132" x="756" y="357.8906"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="124" x="760" y="373.9575">Seed is provisioned</text><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="88" x="760" y="389.0903">in the factory.</text><polygon fill="#181818" points="278,418.2891,268,422.2891,278,426.2891,274,422.2891" style="stroke:#181818;stroke-width:1.0;"/><line style="stroke:#181818;stroke-width:1.0;stroke-dasharray:2.0,2.0;" x1="272" x2="425" y1="422.2891" y2="422.2891"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="121" x="284" y="417.2231">get_delegated_key</text><polygon fill="#181818" points="93,447.4219,83,451.4219,93,455.4219,89,451.4219" style="stroke:#181818;stroke-width:1.0;"/><line style="stroke:#181818;stroke-width:1.0;stroke-dasharray:2.0,2.0;" x1="87" x2="266" y1="451.4219" y2="451.4219"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="94" x="99" y="446.356">get_realm_key</text><rect fill="#FEFFDD" height="129" style="stroke:#181818;stroke-width:0.5;" width="154" x="5" y="464.4219"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="102" x="9" y="480.4888">Only private key</text><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="114" x="9" y="495.6216">is returned. Public</text><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="104" x="9" y="510.7544">key and its hash</text><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="126" x="9" y="525.8872">must be computed.</text><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="134" x="9" y="541.02">Public key is included</text><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="121" x="9" y="556.1528">in the realm token.</text><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="125" x="9" y="571.2856">Its hash is the input</text><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="146" x="9" y="586.4185">for get_platform_token</text><polygon fill="#181818" points="255,630.75,265,634.75,255,638.75,259,634.75" style="stroke:#181818;stroke-width:1.0;"/><line style="stroke:#181818;stroke-width:1.0;" x1="82" x2="261" y1="634.75" y2="634.75"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="130" x="89" y="614.5513">get_platform_token(</text><text fill="#000000" font-family="sans-serif" font-size="13" font-weight="bold" lengthAdjust="spacing" textLength="104" x="121" y="629.6841">pub_key_hash</text><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="25" x="225" y="629.6841">, ...)</text><polygon fill="#181818" points="414,659.8828,424,663.8828,414,667.8828,418,663.8828" style="stroke:#181818;stroke-width:1.0;"/><line style="stroke:#181818;stroke-width:1.0;" x1="267" x2="420" y1="663.8828" y2="663.8828"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="135" x="274" y="658.8169">get_delegated_token</text><rect fill="#FEFFDD" height="38" style="stroke:#181818;stroke-width:0.5;" width="155" x="348" y="676.8828"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="39" x="352" y="692.9497">Check</text><text fill="#000000" font-family="sans-serif" font-size="13" font-weight="bold" lengthAdjust="spacing" textLength="104" x="395" y="692.9497">pub_key_hash</text><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="128" x="352" y="708.0825">against derived key.</text><polygon fill="#181818" points="542,737.2813,552,741.2813,542,745.2813,546,741.2813" style="stroke:#181818;stroke-width:1.0;"/><line style="stroke:#181818;stroke-width:1.0;" x1="426" x2="548" y1="741.2813" y2="741.2813"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="104" x="433" y="736.2153">get_initial_token</text><rect fill="#FEFFDD" height="53" style="stroke:#181818;stroke-width:0.5;" width="181" x="463" y="754.2813"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="168" x="467" y="770.3481">Create the token including</text><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="21" x="467" y="785.481">the</text><text fill="#000000" font-family="sans-serif" font-size="13" font-weight="bold" lengthAdjust="spacing" textLength="104" x="492" y="785.481">pub_key_hash</text><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="40" x="600" y="785.481">as the</text><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="99" x="467" y="800.6138">challenge claim</text><polygon fill="#181818" points="693,829.8125,703,833.8125,693,837.8125,697,833.8125" style="stroke:#181818;stroke-width:1.0;"/><line style="stroke:#181818;stroke-width:1.0;" x1="554" x2="699" y1="833.8125" y2="833.8125"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="127" x="561" y="828.7466">read_measurement</text><polygon fill="#181818" points="810.5,858.9453,820.5,862.9453,810.5,866.9453,814.5,862.9453" style="stroke:#181818;stroke-width:1.0;"/><line style="stroke:#181818;stroke-width:1.0;" x1="554" x2="816.5" y1="862.9453" y2="862.9453"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="69" x="561" y="857.8794">sign_token</text><polygon fill="#181818" points="437,888.0781,427,892.0781,437,896.0781,433,892.0781" style="stroke:#181818;stroke-width:1.0;"/><line style="stroke:#181818;stroke-width:1.0;stroke-dasharray:2.0,2.0;" x1="431" x2="553" y1="892.0781" y2="892.0781"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="104" x="443" y="887.0122">get_initial_token</text><polygon fill="#181818" points="278,917.2109,268,921.2109,278,925.2109,274,921.2109" style="stroke:#181818;stroke-width:1.0;"/><line style="stroke:#181818;stroke-width:1.0;stroke-dasharray:2.0,2.0;" x1="272" x2="425" y1="921.2109" y2="921.2109"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="135" x="284" y="916.145">get_delegated_token</text><polygon fill="#181818" points="93,946.3438,83,950.3438,93,954.3438,89,950.3438" style="stroke:#181818;stroke-width:1.0;"/><line style="stroke:#181818;stroke-width:1.0;stroke-dasharray:2.0,2.0;" x1="87" x2="266" y1="950.3438" y2="950.3438"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="125" x="99" y="945.2778">get_platform_token</text><rect fill="#FEFFDD" height="68" style="stroke:#181818;stroke-width:0.5;" width="116" x="24" y="963.3438"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="108" x="28" y="979.4106">Platform token is</text><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="101" x="28" y="994.5435">cached. It is not</text><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="98" x="28" y="1009.6763">changing within</text><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="90" x="28" y="1024.8091">a power cycle.</text><!--MD5=[84fabec568a656165bea957fac178b53] +<?xml version="1.0" encoding="UTF-8" standalone="no"?><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" contentStyleType="text/css" height="1087px" preserveAspectRatio="none" style="width:900px;height:1087px;background:#FFFFFF;" version="1.1" viewBox="0 0 900 1087" width="900px" zoomAndPan="magnify"><defs/><g><rect fill="#DDDDDD" height="1075.1719" style="stroke:#181818;stroke-width:0.5;" width="261.5" x="44" y="6"/><text fill="#000000" font-family="sans-serif" font-size="13" font-weight="bold" lengthAdjust="spacing" textLength="20" x="164.75" y="18.0669">AP</text><rect fill="#DDDDDD" height="1075.1719" style="stroke:#181818;stroke-width:0.5;" width="502" x="364" y="6"/><text fill="#000000" font-family="sans-serif" font-size="13" font-weight="bold" lengthAdjust="spacing" textLength="28" x="601" y="18.0669">RSE</text><line style="stroke:#181818;stroke-width:0.5;stroke-dasharray:5.0,5.0;" x1="82" x2="82" y1="56.4297" y2="1046.875"/><line style="stroke:#181818;stroke-width:0.5;stroke-dasharray:5.0,5.0;" x1="266.5" x2="266.5" y1="56.4297" y2="1046.875"/><line style="stroke:#181818;stroke-width:0.5;stroke-dasharray:5.0,5.0;" x1="426" x2="426" y1="56.4297" y2="1046.875"/><line style="stroke:#181818;stroke-width:0.5;stroke-dasharray:5.0,5.0;" x1="553.5" x2="553.5" y1="56.4297" y2="1046.875"/><line style="stroke:#181818;stroke-width:0.5;stroke-dasharray:5.0,5.0;" x1="705" x2="705" y1="56.4297" y2="1046.875"/><line style="stroke:#181818;stroke-width:0.5;stroke-dasharray:5.0,5.0;" x1="822" x2="822" y1="56.4297" y2="1046.875"/><rect fill="#E2E2F0" height="30.2969" rx="2.5" ry="2.5" style="stroke:#181818;stroke-width:0.5;" width="48" x="58" y="25.1328"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="34" x="65" y="45.1279">RMM</text><rect fill="#E2E2F0" height="30.2969" rx="2.5" ry="2.5" style="stroke:#181818;stroke-width:0.5;" width="48" x="58" y="1045.875"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="34" x="65" y="1065.8701">RMM</text><rect fill="#E2E2F0" height="30.2969" rx="2.5" ry="2.5" style="stroke:#181818;stroke-width:0.5;" width="49" x="242.5" y="25.1328"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="35" x="249.5" y="45.1279">BL31</text><rect fill="#E2E2F0" height="30.2969" rx="2.5" ry="2.5" style="stroke:#181818;stroke-width:0.5;" width="49" x="242.5" y="1045.875"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="35" x="249.5" y="1065.8701">BL31</text><rect fill="#E2E2F0" height="30.2969" rx="2.5" ry="2.5" style="stroke:#181818;stroke-width:0.5;" width="96" x="378" y="25.1328"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="82" x="385" y="45.1279">DelegAttest</text><rect fill="#E2E2F0" height="30.2969" rx="2.5" ry="2.5" style="stroke:#181818;stroke-width:0.5;" width="96" x="378" y="1045.875"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="82" x="385" y="1065.8701">DelegAttest</text><rect fill="#E2E2F0" height="30.2969" rx="2.5" ry="2.5" style="stroke:#181818;stroke-width:0.5;" width="75" x="516.5" y="25.1328"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="61" x="523.5" y="45.1279">InitAttest</text><rect fill="#E2E2F0" height="30.2969" rx="2.5" ry="2.5" style="stroke:#181818;stroke-width:0.5;" width="75" x="516.5" y="1045.875"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="61" x="523.5" y="1065.8701">InitAttest</text><rect fill="#E2E2F0" height="30.2969" rx="2.5" ry="2.5" style="stroke:#181818;stroke-width:0.5;" width="116" x="647" y="25.1328"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="102" x="654" y="45.1279">MeasuredBoot</text><rect fill="#E2E2F0" height="30.2969" rx="2.5" ry="2.5" style="stroke:#181818;stroke-width:0.5;" width="116" x="647" y="1045.875"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="102" x="654" y="1065.8701">MeasuredBoot</text><rect fill="#E2E2F0" height="30.2969" rx="2.5" ry="2.5" style="stroke:#181818;stroke-width:0.5;" width="59" x="793" y="25.1328"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="45" x="800" y="45.1279">Crypto</text><rect fill="#E2E2F0" height="30.2969" rx="2.5" ry="2.5" style="stroke:#181818;stroke-width:0.5;" width="59" x="793" y="1045.875"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="45" x="800" y="1065.8701">Crypto</text><rect fill="#EEEEEE" height="3" style="stroke:#EEEEEE;stroke-width:1.0;" width="893" x="0" y="86.9961"/><line style="stroke:#000000;stroke-width:1.0;" x1="0" x2="893" y1="86.9961" y2="86.9961"/><line style="stroke:#000000;stroke-width:1.0;" x1="0" x2="893" y1="89.9961" y2="89.9961"/><rect fill="#EEEEEE" height="23.1328" style="stroke:#000000;stroke-width:2.0;" width="144" x="374.5" y="76.4297"/><text fill="#000000" font-family="sans-serif" font-size="13" font-weight="bold" lengthAdjust="spacing" textLength="125" x="380.5" y="92.4966">RMM Boot phase</text><polygon fill="#181818" points="255,141.8281,265,145.8281,255,149.8281,259,145.8281" style="stroke:#181818;stroke-width:1.0;"/><line style="stroke:#181818;stroke-width:1.0;" x1="82" x2="261" y1="145.8281" y2="145.8281"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="99" x="89" y="125.6294">get_realm_key(</text><text fill="#000000" font-family="sans-serif" font-size="13" font-weight="bold" lengthAdjust="spacing" textLength="74" x="121" y="140.7622">hash_algo</text><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="25" x="195" y="140.7622">, ...)</text><polygon fill="#181818" points="414,170.9609,424,174.9609,414,178.9609,418,174.9609" style="stroke:#181818;stroke-width:1.0;"/><line style="stroke:#181818;stroke-width:1.0;" x1="267" x2="420" y1="174.9609" y2="174.9609"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="121" x="274" y="169.895">get_delegated_key</text><polygon fill="#181818" points="693,200.0938,703,204.0938,693,208.0938,697,204.0938" style="stroke:#181818;stroke-width:1.0;"/><line style="stroke:#181818;stroke-width:1.0;" x1="426" x2="699" y1="204.0938" y2="204.0938"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="127" x="433" y="199.0278">read_measurement</text><rect fill="#FEFFDD" height="53" style="stroke:#181818;stroke-width:0.5;" width="167" x="342" y="217.0938"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="95" x="346" y="233.1606">Compute input</text><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="110" x="346" y="248.2935">for key derivation</text><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="159" x="346" y="263.4263">(hash of measurements)</text><polygon fill="#181818" points="810.5,292.625,820.5,296.625,810.5,300.625,814.5,296.625" style="stroke:#181818;stroke-width:1.0;"/><line style="stroke:#181818;stroke-width:1.0;" x1="426" x2="816.5" y1="296.625" y2="296.625"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="68" x="433" y="291.5591">derive_key</text><rect fill="#FEFFDD" height="38" style="stroke:#181818;stroke-width:0.5;" width="150" x="351" y="309.625"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="126" x="355" y="325.6919">Compute public key</text><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="60" x="355" y="340.8247">hash with</text><text fill="#000000" font-family="sans-serif" font-size="13" font-weight="bold" lengthAdjust="spacing" textLength="74" x="419" y="340.8247">hash_algo</text><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="4" x="493" y="340.8247">.</text><rect fill="#FEFFDD" height="38" style="stroke:#181818;stroke-width:0.5;" width="132" x="756" y="357.8906"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="124" x="760" y="373.9575">Seed is provisioned</text><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="88" x="760" y="389.0903">in the factory.</text><polygon fill="#181818" points="278,418.2891,268,422.2891,278,426.2891,274,422.2891" style="stroke:#181818;stroke-width:1.0;"/><line style="stroke:#181818;stroke-width:1.0;stroke-dasharray:2.0,2.0;" x1="272" x2="425" y1="422.2891" y2="422.2891"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="121" x="284" y="417.2231">get_delegated_key</text><polygon fill="#181818" points="93,447.4219,83,451.4219,93,455.4219,89,451.4219" style="stroke:#181818;stroke-width:1.0;"/><line style="stroke:#181818;stroke-width:1.0;stroke-dasharray:2.0,2.0;" x1="87" x2="266" y1="451.4219" y2="451.4219"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="94" x="99" y="446.356">get_realm_key</text><rect fill="#FEFFDD" height="129" style="stroke:#181818;stroke-width:0.5;" width="154" x="5" y="464.4219"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="102" x="9" y="480.4888">Only private key</text><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="114" x="9" y="495.6216">is returned. Public</text><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="104" x="9" y="510.7544">key and its hash</text><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="126" x="9" y="525.8872">must be computed.</text><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="134" x="9" y="541.02">Public key is included</text><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="121" x="9" y="556.1528">in the realm token.</text><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="125" x="9" y="571.2856">Its hash is the input</text><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="146" x="9" y="586.4185">for get_platform_token</text><polygon fill="#181818" points="255,630.75,265,634.75,255,638.75,259,634.75" style="stroke:#181818;stroke-width:1.0;"/><line style="stroke:#181818;stroke-width:1.0;" x1="82" x2="261" y1="634.75" y2="634.75"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="130" x="89" y="614.5513">get_platform_token(</text><text fill="#000000" font-family="sans-serif" font-size="13" font-weight="bold" lengthAdjust="spacing" textLength="104" x="121" y="629.6841">pub_key_hash</text><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="25" x="225" y="629.6841">, ...)</text><polygon fill="#181818" points="414,659.8828,424,663.8828,414,667.8828,418,663.8828" style="stroke:#181818;stroke-width:1.0;"/><line style="stroke:#181818;stroke-width:1.0;" x1="267" x2="420" y1="663.8828" y2="663.8828"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="135" x="274" y="658.8169">get_delegated_token</text><rect fill="#FEFFDD" height="38" style="stroke:#181818;stroke-width:0.5;" width="155" x="348" y="676.8828"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="39" x="352" y="692.9497">Check</text><text fill="#000000" font-family="sans-serif" font-size="13" font-weight="bold" lengthAdjust="spacing" textLength="104" x="395" y="692.9497">pub_key_hash</text><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="128" x="352" y="708.0825">against derived key.</text><polygon fill="#181818" points="542,737.2813,552,741.2813,542,745.2813,546,741.2813" style="stroke:#181818;stroke-width:1.0;"/><line style="stroke:#181818;stroke-width:1.0;" x1="426" x2="548" y1="741.2813" y2="741.2813"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="104" x="433" y="736.2153">get_initial_token</text><rect fill="#FEFFDD" height="53" style="stroke:#181818;stroke-width:0.5;" width="181" x="463" y="754.2813"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="168" x="467" y="770.3481">Create the token including</text><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="21" x="467" y="785.481">the</text><text fill="#000000" font-family="sans-serif" font-size="13" font-weight="bold" lengthAdjust="spacing" textLength="104" x="492" y="785.481">pub_key_hash</text><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="40" x="600" y="785.481">as the</text><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="99" x="467" y="800.6138">challenge claim</text><polygon fill="#181818" points="693,829.8125,703,833.8125,693,837.8125,697,833.8125" style="stroke:#181818;stroke-width:1.0;"/><line style="stroke:#181818;stroke-width:1.0;" x1="554" x2="699" y1="833.8125" y2="833.8125"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="127" x="561" y="828.7466">read_measurement</text><polygon fill="#181818" points="810.5,858.9453,820.5,862.9453,810.5,866.9453,814.5,862.9453" style="stroke:#181818;stroke-width:1.0;"/><line style="stroke:#181818;stroke-width:1.0;" x1="554" x2="816.5" y1="862.9453" y2="862.9453"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="69" x="561" y="857.8794">sign_token</text><polygon fill="#181818" points="437,888.0781,427,892.0781,437,896.0781,433,892.0781" style="stroke:#181818;stroke-width:1.0;"/><line style="stroke:#181818;stroke-width:1.0;stroke-dasharray:2.0,2.0;" x1="431" x2="553" y1="892.0781" y2="892.0781"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="104" x="443" y="887.0122">get_initial_token</text><polygon fill="#181818" points="278,917.2109,268,921.2109,278,925.2109,274,921.2109" style="stroke:#181818;stroke-width:1.0;"/><line style="stroke:#181818;stroke-width:1.0;stroke-dasharray:2.0,2.0;" x1="272" x2="425" y1="921.2109" y2="921.2109"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="135" x="284" y="916.145">get_delegated_token</text><polygon fill="#181818" points="93,946.3438,83,950.3438,93,954.3438,89,950.3438" style="stroke:#181818;stroke-width:1.0;"/><line style="stroke:#181818;stroke-width:1.0;stroke-dasharray:2.0,2.0;" x1="87" x2="266" y1="950.3438" y2="950.3438"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="125" x="99" y="945.2778">get_platform_token</text><rect fill="#FEFFDD" height="68" style="stroke:#181818;stroke-width:0.5;" width="116" x="24" y="963.3438"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="108" x="28" y="979.4106">Platform token is</text><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="101" x="28" y="994.5435">cached. It is not</text><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="98" x="28" y="1009.6763">changing within</text><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="90" x="28" y="1024.8091">a power cycle.</text><!--MD5=[84fabec568a656165bea957fac178b53] @startuml skinparam ParticipantPadding 10 skinparam BoxPadding 10 @@ -6,7 +6,7 @@ box AP participant RMM participant BL31 endbox -box RSS +box RSE participant DelegAttest participant InitAttest participant MeasuredBoot diff --git a/docs/resources/diagrams/rss_measured_boot_flow.svg b/docs/resources/diagrams/rse_measured_boot_flow.svg similarity index 90% rename from docs/resources/diagrams/rss_measured_boot_flow.svg rename to docs/resources/diagrams/rse_measured_boot_flow.svg index f5bf3113..0ccfbc2d 100644 --- a/docs/resources/diagrams/rss_measured_boot_flow.svg +++ b/docs/resources/diagrams/rse_measured_boot_flow.svg @@ -1,12 +1,12 @@ -<?xml version="1.0" encoding="UTF-8" standalone="no"?><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" contentStyleType="text/css" height="1826px" preserveAspectRatio="none" style="width:1254px;height:1826px;background:#FFFFFF;" version="1.1" viewBox="0 0 1254 1826" width="1254px" zoomAndPan="magnify"><defs/><g><rect fill="#DDDDDD" height="1814.0938" style="stroke:#181818;stroke-width:0.5;" width="610.5" x="27" y="6"/><text fill="#000000" font-family="sans-serif" font-size="13" font-weight="bold" lengthAdjust="spacing" textLength="28" x="318.25" y="18.0669">RSS</text><rect fill="#DDDDDD" height="1814.0938" style="stroke:#181818;stroke-width:0.5;" width="103" x="659.5" y="6"/><text fill="#000000" font-family="sans-serif" font-size="13" font-weight="bold" lengthAdjust="spacing" textLength="29" x="696.5" y="18.0669">SCP</text><rect fill="#DDDDDD" height="1814.0938" style="stroke:#181818;stroke-width:0.5;" width="451.5" x="784.5" y="6"/><text fill="#000000" font-family="sans-serif" font-size="13" font-weight="bold" lengthAdjust="spacing" textLength="20" x="1000.25" y="18.0669">AP</text><rect fill="#008000" height="205.9297" style="stroke:#181818;stroke-width:1.0;" width="10" x="81.5" y="130.6953"/><rect fill="#008000" height="106.5313" style="stroke:#181818;stroke-width:1.0;" width="10" x="227.5" y="336.625"/><rect fill="#008000" height="414.9922" style="stroke:#181818;stroke-width:1.0;" width="10" x="408.5" y="443.1563"/><rect fill="#008000" height="918.6484" style="stroke:#181818;stroke-width:1.0;" width="10" x="589.5" y="858.1484"/><rect fill="#008000" height="1182.8438" style="stroke:#181818;stroke-width:1.0;" width="10" x="706" y="593.9531"/><rect fill="#008000" height="460.3906" style="stroke:#181818;stroke-width:1.0;" width="10" x="826" y="829.0156"/><rect fill="#008000" height="435.2578" style="stroke:#181818;stroke-width:1.0;" width="10" x="1003" y="1289.4063"/><rect fill="#008000" height="52.1328" style="stroke:#181818;stroke-width:1.0;" width="10" x="1180" y="1724.6641"/><line style="stroke:#181818;stroke-width:0.5;stroke-dasharray:5.0,5.0;" x1="86" x2="86" y1="56.4297" y2="1785.7969"/><line style="stroke:#181818;stroke-width:0.5;stroke-dasharray:5.0,5.0;" x1="232" x2="232" y1="56.4297" y2="1785.7969"/><line style="stroke:#181818;stroke-width:0.5;stroke-dasharray:5.0,5.0;" x1="413" x2="413" y1="56.4297" y2="1785.7969"/><line style="stroke:#181818;stroke-width:0.5;stroke-dasharray:5.0,5.0;" x1="594.5" x2="594.5" y1="56.4297" y2="1785.7969"/><line style="stroke:#181818;stroke-width:0.5;stroke-dasharray:5.0,5.0;" x1="710.5" x2="710.5" y1="56.4297" y2="1785.7969"/><line style="stroke:#181818;stroke-width:0.5;stroke-dasharray:5.0,5.0;" x1="830.5" x2="830.5" y1="56.4297" y2="1785.7969"/><line style="stroke:#181818;stroke-width:0.5;stroke-dasharray:5.0,5.0;" x1="1007.5" x2="1007.5" y1="56.4297" y2="1785.7969"/><line style="stroke:#181818;stroke-width:0.5;stroke-dasharray:5.0,5.0;" x1="1185" x2="1185" y1="56.4297" y2="1785.7969"/><rect fill="#E2E2F0" height="30.2969" rx="2.5" ry="2.5" style="stroke:#181818;stroke-width:0.5;" width="91" x="41" y="25.1328"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="77" x="48" y="45.1279">RSS_BL1_1</text><rect fill="#E2E2F0" height="30.2969" rx="2.5" ry="2.5" style="stroke:#181818;stroke-width:0.5;" width="91" x="41" y="1784.7969"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="77" x="48" y="1804.792">RSS_BL1_1</text><rect fill="#E2E2F0" height="30.2969" rx="2.5" ry="2.5" style="stroke:#181818;stroke-width:0.5;" width="91" x="187" y="25.1328"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="77" x="194" y="45.1279">RSS_BL1_2</text><rect fill="#E2E2F0" height="30.2969" rx="2.5" ry="2.5" style="stroke:#181818;stroke-width:0.5;" width="91" x="187" y="1784.7969"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="77" x="194" y="1804.792">RSS_BL1_2</text><rect fill="#E2E2F0" height="30.2969" rx="2.5" ry="2.5" style="stroke:#181818;stroke-width:0.5;" width="75" x="376" y="25.1328"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="61" x="383" y="45.1279">RSS_BL2</text><rect fill="#E2E2F0" height="30.2969" rx="2.5" ry="2.5" style="stroke:#181818;stroke-width:0.5;" width="75" x="376" y="1784.7969"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="61" x="383" y="1804.792">RSS_BL2</text><rect fill="#E2E2F0" height="30.2969" rx="2.5" ry="2.5" style="stroke:#181818;stroke-width:0.5;" width="58" x="565.5" y="25.1328"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="44" x="572.5" y="45.1279">RSS_S</text><rect fill="#E2E2F0" height="30.2969" rx="2.5" ry="2.5" style="stroke:#181818;stroke-width:0.5;" width="58" x="565.5" y="1784.7969"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="44" x="572.5" y="1804.792">RSS_S</text><rect fill="#E2E2F0" height="30.2969" rx="2.5" ry="2.5" style="stroke:#181818;stroke-width:0.5;" width="75" x="673.5" y="25.1328"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="61" x="680.5" y="45.1279">SCP_BL1</text><rect fill="#E2E2F0" height="30.2969" rx="2.5" ry="2.5" style="stroke:#181818;stroke-width:0.5;" width="75" x="673.5" y="1784.7969"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="61" x="680.5" y="1804.792">SCP_BL1</text><rect fill="#E2E2F0" height="30.2969" rx="2.5" ry="2.5" style="stroke:#181818;stroke-width:0.5;" width="65" x="798.5" y="25.1328"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="51" x="805.5" y="45.1279">AP_BL1</text><rect fill="#E2E2F0" height="30.2969" rx="2.5" ry="2.5" style="stroke:#181818;stroke-width:0.5;" width="65" x="798.5" y="1784.7969"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="51" x="805.5" y="1804.792">AP_BL1</text><rect fill="#E2E2F0" height="30.2969" rx="2.5" ry="2.5" style="stroke:#181818;stroke-width:0.5;" width="65" x="975.5" y="25.1328"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="51" x="982.5" y="45.1279">AP_BL2</text><rect fill="#E2E2F0" height="30.2969" rx="2.5" ry="2.5" style="stroke:#181818;stroke-width:0.5;" width="65" x="975.5" y="1784.7969"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="51" x="982.5" y="1804.792">AP_BL2</text><rect fill="#E2E2F0" height="30.2969" rx="2.5" ry="2.5" style="stroke:#181818;stroke-width:0.5;" width="74" x="1148" y="25.1328"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="60" x="1155" y="45.1279">AP_BL31</text><rect fill="#E2E2F0" height="30.2969" rx="2.5" ry="2.5" style="stroke:#181818;stroke-width:0.5;" width="74" x="1148" y="1784.7969"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="60" x="1155" y="1804.792">AP_BL31</text><rect fill="#008000" height="205.9297" style="stroke:#181818;stroke-width:1.0;" width="10" x="81.5" y="130.6953"/><rect fill="#008000" height="106.5313" style="stroke:#181818;stroke-width:1.0;" width="10" x="227.5" y="336.625"/><rect fill="#008000" height="414.9922" style="stroke:#181818;stroke-width:1.0;" width="10" x="408.5" y="443.1563"/><rect fill="#008000" height="918.6484" style="stroke:#181818;stroke-width:1.0;" width="10" x="589.5" y="858.1484"/><rect fill="#008000" height="1182.8438" style="stroke:#181818;stroke-width:1.0;" width="10" x="706" y="593.9531"/><rect fill="#008000" height="460.3906" style="stroke:#181818;stroke-width:1.0;" width="10" x="826" y="829.0156"/><rect fill="#008000" height="435.2578" style="stroke:#181818;stroke-width:1.0;" width="10" x="1003" y="1289.4063"/><rect fill="#008000" height="52.1328" style="stroke:#181818;stroke-width:1.0;" width="10" x="1180" y="1724.6641"/><rect fill="#EEEEEE" height="3" style="stroke:#EEEEEE;stroke-width:1.0;" width="1247" x="0" y="86.9961"/><line style="stroke:#000000;stroke-width:1.0;" x1="0" x2="1247" y1="86.9961" y2="86.9961"/><line style="stroke:#000000;stroke-width:1.0;" x1="0" x2="1247" y1="89.9961" y2="89.9961"/><rect fill="#EEEEEE" height="23.1328" style="stroke:#000000;stroke-width:2.0;" width="136" x="555.5" y="76.4297"/><text fill="#000000" font-family="sans-serif" font-size="13" font-weight="bold" lengthAdjust="spacing" textLength="117" x="561.5" y="92.4966">RSS Boot phase</text><polygon fill="#181818" points="69.5,126.6953,79.5,130.6953,69.5,134.6953,73.5,130.6953" style="stroke:#181818;stroke-width:1.0;"/><line style="stroke:#181818;stroke-width:1.0;" x1="0" x2="75.5" y1="130.6953" y2="130.6953"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="36" x="7" y="125.6294">Reset</text><rect fill="#FEFFDD" height="23" style="stroke:#181818;stroke-width:0.5;" width="99" x="37" y="143.6953"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="91" x="41" y="159.7622">ROM code, XIP</text><rect fill="#FEFFDD" height="23" style="stroke:#181818;stroke-width:0.5;" width="95" x="185" y="176.8281"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="87" x="189" y="192.895">OTP code, XIP</text><rect fill="#FEFFDD" height="23" style="stroke:#181818;stroke-width:0.5;" width="861" x="368" y="209.9609"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="281" x="658" y="226.0278">Stored in flash, loaded and executed in RAM</text><line style="stroke:#181818;stroke-width:1.0;" x1="230.5" x2="220.5" y1="259.2266" y2="255.2266"/><line style="stroke:#181818;stroke-width:1.0;" x1="230.5" x2="220.5" y1="259.2266" y2="263.2266"/><line style="stroke:#181818;stroke-width:1.0;stroke-dasharray:2.0,2.0;" x1="91.5" x2="231.5" y1="259.2266" y2="259.2266"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="117" x="98.5" y="254.1606">Validate, measure</text><rect fill="#FEFFDD" height="38" style="stroke:#181818;stroke-width:0.5;" width="164" x="5" y="272.2266"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="134" x="9" y="288.2935">BL1_2 measurement</text><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="156" x="9" y="303.4263">saved to a shared buffer</text><polygon fill="#181818" points="215.5,332.625,225.5,336.625,215.5,340.625,219.5,336.625" style="stroke:#181818;stroke-width:1.0;"/><line style="stroke:#181818;stroke-width:1.0;" x1="86.5" x2="221.5" y1="336.625" y2="336.625"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="96" x="93.5" y="331.5591">Pass execution</text><line style="stroke:#181818;stroke-width:1.0;" x1="411.5" x2="401.5" y1="365.7578" y2="361.7578"/><line style="stroke:#181818;stroke-width:1.0;" x1="411.5" x2="401.5" y1="365.7578" y2="369.7578"/><line style="stroke:#181818;stroke-width:1.0;stroke-dasharray:2.0,2.0;" x1="237.5" x2="412.5" y1="365.7578" y2="365.7578"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="152" x="244.5" y="360.6919">Validate, measure, load</text><rect fill="#FEFFDD" height="38" style="stroke:#181818;stroke-width:0.5;" width="164" x="150" y="378.7578"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="152" x="154" y="394.8247">RSS_BL2 measurement</text><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="156" x="154" y="409.9575">saved to a shared buffer</text><polygon fill="#181818" points="396.5,439.1563,406.5,443.1563,396.5,447.1563,400.5,443.1563" style="stroke:#181818;stroke-width:1.0;"/><line style="stroke:#181818;stroke-width:1.0;" x1="232.5" x2="402.5" y1="443.1563" y2="443.1563"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="96" x="239.5" y="438.0903">Pass execution</text><line style="stroke:#181818;stroke-width:1.0;" x1="592.5" x2="582.5" y1="472.2891" y2="468.2891"/><line style="stroke:#181818;stroke-width:1.0;" x1="592.5" x2="582.5" y1="472.2891" y2="476.2891"/><line style="stroke:#181818;stroke-width:1.0;stroke-dasharray:2.0,2.0;" x1="418.5" x2="593.5" y1="472.2891" y2="472.2891"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="152" x="425.5" y="467.2231">Validate, measure, load</text><line style="stroke:#181818;stroke-width:1.0;" x1="709" x2="699" y1="501.4219" y2="497.4219"/><line style="stroke:#181818;stroke-width:1.0;" x1="709" x2="699" y1="501.4219" y2="505.4219"/><line style="stroke:#181818;stroke-width:1.0;stroke-dasharray:2.0,2.0;" x1="418.5" x2="710" y1="501.4219" y2="501.4219"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="152" x="425.5" y="496.356">Validate, measure, load</text><rect fill="#FEFFDD" height="53" style="stroke:#181818;stroke-width:0.5;" width="148" x="339" y="514.4219"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="131" x="343" y="530.4888">RSS_S and SCP_BL1</text><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="140" x="343" y="545.6216">measurements saved</text><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="114" x="343" y="560.7544">to a shared buffer</text><polygon fill="#181818" points="694,589.9531,704,593.9531,694,597.9531,698,593.9531" style="stroke:#181818;stroke-width:1.0;"/><line style="stroke:#181818;stroke-width:1.0;" x1="418.5" x2="700" y1="593.9531" y2="593.9531"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="121" x="425.5" y="588.8872">Release from reset</text><rect fill="#FEFFDD" height="23" style="stroke:#181818;stroke-width:0.5;" width="387" x="368" y="606.9531"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="200" x="461.75" y="623.02">MHU init between RSS and SCP</text><rect fill="#FEFFDD" height="23" style="stroke:#181818;stroke-width:0.5;" width="127" x="647" y="640.0859"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="119" x="651" y="656.1528">Configure memory</text><rect fill="#FEFFDD" height="23" style="stroke:#181818;stroke-width:0.5;" width="93" x="367" y="673.2188"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="85" x="371" y="689.2856">Waits for SCP</text><polygon fill="#181818" points="429.5,718.4844,419.5,722.4844,429.5,726.4844,425.5,722.4844" style="stroke:#181818;stroke-width:1.0;"/><line style="stroke:#181818;stroke-width:1.0;stroke-dasharray:2.0,2.0;" x1="423.5" x2="705" y1="722.4844" y2="722.4844"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="34" x="435.5" y="717.4185">Done</text><line style="stroke:#181818;stroke-width:1.0;" x1="829" x2="819" y1="751.6172" y2="747.6172"/><line style="stroke:#181818;stroke-width:1.0;" x1="829" x2="819" y1="751.6172" y2="755.6172"/><line style="stroke:#181818;stroke-width:1.0;stroke-dasharray:2.0,2.0;" x1="418.5" x2="830" y1="751.6172" y2="751.6172"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="152" x="425.5" y="746.5513">Validate, measure, load</text><rect fill="#FEFFDD" height="38" style="stroke:#181818;stroke-width:0.5;" width="164" x="331" y="764.6172"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="143" x="335" y="780.6841">AP_BL1 measurement</text><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="156" x="335" y="795.8169">saved to a shared buffer</text><polygon fill="#181818" points="814,825.0156,824,829.0156,814,833.0156,818,829.0156" style="stroke:#181818;stroke-width:1.0;"/><line style="stroke:#181818;stroke-width:1.0;" x1="418.5" x2="820" y1="829.0156" y2="829.0156"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="121" x="425.5" y="823.9497">Release from reset</text><polygon fill="#181818" points="577.5,854.1484,587.5,858.1484,577.5,862.1484,581.5,858.1484" style="stroke:#181818;stroke-width:1.0;"/><line style="stroke:#181818;stroke-width:1.0;" x1="413.5" x2="583.5" y1="858.1484" y2="858.1484"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="96" x="420.5" y="853.0825">Pass execution</text><rect fill="#FEFFDD" height="68" style="stroke:#181818;stroke-width:0.5;" width="182" x="503" y="871.1484"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="163" x="507" y="887.2153">Measurements read from</text><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="174" x="507" y="902.3481">shared buffer and saved by</text><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="163" x="507" y="917.481">Measured Boot service to</text><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="129" x="507" y="932.6138">measurement slots.</text><rect fill="#EEEEEE" height="3" style="stroke:#EEEEEE;stroke-width:1.0;" width="1247" x="0" y="965.2461"/><line style="stroke:#000000;stroke-width:1.0;" x1="0" x2="1247" y1="965.2461" y2="965.2461"/><line style="stroke:#000000;stroke-width:1.0;" x1="0" x2="1247" y1="968.2461" y2="968.2461"/><rect fill="#EEEEEE" height="23.1328" style="stroke:#000000;stroke-width:2.0;" width="237" x="505" y="954.6797"/><text fill="#000000" font-family="sans-serif" font-size="13" font-weight="bold" lengthAdjust="spacing" textLength="218" x="511" y="970.7466">RSS Runtime / AP Boot phase</text><rect fill="#FEFFDD" height="23" style="stroke:#181818;stroke-width:0.5;" width="313" x="556" y="992.8125"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="191" x="617" y="1008.8794">MHU init between RSS and AP</text><rect fill="#FEFFDD" height="53" style="stroke:#181818;stroke-width:0.5;" width="126" x="768" y="1025.9453"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="118" x="772" y="1042.0122">Measure and load:</text><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="74" x="772" y="1057.145">FW_CONFIG</text><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="97" x="772" y="1072.2778">TB_FW_CONFIG</text><polygon fill="#181818" points="610.5,1101.4766,600.5,1105.4766,610.5,1109.4766,606.5,1105.4766" style="stroke:#181818;stroke-width:1.0;"/><line style="stroke:#181818;stroke-width:1.0;" x1="604.5" x2="825" y1="1105.4766" y2="1105.4766"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="139" x="616.5" y="1100.4106">Extend measurement</text><rect fill="#FEFFDD" height="38" style="stroke:#181818;stroke-width:0.5;" width="136" x="526" y="1118.4766"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="101" x="530" y="1134.5435">Measured Boot:</text><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="128" x="530" y="1149.6763">store measurement</text><line style="stroke:#181818;stroke-width:1.0;" x1="1006" x2="996" y1="1182.875" y2="1178.875"/><line style="stroke:#181818;stroke-width:1.0;" x1="1006" x2="996" y1="1182.875" y2="1186.875"/><line style="stroke:#181818;stroke-width:1.0;stroke-dasharray:2.0,2.0;" x1="836" x2="1007" y1="1182.875" y2="1182.875"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="148" x="843" y="1177.8091">Validate, measure,load</text><polygon fill="#181818" points="610.5,1208.0078,600.5,1212.0078,610.5,1216.0078,606.5,1212.0078" style="stroke:#181818;stroke-width:1.0;"/><line style="stroke:#181818;stroke-width:1.0;" x1="604.5" x2="825" y1="1212.0078" y2="1212.0078"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="139" x="616.5" y="1206.9419">Extend measurement</text><rect fill="#FEFFDD" height="38" style="stroke:#181818;stroke-width:0.5;" width="136" x="526" y="1225.0078"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="101" x="530" y="1241.0747">Measured Boot:</text><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="128" x="530" y="1256.2075">store measurement</text><polygon fill="#181818" points="991,1285.4063,1001,1289.4063,991,1293.4063,995,1289.4063" style="stroke:#181818;stroke-width:1.0;"/><line style="stroke:#181818;stroke-width:1.0;" x1="831" x2="997" y1="1289.4063" y2="1289.4063"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="96" x="838" y="1284.3403">Pass execution</text><rect fill="#FEFFDD" height="38" style="stroke:#181818;stroke-width:0.5;" width="126" x="945" y="1302.4063"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="118" x="949" y="1318.4731">Measure and load:</text><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="77" x="949" y="1333.606">HW_CONFIG</text><polygon fill="#181818" points="610.5,1362.8047,600.5,1366.8047,610.5,1370.8047,606.5,1366.8047" style="stroke:#181818;stroke-width:1.0;"/><line style="stroke:#181818;stroke-width:1.0;" x1="604.5" x2="1002" y1="1366.8047" y2="1366.8047"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="139" x="616.5" y="1361.7388">Extend measurement</text><rect fill="#FEFFDD" height="38" style="stroke:#181818;stroke-width:0.5;" width="136" x="526" y="1379.8047"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="101" x="530" y="1395.8716">Measured Boot:</text><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="128" x="530" y="1411.0044">store measurement</text><line style="stroke:#181818;stroke-width:1.0;" x1="1183" x2="1173" y1="1444.2031" y2="1440.2031"/><line style="stroke:#181818;stroke-width:1.0;" x1="1183" x2="1173" y1="1444.2031" y2="1448.2031"/><line style="stroke:#181818;stroke-width:1.0;stroke-dasharray:2.0,2.0;" x1="1013" x2="1184" y1="1444.2031" y2="1444.2031"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="148" x="1020" y="1439.1372">Validate, measure,load</text><rect fill="#FEFFDD" height="38" style="stroke:#181818;stroke-width:0.5;" width="126" x="945" y="1457.2031"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="118" x="949" y="1473.27">Measure and load:</text><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="32" x="949" y="1488.4028">BL31</text><polygon fill="#181818" points="610.5,1517.6016,600.5,1521.6016,610.5,1525.6016,606.5,1521.6016" style="stroke:#181818;stroke-width:1.0;"/><line style="stroke:#181818;stroke-width:1.0;" x1="604.5" x2="1002" y1="1521.6016" y2="1521.6016"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="139" x="616.5" y="1516.5356">Extend measurement</text><rect fill="#FEFFDD" height="38" style="stroke:#181818;stroke-width:0.5;" width="136" x="526" y="1534.6016"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="101" x="530" y="1550.6685">Measured Boot:</text><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="128" x="530" y="1565.8013">store measurement</text><rect fill="#FEFFDD" height="38" style="stroke:#181818;stroke-width:0.5;" width="126" x="945" y="1582.8672"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="118" x="949" y="1598.9341">Measure and load:</text><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="30" x="949" y="1614.0669">RMM</text><polygon fill="#181818" points="610.5,1643.2656,600.5,1647.2656,610.5,1651.2656,606.5,1647.2656" style="stroke:#181818;stroke-width:1.0;"/><line style="stroke:#181818;stroke-width:1.0;" x1="604.5" x2="1002" y1="1647.2656" y2="1647.2656"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="139" x="616.5" y="1642.1997">Extend measurement</text><rect fill="#FEFFDD" height="38" style="stroke:#181818;stroke-width:0.5;" width="136" x="526" y="1660.2656"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="101" x="530" y="1676.3325">Measured Boot:</text><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="128" x="530" y="1691.4653">store measurement</text><polygon fill="#181818" points="1168,1720.6641,1178,1724.6641,1168,1728.6641,1172,1724.6641" style="stroke:#181818;stroke-width:1.0;"/><line style="stroke:#181818;stroke-width:1.0;" x1="1008" x2="1174" y1="1724.6641" y2="1724.6641"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="96" x="1015" y="1719.5981">Pass execution</text><rect fill="#EEEEEE" height="3" style="stroke:#EEEEEE;stroke-width:1.0;" width="1247" x="0" y="1753.2305"/><line style="stroke:#000000;stroke-width:1.0;" x1="0" x2="1247" y1="1753.2305" y2="1753.2305"/><line style="stroke:#000000;stroke-width:1.0;" x1="0" x2="1247" y1="1756.2305" y2="1756.2305"/><rect fill="#EEEEEE" height="23.1328" style="stroke:#000000;stroke-width:2.0;" width="148" x="549.5" y="1742.6641"/><text fill="#000000" font-family="sans-serif" font-size="13" font-weight="bold" lengthAdjust="spacing" textLength="129" x="555.5" y="1758.731">RSS / AP Runtime</text><!--MD5=[e3f0ee259d2a4aa9c2a97ff856de0312] +<?xml version="1.0" encoding="UTF-8" standalone="no"?><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" contentStyleType="text/css" height="1826px" preserveAspectRatio="none" style="width:1254px;height:1826px;background:#FFFFFF;" version="1.1" viewBox="0 0 1254 1826" width="1254px" zoomAndPan="magnify"><defs/><g><rect fill="#DDDDDD" height="1814.0938" style="stroke:#181818;stroke-width:0.5;" width="610.5" x="27" y="6"/><text fill="#000000" font-family="sans-serif" font-size="13" font-weight="bold" lengthAdjust="spacing" textLength="28" x="318.25" y="18.0669">RSE</text><rect fill="#DDDDDD" height="1814.0938" style="stroke:#181818;stroke-width:0.5;" width="103" x="659.5" y="6"/><text fill="#000000" font-family="sans-serif" font-size="13" font-weight="bold" lengthAdjust="spacing" textLength="29" x="696.5" y="18.0669">SCP</text><rect fill="#DDDDDD" height="1814.0938" style="stroke:#181818;stroke-width:0.5;" width="451.5" x="784.5" y="6"/><text fill="#000000" font-family="sans-serif" font-size="13" font-weight="bold" lengthAdjust="spacing" textLength="20" x="1000.25" y="18.0669">AP</text><rect fill="#008000" height="205.9297" style="stroke:#181818;stroke-width:1.0;" width="10" x="81.5" y="130.6953"/><rect fill="#008000" height="106.5313" style="stroke:#181818;stroke-width:1.0;" width="10" x="227.5" y="336.625"/><rect fill="#008000" height="414.9922" style="stroke:#181818;stroke-width:1.0;" width="10" x="408.5" y="443.1563"/><rect fill="#008000" height="918.6484" style="stroke:#181818;stroke-width:1.0;" width="10" x="589.5" y="858.1484"/><rect fill="#008000" height="1182.8438" style="stroke:#181818;stroke-width:1.0;" width="10" x="706" y="593.9531"/><rect fill="#008000" height="460.3906" style="stroke:#181818;stroke-width:1.0;" width="10" x="826" y="829.0156"/><rect fill="#008000" height="435.2578" style="stroke:#181818;stroke-width:1.0;" width="10" x="1003" y="1289.4063"/><rect fill="#008000" height="52.1328" style="stroke:#181818;stroke-width:1.0;" width="10" x="1180" y="1724.6641"/><line style="stroke:#181818;stroke-width:0.5;stroke-dasharray:5.0,5.0;" x1="86" x2="86" y1="56.4297" y2="1785.7969"/><line style="stroke:#181818;stroke-width:0.5;stroke-dasharray:5.0,5.0;" x1="232" x2="232" y1="56.4297" y2="1785.7969"/><line style="stroke:#181818;stroke-width:0.5;stroke-dasharray:5.0,5.0;" x1="413" x2="413" y1="56.4297" y2="1785.7969"/><line style="stroke:#181818;stroke-width:0.5;stroke-dasharray:5.0,5.0;" x1="594.5" x2="594.5" y1="56.4297" y2="1785.7969"/><line style="stroke:#181818;stroke-width:0.5;stroke-dasharray:5.0,5.0;" x1="710.5" x2="710.5" y1="56.4297" y2="1785.7969"/><line style="stroke:#181818;stroke-width:0.5;stroke-dasharray:5.0,5.0;" x1="830.5" x2="830.5" y1="56.4297" y2="1785.7969"/><line style="stroke:#181818;stroke-width:0.5;stroke-dasharray:5.0,5.0;" x1="1007.5" x2="1007.5" y1="56.4297" y2="1785.7969"/><line style="stroke:#181818;stroke-width:0.5;stroke-dasharray:5.0,5.0;" x1="1185" x2="1185" y1="56.4297" y2="1785.7969"/><rect fill="#E2E2F0" height="30.2969" rx="2.5" ry="2.5" style="stroke:#181818;stroke-width:0.5;" width="91" x="41" y="25.1328"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="77" x="48" y="45.1279">RSE_BL1_1</text><rect fill="#E2E2F0" height="30.2969" rx="2.5" ry="2.5" style="stroke:#181818;stroke-width:0.5;" width="91" x="41" y="1784.7969"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="77" x="48" y="1804.792">RSE_BL1_1</text><rect fill="#E2E2F0" height="30.2969" rx="2.5" ry="2.5" style="stroke:#181818;stroke-width:0.5;" width="91" x="187" y="25.1328"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="77" x="194" y="45.1279">RSE_BL1_2</text><rect fill="#E2E2F0" height="30.2969" rx="2.5" ry="2.5" style="stroke:#181818;stroke-width:0.5;" width="91" x="187" y="1784.7969"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="77" x="194" y="1804.792">RSE_BL1_2</text><rect fill="#E2E2F0" height="30.2969" rx="2.5" ry="2.5" style="stroke:#181818;stroke-width:0.5;" width="75" x="376" y="25.1328"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="61" x="383" y="45.1279">RSE_BL2</text><rect fill="#E2E2F0" height="30.2969" rx="2.5" ry="2.5" style="stroke:#181818;stroke-width:0.5;" width="75" x="376" y="1784.7969"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="61" x="383" y="1804.792">RSE_BL2</text><rect fill="#E2E2F0" height="30.2969" rx="2.5" ry="2.5" style="stroke:#181818;stroke-width:0.5;" width="58" x="565.5" y="25.1328"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="44" x="572.5" y="45.1279">RSE_S</text><rect fill="#E2E2F0" height="30.2969" rx="2.5" ry="2.5" style="stroke:#181818;stroke-width:0.5;" width="58" x="565.5" y="1784.7969"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="44" x="572.5" y="1804.792">RSE_S</text><rect fill="#E2E2F0" height="30.2969" rx="2.5" ry="2.5" style="stroke:#181818;stroke-width:0.5;" width="75" x="673.5" y="25.1328"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="61" x="680.5" y="45.1279">SCP_BL1</text><rect fill="#E2E2F0" height="30.2969" rx="2.5" ry="2.5" style="stroke:#181818;stroke-width:0.5;" width="75" x="673.5" y="1784.7969"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="61" x="680.5" y="1804.792">SCP_BL1</text><rect fill="#E2E2F0" height="30.2969" rx="2.5" ry="2.5" style="stroke:#181818;stroke-width:0.5;" width="65" x="798.5" y="25.1328"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="51" x="805.5" y="45.1279">AP_BL1</text><rect fill="#E2E2F0" height="30.2969" rx="2.5" ry="2.5" style="stroke:#181818;stroke-width:0.5;" width="65" x="798.5" y="1784.7969"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="51" x="805.5" y="1804.792">AP_BL1</text><rect fill="#E2E2F0" height="30.2969" rx="2.5" ry="2.5" style="stroke:#181818;stroke-width:0.5;" width="65" x="975.5" y="25.1328"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="51" x="982.5" y="45.1279">AP_BL2</text><rect fill="#E2E2F0" height="30.2969" rx="2.5" ry="2.5" style="stroke:#181818;stroke-width:0.5;" width="65" x="975.5" y="1784.7969"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="51" x="982.5" y="1804.792">AP_BL2</text><rect fill="#E2E2F0" height="30.2969" rx="2.5" ry="2.5" style="stroke:#181818;stroke-width:0.5;" width="74" x="1148" y="25.1328"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="60" x="1155" y="45.1279">AP_BL31</text><rect fill="#E2E2F0" height="30.2969" rx="2.5" ry="2.5" style="stroke:#181818;stroke-width:0.5;" width="74" x="1148" y="1784.7969"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="60" x="1155" y="1804.792">AP_BL31</text><rect fill="#008000" height="205.9297" style="stroke:#181818;stroke-width:1.0;" width="10" x="81.5" y="130.6953"/><rect fill="#008000" height="106.5313" style="stroke:#181818;stroke-width:1.0;" width="10" x="227.5" y="336.625"/><rect fill="#008000" height="414.9922" style="stroke:#181818;stroke-width:1.0;" width="10" x="408.5" y="443.1563"/><rect fill="#008000" height="918.6484" style="stroke:#181818;stroke-width:1.0;" width="10" x="589.5" y="858.1484"/><rect fill="#008000" height="1182.8438" style="stroke:#181818;stroke-width:1.0;" width="10" x="706" y="593.9531"/><rect fill="#008000" height="460.3906" style="stroke:#181818;stroke-width:1.0;" width="10" x="826" y="829.0156"/><rect fill="#008000" height="435.2578" style="stroke:#181818;stroke-width:1.0;" width="10" x="1003" y="1289.4063"/><rect fill="#008000" height="52.1328" style="stroke:#181818;stroke-width:1.0;" width="10" x="1180" y="1724.6641"/><rect fill="#EEEEEE" height="3" style="stroke:#EEEEEE;stroke-width:1.0;" width="1247" x="0" y="86.9961"/><line style="stroke:#000000;stroke-width:1.0;" x1="0" x2="1247" y1="86.9961" y2="86.9961"/><line style="stroke:#000000;stroke-width:1.0;" x1="0" x2="1247" y1="89.9961" y2="89.9961"/><rect fill="#EEEEEE" height="23.1328" style="stroke:#000000;stroke-width:2.0;" width="136" x="555.5" y="76.4297"/><text fill="#000000" font-family="sans-serif" font-size="13" font-weight="bold" lengthAdjust="spacing" textLength="117" x="561.5" y="92.4966">RSE Boot phase</text><polygon fill="#181818" points="69.5,126.6953,79.5,130.6953,69.5,134.6953,73.5,130.6953" style="stroke:#181818;stroke-width:1.0;"/><line style="stroke:#181818;stroke-width:1.0;" x1="0" x2="75.5" y1="130.6953" y2="130.6953"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="36" x="7" y="125.6294">Reset</text><rect fill="#FEFFDD" height="23" style="stroke:#181818;stroke-width:0.5;" width="99" x="37" y="143.6953"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="91" x="41" y="159.7622">ROM code, XIP</text><rect fill="#FEFFDD" height="23" style="stroke:#181818;stroke-width:0.5;" width="95" x="185" y="176.8281"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="87" x="189" y="192.895">OTP code, XIP</text><rect fill="#FEFFDD" height="23" style="stroke:#181818;stroke-width:0.5;" width="861" x="368" y="209.9609"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="281" x="658" y="226.0278">Stored in flash, loaded and executed in RAM</text><line style="stroke:#181818;stroke-width:1.0;" x1="230.5" x2="220.5" y1="259.2266" y2="255.2266"/><line style="stroke:#181818;stroke-width:1.0;" x1="230.5" x2="220.5" y1="259.2266" y2="263.2266"/><line style="stroke:#181818;stroke-width:1.0;stroke-dasharray:2.0,2.0;" x1="91.5" x2="231.5" y1="259.2266" y2="259.2266"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="117" x="98.5" y="254.1606">Validate, measure</text><rect fill="#FEFFDD" height="38" style="stroke:#181818;stroke-width:0.5;" width="164" x="5" y="272.2266"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="134" x="9" y="288.2935">BL1_2 measurement</text><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="156" x="9" y="303.4263">saved to a shared buffer</text><polygon fill="#181818" points="215.5,332.625,225.5,336.625,215.5,340.625,219.5,336.625" style="stroke:#181818;stroke-width:1.0;"/><line style="stroke:#181818;stroke-width:1.0;" x1="86.5" x2="221.5" y1="336.625" y2="336.625"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="96" x="93.5" y="331.5591">Pass execution</text><line style="stroke:#181818;stroke-width:1.0;" x1="411.5" x2="401.5" y1="365.7578" y2="361.7578"/><line style="stroke:#181818;stroke-width:1.0;" x1="411.5" x2="401.5" y1="365.7578" y2="369.7578"/><line style="stroke:#181818;stroke-width:1.0;stroke-dasharray:2.0,2.0;" x1="237.5" x2="412.5" y1="365.7578" y2="365.7578"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="152" x="244.5" y="360.6919">Validate, measure, load</text><rect fill="#FEFFDD" height="38" style="stroke:#181818;stroke-width:0.5;" width="164" x="150" y="378.7578"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="152" x="154" y="394.8247">RSE_BL2 measurement</text><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="156" x="154" y="409.9575">saved to a shared buffer</text><polygon fill="#181818" points="396.5,439.1563,406.5,443.1563,396.5,447.1563,400.5,443.1563" style="stroke:#181818;stroke-width:1.0;"/><line style="stroke:#181818;stroke-width:1.0;" x1="232.5" x2="402.5" y1="443.1563" y2="443.1563"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="96" x="239.5" y="438.0903">Pass execution</text><line style="stroke:#181818;stroke-width:1.0;" x1="592.5" x2="582.5" y1="472.2891" y2="468.2891"/><line style="stroke:#181818;stroke-width:1.0;" x1="592.5" x2="582.5" y1="472.2891" y2="476.2891"/><line style="stroke:#181818;stroke-width:1.0;stroke-dasharray:2.0,2.0;" x1="418.5" x2="593.5" y1="472.2891" y2="472.2891"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="152" x="425.5" y="467.2231">Validate, measure, load</text><line style="stroke:#181818;stroke-width:1.0;" x1="709" x2="699" y1="501.4219" y2="497.4219"/><line style="stroke:#181818;stroke-width:1.0;" x1="709" x2="699" y1="501.4219" y2="505.4219"/><line style="stroke:#181818;stroke-width:1.0;stroke-dasharray:2.0,2.0;" x1="418.5" x2="710" y1="501.4219" y2="501.4219"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="152" x="425.5" y="496.356">Validate, measure, load</text><rect fill="#FEFFDD" height="53" style="stroke:#181818;stroke-width:0.5;" width="148" x="339" y="514.4219"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="131" x="343" y="530.4888">RSE_S and SCP_BL1</text><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="140" x="343" y="545.6216">measurements saved</text><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="114" x="343" y="560.7544">to a shared buffer</text><polygon fill="#181818" points="694,589.9531,704,593.9531,694,597.9531,698,593.9531" style="stroke:#181818;stroke-width:1.0;"/><line style="stroke:#181818;stroke-width:1.0;" x1="418.5" x2="700" y1="593.9531" y2="593.9531"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="121" x="425.5" y="588.8872">Release from reset</text><rect fill="#FEFFDD" height="23" style="stroke:#181818;stroke-width:0.5;" width="387" x="368" y="606.9531"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="200" x="461.75" y="623.02">MHU init between RSE and SCP</text><rect fill="#FEFFDD" height="23" style="stroke:#181818;stroke-width:0.5;" width="127" x="647" y="640.0859"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="119" x="651" y="656.1528">Configure memory</text><rect fill="#FEFFDD" height="23" style="stroke:#181818;stroke-width:0.5;" width="93" x="367" y="673.2188"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="85" x="371" y="689.2856">Waits for SCP</text><polygon fill="#181818" points="429.5,718.4844,419.5,722.4844,429.5,726.4844,425.5,722.4844" style="stroke:#181818;stroke-width:1.0;"/><line style="stroke:#181818;stroke-width:1.0;stroke-dasharray:2.0,2.0;" x1="423.5" x2="705" y1="722.4844" y2="722.4844"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="34" x="435.5" y="717.4185">Done</text><line style="stroke:#181818;stroke-width:1.0;" x1="829" x2="819" y1="751.6172" y2="747.6172"/><line style="stroke:#181818;stroke-width:1.0;" x1="829" x2="819" y1="751.6172" y2="755.6172"/><line style="stroke:#181818;stroke-width:1.0;stroke-dasharray:2.0,2.0;" x1="418.5" x2="830" y1="751.6172" y2="751.6172"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="152" x="425.5" y="746.5513">Validate, measure, load</text><rect fill="#FEFFDD" height="38" style="stroke:#181818;stroke-width:0.5;" width="164" x="331" y="764.6172"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="143" x="335" y="780.6841">AP_BL1 measurement</text><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="156" x="335" y="795.8169">saved to a shared buffer</text><polygon fill="#181818" points="814,825.0156,824,829.0156,814,833.0156,818,829.0156" style="stroke:#181818;stroke-width:1.0;"/><line style="stroke:#181818;stroke-width:1.0;" x1="418.5" x2="820" y1="829.0156" y2="829.0156"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="121" x="425.5" y="823.9497">Release from reset</text><polygon fill="#181818" points="577.5,854.1484,587.5,858.1484,577.5,862.1484,581.5,858.1484" style="stroke:#181818;stroke-width:1.0;"/><line style="stroke:#181818;stroke-width:1.0;" x1="413.5" x2="583.5" y1="858.1484" y2="858.1484"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="96" x="420.5" y="853.0825">Pass execution</text><rect fill="#FEFFDD" height="68" style="stroke:#181818;stroke-width:0.5;" width="182" x="503" y="871.1484"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="163" x="507" y="887.2153">Measurements read from</text><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="174" x="507" y="902.3481">shared buffer and saved by</text><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="163" x="507" y="917.481">Measured Boot service to</text><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="129" x="507" y="932.6138">measurement slots.</text><rect fill="#EEEEEE" height="3" style="stroke:#EEEEEE;stroke-width:1.0;" width="1247" x="0" y="965.2461"/><line style="stroke:#000000;stroke-width:1.0;" x1="0" x2="1247" y1="965.2461" y2="965.2461"/><line style="stroke:#000000;stroke-width:1.0;" x1="0" x2="1247" y1="968.2461" y2="968.2461"/><rect fill="#EEEEEE" height="23.1328" style="stroke:#000000;stroke-width:2.0;" width="237" x="505" y="954.6797"/><text fill="#000000" font-family="sans-serif" font-size="13" font-weight="bold" lengthAdjust="spacing" textLength="218" x="511" y="970.7466">RSE Runtime / AP Boot phase</text><rect fill="#FEFFDD" height="23" style="stroke:#181818;stroke-width:0.5;" width="313" x="556" y="992.8125"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="191" x="617" y="1008.8794">MHU init between RSE and AP</text><rect fill="#FEFFDD" height="53" style="stroke:#181818;stroke-width:0.5;" width="126" x="768" y="1025.9453"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="118" x="772" y="1042.0122">Measure and load:</text><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="74" x="772" y="1057.145">FW_CONFIG</text><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="97" x="772" y="1072.2778">TB_FW_CONFIG</text><polygon fill="#181818" points="610.5,1101.4766,600.5,1105.4766,610.5,1109.4766,606.5,1105.4766" style="stroke:#181818;stroke-width:1.0;"/><line style="stroke:#181818;stroke-width:1.0;" x1="604.5" x2="825" y1="1105.4766" y2="1105.4766"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="139" x="616.5" y="1100.4106">Extend measurement</text><rect fill="#FEFFDD" height="38" style="stroke:#181818;stroke-width:0.5;" width="136" x="526" y="1118.4766"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="101" x="530" y="1134.5435">Measured Boot:</text><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="128" x="530" y="1149.6763">store measurement</text><line style="stroke:#181818;stroke-width:1.0;" x1="1006" x2="996" y1="1182.875" y2="1178.875"/><line style="stroke:#181818;stroke-width:1.0;" x1="1006" x2="996" y1="1182.875" y2="1186.875"/><line style="stroke:#181818;stroke-width:1.0;stroke-dasharray:2.0,2.0;" x1="836" x2="1007" y1="1182.875" y2="1182.875"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="148" x="843" y="1177.8091">Validate, measure,load</text><polygon fill="#181818" points="610.5,1208.0078,600.5,1212.0078,610.5,1216.0078,606.5,1212.0078" style="stroke:#181818;stroke-width:1.0;"/><line style="stroke:#181818;stroke-width:1.0;" x1="604.5" x2="825" y1="1212.0078" y2="1212.0078"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="139" x="616.5" y="1206.9419">Extend measurement</text><rect fill="#FEFFDD" height="38" style="stroke:#181818;stroke-width:0.5;" width="136" x="526" y="1225.0078"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="101" x="530" y="1241.0747">Measured Boot:</text><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="128" x="530" y="1256.2075">store measurement</text><polygon fill="#181818" points="991,1285.4063,1001,1289.4063,991,1293.4063,995,1289.4063" style="stroke:#181818;stroke-width:1.0;"/><line style="stroke:#181818;stroke-width:1.0;" x1="831" x2="997" y1="1289.4063" y2="1289.4063"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="96" x="838" y="1284.3403">Pass execution</text><rect fill="#FEFFDD" height="38" style="stroke:#181818;stroke-width:0.5;" width="126" x="945" y="1302.4063"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="118" x="949" y="1318.4731">Measure and load:</text><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="77" x="949" y="1333.606">HW_CONFIG</text><polygon fill="#181818" points="610.5,1362.8047,600.5,1366.8047,610.5,1370.8047,606.5,1366.8047" style="stroke:#181818;stroke-width:1.0;"/><line style="stroke:#181818;stroke-width:1.0;" x1="604.5" x2="1002" y1="1366.8047" y2="1366.8047"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="139" x="616.5" y="1361.7388">Extend measurement</text><rect fill="#FEFFDD" height="38" style="stroke:#181818;stroke-width:0.5;" width="136" x="526" y="1379.8047"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="101" x="530" y="1395.8716">Measured Boot:</text><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="128" x="530" y="1411.0044">store measurement</text><line style="stroke:#181818;stroke-width:1.0;" x1="1183" x2="1173" y1="1444.2031" y2="1440.2031"/><line style="stroke:#181818;stroke-width:1.0;" x1="1183" x2="1173" y1="1444.2031" y2="1448.2031"/><line style="stroke:#181818;stroke-width:1.0;stroke-dasharray:2.0,2.0;" x1="1013" x2="1184" y1="1444.2031" y2="1444.2031"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="148" x="1020" y="1439.1372">Validate, measure,load</text><rect fill="#FEFFDD" height="38" style="stroke:#181818;stroke-width:0.5;" width="126" x="945" y="1457.2031"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="118" x="949" y="1473.27">Measure and load:</text><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="32" x="949" y="1488.4028">BL31</text><polygon fill="#181818" points="610.5,1517.6016,600.5,1521.6016,610.5,1525.6016,606.5,1521.6016" style="stroke:#181818;stroke-width:1.0;"/><line style="stroke:#181818;stroke-width:1.0;" x1="604.5" x2="1002" y1="1521.6016" y2="1521.6016"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="139" x="616.5" y="1516.5356">Extend measurement</text><rect fill="#FEFFDD" height="38" style="stroke:#181818;stroke-width:0.5;" width="136" x="526" y="1534.6016"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="101" x="530" y="1550.6685">Measured Boot:</text><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="128" x="530" y="1565.8013">store measurement</text><rect fill="#FEFFDD" height="38" style="stroke:#181818;stroke-width:0.5;" width="126" x="945" y="1582.8672"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="118" x="949" y="1598.9341">Measure and load:</text><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="30" x="949" y="1614.0669">RMM</text><polygon fill="#181818" points="610.5,1643.2656,600.5,1647.2656,610.5,1651.2656,606.5,1647.2656" style="stroke:#181818;stroke-width:1.0;"/><line style="stroke:#181818;stroke-width:1.0;" x1="604.5" x2="1002" y1="1647.2656" y2="1647.2656"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="139" x="616.5" y="1642.1997">Extend measurement</text><rect fill="#FEFFDD" height="38" style="stroke:#181818;stroke-width:0.5;" width="136" x="526" y="1660.2656"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="101" x="530" y="1676.3325">Measured Boot:</text><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="128" x="530" y="1691.4653">store measurement</text><polygon fill="#181818" points="1168,1720.6641,1178,1724.6641,1168,1728.6641,1172,1724.6641" style="stroke:#181818;stroke-width:1.0;"/><line style="stroke:#181818;stroke-width:1.0;" x1="1008" x2="1174" y1="1724.6641" y2="1724.6641"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="96" x="1015" y="1719.5981">Pass execution</text><rect fill="#EEEEEE" height="3" style="stroke:#EEEEEE;stroke-width:1.0;" width="1247" x="0" y="1753.2305"/><line style="stroke:#000000;stroke-width:1.0;" x1="0" x2="1247" y1="1753.2305" y2="1753.2305"/><line style="stroke:#000000;stroke-width:1.0;" x1="0" x2="1247" y1="1756.2305" y2="1756.2305"/><rect fill="#EEEEEE" height="23.1328" style="stroke:#000000;stroke-width:2.0;" width="148" x="549.5" y="1742.6641"/><text fill="#000000" font-family="sans-serif" font-size="13" font-weight="bold" lengthAdjust="spacing" textLength="129" x="555.5" y="1758.731">RSE / AP Runtime</text><!--MD5=[e3f0ee259d2a4aa9c2a97ff856de0312] @startuml skinparam ParticipantPadding 10 skinparam BoxPadding 10 -box RSS -participant RSS_BL1_1 -participant RSS_BL1_2 -participant RSS_BL2 -participant RSS_S +box RSE +participant RSE_BL1_1 +participant RSE_BL1_2 +participant RSE_BL2 +participant RSE_S endbox box SCP participant SCP_BL1 @@ -17,65 +17,65 @@ participant AP_BL2 participant AP_BL31 endbox -== RSS Boot phase == --> RSS_BL1_1: Reset -Rnote over RSS_BL1_1: ROM code, XIP -Rnote over RSS_BL1_2: OTP code, XIP -Rnote over RSS_BL2, AP_BL31: Stored in flash, loaded and executed in RAM -activate RSS_BL1_1 #Green -RSS_BL1_1 - ->> RSS_BL1_2: Validate, measure -Rnote over RSS_BL1_1: BL1_2 measurement\n\ saved to a shared buffer -RSS_BL1_1 -> RSS_BL1_2: Pass execution -deactivate RSS_BL1_1 -activate RSS_BL1_2 #Green -RSS_BL1_2 - ->> RSS_BL2: Validate, measure, load -Rnote over RSS_BL1_2: RSS_BL2 measurement\n\ saved to a shared buffer -RSS_BL1_2 -> RSS_BL2: Pass execution -deactivate RSS_BL1_2 -activate RSS_BL2 #Green -RSS_BL2 - ->> RSS_S: Validate, measure, load -RSS_BL2 - ->> SCP_BL1: Validate, measure, load -Rnote over RSS_BL2: RSS_S and SCP_BL1\n\ measurements saved\n\ to a shared buffer -RSS_BL2 -> SCP_BL1: Release from reset +== RSE Boot phase == +-> RSE_BL1_1: Reset +Rnote over RSE_BL1_1: ROM code, XIP +Rnote over RSE_BL1_2: OTP code, XIP +Rnote over RSE_BL2, AP_BL31: Stored in flash, loaded and executed in RAM +activate RSE_BL1_1 #Green +RSE_BL1_1 - ->> RSE_BL1_2: Validate, measure +Rnote over RSE_BL1_1: BL1_2 measurement\n\ saved to a shared buffer +RSE_BL1_1 -> RSE_BL1_2: Pass execution +deactivate RSE_BL1_1 +activate RSE_BL1_2 #Green +RSE_BL1_2 - ->> RSE_BL2: Validate, measure, load +Rnote over RSE_BL1_2: RSE_BL2 measurement\n\ saved to a shared buffer +RSE_BL1_2 -> RSE_BL2: Pass execution +deactivate RSE_BL1_2 +activate RSE_BL2 #Green +RSE_BL2 - ->> RSE_S: Validate, measure, load +RSE_BL2 - ->> SCP_BL1: Validate, measure, load +Rnote over RSE_BL2: RSE_S and SCP_BL1\n\ measurements saved\n\ to a shared buffer +RSE_BL2 -> SCP_BL1: Release from reset activate SCP_BL1 #Green -Rnote over RSS_BL2, SCP_BL1: MHU init between RSS and SCP +Rnote over RSE_BL2, SCP_BL1: MHU init between RSE and SCP Rnote over SCP_BL1: Configure memory -Rnote over RSS_BL2: Waits for SCP -SCP_BL1 - -> RSS_BL2: Done -RSS_BL2 - ->> AP_BL1: Validate, measure, load -Rnote over RSS_BL2: AP_BL1 measurement\n\ saved to a shared buffer -RSS_BL2 -> AP_BL1: Release from reset +Rnote over RSE_BL2: Waits for SCP +SCP_BL1 - -> RSE_BL2: Done +RSE_BL2 - ->> AP_BL1: Validate, measure, load +Rnote over RSE_BL2: AP_BL1 measurement\n\ saved to a shared buffer +RSE_BL2 -> AP_BL1: Release from reset activate AP_BL1 #Green -RSS_BL2 -> RSS_S: Pass execution -deactivate RSS_BL2 -activate RSS_S #Green -Rnote over RSS_S: Measurements read from\n\ shared buffer and saved by\nMeasured Boot service to\n\ measurement slots. +RSE_BL2 -> RSE_S: Pass execution +deactivate RSE_BL2 +activate RSE_S #Green +Rnote over RSE_S: Measurements read from\n\ shared buffer and saved by\nMeasured Boot service to\n\ measurement slots. -== RSS Runtime / AP Boot phase == -Rnote over RSS_S, AP_BL1: MHU init between RSS and AP +== RSE Runtime / AP Boot phase == +Rnote over RSE_S, AP_BL1: MHU init between RSE and AP Rnote over AP_BL1: Measure and load:\n\ FW_CONFIG\n\ TB_FW_CONFIG -AP_BL1 -> RSS_S: Extend measurement -Rnote over RSS_S: Measured Boot:\n\ store measurement +AP_BL1 -> RSE_S: Extend measurement +Rnote over RSE_S: Measured Boot:\n\ store measurement AP_BL1 - ->> AP_BL2: Validate, measure,load -AP_BL1 -> RSS_S: Extend measurement -Rnote over RSS_S: Measured Boot:\n\ store measurement +AP_BL1 -> RSE_S: Extend measurement +Rnote over RSE_S: Measured Boot:\n\ store measurement AP_BL1 -> AP_BL2: Pass execution deactivate AP_BL1 activate AP_BL2 #Green Rnote over AP_BL2: Measure and load:\n\ HW_CONFIG -AP_BL2 -> RSS_S: Extend measurement -Rnote over RSS_S: Measured Boot:\n\ store measurement +AP_BL2 -> RSE_S: Extend measurement +Rnote over RSE_S: Measured Boot:\n\ store measurement AP_BL2 - ->> AP_BL31: Validate, measure,load Rnote over AP_BL2: Measure and load:\n\ BL31 -AP_BL2 -> RSS_S: Extend measurement -Rnote over RSS_S: Measured Boot:\n\ store measurement +AP_BL2 -> RSE_S: Extend measurement +Rnote over RSE_S: Measured Boot:\n\ store measurement Rnote over AP_BL2: Measure and load:\n\ RMM -AP_BL2 -> RSS_S: Extend measurement -Rnote over RSS_S: Measured Boot:\n\ store measurement +AP_BL2 -> RSE_S: Extend measurement +Rnote over RSE_S: Measured Boot:\n\ store measurement AP_BL2 -> AP_BL31: Pass execution deactivate AP_BL2 activate AP_BL31 #Green -== RSS / AP Runtime == +== RSE / AP Runtime == @enduml PlantUML version 1.2022.7(Mon Aug 22 19:01:30 CEST 2022) diff --git a/docs/resources/diagrams/secure_sw_stack_sp.png b/docs/resources/diagrams/secure_sw_stack_sp.png index 5cb2ca7a2c7b22955691bf1ef5f19b2e7fe14ec1..dc18cb4755c4237133a97546cbde65d14ac1c4e8 100644 GIT binary patch literal 34589 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 B&y@fG literal 34909 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 diff --git a/docs/resources/diagrams/secure_sw_stack_tos.png b/docs/resources/diagrams/secure_sw_stack_tos.png index 1f2d55506a5b4677d89b37159b1a6a00ed759d26..2b42019fdc4db9ad60cd0affd2cd4786f3c5215d 100644 GIT binary patch literal 34126 zcmc$`Wpo@tvL-5KmIW4DXfZQ0Tg=SN%*<p9Eow0{Gn2*4%*@Q&p4t1}oSl2_-rXPX zojjFYT^W@b5gG7RR(40o{}4xn!+`?<0YQ|M5K#mH0qX)D!!S_5D;<W?Oduc>O`gi? z&WZ+ZMD~t$rWV#FM9v=eCPXIg7N#H|?kg1;8m{=9XvUv4k(yu!2AtTF$M|<QuQ)pn zXq1#?wbDt%qGI~a?Cee)faTA}PrTc#&$ppv)sBdgxi!beH~PjU@56M<tj~tmrn>77 zpHKGK`>HJ8=a$s-d-%yXFulIpsF|#)%G=w!>s~EFTkrRllB&(87t5pjs3xc&PgS+k zTcYO{Ki`X;vYf^&JIAv9(Jugpm1oZ@r2Tt68`pLmFW-T#@k7G?UaATa@t#+F?dr$N z7asIGJ%r4vm(^>8UBr+};qM2}(^jN3e^QP+d>S5Z!F)9KnDvxiUG6u#UQMoj{N8!q zYLlNUa^7y~wO>50FVXx?INCdz-=0S4L+{U5opnDm>rOadQ+47}F~;emY7cB&F1#lV z*$tbkOg_GRNPFvDWDRCnwq;sB*Cy~mZ?YdQCmCrwkGi<6b?OzRBQU<4!|Qcq@vUXe z%X{B4n`h;IH1A5A8-$GL{^&x~!V2;ujB0ytT-|g|0;KFJT$)F(zrPdS7U_Mk`;E1P zZe>(dxumV=(~lZruW-JPD_MtgSNTZ01#Et_H*WgY63Qgu%ebXdqn~lS+8B~P8fa}_ zIBm7`FAjxdP@c|%NQ-Pn^fY%PEu9HDE-&x+IK;Y?Tg#j6Jw*_n`AZMeLmeBzET`|3 zscp6^t@$iaWOeu=u+^Ya8o!m)3TKW-G8*ucsah8o{WO%KEQ&9cq`k*;9HTtct`(?E zkvEhiOHs76C|Hy?l4V^~w6bYn-vq<{t!Y~@zo2=!;mmbXKkw0bo3Jg8?U4UV{Hsg- zG{ph)t_<@e)2VeGeOE6SxvH+o{JiSsg!NCup-k5elf`vc{-qUI3V`zsWf8#j<+ZXU zW%c9buxp9+afQ{-O=>dfQD?WPFtLd|V~f;ca*2@;pIf&cT!`U3wSrEpDsvXFeBcnd z_tki2<M)2Ok+s3vMA7cX<Au@T$8=|^^5=BtbJuys^uqh+5@+9n&t?;$s}1FZ4!^fY z&cYm2dmP2_^d8Ow-ox2>Uyc{&`3-O8w=qruSq?L6Z;GluE8ANJz015z50F5kNfT?% zTlH^825KXgnTIQ`EVWl808(eqPs<d>wPXtko2)U82|8BHJi=rPuhIL8>&M25_cbns zheo-n_YGkNLgCN3H_k4r*QQ-I7F^ewB3r$5Ba5E;6)&^>K!2JrrBwo_{TiXA9D|d4 z8np>3ml+c`1B^yOTX|K9p=4PE{?+^S10JL)J?MTp91bI+yv3$p2ph!)=B<N69y{xC zwai_vj~Hoj$D`U=NeEp(l$J0BPYiuE|GexH44}?7J#1>R>$x3u1nE&IbS=-*pQrgT zUExOxj4Z}fB>%?xvF=7kyCu1T_>@;dnH2DACXL*Dx6<u;X65?Fe56U~t)lj%v(mfn z?peTLY`miWuZE=<wa^D9f9f{YOGC%Uj{Ttao$H7plC+(dqn@S?@^SKwi~>1X8?S~# zZbq(MqiauZ93zXf{xdv7$YdIGmeUO~8mOOK9kjiO7h23U@K|4A>9!VD{O7_Qp1Gg~ zQ-dxw>`zF-)2+EH=GGU3=bBrcpqpkm!2L(9!delnroGB2m(GPIqT3sJ>cA{l13s-C ztc*T-OC?{kli5aMn1&|2dt9%fmEcdOMxg3?&ud8+ei}4@O+>@>k|O{B2CA{^G!7Qj zXBW{J>JK?}Ame)mx0;z^3xOap63ul+3k2j=m$X6P>p0}!mZG?uIEiXq=!7DT-XQWI zYrl#FnHa#U?k&CT{P;80+fY|aE&|dWpGNPF*`AFbIIkbFpgr3Z8e`%n#$6cjd%Y5} zS+1Hd1rEIrBzE)Wv<KdZjs2_O!|(+tRo(A5UY{^Uu(!$yOT;NW21_0hh!TO1WND{D zKDEoT$#WY+Z-9w}^d-nAhv%jRL}PfelqvZ*DZAV<EuytiK8Dti0Lf_Ppl<)LO#2nM zQQ@?C1kwHvk6l<7y~Ez;D-I_#D$vPN1VnTh>_>;iBR3jHECJfEAq@z7r9y#XkG>ps zY$iQ>bIXzbof!+VmO%-#@3T7XmH?eUSjqxCt$=8@ranIOE!o56P=%}r$MhDY+<9%p zv?;k80|Qb}iBKAj@-*6IlJWIH4MrP7>+Yg->AtbO1}#_r(h3avAJ+I?eas*xM88GT zuJ5Fl!Xx{8p8MS3scXHeIwBs>_0u2fl^N~}H!h^m`We#zxYkCfw$FUE*x+>rl7^%V zYJu*u*ZmzN54Ca26#Hmqqj$MKS<b*tm?wV7Sw#mYY|(|;k6+BhK;H=5_t4niFI2ne zu7q;z`cown?Lm1-co{``tPa1H1ll^)q<33qoPmh+dKrjbuMmWl4!*o+gsOBep2);A zf}>wNz45r$r@P~KFpY;k-zVvd3uOgXO%P?yfRc8%?XC@yZ$V!L{H&o?M-hO<d7SMl z?s=FMkWxeAV|fk(UlMKs{{|!X6Y8>QRfzbiCoL5Q6cgQ~$b&(3Dl^r!Sr@}`f{B5? zE%Z0uN|<OO8m7bK8}pdqjp<Pb2>E2;Q4IFYaQ}ybEmSrE87R`X<IoDL$wo@c%K+ii zP5kvuSibi!jHRzji)O7p1l>jt)Vkh?SU8-M9!5jWrpOE6@YF;vRKcRcAZQqFV6UDe zgyZbVY%Ov<!Zm$;T4`VQs6XD$tx~yl{60>`>GjxNGb^@?^q%1Iys<}x)nV4g{C0D1 z>SH_@ueX+70s>)7(QhY7pXhiz@d2D7+Yt1jGez8KUA~ZZVVrIscYF4XP?}eq?-19? zjUTHifdJ?nKp4a9Va=j2OT$=uR3&0Z*@x!sm(*%adgK(0C&RU`6CZ5hN$RZGsAMpu z;^1^y^QCURV~%2xmTt|ASqfXmyJ)HKulTfJA?-h(@4w#i1S94kFEk!9(yw6BQ^C6U zLEF!;iglCJ9j%nr5(@DBgu~7gtlGi>G2_rcKq%x`^*s6$G;gO9waed!9<<uD-L#Rz zdN|)9u}Kf^eyQ8-2Yt2hUgkDXUF3)J!WW#ia9E|J2gQ~80M^1b!R+LS^`LKN1zIH{ zEuCSLfO6j5VQ>G_VlgUGat?*f(ICcMnG32X*>n7TFNCyrx}*6Xht0HFhmiq9a-V&c zm-hF^69wNj-@P3C<%c;OTv8uzl<kJ<`)KZvoQ~hJdFw}x8EvbF*1g(e*Xmi7@RPyz z_~M0M<}-g*^RvSN4Yj-aPa4JwFiHQ*-uWJuUuTFtnl_QmB=Tk8!s~3vIGy%);$Pn9 zbNB<k=U|>7WLj)M6lltii6}Y`xo;VUt5{<NymIF2&Di{8@GL^s5fv4#&VCma>@O5M zBb<JKT~*Z%%A+V=C;omtDJg(@o})Dee=c8r4FVhFC}zw*4R3MzO&D7C)oMttN&V1> zkO1#Nje@sLt4g=mu`RQWea%XJX@iAvK&l>t!m(WE7noyEXZHC|RG~^UG*CF0bfTpl zS_xVa>btr9;-B^@eM<!JbgoHD?fhtzJU%*QTab&DSDrMMx}c$F>S#-3H5_$yo%o(> z)sZZ`Jg31>m10*nV=!c;{=>h2elCTZE)jilfWl!nyqZuhdq@rRJVub`fKQyCuYw#% zYZ&sm<#*wF5nmY3hHG;?;9-L^2FUj9fh4SC3-_l^IMNym8sX#~HXRcp^tEdYhEy(M z>XekrmeTi>%Ytcr%w;@VntysZ9Z<9l1c0Gp*CZt&M!K}2`jDsf$ws)96F064XN5h< zWQ*HxqDHuzzTrP2g1WgF3D_v)qx8THr>Rl5SKN<7X$NyX48#3V9V2`w4d+GCpHAVG zV%#Bb4q+k`xE1@p)6lILY{}6#=5+MEgIqDpUCsm?w^<|7R&X<$+|#PAm?a3%eBe!c zaEm?uDEQ$K4b$5>#Rm(}#{8K4KHYHR`y!nTN&DNzWhYOaVN5Qf6|)1G#&WAI*57as zxeB6IY+$?Qcqo6I>T@s`JQmVW7mZQBHa?iHpzu=k3AMEc^*q#3six1t{EO~HA;q1q zOnH{@8pJ+l`}s0k9i+K~a8ZY|!6!3Z$mUL=hd(;QmCNkp`od=c<846iXC>n;#tc6@ zW}je#8rBTa!MK0?(jVS@gh>4#U}Rj3d5cWCB%W*jN(-hg*FNe-e%gsd3L@<2WBj1) zfPzmM8om13CW;bcKC`6U*F;{pBX*Ox$c_NI%!(&8l%Je~k*N0rL>3$}qIOOLeX?z> z3(|cZsXtcuh*hN@`llriMFUszI$)I%TS`#w>>*`tgJYw3v{9rF*dcy|vf}+<U?j5! zT!qQoIyN!j=RPWv@~m*%o-t=-CJ7s<R9O6?kpIp>{jEcyd#n8ff_OUVl2Y%*7Hhd* ziYGT!7bydY$rR<XBbeh3s+<8o!V|^~3%0qmBst6&b=muChv0GYOf^JHdt%b!Q{Z(Z zmp%_s=n(m!viQ@S<vUCBms{1TJ6|GOha?UKiRv%D*6QOBsW5Gas3a8;YnC(Bxiu8h zw;mYlMRcBt;{%I9P+)a4&Qh;7@7J08<=x$BE$Bb21~Q@1f;!T?%m}EZU<@j?NVe_v z<xz(y^$0|yx0I@W;$AXe&(D&$DkpNRg`tX(;V`)_UEzq6_FQfwsH`FIjA;6=z!!5# z=QV}#n}$d!b1c=`L?u8PTM<tWy$tf{kt$P-->|o~rY9z2cK!H#z_-dQ!UV&g6XDId z>)ADjS^9!EjnqOn(#2RZo)!xQ&*ZQqYCagPagAWi$lfVnkK;haO^kQ*2t_NO7J}$C zcx!801v{Xx&nLr1dkm|NZMv*+1pl?%9m6RazDWCHIWr|fKz+fAy+Mfc_o{x7hp7qT zL*Q^t_WbIYt{ofk-UNS+0rd+CY$b_nx{rgB<QHx^sOUbnK7fH9;MZmt4&O`AF+nm( zN;;MSpM=(_G<pHvJlR&{_?QGtj>`z;Xd=U(5(Kd&JA6{Et4sW~x*v8nuCz4i*{~2T zLgJ?eB<@pvFZZ}K&JRn9=NR*`!ufcKRDZ0Kem}YJxspg0<s)2A8ZkET^ZiusXg}1u zA>O$?P}1%94ZlXGfU;+p7T?NmNyOexQnb*tplv2|&kbtnhbTR+lzOK9oQ*Qj6ZU7M zwj-J<5Ppb;>9ne^C_*HNk$4*ty&+}W(quJ(#!%|~m8syUCBWuvS?*-Wj}Ei2W<1lm zJFXU`X?&>AIZ+<7&f*_=fO`>LLHb6ahaRv_=$|B-Qcpaw`$gR@b2Jv#1$Ketn`3B0 zYKC`wS7iseE{GZ&<+<e25mD+5;H(Y_L(956Zc@c#sZvDmF1y)Hz!L@ulC$p`jfKH& zN78K_jhYJP4*{Pf)x!*5;4J;q8zz-4NUh0{!M)6PydZ85vfJ13?+xy=)g#91L|+MC zLaZZh#E3-_ZvYU&AeccUwjppHBjod7Xu_*l*Vwmw4YdtMW5vSIY1a5GDuK<x>3uZ% zT{ki`q=Hc(&Y_b!lAC;vas+`Q`B#ms_&0>7R5<}I14h!6UmXWx1)_8ED>*XJvdgNU zbwI%Z3?rljCdfZKK~S9@J5^M%Z_zMT$->9UrJE4C`DC2t)QUaHDnJ@5?`DD{;7@<W zPK#~d7h9NSmy%A|`7a%Myn3kFNkK)kXa!|o?pIcU!m21kYG|&*S)m-E%Vr(@3U9m_ z14GjVMZd%e)-t7alaKiM!za2b3YHP>r_MZCcRrF8F)qKV`#HYtT6{FD(xpkQRplr< z2z4N&bx#Q~mC@8Qx#3rB6S5)yVQR9hI)qBGUj&3X)!*#0vCUQ?<^pAF62M;uex&I( zaNY00d<{~pGcG88g8#`2c?nA;(M`MkGUg*Hdg0K&+X|OL)M9M`vD6RNm@M9Bi0~c3 zI~kwP3MQ{;W=7%Wo*a!LQK1KJ0or5M0Gui>r?v)f@BlSVNimHGru0cvUam6w5If^I z^2FeFi;XLRjO+k3os&f+5k;*6RanKMH#U4rT_BZp$uB?>R@T~PAmPrhd#0`k|5?!J z4gMk)({K^9kA)2k6NxJ<CXkVFr!8JtfE7MgxSn&%o_Py%=T@}69?ngDfd_@FfA~D= zm^kg3K#L`&F6y<!tyoQ~|I3iCTUe}^B7*@Yy8z|&P0npyvI-U`IO{Ki9YfKB{xw5X z#=iA+&3+~8_;B^mKO!MGTh+lK4%Labc)emKiEmQmyDbs;T!x-HSDyVh*jT?vP|vAR zvCswPPPFl*e=-phZ$gQ!2q2V0^Stzd`y+*e|M|EseuPyxh){qZNX^Lyx9^vpl>jdy z0)sfh(nmxJ6soLITJoe3-D%7#%`x@K+3Ns3V-g7TxM8|6E_IZ{z%R!Yhzo~R|5k}6 z6wmZQw%0L4E4<|yLTi=P4axS7f|GJrhh)Ay?i?C>Y(E&1C7hBw3B^T{eCf{;igTL= z?o6+UB#$p2e7KRyEz2A2Dk9+}qlp9pD_8;tF=so~!k`MzwmY}YZdZ0-O80B|K9}Gk z)02UifgCLWgQKwj9fe6)?0FwFYeKmCxSYKg>>wF#rER6dfSraePr%mfc6C|}Bc}(w zUb<VMmZMy!bQAj_%pg7P(Y@k(zRpkiWuYcGd1`CMIGxamKfhM>aoN&P9(-4O!9aC4 zQt33S(rP&yB;W<f?bE5?gCkHuiIw(}a*n2#c<y?>5eR=>N~T(M`zdsbxX`9gBAAjK znSRHY;Li)+2zn9+OJ=ZNQZY`%4VU_pFMQ<|usE(i(8?<{kES`rk7C!KRmgi`fGVxR zN55|+9^FWKGWC(ONbgv2&3lJ)7y_Rqe!u-)1|zvOQQ0uc3;j!WK!U}3)8u5RT)IIS z(;wS&fiK;f>el2rwHiDifkDe0J%|s#W$QS0j?ZIPdPVrHhkPtQ3-f)&a<iRIjdY2T zJPJo>dTWu49~A&6XPoj}Sn)5oOd*>@3D5q8pQx$MgD`_>>e;jH7;4R9zvU6rqv~LP zge!#T7uAAhvgM7gJu1<4`*$MfUpRl)tCmB66!=D96DYYt@JX!L?eI%>Ea5wW3HpfC zvASZ2jk>NmwvJiMK{ovsvX9VjqS|L=0m+&LUu<+i6G7J@fetc$ylHSG-WguXB|+$i z!_d656j75Vw%oJt#V{~79&+5mm<n+>-ygy02J6-AsKUhXq=`?y`w5C*ObYD2Z53<` z?P!3VsSPK9qa7P#l(n<HKW9M^tenVyopjvZr7fZF>G)y#?Npklr38JS9pFcm^7=+( z#y$?3vz2hEL=@lb7&ncAaa0W-W>B-D{(hR3y><((?&TiS^X7p<*zv(cg}C#z&mFV( z&5asV9-<&g8Wc;PN1CDvtyP+=uZH+69u2(I=_ov+d}R|f5X1OS1edsA*PgsH0<&nK z^W1{}I^bP7)syMLJ-O{zWYTUiTv*Dlm`zya(>@W!Ab_@5S?L}m3lYqfCu!A*cV-HY zx>qi-_grpRqpNaq3l@*f7OYH;@br++X0UmvQ-+)DmZp!kA07tL1~S9jrC{4W1dP#J zuWN`;4>Nhq!*d}tTNHqQ+@xBm$04jFcx*E0basmj<uqgn==sC+!IUw%@T=3cLX=TY zb-+ety;#F<@XflAZMw^rCgY_7{}0-j9|9nN2dR+-VjLc((%<<DL1PzFL(^t@7J>bY z)NPoOfIXyREBj}zTU@cAe$fRSBC36k!xg@nw7=`kz(a|*w<%}N`(;aLlX73SMmZQ# z24<^YJs4<NDX*A=SM{%_?I@|kb=l9Q6Skl+h&`z40D0Uu;hVGh+IU_-F`RBIV*yf3 zNQ(0Or7Wf&@?lcHQ%sG16O4A)#BZzSQm}ne^?nUYkdX6!xQSf&BQX{`3qy-oxriOo zYyWDI>YxpE4>Rpz^eKB}go-Hi=@Q)QVc%3_;||tvk%i(pw7CsW9kpgicHA{i%I^yP z_Ja1b-_fF_lN7LdZts7>nDkl?W59wZGCS_?I4}^Kp1GK1V8Ws^PN`;N{y@S`JU02o zfU>6@;{4l3fed5syC-teRsLYmtIaP7F|S@3Dfz}@xEef#Wsj!PQu5#I1k+8zi|fL) z&Z?gHmL-EPuVuk=<!Sw(Cew!KH>S`rXQtf-GS%Pz1j0DszSo_(?&_OLd{MtsHcjbJ z7(s94h^M;qj2jP+Fk(FeBa++;s?5*LWo5To@Q{_1+SG{V<CQG&IbKx)B}x10UZj>^ ztSEKXFcFD@WYrLaW^=}6S<V3R@O4>>9VWuBH8h8{h?to}2hB1s?VXJZX_d<(QM`up zyErMVEm-=E2N?!a?ic%@K)oJ9!{YwcB`Qq}YN6cHn{k}M?Ydiu{ebngTQ|fT&QQP4 z6=!E~!oJciyUF}CxXn|mR;)Ow4nP)0WIuVF$^)90kn=;=KH=s}+`;4XQ0>jrwlmcA zJS{28uWc2qv}`9|nZB8Axn`9b0io8_)Jmkv>u^1gHU9Z;-Kj@%tk;r|4nGx85at9W z;3WcbwXljFuQw7;mC~;52{;egH|KJYPfc~VMN>y!fS?=T<8j^oE3_+%3X$;AHU{Yy z%xFzNf(TxVb)^4&%b3yRm@dT8RkFdO(zd`M_g=A<n>*-Bg2E{RT7CmaVHlzic4%JT zPiIXb=rcA3%_K@?DL_Zh$<R2f$J#Xt5ypPoWy-H;sF<p_`#WSO91&-s6?1l(wcr-$ zXQv=hHjKjiJjW$noL%`^y|ATH-F%1ZyAH^nGVLY3X2?SSv2#h*@+sITc+}C}P?5U# zq@|XT3bLMYdw&EQfcv`>(y*JQ?b+iSAQ(-RPs~edVKm05wex7{MB7wN+oS^tWhIz4 zmJ>Otth#Y3APGMfdX0S>VgxDTDKY*aU$d+HRjawIai^b^x1uzdYx~fpLP#HCJJ^f> z+|Bw|oC3w6lp1A^<)sdHkkG(;I+9jtsbs)Okp7P=iYXF~#JIRp#S5x*`jL6spOMd) zjHL=}+_eepI+Qb*XSrJSM*uD!4Aa0DhxyX@F{D6iP?>xa_uR<08tQO3)#`S2FBbPe zmu%bqUFTG2yB?@5A^A6kD7fwf^L$aBUexvcI7Wqu=6YnRw1*~$b%xVD-d}Pab=ir~ zg@dNS%ks=h9*4L*p3`Kp@B)EZqjOR|CN0x)2WEeW^JV_nW==L>@koyp`0`@-X2N#G zcXDBVxZ1z%qw>~}-jJpGdI=eJ&L;ag3xQcZ)KNbUy@b!Fg2iZc3E~P=Qtk8D#|#3( zO5rxOSell)Zj_<3!S%nA3vcF!{JQp7d(1`Z7uiszbw%ebi!es2emfn_+GN$!t$fre zD}$UG^Cn*<_tw)wI)<vlQL2U6S#`;Pj8Ck5{np2U`J|Py$^B;KcR=X+)W9DuGdjS& zPYQ$jWH9*Z6<%7VaEyf$3W=;?p+WO|UL*LXQS=~lV&1?b^&TKuqV=W2jbrS}gf6d7 z(IODm3XRtvZeb@?^lNEwusw*Y($Mih#Zxv3%B#hlU{gz}2QFV(Br(O*=>^!v8mOMf zfM&er$iaKF)^$7P<l4Lygl}O!O%M9GN{EA%Dske&kP3pH+OLu%DCb=8n#I=>1$>>4 zqk3PKM2(6zt17XFL@YGBE483q3DwrcBR~F!#lb_;ETZ2hDB=_b-_>@mnCy&_RzyvY zJ}T*0gVUSA?Ru@wRMk*+iJt_EVP2{YG2@FxjW@0OzU9bzEnnxta!ITT@7;=?O;N*d zeV#Ithj-s`eWpkL$?ER${*ku#G>3JV4M#@C^1NU6K@oRHJKQT#8!M3Xi(LXUMPV0m z%*N=bF-qlr9&{_W9}g_o6|6YUiYQsz2HgaVs!o83JBeeSLv~tlASgut7xoqH{<KZ2 z5?zR}VzH&=SPmMcbU#c#+aJ++TEx?c@Kv(Dcy;@6LC1@R69`0CN=s^#L+YoBJouMG zt3k9#q0#C{1+LOX3mejM=vjrIt?$Q4LZtP@zjF>*O>CJiRBP%S`nXLUs3loD7nwG6 z+TbnpmkNl3laPV{ecb|JsW_Tj+uvxqnLp!}S1)hb?clp#g1DuiMvPX_DX|y&!akT7 zLQVMCo{$iD(}<8Bszu@;w^gM$KdRsEZ{5ciLF>q=D2~C&%GaPrEWs(3n6CA5YaT7G zy_Kuus=dM+DEhV75u-J+TtTFK3zgn52AF=S%6eN12=fMs(xQqr6~-qqU~iYQ|4Ahu zL??&iiOTb7u>{AuqQ$SF`_PMe$q9haB+-ld7%V7{7YXmo%llcm>*kG^E#8-_fDXa< zJx>$hbuP{|*0J!-dxae|8(cQNvX}!oshdew0G*^?nF=H#+>Nr)@k^`otgURDvSYB% z3&}5ra>w!HoVCcG6>Dyr42^U>$%hWq1<eU%FmRb=_nR^M%WjVYM5{{PBeoV3#qTQ? zV0Z+hoYBn3h2Kua=Z;!Gg`J8}^?tL)GXbI1Av8DESk?v^BWQOn_MG3@WdiG}+yM*Z z+!>|f=v8H<2BC;{JKHTkn~Cw*h`zp$__3FVXxoG)-?PYv^)UZgc3*6~sk$F~z8v@r zqxsSXUEI&FtrKu&9m^Bn#yZ)Vi!y|%5c<w1&v8_8E@}YO_8{<6Ox2I&G@?OANNS*z z^*x@oMq>25uq>!u`+7Y0DWoe|fL>DuBId!YQS?s>m)sT&3b6cGTxUUP_O~U``p2_e zer|Jo-*6(QprAaoy2$trQ)<!S1A(BgHynTZ>q0AQg631Rt4I-J=czOU$kNwC1+->n z2EI`zCSC7i7j|ps*a--Zz@2X_YAZVi+>CXq%-aeE$g|IKEn)uxuZS!`FLZTk-Es3~ z?u_k)LlH`kQ3$b=`c;evCs|b6M4<1R;3JG%W2;0fk)BI}E5aAMR<=;Q)n%_Qa<3Ru z>ei~H?wlJ7Gnf$Z8c)|v>B(j6Nl8avW`*+o1SFA81in}9kPlqbz=c<J&m=cP!=S)s z@rb4YY24)z-KSX@wVQ9b(2*#)7QC1BL?Os@l0-jk2Yv!0B&TC1R?w|)#TEP&xAjcA zuF<F&nP1=Dda3rDQdT{*L~LInMdVzBP++^zwlC)o=H58<CAU8lCT`I*PPKh&R?V_I zcAjMbx#xJOa@Zu5^+OGYk3eh?nSgtk?G57ckLea%yy&X$B?%D%T=R4s%N={=yRPQ4 zbL*mHUM?eWE5Z}oVMvlnwG*yMcC+(KcyyFfRLHARZ%D_ra|Fw_Tx{1F64bKkDSYul zL+F@=jF$zP_*K2DQ94amg~-+ZdPEa%+P9Qx%hp}B{)q9ks{?X+kN|ki&xUSvs#N<> zHO1*J3XJJ;b#3+<g3KgV(QarK*ZKFjJNL1)5pHCRw9`I(HZKf4CbLq$0ce==1Wb>} zVc2?gGmlY1uv7cmL^gp==V6fNbR|VqUP{ndR;q6d%r&!3<RT3BE!5ps(cqICf^J-U z<YcQ`*K90BP@r{OrC8EAEZNE_c^WNa(e1b!IO<#(JfxXM`j{oQ`&?U<N@1BG;GZr! zo}u;hLKecUV2lN9^Ix|4WC_$mbL{+_Z)pI6Q-$0L?xGAdt>`TZYA;Xz#lZ<+NB%xX zOIjwh`fQ!ZO=9>SNtH*KmhcWP^T?1e!j;auxRqeZTe~AErx!w@duEt3ArYtfUW$q? zc^x=KIPJdc{rfp!IcI4j4~Em6{5-kN{BYRt2C4vXyV&noKIAa@!R|B8dXXTh`o%xo zW=-WoXOAcT%oKgi$iCeOfe1h!4OcNB-LES91$X$e1~Q+%Mh*TG#{PQp{?xC^UXSyu z3Dr)QDzQzP-cE1Cb5Z!WENI%JlcV1K<SkW2r=zRj?(;kioyUkmMUmfE`jF1%3svn3 z4k14_J6S<BIxN0rx@|+K$FoMIM#W?F@V_-o{ARb*b_wa#qDk4R+|4|25eU4}ePIbM zfn^)Wtwh#7GS|!(%BiY$A#^#;qci`JH4@nWu-|f5xr3LyEC)eTlkn&BltXqFT5m16 z+-GIRrN=>TMQpZb8@aAC3-X51DzJy~x<x0aWOV%_O0BEY0-!cf)0wwHF=d4{t&&#x zg!?92sj3q*I+^KM2cJ<4`rc2-9?FLU{ZNh@3Za>spYBon=ca!_nZJ*XbO-ZV&_g3d z*Ck<alUS(yCP2POTJ9Y($AC}N(~iO1#`glEV|z>L(TP!c#?tvawCz@oF6(qZrI2wj zzM+**%B9y!Y;G_B$7R`?ReORoBYo;)HuTdNKwzvTGKI>ctvygw6+5O^J_Hye{s13k z&yv*(f!ySN9q8<Qc-%<$Xnmz60A<@8;>@mX2;b%S15?9~WM!!K%aW1{zn#^d>g+eJ zYel^Bp|$RmC1YaOp4f*Z7^L5Tl-Ik`bk;NE*vX;Q^Kju;mt1JtEUt?2Nko-QnD2`? z9i;}AxQvKO(m{MVGvpw|qnF7(GZa`SOuzdjA7BEr+r&w}p<Rb*-9-G9Oi(vh=|NFO zDpT}ff#MN8q#5ZW6cTNN*8O=-1XV0o+sZm&UgMgL>yCCOA@#k_Dm07yM4#?7kz{Sp zS67L|Q9R50+sD#<iDMATs(p@XW_Tf0TZc&9UBd0l7(t#~tw9qRpwU)Bh1H2#y=meL zYKzn&T2EmvXT0}rZ>fCuv<sPA8I3zA2^YVy-7HM&;i_}713wW$f6UD4i$9{mayN`9 zV1%}|D=TH^?ioGc_$hyqKd{7tO7_^sMB#LNI#Gz!l&+K~rZv2-Z-GLNCDiM9Ou@&l zttD2<d^Hrr(<)u6zZcwBW~oe~cSOV^quP!CRwN5+QJpg7D=Q6~CMig}<8-#Un~~*< zKk770k_ZA>q?g7PD9bz+6`5b4Ukvy7$xz+q^u`6y^eF0)W<kn=XN@oyz#{NK5 z`UDER!Kfri1T!9txJgl>kyfB8{s)5>hT?c0@})&+cT^Bvj8U07Sa?ILB*HqZ$#LhJ zs(~Lx>5-Bgie9yzdQ=!gSv)kIu(8u<J5=j7GjVn_sm#0+aiBpE2Nax4gbj`n;}7+h zx7>VWGy9lt9!0$}fnl7|kEJdtHutzXtm;iW9TY3hL4rq&&eM0fYz8}`wAJI++G<Th zs(CWq?Jq8?M_4x~Sm&VJ){sQK=F^_?Q(6pF(t}i^(Ogy*k4!w%JI@FZ?iZ(LCYtJi zwC^er`YIpzD2`q_vbhvlpzmOh<Ve@bQ)YDYtq~YknR)OV>W(pP(icNATO4#<J_o(C z+gf%_^my!@lgpjsnq-WV`CUJr7r(A%>3+ObKC{#N`7XYtr|@@KeT<Jjt~c81Gx$xE zpT_%R=&SlvCg%&NWuk0;|CXX$$wqHa2hIl0JaBCEQ{L;PIQ#xqJZ#U4AM$;{bNVa4 zta0vw&zNl=iAhY9NA52!5`d%%DAxqvOK|r0z{R_7G~$mCojm1M`bwNwjalk8AB4#g zn*ntzRTCf}purZx!t#>B!vC>_54dqJ!{-;TM4tfOfF6H7F$N@eIE-oW4@?nJ{02DD zVid~|ReTw`_ds$)$;ha@Elv9?qneYgo$|7vG9nmXe-3UA%2tVpPiZ(>LmrNcp*F9$ zHaGt+UDML9c;L$8Oo|cBKZh&fa?~JD#ppvBXc?8V&<99)h8B6fy*r|=X)c}oVr|LH zn7S!YCYjc&!`Z`&*l06v`5b~V$U>en+~>04(fA#8T%Ti{dJOm{qp^?YcpQS&zt`8S z4VIf1_b2o#h|YiC%yQ7!ZXeK~RnxcyyoGFMG}ex}qy<Unqe`QE6A;57OEmZ<Rwfo1 z@#TDo_YFK&k2FQ55)GRZQ$7H0_&Vsb=hAlkt-m?_ODs4(ed9YLLa=Lq*mz@CoK2ep z7z7}Ix?;a-E+AEI#)_8K6XMPq9BYwQb_1Nw&HsZ!hlx}iZtCvvw)^#~j}qol8)WOk z+D6(dhY!fioXQOp2M2^0Tw=$?^~J=)#s}~0*(G#ZmtzyUdFecGXC;)Ow73Y!=iidu zQIr6@0&6d!;RFJLh4%LVHOra-Zp8$Fc9xVCg+78nKp{hLe!z$V0U-jB6cJQ*UpZU% z(pEOv8M?L$T<cFlxr$FaSZ_zc@vuUlWujeWtDs?RT9~g*PfaQav2bN|8eLteBuh%t zEa4x=RI6wg4Rjx-CXXb}C5E6P%Io4c|G_+lun21Ud-C;u=YEFya4f?Ez&!ap!93|P zfzgDB<IKQLUzG%s4ff{klhCQtP};zKMtx4;pSE-$dVlIBk7lEF9pCPBd!sp?#T=Fk zBLB^3D8?xrD#aC90MX_arn3Nu?h`?QBJn_OV*x*c^?;TC6+Ey;@%?+fmmXLNJV63$ z7~l?65RiY65xs!{PydDZze)CQeg5aY{Ttc;I<)_lG%ApR{|iR{(e?jcp8uSKe-ryZ zC;K-Y{gVuNTSr4KBPFFMwa)a|TuQ3ul=t>-)^fhKV7@l1$!4|-i_ZDU-9)p+GD)>U z*XGTy|7!bWJ}+#D@Vn?}K|WV7t@x0O3&CY}N8UdQ_;yod$NK~~91_yPT#ByZ`AS2X zi{1gPdNw|{o1=1Kjm(DcA-<$zswadt#yS2uzW>P~S|0%u6Vu%db=A?%K-ByT?fe>W zs}i5~?cvz;8>bUbBi5e1W9=aCwMgoZ*h=|0|FXYhfy;N4<{vIjvm1|Q@dNMw(qyC6 zz}mUGvm4_RbG87?DUp4{!=A61ZpKWhQsu%WPgyXaa>8EXUeNwmr&IS`?5zjrLY9`6 z1sw}<xa_5`i0n%?V>dT9&JSlAk~1#b+XU~-s;;h$rsDvc3>c}~*$w<4sN-0{pv26~ z3bzfPMY2sbhkN9yjcH&31>~)S@zz_ds`ByiCB5KKt5wW2ZTcKuUBA6~7i97bPZL@6 zA@hId=kJ=M0F!n(U6>zLDs0vc(o%c}uXp=fK(?Dk&Q`lN&AqA0zHL#I&J0gXsGhAf z#FP5@`SHH*CD?8{3B30u{m%jCt2D!S)nfw^;QRuqC&OM3I(=C0F4}As8rmJ%Nl1j_ z`0o?vcjcmSi|ka(tJ45Sey0n^z@x;e=m*YgoL{kO1%D?YN7}@njaJj--QlrW7>!Q5 z(T&htrEV4&_}M7>4&w}(_xJbvo!d!jQqq3&_9(A*Vu}L*uV><`2cCoKGe!q-Tl>8o zz_f_QMfum`&e)`sLzN2P5qyRFp9RG?^Unr!P4Pq~*KryqZRC5h?edkz%Vw*~3gEZ7 zi$Av)PS#GR!sS|nbR6G@d&$8cnirn0U%$R=ZGJ8+`Mvd7c2sqKXm%0sSvQ>ZhQO!2 z-HhTedN%{7+xlVX&E4bhNBtXRJqyJ_DP7m5@2jj_G@+dn0!Q0NR9T*8gC*y=dhs9F zxtj*5(#!Y_Wb?~^7J;V<!=9cVocF7~0xpIq-za9nBhe~=ukjdmnOM(sQx_6GmwKl8 znNL~c8w1AsgGGzY7)6#ue4m*9Y@zJP*vO36WoOw+gB3M5w-)n;yYM5zSHs&4KVI*p zQo))xS1VuqQJ)6yH-17N<x0EOp;&xoPvyXq`LaZGLD3Pr=SYl!g->>BRqTC*l^1#| ze9qI3#6uDN<s&UVHnR<$FkXEV&I19UrT#6@a`F}8L0w&4g>sptUuO%GO1E*YyTRE` zH)g7`(pYV%Bv)#T!sv8b_vU<fGBV0pyYzVMoi;l?ozK+EkQLTjUG66}>gEcZ%p_-K zOh%nw?kw6SU5IJNEwwfNHm=+qyYlx>^1Rop2+!ntI3YIQtCkJX%4sIY={4I6YG`QC zIrwwdy>Ypn<R2>)ag~3bM?_!%oa&mn91pxGV=~)Zmw8bo;;X8v$fF6XOl8wgfHS4} z`TmH(_x}pfTZQh$wc+)sV?^w7yIJ0}Np~-((PXP*RGM8{f6#Ub^wBSPc;#IP1ZVag zpiLW8^z>DR{J$~q)fTD+gA55T-On2a`HYiyji>S@(RYpGkMk2Em`AImQ|Q<>H++12 z=1Y_)iZUUh%1F#QQ)U<@2TqPC97i87jzrYI8hsV~4`sR?oKJAu**Q9z)n*{q4XdaP zkB*LRx5nVI&ZxxTo|;QV{>bDi0QS1fq8)?FURhhqnF`Q7aIjCNNdGH$=V5EzXcZMl zX<n%>eR7o<!Ko_VZA7(spI*`s-oMyaKGe#zzq?so1~M}XOZ^gZJy1QB?Ke{RvXav^ zyF(^SscW{;zdVp;fZ@SrqgGUbeO)=nZkn~^t@NdX0gL*dU4isgjo(qQKc1KMG;Qiy zP(TwB60%xPWh-x*kKV{GtIStushzJj%*JMNyKMJD`m{J(gjD{lkla_BzZ|%^r?TSu z_RnZBz0rf-GCrLIj9kCv_>+`5$jvXjE|)V;r&7l@&B?xQnrB|iE+-*&efsVqDRaA> z&+Mh>(anltL>U@JM-f;mKC9e&QI4MG9OV2n%)@kX`5Z)F2Q(M>eJc8Ok72PUrg$)P zd02iG=X*Y5HsTXAoBXOV-|>xvz`gsa%VS~&t5C+lUGd3dl`RIJ{Rc4R&pzODYnQ%J zX@1T>>$bX_Th0kOR8dIk(P$`cQ+NG8GuB%b^<rmT&NvXafA?K+TaE#j-G=GIuCus@ zhrV#%^Y-(;3XNYNS?T1>Zk=m9lYi6FGpD_sXWaeW#VGJP{q>>ZDg=H0{Ka<t?rQVH zv|-F!k%9ds*-!Mce^+W>ZTh8Z;?dEk$##%M=s|b#M&xV^7tjA8_8$d_HFY)mxuHFG zZhAiZuU4MS0Mp|>uQ;QBz2UE^0}aNI@45eXv=&uHd@;)fHj6}jz2*IhVU>u>f{v~B ze(+SKjaHajs-y85N!*e)i(=;fVf5cIZ>EYB2C*k+v>{{C0{&yXq#+wmUs{Tm#<%QA zpG)2{yK~V?^}W;6GAw}K#*>(Z#Z-4dK_~STFcVoka9^r-_)fnUQ&NC2-;y2x(`Aj8 zuju9{FlXZV-d0uKIH|s?EqXCtembF3taSYB=P9zbu*(4XkLc{)N}c<1-^SxNfhPqf z5E2cXmea+`j$ZtSBp?)%wZ8q^Dl?uK-68_SHUIzMlmBxJ2h;WCFGl=d(b>NO!~Y5) z{R@8lugLxlb^V_+`cH-a3)p0ha;wm6a2$-q#gb3}DV}0K27X(!(6e|1ahFPmwo;Ra z019?NU#izNnkYos+5C_1|HjFGlkNW|*}uWr|F0GM<pv1>f#-KZ!fa$N$QISh-LX-f zbzdmr7X*+LRBY_uiNKb$c>BJqbl}O!(za3}1W3wtkND*(&8zV?8XT1WsQC<WVyrOx zKs7W_fB*M#@88cy7T_>?{@(Doq)nwuJ*%uEv#k@0CMZP2WpDQB+27x{7^#=^JZTwn zx!xH-Tl90A7&F{#dwvGesGd2cjXuxP8<n2F<8fu=D;SA$_z?P!JZL@Gy}TObu^8Sl zl<~{)CR?NcS<KXboq!;FPZJr&bm+V1C6$C-^=o|H-%_`XRE#A|Al3QPkzJQ+b`F4P zybMnvb%_ekmk(g_6(oGT!v?ak%+pBj+VGdNcga_a6!R32Is1*I>-<|jcRf2<Kk){u z#lm1eH=EbkoBR9uX(Mr$VP{F*hW2uT%^GS2a;1P=+q1HJ{>oXvyVn;?98;h8;mO&E z1PWz-4v|G|AH<MIoHDCsC*irAK1t0Q&lXaP8>eZ@)zXUj-OGv8lBa$ff<@P9I?yYT zAD7QI>$MFN#u*DKdVW8SHLvckRq~)>oXL0VeR*%n?uU0D0SPZX-*CKNb%4w?tfJqJ zZmf@tj?UiekUSd(;LsBD%p6n^kA1t?h8|ji(3AAET!U~yyfl5hm24AfgUB{@@SMEV zY1*uy@A}Z@w|>!m&$DefCbKt|UNaqk-{LJZdGIvv)T~kd+m?{!r7_uc7Y)SuUr^-5 zl>k1H@E^=_%bc8^RxjLXw^e>rd=BZP76P9Eo?8IazkJ-W=OuZwEL(a^Lv1?Z*8zFO zI6?iZHy52&s%P`sANiTpo_Ys>gkt{9@>Z9VQiUL~m8^O_vx$rvsxEYklKA{G9QKOD zo4UiMv(3A^e2VC-Na#Dl;)||NWw<#~xtR2=s~X|31eWud5~ftR$anA5RIm7P0$=;+ zm%9t2x`_>NFIJlygtEz&=Z2TZjsf#5JRe)yCfZZJ8K91pfjYjwzMbvXx}18xJ-iz} zB;;;?8fUWpk;*n)7?7BMY$w9q?!LVl#m9it*;uhc#lg|<ewN8;r<Lix&|kSwxz24Z zbe^^}uZrdQ*EAXBKGw;fq-Ij1XFbnS#S?w0I@U~P3zVAM+uLnC<;Hj^)L3mTGomN5 z_=|X7AD8ZWAt2@y!W!|Z8s2C|cOxQp%LgSYBXlZUHmmj%z?%#|&fZ^lAx%1oPS!b@ zF?fw4Rf?VOo|*gzXjcg(Uml0xGaP}~XVT{V<$kn1u3j31N2ehA`5bjwiT}M_^g3eo zZufxD#*>Gp;>ETgK8~FGY(O2oP4h1o_jqPZKTd@%AjdNpSJgMyPyoXmw308AYoet( zZH?2nR|jW{E;@%WqFo$1Km(872RhP146j|H8ZCW=cE{S2#oW#PSO>yEXjgs5vM8&? znzC$_r6F_YMNw9hdL~b+Gu&e2{HO~L>p&q^q426z47#pKV%GlfX`b8%Z9++RY1o>s z9MaZ_W}|sQ5BTNWB6W&|?3rangpLi5D{x?5&I^n5>&k;&UZ7o#)AJwbg#&f2+7vF4 zM=|^6QLnxErlH-whwfs&nzMEu)-h@0@$kzP*1xE%-ouv=hf}k&v-8DtpD9dfPW=Jr z%lmPaNt9~Z&DyA|7b&U5($qGR9C2K<?-&Q@Z~7w`nBu(cM_zN5Gg11xpKyK;(a-ud zvg)5o>--j5v`uQwGTuHuD*PP%mfq`j%Uu?a8#<~T90v*0lh+Pn%c))l#FcFu?#M|h zzB7bs9(s$MLXU_on<r7@I&N#@6wpaj-xwzjs&YTL+{5|2KS@~a7S=4EA2MNaSWTPP zmkGu|uR_cU)e$}ZdPjc|`S9YR+A7+PHD6rvX?l>`wC?Op^<xX|=wGuHAiIClgc__; zqniZ=23zLF#%A51cu!K&5`{i7S3P#3?AXe3w|zpy*GnvC<HNn9k!<SGI|96z&x&x| z%F7&Jj2V35@}fY#9sUk<!oRU(rO~o1%Xh=@wv#&VZEQxaVAPmOJ_riY#<PEnS3tld zq6yuVB!dAw7>LCSWj)59{H7ghXE(#y2J;X0_m5=;Ei(E2exU2R&ulvZcv8t{$&_lc zhPsdla)F29_B;Jc3()0OPS*={z_u)??vd<*Y&(R<(^(gRzJrm*KRf*v|M2wsoWi-M z36K~=Z;iv|mGSelPE-o2x)YD9doB{b9UvCP^|^gZ2dX6+UE8AwjY|3%!{m|}GrQ1y zIX8(fjWF*wGA1r>SXrlz0fwox;q$42iqpbB9G!F{>3I)m4mhlD2C`m8rK&z{Lci8+ zzc1b@lTIZMv?Fge!L=o;k@+<kmUQa2l<%0_c*)%Xrxu6jihfX8NK}phIMu@m7F_X; zB9udMC4QS)Bdn_;q9c=z-h^ARV@40$^&Jb_OS+OLST0ejz+YfTXPZBlDOi|#w*2(U z8QIj*cm&!e2vy7B;#wWr2Br3@8~KuUjFqkZ`0U{q6N1h8a!Ka-o6<EM);Nc@N~d5x z&8rAQK6Psdy-{v#&RzH(V9Y!Ldh7X%Nf9uS)p7G5WSx9S;;*{R{LMndv?{>lGrpcc zRrl5_)VH_4Z}X;YIm}~5wS_d6l@Ncy=W*iP3I``McsQ`3zBJPw+cBA!r*#Pc&YM}? zMDpvc%7Rb64wyd`OxFw{Ru?{x^OS9n*T);DZ0bkgJd0tn(KXFbrb=Tj;=^D2UkhgZ zf&VcD3~j)CQ1ANg<|;>E%C5aB{5Pod#bGT)n^d0N)V<BzJkLy)15zAZ?8~lzXgr{4 z<J<5xi&iWj{Xy1qQ5UKWv-4>ghBPZMiSIts@W(N408F*Nm~sjS?3;CH%$AY8Gmho9 zXI0gnk({G}DBn~=k8T8piGTE6D6{zC^N5Hm#eN6Ihjn<i%2T`?T(1pvve%^=ABwP7 zgM;X-RUzTtz4P;epfDs%ZrzEsQ3ECnbsZNSm3_CNZtkRJ>S<HUV*bZg$@E6somudv zX_DHaANd6<nI~g2{vY5Krd~A}jSerk(`=`U$Vk(?ZtG@mep3~0=C85tWQ4K{!f~<n z&oqs#g-I$)+Q4+qxGLhouV=C@zCs29s8m(ykQYg*WK%*&<z=-EB*1@f9={o$6g@p2 zqEOYSs(QvO66Jl{!WQjf7~h_>*+*Q7k9<F#^j4|QH}sOEDtr4I8X@cW>r0zJ-=Rw{ zW#6Cny3aY%PGqPu$V?BSmtD;Y9PXZRru?!ue0F)$j|cubXCeMq;DvFsX~z}%(Z)ia z|InSBTS)%s5K>kyDM)iPGN0$B>GF|6oB{)gwpapY&H>&&)Bk*GDaRBAqP_oetytwx zIx)zWWoxLoBFsN1Cs71}tIEn~p}fF5{}uVN5B$~xp`!qXKhv+f{-A#!CRi1DfY@DL z68qtQH_SgCDYDU&S9rSUWzzE#^M(_vq2XZ4%WgTBzXXR_tdzHy%6N>^KHLvjOdcOi z=P9Q_mnyLf9M(=Mp%4^n)R}s^jNpEOprU^(#FJgPqgPYtB{09?4J!6Ru5fd6xI0ql zz<A|KItU<t@B_3yZnZ*_{+k5<cs|)WX{Q7OsVuZ|Ck+9<A9w@1LO=y_CqQPf1P+wq zU!c9cQuH(AmLnS4u5~ga5;ll@Uf<<}?B?m{vR$&}dKb@4eyiv@v80sbN)@u6rz-oJ zXB!kBN9$UZ8a=7<uGy#bMuQKBRI!(GjXOJR7iTJ`r%iwQ$mDpFrtj{02dn$pxrt|G z|2_BJar8ierrS~Qw@ADx*?4E*>-mG&tSW%p!}nP$OrMVIY9ijItFIofQxss?rbbIa z`Er|Srs$2eKeA>{Ke2P_C}zJCoe)%CZ;D#6x*R#DZ&6F~)2$Xqyk{+LuMg9;ToQJ) z{~<RwD>OSv^3qcoj6k5m5{Nmt(YvPgdY}@Pn(@V7>Xh|$0b{+mUv8}x6ejs{{_aus zahV6{xs^Ixy1zd#Ewm0E59g(|NUKXP>FK<n-f*<EMy~x)>k-H2b$-kASg84MqPoy| z8MR9PDd5M;`se`34%fJHxbLKP(sP7bx-YsZVKMcJ+)Nl}x9<D&BAp5c$FkFs5`9G? zDV+7Yc#u}}<WDE>2$xSJNk}U+!t2h%sIvad6s)jyU=?Xh8kgeY!&&y&olF59$ARK& zr|q?8DxA#AYCUueP~1nkRynl>%ksXqgrrme@b44AIzIiqZjP$lo;_NCeM;R63Aw0@ zE~v&mKfRRLG~w3!4x4e+n-#Tc1e3@=3P_5{;vahs`Pg<c%`#(r!OSnTNmD0$^yNF5 z$_p|yTA;ERs*0?K8LSq)<lpM#dHK`%JH1S)WN7~C1hZ6S^r7f{YA}Q%>@H87L$TPa zdy1~MqP@MN-uOVV;`gWD#ccsSyIxUjrs}R0&N3pKfvhl@q{VQw8$B$L%~!`~z^2{7 z_yYT_DYth!vD$%I23Lu6Ay`<P-f}i@9p~$-$U68!T7y+-XJ76Y+&b02lS`Ol!$NR6 z)!`H3Ii5VGT}sBB!Ueo}mx+01VnkWr@yXpjPv66Xd2n6h=p9-cy~_ebfOCzhHcMCM zIkM7ne#!C~P~mmI=8vWA*-k+fyW!XE<MC#VoP7~+b8~$=t#k`u0Io2dY^=3$xxINC z@f7D#KS#x4Rh(S|?2$Y_K_B<0dAoXY5S2$q9d;cajMHQwA@RuOc;!z7?p~UzPAZlb z=lw+ZC1*nEI~U1j$rd>-aCJ4TtA{y$DOfWEvi|L2Z#oFa>h+nj{#r$f+fhA*F4g+L zi8o2p{g;q>T^T1(l>#$_x2=tEb{f`~x|2QY_wVt9i7)@7y!VcZs_FVfTM<wZ&<7AD z8c>pehy=-CfF|c0L<9uM(Bve7NLG-XCFh)@ARt*n(?HV%$+-#LWSDArzIUE^@64U= zo4e+(%UZn_oKsz=YS-Ssz3aDk9agUBKG9WjQla#ffMpN6?{01@z6-x@m=v(?>LN8g z<Ut(A)dJP*{08P!?_}Rs7+RRL?CZm_vcpU(V*h)&_^3{2Z4w)Pw0tSY$gh`%c}VCr z#hqPqRPKG(P=1}hqwSK^;s|*8hM0kvgUOv=eLaXoHrHAD#X>dRf|T!F{u7=cd$Prk zIVxpfo)1>fvoxulO|oLfpzrFU3O<5I*`_J%dfm8lIBpl7)~qMwN-3a;J<&z3Xhp?x zaM#HjY-~3((`%!;r%DmRh|M$=U`#sTVJh~VwomMK{?KxmPJasxBrX>sbc_m33RT@W z)h;eX9*xGa<d-zo#q4G?xtva48TZKHf|?<@2j+Ro;?&(!dvY6Ws(KdHCx+l_1*!{r zyrzhG`m)`H{Mh}y7PXZ<_?4lx)HZ8ZZGaUdUlet}CE9kbx5`CY*3~-3cs8&vy<zM) z!B=fv^V?~cUzr9Csto)*M(ZRBgWU$rXn|Ry_7`c#$CJNZ7#Kg?V5+OJ-8coK2Nr2u zzOi%CqWrrV>hk&zIcj^hRILlhVR_%PRRe{+IV6<x3W1i46H%X8e8Xt>IlgRBlbFu6 zRyuMcEBijCiKrzn65khpDV;;FtY+x~4WT4XJ7DKt&h~%|7eo#=>b$(-n%uE_?fbe= z*@U66#_G*Oeh_T@cif*ag`ENnq@XUX7=httmECyhS<RhZx~_@S@06p@-Z&d1?5nk5 z8>%!XwyHAV^<2oThiu|K&pM4>fUIncymQCmK0YLw23X8E*{&7%H9kNNrgmd4fuv8c z<%McJ-PAg+1;nd8d4bZa3o02@s(EwTo3j1_&T%LhZ2r$Z$$yETI&+ohj<@QWRRF{* zcZrG)dKc%KRPTy#sM9y><SCKTEA@$a_BRnqC^G@+YFwH|oN&g*6;{tItT-v<fXP=9 zgw`jBr>tN2cB6$ZfG@`O1*39^WY}LkdGOJ2Q}U}Y(kh>_*>EXoVP@zUR=4hn+*<|E z6{Oj!^$3lLdO|@i8D`rzxA;3T;k+>{5CIT54Dc@1$!WBWO6QID6S_K826|uQ8m&aR zWq1eflas(mRdVCIX=r66OSEnRxWeDZ$6fS7YYS_i)=E)s+dhEyjFm+(7$pd&{|u&P zqFJU>Wx{Q%`UWlZidFZkef8T4)_=={eTzxV_JR4+JU$!O=-k4b*=a=*0~Ckmead~= z6@^I*rdH!Ob)UpAF@R1OgmImuhW3xUt&NsQgpmUzC)BIcpL{<g_aC(Y!XCn5Y5F|J z-=#C8X`Y^Uyo$tz1CfR?yd<vI7rUEc27jg#Rwg1W$4t1ZC<!o)4QoD{I|BtY#>xGI zrOu)|<st7>_eO^6`Ac@0d;vT&J;>lcL>2UiNQV6lD`9COldw<{^T~(GVjn#AO-l<b z9rIBZ%3;`*mZ(H-_DGf-J;|)LzLTiB_lJBiZG~O*HGgUc@O`{vcup60SQ<e4G7if| z8T^N9V^!L0{5+MlYyIxbu>u|qHpME605DuEr@6MUn-0qr$?F%?U-oTm+?{R0S^%;I z2~(*5q5xI;n4z9424Zhj^X1&U!A$bwCfxvML$er9@xou=OVcMI>(c!VRmWjzH)-Qo znI1`o4HY@#ahp$wDz#=bbjtJ%rHO@?TX(h_om{EEhxD%TGl}Li@E&e$Gt)KCn{U`n ztgEkA<$bJ>)b>+@Emz9TC_D%pl0Dc?cn%zcblB|7N@fMhQek=ed8bfRD%*RtNT->P zf5%R5%2x*~GfX%7w86<?xugamT~a}=Q72*(3!-H@?(k|q`LKg=*Kjju3asYd-hS5e zP!j&cZ#hKID9dd2=l`;Ddu-o){UynwartU$6y;J>k87F9ik>?SZ7N}^#I3^VJPR>Y z%l6LVgl^#riLS0*XI245iH*LPRQ2zv@B&={SmQK-#dukEC$?uMbjVIKW~}NDE7Vtn zOCMfR4xd3;-F~Zcw9_RY*U626<B?@DnJpfNU2XsJ@Ce<=G73;_TwBgKq(NVa?-3&q zoh<Tcu}j^;)OuU1e67d0Zb5b-2rIII#z0wUp4YJPq)1iZ!$M<OA3c)ckdJx8yBE7Q zQLnv}DM#T^UAvUQf)WWOZS-^4c$`rcK;_cdv)9Rw@eT@1VtImr2Y=j9o4LvMAi|yC zLx4Gu&pNB=;a$UkT+sb!P~o*}SD&(b<op^^cVFKx1^&GX9jJHP<a!ovl@+<_`Aoyy z+^+Uz({pI0H5?zlN35gNRsf~I#9554vKY@&j39AGFB*`7aA~aSP0Z%$-vJ3_ZO=p% zTfp9SkI~8gXgfk4m|ld--j{(8n&EWm3?&hXmtan$yh5++w#%oxHiAz%Vyac7joH~8 z;&4W0d3?X~XuA|RG4Ez?dqHN^?8e034+hi!{$glsrseBoadGjWsY4qX8Gq!{BcY?4 z<SISE+JXm6Lg%3M%``4R#vNX?&pkJRIG;Ns+kV<Wn0(~<ohqXl+l}|QfrQ4kirf*! zeEb*z$~k(pD{E$jUF`Rcr&l;=<+xN=c~_HoqDsYBPwPimOW3kptC9ey3B()c@YV9H zvm;j0>7>|p8`R$7j8R86_Gxw`0yAPjx=`{YqO8<94~i`K`alor$<C^5C4wztpPyqf zXwkylvi2)5|6Qb0h&Yc1b8*W|x*HxIllt)rG%Sq-g^Y4~;0{zpk9sp>3kYhh)(O*4 zn(0lAAGLy^(jUx{5z$r0JvGajJzk`c>-KH4p`w&^T7H#5lkTEgz0h?OJjLA=sw6J% zZEI(jYfdkOl|&s(s<r|5t*rA*DO2x@3+YKN!);~#h*sWHmqMAsg){9xHrFeLSB-So z2Czz8{CE*I_*;ZB(a9s}Am>qJ??KMr%~I!!>%Qb$seF6bi1zN`f9`j!KX)+WcBz>8 z-pL^gN+MKHX?xqY+!HAuwkmHkCuy3|pw&3bl{*L2CH8QfZ0yi(x+*I1<H;Yir2z%b zGR%JAypfPLR<cB-HR67H%mE9JaXx(OXxy#-@RtatYrYv#ySY9=vT;-GE6z+yyY-7i z83zV}Aa(3KY6<)fDzy%4JCjz8@cV{sw^r7XmGau22ia1)efRx@{sXVydV*<n3wZ(8 zV=UvYy3=-PzSeehu;P6h!8Olc)7$4Vy?qYezB5c~vNm&{s|g>2p>SeU`>DyIH#&{v z{npoDhl?Lbk{ZteuA{f3fA0!e*}G|R!%nW+g<1E*ou7M~sytmE+dt`dF&F%e@J3?| zhio6qY+BW8qN8##(!$D48^UU#yzd9~BuH1wwdU0s6=w93bpP=6<FvNnA@Ji%G3{Bc z<mx9MPR!ux=3MSOG~>FH9Q$JX)%yX#nz^4Frqx3AQ7JJYtHp^onnZr<%lEj~{j~Xh zCR9Or7nd4>YgB0|&p&BkcBGW_^b!+qiRclM(&a_EZ&#sL0NP4S?wM3q7AkW{a<o6P z+jIKaQ@Kb#ILVH%JCg=C)W`3WKD`LN5ch_^trh((@+@`rhCk@dU&woykyVK<%=r1U zq%}06Z?frRwI+EMSX{ZXjBIR_UQ6Ot#pT~jI47sqetf<E0NQx86)3$%A7u>c&YGB; zo12F!WT~M8HJBA?m>=)ihI>5BIZ+?%lA$&o?hHp4t)E@-x8d=GKoBO0OLs*?^m$*r z>O>+juSWjzxKI_p_meM91mflTxNpb0I9ZgnasO4mcBMo1@r*Ywcru@_a|U78zB^@i zg7zv;jHYId=N_7+wv}@laU~XTpV>)lg<C+M@E({B7i8<5V&N>4Kpm%Y&MAseu|8bg zTzs?O1R|n;8&^&jilaLqq7iXI?<0X*%W*8!JBz@E%)>Kf`u)qEqC6+vRr%>38{+Ej zrc|&Qf_6VAC-o;LCRmu5vclX`7jOl7a)YU^j_x(@6tlgpU}y1Olj(ZxuAZ~AbA-$B zTvwF(C^7pvCD8<3CLmy8V0aORNkBf7i%!1T6Sv~OITJihjI+Gwsjp18hx1iyLU(Z0 zd`-+pco#v^Nh1svy51%mH&g?Ak8@mxDDL;eOf##RC;@w>vkWSC`#P#N!ejZHUi?!i z5LzSyY;{D@ASET>^XD%~N%|3ML@jiXs`!HGujCgYRVhRZeJe7rK2dR-XJyF7QFWEt z*?x<T&O#Dg`|VwAmkXo6(g@L!y9D|kS8|XGY0zxY2@SfP3idNOpmvMj37yPfdg6_L zm6TLH^%HS^1yoWpYk+9tT+*4ECHC14YZGkx`uc3q0*I=Q+Mtg%;jPbrmAk!Cpxvkk zuq|ug_;2xHX7@H7qD%nE{o%&_9N6S}n62#N>$|iqFOok%G}0N6_)q?+N$3(jzA2_N z0+*j6n`x&u^Z?_X62&o6<B87Qg}J`3&lsR4=7GUM?$^KXxYrs7%U(Xgo}TnY&qU#B zDZQv>@?*H*m7bopcbz~y!-<EREd;{NeyLBpsL5=fwZ9*J(;M^)fn=#jN3yF<o`71g zZ_H0(WYq5N<)%wl*K>k<W}j+4e25jXO6*BMF_=y^xVyYLa-tw7Kg+XDHQ?45NVVv2 zM7qC!tjbime3kO-IdV5{-pqJOMA_Ni-acQvTou82c3HKJ!pi<!Ez=*%TRp6n>*FZJ z(Q!@8-MrOI{OyGdnBahcnh}k8+-5|y$1rhbx3_kE0BCD&d-wK{8s@3VnG&nytj5y_ zJ$~bZa`yPR3?DQ}a%&bmo6WONB)7jyv;`Gw>d`7E-oTmOxn2xGLgo*QfT)XbM4%TH zrHii77}uF4$2U*Nvg=qB=%4@9OJQYwHcBsLBV%K;p%zkBUS7n5-B2`9(;Nds+vp=h zn#Yi;hMgUI)NT_VdwnPv6FXxfko=i}B-f1AX64^|Qd(kvik?>Gk@-H*Y$Ym6E8Adl zUE(t#prbR^g%rRA-Z2D3cJ)*+V%~o(ypj^OQEZ_C@%8E?ZX`1Hlw7)K4J9%@jT8Ty z=v1f!4<x=dyGJ=OT56oF+#Yh^)PK*NhHS>Vo(@&J60x|*GI^?{0(Ml8_>}YT<J-T& z1pq?8%E?bIT*#rPSsgf(#G!+i^8Ud=j+ll<@yb)vffssu=MYhq`L_u%EG=!$%q=V| zSfeKDU7%qDa`6gcVi(R9bNvAbI{p3q!_tynFguumbTd><Ih-Gchleqh3F3#XGC>cR z*$bpv{$66!c@B+aG}@ZmY(xPhI<y`;7PP>2*N~a=lpF>BGMDg#@YLc^!S7_IrmY20 zplmslQ7&&l?|fxg9zaCEnuChd*Snih-%~M&yU%l0Bika+Ow&uM`0bJsn`X^WI@TYz zRQAN7Y1bi5vcA5)rv1&nT1Wc}W07By^*#1|!BVyQgT?Yfy4Vab?C<R@;!wY1u9@Q; z4ap4*49t)j!3?IT1Iz*6!?ro>*Rvw!%%ZxEmr_yz((CunHT^3X@Fv+qQ2hCCfba7+ z;7iKOBfP{u+Mk1d+dPRCJeX(#z?GALK~&%rqzMya;)34X<cy?n^yAG&8&mF*((4)7 zoecZ~#=Y8KpRu_Ae+GD*`cn2LA!35A;dDqgj)yxE;3^X*r(z}Xtz>+=1+^97Ghh23 zjsReUd#PJB-EFQ{^)6VImEn9CdP)pBaD-1pPDfYpikMo<BK*r2g>=d~-MdR$#Vwgx zY``d#%m(fa#i#1LQfc&v(lydI?u$*AqZN<}(+db02A+z6Lhm}S=Y-&WNa25=z59Kf zOS|qNL_k>w?ChN77<#IMCiojM0;lfrL9DL;wbClHP8qb+H+SZ`2H+-?T>igjC7KL> z<JR0uN%~x80(4Clzua{JrK`|k<plU5`tXP~++OfO%>7H~>+0MV1qgi?Txy}(w2uYz z?rH>q)0eiM*yUhmE2ZmMb!b<?2by@N814txoIb-(tGX<(+?aO(_Y70BpEiB`iCN$Z z6$gm`fEH(({J-aNIzy4vAeE3j1i~R)5DCoYo7pkga|XH#a<NJSwJjJQ*8!928{!rW z1F+>}yeAucsOV4Mpqv8}5Za55#nx1#wSP{39NU^Fs^K?F<{#g#w5`W}h)7x)$$%|$ zt+~T<)>`^6s(if+QVWz^eWvZ79bwG7-{D&Xj=R6%hE{BYb_tq|(tgH|Sm8B2jN{ow z7ZA`eTP*G1#3m7igUQ<%{lV3d@5KDaC<mC{UX|HC$Swd{^Q+Q~hsr=F$1Ej{lmVoO z`j)aG&xrf#YzZ5GwRzxJpNk|+fX8lW*T(pmJ-yBRi@b&q3x#NQT4v_^reran$xh?Z zT<uYkydxbbYva@+!E5#221EYhJqc(DwKZv7okmRD?#l1?U7h1`W}s!l1v-cs3#j*t z_LI`#p<t@&DX(#2m!RZ`&wLHhsHCug*66%e+P>mAwl-?P1&hAn)kiA{tDex`R`Bn_ ze+XZ5E5BM|XT%>R-ForPkxT6JNp`rA@&|q(L@LLXbT+Gc$rK3u4lZe>=@X}W31Pha zI0{uc)G9(XUGI_fzTEN1lW!5Hk%|Zj#@nYnbNxgYRid0INgKt!=G(v743$c()}ag= z>~c?uVZCIhY$;4g14>d^$1t0RxDN^aE)j{58UaN0UXd;tH<d%Gk|ED6%F5e6s$8kd zLmK4Oogy~q#5=g6Se@r>{X!3Y;D(@e7Gxbw0Ga#!_ANb3ci;P>7I&FeVl~FggAN_= z=RkthbyxA~Rfx_t;PyqIIq&ah&D0gQow9K-gs0WdoEml$OuwU>{9gJ}WzL)wGUbnp zmKd*?BVHgp1zvtF?l%+s9?G!vlad-XYVY5C0YdpBnG?c<x2=mhA)oj@Qe00=pkG+_ zAkO{-5nf|nlXjq(pdyk(hJP6X4+5^ANjh~&UE4!;nbmIP!rZFRymftDS!s=qHS_}F zDbsGN)tReRVdW5^=dQ_&N^9n<mzei7DGiw$nM~GO!^L6ZX5A_#VdTTraW%pRbp=^- zMvtWRYQ;O=tMiR#9F}sCkLL(UT=Y`PrH4Z)m3l`&o>u8&t?Re_KjmQ={NbzenQG%6 zl31&jYmmgx>z5%+fay$thN&kf)jV<;ShECfI<aZa)q`4MBCG8yZ=`c=^38l0vhKm0 zhc1V{2qsZ5{Qf1c8Hew`R7@xoCy4%ZbIDXSyFwt2qGwxx{~^yeK6}xt>t8G@MDRH- z4E&#@HOI+;+pCJlxh$g~JOZFL7ltJNzj3}^w<~#HW&iARmt|ZVw^a+nX)ezEhx&{< zyWo+PdP7zcp4^f9_<lKkWxJ}m6{NnzeEr2QslQQ`ri>$9@ei_R#yPhv>>v2<*K(~m zF8vNN3QNP5(kY=rdRUI)3H9{yx_b`SgooXl6RuKQ>n<Tj06R*1N}nJv#TB{l16N+~ z4b6=t&2;yYztBmpv^M&;W?x+ym(`_r*Ha(bvaPZ-8xNH_Les|0Ro~7mGaj!uA{l^8 zRaoRDd@+;X>Yl?{i5Lo*1>142Y0g#4_O4Dp+IeI_{j|PYj4I0?1Rj2OF}3Y}5x3mf z(l^F4Ml$J#_LUjegcF_jk{uo$nfQzG-#6u;fF!>XDD{Z?vk|TM23>CUypm3#`3rA^ z<DWcKyBz?pLMspDH(z=u+`nD*k^ox=hmThM6PgnBH7{NLh<cL-KT)>loNHG-yvDCV zH>WG(B2;PH<0E`JvGKT{^=m>ph9l@JGe_uEud|%D+yF2^?%Sj7>xPlf@5N)bg`_Cm z0<pdP%W$?a0rSodc*xh%wPE$;KlHHbCI+aPwIivn5LRubI51|R%<yMz%{SMtS0U3; z_q@`NRv)be#26kmrWIG|HFzuwIcLZ^$sSop!W}Z|baa35SzKDeK7XYp6e^E$sln9u z!CucTs9Yo$+k45JRv4fzdCG3d9fEl?|BTkdq?O)6|B9ha{gK;{&4flA+GVlaNDUcd zyB(2~Xmn}GIQ39lNPaDuf4O4oC9QVZ2RuT$Py^DU5?14a8?RgV9hHZIoYQMA_hecM zVO$UIA7_tp1MU*Net0nWq|qZzV7+vSlfdhH2-{flP@ji&?Xj@nd*2(cKh{(#M!l4e zWT``t<fN`d%BYCue)6TISkjc*?>p^0I*@&ZP4T1m0Y}J<_eVAP>mB_VIs}qK_{R@- z-*D)aE~TI`%I$=0%U27VKMArK1hVUP^U`gTl6+O+<8j&;cuCIZwM962wfnWAF1vB! zpw$)M=RfN%9@}G5D=AzzV`ZsV7E<TsCkj~Wh>&xoBJ2I>M;B)}lZsrx=n3eL??mF^ zTYDLhK8~NL9bCj%3~U?Y`|vPr+g&PumtQ1zgFoh0=6H~Dr=abx#SRN+<n{Coso%gO zgdqwPw3=^b@Z#xc0FN{#;E}>k86Bi~@lkuO*2!BdD_w~c_{5GWwk>Up`mfi|9z;76 z;7McKnj?*Z^3w-;+Z41KEl%N1o_8zt)C-noE+V-V>wl?rj@Rk$FKO~S-hn$c2K%A1 z@~Rkg*&i?A{N>>c9eCU-$P{_TFUryJo}eCocdbs!RIHq6YwSyqDZkpLdxG`Z^xWPt zW{x|Nm~N30e-qzBpEC)}J5T(rhf8EwhI@8bhBO7qsDH~uarLb(8q1ziGICzZf>d<P z;n#hK%#+S>7f}&Xr`G9i!uFdMOxxzy9#ns^ZQFCS-q<4D$eGNXq3?5E4N`sFFhANj z@=gCBc8#Xa3%`M+`AdRr6*~EH*8N78A9EgwUcW!pG<Ru7$>MhkTe`n7rEyNZC{AtG z6~}RlOJwz6&z)9hAHTlo@lWKHTAfSk^>UtZ$wz~Y*~lDFAuy8<%weZ1m8kP*uwH0h z^Xem4pEXQPMGSv8%Oy(3VvYVaHdh}udV$AtTyc8ux(q0=F4<iItACDC$lB}<h6=UU zw*ZKx7of2Na~H$e88WpAXR_pQm->k~DCDdnsY*ZSy`FO1Ir`p-50)A^&!&hM_&7Yy zM(RDI@T_&nYY2R$xnJ4r6w7p95Ds(~-IwUFoG@8;(M|PmSK(daT=E=|W7JCZlgiaf zYPX&~l*jP%tMA|n8Sy&lY^O6_!>!$RQraa@rRk!;l?JD_d7%T|{EYz^#YlmF!Kp)l zw@U?g<XonS>0!N<K>Yn%caEA<DdI&k$Mb@0tvpXuV*7KocUjuiDIDGH!<tV|%HS)O z3C~)qBuTthm^N;~2QI%G{dM`Bz$rG-=0=ptNVsO==#^gc5#2qbT32fP3adBNWyfyh zL#YW2D@5&&+UF|b?H$v1zQx+wMtDA~9Y-!C@V(zzKHw#_tI~_hZpmv9jJCBc-;D{) z>!d`#I6ArTJk_!uwzZoS2b7C#_nRSOg~iho-Au8WHwX)`n&M-QJoA@%<i8p_<6VQm z3#-1UPFE}>CU%aO$S;Hs1li=ISKsL|s49!z9u};q9KrN0Ed7Zw-{+|rxZERbfKkbm zHOy@2J8<WxGK}P)5l~)dZtp&1f}Y3*Z1@%cKkiwxPBrsJe4E@flD7#WNG#O&IJ$jV zYT!qN{<mC0o^7-kFh-^$W7W>$7Gc;5vsv3k=luhzlEiz&qCF;be||f?ND>(f`-@$Y zwKsh{u(Q1CdbcXB2PL!aPqvt*`Bj08ysETjTh_Ue=`k_b(1Uqq_*O1kM@=R*`o+#s zvx0qWi4VTjXnlI|eSo-YC-x1Tvnh`T)Axt+GrwSU?VoVU;a4w2#5q^!DCb|W)(2pw znCj}HrJH5E;awIs1UhSch=B<+=Zg#d>Qw{XrJ3qSnoi18?@J{fd$xV?E4lVEn^r}_ zGob1fh-9@f_IQb(6ZJJr0n()I^13hZ<a<tkRl+pSo0yocai#7c`<t(fAA3KCuNM&J zdD|S-A(r~_74p7Q*2LJ_!rrb|8pX=>Y<(hK@D$R_u;>~59E110l$*{Wwf`^<#Dfk& z@wz#BBRF5<zNp<6brAre*}?A~wI6%0cNTUQFZ$?zi%6|@s;ocFVb^~$N?boF<dHNi zcgtw}&U2uJ!V|`0B}67PA_%Z|pq>f-s9hfAd3>D(&-#7?ap4ffgm31zZdung0=aD! z{Ycz2_a6@%AA@)>X!L3TfyOqzZTgks^lD=C*Xl{CludbrZmO0?xrOlu>zfjGn{?4z zF{mfC(av(nbthG%UxN`hX$d&6z9nbO?^a+K$a8BZc*Vio*zFla5Tv1BBe`xkr!p{a zwm1eEEeTXEA&Z|&2`67%&e{#+3ZyMj7_j56DsYXl%)OR;pm~yqi->Kpbfp4?1wxve ze)fL?Wz`%%mC_)otYz_XQ!dPBw`~oU+Pk3G4XKqa8$8^PS<BrFJ}*na24aY7BdVPn z<=Bzm>t;;MKi!JnIGQI@lb1c6m~^5i_QL|5B1q7wco`P&VWV-JB^UM<wjDg66U{i9 z<XDvzmdKi=?izKA;No?UY1<1Uwl_ZwTwWQ^mofBNGo7$10wG$=RmgZTs;H`dePZ*p z_=*K4m4CVW07X(M4`Lw@?<39CcV1D635~HNt3C(8R_SP4iA3pKT5a%e-7#xsiVz`T z&qbdb@jZ7(M$aE$-L^Lewr@qQE2dy)oQ1wuragJ66Gkqw%WbG3u9!gQmiM!1d2S_h zBHV;UM4-zunO!!N6N0!25b9ooM{h<{<qwpQJ!V83X5LSLteD>AY?#*@8SoL>=D}Jz zjyt`eH{|nju8((b&s%pgzWt3Bg~{s~GN_4mE|8fn%QGW{D^^BuNU@v@_e2W)AwiBz zPUfb%;~p}_T=2zyADk?a^Zauo)z5UQu_43f0%`F9y(9Dd$5uJ#f`8Q^J}Y+*2nx;^ zfRO%$5E(PI_{P9(dc&+&k^MgI(9V#3$E_V)q1_pWs4)0=9@J6%=R@yH0=y~)fK>$Y zNq!Z;2-pOcD}dDgFC5KsR#~!yK&Y*8?*;g`GxY!1+Bfw$D_?@>$bzbss~2$RzNT*9 z16u|!spfw_Kr@wbYx|#k=l}W0{*OoF6eP8>k0H%U1?)xOUj%|ap68rtD<%{yL#SEm zMW#!VVSg_mI2r^IW&R(QM5g0^J%xYpG-qt~|7$VnKT2258vo<GoLAg-#$`xV>^bKI zgw(|K0@o(O$AA<p-#<k!%Zby8v)Y<AzSlU~MKt)I^bhzl_{RVJ(QwPA6T3ROY+Xv= zkCD29M|ZDG%&?xzJwtnSWsi9V#?KZkg(oN}+42dTK)#9pp_8aFQ!az00cIt23w>Re z)Y5*;?g5Skx0~mi8Z<T4nhcbtgUJ6??$1$|H4El2N1@*V_L)=FK>A=pX9n>PiT&^C zrq}aZmAoFS^K>qlb#1Bg_4m0vix|~O=`hQcp;t*k)#_bj*u|97N358YdJ(sxS37mR z)7uxD28B*?Vk$$x8(R9Qh5Ltylcd7d-?@HlsBsL4Tp!Ickk;qGtQ;TqZn`Yih>ZPE zUMLxQgJnwS>PvqDNe)`vFE27~p2SsU%2EwUtRx=Sy4BS)F+f!3rA|eU4`#)4K|y8; zY?*n%w-6KMAOvnP-M4Za?eAQ7cL#%A?k|a9pVc4A=b|)|=)3~;+_~du;SrJ8P|hpV z1i5xPIg#Kep*rbGbNUp?fEVUdBy~B%OYFlJ{O^w<r@S9|tBzNRQ#=}oWXet^=g=w6 zBB#9c<KT|B)(n2G!i5XI?Kf4rxvyXL7KdEWB=Qlu^pI#m<WEduM>m6=_+Jk@4l#oJ z_qo8iq)Dq!=$H++zK!VI8o0p=asAd;71p^sR1s6Wgu(Dwp}1@ao)^A~$Agz6&oS@J z0<_T)X{y?eW+UUIDsD?*2M*@)7|N^*9aoqwMKH2&HJ+!ZUsDqM`uzCG!|F@p5|Ir# zg;>u_g1adJfdTWxc#xZyIPsNQ6yjoe&9dAiQXFV|>}ow%Qc_vitAD<@z|D|N_X$-Z zr~3=RVG@j{3ewi4Xr;ynHD3{S=-J!c^oMnbLxD=Og5)O1T9-h2^Pj&SUTdN#H(w+N z<Np2KVx?^9m*vAtkmWaf34D)L$p|xC>P0vjMH3i%RKyGvBZRGdgj5DMRtg94DI+3_ zgB-6xGMTemo<4v@BMx(IRS68!KPP()4CgMd^AdQ~1SC#geROcFQ0^b_dt^6C-MI*y zA-pm%85EoSMYN#_55lUk_9O7JYgR)vpO(e<dplF(q&ppvv~}8ER3*%eU28m^t9S(+ z%PU*sR^&$u0`ZNW&$f&(0ZUnq8&p=NJ+FbqHwPEjX)4<IdvLGGM#1SJDvhgJe|Z$9 z5mGj0jknJ)MUGtJzI?@~nhUg53$b}wz3T2aWYva@pCV}DNML>R`>P!z{4qmN2LwX2 zxX&HKWnPp>KcF7SYdP4%(eM(DZ{7OShj-8yDh*rhcxiV1uVsf*)w>Smbveb@#FNSZ zseqWrplhvg2Wdy*jjvg))Rm^Q-mNm1y;#joV+_ZfoT6fRE+e!*4(hH4UVEQaPAaed zPT{lK`dG3!FxI`i-bb*99frBhUkVbQroCq@40X0%C+vJ?{UV%jQfC@lCJuR=`!1Uc z+R6VdH|}PT<CQE@`{jH>ehl^?y7uO=CjG{*p($VGZ^w(~g-DLEijcWpNYg!*#xRcr zr+V>^rk(^-`a!vMY$J7JhN8w1gpi`%4YJAjJ}&NsZJDl6?Q&S=_kj#87di;KHK_YR z?APj_TQi;P>hrlc^#h;IpLn7a$y@&^bV;VNHX?+0Fh0BM3q3>EAyO#7b5ZV;HNe#W z^XTx95=cy=1>$T=#w}AxZ{fV70;3Tb0CTuc#sB(vsh9riaz7Sw15!98nMAIviqF;- z(U9|i3@Eo&-K$z`o9^^@D4X&_taHLq^4Pd8AE)Bvq6yrmy&`6B-%Lu0G$j8@0b)dC z+^>BA)emvgtbfC=wpW$+yt>BphZ994x&H8`#3ybQY-%Y5^{9AYMgHLL(39rCt3!XO zcqqQ#*>KC3>yF9u>~qu7)vA0e4$<KRJ><Zgl%WGtRn!qfCA8rn-20HjT_jD2kAI2j z5rf9@Ora63p**HeUF^b#Fb3Bm*^hrLUn_ZOc~=k)QL$F6{{Br}o0V1dBDVq6;R7ij zxi75`2Oo@6QuTvB1us2^+XbGuPgspQd|tjzHX);1Xt`mFjJEu^vxNwRHK6Ztuum;y z9eoES^=HeO^YVab%y#NB*~O^<r-v9ZUjkprFoz_;M^Lk|J32G2*9>gWTm?)5U=F7G zp<bB%(a~*GHCj{m5&Jg=;&+p>cR#qLT*djvtoyGRc2v#Z5<v%6s3TVnXAa9Z(d<wq zsa!tLE*N_8h=yMA_?z=+8IkYHn&XH{ArJUu<92!HE%!$6ix=a&(onX#+6xwmdo|az z$JkJqqw9$D&vk6A#=;?{ti#J3ti#oU6G!n$A=D}B7KUzgh(E!|r?x-dL>&w9?c%wY z82Q<`Zrj>4gbWI{Hw5)B^IaGca@0jUS2DHQ1jxVu<tR8+;C<z&pdgyOzA=PU3Vi|! zP!syoQgkJZg>{$Olh~`Wu6XLF2lel=g5@x@T1ojzbhFlix57%^<X6JxsJd`IsX0wt z<(CCEaw(-P?tJ^Ba;W8WY2LFIg7;3LFQOKX7>@5^;x;Rc^YscXQ&Zik5Wda#dbGA- z)ia!gO7b&1_IXC3eS^1xt+f-k+B&p)BBJ8IG&3YCt^VLK3Qb(YoRq8_MOF%152xs$ z9&@1f50k5??G5b-p#v3$hh=Lc!q5Tr!^Xi6T<t*ZnyXheVTsn@{W{tv`C~Q8)L1%| z-3m^s>-3rI`Pwxq&{S$aR?c}dXga(TKDlO#^vi^psOW_knx@Mq-J1SusFOh7&id)( z?(pZ@dNqp;d4KiPB874pPceMomjRYE1ssea!}OE|3I;a*PdrBopJ(>w09$f#+)H1* z`-44V;3T0?wQ}SgbfDA9j#aR_hvr+bAqz)S9Nzm;Qpo((t)CyV!)Cc5>QCiV^!}Qr z^jJjDy6{Eu#$o3Odm--2;teO|8`{99d=VPSV04{mdC370dDT~#6j$z2AHbn&s|M{k zd750g{oX@Aw;^n3Bcc*Bfrxt$q4q4Psy=GF!2m@jM4+EtMs=KPYKNKeAEXOy|NZ2M z1tlcVa{C==qG0L>qrUy85#Wa~^J9;o=@A3%U90UHeTb->!qNTadu#l$3vMBM9i=;y zf$Fv9gFqjtTns6p13<Uzw*#%JyOBS_O@`c7w>ysv8X1_6D-!iMvK0G)Zqag1_w48= zNQ+#j7x8@c9&7GUeL7mcAt3Ko`+!G`SX?~jX_LI-e*VLV`Z^oUiJZp%;1-xwWp7xW zx_cLwX@}V{3%eTdW<!;Rt|m?m_fwu%9}4PPE9%d#!_jp9bh#B_R`9W)?jDz?+%G}9 zr#X%RdfMCNr7mVcD~B!ZWyimYueO&QE?$9IQKvp6UK=~msp+cg+aE9A?algYVUCiC z%}}$No}b062W=D&F^h!5Jgl@HPh_sGS2sl2IhP`MA319GIsWZ_Tw3kz*<G=#W4q2` z!01_K!sBU|CwST(e2&R&$ZbR#?Z3wYIl}hrO;^O?9)7)g_3{@KU0&{eD(Hdy<a=i- zpG&#!+Yc=-STFR!5dn#(J>{A%9+LXprtc6vh;lfiwu4GetOd!|9EM9`<axr(A?L<7 zu*h3A@^<grxMlBA{6)U#@Teh(OmvO%_OOB}^T+r`c_?iLh#WfIyS=a|9j*J2J<>5c zvkyQj2b({a21K&JVasp7e`wP7baDKCe^U#u+kswaddu}Z_R%n$IdIzEt<g|9pl>0o z+*Lvv2eYd7@MzHlsgc+@HKs}vhRz_P9COBBTe$IknLV=hiY>(j6ogeuKpZM7=!{6Z zmJ$=M)9+i_MA5lv26umSI_-EYxNi>|sk&(9JM^ULc=R<c{_Zc`HzT7^T({L%otx_5 zI<Rh0!+H7H_gbb0^~bF67IVoijSQ%?T5bHIu`p+!ps*EO=VJs5XO3QlUr`s-x=JZ= z*|~801uv@x_S&7JKTo1v-x5bDW4H^+4SZ_7z3)}3eHK^U$ies2td`q#Sjqc6bq6(E zoM5MOI)3szU4-eUeBzvrD=)yULK50653UL9$nvy>n_A9K$Z|n6+IZOo<_cZ!N^?dE z?-n~>CBAAf%GD&lscXq*pm+C&^)uDU%2(rS#hnLGxus<iS*kH^laZ+{y#3VO4!XbG zG-vpowt{#zM0A#HK#a=mIeXprQni}Vyj$XuPVtZNir0CGiv4@t+ocSL0ux@MLKvCl zkG*6lx20*6=X~^u<BAs4mfe-W3mqu+WcTDi3quPRXnm0U3dFl>L&ZSPUMVfv>d()0 zcc<7$BIFJhlpA40k*1aQh?qv<)b+J7m5!<)Q8ObQW7L~e7=Yb_!KR5`WcPdgIOI(= z3sko6X=Urm9!o4e1oJOvwJ_IHhnr?&KUN_y8Zo6?X&5RG@82qT8dbmX#bdWJ6lLi- z73|yFpsBCsUZpcXH||Co#R%eAehwLfDg}*MhyE*u{#>bu5#*j*bu+bB&HMA<{mU#k z?qn6Tyz%R2!y{LMy}K-p<u5?c8=8^9hnG-D={aI|l!mmRyc)QZYid=aJN4uBPLD9U zO+1v*Wp0i#%jkf}>~Kgrf+WYNN2^|Mft~-6?ohK^o)L$!fzM&|=AckPYg}Zpj76Kg zquM~1Fh~OQ-EjNh(p^`l3F7`PyT;-jCp(OzOr@9%)0BkA-KETruA%DtA%3A$Ijh+E z=xLTW@l6jRJSaAnz7u`I;dI;LAqDw$QOsZ551kR}E>;uGETf1z$N4H0&+>E`%kkk( zVWQh6{t%=4Fs1tFBoj0s#KLTr>GbwYe&xweR$WdoMIW0v?%b%Ov-*TeHO{2E)(0>U zuw8D1jg2%2;kt)|k~ZLuDitxH(Ve4V>Q`{}KW}hBq0c0Lw+^NU1Y7`2TGPN49fIM5 zO~c%ZbrqGdAu3lIa0zTt%`|MquY$KOf?awReHe}hF%$pyENWIdNJ39J^31)hM!ljx zXK3U)9|DFZ=ZeL@ZSnt0JNSQW^cA}BIq@M!vj5M~Aph;`=KtN%c-63E)i{_F5i4&M z6h<2)Cl>jBf`2z8R!zcCI`@0IV0c-+LA@B3D8oGXt9JRgE;LzzOrmWq8}nUX{|i&R z-worao^@L7RAYvdoQzm*{v?N>c`xZgL}4js#iak>QT`6L<3Y6mPWrqhAh`~t|L^nt z2Y+%d+@Ulx&E@WC>&0ry%y*L~?y*k|0{)D(!biM<7KEdc3ZoL^Ql_R}8yUOjH}=Nm zOVCl}59wudJJ7^vu0*&D_VgkPe%d&_p^<R@YbN}uLpRygYnNyBuYfv8uAKBOS2X$p zEuXzYSlJu+lL#G_Z1wPimI)M<5Vn%vepVr$wq@Rm*L3`+Jp~2DW+7mlhHk@^_H`R< zv(0(pnyI;!*VlCt_-w>`VtH!cd|XW&c#cwb(b??4M$J5S$`y@c-Ss?ezbE80X?>Co zl$SrHleN*LF9BY$rcHrhm-|RKp6%_ueIUH+YFu-(c+}kGiTiiZGtXNS#bco4uvZc4 z9*xKbSrWhJP=6HpB(oS#oGd+ueqJK)j*7K)kzH*`eRW~uer$;yk5t%qDqb7xiWX`( ze9Ohcq~RpMP9OMrh0>C~#<!yq2kl#}VQKP+yrInZYW%yJ6E;rEEtnw=p>2$Mnfro+ ztjDtFQ^z0F?Cr`C$MA~laauf9*>?N;y~xN%hm!hv`k7F&#QvlclR6p_zf~+jEdLU| ziP0p&MkB#9yDZ`D@{WnBl9JAJgAlD>rMvd%FoN^oi8`nsnpbt6+l?cRDM3;%9#pNs z7js@M+v}Pn@Wo^biL7rPM7K(d*k2K4l$&R^INE@%?iF{9RS1G9hiA&!k47={csdpg zdz25e*$2}dYFAFVKI_xR@(Ct*VaIovT~0SPs!@6h)_P=_GS&i#w@dYNc5JF>)l;5J zgwQF<*auy_s5|*^`;<o>^cY4Cq^_u_7};)MbQ1hUaL%`b{RaZs5cTNr*Nja!c#v_o zM@Kp#s<R@(IbX1c0s^VB%_-YD1WM648k@Qove|n&YqdD6JNk^e9Oz8>c9Bbyf4H;O z=BRB{XN)4T&URs+&Vg*cx%O9zBsK&%qoTu7`O3&<+V6Ia@VbkMujPJcItw$iLoPw0 zkfWNsjNAC=Sbv@=7fNW44z?7ZoIRKXx8U8)M@I+EFlkdf4VfF{W`!>=4_m5AZe!<j zz`smK|GX8Y_=C667>I1Cxs}Le&R3J2qB}k5xMhGZuiMjj7}41~x`?6v09IQCkw)HH z9ymB^XHss9VYF;FFn_-;R#{?Uq{nJy2b8QJo+bat5=<^E>`heD&}NNX$*6hno#~GI zq`?mRoldh>ubkI-%S-ZkfdJzw-*#DAC1<wFJABHnC|(o9GG~U~&fJ}6vnNUpE-*{G z;J%Ty;}#Q|x{8>#FhrVe`IwO3x|Jpy$6J*j&ABsS(7R^A6Gt#E{A+8w56JsdDAh8S zYu>-#mX9XDZ2X0g)#4JCZ`)4Ym_}`3a*yXnT|TE}DzEcQMxJfES?TCg*w)jY1dN)m z*wA1_gnNExFNIrp{FgsiJcy`jR!L{!4U!-Q?o(8?QKhw-L{HF7|B{rFL0CdL(C`Fs zfO<N(naxUjz4+uP;sj$ui)#4P;EEQi0UwGGxbkV?+%wh%J_hE4<zX`vi`F6OIwCzx zx|ftZaXv+J(D>@Lyc%&2XQ4ti?XtQ&8;uxGzm^m-#%&d#e=~`6D;TFxy5mYI_6WX7 zQlKHa6}wOASdm(^sg>2<R$-TGArMc}#<+^N>bD4l687e0Wmb5=N@XdVu#l9gRpgr& z6$TV(Yf`f7{mvfD%&GI%7^Fl#Zo5H6Wwwte{<53?`apY-oxs=LJyEp^SaWBES{r!T z!LrldvGMZ^gs2fosH?~L%wWK!L((Ir+WHUoTJGoCBRidD@}KFct3EDVSI`Bo;vm0$ zLvv-)yp4}tr*3N7usc^y<I49X!#3xmDce796Om)BU??1gK=)y)5j#iVWV6f4jq*`* zsnxJYP|Nz5#Ni7(SalvrjnUyK(Yb8WeZTrz40_{}K;JkoHtAMUn&Uk9FV)UO2|p0y z%Zj(>X#IAQSm&IzY<?tZ(E3H}SB{#4l}>A>cwR?`>^rXA=+VwXm~=<<7~55VPAdxP z)NsU2u2*q|Rpl&sVMMj|;SRVvNV$TjW@|4;w_IDW0J_N06F(30RI6a)xc<NaswVcs zxgNcxvfytE?rps7R)<21YaUi(X(EY*aF`oylgg1x(d<S8-WxS$q}Tm^EK=9z$3}Bu zD?qSQ>Ka&-N=>{50Q9H@J@9T1VtVeO+mXTOc;RWzO0qk_yQZVbYh4wvx$+z~TySs< zfhbV0*RK7TwthEf(q&Z1``g0Za<?X7fA;%^U8DML5?(NBLKNs5O3oOk*}Eu=SC<74 z?XM;zq3L#?d*}UAG<DP=@JmDMQETHNwRL0#B(%a)4%(xEI!?B`Q?6yFO`&?bIFZJ= zjL+^`G33smVJGPWUE-dlca?#HVE_VJ9`ZDv08?X5vGWFQpk@u>L`v*Bbr+xDlGa!S zd%aq@*!eyH;+11P8lgY4qNjfoP5*RCF0@N5*wf8BjI5@{=d*f7&yGe%d5MK{P+?Wi zVOW|+CGl^vP@yHIdTDyDUJHTjvPS35L1l_tw`R9gYl`2hJO3DY<lfD9Y%pYKSt%d; zxOGkAC?*0J#pxCnwNY^)_L+M{*UE+k^5-7IF$P}E&Pbv)iW?Acqw0-cb_Rdcx4%E~ z4-O9unWHU4hZU+-;42*VxSE6@EmG;^4BtTZf)__JUWIH?yIa;{81-TTY>Y`jB=?V+ zx2$yUf*~?$MnP3f1-RqIs{-hv|HS(ZRX*OyfOs`R<s=Qjo44U)9v{8Ew^9v(=Vbxi xxREIAx3;#PIB4vL{j#U%!@Ua{_meEQ@2TEtDW!J!%L?~5^rhU30`b@0{|(hE{ki}E literal 34202 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 A1^@s6 diff --git a/docs/resources/diagrams/tf-a_attack_tree.png b/docs/resources/diagrams/tf-a_attack_tree.png new file mode 100755 index 0000000000000000000000000000000000000000..0ade8e897556f15c876243cbdc1b61e1de7c156b GIT binary patch literal 37881 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�ALkqVXOz}iMD%kVD^L<m zy#v62W(7!jYxms!f-z0F8o8mtdt=?n)55|1!Mk&Lj?J`>JMzQRauX+qmVI}qM7;yj z?22^u+Nmaoz-KRI&9UQZAPsU{E0SpN?JQBDmwZ@QIS({Ol5gcUd)_k3swv0a?jv-3 zMQj>qNJD;gZYaM#T6A)tPF}b3z9B1`v!?jsq1HIiL2$+QJEYtTa)zdLQhlY~*wH1p zaPA))@DpY)LdGZvzf=9Qll#BLKNS`MBlPnB0rWX1jT+2TuJp3&h@q18%vE%zmJ8_H zMv_gl&Nw&Vn}09rl0BOHK3m1^%5EpMYQ9NGT$t<kJM`^AVa|nLd|v~O?YD%;=S0>& zK<UIQ>NxK!W2G?M{Ea2U^asW*z}awbt7+nkTSldFnVSl4>jH%ZND{l>W|Qo<8$fP8 zJeHkPil~n1AhER_2YjnbhnDZ~iviCiDc(Kt^9b<ipDpPB7BkVj-V)6#)YI8+9{-yB zt03g*n?AD6#3wump*Lm#qQCG%ikNg(Yd@OyuU_lx*sD#`JIKqpEk{$y*Fe7o8W`vl z5&*)h{hgWZR0rDs4Hd<pfuqea?BLJ$zC85xoC1aEn!6U28H3i^a8L>jV9rhS*2r3K zAo?LQm?sNApOc-B^>ddq6NIVu-}SjNtoB6vRZ0hQartF|+ocn4p7_b2HmuZkeLmD9 zJcbo7SC-Tiom$ws_Gh|_JfgVo9=})<I01X<>8<zMb3Ee~;XZJcR$Nqg^(gUo*ND=* ziq^HC63ZMCwwyRX2mg1^M(jK$UO~NEL`zy>_;Q&cAd^)_$cJpHITTf$W|w&fSNv<L zH_b2u^=oWLSeWILC(-dd_?^TYa3FqI@UwqSTHa99i%Gz6gL<7NF;dT3?lqvx0$-Di zy<&p61&7&d>8^&9X_ISNQ>f(pxW}*C^f7^Fi5a*-&u4hXi-yeF=~M@2rnX2fuxXB1 zzhr5tD>YA)J0)PsU)Xy3Ow72N0fOh;DF}N1<hGIu)-&n=D_}#HKHk8}LMkM)?Ay^} zrsM~oN!e*l1H@cQqpM>=pR0^ccir^8pnjFDpZTZnMl3>INwCiA_MP9BEnE0uPfW^@ zWF$$F{+??Ae==~BM?>9?jwo9VEV}exJBstqbMWl<A^l#Ha1=dkWgDCBYFjHsrV549 zbaF9iYvK8QAI&7uAsFt7Tvf^+8z?^F!Vuzh0KRkzMK;+%Lp;^G8*~<CXNj&ym;giI ziCuTqrFy_!Uc#eSJ*r2_Dm@o^UgT%#$F7{Ij^T#y?gNwi#~PsPykhNSRr3!Q!x|Z@ z1ukmZ+H(C_H1V_g=oFrku5(e^C)vSTk9;P1Zx4E!-ORb0SAbkWMxS9CUHKr@<J~K3 zXUKhBzy4X-M7Z#auH3x?F~5HO{D&4e0w+?pJ^0ahbYB~8&qgXO&Tp}H-Wy@h_Ck^4 zEq<f<X{&yrx~9h{;t@XqI`2fzH`LlB7=wUp3bD2XAGB)|lM}7Wg(k+A3ni@#wQYFm z-`?+Ct6n{gpQyn8LEYuG%)cREv!1g&iRa0Y<FAi8!j>#9zZk;37Jr$)lGimlJvQ(T zD;IQXQMz>R&CID@QT8B8w<7^3%+yW%8t@(<J!}iJeb;ZWJrdHs{WZ;eDUNH)r;l)Y z$87MzPUPc{7i0IJproGnsXc!=BDz3Hc_r`Nk-1N`Q$x)qBALtupxG+r!wg-y9#g<l zBJ62jDXT?yP>LA(N<6sn%A)&^Yk0-kLLW{ce`pnXcQnG*buN*L)s?E~uZPF%A(dzP znfHE~?)Cwbr(*4)8}p6t1_BQ)h&OJ`O4G<at~#DDckv=lHlxvQaFw%8h1}4sc!Pa9 zoo?Kir8c75#-%kV(EUAg&eM&Wr~Z8$g^Tr)kc3QC_6ElSAx;8JeFx@U9CL=pl#_k} zlO`u~0`Jo}o6#ezVk=E77hEi?p>MJzRN?&sECuu0F7nX9-JSsvlm#^1HqCd+ehEp3 z`G0A$xUsr`0t)}Wg!IeQ_~l-uf#BU~+Q)~<?R_I<_Hz$z#CXvJf)(5Ff)=G0?8g42 zhgNn|vP=|G)S3=>vz#ew48$(asQ+;A<oF^p_*quL(8>cqZ}^cdcg3NFEs~b^jLAj7 zgybDhRU`$NYu376qq$>Zm*rmal42uGxCYvJ>|$xvgF+{3ZsO;~%$2duwWyP$bZUhC zffq&diT0%&P=)Li2W5VCDAy=X6vwjuQC4N&Tm=fSJQzcUY-?71&5pVWDBOz8IJ+ZG zFDR1B)P4w0p`#B|xoAwZS7Ev`o_0{+NuhJ=AhPh-^k2CO!XD?l3b{)gloIMBq%g@< zRekcH7ZBkju(?SKAJ3IgHn9XjM{+PKH*E;VU(n=!D|gB^16e<A$B0Zgl7yHv%@{Sn zleG}npJ$YUe%~J%{vu8xa6V>~K?rxS<pc08imBPljmTuHj)$Hmmo>R~C7K;JXC#Et z*MJ8b9CvnBJ$6*rf%ACIq(z1gMTT&E_bo%&T3fFmSa<tNtd(g=g*VJqz-yAcg@_=a zAoX`_AuFDl%Vl6W3kagH`6ZuAlqYZmsC@y)Mf5op&5T%0vY#*52bK#$E;OSvoHSL^ zl8g`Zx`c?}`>Kp5{P?+Q7JmLYxqZ~XI5^?X)@e-Y!rQ+$J<ew|#$(!k9z0p3{;*n{ zBgEaYQX36nRa=I>%DwGGcyxL=t#`2SM5p*7RAmGRdWSYCM)L)8<<rEbYaLdWfug+x z3FnAWaFw%)&NKNmtLZay5KE%xW0&aa7TCcc<HXwj_nMkw{NBEzdVh6jGVClCqP{86 z=A!oT!-T{-jzWi=2!E=1i1IiX0f83_x;jTqn?}AQxt(h=!*FL4sFvi^gIR@^gzivA z2Mx#YB0tO~r^zSqJa@@jZgYepRK_@na4lRTA_bK%iW7!=T{pdZJWX>(2uqw_1P?p* z+rj9NnApk&I7To-;~lQDi&spMLV`mof+2>sLGDnhxt3sCBFJd4aXR<V`2t{))W?69 zY`V^}&O4L9oMm2M7a-y<Syj*EvvZ0nTFMOoThj&H-(RTrE;%b+^moW6rz%YHiYQ}v z)k`KtK?3RP>+fSb4~BAvh_&TO_)TLTCU%GfzZji*jAH2LznlepZ+HAEzKE?OFc34o zrGCO8CeVsXv#!&pnnW#^aBiUF=2t8f4C^~xj`m9n8(=8oZAkf&;|6v0gyX)9yp0>0 zi{x7{A$gjEhlIQ;x~V?(KqbC~?;#dym-tKe(x&SEr&fy}`lFQ9pNs8B)^My0(cOyf zIT3q_?IH>Uz-S(zPwZr|(?q$3O5C4&l^`rfc6)}$#wPx{9}uZdZ_Iw&D37Ys2+vck z-eQdZYB;_*ZsKCZTDadc=9^ORDN~lzF&<bh8T_$TQ_fo-3=B?zc4ji+E-^pA(VzNR zaXa<+nwf}=AKc}?6?y|5HVUjs+e!R?<DSO=afsE*E^*jhqbKnF;inWOai$KI435fE zpc1lmfHTQxl`=9blr;I4iQ+cOlH6eExihXrj@ZpqrDCaT(?Av$$|LN5mb?utW}a82 zkZ%AbkU|7R?6zL>rnuT0E3Fbe0c3+x^gvwX3Sao$2%Og7J(y>tn)2qQxQ&^ZvC$Ot zux;KrC}=0UjO~?W3pkjA;bMy_Hsc5<LCr=z9C(d^AEU=bb<9V1`iY%C9eZYXJcEPb zd2juf$k|PY1IB}uIE6<p#AN;#eWWOreWCuv*p(S**p~sNkjl5;;EL4HTHQofcTGC~ z$hTIEJazA;NEPd`Wxq0c*F;!#i)+-p)uEjSTMhr*P2&QJroTw8BObej>bSr>rxQ;A z=NQy`R5-VL12Nmz+bw+iAFP(eBr)sta1SFle9NoS%f_3OJc54S@MlcJYu&FX2mn6Q zDZkhCvU|b_W}1~r$0I$!Q+FbiKml2%aes%=vxEqab4$9~Q7CBABpP)TLmeHMx^l0h z+qcMA%QzcUtlKH+Fln-ylfx{Ukd>jB=se7vokJ#Oo9qFpK84ZC@yXG#D~gIVh4M`O zsvI0p?PMYT@y^Y#yH3;56-?9131pj3Hm+a(?c8a$`0sgR$cBeRRF1LS50xVuVM<lr zIMM-Zn+&=brZj)=P#ih8{{rDbxopO6I>cE}NDwq(47F5scKCPR_%LypRQv=l+a^z> z=(W5A^;6``Eq0MsYNvFt$xkrfxL`jEmsxblf-E%t*p`0sIc>$zt?NR~`2hMw=L^LQ zRwDpV<N>T2uZ@5_UQ=Zj&|C3yzZ+yV&1fOjMqIhLcg99{3jja_G$R{pg~0ypZRvbd zH~fxs#w=JQ*Vd;9wrKz8{YA&%`HBaoWteg;yNDC^>H4m~nnGrs;Z54g5xLX>P<|wG z4v<af)^dHil0Q88jMrg=wiS*^oBBN0rhduqb4*`nms1y`AEeuubHRQ!E5j=}Ob{tY zLT3@;fuS2YYN)ML1t*E_8nt`^{67zoZ7RHaW*AP3bHc2=dj=UxXNi^Ynx7i|bR=AF zV$L1C29Dr6f$&DJMR<}<8yaVTY#A{t<)x~ZW0|rc$hJkjcs3yr^Tdg8_yYj~C!Fbe z?rURBV_m}u*;XqQ9}DX(U{~pIMu8U(D07qNGVe`N!x}n@n2rXnQ+R-AD%B9=e{Kdy zWb1Hqm_SA-`ejSbu(jq8doYQ3Zrch3&F-w2wcPPUBQ5@gT}CX@Kp`Pq$8a8CPv;1# zQKmDygf0Wuq=HG_P0@fPFi?i;rz5_DzdlA)CK2^RLyBC@g^h94KmXH+BJPgQWN2z{ zX#y$?|H$ZL)}t&Yh1QYJ0`kPax+OhRoIx%QTL*=~<x2A*2N;xkHEL9P6=twb5hDWo z6+UM6bT)43Kx5rm!R;H1{*<KYCbhRIn+rRt)-Ua>jwQnX6|=KNuzcIaT16aabS(g0 zv56lx^cY~13@F9gQgu{@1}8__J+o^9Lv&Tt_3{SH*JGHmtLkeS>3W=6<AVkBNt0vG zR@R8ePom`%y#TKT#{s9sSHR>{|18NutqUWL{p8S_8JId_lxAxc{6uWkrD5B8-dq=r zUSYxw*W`#o3pyQJg$L`uQ)X-CZ7kmM{%lx$KnoztIJx&1`2wfVa%!FJK`ELpZ1<Up z^?|^e?t|O*$rV~C3_<yN@o@qm2WCM&!x>4Lj(-griEnq~kYu-PUYU2E_8kIx37F*I zJPEY;=G{z{IQR|Yy7fn>K>HOdzvzR@5A^h`@cnz43-RU)V*xBJnB#kqD0J^oIK+z| zR}DzMh~GCRA@IRqtN#$g+(D#n>#C{ES6TdWNNWv}IwqUievC|u|6(XZOdjrp-k%s~ zh&Tz)%jJ*<r^=Fu|M(Ht!tj7eak@;U19I3^E}Jjq&C4I@$G<{$@C+{CJTJzuBNS>r zZTLgQL`O~iBx_MVc10aO&rex|yZ%O#N0Bok)M+hHlkp;0C91XW!S}E*f){a-eNHZR zy-ontQ~P$*!4ZeilQlu6r_V|8b#2tJu8ms>tA17wDq3AA(Xsk#E=!nZN+$ylU_1(q zKC9?-(a_k|6?ss(UgpJBy7mZ{(KEtzzd>_b1x;him|%QHCac#o@Zv@jb>7I#J;}$` zmD^Up=XLay*x{Y1MEx!>%Pl;1G7)RM^eb>hiDVZ0G^7-pyH;h^Vz^wM`JB<=W+njm zFKl_vS|HatU3N=N2vDl14+nD6o2JIKZ;AK7rKz<bj5BJ05<C$vw4}iB;e`W*s-1VL z?eWsk4}*Ei!ETHD@PEUPLHR7I$KtAwO$GhVwPfGGWLUh!{r#*z@OEl%h+CM_`i?K? zi%MwaqzH(eX&zStu8mjq@(Hr$8XXT0FQ%Pf%tnt)-F#x)Y13tqiZ5I;<Mc|7zc#*L zOf?gdNkun=6i0ChEU8*jqGn@1-EMIKv$B!u!l&-W9~;bp8-s<MXUyAVKHLPjsS-u{ ztd<9#1X$93LZ3cWM-$^>@ZpV=#bnhJI!h%9N|^e^?2+YJG>UtF#`!cvW!pqD!L3H> z_H{iNU)Tz+AmtSTZMfko@TO+nne6+*qR-nmBg3h1x;m7Op#(hq|IZ$anDdzNrSgAa zwm0hUVIexjBMEair5%a93A}bXT>U>K-3rFGhHfoKr1fvAnlQikmXrz+6n|W{o@5jQ zGdRhkK3g@TpBYY(F8@V^|GC=J1HIr|3Kfw2pbN85L)eK)G6imXTR!4OowwD%#fT^z z)7}R55(K9d{V#M;xIL4S)UCi9FfIh_L48wl|MyRW?^70Pd$(;BoI-b-Ao%t*^Rr2d zKEy+~Q~OpK-pL=rrE>rOo5y(^W-Xrh-=%I3z*-2TH>gmSDgac|ABqfNuYL$I{qI_! z!k-nlBa>MF3!uI@PEc9q1+8=aU7*n3t#o$P6C_^;3dWnfWa_zda{FVVT4kW6&*Fah zv_;U-o|x8=`15kSK+;=x_1O)nIctU%zefo`@=#PB2+<-rEP6M@U_ZtN_~Cf^Uso9- z^NtSfoXy8;<|}0vw^?pbP9e8N;e4eAYlBt&`WueBvlAP_gMQD6aNP!HcjGdg@S*op zJ+^?&DmU%d8>F|ti|M<2&#VsY5j?fN`Eok?&9*@Krkff)omte%3aP7JnsB&-RYOlv zxT<&o&6Pu*pw;;oRO$g)nI%K`GdGLEB%|Dfk*)r0jD}9(g&`vcN>w|l155&btve}3 z4~&IQ@f;S1R@NgHSDs7bM<Z`Q*z^l)Pt>g*2sTX7zcO9>7+FFrC-3i#RLK04-9!B* z@NHibBk+0QdY!h^wl=|Cz|*nW*g7Cy=>KW!I^&wy^5{eM$$}_PK~%Z|4+(-0LXoOS zuLeR;+M@^<5h6;6L;<B31VWP{Em0r@4J0Bc9hM>}4~Qf*l@I|XCP*OC(dbTi@3*@j zc0SCPnYq8;opSG)|2g-ZV!iB3F(c4-6?t~(-;4dp(`uOUd3dpt)JNvUyQYp*;!x!g zAGL>Waz$GZ(?6z)Ky$iV!HBc(`tPy;zd~Kh!hKK)=GHrCF_YApqhtj4?CBOY3K(iw zmx*)sn=W%|by5D?z?&k#1SwIl{N{!#FVV~S-Q1TO9=uM4dZqL3u>Hf)D={a_4HSN6 zFWwm11g6dQI#ZHmF0L!*FXiFuf*GX^_}q%5k!C9!14g(!o>54*m)1~;P&^aCkaL$G zNk)Bf&u3vkUOZoAY1yaF=<!wU7t_J_7{1bB<-G`)O=->x<M8~7=`4mv{Pt=_+pXD3 z6md*Fw{U;I{xhg&3)&D&eVZ0mb>wzpiq4{(lV4*jtB-0x^<jHOx%rnhonwYC#JjwA z=Ut~RMrGWLdt$uUHt<1k0=8L8x<5uF4fMYYO;=rm1MEeV*;<5@e!QPe3mWeQWr6TT zq1wXGzC=A-TIZJ46F?HtWgYn$@x7XMoLd%f{Vx&LSBS(-7eUF4|Fzpw>kt!3(tggx z@2RkZc6k;!!1r1&Q<_2=&T|VXAS^v!X?D!=e4)m0rm7G9u$R8eaJ<z>yxf5K-u+NG zUfzS_h_jFE5a=0balS<XCgdaI7mI1XO*x_~@JL`y&T_;lYh{lx!%0cBY~ff7Es}8< zHe-0CFQCXY1@p6r5gL58py}rYTb*4FSxhI%q|wxbJpYiS(Zio+UtL*lA?)8UTME;& zPIH}{Y$bdy61BS&CX$X5#C~(3(tlk&2k45A@@fnaH2Pree2UUxyO%|*BT*7RoL2!6 zo~pkBJ211J=}gla{t?xnx42}Bktad<i7C<cu?|P3RtMwt+XqFTNDep=(W((Fq!Y4a z<VKB7d(rx;#5kkOL5E-cF#3@NlfDMK)`JE9RBu3~SS4aj9V?v%JeLX4)hUq${}uDM zDjU5`$Z&5U;O;#S6;+5F#KhsaW}9}~r!7kH#(-W;LEro_Vf3c~kKf0EJ^K(Fyjbdt zoCTXjX!YD8fUndOH!><U@TJqgvsPgzT(bx>MR1He@-^xed46!4VvjZ?g^!~7xYDW? z9TziT2=jE;!wsBiLGhvQUhcJWsxXn`B_hwC`2j!Juhge3&eQIBjP5$_=1p54Nj_R} zrVa4q-qA`FVzA;V!WCST<>aan=DZ+cjA-cxvVHi~iP%d9bu0Q7zuu@4A*&DVPa29o zB+i`#@BU=W$8dZ50z4N^?sB9~>e}Nr_f{LpS0+km>_(QyDg@cc`4xS4M~IAU21aW) zJ{^t42CiP(v(ltS=r|vT<*prMZuN={oZT`2R`>IOYM$nOo)T@b8LT?Y4(C0LX))Hu zmx~MBw^FmDTcLNKoJA~O2+?IMEFU*7rX)sTzTsAjzZK#k7EB*j#WB|<IW6<Dnrqut zC6oCFSe?c1ZVFx2fg1lzqvM`?edgX#$W4E#xoz1|^9#(@S-i90_R^ieE3euMw&63? zd)9Kc=cNLgH8S<&KFWt#B~X-OUu<^7lnX9b(yN#3+6I;{?a_~LT}r=9m!~=*-?zdb zyqq+)9m$;)y>WQ0<V}9=^f%GxgiE;S&0g{&;7=Q0{lHe;&+3somX~LUqC~JYoQiMV zr$1KFX#7e!CyuaQhrAdhHiL1FndG#JkN2@V2HlQV;=gwuoTrb<%<TxAp7qx$*P9GJ z@=HHiMGq?RsTNL2T?OXKfDX!6@&y18DT%6%wCNBjud%vP*vz3zn8Dg~TvLH9DqFWI zp-IL;?OTAOZWhu%Iypw6`pt2~)$L^d*wD#wGgo!-dH&GJcoGJ`$Z5XSy{X-0)?rf; zDY521#PgdbEqF&04Y(_R>?e&&$hI_y<_9RTzRouKZ(NMVnvmKip5OCuNQ}KzuVb#7 zj8;fcd(aPJ3OYDdZxqRI&97^tu8iQuW%A{f*{6ZxLKHCcfIbdX_8*cK=nXwMt99K! zB(iN&+&@rSjIm=yo!`A(nN#T6Q;Q!1%^jX(G-l7iiw-fSh7~$Cc8z`0uD+^x7s}qC zfa%;)H%<Ce13^uvSYeF8O3<fD55m6Cso%nm7_Jdh|jpFQWqaNoPJWA1)!{BC1r z?e#9>(BL<QK{Dqs11bjGw>)GD9|fdZ0>y}UtsI<yG+$ST#3Re>Xhq>wAE#yc?Vj!Z z7P*?*T8-Z3U9o^6dU+_=6E`R0q_F-Jd+5R>ZLE$Y8H({CRIt~J;vKZBjVB%`5r-q5 zJtdIm_8GZLkQ)0PX}|A5MSz$fOugw~Rm4gEay;WSdpUh)gxA!|Us}7~fTfKBA(gSd zEN)ZrO{*>2IQfkr(c0YYg8YfGmwI&sa<C_BMT=|4HD#GmTrd+TQXXPO3$I*qV(MOK z%NMS^EvMye18V;Sy6Df(mTyw{9soUZG8u8lm=TzH%5D6?3Wxzu#U--|uOQzeAd8(H z*Uoq0`=pZ|8~y1$S(|l#_MmN9r-_}*7W6!)9TfN_P`xyNTtQBde2{c3!z&(SvD^)r zya=-LDd~bsp9p@cEuVzl<wAqlFXe4I`f)rXRdyu=2LH4vhic2Yf19t_%fV|pUqfn4 zZOSs$0bYVb0|y;%xRI7?2mv#fR4{>t&vSWUnfmy-)jE28|KkIiN$Ix45>CV=%D_WR zy?VR+ba^WyykMH}g%efk6*s45aiwZOaekQ|8C~YNUOFE=feJkpw7c2Jm&IWpnRqZ! zs)Pt+hIeTAqB}OMfnZh25{~U5hC*VXTPrmBbCfY074@WYkAU-Q`N$!7USPFlNc92N zEGi-+AK$B~vyoXrk9%+Gx9|GrPYYVyn;jQeb)GZ8T?G3Imo#^L_`-NLY}wku`A6vh zAVXqI^#4bQB+At@siy2Zd(TXnjTLMiFSm5_wTr0Q2VbI!jH%N48i7-H48$W}!&#A( zy8uv*GA9(E0O?Xn^C@3zs*AT<t%jww%bNpKSu#cJs$c7_>Tm!S@9u`+BqR#{hQd3c zz(QW<^zd&00F|C8vHkUzRNbjTsPs?|@nTExK+mZ4f|#fKxspc~2&~QPQ6L2!wr-N} zO7($)5NknzTC_mylppEG9eRHf5L9dku9-T@2K|?V)Wv}YU_h}hdj<Ghg>O)R+(97b z*T)3hwCj||ndjC!%-O1m5MB06E^i%6E`99r<MdWC0JtIGolD$c&K1uLQdO{yAhKNV zAM#SyGBci}{WG`mg>$z4MVQCA?1l!Q2iZ^;p#4L__IEZFwaF?d2Ti+ETHz6ceWavF zwMQcy*R%D7rO&@R%-uU1HcBl-*~Yc;$2dF2*w9b%z0j;-Qj3YC`qx_!6LLJfr=BZh zhXKPw@p1mcwlls$s(Cr&1FWW{Padd>^KUPtC}Jr!3o*>gcv0;C_*yHS%NjW=B(MT9 zOqBJk6D;D70mSp@8lX)&6JjburT`v!zs!n!KP>W_*uM_r_9X_UI{Hmin3zvR8g;M~ t2WhA&q!i-)YVeB8U!D3t``%;LKb2D;W!{;CamB*p4mPgV6sv&5e*^iW6{7$E literal 0 HcmV?d00001 diff --git a/docs/resources/diagrams/tf-a_data_flow_diagram.png b/docs/resources/diagrams/tf-a_data_flow_diagram.png new file mode 100755 index 0000000000000000000000000000000000000000..f65da012785ad9609873e58c45830db2724758f2 GIT binary patch literal 44179 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 literal 0 HcmV?d00001 diff --git a/docs/resources/diagrams/tf-a_system_diagram.png b/docs/resources/diagrams/tf-a_system_diagram.png new file mode 100755 index 0000000000000000000000000000000000000000..f9bb9e95bbfd4e27cd5b2b2f24346c83cf2de022 GIT binary patch literal 191986 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%0A!F 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%0D~xfeZZO`+sJB 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 literal 0 HcmV?d00001 diff --git a/docs/security_advisories/index.rst b/docs/security_advisories/index.rst index c9b0f781..ad555467 100644 --- a/docs/security_advisories/index.rst +++ b/docs/security_advisories/index.rst @@ -15,3 +15,4 @@ Security Advisories security-advisory-tfv-8.rst security-advisory-tfv-9.rst security-advisory-tfv-10.rst + security-advisory-tfv-11.rst diff --git a/docs/security_advisories/security-advisory-tfv-10.rst b/docs/security_advisories/security-advisory-tfv-10.rst index 91dba074..f53bae13 100644 --- a/docs/security_advisories/security-advisory-tfv-10.rst +++ b/docs/security_advisories/security-advisory-tfv-10.rst @@ -98,7 +98,7 @@ All standard chains of trust provided in TF-A source tree (that is, under ``drivers/auth/``) require that the certificate's signature has already been validated prior to calling ``get_ext()``, or any function that calls ``get_ext()``. Platforms taking their chain of trust from a dynamic configuration file (such as -``fdts/cot_descriptors.dtsi``) are also safe, as signature verification will +``fdts/tbbr_cot_descriptors.dtsi``) are also safe, as signature verification will always be done prior to any calls to ``get_ext()`` or ``auth_nvctr()`` in this case, no matter the order of the properties in the file. Therefore, it is not possible to exploit this vulnerability pre-authentication in upstream TF-A. diff --git a/docs/security_advisories/security-advisory-tfv-11.rst b/docs/security_advisories/security-advisory-tfv-11.rst new file mode 100644 index 00000000..b5063f09 --- /dev/null +++ b/docs/security_advisories/security-advisory-tfv-11.rst @@ -0,0 +1,86 @@ +Advisory TFV-11 (CVE-2023-49100) +================================ + ++----------------+-------------------------------------------------------------+ +| Title | A Malformed SDEI SMC can cause out of bound memory read. | ++================+=============================================================+ +| CVE ID | `CVE-2023-49100`_ | ++----------------+-------------------------------------------------------------+ +| Date | Reported on 12 Oct 2023 | ++----------------+-------------------------------------------------------------+ +| Versions | TF-A releases v1.5 to v2.9 | +| Affected | LTS releases lts-v2.8.0 to lts-v2.8.11 | ++----------------+-------------------------------------------------------------+ +| Configurations | Platforms with SDEI support | +| Affected | | ++----------------+-------------------------------------------------------------+ +| Impact | Denial of Service (secure world panic) | ++----------------+-------------------------------------------------------------+ +| Fix Version | `a7eff3477`_ "fix(sdei): ensure that interrupt ID is valid" | ++----------------+-------------------------------------------------------------+ +| Credit | Christian Lindenmeier `@_chli_`_ | +| | Marcel Busch `@0ddc0de`_ | +| | `IT Security Infrastructures Lab`_ | ++----------------+-------------------------------------------------------------+ + +This security advisory describes a vulnerability in the SDEI services, where a +rogue Non-secure caller invoking a SDEI_INTERRUPT_BIND SMC call with an invalid +interrupt ID causes out of bound memory read. + +SDEI_INTERRUPT_BIND is used to bind any physical interrupt into a normal +priority SDEI event. The interrupt can be a private peripheral interrupt +(PPI) or a shared peripheral interrupt (SPI). +Refer to SDEI_INTERRUPT_BIND in the `SDEI Specification`_ for further details. + +The vulnerability exists when the SDEI client passes an interrupt ID which +is not implemented by the GIC. This will result in a data abort exception +or a EL3 panic depending on the GIC version used in the system. + +- **GICv2 systems:** + +.. code:: c + + Call stack: + sdei_interrupt_bind(interrupt ID) + -> plat_ic_get_interrupt_type(interrupt ID) + -> gicv2_get_interrupt_group(interrupt ID) + -> gicd_get_igroupr(distributor base, interrupt ID) + -> gicd_read_igroupr(distributor base, interrupt ID). + + gicd_read_igroupr() will eventually do a MMIO read to an unimplemented IGROUPR + register. Which may cause a data abort or an access to a random EL3 memory region. + +- **GICv3 systems:** + +.. code:: c + + Call stack: + sdei_interrupt_bind(interrupt ID) + -> plat_ic_get_interrupt_type(interrupt ID) + -> gicv3_get_interrupt_group(interrupt ID, core ID) + -> is_sgi_ppi(interrupt ID) + + is_sgi_ppi() will end up in an EL3 panic on encountering an invalid interrupt ID. + +The vulnerability is fixed by ensuring that the Interrupt ID provided by the +SDEI client is a valid PPI or SPI, otherwise return an error code indicating +that the parameter is invalid. + +.. code:: c + + /* Bind an SDEI event to an interrupt */ + static int sdei_interrupt_bind(unsigned int intr_num) + { + sdei_ev_map_t *map; + bool retry = true, shared_mapping; + + /* Interrupt must be either PPI or SPI */ + if (!(plat_ic_is_ppi(intr_num) || plat_ic_is_spi(intr_num))) + return SDEI_EINVAL; + +.. _CVE-2023-49100: https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2023-49100 +.. _a7eff3477: https://git.trustedfirmware.org/TF-A/trusted-firmware-a.git/commit/?id=a7eff3477dcf3624c74f5217419b1a27b7ebd2aa +.. _IT Security Infrastructures Lab: https://www.cs1.tf.fau.de/ +.. _SDEI Specification: https://developer.arm.com/documentation/den0054/latest/ +.. _@_chli_: https://twitter.com/_chli_ +.. _@0ddc0de: https://twitter.com/0ddc0de diff --git a/docs/security_advisories/security-advisory-tfv-9.rst b/docs/security_advisories/security-advisory-tfv-9.rst index 762801d7..014221e3 100644 --- a/docs/security_advisories/security-advisory-tfv-9.rst +++ b/docs/security_advisories/security-advisory-tfv-9.rst @@ -87,7 +87,7 @@ revisions of Cortex-A73 and Cortex-A75 that implements FEAT_CSV2). +----------------------+ | Neoverse-V2 | +----------------------+ -| Neoverse-Poseidon | +| Neoverse-V3 | +----------------------+ For all other cores impacted by Spectre-BHB, some of which that do not implement diff --git a/docs/threat_model/firmware_threat_model/index.rst b/docs/threat_model/firmware_threat_model/index.rst new file mode 100644 index 00000000..ce1752fd --- /dev/null +++ b/docs/threat_model/firmware_threat_model/index.rst @@ -0,0 +1,41 @@ +TF-A Firmware Threat Model +========================== + +As the TF-A codebase is highly configurable to allow tailoring it best for each +platform's needs, providing a holistic threat model covering all of its features +is not necessarily the best approach. Instead, we provide a collection of +documents which, together, form the project's threat model. These are +articulated around a core document, called the :ref:`Generic Threat Model`, +which focuses on the most common configuration we expect to see. The other +documents typically focus on specific features not covered in the core document. + +As the TF-A codebase evolves and new features get added, these threat model +documents will be updated and extended in parallel to reflect at best the +current status of the code from a security standpoint. + + .. note:: + + Although our aim is eventually to provide threat model material for all + features within the project, we have not reached that point yet. We expect + to gradually fill these gaps over time. + +Each of these documents give a description of the target of evaluation using a +data flow diagram, as well as a list of threats we have identified using the +`STRIDE threat modeling technique`_ and corresponding mitigations. + +.. toctree:: + :maxdepth: 1 + :caption: Contents + + threat_model + threat_model_el3_spm + threat_model_fvp_r + threat_model_rse_interface + threat_model_arm_cca + threat_model_fw_update_and_recovery + +-------------- + +*Copyright (c) 2021-2024, Arm Limited and Contributors. All rights reserved.* + +.. _STRIDE threat modeling technique: https://docs.microsoft.com/en-us/azure/security/develop/threat-modeling-tool-threats#stride-model diff --git a/docs/threat_model/threat_model.rst b/docs/threat_model/firmware_threat_model/threat_model.rst similarity index 89% rename from docs/threat_model/threat_model.rst rename to docs/threat_model/firmware_threat_model/threat_model.rst index 0da25585..ae0219ee 100644 --- a/docs/threat_model/threat_model.rst +++ b/docs/threat_model/firmware_threat_model/threat_model.rst @@ -42,6 +42,8 @@ assumptions: - No experimental features are enabled. We do not consider threats that may come from them. +- The platform's hardware complies with the `PSR specification`_, defining the + bare-minimum security prerequisites for System-on-Chips (SoC). Data Flow Diagram ================= @@ -53,7 +55,7 @@ is given on Table 1. On the diagram, the red broken lines indicate trust boundaries. Components outside of the broken lines are considered untrusted by TF-A. -.. uml:: ../resources/diagrams/plantuml/tfa_dfd.puml +.. uml:: ../../resources/diagrams/plantuml/tfa_dfd.puml :caption: Figure 1: TF-A Data Flow Diagram .. table:: Table 1: TF-A Data Flow Diagram Description @@ -161,6 +163,15 @@ in scope of this threat model. ion beam (FIB) workstation or decapsulate the chip using chemicals) is considered out-of-scope. + Certain non-invasive physical attacks that do not need modifications to the + chip, notably those like Power Analysis Attacks, are out-of-scope. Power + analysis side-channel attacks represent a category of security threats that + capitalize on information leakage through a device's power consumption during + its normal operation. These attacks leverage the correlation between a + device's power usage and its internal data processing activities. This + correlation provides attackers with the means to extract sensitive + information, including cryptographic keys. + Threat Types ============ @@ -612,6 +623,62 @@ General Threats for All Firmware Images | | UART interface(s). | +------------------------+-----------------------------------------------------+ ++------------------------+-----------------------------------------------------+ +| ID | 16 | ++========================+=====================================================+ +| Threat | | **An attacker could analyse the timing behaviour | +| | of implemented methods in the system to infer | +| | sensitive information.** | +| | | +| | | A timing side-channel attack is a type of attack | +| | that exploits variations in the time it takes a | +| | system to perform different operations. This | +| | form of attack focuses on analyzing the time- | +| | related information leakage that occurs during | +| | the execution of cryptographic algorithms or | +| | other security-sensitive processes. By observing | +| | these timing differences, an attacker can gain | +| | insights into the internal workings of a system | +| | and potentially extract sensitive information. | +| | Sensitive information that, when revealed even | +| | partially, could heighten the susceptibility to | +| | traditional attacks like brute-force attacks. | ++------------------------+-----------------------------------------------------+ +| Diagram Elements | DF2 | ++------------------------+-----------------------------------------------------+ +| Affected TF-A | BL1, BL2, BL31 | +| Components | | ++------------------------+-----------------------------------------------------+ +| Assets | Sensitive Data | ++------------------------+-----------------------------------------------------+ +| Threat Agent | AppDebug | ++------------------------+-----------------------------------------------------+ +| Threat Type | Information Disclosure | ++------------------------+------------------+----------------+-----------------+ +| Application | Server | IoT | Mobile | ++------------------------+------------------+----------------+-----------------+ +| Impact | Critical (5) | Critical (5) | Critical (5) | ++------------------------+------------------+----------------+-----------------+ +| Likelihood | Critical (5) | Critical (5) | Critical (5) | ++------------------------+------------------+----------------+-----------------+ +| Total Risk Rating | Critical (25) | Critical (25) | Critical (25) | ++------------------------+------------------+----------------+-----------------+ +| Mitigations | | Ensure that the execution time of critical | +| | operations is constant and independent of | +| | secret data. This prevents attackers from | +| | exploiting timing differences to infer | +| | information about sensitive data. | +| | | +| | | Introduce random delays/timing jitter or dummy | +| | operations to make the timing behavior of program| +| | execution less predictable. This can disrupt the | +| | correlation between the execution time and | +| | sensitive data. | +| | | ++------------------------+-----------------------------------------------------+ +| Mitigations | | Not implemented | +| implemented? | | ++------------------------+-----------------------------------------------------+ .. _Boot Firmware Threats: @@ -825,28 +892,65 @@ nonetheless once execution has reached the runtime EL3 firmware. .. topic:: Measured Boot Threats (or lack of) - In the current Measured Boot design, BL1, BL2, and BL31, as well as the - secure world components, form the |SRTM|. Measurement data is currently - considered an asset to be protected against attack, and this is achieved - by storing them in the Secure Memory. - Beyond the measurements stored inside the TCG-compliant Event Log buffer, - there are no other assets to protect or threats to defend against that - could compromise |TF-A| execution environment's security. + In the current Measured Boot design the following components form the |TCB|: + + - BL1, BL2, BL31 + - Secure world components + - RMM (if RME extension is implemented) + - The configuration data of the above components + + Across various Measured Boot backends, the data recorded during the flow as + well as the criticality of this data can vary. In most cases, these attributes + are considered valuable assets and are protected against potential attacks: + + - Image measurement: the digest value of a component produced by a hash + function. + - Signer-id: the digest value of the image verification publiy key. The + verification public key is part of the image metadata. + + In addition to these, other metadata attributes (image version, hash algorithm + identifier, etc) could be recorded during the Measured Boot process. But these + are not critical data. + + In this context, an attack means modifying the measurement data (image or + public key hash) or recording arbitrary data as valid measurements. + + The current Measured Boot design consists of two main parts. A frontend, which + is responsible for taking the measurements, and a backend which is responsible + for storing them. |TF-A| makes it possible to integrate various backends. Some + of these are implemented by the |TF-A| projects, while others are part of + different projects, and |TF-A| provides an integration layer. + + - TCG-compliant Event Log: Implemented by |TF-A|. Measurements are stored in + the Event Log which is located on the secure on-chip memory of the AP. The + address of the Event Log buffer is handed off between boot stages and new + measurements are appended to the Event Log. A limitation of the current + Measured Boot implementation in |TF-A| is that it does not extend the + measurements into a |PCR| of a Discrete |TPM|, where measurements would + be securely stored and protected against tampering. + - `CCA Measured Boot`_: Implemented by |TF-M|. Measurements are stored in + |HES| secure on-chip memory. |HES| implements protection against tampering + its on-chip memory. |HES| interface is available for BL1 and BL2. + - `DICE Protection Environment`_ (DPE): Implemented by |TF-M|. Measurements + are stored in |RSE| secure on-chip memory. |RSE| implements protection + against tampering its on-chip memory. DPE provides additional protection + against unauthorized access by malicious actors through the use of one-time + context handles and the identification of the client's target locality + (location of the client). + + Beyond the measurements (image digest and signer-id) there are no other assets + to protect or threats to defend against that could compromise |TF-A| execution + environment's security. There are general security assets and threats associated with remote/delegated attestation. However, these are outside the |TF-A| security boundary and should be dealt with by the appropriate agent in the platform/system. Since current Measured Boot design does not use local attestation, there would - be no further assets to protect(like unsealed keys). - - A limitation of the current Measured Boot design is that it is dependent upon - Secure Boot as implementation of Measured Boot does not extend measurements - into a discrete |TPM|, where they would be securely stored and protected - against tampering. This implies that if Secure-Boot is compromised, Measured - Boot may also be compromised. + be no further assets to protect (like unsealed keys). - Platforms must carefully evaluate the security of the default implementation - since the |SRTM| includes all secure world components. + System integrators must carefully evaluate the security requirement and + capabilities of their platform and choose an appropriate Measured Boot + solution. .. _Runtime Firmware Threats: @@ -1088,7 +1192,7 @@ Threats to be Mitigated by an External Agent Outside of TF-A -------------- -*Copyright (c) 2021-2023, Arm Limited. All rights reserved.* +*Copyright (c) 2021-2024, Arm Limited. All rights reserved.* .. _STRIDE threat analysis technique: https://docs.microsoft.com/en-us/azure/security/develop/threat-modeling-tool-threats#stride-model @@ -1101,3 +1205,6 @@ Threats to be Mitigated by an External Agent Outside of TF-A .. _Secure Development Guidelines: https://trustedfirmware-a.readthedocs.io/en/latest/process/security-hardening.html#secure-development-guidelines .. _Trusted Firmware-A Tests: https://git.trustedfirmware.org/TF-A/tf-a-tests.git/about/ .. _OP-TEE Dispatcher: https://github.com/ARM-software/arm-trusted-firmware/blob/master/docs/components/spd/optee-dispatcher.rst +.. _PSR Specification: https://developer.arm.com/documentation/den0106/0100 +.. _CCA Measured Boot: https://trustedfirmware-m.readthedocs.io/projects/tf-m-extras/en/latest/partitions/measured_boot_integration_guide.html +.. _DICE Protection Environment: https://trustedfirmware-m.readthedocs.io/projects/tf-m-extras/en/latest/partitions/dice_protection_environment/dice_protection_environment.html diff --git a/docs/threat_model/threat_model_arm_cca.rst b/docs/threat_model/firmware_threat_model/threat_model_arm_cca.rst similarity index 98% rename from docs/threat_model/threat_model_arm_cca.rst rename to docs/threat_model/firmware_threat_model/threat_model_arm_cca.rst index fbf3327b..af38ea3c 100644 --- a/docs/threat_model/threat_model_arm_cca.rst +++ b/docs/threat_model/firmware_threat_model/threat_model_arm_cca.rst @@ -86,7 +86,7 @@ with TF-A. A description of each diagram element is given on Table 1. On the diagram, the red broken lines indicate trust boundaries. Components outside of the broken lines are considered untrusted by TF-A. -.. uml:: ../resources/diagrams/plantuml/tfa_arm_cca_dfd.puml +.. uml:: ../../resources/diagrams/plantuml/tfa_arm_cca_dfd.puml :caption: Figure 1: Data Flow Diagram .. table:: Table 1: Data Flow Diagram Description @@ -220,6 +220,6 @@ of this threat model. Only deltas are pointed out. | 14 | Yes | | +----+-------------+-------------------------------------------------------+ -*Copyright (c) 2023, Arm Limited. All rights reserved.* +*Copyright (c) 2023-2024, Arm Limited. All rights reserved.* .. _Arm CCA Security Model: https://developer.arm.com/documentation/DEN0096/A_a diff --git a/docs/threat_model/threat_model_el3_spm.rst b/docs/threat_model/firmware_threat_model/threat_model_el3_spm.rst similarity index 99% rename from docs/threat_model/threat_model_el3_spm.rst rename to docs/threat_model/firmware_threat_model/threat_model_el3_spm.rst index 8adf3dfd..a2d67985 100644 --- a/docs/threat_model/threat_model_el3_spm.rst +++ b/docs/threat_model/firmware_threat_model/threat_model_el3_spm.rst @@ -37,7 +37,7 @@ red broken lines indicate trust boundaries. Components outside of the broken lines are considered untrusted. -.. uml:: ../resources/diagrams/plantuml/el3_spm_dfd.puml +.. uml:: ../../resources/diagrams/plantuml/el3_spm_dfd.puml :caption: Figure 1: EL3 SPMC Data Flow Diagram .. table:: Table 1: EL3 SPMC Data Flow Diagram Description @@ -644,7 +644,7 @@ element of the data flow diagram. --------------- -*Copyright (c) 2022-2023, Arm Limited. All rights reserved.* +*Copyright (c) 2022-2024, Arm Limited. All rights reserved.* .. _Arm Firmware Framework for Arm A-profile: https://developer.arm.com/docs/den0077/latest .. _FF-A ACS: https://github.com/ARM-software/ff-a-acs/releases diff --git a/docs/threat_model/threat_model_fvp_r.rst b/docs/threat_model/firmware_threat_model/threat_model_fvp_r.rst similarity index 98% rename from docs/threat_model/threat_model_fvp_r.rst rename to docs/threat_model/firmware_threat_model/threat_model_fvp_r.rst index 725eeed9..0b71bf07 100644 --- a/docs/threat_model/threat_model_fvp_r.rst +++ b/docs/threat_model/firmware_threat_model/threat_model_fvp_r.rst @@ -96,4 +96,4 @@ implementation: -------------- -*Copyright (c) 2021-2023, Arm Limited. All rights reserved.* +*Copyright (c) 2021-2024, Arm Limited. All rights reserved.* diff --git a/docs/threat_model/firmware_threat_model/threat_model_fw_update_and_recovery.rst b/docs/threat_model/firmware_threat_model/threat_model_fw_update_and_recovery.rst new file mode 100644 index 00000000..7b55c746 --- /dev/null +++ b/docs/threat_model/firmware_threat_model/threat_model_fw_update_and_recovery.rst @@ -0,0 +1,103 @@ +Threat Model for TF-A with PSA FWU or TBBR FWU support +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Introduction +************ + +This document provides a threat model of TF-A firmware for platforms with +the feature PSA firmware update or TBBR firmware update or both enabled. +To understand the design of the firmware update refer +:ref:`Firmware Update (FWU)`. + +Although it is a separate document, it references the :ref:`Generic Threat +Model` in a number of places, as some of the contents are applicable to this +threat model. + +Target of Evaluation +******************** + +In this threat model, the target of evaluation is the Trusted Firmware for +A-class Processors (TF-A) when PSA FWU support is enabled or TBBR FWU mode +is enabled. This includes the boot ROM (BL1), the trusted boot firmware (BL2). + +Threat Assessment +***************** + +For this section, please reference the Threat Assessment under the +:ref:`Generic Threat Model`. Here only the differences are highlighted. + +PSA FWU +******* + +Threats to be Mitigated by the Boot Firmware +-------------------------------------------- + +The following table analyses the :ref:`Boot Firmware Threats` in the context +of this threat model. Only additional details are pointed out. + ++----+-------------+-------------------------------------------------------+ +| ID | Applicable? | Comments | ++====+=============+=======================================================+ +| 01 | Yes | | Attacker can use arbitrary images to update the | +| | | system. | ++----+-------------+-------------------------------------------------------+ +| 02 | Yes | | Attacker tries to update the system with the | +| | | vulnerable/older firmware. | ++----+-------------+-------------------------------------------------------+ +| 03 | Yes | | ++----+-------------+-------------------------------------------------------+ +| 04 | Yes | | ++----+-------------+-------------------------------------------------------+ + + +Threats to be mitigated by platform design +------------------------------------------ + +PSA FWU is driven by metadata stored in non-volatile storage. This metadata +is not cryptographically signed. Also, depending on the hardware design, +it may be stored in untrusted storage, which makes it possible for software +outside of TF-A security boundary or for a physical attacker to modify it +in order to change the behaviour of the FWU process. + +Below we provide some possible FWU metadata corruption scenarios: + +1. The FWU metadata includes the firmware bank for booting; the attacker + tries to modify it to prevent the execution of the updated firmware. +2. The FWU metadata features a field indicating the firmware's status, either + in trial run or accepted run. The attacker tries to manipulate this field, + ensuring the updated firmware consistently runs in trial mode, with the + intention of preventing the anti-rollback update. + +By design, no software mitigations exist to prevent this. The safeguarding +of FWU metadata relies on the platform's hardware design to mitigate potential +attacks on it, if this is a concern in the platform's threat model. +For example, FWU metadata may be stored in secure storage under exclusive +access from secure software, protecting it from physical, unauthenticated +accesses and from non-secure software accesses. + +TBBR FWU - Firmware Recovery +**************************** + +Threats to be Mitigated by the Boot Firmware +-------------------------------------------- + +The following table analyses the :ref:`Boot Firmware Threats` in the context +of this threat model. Only additional details are pointed out. + ++----+-------------+-------------------------------------------------------+ +| ID | Applicable? | Comments | ++====+=============+=======================================================+ +| 01 | Yes | | Attacker can use arbitrary images to recover the | +| | | system. | ++----+-------------+-------------------------------------------------------+ +| 02 | Yes | | Attacker tries to recover the system with the | +| | | vulnerable/older firmware. | ++----+-------------+-------------------------------------------------------+ +| 03 | Yes | | ++----+-------------+-------------------------------------------------------+ +| 04 | Yes | | ++----+-------------+-------------------------------------------------------+ + +-------------- + +*Copyright (c) 2024, Arm Limited. All rights reserved.* diff --git a/docs/threat_model/threat_model_rss_interface.rst b/docs/threat_model/firmware_threat_model/threat_model_rse_interface.rst similarity index 72% rename from docs/threat_model/threat_model_rss_interface.rst rename to docs/threat_model/firmware_threat_model/threat_model_rse_interface.rst index 4bceb631..3b391c14 100644 --- a/docs/threat_model/threat_model_rss_interface.rst +++ b/docs/threat_model/firmware_threat_model/threat_model_rse_interface.rst @@ -1,41 +1,41 @@ -Threat Model for RSS - AP interface +Threat Model for RSE - AP interface *********************************** ************ Introduction ************ This document is an extension for the general TF-A threat-model. It considers -those platforms where a Runtime Security Subsystem (RSS) is included in the SoC +those platforms where a Runtime Security Engine (RSE) is included in the SoC next to the Application Processor (AP). ******************** Target of Evaluation ******************** -The scope of this threat model only includes the interface between the RSS and +The scope of this threat model only includes the interface between the RSE and AP. Otherwise, the TF-A :ref:`Generic Threat Model` document is applicable for -the AP core. The threat model for the RSS firmware will be provided by the RSS +the AP core. The threat model for the RSE firmware will be provided by the RSE firmware project in the future. Data Flow Diagram ================= This diagram is different only from the general TF-A data flow diagram in that -it includes the RSS and highlights the interface between the AP and the RSS -cores. The interface description only focuses on the AP-RSS interface the rest +it includes the RSE and highlights the interface between the AP and the RSE +cores. The interface description only focuses on the AP-RSE interface the rest is the same as in the general TF-A threat-model document. -.. uml:: ../resources/diagrams/plantuml/tfa_rss_dfd.puml - :caption: Figure 1: TF-A Data Flow Diagram including RSS +.. uml:: ../../resources/diagrams/plantuml/tfa_rse_dfd.puml + :caption: Figure 1: TF-A Data Flow Diagram including RSE -.. table:: Table 1: TF-A - RSS data flow diagram +.. table:: Table 1: TF-A - RSE data flow diagram +-----------------+--------------------------------------------------------+ | Diagram Element | Description | +=================+========================================================+ - | DF7 | | Boot images interact with RSS over a communication | + | DF7 | | Boot images interact with RSE over a communication | | | channel to record boot measurements and get image | | | verification keys. At runtime, BL31 obtains the | - | | realm world attestation signing key from RSS. | + | | realm world attestation signing key from RSE. | +-----------------+--------------------------------------------------------+ Threat Assessment @@ -44,16 +44,16 @@ For this section, please reference the Threat Assessment under the general TF-A threat-model document, :ref:`Generic Threat Model`. All the threats listed there are applicable for the AP core, here only the differences are highlighted. - - ID 11: The access to the communication interface between AP and RSS is + - ID 11: The access to the communication interface between AP and RSE is allowed only for firmware running at EL3. Accidentally exposing this - interface to NSCode can allow malicious code to interact with RSS and + interface to NSCode can allow malicious code to interact with RSE and gain access to sensitive data. - ID 13: Relevant in the context of the realm attestation key, which can be - retrieved by BL31 through DF7. The RSS communication protocol layer + retrieved by BL31 through DF7. The RSE communication protocol layer mitigates against this by clearing its internal buffer when reply is received. The caller of the API must do the same if data is not needed anymore. -------------- -*Copyright (c) 2022, Arm Limited. All rights reserved.* \ No newline at end of file +*Copyright (c) 2022-2024, Arm Limited. All rights reserved.* diff --git a/docs/threat_model/index.rst b/docs/threat_model/index.rst index e22378b0..446e610d 100644 --- a/docs/threat_model/index.rst +++ b/docs/threat_model/index.rst @@ -4,40 +4,14 @@ Threat Model Threat modeling is an important part of Secure Development Lifecycle (SDL) that helps us identify potential threats and mitigations affecting a system. -As the TF-A codebase is highly configurable to allow tailoring it best for each -platform's needs, providing a holistic threat model covering all of its features -is not necessarily the best approach. Instead, we provide a collection of -documents which, together, form the project's threat model. These are -articulated around a core document, called the :ref:`Generic Threat Model`, -which focuses on the most common configuration we expect to see. The other -documents typically focus on specific features not covered in the core document. - -As the TF-A codebase evolves and new features get added, these threat model -documents will be updated and extended in parallel to reflect at best the -current status of the code from a security standpoint. - - .. note:: - - Although our aim is eventually to provide threat model material for all - features within the project, we have not reached that point yet. We expect - to gradually fill these gaps over time. - -Each of these documents give a description of the target of evaluation using a -data flow diagram, as well as a list of threats we have identified using the -`STRIDE threat modeling technique`_ and corresponding mitigations. .. toctree:: :maxdepth: 1 :caption: Contents - threat_model - threat_model_el3_spm - threat_model_fvp_r - threat_model_rss_interface - threat_model_arm_cca + firmware_threat_model/index + supply_chain_threat_model -------------- -*Copyright (c) 2021-2023, Arm Limited and Contributors. All rights reserved.* - -.. _STRIDE threat modeling technique: https://docs.microsoft.com/en-us/azure/security/develop/threat-modeling-tool-threats#stride-model +*Copyright (c) 2021-2024, Arm Limited and Contributors. All rights reserved.* diff --git a/docs/threat_model/supply_chain_threat_model.rst b/docs/threat_model/supply_chain_threat_model.rst new file mode 100644 index 00000000..a0fed5c8 --- /dev/null +++ b/docs/threat_model/supply_chain_threat_model.rst @@ -0,0 +1,760 @@ +TF-A Supply Chain Threat Model +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Introduction +************ + +Software supply chain attacks aim to inject malicious code into a software +product. There are several ways a malicious code can be injected into a +software product (open-source project). These include: + +- Malicious code commits: This attack directly injects code into a project + repository. This can happen for example through developer/maintainer + credential hijacks, or malicious external contributors. + +- Malicious dependencies: In this case malicious code is introduced into a + project through other piece of code or packages the project depends on. This + can happen through for example typosquatting attack where an attacker creates + a malicious package with a very similar name to a popular package and hosts + it on popular package repositories. + +- Malicious toolchains: This involves malicious code introduced by compromised + resources used throughout the development and/or build process such as + compilers and IDEs. + +This document provides analysis of software supply chain attack threats for the +TF-A project. + +TF-A Overview +************* + +Figure 1 shows the different software components surrounding the TF-A project. +A brief description of each component is provided below. + +TF-A Repository +=============== + +The TF-A repository contains generic and platform code contributed by TF-A +contributors as well as libraries imported from other open-source projects, +referred to as internal dependencies on Figure 1. These libraries include: + +- *libfdt*: libfdt is a utility library for reading and manipulating Device + Tree Binary (DTB) files. It is part of the Device Tree Compiler (DTC) + toolchain [1]_. DTC is used as part of the build process on the host machine + to build DTB files. libfdt is used to parse the DTB files at boot time. + +- *zlib*: zlib is a data compression library imported from [2]_. + +- *compiler-rt*: This is a collection of runtime libraries from the LLVM + compiler infrastructure project [3]_. We import the builtins library which + provides low-level, target-specific compiler builtins from compiler-rt. + +The TF-A repository also includes source code for host tools that supplement +the TF-A build process. These tools include: + +- *fiptool*: This tool is used to create a Firmware Image Package (FIP) which + allows for packing bootloader images into a single archive that can be + loaded by TF-A from non-volatile platform storage. + +- *cert_create*: This tool is used to generate certificates for binary images. + +- *encrypt_fw*: This tool takes the plain firmware image as input and generates + the encrypted firmware image which can then be passed as input to the fiptool + utility for creating the FIP. + +- *sptool*: This tool is used to build the secure partition packages. + +|TF-A System Diagram| +*Figure 1: TF-A System Diagram* + +External Dependencies +===================== + +These are software components that are not part of the TF-A repository but are +required to build TF-A binaries and host tools. + +- *Mbed TLS Library*: This is a cryptography library from trustedfirmware.org + (tf.org). It is required to build TF-A binaries where cryptography features + are needed, such as Trusted Board Boot (TBB). + +- *OpenSSL Library*: This is another cryptography library used by TF-A host + tools: fiptool, cert_create, and encrypt_fw. + +The following table lists TF-A dependencies including the sources of the +dependencies. + +.. table:: Table 1: TF-A Dependencies + + +-------------+------------------------+------------------------------------+ + | Dependency | Location of Dependency | Original Source | + +=============+========================+====================================+ + | libfdt | Local copy | [1]_ | + +-------------+------------------------+------------------------------------+ + | zlib | Local copy | [2]_ | + +-------------+------------------------+------------------------------------+ + | compiler-rt | Local copy | [3]_ | + +-------------+------------------------+------------------------------------+ + | Mbed TLS | External | [4]_ | + +-------------+------------------------+------------------------------------+ + | OpenSSL | External | [5]_ | + +-------------+------------------------+------------------------------------+ + +Supplementary Binaries +====================== + +These are binaries used to test TF-A based systems. Below is a brief +description of each component and where they are sourced from. + +- *SCP-firmware*: For our tests, we use SCP-firmware binaries supplied by the + Arm SCP team built from the source from the GitHub repository [6]_. + +- *OP-TEE*: Trusted Execution Environment (TEE) from tf.org that runs as + Secure EL1. We use OP-TEE built from source or binaries supplied with Arm + Reference Platforms depending on the test configuration. + +- *EDK2 UEFI*: Normal world bootloader from the EDK2 project [7]_. We use EDK2 + UEFI binaries hosted on tf.org servers for testing [8]_. + +Other software components used to test TF-A include U-Boot, Linux kernel, RSE, +MCP, and file systems, all sourced from the Arm Reference Platforms teams. + +TF-A Toolchain +============== + +The TF-A project uses several tools to build, analyze and test the TF-A source +code. + +Node.js Tools +------------- + +These are optional quality assurance and developer utility tools that are +installed through the use of the Node.js package manager. They are pinned to +specific versions described by the package.json file in the root of the TF-A +repository, and their dependencies are downloaded from the internet at the +point of installation. These tools may be installed locally on the developer +machine and are installed within a Docker container in certain CI jobs. At +present, these are: + +- Commitlint + +- Commitizen + +- Husky + +Infrastructure +============== + +TF-A uses trustedfirmware.org (tf.org) and Arm infrastructures to host the +source code, review code and run tests. Appendix A provides a security analysis +of tf.org infrastructure. + +TF-A Data Flow +************** + +Figure 2 below shows the data flow diagram for TF-A. The broken red lines +indicate trust boundaries. + +|TF-A Data Flow Diagram| +*Figure 2: TF-A Data Flow Diagram* + +Attack Tree +*********** + +|TF-A Attack Tree| +*Figure 3: TF-A Attack Tree* + +Threat Assessment and Mitigations +********************************* + +Impact and Likelihood Ratings +============================= + + +--------+------------------------------+-----------------------------------+ + | Rating | Impact | Likelihood | + +========+==============================+===================================+ + | HIGH | Major impact to entire | Threat is relatively easy to | + | | organization or single line | exploit by an attacker with | + | | of business if exploited. | little effort and skill. | + +--------+------------------------------+-----------------------------------+ + | MEDIUM | Noticeable impact to line of | An expert attacker could exploit | + | | business if exploited. | the threat without much | + | | | difficulty. | + +--------+------------------------------+-----------------------------------+ + | LOW | Minor damage if exploited or | Exploiting the threat would | + | | could be used in conjunction | require considerable effort and | + | | with other vulnerabilities | resources. | + | | to perform a more serious | | + | | attack. | | + +--------+------------------------------+-----------------------------------+ + +Threats and Mitigations +======================= + +Threat naming convention key + +- SC – Supply Chain + +- SRC – Source + +- DEP – Dependency + +- TOOL – Toolchain + +- REPO – Repository + +- MAIN – Maintainer + +- CONT – Contributor + + +---------------------------------------------------------------------------+ + | Threat: TFA-SC-SRC-MAIN-01 | + +=============+=============================================================+ + | Description | An attacker can submit and merge malicious code by posing | + | | as a maintainer after compromising maintainers’ | + | | credentials. | + +-------------+-------------------------------------------------------------+ + | Impact | HIGH | + +-------------+-------------------------------------------------------------+ + | Likelihood | MEDIUM | + +-------------+-------------------------------------------------------------+ + | Threat and | | In the TF-A code review process all submitted changes | + | impact | undergo review by a code owner and a maintainer. If the | + | | change is accepted, it will be merged (integrated) into  | + | | an integration branch by a maintainer. A maintainer has | + | | the right to give a code owner review, a maintainer | + | | review and merge the submitted change.  | + | | | + | | | tf.org users (including maintainers) are authenticated | + | | through GitHub. The likelihood of a credential compromise | + | | depends on multiple factors. The authentication mechanism | + | | of GitHub is strong if the recommended best practices are | + | | followed [9]_ making credential compromise unlikely. | + | | GitHub (therefore tf.org) allows logins with two-factor | + | | authentication, requiring both a password and access to | + | | the user's authentication code. Depending on the strength | + | | of the password and factors such as whether the | + | | maintainer reuses passwords across services, the | + | | likelihood of a compromise can be higher. | + | | | + | | | If an attacker manages to compromise a maintainer’s | + | | credentials, posing as the maintainer, they can in theory | + | | submit a malicious change (as a maintainer or as a | + | | contributor), give all the necessary reviews and merge | + | | the change. | + +-------------+-------------------------------------------------------------+ + | Mitigations | | - Enforce best practices recommended by GitHub [9]_ | + | | | + | | | - Not allowing a committer to both self-review and merge | + | | patches they have submitted. To achieve the commit the | + | | attacker would be required to compromise at least two | + | | credentials (reviewers and maintainer). | + +-------------+-------------------------------------------------------------+ + | Mitigations | We have not disallowed self-review/merge of patches | + | implemented?| | + +-------------+-------------------------------------------------------------+ + + +---------------------------------------------------------------------------+ + | Threat: TFA-SC-SRC-MAIN-02 | + +=============+=============================================================+ + | Description | An attacker can submit and merge malicious code after | + | | becoming a maintainer through social engineering | + | | techniques. | + +-------------+-------------------------------------------------------------+ + | Impact | HIGH | + +-------------+-------------------------------------------------------------+ + | Likelihood | LOW | + +-------------+-------------------------------------------------------------+ + | Threat and | | According to the TF project maintenance process [10]_, | + | impact | maintainers of TF-A are selected by their peers based on | + | | merit. Some of the criteria of becoming a maintainer | + | | include being an active member of the project for a | + | | minimum duration and contributing a substantial number of | + | | non-trivial and high-quality patches. However, there are | + | | some weaknesses in the process: | + | | | + | | | - There is no structured mechanism to establish trust | + | | with a maintainer other than the recommendations by | + | | peers | + | | | - There is no continuous monitoring of the status of a | + | | maintainer (e.g. maintainer can move from one | + | | organization to another) | + | | | + | | | To perform such an attack, in addition to becoming a | + | | maintainer, an attacker also must deal with all | + | | restrictions put on maintainers. | + +-------------+-------------------------------------------------------------+ + | Mitigations | | - Structured mechanism to establish trust with | + | | maintainers | + | | | + | | | - Not allowing a committer to both self-review and merge | + | | patches they have submitted. To achieve the commit the | + | | attacker would be required to compromise at least two | + | | credentials (reviewers and maintainer). | + +-------------+-------------------------------------------------------------+ + | Mitigations | There is a structured mechanism to establish trust with | + | implemented?| maintainers, but self-review/merge of patches is not | + | | disallowed | + +-------------+-------------------------------------------------------------+ + + +---------------------------------------------------------------------------+ + | Threat: TFA-SC-SRC-CONT-01 | + +=============+=============================================================+ + | Description | An attacker can submit malicious code patch as a | + | | contributor. | + +-------------+-------------------------------------------------------------+ + | Impact | HIGH | + +-------------+-------------------------------------------------------------+ + | Likelihood | LOW | + +-------------+-------------------------------------------------------------+ + | Threat and | | TF-A accepts external contributions to both the generic | + | impact | and platform code. Unlike maintainers, contributors do | + | | not have maintainer review or merging privileges, | + | | therefore the likelihood of injecting malicious code as a | + | | contributor is lower. However, even though unlikely, it | + | | is still possible for a malicious commit to go unnoticed | + | | through the code review and verification processes. | + | | | + | | | If successful, the impact can range from low to high | + | | depending on the injected code. For example, an attacker | + | | can potentially deliberately insert a memory corruption | + | | vulnerability that is hard to notice on code review and | + | | will not be detected by the verification process. This | + | | vulnerability by itself may have a low impact but can | + | | have a major impact if used in combination with other | + | | vulnerabilities. | + +-------------+-------------------------------------------------------------+ + | Proposed | - Code review and verification | + | Mitigations | - Static analysis to try to pick up issues that typically | + | | end in some form of attack vector | + +-------------+-------------------------------------------------------------+ + | Mitigations | Yes, contributions go through the thorough review, | + | implemented?| verification, and static analysis process automated through | + | | CI | + +-------------+-------------------------------------------------------------+ + + +---------------------------------------------------------------------------+ + | Threat: TFA-SC-DEP-01 | + +=============+=============================================================+ + | Description | An attacker can inject malicious code into TF-A internal | + | | dependencies. | + +-------------+-------------------------------------------------------------+ + | Impact | HIGH | + +-------------+-------------------------------------------------------------+ + | Likelihood | LOW | + +-------------+-------------------------------------------------------------+ + | Threat and | | TF-A has two types of dependencies: those that are copied | + | impact | into the TF-A repository and shipped as part of TF-A code | + | | (referred to as *internal dependencies* here) and those | + | | that are downloaded from external repositories and used | + | | when building TF-A (referred to as | + | | *external dependencies* here).  | + | | | + | | | Currently TF-A has three internal dependencies: *libfdt* | + | | [1]_, *zlib* [2]_ and *compiler-rt* [3]_ libraries. These | + | | libraries are periodically updated by copying them from | + | | their source repositories. Although unlikely, it is | + | | possible for a contributor to copy the libraries from the | + | | wrong (and potentially malicious) repositories. For | + | | example, there are already multiple forks of *libfdt* | + | | (DTC) on GitHub. In addition to this, the official | + | | repositories are not immune to threats described above | + | | (TFA-SC-SRC-MAIN-01, TFA-SC-SRC-MAIN-02 and | + | | TFA-SC-SRC-CONT-01). | + | | | + | | | The likelihood of an attack on TF-A through internal | + | | dependencies is lower than external dependencies for the | + | | following reasons:  | + | | | + | | | - Internal dependencies go through the normal code review | + | | process during upgrade | + | | | - Once upgraded internal dependencies stay unchanged | + | | until the next upgrade. The upgrade window is typically | + | | long (for example *libfdt* has only changed 4 times | + | | over the past 4 years). This reduces the window of | + | | opportunity for an attacker to inject malicious code | + | | into the dependencies | + +-------------+-------------------------------------------------------------+ + | Proposed | - Explicitly document versions and official sources of | + | Mitigations | dependencies | + | | - Keep a copy of a pinned version of the source code inside | + | | the TF-A tree so that the risk of getting malicious code | + | | from dependencies only arises when we upgrade them | + | | - Monitor alerts for vulnerable dependencies from GitHub | + | | [11]_ | + +-------------+-------------------------------------------------------------+ + | Mitigations | Yes, we explicitly document versions and official sources | + | implemented?| of dependencies, keep a copy of pinned versions of the | + | | source code, and monitor alerts for vulnerable dependencies | + | | for Python and Node.js, but we aren't able to do this for C | + | | dependencies | + +-------------+-------------------------------------------------------------+ + + +---------------------------------------------------------------------------+ + | Threat: TFA-SC-DEP-02 | + +=============+=============================================================+ + | Description | An attacker can inject malicious code into TF-A external | + | | dependencies. | + +-------------+-------------------------------------------------------------+ + | Impact | HIGH | + +-------------+-------------------------------------------------------------+ + | Likelihood | MEDIUM | + +-------------+-------------------------------------------------------------+ + | Threat and | | Unlike internal dependencies, external dependencies are | + | impact | downloaded from external repositories by end-users. | + | | Although the TF-A documentation provides information | + | | about the versions of dependencies used for testing and | + | | links to repositories, it is up to the end-user to decide | + | | where to get the dependencies from. As such, the | + | | likelihood of an attack through an external dependency is | + | | higher compared to an internal dependency. | + | | | + | | | The impact of an attack ranges from low to critical | + | | depending on which dependency and what part of the | + | | dependency is affected. For example, a malicious code | + | | that affects the signature verification functions in | + | | MbedTLS is considered critical as it can be used to | + | | bypass the TBB process of TF-A. | + +-------------+-------------------------------------------------------------+ + | Proposed | - Explicitly document versions and official sources of | + | Mitigations | dependencies | + | | - Provide scripts and build options to automatically fetch | + | | the latest stable release of external dependencies | + +-------------+-------------------------------------------------------------+ + | Mitigations | We explicitly document versions and official sources of | + | implemented?| dependencies, but do not yet provide scripts and build | + | | options to automatically fetch the latest stable release of | + | | external dependencies | + +-------------+-------------------------------------------------------------+ + + +---------------------------------------------------------------------------+ + | Threat: TFA-SC-REPO-01 | + +=============+=============================================================+ + | Description | An attacker can upload malicious versions of TF-A by | + | | compromising credentials of administrator accounts on | + | | tf.org or GitHub. | + +-------------+-------------------------------------------------------------+ + | Impact | HIGH | + +-------------+-------------------------------------------------------------+ + | Likelihood | LOW | + +-------------+-------------------------------------------------------------+ + | Threat and | | This attack is like TFA-SC-SRC-MAIN-01, but the | + | impact | likelihood and impact of the two attacks are different. | + | | | + | | | The likelihood of compromising administrator credentials | + | | is lower than that of a maintainer’s (assuming both use | + | | authentication methods of similar strength) as there are | + | | smaller number of administrators than maintainers. On the | + | | other hand, the impact is higher since an administrator | + | | has more privileges than a maintainer: | + | | | + | | | - An administrator can upload a malicious TF-A | + | | contribution unnoticed by other reviewers | + | | - An administrator can potentially rewrite the history of | + | | the repository to evade detection | + +-------------+-------------------------------------------------------------+ + | Proposed | Strong authentication (Follow best practices recommended by | + | Mitigations | GitHub [9]_) | + +-------------+-------------------------------------------------------------+ + | Mitigations | Yes, strong authentication is implemented through | + | implemented?| recommended best practices | + +-------------+-------------------------------------------------------------+ + + +---------------------------------------------------------------------------+ + | Threat: TFA-SC-REPO-02 | + +=============+=============================================================+ + | Description | An attacker can upload malicious versions of TF-A after | + | | getting write access to the repository by exploiting a | + | | vulnerability on tf.org or GitHub. | + +-------------+-------------------------------------------------------------+ + | Impact | HIGH | + +-------------+-------------------------------------------------------------+ + | Likelihood | LOW | + +-------------+-------------------------------------------------------------+ + | Threat and | | There are no reports of someone exploiting a | + | impact | vulnerability on GitHub or tf.org to upload malicious | + | | contributions. However, there are examples of | + | | vulnerabilities that allowed arbitrary code execution on | + | | popular hosting services [12]_. Such vulnerabilities can | + | | potentially be used to upload malicious packages. In | + | | addition to being hard to exploit, vulnerabilities on | + | | popular hosting sites such as GitHub are typically | + | | detected quickly, making the window of opportunity for | + | | such attack small. | + +-------------+-------------------------------------------------------------+ + | Proposed | - Monitor alerts of any vulnerabilities that might affect | + | Mitigations | TF-A repository | + | | - Ensure tf.org is up to date with latest security patches | + +-------------+-------------------------------------------------------------+ + | Mitigations | Yes, alerts of vulnerabilities are monitored and tf.org is | + | implemented?| ensured to be up to date with the latest security patches | + +-------------+-------------------------------------------------------------+ + + +---------------------------------------------------------------------------+ + | Threat: TFA-SC-REPO-03 | + +=============+=============================================================+ + | Description | An attacker can host a malicious version of TF-A on an | + | | attacker-controlled repository, and trick end-users into | + | | downloading from that repository. | + +-------------+-------------------------------------------------------------+ + | Impact | HIGH | + +-------------+-------------------------------------------------------------+ + | Likelihood | MEDIUM | + +-------------+-------------------------------------------------------------+ + | Threat and | | It is not difficult for an attacker to create a website | + | impact | with a similar domain name and look as tf.org (website | + | | spoofing) and host a malicious TF-A source repository. | + | | Similarly, an attacker can create a mirror of the TF-A | + | | repository on GitHub with malicious code in it. However, | + | | for this attack to succeed the attacker needs to trick | + | | the end-user into using the attacker-controlled | + | | repositories. | + +-------------+-------------------------------------------------------------+ + | Proposed | - Users should carefully check the URL of the website | + | Mitigations | before visiting it and the URL of the repository before | + | | checking it out | + | | - Accept reports of spoofing attacks on tf.org and | + | | broadcast a warning to partners | + +-------------+-------------------------------------------------------------+ + | Mitigations | We accept reports of spoofing attacks on tf.org and will | + | implemented?| broadcast a warning to partners | + +-------------+-------------------------------------------------------------+ + + +---------------------------------------------------------------------------+ + | Threat: TFA-SC-TOOL-01 | + +=============+=============================================================+ + | Description | Malicious code can be injected at build time through | + | | malicious tools. | + +-------------+-------------------------------------------------------------+ + | Impact | HIGH | + +-------------+-------------------------------------------------------------+ + | Likelihood | LOW | + +-------------+-------------------------------------------------------------+ + | Threat and | | End-users of TF-A use make (or cmake), compilers and | + | impact | linkers (armgcc, armclang or LLVM) to build TF-A | + | | binaries. Although TF-A documentation specifies versions | + | | and official sources of tools used to build TF-A, users | + | | can potentially be tricked into using unofficial, | + | | malicious toolchains. Similar attacks have been used in | + | | the past to inject malicious code into final products | + | | [13]_. | + +-------------+-------------------------------------------------------------+ + | Proposed | - Explicitly document versions and official sources of | + | Mitigations | toolchains | + | | - Provide scripts to automatically fetch the latest stable | + | | release of toolchains | + +-------------+-------------------------------------------------------------+ + | Mitigations | We explicitly document versions and official sources of | + | implemented?| toolchains, but have not yet provided scripts to | + | | automatically fetch the latest stable release of toolchains | + +-------------+-------------------------------------------------------------+ + + +---------------------------------------------------------------------------+ + | Threat: TFA-SC-TOOL-02 | + +=============+=============================================================+ + | Description | Malicious code can be executed by developer’s tools at | + | | installation time through malicious Node.js dependencies. | + +-------------+-------------------------------------------------------------+ + | Impact | HIGH | + +-------------+-------------------------------------------------------------+ + | Likelihood | LOW | + +-------------+-------------------------------------------------------------+ + | Threat and | | Users of the Node.js tools, including the CI, may be | + | impact | exposed to malicious dependencies that have been missed | + | | by the Node.js dependency auditor. Users of these tools | + | | could potentially be executing malicious code when using | + | | these tools, which could potentially allow a malicious | + | | actor to make silent modifications to the repository or | + | | enable retrieval of user credentials. | + | | | + | | | If successful, the impact can range from low to high | + | | depending on the user's credentials. If the user is an | + | | administrator, this could imply TFA-SC-REPO-01. | + +-------------+-------------------------------------------------------------+ + | Proposed | - Limit Node.js tools to a minimal set of trusted packages | + | Mitigations | - Pin Node.js packages to known versions | + | | - Update dependencies for which Node.js’s auditor reports | + | | known CVEs | + | | - Execute Node.js tools in the CI only from within a | + | | trusted container | + +-------------+-------------------------------------------------------------+ + | Mitigations | Yes, Node.js tools are limited to a minimal set of trusted | + | implemented?| packages, packages are pinned to known versions, | + | | dependencies are updated when there are known CVEs | + | | reported, and Node.js tools are only executed within a | + | | trusted container in CI | + +-------------+-------------------------------------------------------------+ + +Appendix A +********** + +Summary of trustedfirmware.org security: + +.. table:: Table 2: Security information of trustedfirmware.org + + +------------+--------------------+--------------------+--------------------+ + | Software/ | Source and | Credential and | Security incident | + | System | integrity | permission | response plan | + | | | management | | + +============+====================+====================+====================+ + | Jenkins | - Jenkins is built | - Use oauth from | - Monitor CVE’s | + | (including | using Dockerfile | Github only | and update | + | plugins) | which is based | - The password | Jenkins LTS on a | + | | on the official | strength follows | monthly cycle | + | | Jenkins docker | Github policy | - Keep plugins up- | + | | image | - Do not enforce | to-date. But it | + | | - Jenkins plugins | using two-factor | is up to the | + | | are built using | authentication | plugin owner to | + | | the official | - Jenkins uses | maintain said | + | | install- | matrix auth | plugin | + | | plugins.sh | which allows | | + | | | users to manage | | + | | | "job" level ACL | | + | | | using Jenkins | | + | | | Job Builder | | + | | | - No API token | | + | | | enabled | | + | | | - Jenkins uses the | | + | | | inbuilt | | + | | | credential store | | + | | | where we store | | + | | | credentials for | | + | | | LAVA, Jenkins | | + | | | Job Builder, | | + | | | DockerHub, AWS | | + | | | and Gerrit | | + | | | tokens. The | | + | | | credentials are | | + | | | stored as a | | + | | | secret in | | + | | | Jenkins | | + | | | credential | | + | | | store. These | | + | | | credentials | | + | | | can be accessed | | + | | | via a Jenkins | | + | | | job, but someone | | + | | | would have to | | + | | | push a Jenkins | | + | | | Job through a | | + | | | Gerrit review to | | + | | | do this. Gerrit | | + | | | maintains the | | + | | | ACL for this and | | + | | | only admins and | | + | | | project approver | | + | | | can +2 a review. | | + +------------+--------------------+--------------------+--------------------+ + | Gerrit | - Gerrit package | - Use oauth from | - Keep plugins up- | + | (including | is installed | Github only | to-date. But it | + | plugins) | from Linaro top | - The password | is up to the | + | | level role, | strength follows | plugin owner to | + | | which has a | Github policy | maintain said | + | | md5sum check | - Do not enforce | plugin | + | | - Gerrit Plugins | using two-factor | | + | | are installed | authentication | | + | | from Ansible | - Gerrit has ACL | | + | | playbook, from | setup within the | | + | | the official | UI per-project | | + | | Gerrit CI. The | level | | + | | plugins are | - No API token | | + | | downloaded from | enabled | | + | | https://gerrit- | - A ci-bot-user | | + | | ci.gerritforge. | created for | | + | | com/ | getting comments | | + | | - Do not check | from Jenkins | | + | | md5sum for every | | | + | | plugin | | | + +------------+--------------------+--------------------+--------------------+ + | Git | - Package is from | - All credentials | - Monitor all | + | | Linaro OBS (Open | use GitHub. So | CVE's and apply | + | | Build Service) | password | them immediately | + | | with a couple of | strength etc are | and keep servers | + | | “Linaro | based on GitHub | up-to-date | + | | modificationsâ€. | policy | monthly | + | | (reference: | | - The security | + | | Ansible playbook | | incident | + | | and cgit repo) | | response plan is | + | | - No special | | working in | + | | integrity check | | progress | + +------------+--------------------+--------------------+--------------------+ + | Mailman | - Installed from | - It has | - Plan to monitor | + | | Ubuntu- | administrator | the CVE’s but no | + | | distributed | passwords for | timetable at the | + | | package | the various | moment | + | | - No special | mailing lists | | + | | integrity check | - The password | | + | | (reply on APT | strength is not | | + | | security) | specified | | + +------------+--------------------+--------------------+--------------------+ + | Website | The website is | There are no | - The websites | + | | built on the IT | credentials | themselves are | + | | Services' CI/CD | associated with | static files | + | | server, | the website | hosted on AWS S3 | + | | bamboo.linaro.org, | itself. Any | and cached by | + | | from a Jekyll git | permissions | AWS CloudFront | + | | repository stored | required by bamboo | - The software | + | | on GitHub | to carry out its | used to build | + | | | tasks are provided | the website is | + | | | through AWS | all open source | + | | | instance role | and Linaro | + | | | permissions | occasionally | + | | | | gets reports | + | | | | from GitHub when | + | | | | an issue is | + | | | | detected. Apply | + | | | | a fix if it is | + | | | | available. This | + | | | | includes any | + | | | | Javascript | + | | | | frameworks that | + | | | | might be used | + | | | | within the web | + | | | | pages | + +------------+--------------------+--------------------+--------------------+ + | ReadTheDocs| - One webhook ID | - One TF-A account | - Keep database | + | | per project is | with password | access list up | + | | used by TF CI | stored in | to date | + | | for building | engineering | - Monitor security | + | | documentation | password | advisories | + | | hosted by | database is used | | + | | ReadTheDocs | to manage | | + | | - Secret token | documentation | | + | | supplied as part | - Access request | | + | | of the webhook | is required | | + | | post build | for database | | + | | - Updated content | access | | + | | goes live | - Token for | | + | | automatically | Jenkins webhook | | + | | | for CI uses | | + | | | secret | | + | | | credential | | + | | | storage in | | + | | | internal Jenkins | | + | | | and viewable | | + | | | only through | | + | | | ReadTheDocs | | + | | | admin page | | + +------------+--------------------+--------------------+--------------------+ + +References +********** + +.. [1] https://git.kernel.org/pub/scm/utils/dtc/dtc.git +.. [2] http://zlib.net/ +.. [3] https://compiler-rt.llvm.org/ +.. [4] https://tls.mbed.org/ +.. [5] https://www.openssl.org/ +.. [6] https://github.com/ARM-software/SCP-firmware +.. [7] https://github.com/tianocore/edk2 +.. [8] https://downloads.trustedfirmware.org/tf-a/ +.. [9] https://docs.github.com/en/github/authenticating-to-github/creating-a-strong-password +.. [10] https://trustedfirmware-a.readthedocs.io/en/latest/process/maintenance.html#how-to-become-a-maintainer +.. [11] https://docs.github.com/en/github/managing-security-vulnerabilities/about-alerts-for-vulnerable-dependencies +.. [12] "Backstabber’s Knife Collection: A Review of Open Source Software Supply Chain Attacks" +.. [13] https://www.wired.com/story/supply-chain-hackers-videogames-asus-ccleaner/ + +*Copyright (c) 2024, Arm Limited. All rights reserved.* + +.. |TF-A System Diagram| image:: ../resources/diagrams/tf-a_system_diagram.png +.. |TF-A Data Flow Diagram| image:: ../resources/diagrams/tf-a_data_flow_diagram.png +.. |TF-A Attack Tree| image:: ../resources/diagrams/tf-a_attack_tree.png diff --git a/docs/tools/cot-dt2c.rst b/docs/tools/cot-dt2c.rst new file mode 100644 index 00000000..e8bb1ace --- /dev/null +++ b/docs/tools/cot-dt2c.rst @@ -0,0 +1,119 @@ +TF-A CoT dt2c Tool +================== + +This tool is used to automatically generate the corresponding c file for a +CoT DT file. Since currently TF-A support two type of CoT file: static c file +and CoT DT binding. This is error prone and hard to maintain, therefore this +tool can generate the c file for the platform that does not support CoT DT +binding, given the CoT DT file so the c file can be deprecated. + +Prerequisites +~~~~~~~~~~~~~ + +#. Python (3.8 or later) +#. `Poetry`_ Python package manager + +Getting Started +~~~~~~~~~~~~~~~ + +``cot-dt2c`` is installed by default with TF-A's poetry environment. All of it's +dependencies are listed in `tools/cot_dt2c/pyproject.toml`_. + +``cot-dt2c`` requires a standard DTS file without #ifdef, macros, or other +preprocessor directives. Therefore, you need to provide a preprocessed device +tree source(DTS) as input to the tool. + +#. Usage of the tool + + .. code:: + + cot-dt2c + + This command will output the following as usage for this command + + .. code-block:: text + + Usage: cot-dt2c [OPTIONS] COMMAND [ARGS]... + + Options: + --version Show the version and exit. + --help Show this message and exit. + + Commands: + convert-to-c + validate-cot + visualize-cot + validate-dt + +Convert CoT descriptors to C file +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +To convert the CoT descriptors + +This command is for the platform that does not use CoT DT parser, +which can generate the C file given the CoT descriptors. Before +the conversion to C file, the tool will do an implicit checks on +the validity of the CoT DT file. + +.. code:: + + cot-dt2c convert-to-c [INPUT DTS PATH] [OUTPUT C PATH] + cot-dt2c convert-to-c fdts/tbbr_cot_descriptors.dtsi test.c + + +Validate CoT descriptors +~~~~~~~~~~~~~~~~~~~~~~~~~ + +To validate the certificate + +The tests folder in the tool folder provides some bad-example of the +DT file, and the tool will print out "not a valid CoT DT file" on console. + +The command will check the format of the CoT file + +#. The open bracket +#. The open ifdef macro +#. The missing mandatory attribute +#. Malformed DT file (cert missing parent, missing root certs. etc.) + +Currently the validation is specifically for checking the CoT DT file + +.. code:: + + cot-dt2c validate-cot [INPUT DTS PATH] + cot-dt2c validate-cot fdts/tbbr_cot_descriptors.dtsi + + +Visualize CoT descriptors +~~~~~~~~~~~~~~~~~~~~~~~~~ + +This command create a HTML to visualize the relationship between +the certificates and the image of a CoT DT file. + +.. code:: + + cot-dt2c visualize-cot [INPUT DTS PATH] + cot-dt2c visualize-cot fdts/tbbr_cot_descriptors.dtsi + + +Validate Other DT files +~~~~~~~~~~~~~~~~~~~~~~~ + +The command will transform the dtsi/dts file into a more standard +dtsi/dts file inside /tmp folder that can be used as input to dt-schema +for further validation. Currently the tool will perform some basic validation +for the file (syntax) and dt-schema can be used for advance checks. dt-schema +is not installed along with the tool. + +.. code:: + + cot-dt2c validate-dt [INPUT DTS PATH or INPUT DTS folder] + cot-dt2c validate-dt fdts/ + cot-dt2c validate-dt fdts/fvp-bsae-gicv3.dtsi + +-------------- + +*Copyright (c) 2024, Arm Limited. All rights reserved.* + +.. _tools/cot_dt2c/pyproject.toml: https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/refs/heads/integration/tools/cot_dt2c/pyproject.toml +.. _Poetry: https://python-poetry.org/docs/ diff --git a/docs/tools/index.rst b/docs/tools/index.rst index 2dee2c07..c0e214a9 100644 --- a/docs/tools/index.rst +++ b/docs/tools/index.rst @@ -6,7 +6,9 @@ Tools :caption: Contents memory-layout-tool + transfer-list-compiler + cot-dt2c -------------- -*Copyright (c) 2023, Arm Limited. All rights reserved.* +*Copyright (c) 2023-2024, Arm Limited. All rights reserved.* diff --git a/docs/tools/transfer-list-compiler.rst b/docs/tools/transfer-list-compiler.rst new file mode 100644 index 00000000..fa660dc6 --- /dev/null +++ b/docs/tools/transfer-list-compiler.rst @@ -0,0 +1,311 @@ +Transfer List Compiler +====================== + +The Transfer List Compiler (tlc) is a host tool used by TF-A to generate transfer +lists compliant with the v0.9 of the `Firmware Handoff specification`_. It enables +developers to statically generate transfer list blobs containing any number of +transfer entries. + +Getting Started +~~~~~~~~~~~~~~~ + +``tlc`` is installed by default with TF-A's poetry environment. All of it's +dependencies are listed in `tools/tlc/pyproject.toml`_. + +To install ``tlc`` seperately, run the following command: + +.. code:: + + make -C tools/tlc install + +Creating a Transfer List +~~~~~~~~~~~~~~~~~~~~~~~~ + +To create an empty TL, you can use the ``create`` command. + +.. code:: + + tlc create tl.bin + +This commands generates a binary blob representing an empty TL, shown in the +hexdump below. + +.. code:: + + $ hexdump tl.bin | head + 0000000 b10b 4a0f 01a6 0318 0018 0000 1000 0000 + 0000010 0001 0000 0000 0000 + +A common use-case this tool supports is the addition of TE's via the option +``--entry``. This takes as input the tag ID and path to a binary blob to be +included in the transfer list. The snippet below shows how to include an FDT in +the TL. + +.. code:: + + tlc create --entry 1 fdt.dtb tl.bin + +Alternatively, addition of a device tree is supported through the option +``--fdt``. This has the same effect as passing the device tree and it's tag ID +through the ``--entry`` option. + +.. code:: + + tlc create --fdt fdt.dtb tl.bin + +.. note:: + + ``tlc`` makes no effort to verify the contents of a binary blob against the + provided tag ID. It only checks that the tags provided as input are within + range and that there is sufficient memory to include their TE's. + +You can also create a TL from a YAML config file. + +.. code :: + + tlc create --from-yaml config.yaml tl.bin + +Printing the contents of a TL +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Support is provided for dumping the contents of a TL via the ``info`` command. +This prints the header of the TL and all included TE's. + +.. code:: + + $ tlc info tl.bin + signature 0x4a0fb10b + checksum 0xe1 + version 0x1 + hdr_size 0x18 + alignment 0x3 + size 0x2a6f + total_size 0x4e20 + flags 0x1 + ---- + id 0x1 + data_size 0x2a47 + hdr_size 0x8 + offset 0x18 + ---- + id 0x0 + data_size 0x0 + hdr_size 0x8 + offset 0x2a68 + +The example above shows the dump produced by ``tlc`` for a 20Kb TL containing a +device tree (tag_id=1) and a NULL entry (tag_id=0). + +Modifying the contents of an existing TL +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +`tlc` supports removal of one or more entries from a TL through the ``remove`` +command. It takes as argument the filename, and one or more tag ID's, passed +through the ``--tags`` option. It produces a valid TL blob without those +entries. + + +For example, using the same blob as in the section above, we can remove the FDT +TE with the command. + +.. code:: + + $ tlc remove --tags 1 tl.bin + +Using the ``info`` command, shows the the TE has been remove: + +.. code:: + + $ tlc info tl.bin + + signature 0x4a0fb10b + checksum 0x38 + version 0x1 + hdr_size 0x18 + alignment 0x3 + size 0x20 + total_size 0x4e20 + flags 0x1 + ---- + id 0x0 + data_size 0x0 + hdr_size 0x8 + offset 0x18 + +Note that more than one entry can be removed at a time. The ``--tags`` option +accepts multiple tag ID's. + +Conversely, TE's can be added to an existing TL. This is achieved through the +`add` command. + +.. code:: + + $ tlc add --entry 1 fdt.dtb tl.bin + + +The result of this modification is shown below: + +.. code:: + + $ tlc info tl.bin + + signature 0x4a0fb10b + checksum 0xe1 + version 0x1 + hdr_size 0x18 + alignment 0x3 + size 0x2a6f + total_size 0x4e20 + flags 0x1 + ---- + id 0x0 + data_size 0x0 + hdr_size 0x8 + offset 0x18 + ---- + id 0x1 + data_size 0x2a47 + hdr_size 0x8 + offset 0x20 + +Unpacking a Transfer List +~~~~~~~~~~~~~~~~~~~~~~~~~ + +Given a transfer list, ``tlc`` also provides a mechanism for extracting TE data. +Running the command ``unpack``, yields binary files containing data from all the TE's. + +.. code:: + + $ tlc create --size 20000 --fdt build/fvp/debug/fdts/fvp-base-gicv3-psci.dtb tl.bin + $ tlc unpack tl.bin + $ file te_1.bin + te_1.bin: Device Tree Blob version 17, size=10823, boot CPU=0, string block size=851, DT structure block size=9900 + +Validate a Transfer List +~~~~~~~~~~~~~~~~~~~~~~~~ + +``tlc validate`` provides a quick and simple mechanism for checking wether the TL +is compliant with version of the specification supported by the tool. It +performs the following checks: + +#. Validates the signature. +#. Ensures that the specified version is greater than or equal to the tool’s current version. +#. Verifies alignment criteria for all TE’s. + +YAML Config File Format +~~~~~~~~~~~~~~~~~~~~~~~ + +Example YAML config file: + +.. code:: + + execution_state: aarch32 + has_checksum: true + max_size: 4096 + entries: + - tag_id: 258 # entry point info + ep_info: + args: + - 67112968 + - 67112960 + - 0 + - 0 + - 0 + - 0 + - 0 + - 0 + h: + attr: 8 + type: 1 + version: 2 + pc: 67239936 + spsr: 467 + - tag_id: 3 # memory layout + addr: 8 + size: 8 + - tag_id: 1, # fdt + blob_file_path: "fdt.bin", + +`max_size` defaults to `0x1000`, `execution_state` defaults to `aarch64`, and `has_checksum` +defaults to `true`. + +The fields of the YAML file should match the fields in the specification for the transfer list. You +don't need to give the hdr_size or data_size fields. For example, a memory layout entry would have +an entry like: + +.. code:: + + tag_id: 3 + addr: 8 + size: 8 + +You can input blob files by giving paths to the current working directory. You can do this for any +TE type. For example, an FDT layout would have an entry like: + +.. code:: + + tag_id: 1, + blob_file_path: "fdt.bin", + +You can input C-types by giving its fields. For example, an entry point +info entry would have an entry like: + +.. code:: + + tag_id: 258 + ep_info: + args: + - 67112968 + - 67112960 + - 0 + - 0 + h: + attr: 8 + type: 1 + version: 2 + lr_svc: 0 + pc: 67239936 + spsr: 467 + +You can give the name of the tag instead of the tag id number. The valid tag names are in the +`transfer_entry_formats` dict in `tools/tlc/tlc/tl.py`_. Some examples are: + +* empty +* fdt +* hob_block +* hob_list + +You can input the attr field of entry_point_info as a string of flag +names separated by `|`. The names are taken from ep_info_exp.h in TF-A. +For example: + +.. code:: + + has_checksum: true + max_size: 4096 + entries: + - tag_id: 0x102 + ep_info: + args: + - 67112976 + - 67112960 + - 0 + - 0 + - 0 + - 0 + - 0 + - 0 + h: + attr: EP_NON_SECURE | EP_ST_ENABLE + type: 1 + version: 2 + pc: 67239936 + spsr: 965 + +-------------- + +*Copyright (c) 2024, Arm Limited. All rights reserved.* + +.. _Firmware Handoff specification: https://github.com/FirmwareHandoff/firmware_handoff/ +.. _tools/tlc/pyproject.toml: https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/refs/heads/master/tools/tlc/pyproject.toml +.. _tools/tlc/tlc/tl.py: https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/refs/heads/master/tools/tlc/tlc/tl.py diff --git a/drivers/arm/css/dsu/dsu.c b/drivers/arm/css/dsu/dsu.c new file mode 100644 index 00000000..f0e8df12 --- /dev/null +++ b/drivers/arm/css/dsu/dsu.c @@ -0,0 +1,135 @@ +/* + * Copyright (c) 2024, Arm Limited. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <assert.h> + +#include <arch.h> +#include <arch_helpers.h> +#include <common/bl_common.h> +#include <common/debug.h> +#include <drivers/arm/css/dsu.h> + +#include <plat/arm/common/plat_arm.h> +#include <plat/common/platform.h> + +/* + * Context structure that saves the state of DSU PMU registers + */ +cluster_pmu_state_t cluster_pmu_context[PLAT_ARM_CLUSTER_COUNT]; + +/**************************************************************************** + * This function, save_dsu_pmu_state, is designed to save the + * current state of the Performance Monitoring Unit (PMU) for a cluster. + * + * The function performs the following operations: + * 1. Saves the current values of several PMU registers + * (CLUSTERPMCR_EL1, CLUSTERPMCNTENSET_EL1, CLUSTERPMCCNTR_EL1, + * CLUSTERPMOVSSET_EL1, and CLUSTERPMSELR_EL1) into the cluster_pmu_state + * structure. + * + * 2. Disables the PMU event counting by + * clearing the E bit in the clusterpmcr_el1 register. + * + * 3. Iterates over the available PMU counters as + * determined by the read_cluster_eventctr_num() function. + * For each counter, it: + * a. Selects the counter by writing its index to CLUSTERPMSELR_EL1. + * b. Reads the current counter value (event count) and + * the event type being counted from CLUSTERPMXEVCNTR_EL1 and + * CLUSTERPMXEVTYPER_EL1 registers, respectively. + * + * This function is useful for preserving the DynamIQ Shared Unit's (DSU) + * PMU registers over a power cycle. + ***************************************************************************/ + +void save_dsu_pmu_state(cluster_pmu_state_t *cluster_pmu_state) +{ + unsigned int idx = 0U; + unsigned int cluster_eventctr_num = read_cluster_eventctr_num(); + + assert(cluster_pmu_state != 0); + + save_pmu_reg(cluster_pmu_state, clusterpmcr); + + write_clusterpmcr(cluster_pmu_state->clusterpmcr & + ~(CLUSTERPMCR_E_BIT)); + + save_pmu_reg(cluster_pmu_state, clusterpmcntenset); + + save_pmu_reg(cluster_pmu_state, clusterpmccntr); + + save_pmu_reg(cluster_pmu_state, clusterpmovsset); + + save_pmu_reg(cluster_pmu_state, clusterpmselr); + + for (idx = 0U ; idx < cluster_eventctr_num ; idx++) { + write_clusterpmselr(idx); + cluster_pmu_state->counter_val[idx] = read_clusterpmxevcntr(); + cluster_pmu_state->counter_type[idx] = read_clusterpmxevtyper(); + } +} + +void cluster_off_dsu_pmu_context_save(void) +{ + unsigned int cluster_pos; + + cluster_pos = (unsigned int) plat_cluster_id_by_mpidr(read_mpidr_el1()); + + save_dsu_pmu_state(&cluster_pmu_context[cluster_pos]); +} + +/***************************************************************************** + * This function, restore_dsu_pmu_state, restores the state of the + * Performance Monitoring Unit (PMU) from a previously saved state. + * + * The function performs the following operations: + * 1. Restores the CLUSTERPMCR_EL1 register with the + * saved value from the cluster_pmu_state structure. + * 2. Iterates over the available PMU counters as determined + * by the read_cluster_eventctr_num() function. For each counter, it: + * a. Selects the counter by writing its index to CLUSTERPMSELR_EL1. + * b. Restores the counter value (event count) and the event type to + * CLUSTERPMXEVCNTR_EL1 and CLUSTERPMXEVTYPER_EL1 registers, respectively + * 3. Restores several other PMU registers (CLUSTERPMSELR_EL1, + * CLUSTERPMOVSCLR_EL1, CLUSTERPMOVSSET_EL1, CLUSTERPMCCNTR_EL1, + * and CLUSTERPMCNTENSET_EL1) with their saved values. + * + *****************************************************************************/ +void restore_dsu_pmu_state(cluster_pmu_state_t *cluster_pmu_state) +{ + unsigned int idx = 0U; + unsigned int cluster_eventctr_num = read_cluster_eventctr_num(); + + assert(cluster_pmu_state != 0); + + for (idx = 0U ; idx < cluster_eventctr_num ; idx++) { + write_clusterpmselr(idx); + write_clusterpmxevcntr(cluster_pmu_state->counter_val[idx]); + write_clusterpmxevtyper(cluster_pmu_state->counter_type[idx]); + } + + restore_pmu_reg(cluster_pmu_state, clusterpmselr); + + write_clusterpmovsclr(~(uint32_t)cluster_pmu_state->clusterpmovsset); + + restore_pmu_reg(cluster_pmu_state, clusterpmovsset); + + restore_pmu_reg(cluster_pmu_state, clusterpmccntr); + + restore_pmu_reg(cluster_pmu_state, clusterpmcntenset); + + write_clusterpmcr(cluster_pmu_state->clusterpmcr); +} + +void cluster_on_dsu_pmu_context_restore(void) +{ + unsigned int cluster_pos; + + cluster_pos = (unsigned int) plat_cluster_id_by_mpidr(read_mpidr_el1()); + + restore_dsu_pmu_state(&cluster_pmu_context[cluster_pos]); +} + diff --git a/drivers/arm/css/scmi/scmi_common.c b/drivers/arm/css/scmi/scmi_common.c index ec749fb5..ca855fe7 100644 --- a/drivers/arm/css/scmi/scmi_common.c +++ b/drivers/arm/css/scmi/scmi_common.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2019, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2017-2024, Arm Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -9,6 +9,7 @@ #include <arch_helpers.h> #include <common/debug.h> #include <drivers/arm/css/scmi.h> +#include <drivers/delay_timer.h> #include "scmi_private.h" @@ -60,8 +61,10 @@ void scmi_send_sync_command(scmi_channel_t *ch) dmbsy(); /* Wait for channel to be free */ - while (!SCMI_IS_CHANNEL_FREE(mbx_mem->status)) - ; + while (!SCMI_IS_CHANNEL_FREE(mbx_mem->status)) { + if (ch->info->delay != 0) + udelay(ch->info->delay); + } /* * Ensure that any read to the SCMI payload area is done after reading diff --git a/drivers/arm/css/scp/css_sds.c b/drivers/arm/css/scp/css_sds.c index e42ee10d..d9965c67 100644 --- a/drivers/arm/css/scp/css_sds.c +++ b/drivers/arm/css/scp/css_sds.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2017, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2014-2024, Arm Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -20,7 +20,7 @@ int css_scp_boot_image_xfer(void *image, unsigned int image_size) int ret; unsigned int image_offset, image_flags; - ret = sds_init(); + ret = sds_init(SDS_SCP_AP_REGION_ID); if (ret != SDS_OK) { ERROR("SCP SDS initialization failed\n"); panic(); @@ -28,13 +28,15 @@ int css_scp_boot_image_xfer(void *image, unsigned int image_size) VERBOSE("Writing SCP image metadata\n"); image_offset = (uintptr_t) image - ARM_TRUSTED_SRAM_BASE; - ret = sds_struct_write(SDS_SCP_IMG_STRUCT_ID, SDS_SCP_IMG_ADDR_OFFSET, + ret = sds_struct_write(SDS_SCP_AP_REGION_ID, + SDS_SCP_IMG_STRUCT_ID, SDS_SCP_IMG_ADDR_OFFSET, &image_offset, SDS_SCP_IMG_ADDR_SIZE, SDS_ACCESS_MODE_NON_CACHED); if (ret != SDS_OK) goto sds_fail; - ret = sds_struct_write(SDS_SCP_IMG_STRUCT_ID, SDS_SCP_IMG_SIZE_OFFSET, + ret = sds_struct_write(SDS_SCP_AP_REGION_ID, + SDS_SCP_IMG_STRUCT_ID, SDS_SCP_IMG_SIZE_OFFSET, &image_size, SDS_SCP_IMG_SIZE_SIZE, SDS_ACCESS_MODE_NON_CACHED); if (ret != SDS_OK) @@ -42,7 +44,8 @@ int css_scp_boot_image_xfer(void *image, unsigned int image_size) VERBOSE("Marking SCP image metadata as valid\n"); image_flags = SDS_SCP_IMG_VALID_FLAG_BIT; - ret = sds_struct_write(SDS_SCP_IMG_STRUCT_ID, SDS_SCP_IMG_FLAG_OFFSET, + ret = sds_struct_write(SDS_SCP_AP_REGION_ID, + SDS_SCP_IMG_STRUCT_ID, SDS_SCP_IMG_FLAG_OFFSET, &image_flags, SDS_SCP_IMG_FLAG_SIZE, SDS_ACCESS_MODE_NON_CACHED); if (ret != SDS_OK) @@ -68,7 +71,8 @@ int css_scp_boot_ready(void) /* Wait for the SCP RAM Firmware to complete its initialization process */ while (retry > 0) { - ret = sds_struct_read(SDS_FEATURE_AVAIL_STRUCT_ID, 0, + ret = sds_struct_read(SDS_SCP_AP_REGION_ID, + SDS_FEATURE_AVAIL_STRUCT_ID, 0, &scp_feature_availability_flags, SDS_FEATURE_AVAIL_SIZE, SDS_ACCESS_MODE_NON_CACHED); diff --git a/drivers/arm/css/sds/sds.c b/drivers/arm/css/sds/sds.c index 1fb196c7..91f0a27a 100644 --- a/drivers/arm/css/sds/sds.c +++ b/drivers/arm/css/sds/sds.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2017-2024, Arm Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -15,40 +15,39 @@ #include "sds_private.h" -/* - * Variables used to track and maintain the state of the memory region reserved - * for usage by the SDS framework. - */ +/* Array of SDS memory region descriptions */ +static sds_region_desc_t *sds_regions; -/* Pointer to the base of the SDS memory region */ -static uintptr_t sds_mem_base; - -/* Size of the SDS memory region in bytes */ -static size_t sds_mem_size; +/* Total count of SDS memory regions */ +static unsigned int sds_region_cnt; /* * Perform some non-exhaustive tests to determine whether any of the fields * within a Structure Header contain obviously invalid data. * Returns SDS_OK on success, SDS_ERR_FAIL on error. */ -static int sds_struct_is_valid(uintptr_t header) +static int sds_struct_is_valid(unsigned int region_id, uintptr_t header) { size_t struct_size = GET_SDS_HEADER_STRUCT_SIZE(header); /* Zero is not a valid identifier */ - if (GET_SDS_HEADER_ID(header) == 0) + if (GET_SDS_HEADER_ID(header) == 0) { return SDS_ERR_FAIL; + } /* Check SDS Schema version */ - if (GET_SDS_HEADER_VERSION(header) == SDS_REGION_SCH_VERSION) + if (GET_SDS_HEADER_VERSION(header) == SDS_REGION_SCH_VERSION) { return SDS_ERR_FAIL; + } /* The SDS Structure sizes have to be multiple of 8 */ - if ((struct_size == 0) || ((struct_size % 8) != 0)) + if ((struct_size == 0) || ((struct_size % 8) != 0)) { return SDS_ERR_FAIL; + } - if (struct_size > sds_mem_size) + if (struct_size > sds_regions[region_id].size) { return SDS_ERR_FAIL; + } return SDS_OK; } @@ -57,10 +56,11 @@ static int sds_struct_is_valid(uintptr_t header) * Validate the SDS structure headers. * Returns SDS_OK on success, SDS_ERR_FAIL on error. */ -static int validate_sds_struct_headers(void) +static int validate_sds_struct_headers(unsigned int region_id) { unsigned int i, structure_count; uintptr_t header; + uintptr_t sds_mem_base = sds_regions[region_id].base; structure_count = GET_SDS_REGION_STRUCTURE_COUNT(sds_mem_base); @@ -71,7 +71,7 @@ static int validate_sds_struct_headers(void) /* Iterate over structure headers and validate each one */ for (i = 0; i < structure_count; i++) { - if (sds_struct_is_valid(header) != SDS_OK) { + if (sds_struct_is_valid(region_id, header) != SDS_OK) { WARN("SDS: Invalid structure header detected\n"); return SDS_ERR_FAIL; } @@ -84,10 +84,12 @@ static int validate_sds_struct_headers(void) * Get the structure header pointer corresponding to the structure ID. * Returns SDS_OK on success, SDS_ERR_STRUCT_NOT_FOUND on error. */ -static int get_struct_header(uint32_t structure_id, struct_header_t **header) +static int get_struct_header(unsigned int region_id, uint32_t structure_id, + struct_header_t **header) { unsigned int i, structure_count; uintptr_t current_header; + uintptr_t sds_mem_base = sds_regions[region_id].base; assert(header); @@ -116,12 +118,14 @@ static int get_struct_header(uint32_t structure_id, struct_header_t **header) * Returns SDS_OK if structure header exists else SDS_ERR_STRUCT_NOT_FOUND * if not found. */ -int sds_struct_exists(unsigned int structure_id) +int sds_struct_exists(unsigned int region_id, unsigned int structure_id) { struct_header_t *header = NULL; int ret; - ret = get_struct_header(structure_id, &header); + assert(region_id < sds_region_cnt); + + ret = get_struct_header(region_id, structure_id, &header); if (ret == SDS_OK) { assert(header); } @@ -136,18 +140,21 @@ int sds_struct_exists(unsigned int structure_id) * The `data` is the pointer to store the read data of size specified by `size`. * Returns SDS_OK on success or corresponding error codes on failure. */ -int sds_struct_read(uint32_t structure_id, unsigned int fld_off, - void *data, size_t size, sds_access_mode_t mode) +int sds_struct_read(unsigned int region_id, uint32_t structure_id, + unsigned int fld_off, void *data, size_t size, + sds_access_mode_t mode) { int status; uintptr_t field_base; struct_header_t *header = NULL; + assert(region_id < sds_region_cnt); + if (!data) return SDS_ERR_INVALID_PARAMS; /* Check if a structure with this ID exists */ - status = get_struct_header(structure_id, &header); + status = get_struct_header(region_id, structure_id, &header); if (status != SDS_OK) return status; @@ -182,18 +189,21 @@ int sds_struct_read(uint32_t structure_id, unsigned int fld_off, * The `data` is the pointer to data of size specified by `size`. * Returns SDS_OK on success or corresponding error codes on failure. */ -int sds_struct_write(uint32_t structure_id, unsigned int fld_off, - void *data, size_t size, sds_access_mode_t mode) +int sds_struct_write(unsigned int region_id, uint32_t structure_id, + unsigned int fld_off, void *data, size_t size, + sds_access_mode_t mode) { int status; uintptr_t field_base; struct_header_t *header = NULL; + assert(region_id < sds_region_cnt); + if (!data) return SDS_ERR_INVALID_PARAMS; /* Check if a structure with this ID exists */ - status = get_struct_header(structure_id, &header); + status = get_struct_header(region_id, structure_id, &header); if (status != SDS_OK) return status; @@ -226,15 +236,21 @@ int sds_struct_write(uint32_t structure_id, unsigned int fld_off, /* * Initialize the SDS driver. Also verifies the SDS version and sanity of - * the SDS structure headers. + * the SDS structure headers in the given SDS region. * Returns SDS_OK on success, SDS_ERR_FAIL on error. */ -int sds_init(void) +int sds_init(unsigned int region_id) { - sds_mem_base = (uintptr_t)PLAT_ARM_SDS_MEM_BASE; + if (sds_regions == NULL) { + sds_regions = plat_sds_get_regions(&sds_region_cnt); + } + + assert(region_id < sds_region_cnt); + + uintptr_t sds_mem_base = sds_regions[region_id].base; if (!IS_SDS_REGION_VALID(sds_mem_base)) { - WARN("SDS: No valid SDS Memory Region found\n"); + VERBOSE("SDS: No valid SDS Memory Region found\n"); return SDS_ERR_FAIL; } @@ -244,15 +260,16 @@ int sds_init(void) return SDS_ERR_FAIL; } - sds_mem_size = GET_SDS_REGION_SIZE(sds_mem_base); - if (sds_mem_size > PLAT_ARM_SDS_MEM_SIZE_MAX) { + sds_regions[region_id].size = GET_SDS_REGION_SIZE(sds_mem_base); + if (sds_regions[region_id].size > PLAT_ARM_SDS_MEM_SIZE_MAX) { WARN("SDS: SDS Memory Region exceeds size limit\n"); return SDS_ERR_FAIL; } - INFO("SDS: Detected SDS Memory Region (%zu bytes)\n", sds_mem_size); + INFO("SDS: Detected SDS Memory Region (%zu bytes)\n", + sds_regions[region_id].size); - if (validate_sds_struct_headers() != SDS_OK) + if (validate_sds_struct_headers(region_id) != SDS_OK) return SDS_ERR_FAIL; return SDS_OK; diff --git a/drivers/arm/dcc/dcc_console.c b/drivers/arm/dcc/dcc_console.c index 19c3450b..841c1fde 100644 --- a/drivers/arm/dcc/dcc_console.c +++ b/drivers/arm/dcc/dcc_console.c @@ -1,5 +1,6 @@ /* - * Copyright (c) 2015-2021, Xilinx Inc. + * Copyright (c) 2015-2022, Xilinx Inc. + * Copyright (c) 2022-2024, Advanced Micro Devices, Inc. All rights reserved. * Written by Michal Simek. * * SPDX-License-Identifier: BSD-3-Clause @@ -45,7 +46,7 @@ #define TIMEOUT_COUNT_US U(0x10624) struct dcc_console { - struct console console; + console_t console; }; static inline uint32_t __dcc_getstatus(void) @@ -147,13 +148,14 @@ static struct dcc_console dcc_console = { }, }; -int console_dcc_register(void) +int console_dcc_register(console_t *console) { - return console_register(&dcc_console.console); + memcpy(console, &dcc_console.console, sizeof(console_t)); + return console_register(console); } -void console_dcc_unregister(void) +void console_dcc_unregister(console_t *console) { - dcc_console_flush(&dcc_console.console); - (void)console_unregister(&dcc_console.console); + dcc_console_flush(console); + (void)console_unregister(console); } diff --git a/drivers/arm/gic/v3/arm_gicv3_common.c b/drivers/arm/gic/v3/arm_gicv3_common.c index 44898928..cc82ddb1 100644 --- a/drivers/arm/gic/v3/arm_gicv3_common.c +++ b/drivers/arm/gic/v3/arm_gicv3_common.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2017-2024, Arm Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -28,10 +28,13 @@ void arm_gicv3_distif_pre_save(unsigned int rdist_proc_num) { uintptr_t gicr_base = 0; + unsigned int typer_reg; assert(gicv3_driver_data); assert(gicv3_driver_data->rdistif_base_addrs); + assert(gicv3_driver_data->gicd_base != 0U); + typer_reg = gicd_read_typer(gicv3_driver_data->gicd_base); /* * The GICR_WAKER.Sleep bit should be set only when both * GICR_WAKER.ChildrenAsleep and GICR_WAKER.ProcessorSleep are set on @@ -60,9 +63,14 @@ void arm_gicv3_distif_pre_save(unsigned int rdist_proc_num) */ gicr_write_waker(gicr_base, gicr_read_waker(gicr_base) | WAKER_SL_BIT); - /* Wait until the GICR_WAKER.Quiescent bit is set */ - while (!(gicr_read_waker(gicr_base) & WAKER_QSC_BIT)) - ; + /* + * If LPIs are supported, wait until the GICR_WAKER.Quiescent bit is + * set. + */ + if ((typer_reg & TYPER_LPIS) != 0U) { + while (!(gicr_read_waker(gicr_base) & WAKER_QSC_BIT)) + ; + } } /* diff --git a/drivers/arm/gic/v3/gic600_multichip.c b/drivers/arm/gic/v3/gic600_multichip.c index a4786bb8..5e44aa95 100644 --- a/drivers/arm/gic/v3/gic600_multichip.c +++ b/drivers/arm/gic/v3/gic600_multichip.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019-2023, Arm Limited. All rights reserved. + * Copyright (c) 2019-2024, Arm Limited. All rights reserved. * Copyright (c) 2022-2023, NVIDIA Corporation. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause @@ -75,7 +75,7 @@ static void set_gicd_dchipr_rt_owner(uintptr_t base, unsigned int rt_owner) panic(); } - /* Poll till PUP is zero before intiating write */ + /* Poll till PUP is zero before initiating write */ gicd_dchipr_wait_for_power_update_progress(base); write_gicd_dchipr(base, read_gicd_dchipr(base) | diff --git a/drivers/arm/gic/v3/gicv3_main.c b/drivers/arm/gic/v3/gicv3_main.c index 3c995171..2be19db7 100644 --- a/drivers/arm/gic/v3/gicv3_main.c +++ b/drivers/arm/gic/v3/gicv3_main.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015-2023, Arm Limited and Contributors. All rights reserved. + * Copyright (c) 2015-2024, Arm Limited and Contributors. All rights reserved. * Copyright (c) 2023, NVIDIA Corporation. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause @@ -34,8 +34,8 @@ static spinlock_t gic_lock; #pragma weak gicv3_rdistif_off #pragma weak gicv3_rdistif_on -/* Check interrupt ID for SGI/(E)PPI and (E)SPIs */ -static bool is_sgi_ppi(unsigned int id); +/* Check for valid SGI/PPI or SPI interrupt ID */ +static bool is_valid_interrupt(unsigned int id); /* * Helper macros to save and restore GICR and GICD registers @@ -447,8 +447,12 @@ unsigned int gicv3_get_interrupt_group(unsigned int id, unsigned int proc_num) return INTR_GROUP1NS; } + if (!is_valid_interrupt(id)) { + panic(); + } + /* Check interrupt ID */ - if (is_sgi_ppi(id)) { + if (IS_SGI_PPI(id)) { /* SGIs: 0-15, PPIs: 16-31, EPPIs: 1056-1119 */ assert(gicv3_driver_data->rdistif_base_addrs != NULL); gicr_base = gicv3_driver_data->rdistif_base_addrs[proc_num]; @@ -682,6 +686,8 @@ void gicv3_rdistif_init_restore(unsigned int proc_num, gicr_write_ctlr(gicr_base, rdist_ctx->gicr_ctlr & ~(GICR_CTLR_EN_LPIS_BIT)); + gicr_wait_for_pending_write(gicr_base); + /* Restore registers' content */ gicr_write_propbaser(gicr_base, rdist_ctx->gicr_propbaser); gicr_write_pendbaser(gicr_base, rdist_ctx->gicr_pendbaser); @@ -942,8 +948,11 @@ unsigned int gicv3_get_interrupt_active(unsigned int id, unsigned int proc_num) assert(proc_num < gicv3_driver_data->rdistif_num); assert(gicv3_driver_data->rdistif_base_addrs != NULL); + if (!is_valid_interrupt(id)) { + panic(); + } /* Check interrupt ID */ - if (is_sgi_ppi(id)) { + if (IS_SGI_PPI(id)) { /* For SGIs: 0-15, PPIs: 16-31 and EPPIs: 1056-1119 */ return gicr_get_isactiver( gicv3_driver_data->rdistif_base_addrs[proc_num], id); @@ -973,9 +982,11 @@ void gicv3_enable_interrupt(unsigned int id, unsigned int proc_num) * interrupt trigger are observed before enabling interrupt. */ dsbishst(); - + if (!is_valid_interrupt(id)) { + panic(); + } /* Check interrupt ID */ - if (is_sgi_ppi(id)) { + if (IS_SGI_PPI(id)) { /* For SGIs: 0-15, PPIs: 16-31 and EPPIs: 1056-1119 */ gicr_set_isenabler( gicv3_driver_data->rdistif_base_addrs[proc_num], id); @@ -1004,9 +1015,11 @@ void gicv3_disable_interrupt(unsigned int id, unsigned int proc_num) * Disable interrupt, and ensure that any shared variable updates * depending on out of band interrupt trigger are observed afterwards. */ - + if (!is_valid_interrupt(id)) { + panic(); + } /* Check interrupt ID */ - if (is_sgi_ppi(id)) { + if (IS_SGI_PPI(id)) { /* For SGIs: 0-15, PPIs: 16-31 and EPPIs: 1056-1119 */ gicr_set_icenabler( gicv3_driver_data->rdistif_base_addrs[proc_num], id); @@ -1041,8 +1054,11 @@ void gicv3_set_interrupt_priority(unsigned int id, unsigned int proc_num, assert(proc_num < gicv3_driver_data->rdistif_num); assert(gicv3_driver_data->rdistif_base_addrs != NULL); + if (!is_valid_interrupt(id)) { + panic(); + } /* Check interrupt ID */ - if (is_sgi_ppi(id)) { + if (IS_SGI_PPI(id)) { /* For SGIs: 0-15, PPIs: 16-31 and EPPIs: 1056-1119 */ gicr_base = gicv3_driver_data->rdistif_base_addrs[proc_num]; gicr_set_ipriorityr(gicr_base, id, priority); @@ -1088,8 +1104,11 @@ void gicv3_set_interrupt_group(unsigned int id, unsigned int proc_num, break; } + if (!is_valid_interrupt(id)) { + panic(); + } /* Check interrupt ID */ - if (is_sgi_ppi(id)) { + if (IS_SGI_PPI(id)) { /* For SGIs: 0-15, PPIs: 16-31 and EPPIs: 1056-1119 */ gicr_base = gicv3_driver_data->rdistif_base_addrs[proc_num]; @@ -1228,12 +1247,14 @@ void gicv3_clear_interrupt_pending(unsigned int id, unsigned int proc_num) * Clear pending interrupt, and ensure that any shared variable updates * depending on out of band interrupt trigger are observed afterwards. */ - + if (!is_valid_interrupt(id)) { + panic(); + } /* Check interrupt ID */ - if (is_sgi_ppi(id)) { + if (IS_SGI_PPI(id)) { /* For SGIs: 0-15, PPIs: 16-31 and EPPIs: 1056-1119 */ gicr_set_icpendr( - gicv3_driver_data->rdistif_base_addrs[proc_num], id); + gicv3_driver_data->rdistif_base_addrs[proc_num], id); } else { /* For SPIs: 32-1019 and ESPIs: 4096-5119 */ gicd_base = gicv3_get_multichip_base(id, gicv3_driver_data->gicd_base); @@ -1263,8 +1284,12 @@ void gicv3_set_interrupt_pending(unsigned int id, unsigned int proc_num) */ dsbishst(); + if (!is_valid_interrupt(id)) { + panic(); + } + /* Check interrupt ID */ - if (is_sgi_ppi(id)) { + if (IS_SGI_PPI(id)) { /* For SGIs: 0-15, PPIs: 16-31 and EPPIs: 1056-1119 */ gicr_set_ispendr( gicv3_driver_data->rdistif_base_addrs[proc_num], id); @@ -1297,6 +1322,31 @@ unsigned int gicv3_set_pmr(unsigned int mask) return old_mask; } +/******************************************************************************* + * This function restores the PMR register to old value and also triggers + * gicv3_apply_errata_wa_2384374() that flushes the GIC buffer allowing any + * pending interrupts to processed. Returns the original PMR. + ******************************************************************************/ +unsigned int gicv3_deactivate_priority(unsigned int mask) +{ + + unsigned int old_mask, proc_num; + uintptr_t gicr_base; + + old_mask = gicv3_set_pmr(mask); + + proc_num = plat_my_core_pos(); + gicr_base = gicv3_driver_data->rdistif_base_addrs[proc_num]; + assert(gicr_base != 0UL); + + /* Add DSB to ensure visibility of System register writes */ + dsb(); + + gicv3_apply_errata_wa_2384374(gicr_base); + + return old_mask; +} + /******************************************************************************* * This function delegates the responsibility of discovering the corresponding * Redistributor frames to each CPU itself. It is a modified version of @@ -1371,21 +1421,19 @@ int gicv3_rdistif_probe(const uintptr_t gicr_frame) } /****************************************************************************** - * This function checks the interrupt ID and returns true for SGIs and (E)PPIs - * and false for (E)SPIs IDs. + * This function checks the interrupt ID and returns true for SGIs, (E)PPIs + * and (E)SPIs IDs. Any interrupt ID outside the range is invalid and returns + * false. *****************************************************************************/ -static bool is_sgi_ppi(unsigned int id) +static bool is_valid_interrupt(unsigned int id) { - /* SGIs: 0-15, PPIs: 16-31, EPPIs: 1056-1119 */ - if (IS_SGI_PPI(id)) { + /* Valid interrupts: + * SGIs: 0-15, PPIs: 16-31, EPPIs: 1056-1119 + * SPIs: 32-1019, ESPIs: 4096-5119 + */ + if ((IS_SGI_PPI(id)) || (IS_SPI(id))) { return true; } - /* SPIs: 32-1019, ESPIs: 4096-5119 */ - if (IS_SPI(id)) { - return false; - } - - assert(false); - panic(); + return false; } diff --git a/drivers/arm/mhu/mhu_v3_x.c b/drivers/arm/mhu/mhu_v3_x.c new file mode 100644 index 00000000..118c608e --- /dev/null +++ b/drivers/arm/mhu/mhu_v3_x.c @@ -0,0 +1,475 @@ +/* + * Copyright (c) 2024, Arm Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <assert.h> +#include <stdbool.h> +#include <stdint.h> + +#include "mhu_v3_x.h" + +#include "mhu_v3_x_private.h" + +/* + * Get the device base from the device struct. Return an error if the dev is + * invalid. + */ +static enum mhu_v3_x_error_t get_dev_base(const struct mhu_v3_x_dev_t *dev, + union _mhu_v3_x_frame_t **base) +{ + if (dev == NULL) { + return MHU_V_3_X_ERR_INVALID_PARAM; + } + + /* Ensure driver has been initialized */ + if (dev->is_initialized == false) { + return MHU_V_3_X_ERR_NOT_INIT; + } + + *base = (union _mhu_v3_x_frame_t *)dev->base; + + return MHU_V_3_X_ERR_NONE; +} + +enum mhu_v3_x_error_t mhu_v3_x_driver_init(struct mhu_v3_x_dev_t *dev) +{ + uint32_t aidr = 0; + uint8_t mhu_major_rev; + union _mhu_v3_x_frame_t *p_mhu; + + if (dev == NULL) { + return MHU_V_3_X_ERR_INVALID_PARAM; + } + + /* Return if already initialized */ + if (dev->is_initialized == true) { + return MHU_V_3_X_ERR_NONE; + } + + p_mhu = (union _mhu_v3_x_frame_t *)dev->base; + + /* Read revision from MHU hardware */ + if (dev->frame == MHU_V3_X_PBX_FRAME) { + aidr = p_mhu->pbx_frame.pbx_ctrl_page.pbx_aidr; + } else if (dev->frame == MHU_V3_X_MBX_FRAME) { + aidr = p_mhu->mbx_frame.mbx_ctrl_page.mbx_aidr; + } else { + /* Only PBX and MBX frames are supported. */ + return MHU_V_3_X_ERR_UNSUPPORTED; + } + + /* Read the MHU Architecture Major Revision */ + mhu_major_rev = + ((aidr & MHU_ARCH_MAJOR_REV_MASK) >> MHU_ARCH_MAJOR_REV_OFF); + + /* Return error if the MHU major revision is not 3 */ + if (mhu_major_rev != MHU_MAJOR_REV_V3) { + /* Unsupported MHU version */ + return MHU_V_3_X_ERR_UNSUPPORTED_VERSION; + } + + /* Read the MHU Architecture Minor Revision */ + dev->subversion = + ((aidr & MHU_ARCH_MINOR_REV_MASK) >> MHU_ARCH_MINOR_REV_MASK); + + /* Return error if the MHU minor revision is not 0 */ + if (dev->subversion != MHU_MINOR_REV_3_0) { + /* Unsupported subversion */ + return MHU_V_3_X_ERR_UNSUPPORTED_VERSION; + } + + /* Initialize the Postbox/Mailbox to remain in operational state */ + if (dev->frame == MHU_V3_X_PBX_FRAME) { + p_mhu->pbx_frame.pbx_ctrl_page.pbx_ctrl |= MHU_V3_OP_REQ; + } else if (dev->frame == MHU_V3_X_MBX_FRAME) { + p_mhu->mbx_frame.mbx_ctrl_page.mbx_ctrl |= MHU_V3_OP_REQ; + } else { + /* Only PBX and MBX frames are supported. */ + return MHU_V_3_X_ERR_UNSUPPORTED; + } + + dev->is_initialized = true; + + return MHU_V_3_X_ERR_NONE; +} + +enum mhu_v3_x_error_t mhu_v3_x_get_num_channel_implemented( + const struct mhu_v3_x_dev_t *dev, + enum mhu_v3_x_channel_type_t ch_type, uint8_t *num_ch) +{ + enum mhu_v3_x_error_t status; + union _mhu_v3_x_frame_t *p_mhu; + + if (num_ch == NULL) { + return MHU_V_3_X_ERR_INVALID_PARAM; + } + + /* Get dev->base if it is valid or return an error if dev is not */ + status = get_dev_base(dev, &p_mhu); + if (status != MHU_V_3_X_ERR_NONE) { + return status; + } + + /* Only doorbell channel is supported */ + if (ch_type != MHU_V3_X_CHANNEL_TYPE_DBCH) { + return MHU_V_3_X_ERR_UNSUPPORTED; + } + + /* Read the number of channels implemented in the MHU */ + if (dev->frame == MHU_V3_X_PBX_FRAME) { + *num_ch = (p_mhu->pbx_frame.pbx_ctrl_page.pbx_dbch_cfg0 + 1); + } else if (dev->frame == MHU_V3_X_MBX_FRAME) { + *num_ch = (p_mhu->mbx_frame.mbx_ctrl_page.mbx_dbch_cfg0 + 1); + } else { + /* Only PBX and MBX frames are supported. */ + return MHU_V_3_X_ERR_UNSUPPORTED; + } + + return MHU_V_3_X_ERR_NONE; +} + +enum mhu_v3_x_error_t mhu_v3_x_doorbell_clear(const struct mhu_v3_x_dev_t *dev, + const uint32_t channel, uint32_t flags) +{ + union _mhu_v3_x_frame_t *p_mhu; + struct _mhu_v3_x_mdbcw_reg_t *mdbcw_reg; + enum mhu_v3_x_error_t status; + + /* Get dev->base if it is valid or return an error if dev is not */ + status = get_dev_base(dev, &p_mhu); + if (status != MHU_V_3_X_ERR_NONE) { + return status; + } + + /* Only MBX can clear the Doorbell channel */ + if (dev->frame != MHU_V3_X_MBX_FRAME) { + return MHU_V_3_X_ERR_INVALID_PARAM; + } + + p_mhu = (union _mhu_v3_x_frame_t *)dev->base; + mdbcw_reg = (struct _mhu_v3_x_mdbcw_reg_t *) + &(p_mhu->mbx_frame.mdbcw_page); + + /* Clear the bits in the doorbell channel */ + mdbcw_reg[channel].mdbcw_clr |= flags; + + return MHU_V_3_X_ERR_NONE; +} + +enum mhu_v3_x_error_t mhu_v3_x_doorbell_write(const struct mhu_v3_x_dev_t *dev, + const uint32_t channel, uint32_t flags) +{ + union _mhu_v3_x_frame_t *p_mhu; + struct _mhu_v3_x_pdbcw_reg_t *pdbcw_reg; + enum mhu_v3_x_error_t status; + + /* Get dev->base if it is valid or return an error if dev is not */ + status = get_dev_base(dev, &p_mhu); + if (status != MHU_V_3_X_ERR_NONE) { + return status; + } + + /* Only PBX can set the Doorbell channel value */ + if (dev->frame != MHU_V3_X_PBX_FRAME) { + return MHU_V_3_X_ERR_INVALID_PARAM; + } + + p_mhu = (union _mhu_v3_x_frame_t *)dev->base; + + pdbcw_reg = (struct _mhu_v3_x_pdbcw_reg_t *) + &(p_mhu->pbx_frame.pdbcw_page); + + /* Write the value to the doorbell channel */ + pdbcw_reg[channel].pdbcw_set |= flags; + + return MHU_V_3_X_ERR_NONE; +} + +enum mhu_v3_x_error_t mhu_v3_x_doorbell_read(const struct mhu_v3_x_dev_t *dev, + const uint32_t channel, uint32_t *flags) +{ + union _mhu_v3_x_frame_t *p_mhu; + enum mhu_v3_x_error_t status; + struct _mhu_v3_x_mdbcw_reg_t *mdbcw_reg; + struct _mhu_v3_x_pdbcw_reg_t *pdbcw_reg; + + if (flags == NULL) { + return MHU_V_3_X_ERR_INVALID_PARAM; + } + + /* Get dev->base if it is valid or return an error if dev is not */ + status = get_dev_base(dev, &p_mhu); + if (status != MHU_V_3_X_ERR_NONE) { + return status; + } + + p_mhu = (union _mhu_v3_x_frame_t *)dev->base; + + if (dev->frame == MHU_V3_X_PBX_FRAME) { + pdbcw_reg = (struct _mhu_v3_x_pdbcw_reg_t *) + &(p_mhu->pbx_frame.pdbcw_page); + + /* Read the value from Postbox Doorbell status register */ + *flags = pdbcw_reg[channel].pdbcw_st; + } else if (dev->frame == MHU_V3_X_MBX_FRAME) { + mdbcw_reg = (struct _mhu_v3_x_mdbcw_reg_t *) + &(p_mhu->mbx_frame.mdbcw_page); + + /* Read the value from Mailbox Doorbell status register */ + *flags = mdbcw_reg[channel].mdbcw_st; + } else { + /* Only PBX and MBX frames are supported. */ + return MHU_V_3_X_ERR_UNSUPPORTED; + } + + return MHU_V_3_X_ERR_NONE; +} + +enum mhu_v3_x_error_t mhu_v3_x_doorbell_mask_set( + const struct mhu_v3_x_dev_t *dev, const uint32_t channel, + uint32_t flags) +{ + union _mhu_v3_x_frame_t *p_mhu; + struct _mhu_v3_x_mdbcw_reg_t *mdbcw_reg; + enum mhu_v3_x_error_t status; + + /* Get dev->base if it is valid or return an error if dev is not */ + status = get_dev_base(dev, &p_mhu); + if (status != MHU_V_3_X_ERR_NONE) { + return status; + } + + /* Doorbell channel mask is not applicable for PBX */ + if (dev->frame != MHU_V3_X_MBX_FRAME) { + return MHU_V_3_X_ERR_INVALID_PARAM; + } + + p_mhu = (union _mhu_v3_x_frame_t *)dev->base; + + mdbcw_reg = (struct _mhu_v3_x_mdbcw_reg_t *) + &(p_mhu->mbx_frame.mdbcw_page); + + /* Set the Doorbell channel mask */ + mdbcw_reg[channel].mdbcw_msk_set |= flags; + + return MHU_V_3_X_ERR_NONE; +} + +enum mhu_v3_x_error_t mhu_v3_x_doorbell_mask_clear( + const struct mhu_v3_x_dev_t *dev, const uint32_t channel, + uint32_t flags) +{ + union _mhu_v3_x_frame_t *p_mhu; + struct _mhu_v3_x_mdbcw_reg_t *mdbcw_reg; + enum mhu_v3_x_error_t status; + + /* Get dev->base if it is valid or return an error if dev is not */ + status = get_dev_base(dev, &p_mhu); + if (status != MHU_V_3_X_ERR_NONE) { + return status; + } + + /* Doorbell channel mask is not applicable for PBX */ + if (dev->frame != MHU_V3_X_MBX_FRAME) { + return MHU_V_3_X_ERR_INVALID_PARAM; + } + + p_mhu = (union _mhu_v3_x_frame_t *)dev->base; + + mdbcw_reg = (struct _mhu_v3_x_mdbcw_reg_t *) + &(p_mhu->mbx_frame.mdbcw_page); + + /* Clear the Doorbell channel mask */ + mdbcw_reg[channel].mdbcw_msk_clr = flags; + + return MHU_V_3_X_ERR_NONE; +} + +enum mhu_v3_x_error_t mhu_v3_x_doorbell_mask_get( + const struct mhu_v3_x_dev_t *dev, const uint32_t channel, + uint32_t *flags) +{ + union _mhu_v3_x_frame_t *p_mhu; + struct _mhu_v3_x_mdbcw_reg_t *mdbcw_reg; + enum mhu_v3_x_error_t status; + + if (flags == NULL) { + return MHU_V_3_X_ERR_INVALID_PARAM; + } + + /* Get dev->base if it is valid or return an error if dev is not */ + status = get_dev_base(dev, &p_mhu); + if (status != MHU_V_3_X_ERR_NONE) { + return status; + } + + /* Doorbell channel mask is not applicable for PBX */ + if (dev->frame != MHU_V3_X_MBX_FRAME) { + return MHU_V_3_X_ERR_INVALID_PARAM; + } + + p_mhu = (union _mhu_v3_x_frame_t *)dev->base; + + mdbcw_reg = (struct _mhu_v3_x_mdbcw_reg_t *) + &(p_mhu->mbx_frame.mdbcw_page); + + /* Save the Doorbell channel mask status */ + *flags = mdbcw_reg[channel].mdbcw_msk_st; + + return MHU_V_3_X_ERR_NONE; +} + +enum mhu_v3_x_error_t mhu_v3_x_channel_interrupt_enable( + const struct mhu_v3_x_dev_t *dev, const uint32_t channel, + enum mhu_v3_x_channel_type_t ch_type) +{ + enum mhu_v3_x_error_t status; + + union _mhu_v3_x_frame_t *p_mhu; + struct _mhu_v3_x_pdbcw_reg_t *pdbcw_reg; + struct _mhu_v3_x_mdbcw_reg_t *mdbcw_reg; + + /* Get dev->base if it is valid or return an error if dev is not */ + status = get_dev_base(dev, &p_mhu); + if (status != MHU_V_3_X_ERR_NONE) { + return status; + } + + /* Only doorbell channel is supported */ + if (ch_type != MHU_V3_X_CHANNEL_TYPE_DBCH) { + return MHU_V_3_X_ERR_UNSUPPORTED; + } + + p_mhu = (union _mhu_v3_x_frame_t *)dev->base; + + if (dev->frame == MHU_V3_X_PBX_FRAME) { + pdbcw_reg = (struct _mhu_v3_x_pdbcw_reg_t *) + &(p_mhu->pbx_frame.pdbcw_page); + + /* + * Enable this doorbell channel to generate interrupts for + * transfer acknowledge events. + */ + pdbcw_reg[channel].pdbcw_int_en = MHU_V3_X_PDBCW_INT_X_TFR_ACK; + + /* + * Enable this doorbell channel to contribute to the PBX + * combined interrupt. + */ + pdbcw_reg[channel].pdbcw_ctrl = MHU_V3_X_PDBCW_CTRL_PBX_COMB_EN; + } else if (dev->frame == MHU_V3_X_MBX_FRAME) { + mdbcw_reg = (struct _mhu_v3_x_mdbcw_reg_t *) + &(p_mhu->mbx_frame.mdbcw_page); + + /* + * Enable this doorbell channel to contribute to the MBX + * combined interrupt. + */ + mdbcw_reg[channel].mdbcw_ctrl = MHU_V3_X_MDBCW_CTRL_MBX_COMB_EN; + } else { + /* Only PBX and MBX frames are supported. */ + return MHU_V_3_X_ERR_UNSUPPORTED; + } + + return MHU_V_3_X_ERR_NONE; +} + +enum mhu_v3_x_error_t mhu_v3_x_channel_interrupt_disable( + const struct mhu_v3_x_dev_t *dev, const uint32_t channel, + enum mhu_v3_x_channel_type_t ch_type) +{ + enum mhu_v3_x_error_t status; + + union _mhu_v3_x_frame_t *p_mhu; + struct _mhu_v3_x_pdbcw_reg_t *pdbcw_reg; + struct _mhu_v3_x_mdbcw_reg_t *mdbcw_reg; + + /* Get dev->base if it is valid or return an error if dev is not */ + status = get_dev_base(dev, &p_mhu); + if (status != MHU_V_3_X_ERR_NONE) { + return status; + } + + /* Only doorbell channel is supported */ + if (ch_type != MHU_V3_X_CHANNEL_TYPE_DBCH) { + return MHU_V_3_X_ERR_UNSUPPORTED; + } + + p_mhu = (union _mhu_v3_x_frame_t *)dev->base; + + if (dev->frame == MHU_V3_X_PBX_FRAME) { + pdbcw_reg = (struct _mhu_v3_x_pdbcw_reg_t *) + &(p_mhu->pbx_frame.pdbcw_page); + + /* Clear channel transfer acknowledge event interrupt */ + pdbcw_reg[channel].pdbcw_int_clr = MHU_V3_X_PDBCW_INT_X_TFR_ACK; + + /* Disable channel transfer acknowledge event interrupt */ + pdbcw_reg[channel].pdbcw_int_en &= + ~(MHU_V3_X_PDBCW_INT_X_TFR_ACK); + + /* + * Disable this doorbell channel from contributing to the PBX + * combined interrupt. + */ + pdbcw_reg[channel].pdbcw_ctrl &= + ~(MHU_V3_X_PDBCW_CTRL_PBX_COMB_EN); + } else if (dev->frame == MHU_V3_X_MBX_FRAME) { + mdbcw_reg = (struct _mhu_v3_x_mdbcw_reg_t *) + &(p_mhu->mbx_frame.mdbcw_page); + + /* + * Disable this doorbell channel from contributing to the MBX + * combined interrupt. + */ + mdbcw_reg[channel].mdbcw_ctrl &= + ~(MHU_V3_X_MDBCW_CTRL_MBX_COMB_EN); + } else { + /* Only PBX and MBX frames are supported. */ + return MHU_V_3_X_ERR_UNSUPPORTED; + } + + return MHU_V_3_X_ERR_NONE; +} + +enum mhu_v3_x_error_t mhu_v3_x_channel_interrupt_clear( + const struct mhu_v3_x_dev_t *dev, const uint32_t channel, + enum mhu_v3_x_channel_type_t ch_type) +{ + enum mhu_v3_x_error_t status; + union _mhu_v3_x_frame_t *p_mhu; + struct _mhu_v3_x_pdbcw_reg_t *pdbcw_reg; + + /* Get dev->base if it is valid or return an error if dev is not */ + status = get_dev_base(dev, &p_mhu); + if (status != MHU_V_3_X_ERR_NONE) { + return status; + } + + /* Only doorbell channel is supported */ + if (ch_type != MHU_V3_X_CHANNEL_TYPE_DBCH) { + return MHU_V_3_X_ERR_UNSUPPORTED; + } + + /* + * Only postbox doorbell channel transfer acknowledge interrupt can be + * cleared manually. + * + * To clear MBX interrupt the unmasked status must be cleared using + * mhu_v3_x_doorbell_clear. + */ + if (dev->frame != MHU_V3_X_PBX_FRAME) { + return MHU_V_3_X_ERR_INVALID_PARAM; + } + + p_mhu = (union _mhu_v3_x_frame_t *)dev->base; + pdbcw_reg = (struct _mhu_v3_x_pdbcw_reg_t *)&( + p_mhu->pbx_frame.pdbcw_page); + + /* Clear channel transfer acknowledge event interrupt */ + pdbcw_reg[channel].pdbcw_int_clr |= 0x1; + + return MHU_V_3_X_ERR_NONE; +} diff --git a/drivers/arm/mhu/mhu_v3_x.h b/drivers/arm/mhu/mhu_v3_x.h new file mode 100644 index 00000000..a3a19503 --- /dev/null +++ b/drivers/arm/mhu/mhu_v3_x.h @@ -0,0 +1,226 @@ +/* + * Copyright (c) 2024, Arm Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef MHU_V3_X_H +#define MHU_V3_X_H + +#include <stdbool.h> +#include <stdint.h> + +/* MHU Architecture Major Revision 3 */ +#define MHU_MAJOR_REV_V3 U(0x2) +/* MHU Architecture Minor Revision 0 */ +#define MHU_MINOR_REV_3_0 U(0x0) + +/* MHU Architecture Major Revision offset */ +#define MHU_ARCH_MAJOR_REV_OFF U(0x4) +/* MHU Architecture Major Revision mask */ +#define MHU_ARCH_MAJOR_REV_MASK (U(0xf) << MHU_ARCH_MAJOR_REV_OFF) + +/* MHU Architecture Minor Revision offset */ +#define MHU_ARCH_MINOR_REV_OFF U(0x0) +/* MHU Architecture Minor Revision mask */ +#define MHU_ARCH_MINOR_REV_MASK (U(0xf) << MHU_ARCH_MINOR_REV_OFF) + +/* MHUv3 PBX/MBX Operational Request offset */ +#define MHU_V3_OP_REQ_OFF U(0) +/* MHUv3 PBX/MBX Operational Request */ +#define MHU_V3_OP_REQ (U(1) << MHU_V3_OP_REQ_OFF) + +/** + * MHUv3 error enumeration types + */ +enum mhu_v3_x_error_t { + /* No error */ + MHU_V_3_X_ERR_NONE, + /* MHU driver not initialized */ + MHU_V_3_X_ERR_NOT_INIT, + /* MHU driver alreary initialized */ + MHU_V_3_X_ERR_ALREADY_INIT, + /* MHU Revision not supported error */ + MHU_V_3_X_ERR_UNSUPPORTED_VERSION, + /* Operation not supported */ + MHU_V_3_X_ERR_UNSUPPORTED, + /* Invalid parameter */ + MHU_V_3_X_ERR_INVALID_PARAM, + /* General MHU driver error */ + MHU_V_3_X_ERR_GENERAL, +}; + +/** + * MHUv3 channel types + */ +enum mhu_v3_x_channel_type_t { + /* Doorbell channel */ + MHU_V3_X_CHANNEL_TYPE_DBCH, + /* Channel type count */ + MHU_V3_X_CHANNEL_TYPE_COUNT, +}; + +/** + * MHUv3 frame types + */ +enum mhu_v3_x_frame_t { + /* MHUv3 postbox frame */ + MHU_V3_X_PBX_FRAME, + /* MHUv3 mailbox frame */ + MHU_V3_X_MBX_FRAME, +}; + +/** + * MHUv3 device structure + */ +struct mhu_v3_x_dev_t { + /* Base address of the MHUv3 frame */ + uintptr_t base; + /* Type of the MHUv3 frame */ + enum mhu_v3_x_frame_t frame; + /* Minor revision of the MHUv3 */ + uint32_t subversion; + /* Flag to indicate if the MHUv3 is initialized */ + bool is_initialized; +}; + +/** + * Initializes the MHUv3 + * + * dev MHU device struct mhu_v3_x_dev_t + * + * Returns mhu_v3_x_error_t error code + */ +enum mhu_v3_x_error_t mhu_v3_x_driver_init(struct mhu_v3_x_dev_t *dev); + +/** + * Returns the number of channels implemented + * + * dev MHU device struct mhu_v3_x_dev_t + * ch_type MHU channel type mhu_v3_x_channel_type_t + * num_ch Pointer to the variable that will store the value + * + * Returns mhu_v3_x_error_t error code + */ +enum mhu_v3_x_error_t mhu_v3_x_get_num_channel_implemented( + const struct mhu_v3_x_dev_t *dev, enum mhu_v3_x_channel_type_t ch_type, + uint8_t *num_ch); + +/** + * Clear flags from a doorbell channel + * + * dev MHU device struct mhu_v3_x_dev_t + * channel Doorbell channel number + * flags Flags to be cleared from the channel + * + * Returns mhu_v3_x_error_t error code + */ +enum mhu_v3_x_error_t mhu_v3_x_doorbell_clear(const struct mhu_v3_x_dev_t *dev, + const uint32_t channel, uint32_t flags); + +/** + * Write flags to a doorbell channel + * + * dev MHU device struct mhu_v3_x_dev_t + * channel Doorbell channel number + * flags Flags to be written to the channel + * + * Returns mhu_v3_x_error_t error code + */ +enum mhu_v3_x_error_t mhu_v3_x_doorbell_write(const struct mhu_v3_x_dev_t *dev, + const uint32_t channel, uint32_t flags); + +/** + * Read value from a doorbell channel + * + * dev MHU device struct mhu_v3_x_dev_t + * channel Doorbell channel number + * flags Pointer to the variable that will store the flags read from the + * channel + * + * Returns mhu_v3_x_error_t error code + */ +enum mhu_v3_x_error_t mhu_v3_x_doorbell_read(const struct mhu_v3_x_dev_t *dev, + const uint32_t channel, uint32_t *flags); + +/** + * Set bits in a doorbell channel mask which is used to disable interrupts for + * received flags corresponding to the mask + * + * dev MHU device struct mhu_v3_x_dev_t + * channel Doorbell channel number + * flags Flags to set mask bits in this doorbell channel + * + * Returns mhu_v3_x_error_t error code + */ +enum mhu_v3_x_error_t mhu_v3_x_doorbell_mask_set( + const struct mhu_v3_x_dev_t *dev, const uint32_t channel, + uint32_t flags); + +/** + * Clear bits in a doorbell channel mask which is used to disable interrupts + * for received flags corresponding to the mask + * + * dev MHU device struct mhu_v3_x_dev_t + * channel Doorbell channel number + * flags Flags to clear mask bits in this doorbell channel + * + * Returns mhu_v3_x_error_t error code + */ +enum mhu_v3_x_error_t mhu_v3_x_doorbell_mask_clear( + const struct mhu_v3_x_dev_t *dev, const uint32_t channel, uint32_t flags); + +/** + * Get the mask of a doorbell channel which is used to disable interrupts for + * received flags corresponding to the mask + * + * dev MHU device struct mhu_v3_x_dev_t + * channel Doorbell channel number + * flags Pointer to the variable that will store the flags read from the + * mask value + * + * Returns mhu_v3_x_error_t error code + */ +enum mhu_v3_x_error_t mhu_v3_x_doorbell_mask_get( + const struct mhu_v3_x_dev_t *dev, const uint32_t channel, uint32_t *flags); + +/** + * Enable the channel interrupt + * + * dev MHU device struct mhu_v3_x_dev_t + * channel Doorbell channel number + * ch_type MHU channel type mhu_v3_x_channel_type_t + * + * Returns mhu_v3_x_error_t error code + */ +enum mhu_v3_x_error_t mhu_v3_x_channel_interrupt_enable( + const struct mhu_v3_x_dev_t *dev, const uint32_t channel, + enum mhu_v3_x_channel_type_t ch_type); + +/** + * Disable the channel interrupt + * + * dev MHU device struct mhu_v3_x_dev_t + * channel Doorbell channel number + * ch_type MHU channel type mhu_v3_x_channel_type_t + * + * Returns mhu_v3_x_error_t error code + */ +enum mhu_v3_x_error_t mhu_v3_x_channel_interrupt_disable( + const struct mhu_v3_x_dev_t *dev, const uint32_t channel, + enum mhu_v3_x_channel_type_t ch_type); + +/** + * Clear the channel interrupt + * + * dev MHU device struct mhu_v3_x_dev_t + * channel Doorbell channel number + * ch_type MHU channel type mhu_v3_x_channel_type_t + * + * Returns mhu_v3_x_error_t error code + */ +enum mhu_v3_x_error_t mhu_v3_x_channel_interrupt_clear( + const struct mhu_v3_x_dev_t *dev, const uint32_t channel, + enum mhu_v3_x_channel_type_t ch_type); + +#endif /* MHU_V3_X_H */ diff --git a/drivers/arm/mhu/mhu_v3_x_private.h b/drivers/arm/mhu/mhu_v3_x_private.h new file mode 100644 index 00000000..9594a2a8 --- /dev/null +++ b/drivers/arm/mhu/mhu_v3_x_private.h @@ -0,0 +1,222 @@ +/* + * Copyright (c) 2024, Arm Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef MHU_V3_X_PRIVATE_H +#define MHU_V3_X_PRIVATE_H + +#include <stdint.h> + +/* Flag for PDBCW Interrupt Transfer Acknowledgment */ +#define MHU_V3_X_PDBCW_INT_X_TFR_ACK 0x1 + +/* Flag for PDBCW CTRL Postbox combined interrupts enable */ +#define MHU_V3_X_PDBCW_CTRL_PBX_COMB_EN 0x1 + +/* Flag for MDBCW CTRL Mailbox combined interrupts enable */ +#define MHU_V3_X_MDBCW_CTRL_MBX_COMB_EN 0x1 + +/** + * Postbox control page structure + */ +struct _mhu_v3_x_pbx_ctrl_reg_t { + /* Offset: 0x000 (R/ ) Postbox Block Identifier */ + const volatile uint32_t pbx_blk_id; + /* Offset: 0x004 (R/ ) Reserved */ + const volatile uint8_t reserved_0[0x10 - 0x04]; + /* Offset: 0x010 (R/ ) Postbox Feature Support 0 */ + const volatile uint32_t pbx_feat_spt0; + /* Offset: 0x014 (R/ ) Postbox Feature Support 1 */ + const volatile uint32_t pbx_feat_spt1; + /* Offset: 0x018 (R/ ) Reserved */ + const volatile uint8_t reserved_1[0x20 - 0x18]; + /* Offset: 0x020 (R/ ) Postbox Doorbell Channel Configuration 0 */ + const volatile uint32_t pbx_dbch_cfg0; + /* Offset: 0x024 (R/ ) Reserved */ + const volatile uint8_t reserved_2[0x30 - 0x24]; + /* Offset: 0x030 (R/ ) Postbox FIFO Channel Configuration 0 */ + const volatile uint32_t pbx_ffch_cfg0; + /* Offset: 0x034 (R/ ) Reserved */ + const volatile uint8_t reserved_3[0x40 - 0x34]; + /* Offset: 0x040 (R/ ) Postbox Fast Channel Configuration 0 */ + const volatile uint32_t pbx_fch_cfg0; + /* Offset: 0x044 (R/ ) Reserved */ + const volatile uint8_t reserved_4[0x100 - 0x44]; + /* Offset: 0x100 (R/W) Postbox control */ + volatile uint32_t pbx_ctrl; + /* Offset: 0x164 (R/ ) Reserved */ + const volatile uint8_t reserved_5[0x400 - 0x104]; + /* + * Offset: 0x400 (R/ ) Postbox Doorbell Channel Interrupt Status n, + * where n is 0 - 3. + */ + const volatile uint32_t pbx_dbch_int_st[4]; + /* + * Offset: 0x410 (R/ ) Postbox FIFO Channel <n> Interrupt Status n, + * where n is 0 - 1. + */ + const volatile uint32_t pbx_ffch_int_st[2]; + /* Offset: 0x418 (R/ ) Reserved */ + const uint8_t reserved_6[0xFC8 - 0x418]; + /* Offset: 0xFC8 (R/ ) Postbox Implementer Identification Register */ + const volatile uint32_t pbx_iidr; + /* Offset: 0xFCC (R/ ) Postbox Architecture Identification Register */ + const volatile uint32_t pbx_aidr; + /* + * Offset: 0xFD0 (R/ ) Postbox Implementation Defined Identification + * Register n, where n is 0 - 11. + */ + const volatile uint32_t impl_def_id[12]; +}; + +/** + * Postbox doorbell channel window page structure + */ +struct _mhu_v3_x_pdbcw_reg_t { + /* Offset: 0x000 (R/ ) Postbox Doorbell Channel Window Status */ + const volatile uint32_t pdbcw_st; + /* Offset: 0x004 (R/ ) Reserved */ + const uint8_t reserved_0[0xC - 0x4]; + /* Offset: 0x00C ( /W) Postbox Doorbell Channel Window Set */ + volatile uint32_t pdbcw_set; + /* + * Offset: 0x010 (R/ ) Postbox Doorbell Channel Window Interrupt Status + */ + const volatile uint32_t pdbcw_int_st; + /* + * Offset: 0x014 ( /W) Postbox Doorbell Channel Window Interrupt Clear + */ + volatile uint32_t pdbcw_int_clr; + /* + * Offset: 0x018 (R/W) Postbox Doorbell Channel Window Interrupt Enable + */ + volatile uint32_t pdbcw_int_en; + /* Offset: 0x01C (R/W) Postbox Doorbell Channel Window Control */ + volatile uint32_t pdbcw_ctrl; +}; + +/** + * Postbox structure + */ +struct _mhu_v3_x_pbx { + /* Postbox Control */ + struct _mhu_v3_x_pbx_ctrl_reg_t pbx_ctrl_page; + /* Postbox Doorbell Channel Window */ + struct _mhu_v3_x_pdbcw_reg_t pdbcw_page; +}; + +/** + * Mailbox control page structure + */ +struct _mhu_v3_x_mbx_ctrl_reg_t { + /* Offset: 0x000 (R/ ) Mailbox Block Identifier */ + const volatile uint32_t mbx_blk_id; + /* Offset: 0x004 (R/ ) Reserved */ + const volatile uint8_t reserved_0[0x10 - 0x04]; + /* Offset: 0x010 (R/ ) Mailbox Feature Support 0 */ + const volatile uint32_t mbx_feat_spt0; + /* Offset: 0x014 (R/ ) Mailbox Feature Support 1 */ + const volatile uint32_t mbx_feat_spt1; + /* Offset: 0x018 (R/ ) Reserved */ + const volatile uint8_t reserved_1[0x20 - 0x18]; + /* Offset: 0x020 (R/ ) Mailbox Doorbell Channel Configuration 0 */ + const volatile uint32_t mbx_dbch_cfg0; + /* Offset: 0x024 (R/ ) Reserved */ + const volatile uint8_t reserved_2[0x30 - 0x24]; + /* Offset: 0x030 (R/ ) Mailbox FIFO Channel Configuration 0 */ + const volatile uint32_t mbx_ffch_cfg0; + /* Offset: 0x034 (R/ ) Reserved */ + const volatile uint8_t reserved_4[0x40 - 0x34]; + /* Offset: 0x040 (R/ ) Mailbox Fast Channel Configuration 0 */ + const volatile uint32_t mbx_fch_cfg0; + /* Offset: 0x044 (R/ ) Reserved */ + const volatile uint8_t reserved_5[0x100 - 0x44]; + /* Offset: 0x100 (R/W) Mailbox control */ + volatile uint32_t mbx_ctrl; + /* Offset: 0x104 (R/ ) Reserved */ + const volatile uint8_t reserved_6[0x140 - 0x104]; + /* Offset: 0x140 (R/W) Mailbox Fast Channel control */ + volatile uint32_t mbx_fch_ctrl; + /* Offset: 0x144 (R/W) Mailbox Fast Channel Group Interrupt Enable */ + volatile uint32_t mbx_fcg_int_en; + /* Offset: 0x148 (R/ ) Reserved */ + const volatile uint8_t reserved_7[0x400 - 0x148]; + /* + * Offset: 0x400 (R/ ) Mailbox Doorbell Channel Interrupt Status n, + * where n = 0 - 3. + */ + const volatile uint32_t mbx_dbch_int_st[4]; + /* + * Offset: 0x410 (R/ ) Mailbox FIFO Channel Interrupt Status n, where + * n = 0 - 1. + */ + const volatile uint32_t mbx_ffch_int_st[2]; + /* Offset: 0x418 (R/ ) Reserved */ + const volatile uint8_t reserved_8[0x470 - 0x418]; + /* Offset: 0x470 (R/ ) Mailbox Fast Channel Group Interrupt Status */ + const volatile uint32_t mbx_fcg_int_st; + /* Offset: 0x474 (R/ ) Reserved */ + const volatile uint8_t reserved_9[0x480 - 0x474]; + /* + * Offset: 0x480 (R/ ) Mailbox Fast Channel Group <n> Interrupt Status, + * where n = 0 - 31. + */ + const volatile uint32_t mbx_fch_grp_int_st[32]; + /* Offset: 0x500 (R/ ) Reserved */ + const volatile uint8_t reserved_10[0xFC8 - 0x500]; + /* Offset: 0xFC8 (R/ ) Mailbox Implementer Identification Register */ + const volatile uint32_t mbx_iidr; + /* Offset: 0xFCC (R/ ) Mailbox Architecture Identification Register */ + const volatile uint32_t mbx_aidr; + /* + * Offset: 0xFD0 (R/ ) Mailbox Implementation Defined Identification + * Register n, where n is 0 - 11. + */ + const volatile uint32_t impl_def_id[12]; +}; + +/** + * Mailbox doorbell channel window page structure + */ +struct _mhu_v3_x_mdbcw_reg_t { + /* Offset: 0x000 (R/ ) Mailbox Doorbell Channel Window Status */ + const volatile uint32_t mdbcw_st; + /* Offset: 0x004 (R/ ) Mailbox Doorbell Channel Window Status Masked */ + const volatile uint32_t mdbcw_st_msk; + /* Offset: 0x008 ( /W) Mailbox Doorbell Channel Window Clear */ + volatile uint32_t mdbcw_clr; + /* Offset: 0x00C (R/ ) Reserved */ + const volatile uint8_t reserved_0[0x10 - 0x0C]; + /* Offset: 0x010 (R/ ) Mailbox Doorbell Channel Window Mask Status */ + const volatile uint32_t mdbcw_msk_st; + /* Offset: 0x014 ( /W) Mailbox Doorbell Channel Window Mask Set */ + volatile uint32_t mdbcw_msk_set; + /* Offset: 0x018 ( /W) Mailbox Doorbell Channel Window Mask Clear */ + volatile uint32_t mdbcw_msk_clr; + /* Offset: 0x01C (R/W) Mailbox Doorbell Channel Window Control */ + volatile uint32_t mdbcw_ctrl; +}; + +/** + * Mailbox structure + */ +struct _mhu_v3_x_mbx { + /* Mailbox control */ + struct _mhu_v3_x_mbx_ctrl_reg_t mbx_ctrl_page; + /* Mailbox Doorbell Channel Window */ + struct _mhu_v3_x_mdbcw_reg_t mdbcw_page; +}; + +/** + * MHUv3 frame type + */ +union _mhu_v3_x_frame_t { + /* Postbox Frame */ + struct _mhu_v3_x_pbx pbx_frame; + /* Mailbox Frame */ + struct _mhu_v3_x_mbx mbx_frame; +}; + +#endif /* MHU_V3_X_PRIVATE_H */ diff --git a/drivers/arm/mhu/mhu_wrapper_v2_x.c b/drivers/arm/mhu/mhu_wrapper_v2_x.c index 60de1d38..54a58812 100644 --- a/drivers/arm/mhu/mhu_wrapper_v2_x.c +++ b/drivers/arm/mhu/mhu_wrapper_v2_x.c @@ -308,5 +308,10 @@ size_t mhu_get_max_message_size(void) assert(num_channels != 0); - return num_channels * sizeof(uint32_t); + /* + * Returns only usable size of memory. As one channel is specifically + * used to inform about the size of payload, discard it from avialable + * memory size. + */ + return (num_channels - 1) * sizeof(uint32_t); } diff --git a/drivers/arm/mhu/mhu_wrapper_v3_x.c b/drivers/arm/mhu/mhu_wrapper_v3_x.c new file mode 100644 index 00000000..3efd7017 --- /dev/null +++ b/drivers/arm/mhu/mhu_wrapper_v3_x.c @@ -0,0 +1,462 @@ +/* + * Copyright (c) 2024, Arm Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <assert.h> +#include <stddef.h> +#include <stdint.h> +#include <string.h> + +#include <drivers/arm/mhu.h> + +#include "mhu_v3_x.h" + +#define MHU_NOTIFY_VALUE U(1234) + +#ifndef ALIGN_UP +#define ALIGN_UP(num, align) (((num) + ((align) - 1)) & ~((align) - 1)) +#endif + +/* + * MHUv3 Wrapper utility macros + */ +#define IS_ALIGNED(val, align) (val == ALIGN_UP(val, align)) + +/* + * MHU devices for host: + * HSE: Host to Secure Enclave (sender device) + * SEH: Secure Enclave to Host (receiver device) + */ +struct mhu_v3_x_dev_t mhu_hse_dev = {0, MHU_V3_X_PBX_FRAME}; +struct mhu_v3_x_dev_t mhu_seh_dev = {0, MHU_V3_X_MBX_FRAME}; + +/* MHUv3 driver error to MHUv3 wrapper error mapping */ +static enum mhu_error_t error_mapping_to_mhu_error_t(enum mhu_v3_x_error_t err) +{ + switch (err) { + case MHU_V_3_X_ERR_NONE: + return MHU_ERR_NONE; + + case MHU_V_3_X_ERR_NOT_INIT: + return MHU_ERR_NOT_INIT; + + case MHU_V_3_X_ERR_UNSUPPORTED_VERSION: + return MHU_ERR_UNSUPPORTED_VERSION; + + case MHU_V_3_X_ERR_UNSUPPORTED: + return MHU_ERR_UNSUPPORTED; + + case MHU_V_3_X_ERR_INVALID_PARAM: + return MHU_ERR_INVALID_ARG; + + default: + return MHU_ERR_GENERAL; + } +} + +static enum mhu_error_t signal_and_wait_for_clear( + void *mhu_sender_dev, uint32_t value) +{ + enum mhu_v3_x_error_t err; + struct mhu_v3_x_dev_t *dev; + uint8_t num_channels; + uint32_t read_val; + + dev = (struct mhu_v3_x_dev_t *)mhu_sender_dev; + + if ((dev == NULL) || (dev->base == 0)) { + return MHU_ERR_INVALID_ARG; + } + + err = mhu_v3_x_get_num_channel_implemented(dev, + MHU_V3_X_CHANNEL_TYPE_DBCH, &num_channels); + if (err != MHU_V_3_X_ERR_NONE) { + return error_mapping_to_mhu_error_t(err); + } + + /* Wait for any pending acknowledgment from transmitter side */ + do { + err = mhu_v3_x_doorbell_read(dev, num_channels - 1, &read_val); + if (err != MHU_V_3_X_ERR_NONE) { + return error_mapping_to_mhu_error_t(err); + } + } while ((read_val & value) == value); + + /* Use the last channel to notify that a transfer is ready */ + err = mhu_v3_x_doorbell_write(dev, num_channels - 1, value); + if (err != MHU_V_3_X_ERR_NONE) { + return error_mapping_to_mhu_error_t(err); + } + + /* Wait until receiver side acknowledges the transfer */ + do { + err = mhu_v3_x_doorbell_read(dev, num_channels - 1, &read_val); + if (err != MHU_V_3_X_ERR_NONE) { + return error_mapping_to_mhu_error_t(err); + } + } while ((read_val & value) == value); + + return error_mapping_to_mhu_error_t(MHU_V_3_X_ERR_NONE); +} + +static enum mhu_error_t wait_for_signal( + void *mhu_receiver_dev, uint32_t value) +{ + enum mhu_v3_x_error_t err; + struct mhu_v3_x_dev_t *dev; + uint32_t read_val; + uint8_t num_channels; + + dev = (struct mhu_v3_x_dev_t *)mhu_receiver_dev; + + if ((dev == NULL) || (dev->base == 0)) { + return MHU_ERR_INVALID_ARG; + } + + err = mhu_v3_x_get_num_channel_implemented(dev, + MHU_V3_X_CHANNEL_TYPE_DBCH, &num_channels); + if (err != MHU_V_3_X_ERR_NONE) { + return error_mapping_to_mhu_error_t(err); + } + + do { + err = mhu_v3_x_doorbell_read(dev, num_channels - 1, &read_val); + if (err != MHU_V_3_X_ERR_NONE) { + return error_mapping_to_mhu_error_t(err); + } + } while (read_val != value); + + return error_mapping_to_mhu_error_t(err); +} + +static enum mhu_error_t clear_and_wait_for_signal( + void *mhu_receiver_dev, uint32_t value) +{ + enum mhu_v3_x_error_t err; + struct mhu_v3_x_dev_t *dev; + uint8_t num_channels; + + dev = (struct mhu_v3_x_dev_t *)mhu_receiver_dev; + + if ((dev == NULL) || (dev->base == 0)) { + return MHU_ERR_INVALID_ARG; + } + + err = mhu_v3_x_get_num_channel_implemented(dev, + MHU_V3_X_CHANNEL_TYPE_DBCH, &num_channels); + if (err != MHU_V_3_X_ERR_NONE) { + return error_mapping_to_mhu_error_t(err); + } + + /* Clear all channels */ + for (int i = 0; i < num_channels; i++) { + err = mhu_v3_x_doorbell_clear(dev, i, UINT32_MAX); + if (err != MHU_V_3_X_ERR_NONE) { + return error_mapping_to_mhu_error_t(err); + } + } + + return wait_for_signal(mhu_receiver_dev, value); +} + +static enum mhu_error_t validate_buffer_params(uintptr_t buf_addr) +{ + if ((buf_addr == 0) || (!IS_ALIGNED(buf_addr, sizeof(uint32_t)))) { + return MHU_ERR_INVALID_ARG; + } + + return MHU_ERR_NONE; +} + +enum mhu_error_t mhu_init_sender(uintptr_t mhu_sender_base) +{ + enum mhu_v3_x_error_t err; + struct mhu_v3_x_dev_t *dev; + uint8_t num_ch; + uint32_t ch; + + assert(mhu_sender_base != (uintptr_t)NULL); + + mhu_hse_dev.base = mhu_sender_base; + dev = (struct mhu_v3_x_dev_t *)&mhu_hse_dev; + + /* Initialize MHUv3 */ + err = mhu_v3_x_driver_init(dev); + if (err != MHU_V_3_X_ERR_NONE) { + return error_mapping_to_mhu_error_t(err); + } + + /* Read the number of doorbell channels implemented in the MHU */ + err = mhu_v3_x_get_num_channel_implemented( + dev, MHU_V3_X_CHANNEL_TYPE_DBCH, &num_ch); + if (err != MHU_V_3_X_ERR_NONE) { + return error_mapping_to_mhu_error_t(err); + } else if (num_ch < 2) { + /* This wrapper requires at least two channels implemented */ + return MHU_ERR_UNSUPPORTED; + } + + /* + * The sender polls the postbox doorbell channel window status register + * to get notified about successful transfer. So, disable the doorbell + * channel's contribution to postbox combined interrupt. + * + * Also, clear and disable the postbox doorbell channel transfer + * acknowledge interrupt. + */ + for (ch = 0; ch < num_ch; ch++) { + err = mhu_v3_x_channel_interrupt_disable( + dev, ch, MHU_V3_X_CHANNEL_TYPE_DBCH); + if (err != MHU_V_3_X_ERR_NONE) { + return error_mapping_to_mhu_error_t(err); + } + } + + return MHU_ERR_NONE; +} + +enum mhu_error_t mhu_init_receiver(uintptr_t mhu_receiver_base) +{ + enum mhu_v3_x_error_t err; + struct mhu_v3_x_dev_t *dev; + uint32_t ch; + uint8_t num_ch; + + assert(mhu_receiver_base != (uintptr_t)NULL); + + mhu_seh_dev.base = mhu_receiver_base; + dev = (struct mhu_v3_x_dev_t *)&mhu_seh_dev; + + /* Initialize MHUv3 */ + err = mhu_v3_x_driver_init(dev); + if (err != MHU_V_3_X_ERR_NONE) { + return error_mapping_to_mhu_error_t(err); + } + + /* Read the number of doorbell channels implemented in the MHU */ + err = mhu_v3_x_get_num_channel_implemented( + dev, MHU_V3_X_CHANNEL_TYPE_DBCH, &num_ch); + if (err != MHU_V_3_X_ERR_NONE) { + return error_mapping_to_mhu_error_t(err); + } else if (num_ch < 2) { + /* This wrapper requires at least two channels implemented */ + return MHU_ERR_UNSUPPORTED; + } + + /* Mask all channels except the notifying channel */ + for (ch = 0; ch < (num_ch - 1); ch++) { + /* Mask interrupts on channels used for data */ + err = mhu_v3_x_doorbell_mask_set(dev, ch, UINT32_MAX); + if (err != MHU_V_3_X_ERR_NONE) { + return error_mapping_to_mhu_error_t(err); + } + } + + /* Unmask doorbell notification channel interrupt */ + err = mhu_v3_x_doorbell_mask_clear(dev, (num_ch - 1), UINT32_MAX); + if (err != MHU_V_3_X_ERR_NONE) { + return error_mapping_to_mhu_error_t(err); + } + + /* + * Enable the doorbell channel's contribution to mailbox combined + * interrupt. + */ + err = mhu_v3_x_channel_interrupt_enable(dev, (num_ch - 1), + MHU_V3_X_CHANNEL_TYPE_DBCH); + if (err != MHU_V_3_X_ERR_NONE) { + return error_mapping_to_mhu_error_t(err); + } + + return MHU_ERR_NONE; +} + +/* + * Public function. See mhu.h + * + * The basic steps of transferring a message: + * 1. Send the size of the payload on Channel 0. It is the very first Bytes of + * the transfer. Continue with Channel 1. + * 2. Send the payload, writing the channels one after the other (4 Bytes + * each). The last available channel is reserved for controlling the + * transfer. When the last channel is reached or no more data is left, STOP. + * 3. Notify the receiver using the last channel and wait for acknowledge. If + * there is still data to transfer, jump to step 2. Otherwise, proceed. + * + */ +enum mhu_error_t mhu_send_data(const uint8_t *send_buffer, size_t size) +{ + enum mhu_error_t mhu_err; + enum mhu_v3_x_error_t mhu_v3_err; + uint8_t num_channels; + uint8_t chan; + uint32_t *buffer; + struct mhu_v3_x_dev_t *dev; + + if (size == 0) { + return MHU_ERR_NONE; + } + + dev = (struct mhu_v3_x_dev_t *)&mhu_hse_dev; + chan = 0; + + if ((dev == NULL) || (dev->base == 0)) { + return MHU_ERR_INVALID_ARG; + } + + mhu_err = validate_buffer_params((uintptr_t)send_buffer); + if (mhu_err != MHU_ERR_NONE) { + return mhu_err; + } + + mhu_v3_err = mhu_v3_x_get_num_channel_implemented(dev, + MHU_V3_X_CHANNEL_TYPE_DBCH, &num_channels); + if (mhu_v3_err != MHU_V_3_X_ERR_NONE) { + return error_mapping_to_mhu_error_t(mhu_v3_err); + } + + /* First send the size of the actual message. */ + mhu_v3_err = mhu_v3_x_doorbell_write(dev, chan, (uint32_t)size); + if (mhu_v3_err != MHU_V_3_X_ERR_NONE) { + return error_mapping_to_mhu_error_t(mhu_v3_err); + } + chan++; + + buffer = (uint32_t *)send_buffer; + for (size_t i = 0; i < size; i += 4) { + mhu_v3_err = mhu_v3_x_doorbell_write(dev, chan, *buffer++); + if (mhu_v3_err != MHU_V_3_X_ERR_NONE) { + return error_mapping_to_mhu_error_t(mhu_v3_err); + } + + if (++chan == (num_channels - 1)) { + /* Use the last channel to notify transfer complete */ + mhu_err = signal_and_wait_for_clear( + dev, MHU_NOTIFY_VALUE); + if (mhu_err != MHU_ERR_NONE) { + return mhu_err; + } + chan = 0; + } + } + + if (chan != 0) { + /* Use the last channel to notify transfer complete */ + mhu_err = signal_and_wait_for_clear(dev, MHU_NOTIFY_VALUE); + if (mhu_err != MHU_ERR_NONE) { + return mhu_err; + } + } + + return MHU_ERR_NONE; +} + +/* + * Public function. See mhu.h + * + * The basic steps of receiving a message: + * 1. Read the size of the payload from Channel 0. It is the very first + * 4 Bytes of the transfer. Continue with Channel 1. + * 2. Receive the payload, read the channels one after the other + * (4 Bytes each). The last available channel is reserved for controlling + * the transfer. + * When the last channel is reached clear all the channels + * (also sending an acknowledge on the last channel). + * 3. If there is still data to receive wait for a notification on the last + * channel and jump to step 2 as soon as it arrived. Otherwise, proceed. + * + */ +enum mhu_error_t mhu_receive_data(uint8_t *receive_buffer, size_t *size) +{ + enum mhu_error_t mhu_err; + enum mhu_v3_x_error_t mhu_v3_err; + uint32_t msg_len; + uint8_t num_channels; + uint8_t chan; + uint32_t *buffer; + struct mhu_v3_x_dev_t *dev; + + dev = (struct mhu_v3_x_dev_t *)&mhu_seh_dev; + chan = 0; + + mhu_err = validate_buffer_params((uintptr_t)receive_buffer); + if (mhu_err != MHU_ERR_NONE) { + return mhu_err; + } + + mhu_v3_err = mhu_v3_x_get_num_channel_implemented(dev, + MHU_V3_X_CHANNEL_TYPE_DBCH, &num_channels); + if (mhu_v3_err != MHU_V_3_X_ERR_NONE) { + return error_mapping_to_mhu_error_t(mhu_v3_err); + } + + /* Busy wait for incoming reply */ + mhu_err = wait_for_signal(dev, MHU_NOTIFY_VALUE); + if (mhu_err != MHU_ERR_NONE) { + return mhu_err; + } + + /* The first word is the length of the actual message. */ + mhu_v3_err = mhu_v3_x_doorbell_read(dev, chan, &msg_len); + if (mhu_v3_err != MHU_V_3_X_ERR_NONE) { + return error_mapping_to_mhu_error_t(mhu_v3_err); + } + chan++; + + if (*size < msg_len) { + /* Message buffer too small */ + *size = msg_len; + return MHU_ERR_BUFFER_TOO_SMALL; + } + + buffer = (uint32_t *)receive_buffer; + for (size_t i = 0; i < msg_len; i += 4) { + mhu_v3_err = mhu_v3_x_doorbell_read(dev, chan, buffer++); + if (mhu_v3_err != MHU_V_3_X_ERR_NONE) { + return error_mapping_to_mhu_error_t(mhu_v3_err); + } + + /* Only wait for next transfer if still missing data. */ + if (++chan == (num_channels - 1) && (msg_len - i) > 4) { + /* Busy wait for next transfer */ + mhu_err = clear_and_wait_for_signal( + dev, MHU_NOTIFY_VALUE); + if (mhu_err != MHU_ERR_NONE) { + return mhu_err; + } + chan = 0; + } + } + + /* Clear all channels */ + for (uint8_t i = U(0); i < num_channels; i++) { + mhu_v3_err = mhu_v3_x_doorbell_clear(dev, i, UINT32_MAX); + if (mhu_v3_err != MHU_V_3_X_ERR_NONE) { + return error_mapping_to_mhu_error_t(mhu_v3_err); + } + } + + *size = msg_len; + + return MHU_ERR_NONE; +} + +size_t mhu_get_max_message_size(void) +{ + enum mhu_v3_x_error_t err __maybe_unused; + uint8_t num_channels; + + err = mhu_v3_x_get_num_channel_implemented(&mhu_seh_dev, + MHU_V3_X_CHANNEL_TYPE_DBCH, &num_channels); + + assert(err == MHU_V_3_X_ERR_NONE); + assert(num_channels != U(0)); + /* + * Returns only usable size of memory. As one channel is specifically + * used to inform about the size of payload, discard it from available + * memory size. + */ + return (num_channels - 1) * sizeof(uint32_t); +} diff --git a/drivers/arm/rss/rss_comms.c b/drivers/arm/rse/rse_comms.c similarity index 71% rename from drivers/arm/rss/rss_comms.c rename to drivers/arm/rse/rse_comms.c index 4622af98..cfc5a83c 100644 --- a/drivers/arm/rss/rss_comms.c +++ b/drivers/arm/rse/rse_comms.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, Arm Limited. All rights reserved. + * Copyright (c) 2022-2024, Arm Limited. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -9,16 +9,16 @@ #include <common/debug.h> #include <drivers/arm/mhu.h> -#include <drivers/arm/rss_comms.h> +#include <drivers/arm/rse_comms.h> #include <psa/client.h> -#include <rss_comms_protocol.h> +#include <rse_comms_protocol.h> /* Union as message space and reply space are never used at the same time, and this saves space as * we can overlap them. */ -union __packed __attribute__((aligned(4))) rss_comms_io_buffer_t { - struct serialized_rss_comms_msg_t msg; - struct serialized_rss_comms_reply_t reply; +union __packed __attribute__((aligned(4))) rse_comms_io_buffer_t { + struct serialized_rse_comms_msg_t msg; + struct serialized_rse_comms_reply_t reply; }; static uint8_t select_protocol_version(const psa_invec *in_vec, size_t in_len, @@ -40,13 +40,13 @@ static uint8_t select_protocol_version(const psa_invec *in_vec, size_t in_len, comms_mhu_msg_size = mhu_get_max_message_size(); - comms_embed_msg_min_size = sizeof(struct serialized_rss_comms_header_t) + - sizeof(struct rss_embed_msg_t) - - PLAT_RSS_COMMS_PAYLOAD_MAX_SIZE; + comms_embed_msg_min_size = sizeof(struct serialized_rse_comms_header_t) + + sizeof(struct rse_embed_msg_t) - + PLAT_RSE_COMMS_PAYLOAD_MAX_SIZE; - comms_embed_reply_min_size = sizeof(struct serialized_rss_comms_header_t) + - sizeof(struct rss_embed_reply_t) - - PLAT_RSS_COMMS_PAYLOAD_MAX_SIZE; + comms_embed_reply_min_size = sizeof(struct serialized_rse_comms_header_t) + + sizeof(struct rse_embed_reply_t) - + PLAT_RSE_COMMS_PAYLOAD_MAX_SIZE; /* Use embed if we can pack into one message and reply, else use * pointer_access. The underlying MHU transport protocol uses a @@ -59,11 +59,13 @@ static uint8_t select_protocol_version(const psa_invec *in_vec, size_t in_len, * messages due to ATU configuration costs to allow access to the * pointers. */ - if ((comms_embed_msg_min_size + in_size_total > comms_mhu_msg_size - sizeof(uint32_t)) - || (comms_embed_reply_min_size + out_size_total > comms_mhu_msg_size) - sizeof(uint32_t)) { - return RSS_COMMS_PROTOCOL_POINTER_ACCESS; + if ((comms_embed_msg_min_size + in_size_total > + comms_mhu_msg_size - sizeof(uint32_t)) || + (comms_embed_reply_min_size + out_size_total > + comms_mhu_msg_size - sizeof(uint32_t))) { + return RSE_COMMS_PROTOCOL_POINTER_ACCESS; } else { - return RSS_COMMS_PROTOCOL_EMBED; + return RSE_COMMS_PROTOCOL_EMBED; } } @@ -73,7 +75,7 @@ psa_status_t psa_call(psa_handle_t handle, int32_t type, const psa_invec *in_vec /* Declared statically to avoid using huge amounts of stack space. Maybe revisit if * functions not being reentrant becomes a problem. */ - static union rss_comms_io_buffer_t io_buf; + static union rse_comms_io_buffer_t io_buf; enum mhu_error_t err; psa_status_t status; static uint8_t seq_num = 1U; @@ -82,8 +84,8 @@ psa_status_t psa_call(psa_handle_t handle, int32_t type, const psa_invec *in_vec psa_status_t return_val; size_t idx; - if (type > INT16_MAX || type < INT16_MIN || in_len > PSA_MAX_IOVEC - || out_len > PSA_MAX_IOVEC) { + if (type > PSA_CALL_TYPE_MAX || type < PSA_CALL_TYPE_MIN || + in_len > PSA_MAX_IOVEC || out_len > PSA_MAX_IOVEC) { return PSA_ERROR_INVALID_ARGUMENT; } @@ -92,13 +94,13 @@ psa_status_t psa_call(psa_handle_t handle, int32_t type, const psa_invec *in_vec io_buf.msg.header.client_id = 1U, io_buf.msg.header.protocol_ver = select_protocol_version(in_vec, in_len, out_vec, out_len); - status = rss_protocol_serialize_msg(handle, type, in_vec, in_len, out_vec, + status = rse_protocol_serialize_msg(handle, type, in_vec, in_len, out_vec, out_len, &io_buf.msg, &msg_size); if (status != PSA_SUCCESS) { return status; } - VERBOSE("[RSS-COMMS] Sending message\n"); + VERBOSE("[RSE-COMMS] Sending message\n"); VERBOSE("protocol_ver=%u\n", io_buf.msg.header.protocol_ver); VERBOSE("seq_num=%u\n", io_buf.msg.header.seq_num); VERBOSE("client_id=%u\n", io_buf.msg.header.client_id); @@ -115,7 +117,7 @@ psa_status_t psa_call(psa_handle_t handle, int32_t type, const psa_invec *in_vec #if DEBUG /* * Poisoning the message buffer (with a known pattern). - * Helps in detecting hypothetical RSS communication bugs. + * Helps in detecting hypothetical RSE communication bugs. */ memset(&io_buf.msg, 0xA5, msg_size); #endif @@ -125,12 +127,12 @@ psa_status_t psa_call(psa_handle_t handle, int32_t type, const psa_invec *in_vec return PSA_ERROR_COMMUNICATION_FAILURE; } - VERBOSE("[RSS-COMMS] Received reply\n"); + VERBOSE("[RSE-COMMS] Received reply\n"); VERBOSE("protocol_ver=%u\n", io_buf.reply.header.protocol_ver); VERBOSE("seq_num=%u\n", io_buf.reply.header.seq_num); VERBOSE("client_id=%u\n", io_buf.reply.header.client_id); - status = rss_protocol_deserialize_reply(out_vec, out_len, &return_val, + status = rse_protocol_deserialize_reply(out_vec, out_len, &return_val, &io_buf.reply, reply_size); if (status != PSA_SUCCESS) { return status; @@ -150,16 +152,16 @@ psa_status_t psa_call(psa_handle_t handle, int32_t type, const psa_invec *in_vec return return_val; } -int rss_comms_init(uintptr_t mhu_sender_base, uintptr_t mhu_receiver_base) +int rse_comms_init(uintptr_t mhu_sender_base, uintptr_t mhu_receiver_base) { enum mhu_error_t err; err = mhu_init_sender(mhu_sender_base); if (err != MHU_ERR_NONE) { if (err == MHU_ERR_ALREADY_INIT) { - INFO("[RSS-COMMS] Host to RSS MHU driver already initialized\n"); + INFO("[RSE-COMMS] Host to RSE MHU driver already initialized\n"); } else { - ERROR("[RSS-COMMS] Host to RSS MHU driver initialization failed: %d\n", err); + ERROR("[RSE-COMMS] Host to RSE MHU driver initialization failed: %d\n", err); return -1; } } @@ -167,9 +169,9 @@ int rss_comms_init(uintptr_t mhu_sender_base, uintptr_t mhu_receiver_base) err = mhu_init_receiver(mhu_receiver_base); if (err != MHU_ERR_NONE) { if (err == MHU_ERR_ALREADY_INIT) { - INFO("[RSS-COMMS] RSS to Host MHU driver already initialized\n"); + INFO("[RSE-COMMS] RSE to Host MHU driver already initialized\n"); } else { - ERROR("[RSS-COMMS] RSS to Host MHU driver initialization failed: %d\n", err); + ERROR("[RSE-COMMS] RSE to Host MHU driver initialization failed: %d\n", err); return -1; } } diff --git a/drivers/arm/rse/rse_comms.mk b/drivers/arm/rse/rse_comms.mk new file mode 100644 index 00000000..3b87fe23 --- /dev/null +++ b/drivers/arm/rse/rse_comms.mk @@ -0,0 +1,35 @@ +# +# Copyright (c) 2022-2024, Arm Limited. All rights reserved. +# +# SPDX-License-Identifier: BSD-3-Clause +# + +$(warning "RSE driver is an experimental feature") + +RSE_COMMS_SOURCES := $(addprefix drivers/arm/rse/, \ + rse_comms.c \ + rse_comms_protocol.c \ + rse_comms_protocol_embed.c \ + rse_comms_protocol_pointer_access.c \ + ) + +# Default to MHUv2 if PLAT_MHU_VERSION undefined +PLAT_MHU_VERSION ?= 2 + +ifeq (${PLAT_MHU_VERSION}, 3) +RSE_COMMS_SOURCES += $(addprefix drivers/arm/mhu/, \ + mhu_v3_x.c \ + mhu_wrapper_v3_x.c \ + ) +else ifeq (${PLAT_MHU_VERSION}, 2) +RSE_COMMS_SOURCES += $(addprefix drivers/arm/mhu/, \ + mhu_v2_x.c \ + mhu_wrapper_v2_x.c \ + ) +else +$(error Unsupported MHU version) +endif + +PLAT_INCLUDES += -Idrivers/arm/rse \ + -Idrivers/arm/mhu \ + -Iinclude/lib/psa diff --git a/drivers/arm/rss/rss_comms_protocol.c b/drivers/arm/rse/rse_comms_protocol.c similarity index 62% rename from drivers/arm/rss/rss_comms_protocol.c rename to drivers/arm/rse/rse_comms_protocol.c index a1b1b58c..3eb7eaa5 100644 --- a/drivers/arm/rss/rss_comms_protocol.c +++ b/drivers/arm/rse/rse_comms_protocol.c @@ -7,15 +7,15 @@ #include <assert.h> #include <common/debug.h> -#include "rss_comms_protocol.h" +#include "rse_comms_protocol.h" -psa_status_t rss_protocol_serialize_msg(psa_handle_t handle, +psa_status_t rse_protocol_serialize_msg(psa_handle_t handle, int16_t type, const psa_invec *in_vec, uint8_t in_len, const psa_outvec *out_vec, uint8_t out_len, - struct serialized_rss_comms_msg_t *msg, + struct serialized_rse_comms_msg_t *msg, size_t *msg_len) { psa_status_t status; @@ -25,15 +25,15 @@ psa_status_t rss_protocol_serialize_msg(psa_handle_t handle, assert(in_vec != NULL); switch (msg->header.protocol_ver) { - case RSS_COMMS_PROTOCOL_EMBED: - status = rss_protocol_embed_serialize_msg(handle, type, in_vec, in_len, out_vec, + case RSE_COMMS_PROTOCOL_EMBED: + status = rse_protocol_embed_serialize_msg(handle, type, in_vec, in_len, out_vec, out_len, &msg->msg.embed, msg_len); if (status != PSA_SUCCESS) { return status; } break; - case RSS_COMMS_PROTOCOL_POINTER_ACCESS: - status = rss_protocol_pointer_access_serialize_msg(handle, type, in_vec, in_len, + case RSE_COMMS_PROTOCOL_POINTER_ACCESS: + status = rse_protocol_pointer_access_serialize_msg(handle, type, in_vec, in_len, out_vec, out_len, &msg->msg.pointer_access, msg_len); @@ -45,26 +45,26 @@ psa_status_t rss_protocol_serialize_msg(psa_handle_t handle, return PSA_ERROR_NOT_SUPPORTED; } - *msg_len += sizeof(struct serialized_rss_comms_header_t); + *msg_len += sizeof(struct serialized_rse_comms_header_t); return PSA_SUCCESS; } -psa_status_t rss_protocol_deserialize_reply(psa_outvec *out_vec, +psa_status_t rse_protocol_deserialize_reply(psa_outvec *out_vec, uint8_t out_len, psa_status_t *return_val, - const struct serialized_rss_comms_reply_t *reply, + const struct serialized_rse_comms_reply_t *reply, size_t reply_size) { assert(reply != NULL); assert(return_val != NULL); switch (reply->header.protocol_ver) { - case RSS_COMMS_PROTOCOL_EMBED: - return rss_protocol_embed_deserialize_reply(out_vec, out_len, return_val, + case RSE_COMMS_PROTOCOL_EMBED: + return rse_protocol_embed_deserialize_reply(out_vec, out_len, return_val, &reply->reply.embed, reply_size); - case RSS_COMMS_PROTOCOL_POINTER_ACCESS: - return rss_protocol_pointer_access_deserialize_reply(out_vec, out_len, return_val, + case RSE_COMMS_PROTOCOL_POINTER_ACCESS: + return rse_protocol_pointer_access_deserialize_reply(out_vec, out_len, return_val, &reply->reply.pointer_access, reply_size); default: diff --git a/drivers/arm/rse/rse_comms_protocol.h b/drivers/arm/rse/rse_comms_protocol.h new file mode 100644 index 00000000..24f39657 --- /dev/null +++ b/drivers/arm/rse/rse_comms_protocol.h @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2022, Arm Limited. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + * + */ + +#ifndef __RSE_COMMS_PROTOCOL_H__ +#define __RSE_COMMS_PROTOCOL_H__ + +#include <cdefs.h> +#include <stdint.h> + +#include <psa/client.h> +#include "rse_comms_protocol_embed.h" +#include "rse_comms_protocol_pointer_access.h" + +enum rse_comms_protocol_version_t { + RSE_COMMS_PROTOCOL_EMBED = 0, + RSE_COMMS_PROTOCOL_POINTER_ACCESS = 1, +}; + +struct __packed serialized_rse_comms_header_t { + uint8_t protocol_ver; + uint8_t seq_num; + uint16_t client_id; +}; + +/* MHU message passed from Host to RSE to deliver a PSA client call */ +struct __packed serialized_rse_comms_msg_t { + struct serialized_rse_comms_header_t header; + union __packed { + struct rse_embed_msg_t embed; + struct rse_pointer_access_msg_t pointer_access; + } msg; +}; + +/* MHU reply message to hold the PSA client reply result returned by RSE */ +struct __packed serialized_rse_comms_reply_t { + struct serialized_rse_comms_header_t header; + union __packed { + struct rse_embed_reply_t embed; + struct rse_pointer_access_reply_t pointer_access; + } reply; +}; + +/* in_len and out_len are uint8_ts, therefore if there are more than 255 iovecs + * an error may occur. + */ +CASSERT(PSA_MAX_IOVEC <= UINT8_MAX, assert_rse_comms_max_iovec_too_large); + +psa_status_t rse_protocol_serialize_msg(psa_handle_t handle, + int16_t type, + const psa_invec *in_vec, + uint8_t in_len, + const psa_outvec *out_vec, + uint8_t out_len, + struct serialized_rse_comms_msg_t *msg, + size_t *msg_len); + +psa_status_t rse_protocol_deserialize_reply(psa_outvec *out_vec, + uint8_t out_len, + psa_status_t *return_val, + const struct serialized_rse_comms_reply_t *reply, + size_t reply_size); + +#endif /* __RSE_COMMS_PROTOCOL_H__ */ diff --git a/drivers/arm/rse/rse_comms_protocol_common.h b/drivers/arm/rse/rse_comms_protocol_common.h new file mode 100644 index 00000000..235ea92f --- /dev/null +++ b/drivers/arm/rse/rse_comms_protocol_common.h @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2024, Arm Limited. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + * + */ + +/* + * Packing scheme of the control parameter + * + * 31 30-28 27 26-24 23-20 19 18-16 15-0 + * +------------+-----+------+-------+-----+-------+-------+------+ + * | | | | invec | | | outvec| type | + * | Res | Res | Res | number| Res | Res | number| | + * +------------+-----+------+-------+-----+-------+-------+------+ + * + * Res: Reserved. + */ + +#ifndef RSE_COMMS_PROTOCOL_COMMON +#define RSE_COMMS_PROTOCOL_COMMON + +#define TYPE_OFFSET (0U) +#define TYPE_MASK (0xFFFFUL << TYPE_OFFSET) +#define IN_LEN_OFFSET (24U) +#define IN_LEN_MASK (0x7UL << IN_LEN_OFFSET) +#define OUT_LEN_OFFSET (16U) +#define OUT_LEN_MASK (0x7UL << OUT_LEN_OFFSET) + +#define PARAM_PACK(type, in_len, out_len) \ + (((((uint32_t)(type)) << TYPE_OFFSET) & TYPE_MASK) | \ + ((((uint32_t)(in_len)) << IN_LEN_OFFSET) & IN_LEN_MASK) | \ + ((((uint32_t)(out_len)) << OUT_LEN_OFFSET) & OUT_LEN_MASK)) + +#endif /* RSE_COMMS_PROTOCOL_COMMON */ diff --git a/drivers/arm/rss/rss_comms_protocol_embed.c b/drivers/arm/rse/rse_comms_protocol_embed.c similarity index 69% rename from drivers/arm/rss/rss_comms_protocol_embed.c rename to drivers/arm/rse/rse_comms_protocol_embed.c index c453258f..d425257d 100644 --- a/drivers/arm/rss/rss_comms_protocol_embed.c +++ b/drivers/arm/rse/rse_comms_protocol_embed.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022-2023, Arm Limited. All rights reserved. + * Copyright (c) 2022-2024, Arm Limited. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause * @@ -9,27 +9,16 @@ #include <string.h> #include <common/debug.h> -#include "rss_comms_protocol_embed.h" +#include "rse_comms_protocol_common.h" +#include "rse_comms_protocol_embed.h" -#define TYPE_OFFSET (16U) -#define TYPE_MASK (0xFFFFUL << TYPE_OFFSET) -#define IN_LEN_OFFSET (8U) -#define IN_LEN_MASK (0xFFUL << IN_LEN_OFFSET) -#define OUT_LEN_OFFSET (0U) -#define OUT_LEN_MASK (0xFFUL << OUT_LEN_OFFSET) - -#define PARAM_PACK(type, in_len, out_len) \ - (((((uint32_t)type) << TYPE_OFFSET) & TYPE_MASK) | \ - ((((uint32_t)in_len) << IN_LEN_OFFSET) & IN_LEN_MASK) | \ - ((((uint32_t)out_len) << OUT_LEN_OFFSET) & OUT_LEN_MASK)) - -psa_status_t rss_protocol_embed_serialize_msg(psa_handle_t handle, +psa_status_t rse_protocol_embed_serialize_msg(psa_handle_t handle, int16_t type, const psa_invec *in_vec, uint8_t in_len, const psa_outvec *out_vec, uint8_t out_len, - struct rss_embed_msg_t *msg, + struct rse_embed_msg_t *msg, size_t *msg_len) { uint32_t payload_size = 0; @@ -66,10 +55,10 @@ psa_status_t rss_protocol_embed_serialize_msg(psa_handle_t handle, return PSA_SUCCESS; } -psa_status_t rss_protocol_embed_deserialize_reply(psa_outvec *out_vec, +psa_status_t rse_protocol_embed_deserialize_reply(psa_outvec *out_vec, uint8_t out_len, psa_status_t *return_val, - const struct rss_embed_reply_t *reply, + const struct rse_embed_reply_t *reply, size_t reply_size) { uint32_t payload_offset = 0; diff --git a/drivers/arm/rss/rss_comms_protocol_embed.h b/drivers/arm/rse/rse_comms_protocol_embed.h similarity index 55% rename from drivers/arm/rss/rss_comms_protocol_embed.h rename to drivers/arm/rse/rse_comms_protocol_embed.h index c81c7954..165978d4 100644 --- a/drivers/arm/rss/rss_comms_protocol_embed.h +++ b/drivers/arm/rse/rse_comms_protocol_embed.h @@ -5,8 +5,8 @@ * */ -#ifndef __RSS_COMMS_PROTOCOL_EMBED_H__ -#define __RSS_COMMS_PROTOCOL_EMBED_H__ +#ifndef __RSE_COMMS_PROTOCOL_EMBED_H__ +#define __RSE_COMMS_PROTOCOL_EMBED_H__ #include <cdefs.h> @@ -16,32 +16,32 @@ -struct __packed rss_embed_msg_t { +struct __packed rse_embed_msg_t { psa_handle_t handle; uint32_t ctrl_param; /* type, in_len, out_len */ uint16_t io_size[PSA_MAX_IOVEC]; - uint8_t trailer[PLAT_RSS_COMMS_PAYLOAD_MAX_SIZE]; + uint8_t trailer[PLAT_RSE_COMMS_PAYLOAD_MAX_SIZE]; }; -struct __packed rss_embed_reply_t { +struct __packed rse_embed_reply_t { int32_t return_val; uint16_t out_size[PSA_MAX_IOVEC]; - uint8_t trailer[PLAT_RSS_COMMS_PAYLOAD_MAX_SIZE]; + uint8_t trailer[PLAT_RSE_COMMS_PAYLOAD_MAX_SIZE]; }; -psa_status_t rss_protocol_embed_serialize_msg(psa_handle_t handle, +psa_status_t rse_protocol_embed_serialize_msg(psa_handle_t handle, int16_t type, const psa_invec *in_vec, uint8_t in_len, const psa_outvec *out_vec, uint8_t out_len, - struct rss_embed_msg_t *msg, + struct rse_embed_msg_t *msg, size_t *msg_len); -psa_status_t rss_protocol_embed_deserialize_reply(psa_outvec *out_vec, +psa_status_t rse_protocol_embed_deserialize_reply(psa_outvec *out_vec, uint8_t out_len, psa_status_t *return_val, - const struct rss_embed_reply_t *reply, + const struct rse_embed_reply_t *reply, size_t reply_size); -#endif /* __RSS_COMMS_PROTOCOL_EMBED_H__ */ +#endif /* __RSE_COMMS_PROTOCOL_EMBED_H__ */ diff --git a/drivers/arm/rss/rss_comms_protocol_pointer_access.c b/drivers/arm/rse/rse_comms_protocol_pointer_access.c similarity index 58% rename from drivers/arm/rss/rss_comms_protocol_pointer_access.c rename to drivers/arm/rse/rse_comms_protocol_pointer_access.c index 5007b9de..63524ebe 100644 --- a/drivers/arm/rss/rss_comms_protocol_pointer_access.c +++ b/drivers/arm/rse/rse_comms_protocol_pointer_access.c @@ -1,32 +1,21 @@ /* - * Copyright (c) 2022, Arm Limited. All rights reserved. + * Copyright (c) 2022-2024, Arm Limited. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause * */ #include <assert.h> -#include "rss_comms_protocol_pointer_access.h" +#include "rse_comms_protocol_common.h" +#include "rse_comms_protocol_pointer_access.h" -#define TYPE_OFFSET (16U) -#define TYPE_MASK (0xFFFFUL << TYPE_OFFSET) -#define IN_LEN_OFFSET (8U) -#define IN_LEN_MASK (0xFFUL << IN_LEN_OFFSET) -#define OUT_LEN_OFFSET (0U) -#define OUT_LEN_MASK (0xFFUL << OUT_LEN_OFFSET) - -#define PARAM_PACK(type, in_len, out_len) \ - (((((uint32_t)type) << TYPE_OFFSET) & TYPE_MASK) | \ - ((((uint32_t)in_len) << IN_LEN_OFFSET) & IN_LEN_MASK) | \ - ((((uint32_t)out_len) << OUT_LEN_OFFSET) & OUT_LEN_MASK)) - -psa_status_t rss_protocol_pointer_access_serialize_msg(psa_handle_t handle, +psa_status_t rse_protocol_pointer_access_serialize_msg(psa_handle_t handle, int16_t type, const psa_invec *in_vec, uint8_t in_len, const psa_outvec *out_vec, uint8_t out_len, - struct rss_pointer_access_msg_t *msg, + struct rse_pointer_access_msg_t *msg, size_t *msg_len) { unsigned int i; @@ -53,10 +42,10 @@ psa_status_t rss_protocol_pointer_access_serialize_msg(psa_handle_t handle, return PSA_SUCCESS; } -psa_status_t rss_protocol_pointer_access_deserialize_reply(psa_outvec *out_vec, +psa_status_t rse_protocol_pointer_access_deserialize_reply(psa_outvec *out_vec, uint8_t out_len, psa_status_t *return_val, - const struct rss_pointer_access_reply_t *reply, + const struct rse_pointer_access_reply_t *reply, size_t reply_size) { unsigned int i; diff --git a/drivers/arm/rss/rss_comms_protocol_pointer_access.h b/drivers/arm/rse/rse_comms_protocol_pointer_access.h similarity index 58% rename from drivers/arm/rss/rss_comms_protocol_pointer_access.h rename to drivers/arm/rse/rse_comms_protocol_pointer_access.h index a4d054bd..e5935f3f 100644 --- a/drivers/arm/rss/rss_comms_protocol_pointer_access.h +++ b/drivers/arm/rse/rse_comms_protocol_pointer_access.h @@ -5,38 +5,38 @@ * */ -#ifndef __RSS_COMMS_PROTOCOL_POINTER_ACCESS_H__ -#define __RSS_COMMS_PROTOCOL_POINTER_ACCESS_H__ +#ifndef __RSE_COMMS_PROTOCOL_POINTER_ACCESS_H__ +#define __RSE_COMMS_PROTOCOL_POINTER_ACCESS_H__ #include <cdefs.h> #include <psa/client.h> -struct __packed rss_pointer_access_msg_t { +struct __packed rse_pointer_access_msg_t { psa_handle_t handle; uint32_t ctrl_param; uint32_t io_sizes[PSA_MAX_IOVEC]; uint64_t host_ptrs[PSA_MAX_IOVEC]; }; -struct __packed rss_pointer_access_reply_t { +struct __packed rse_pointer_access_reply_t { int32_t return_val; uint32_t out_sizes[PSA_MAX_IOVEC]; }; -psa_status_t rss_protocol_pointer_access_serialize_msg(psa_handle_t handle, +psa_status_t rse_protocol_pointer_access_serialize_msg(psa_handle_t handle, int16_t type, const psa_invec *in_vec, uint8_t in_len, const psa_outvec *out_vec, uint8_t out_len, - struct rss_pointer_access_msg_t *msg, + struct rse_pointer_access_msg_t *msg, size_t *msg_len); -psa_status_t rss_protocol_pointer_access_deserialize_reply(psa_outvec *out_vec, +psa_status_t rse_protocol_pointer_access_deserialize_reply(psa_outvec *out_vec, uint8_t out_len, psa_status_t *return_val, - const struct rss_pointer_access_reply_t *reply, + const struct rse_pointer_access_reply_t *reply, size_t reply_size); -#endif /* __RSS_COMMS_PROTOCOL_POINTER_ACCESS_H__ */ +#endif /* __RSE_COMMS_PROTOCOL_POINTER_ACCESS_H__ */ diff --git a/drivers/arm/rss/rss_comms.mk b/drivers/arm/rss/rss_comms.mk deleted file mode 100644 index c1c994b6..00000000 --- a/drivers/arm/rss/rss_comms.mk +++ /dev/null @@ -1,22 +0,0 @@ -# -# Copyright (c) 2022, Arm Limited. All rights reserved. -# -# SPDX-License-Identifier: BSD-3-Clause -# - -$(warning "RSS driver is an experimental feature") - -RSS_COMMS_SOURCES := $(addprefix drivers/arm/rss/, \ - rss_comms.c \ - rss_comms_protocol.c \ - rss_comms_protocol_embed.c \ - rss_comms_protocol_pointer_access.c \ - ) - -RSS_COMMS_SOURCES += $(addprefix drivers/arm/mhu/, \ - mhu_v2_x.c \ - mhu_wrapper_v2_x.c \ - ) - -PLAT_INCLUDES += -Idrivers/arm/rss \ - -Idrivers/arm/mhu diff --git a/drivers/arm/rss/rss_comms_protocol.h b/drivers/arm/rss/rss_comms_protocol.h deleted file mode 100644 index 9a38057c..00000000 --- a/drivers/arm/rss/rss_comms_protocol.h +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Copyright (c) 2022, Arm Limited. All rights reserved. - * - * SPDX-License-Identifier: BSD-3-Clause - * - */ - -#ifndef __RSS_COMMS_PROTOCOL_H__ -#define __RSS_COMMS_PROTOCOL_H__ - -#include <cdefs.h> -#include <stdint.h> - -#include <psa/client.h> -#include "rss_comms_protocol_embed.h" -#include "rss_comms_protocol_pointer_access.h" - -enum rss_comms_protocol_version_t { - RSS_COMMS_PROTOCOL_EMBED = 0, - RSS_COMMS_PROTOCOL_POINTER_ACCESS = 1, -}; - -struct __packed serialized_rss_comms_header_t { - uint8_t protocol_ver; - uint8_t seq_num; - uint16_t client_id; -}; - -/* MHU message passed from Host to RSS to deliver a PSA client call */ -struct __packed serialized_rss_comms_msg_t { - struct serialized_rss_comms_header_t header; - union __packed { - struct rss_embed_msg_t embed; - struct rss_pointer_access_msg_t pointer_access; - } msg; -}; - -/* MHU reply message to hold the PSA client reply result returned by RSS */ -struct __packed serialized_rss_comms_reply_t { - struct serialized_rss_comms_header_t header; - union __packed { - struct rss_embed_reply_t embed; - struct rss_pointer_access_reply_t pointer_access; - } reply; -}; - -/* in_len and out_len are uint8_ts, therefore if there are more than 255 iovecs - * an error may occur. - */ -CASSERT(PSA_MAX_IOVEC <= UINT8_MAX, assert_rss_comms_max_iovec_too_large); - -psa_status_t rss_protocol_serialize_msg(psa_handle_t handle, - int16_t type, - const psa_invec *in_vec, - uint8_t in_len, - const psa_outvec *out_vec, - uint8_t out_len, - struct serialized_rss_comms_msg_t *msg, - size_t *msg_len); - -psa_status_t rss_protocol_deserialize_reply(psa_outvec *out_vec, - uint8_t out_len, - psa_status_t *return_val, - const struct serialized_rss_comms_reply_t *reply, - size_t reply_size); - -#endif /* __RSS_COMMS_PROTOCOL_H__ */ diff --git a/drivers/arm/smmu/smmu_v3.c b/drivers/arm/smmu/smmu_v3.c index 6c6f978d..ef04c4d6 100644 --- a/drivers/arm/smmu/smmu_v3.c +++ b/drivers/arm/smmu/smmu_v3.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2022, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2017-2024, ARM Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -69,20 +69,35 @@ int __init smmuv3_security_init(uintptr_t smmu_base) return smmuv3_poll(smmu_base + SMMU_S_GBPA, SMMU_S_GBPA_UPDATE, 0U); } -/* - * Initialize the SMMU by invalidating all secure caches and TLBs. - * Abort all incoming transactions in order to implement a default - * deny policy on reset - */ +/* Initialize the SMMU by invalidating all secure caches and TLBs. */ int __init smmuv3_init(uintptr_t smmu_base) { - /* Abort all incoming transactions */ - if (smmuv3_security_init(smmu_base) != 0) + /* + * Initiate invalidation of secure caches and TLBs if the SMMU + * supports secure state. If not, it's implementation defined + * as to how SMMU_S_INIT register is accessed. + * As per Arm SMMUv3 specification the SMMU_S_INIT register in a SMMU + * with RME implementation has following properties: + * a) all SMMU registers that are specified to be accessible only in + * the Secure physical address space are additionally accessible in + * Root physical address space. + * b) as GPT information is permitted to be cached in a TLB, the + * SMMU_S_INIT.INV_ALL operation also invalidates all GPT information + * cached in TLBs. + * Additionally, it is Root firmware’s responsibility to write to + * INV_ALL before enabling SMMU_ROOT_CR0.{ACCESSEN,GPCEN}. + */ + mmio_write_32(smmu_base + SMMU_S_INIT, SMMU_S_INIT_INV_ALL); + + /* Wait for global invalidation operation to finish */ + if (smmuv3_poll(smmu_base + SMMU_S_INIT, + SMMU_S_INIT_INV_ALL, 0U) != 0) { return -1; + } #if ENABLE_RME - if (get_armv9_2_feat_rme_support() != 0U) { + if (is_feat_rme_present()) { if ((mmio_read_32(smmu_base + SMMU_ROOT_IDR0) & SMMU_ROOT_IDR0_ROOT_IMPL) == 0U) { WARN("Skip SMMU GPC configuration.\n"); @@ -137,23 +152,7 @@ int __init smmuv3_init(uintptr_t smmu_base) #endif /* ENABLE_RME */ - /* - * Initiate invalidation of secure caches and TLBs if the SMMU - * supports secure state. If not, it's implementation defined - * as to how SMMU_S_INIT register is accessed. - * Arm SMMU Arch RME supplement, section 3.4: all SMMU registers - * specified to be accessible only in secure physical address space are - * additionally accessible in root physical address space in an SMMU - * with RME. - * Section 3.3: as GPT information is permitted to be cached in a TLB, - * the SMMU_S_INIT.INV_ALL mechanism also invalidates GPT information - * cached in TLBs. - */ - mmio_write_32(smmu_base + SMMU_S_INIT, SMMU_S_INIT_INV_ALL); - - /* Wait for global invalidation operation to finish */ - return smmuv3_poll(smmu_base + SMMU_S_INIT, - SMMU_S_INIT_INV_ALL, 0U); + return 0; } int smmuv3_ns_set_abort_all(uintptr_t smmu_base) diff --git a/drivers/auth/auth_mod.c b/drivers/auth/auth_mod.c index 608866c8..8c5ff9d1 100644 --- a/drivers/auth/auth_mod.c +++ b/drivers/auth/auth_mod.c @@ -328,7 +328,6 @@ static int auth_nvctr(const auth_method_param_nv_ctr_t *param, unsigned int data_len, len, i; unsigned int plat_nv_ctr; int rc; - bool is_trial_run = false; /* Get the counter value from current image. The AM expects the IPM * to return the counter value as a DER encoded integer */ @@ -388,9 +387,14 @@ static int auth_nvctr(const auth_method_param_nv_ctr_t *param, return 1; } else if (*cert_nv_ctr > plat_nv_ctr) { #if PSA_FWU_SUPPORT && IMAGE_BL2 - is_trial_run = fwu_is_trial_run_state(); + if (fwu_get_active_bank_state() == FWU_BANK_STATE_ACCEPTED) { + *need_nv_ctr_upgrade = true; + } else { + *need_nv_ctr_upgrade = false; + } +#else + *need_nv_ctr_upgrade = true; #endif /* PSA_FWU_SUPPORT && IMAGE_BL2 */ - *need_nv_ctr_upgrade = !is_trial_run; } return 0; diff --git a/drivers/auth/cca/bl1_cot.c b/drivers/auth/cca/bl1_cot.c new file mode 100644 index 00000000..43cb18a9 --- /dev/null +++ b/drivers/auth/cca/bl1_cot.c @@ -0,0 +1,145 @@ +/* + * Copyright (c) 2022-2024, Arm Limited. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <stddef.h> + +#include <mbedtls/version.h> + +#include <common/tbbr/cot_def.h> +#include <drivers/auth/auth_mod.h> +#include <platform_def.h> +#include <tools_share/cca_oid.h> + +/* + * Allocate static buffers to store the authentication parameters extracted from + * the certificates. + */ +static unsigned char fw_config_hash_buf[HASH_DER_LEN]; +static unsigned char tb_fw_hash_buf[HASH_DER_LEN]; +static unsigned char tb_fw_config_hash_buf[HASH_DER_LEN]; + +/* + * Parameter type descriptors. + */ +static auth_param_type_desc_t cca_nv_ctr = AUTH_PARAM_TYPE_DESC( + AUTH_PARAM_NV_CTR, CCA_FW_NVCOUNTER_OID); +static auth_param_type_desc_t subject_pk = AUTH_PARAM_TYPE_DESC( + AUTH_PARAM_PUB_KEY, 0); +static auth_param_type_desc_t sig = AUTH_PARAM_TYPE_DESC( + AUTH_PARAM_SIG, 0); +static auth_param_type_desc_t sig_alg = AUTH_PARAM_TYPE_DESC( + AUTH_PARAM_SIG_ALG, 0); +static auth_param_type_desc_t raw_data = AUTH_PARAM_TYPE_DESC( + AUTH_PARAM_RAW_DATA, 0); + +static auth_param_type_desc_t tb_fw_hash = AUTH_PARAM_TYPE_DESC( + AUTH_PARAM_HASH, TRUSTED_BOOT_FW_HASH_OID); +static auth_param_type_desc_t tb_fw_config_hash = AUTH_PARAM_TYPE_DESC( + AUTH_PARAM_HASH, TRUSTED_BOOT_FW_CONFIG_HASH_OID); +static auth_param_type_desc_t fw_config_hash = AUTH_PARAM_TYPE_DESC( + AUTH_PARAM_HASH, FW_CONFIG_HASH_OID); + +/* CCA Content Certificate */ +static const auth_img_desc_t cca_content_cert = { + .img_id = CCA_CONTENT_CERT_ID, + .img_type = IMG_CERT, + .parent = NULL, + .img_auth_methods = (const auth_method_desc_t[AUTH_METHOD_NUM]) { + [0] = { + .type = AUTH_METHOD_SIG, + .param.sig = { + .pk = &subject_pk, + .sig = &sig, + .alg = &sig_alg, + .data = &raw_data + } + }, + [1] = { + .type = AUTH_METHOD_NV_CTR, + .param.nv_ctr = { + .cert_nv_ctr = &cca_nv_ctr, + .plat_nv_ctr = &cca_nv_ctr + } + } + }, + .authenticated_data = (const auth_param_desc_t[COT_MAX_VERIFIED_PARAMS]) { + [0] = { + .type_desc = &tb_fw_hash, + .data = { + .ptr = (void *)tb_fw_hash_buf, + .len = (unsigned int)HASH_DER_LEN + } + }, + [1] = { + .type_desc = &tb_fw_config_hash, + .data = { + .ptr = (void *)tb_fw_config_hash_buf, + .len = (unsigned int)HASH_DER_LEN + } + }, + [2] = { + .type_desc = &fw_config_hash, + .data = { + .ptr = (void *)fw_config_hash_buf, + .len = (unsigned int)HASH_DER_LEN + } + } + } +}; + +static const auth_img_desc_t bl2_image = { + .img_id = BL2_IMAGE_ID, + .img_type = IMG_RAW, + .parent = &cca_content_cert, + .img_auth_methods = (const auth_method_desc_t[AUTH_METHOD_NUM]) { + [0] = { + .type = AUTH_METHOD_HASH, + .param.hash = { + .data = &raw_data, + .hash = &tb_fw_hash + } + } + } +}; + +static const auth_img_desc_t tb_fw_config = { + .img_id = TB_FW_CONFIG_ID, + .img_type = IMG_RAW, + .parent = &cca_content_cert, + .img_auth_methods = (const auth_method_desc_t[AUTH_METHOD_NUM]) { + [0] = { + .type = AUTH_METHOD_HASH, + .param.hash = { + .data = &raw_data, + .hash = &tb_fw_config_hash + } + } + } +}; + +static const auth_img_desc_t fw_config = { + .img_id = FW_CONFIG_ID, + .img_type = IMG_RAW, + .parent = &cca_content_cert, + .img_auth_methods = (const auth_method_desc_t[AUTH_METHOD_NUM]) { + [0] = { + .type = AUTH_METHOD_HASH, + .param.hash = { + .data = &raw_data, + .hash = &fw_config_hash + } + } + } +}; + +static const auth_img_desc_t * const cot_desc[] = { + [CCA_CONTENT_CERT_ID] = &cca_content_cert, + [BL2_IMAGE_ID] = &bl2_image, + [TB_FW_CONFIG_ID] = &tb_fw_config, + [FW_CONFIG_ID] = &fw_config, +}; + +REGISTER_COT(cot_desc); diff --git a/drivers/auth/cca/cot.c b/drivers/auth/cca/cot.c deleted file mode 100644 index 2a036045..00000000 --- a/drivers/auth/cca/cot.c +++ /dev/null @@ -1,679 +0,0 @@ -/* - * Copyright (c) 2022-2023, Arm Limited. All rights reserved. - * - * SPDX-License-Identifier: BSD-3-Clause - */ - -#include <stddef.h> - -#include <mbedtls/version.h> - -#include <common/tbbr/cot_def.h> -#include <drivers/auth/auth_mod.h> -#include <tools_share/cca_oid.h> - -#include <platform_def.h> - -/* - * Allocate static buffers to store the authentication parameters extracted from - * the certificates. - */ -static unsigned char fw_config_hash_buf[HASH_DER_LEN]; -static unsigned char tb_fw_hash_buf[HASH_DER_LEN]; -static unsigned char tb_fw_config_hash_buf[HASH_DER_LEN]; -static unsigned char hw_config_hash_buf[HASH_DER_LEN]; -static unsigned char soc_fw_hash_buf[HASH_DER_LEN]; -static unsigned char soc_fw_config_hash_buf[HASH_DER_LEN]; -static unsigned char rmm_hash_buf[HASH_DER_LEN]; - -#ifdef IMAGE_BL2 -static unsigned char nt_world_bl_hash_buf[HASH_DER_LEN]; -static unsigned char tos_fw_hash_buf[HASH_DER_LEN]; -static unsigned char tos_fw_config_hash_buf[HASH_DER_LEN]; -static unsigned char nt_fw_config_hash_buf[HASH_DER_LEN]; -#if defined(SPD_spmd) -static unsigned char sp_pkg_hash_buf[MAX_SP_IDS][HASH_DER_LEN]; -#endif /* SPD_spmd */ - -static unsigned char core_swd_pk_buf[PK_DER_LEN]; -static unsigned char plat_pk_buf[PK_DER_LEN]; -#endif /* IMAGE_BL2 */ - -/* - * Parameter type descriptors. - */ -static auth_param_type_desc_t cca_nv_ctr = AUTH_PARAM_TYPE_DESC( - AUTH_PARAM_NV_CTR, CCA_FW_NVCOUNTER_OID); -static auth_param_type_desc_t subject_pk = AUTH_PARAM_TYPE_DESC( - AUTH_PARAM_PUB_KEY, 0); -static auth_param_type_desc_t sig = AUTH_PARAM_TYPE_DESC( - AUTH_PARAM_SIG, 0); -static auth_param_type_desc_t sig_alg = AUTH_PARAM_TYPE_DESC( - AUTH_PARAM_SIG_ALG, 0); -static auth_param_type_desc_t raw_data = AUTH_PARAM_TYPE_DESC( - AUTH_PARAM_RAW_DATA, 0); - -static auth_param_type_desc_t tb_fw_hash = AUTH_PARAM_TYPE_DESC( - AUTH_PARAM_HASH, TRUSTED_BOOT_FW_HASH_OID); -static auth_param_type_desc_t tb_fw_config_hash = AUTH_PARAM_TYPE_DESC( - AUTH_PARAM_HASH, TRUSTED_BOOT_FW_CONFIG_HASH_OID); -static auth_param_type_desc_t hw_config_hash = AUTH_PARAM_TYPE_DESC( - AUTH_PARAM_HASH, HW_CONFIG_HASH_OID); -static auth_param_type_desc_t fw_config_hash = AUTH_PARAM_TYPE_DESC( - AUTH_PARAM_HASH, FW_CONFIG_HASH_OID); -static auth_param_type_desc_t soc_fw_hash = AUTH_PARAM_TYPE_DESC( - AUTH_PARAM_HASH, SOC_AP_FW_HASH_OID); -static auth_param_type_desc_t soc_fw_config_hash = AUTH_PARAM_TYPE_DESC( - AUTH_PARAM_HASH, SOC_FW_CONFIG_HASH_OID); -static auth_param_type_desc_t rmm_hash = AUTH_PARAM_TYPE_DESC( - AUTH_PARAM_HASH, RMM_HASH_OID); - -#ifdef IMAGE_BL2 -static auth_param_type_desc_t trusted_nv_ctr = AUTH_PARAM_TYPE_DESC( - AUTH_PARAM_NV_CTR, TRUSTED_FW_NVCOUNTER_OID); -static auth_param_type_desc_t non_trusted_nv_ctr = AUTH_PARAM_TYPE_DESC( - AUTH_PARAM_NV_CTR, NON_TRUSTED_FW_NVCOUNTER_OID); - -static auth_param_type_desc_t prot_pk = AUTH_PARAM_TYPE_DESC( - AUTH_PARAM_PUB_KEY, PROT_PK_OID); -static auth_param_type_desc_t swd_rot_pk = AUTH_PARAM_TYPE_DESC( - AUTH_PARAM_PUB_KEY, SWD_ROT_PK_OID); -static auth_param_type_desc_t core_swd_pk = AUTH_PARAM_TYPE_DESC( - AUTH_PARAM_PUB_KEY, CORE_SWD_PK_OID); -static auth_param_type_desc_t plat_pk = AUTH_PARAM_TYPE_DESC( - AUTH_PARAM_PUB_KEY, PLAT_PK_OID); - -static auth_param_type_desc_t tos_fw_hash = AUTH_PARAM_TYPE_DESC( - AUTH_PARAM_HASH, TRUSTED_OS_FW_HASH_OID); -static auth_param_type_desc_t tos_fw_config_hash = AUTH_PARAM_TYPE_DESC( - AUTH_PARAM_HASH, TRUSTED_OS_FW_CONFIG_HASH_OID); -static auth_param_type_desc_t nt_world_bl_hash = AUTH_PARAM_TYPE_DESC( - AUTH_PARAM_HASH, NON_TRUSTED_WORLD_BOOTLOADER_HASH_OID); -static auth_param_type_desc_t nt_fw_config_hash = AUTH_PARAM_TYPE_DESC( - AUTH_PARAM_HASH, NON_TRUSTED_FW_CONFIG_HASH_OID); -#if defined(SPD_spmd) -static auth_param_type_desc_t sp_pkg1_hash = AUTH_PARAM_TYPE_DESC( - AUTH_PARAM_HASH, SP_PKG1_HASH_OID); -static auth_param_type_desc_t sp_pkg2_hash = AUTH_PARAM_TYPE_DESC( - AUTH_PARAM_HASH, SP_PKG2_HASH_OID); -static auth_param_type_desc_t sp_pkg3_hash = AUTH_PARAM_TYPE_DESC( - AUTH_PARAM_HASH, SP_PKG3_HASH_OID); -static auth_param_type_desc_t sp_pkg4_hash = AUTH_PARAM_TYPE_DESC( - AUTH_PARAM_HASH, SP_PKG4_HASH_OID); -static auth_param_type_desc_t sp_pkg5_hash = AUTH_PARAM_TYPE_DESC( - AUTH_PARAM_HASH, SP_PKG5_HASH_OID); -static auth_param_type_desc_t sp_pkg6_hash = AUTH_PARAM_TYPE_DESC( - AUTH_PARAM_HASH, SP_PKG6_HASH_OID); -static auth_param_type_desc_t sp_pkg7_hash = AUTH_PARAM_TYPE_DESC( - AUTH_PARAM_HASH, SP_PKG7_HASH_OID); -static auth_param_type_desc_t sp_pkg8_hash = AUTH_PARAM_TYPE_DESC( - AUTH_PARAM_HASH, SP_PKG8_HASH_OID); -#endif /* SPD_spmd */ -#endif /* IMAGE_BL2 */ - -/* CCA Content Certificate */ -static const auth_img_desc_t cca_content_cert = { - .img_id = CCA_CONTENT_CERT_ID, - .img_type = IMG_CERT, - .parent = NULL, - .img_auth_methods = (const auth_method_desc_t[AUTH_METHOD_NUM]) { - [0] = { - .type = AUTH_METHOD_SIG, - .param.sig = { - .pk = &subject_pk, - .sig = &sig, - .alg = &sig_alg, - .data = &raw_data - } - }, - [1] = { - .type = AUTH_METHOD_NV_CTR, - .param.nv_ctr = { - .cert_nv_ctr = &cca_nv_ctr, - .plat_nv_ctr = &cca_nv_ctr - } - } - }, - .authenticated_data = (const auth_param_desc_t[COT_MAX_VERIFIED_PARAMS]) { - [0] = { - .type_desc = &tb_fw_hash, - .data = { - .ptr = (void *)tb_fw_hash_buf, - .len = (unsigned int)HASH_DER_LEN - } - }, - [1] = { - .type_desc = &tb_fw_config_hash, - .data = { - .ptr = (void *)tb_fw_config_hash_buf, - .len = (unsigned int)HASH_DER_LEN - } - }, - [2] = { - .type_desc = &fw_config_hash, - .data = { - .ptr = (void *)fw_config_hash_buf, - .len = (unsigned int)HASH_DER_LEN - } - }, - [3] = { - .type_desc = &hw_config_hash, - .data = { - .ptr = (void *)hw_config_hash_buf, - .len = (unsigned int)HASH_DER_LEN - } - }, - [4] = { - .type_desc = &soc_fw_hash, - .data = { - .ptr = (void *)soc_fw_hash_buf, - .len = (unsigned int)HASH_DER_LEN - } - }, - [5] = { - .type_desc = &soc_fw_config_hash, - .data = { - .ptr = (void *)soc_fw_config_hash_buf, - .len = (unsigned int)HASH_DER_LEN - } - }, - [6] = { - .type_desc = &rmm_hash, - .data = { - .ptr = (void *)rmm_hash_buf, - .len = (unsigned int)HASH_DER_LEN - } - } - } -}; - -#ifdef IMAGE_BL1 -static const auth_img_desc_t bl2_image = { - .img_id = BL2_IMAGE_ID, - .img_type = IMG_RAW, - .parent = &cca_content_cert, - .img_auth_methods = (const auth_method_desc_t[AUTH_METHOD_NUM]) { - [0] = { - .type = AUTH_METHOD_HASH, - .param.hash = { - .data = &raw_data, - .hash = &tb_fw_hash - } - } - } -}; - -static const auth_img_desc_t tb_fw_config = { - .img_id = TB_FW_CONFIG_ID, - .img_type = IMG_RAW, - .parent = &cca_content_cert, - .img_auth_methods = (const auth_method_desc_t[AUTH_METHOD_NUM]) { - [0] = { - .type = AUTH_METHOD_HASH, - .param.hash = { - .data = &raw_data, - .hash = &tb_fw_config_hash - } - } - } -}; - -static const auth_img_desc_t fw_config = { - .img_id = FW_CONFIG_ID, - .img_type = IMG_RAW, - .parent = &cca_content_cert, - .img_auth_methods = (const auth_method_desc_t[AUTH_METHOD_NUM]) { - [0] = { - .type = AUTH_METHOD_HASH, - .param.hash = { - .data = &raw_data, - .hash = &fw_config_hash - } - } - } -}; -#endif /* IMAGE_BL1 */ - -#ifdef IMAGE_BL2 -/* HW Config */ -static const auth_img_desc_t hw_config = { - .img_id = HW_CONFIG_ID, - .img_type = IMG_RAW, - .parent = &cca_content_cert, - .img_auth_methods = (const auth_method_desc_t[AUTH_METHOD_NUM]) { - [0] = { - .type = AUTH_METHOD_HASH, - .param.hash = { - .data = &raw_data, - .hash = &hw_config_hash - } - } - } -}; - -/* BL31 */ -static const auth_img_desc_t bl31_image = { - .img_id = BL31_IMAGE_ID, - .img_type = IMG_RAW, - .parent = &cca_content_cert, - .img_auth_methods = (const auth_method_desc_t[AUTH_METHOD_NUM]) { - [0] = { - .type = AUTH_METHOD_HASH, - .param.hash = { - .data = &raw_data, - .hash = &soc_fw_hash - } - } - } -}; - -/* BL31 Config */ -static const auth_img_desc_t soc_fw_config = { - .img_id = SOC_FW_CONFIG_ID, - .img_type = IMG_RAW, - .parent = &cca_content_cert, - .img_auth_methods = (const auth_method_desc_t[AUTH_METHOD_NUM]) { - [0] = { - .type = AUTH_METHOD_HASH, - .param.hash = { - .data = &raw_data, - .hash = &soc_fw_config_hash - } - } - } -}; - -/* RMM */ -static const auth_img_desc_t rmm_image = { - .img_id = RMM_IMAGE_ID, - .img_type = IMG_RAW, - .parent = &cca_content_cert, - .img_auth_methods = (const auth_method_desc_t[AUTH_METHOD_NUM]) { - [0] = { - .type = AUTH_METHOD_HASH, - .param.hash = { - .data = &raw_data, - .hash = &rmm_hash - } - } - } -}; - -/* Core SWD Key Certificate */ -static const auth_img_desc_t core_swd_key_cert = { - .img_id = CORE_SWD_KEY_CERT_ID, - .img_type = IMG_CERT, - .parent = NULL, /* SWD ROOT CERT */ - .img_auth_methods = (const auth_method_desc_t[AUTH_METHOD_NUM]) { - [0] = { - .type = AUTH_METHOD_SIG, - .param.sig = { - .pk = &swd_rot_pk, - .sig = &sig, - .alg = &sig_alg, - .data = &raw_data - } - }, - [1] = { - .type = AUTH_METHOD_NV_CTR, - .param.nv_ctr = { - .cert_nv_ctr = &trusted_nv_ctr, - .plat_nv_ctr = &trusted_nv_ctr - } - } - }, - .authenticated_data = (const auth_param_desc_t[COT_MAX_VERIFIED_PARAMS]) { - [0] = { - .type_desc = &core_swd_pk, - .data = { - .ptr = (void *)core_swd_pk_buf, - .len = (unsigned int)PK_DER_LEN - } - } - } -}; - -/* SPMC Content Certificate */ -static const auth_img_desc_t trusted_os_fw_content_cert = { - .img_id = TRUSTED_OS_FW_CONTENT_CERT_ID, - .img_type = IMG_CERT, - .parent = &core_swd_key_cert, - .img_auth_methods = (const auth_method_desc_t[AUTH_METHOD_NUM]) { - [0] = { - .type = AUTH_METHOD_SIG, - .param.sig = { - .pk = &core_swd_pk, - .sig = &sig, - .alg = &sig_alg, - .data = &raw_data - } - }, - [1] = { - .type = AUTH_METHOD_NV_CTR, - .param.nv_ctr = { - .cert_nv_ctr = &trusted_nv_ctr, - .plat_nv_ctr = &trusted_nv_ctr - } - } - }, - .authenticated_data = (const auth_param_desc_t[COT_MAX_VERIFIED_PARAMS]) { - [0] = { - .type_desc = &tos_fw_hash, - .data = { - .ptr = (void *)tos_fw_hash_buf, - .len = (unsigned int)HASH_DER_LEN - } - }, - [1] = { - .type_desc = &tos_fw_config_hash, - .data = { - .ptr = (void *)tos_fw_config_hash_buf, - .len = (unsigned int)HASH_DER_LEN - } - } - } -}; - -/* SPMC */ -static const auth_img_desc_t bl32_image = { - .img_id = BL32_IMAGE_ID, - .img_type = IMG_RAW, - .parent = &trusted_os_fw_content_cert, - .img_auth_methods = (const auth_method_desc_t[AUTH_METHOD_NUM]) { - [0] = { - .type = AUTH_METHOD_HASH, - .param.hash = { - .data = &raw_data, - .hash = &tos_fw_hash - } - } - } -}; - -/* SPM Config */ -static const auth_img_desc_t tos_fw_config = { - .img_id = TOS_FW_CONFIG_ID, - .img_type = IMG_RAW, - .parent = &trusted_os_fw_content_cert, - .img_auth_methods = (const auth_method_desc_t[AUTH_METHOD_NUM]) { - [0] = { - .type = AUTH_METHOD_HASH, - .param.hash = { - .data = &raw_data, - .hash = &tos_fw_config_hash - } - } - } -}; - -/* Platform Key Certificate */ -static const auth_img_desc_t plat_key_cert = { - .img_id = PLAT_KEY_CERT_ID, - .img_type = IMG_CERT, - .parent = NULL, /* PLATFORM ROOT CERT */ - .img_auth_methods = (const auth_method_desc_t[AUTH_METHOD_NUM]) { - [0] = { - .type = AUTH_METHOD_SIG, - .param.sig = { - .pk = &prot_pk, - .sig = &sig, - .alg = &sig_alg, - .data = &raw_data - } - }, - [1] = { - .type = AUTH_METHOD_NV_CTR, - .param.nv_ctr = { - .cert_nv_ctr = &non_trusted_nv_ctr, - .plat_nv_ctr = &non_trusted_nv_ctr - } - } - }, - .authenticated_data = (const auth_param_desc_t[COT_MAX_VERIFIED_PARAMS]) { - [0] = { - .type_desc = &plat_pk, - .data = { - .ptr = (void *)plat_pk_buf, - .len = (unsigned int)PK_DER_LEN - } - } - } -}; - -/* Non-Trusted Firmware */ -static const auth_img_desc_t non_trusted_fw_content_cert = { - .img_id = NON_TRUSTED_FW_CONTENT_CERT_ID, - .img_type = IMG_CERT, - .parent = &plat_key_cert, - .img_auth_methods = (const auth_method_desc_t[AUTH_METHOD_NUM]) { - [0] = { - .type = AUTH_METHOD_SIG, - .param.sig = { - .pk = &plat_pk, - .sig = &sig, - .alg = &sig_alg, - .data = &raw_data - } - }, - [1] = { - .type = AUTH_METHOD_NV_CTR, - .param.nv_ctr = { - .cert_nv_ctr = &non_trusted_nv_ctr, - .plat_nv_ctr = &non_trusted_nv_ctr - } - } - }, - .authenticated_data = (const auth_param_desc_t[COT_MAX_VERIFIED_PARAMS]) { - [0] = { - .type_desc = &nt_world_bl_hash, - .data = { - .ptr = (void *)nt_world_bl_hash_buf, - .len = (unsigned int)HASH_DER_LEN - } - }, - [1] = { - .type_desc = &nt_fw_config_hash, - .data = { - .ptr = (void *)nt_fw_config_hash_buf, - .len = (unsigned int)HASH_DER_LEN - } - } - } -}; - -static const auth_img_desc_t bl33_image = { - .img_id = BL33_IMAGE_ID, - .img_type = IMG_RAW, - .parent = &non_trusted_fw_content_cert, - .img_auth_methods = (const auth_method_desc_t[AUTH_METHOD_NUM]) { - [0] = { - .type = AUTH_METHOD_HASH, - .param.hash = { - .data = &raw_data, - .hash = &nt_world_bl_hash - } - } - } -}; - -/* NT FW Config */ -static const auth_img_desc_t nt_fw_config = { - .img_id = NT_FW_CONFIG_ID, - .img_type = IMG_RAW, - .parent = &non_trusted_fw_content_cert, - .img_auth_methods = (const auth_method_desc_t[AUTH_METHOD_NUM]) { - [0] = { - .type = AUTH_METHOD_HASH, - .param.hash = { - .data = &raw_data, - .hash = &nt_fw_config_hash - } - } - } -}; - -/* - * Secure Partitions - */ -#if defined(SPD_spmd) -static const auth_img_desc_t sip_sp_content_cert = { - .img_id = SIP_SP_CONTENT_CERT_ID, - .img_type = IMG_CERT, - .parent = &core_swd_key_cert, - .img_auth_methods = (const auth_method_desc_t[AUTH_METHOD_NUM]) { - [0] = { - .type = AUTH_METHOD_SIG, - .param.sig = { - .pk = &core_swd_pk, - .sig = &sig, - .alg = &sig_alg, - .data = &raw_data - } - }, - [1] = { - .type = AUTH_METHOD_NV_CTR, - .param.nv_ctr = { - .cert_nv_ctr = &trusted_nv_ctr, - .plat_nv_ctr = &trusted_nv_ctr - } - } - }, - .authenticated_data = (const auth_param_desc_t[COT_MAX_VERIFIED_PARAMS]) { - [0] = { - .type_desc = &sp_pkg1_hash, - .data = { - .ptr = (void *)sp_pkg_hash_buf[0], - .len = (unsigned int)HASH_DER_LEN - } - }, - [1] = { - .type_desc = &sp_pkg2_hash, - .data = { - .ptr = (void *)sp_pkg_hash_buf[1], - .len = (unsigned int)HASH_DER_LEN - } - }, - [2] = { - .type_desc = &sp_pkg3_hash, - .data = { - .ptr = (void *)sp_pkg_hash_buf[2], - .len = (unsigned int)HASH_DER_LEN - } - }, - [3] = { - .type_desc = &sp_pkg4_hash, - .data = { - .ptr = (void *)sp_pkg_hash_buf[3], - .len = (unsigned int)HASH_DER_LEN - } - } - } -}; - -DEFINE_SIP_SP_PKG(1); -DEFINE_SIP_SP_PKG(2); -DEFINE_SIP_SP_PKG(3); -DEFINE_SIP_SP_PKG(4); - -static const auth_img_desc_t plat_sp_content_cert = { - .img_id = PLAT_SP_CONTENT_CERT_ID, - .img_type = IMG_CERT, - .parent = &plat_key_cert, - .img_auth_methods = (const auth_method_desc_t[AUTH_METHOD_NUM]) { - [0] = { - .type = AUTH_METHOD_SIG, - .param.sig = { - .pk = &plat_pk, - .sig = &sig, - .alg = &sig_alg, - .data = &raw_data - } - }, - [1] = { - .type = AUTH_METHOD_NV_CTR, - .param.nv_ctr = { - .cert_nv_ctr = &non_trusted_nv_ctr, - .plat_nv_ctr = &non_trusted_nv_ctr - } - } - }, - .authenticated_data = (const auth_param_desc_t[COT_MAX_VERIFIED_PARAMS]) { - [0] = { - .type_desc = &sp_pkg5_hash, - .data = { - .ptr = (void *)sp_pkg_hash_buf[4], - .len = (unsigned int)HASH_DER_LEN - } - }, - [1] = { - .type_desc = &sp_pkg6_hash, - .data = { - .ptr = (void *)sp_pkg_hash_buf[5], - .len = (unsigned int)HASH_DER_LEN - } - }, - [2] = { - .type_desc = &sp_pkg7_hash, - .data = { - .ptr = (void *)sp_pkg_hash_buf[6], - .len = (unsigned int)HASH_DER_LEN - } - }, - [3] = { - .type_desc = &sp_pkg8_hash, - .data = { - .ptr = (void *)sp_pkg_hash_buf[7], - .len = (unsigned int)HASH_DER_LEN - } - } - } -}; - -DEFINE_PLAT_SP_PKG(5); -DEFINE_PLAT_SP_PKG(6); -DEFINE_PLAT_SP_PKG(7); -DEFINE_PLAT_SP_PKG(8); -#endif /* SPD_spmd */ -#endif /* IMAGE_BL2 */ -/* - * Chain of trust definition - */ -#ifdef IMAGE_BL1 -static const auth_img_desc_t * const cot_desc[] = { - [CCA_CONTENT_CERT_ID] = &cca_content_cert, - [BL2_IMAGE_ID] = &bl2_image, - [TB_FW_CONFIG_ID] = &tb_fw_config, - [FW_CONFIG_ID] = &fw_config, -}; -#else /* IMAGE_BL2 */ -static const auth_img_desc_t * const cot_desc[] = { - [CCA_CONTENT_CERT_ID] = &cca_content_cert, - [HW_CONFIG_ID] = &hw_config, - [BL31_IMAGE_ID] = &bl31_image, - [SOC_FW_CONFIG_ID] = &soc_fw_config, - [RMM_IMAGE_ID] = &rmm_image, - [CORE_SWD_KEY_CERT_ID] = &core_swd_key_cert, - [TRUSTED_OS_FW_CONTENT_CERT_ID] = &trusted_os_fw_content_cert, - [BL32_IMAGE_ID] = &bl32_image, - [TOS_FW_CONFIG_ID] = &tos_fw_config, - [PLAT_KEY_CERT_ID] = &plat_key_cert, - [NON_TRUSTED_FW_CONTENT_CERT_ID] = &non_trusted_fw_content_cert, - [BL33_IMAGE_ID] = &bl33_image, - [NT_FW_CONFIG_ID] = &nt_fw_config, -#if defined(SPD_spmd) - [SIP_SP_CONTENT_CERT_ID] = &sip_sp_content_cert, - [PLAT_SP_CONTENT_CERT_ID] = &plat_sp_content_cert, - [SP_PKG1_ID] = &sp_pkg1, - [SP_PKG2_ID] = &sp_pkg2, - [SP_PKG3_ID] = &sp_pkg3, - [SP_PKG4_ID] = &sp_pkg4, - [SP_PKG5_ID] = &sp_pkg5, - [SP_PKG6_ID] = &sp_pkg6, - [SP_PKG7_ID] = &sp_pkg7, - [SP_PKG8_ID] = &sp_pkg8, -#endif -}; -#endif /* IMAGE_BL1 */ - -/* Register the CoT in the authentication module */ -REGISTER_COT(cot_desc); diff --git a/drivers/auth/dualroot/bl1_cot.c b/drivers/auth/dualroot/bl1_cot.c new file mode 100644 index 00000000..a5481709 --- /dev/null +++ b/drivers/auth/dualroot/bl1_cot.c @@ -0,0 +1,246 @@ +/* + * Copyright (c) 2020-2024, Arm Limited. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <stddef.h> + +#include <mbedtls/version.h> + +#include <common/tbbr/cot_def.h> +#include <drivers/auth/auth_mod.h> +#include <platform_def.h> +#include <tools_share/dualroot_oid.h> + +/* + * Allocate static buffers to store the authentication parameters extracted from + * the certificates. + */ +static unsigned char fw_config_hash_buf[HASH_DER_LEN]; +static unsigned char tb_fw_hash_buf[HASH_DER_LEN]; +static unsigned char tb_fw_config_hash_buf[HASH_DER_LEN]; +static unsigned char scp_fw_hash_buf[HASH_DER_LEN]; +static unsigned char nt_world_bl_hash_buf[HASH_DER_LEN]; + +/* + * Parameter type descriptors. + */ +static auth_param_type_desc_t trusted_nv_ctr = AUTH_PARAM_TYPE_DESC( + AUTH_PARAM_NV_CTR, TRUSTED_FW_NVCOUNTER_OID); +static auth_param_type_desc_t subject_pk = AUTH_PARAM_TYPE_DESC( + AUTH_PARAM_PUB_KEY, 0); +static auth_param_type_desc_t sig = AUTH_PARAM_TYPE_DESC( + AUTH_PARAM_SIG, 0); +static auth_param_type_desc_t sig_alg = AUTH_PARAM_TYPE_DESC( + AUTH_PARAM_SIG_ALG, 0); +static auth_param_type_desc_t raw_data = AUTH_PARAM_TYPE_DESC( + AUTH_PARAM_RAW_DATA, 0); + +static auth_param_type_desc_t tb_fw_hash = AUTH_PARAM_TYPE_DESC( + AUTH_PARAM_HASH, TRUSTED_BOOT_FW_HASH_OID); +static auth_param_type_desc_t tb_fw_config_hash = AUTH_PARAM_TYPE_DESC( + AUTH_PARAM_HASH, TRUSTED_BOOT_FW_CONFIG_HASH_OID); +static auth_param_type_desc_t fw_config_hash = AUTH_PARAM_TYPE_DESC( + AUTH_PARAM_HASH, FW_CONFIG_HASH_OID); +static auth_param_type_desc_t scp_bl2u_hash = AUTH_PARAM_TYPE_DESC( + AUTH_PARAM_HASH, SCP_FWU_CFG_HASH_OID); +static auth_param_type_desc_t bl2u_hash = AUTH_PARAM_TYPE_DESC( + AUTH_PARAM_HASH, AP_FWU_CFG_HASH_OID); +static auth_param_type_desc_t ns_bl2u_hash = AUTH_PARAM_TYPE_DESC( + AUTH_PARAM_HASH, FWU_HASH_OID); + +static const auth_img_desc_t trusted_boot_fw_cert = { + .img_id = TRUSTED_BOOT_FW_CERT_ID, + .img_type = IMG_CERT, + .parent = NULL, + .img_auth_methods = (const auth_method_desc_t[AUTH_METHOD_NUM]) { + [0] = { + .type = AUTH_METHOD_SIG, + .param.sig = { + .pk = &subject_pk, + .sig = &sig, + .alg = &sig_alg, + .data = &raw_data + } + }, + [1] = { + .type = AUTH_METHOD_NV_CTR, + .param.nv_ctr = { + .cert_nv_ctr = &trusted_nv_ctr, + .plat_nv_ctr = &trusted_nv_ctr + } + } + }, + .authenticated_data = (const auth_param_desc_t[COT_MAX_VERIFIED_PARAMS]) { + [0] = { + .type_desc = &tb_fw_hash, + .data = { + .ptr = (void *)tb_fw_hash_buf, + .len = (unsigned int)HASH_DER_LEN + } + }, + [1] = { + .type_desc = &tb_fw_config_hash, + .data = { + .ptr = (void *)tb_fw_config_hash_buf, + .len = (unsigned int)HASH_DER_LEN + } + }, + [2] = { + .type_desc = &fw_config_hash, + .data = { + .ptr = (void *)fw_config_hash_buf, + .len = (unsigned int)HASH_DER_LEN + } + } + } +}; + +static const auth_img_desc_t bl2_image = { + .img_id = BL2_IMAGE_ID, + .img_type = IMG_RAW, + .parent = &trusted_boot_fw_cert, + .img_auth_methods = (const auth_method_desc_t[AUTH_METHOD_NUM]) { + [0] = { + .type = AUTH_METHOD_HASH, + .param.hash = { + .data = &raw_data, + .hash = &tb_fw_hash + } + } + } +}; + +/* TB FW Config */ +static const auth_img_desc_t tb_fw_config = { + .img_id = TB_FW_CONFIG_ID, + .img_type = IMG_RAW, + .parent = &trusted_boot_fw_cert, + .img_auth_methods = (const auth_method_desc_t[AUTH_METHOD_NUM]) { + [0] = { + .type = AUTH_METHOD_HASH, + .param.hash = { + .data = &raw_data, + .hash = &tb_fw_config_hash + } + } + } +}; + +static const auth_img_desc_t fw_config = { + .img_id = FW_CONFIG_ID, + .img_type = IMG_RAW, + .parent = &trusted_boot_fw_cert, + .img_auth_methods = (const auth_method_desc_t[AUTH_METHOD_NUM]) { + [0] = { + .type = AUTH_METHOD_HASH, + .param.hash = { + .data = &raw_data, + .hash = &fw_config_hash + } + } + } +}; + +/* FWU auth descriptor */ +static const auth_img_desc_t fwu_cert = { + .img_id = FWU_CERT_ID, + .img_type = IMG_CERT, + .parent = NULL, + .img_auth_methods = (const auth_method_desc_t[AUTH_METHOD_NUM]) { + [0] = { + .type = AUTH_METHOD_SIG, + .param.sig = { + .pk = &subject_pk, + .sig = &sig, + .alg = &sig_alg, + .data = &raw_data + } + } + }, + .authenticated_data = (const auth_param_desc_t[COT_MAX_VERIFIED_PARAMS]) { + [0] = { + .type_desc = &scp_bl2u_hash, + .data = { + .ptr = (void *)scp_fw_hash_buf, + .len = (unsigned int)HASH_DER_LEN + } + }, + [1] = { + .type_desc = &bl2u_hash, + .data = { + .ptr = (void *)tb_fw_hash_buf, + .len = (unsigned int)HASH_DER_LEN + } + }, + [2] = { + .type_desc = &ns_bl2u_hash, + .data = { + .ptr = (void *)nt_world_bl_hash_buf, + .len = (unsigned int)HASH_DER_LEN + } + } + } +}; + +/* SCP_BL2U */ +static const auth_img_desc_t scp_bl2u_image = { + .img_id = SCP_BL2U_IMAGE_ID, + .img_type = IMG_RAW, + .parent = &fwu_cert, + .img_auth_methods = (const auth_method_desc_t[AUTH_METHOD_NUM]) { + [0] = { + .type = AUTH_METHOD_HASH, + .param.hash = { + .data = &raw_data, + .hash = &scp_bl2u_hash + } + } + } +}; + +/* BL2U */ +static const auth_img_desc_t bl2u_image = { + .img_id = BL2U_IMAGE_ID, + .img_type = IMG_RAW, + .parent = &fwu_cert, + .img_auth_methods = (const auth_method_desc_t[AUTH_METHOD_NUM]) { + [0] = { + .type = AUTH_METHOD_HASH, + .param.hash = { + .data = &raw_data, + .hash = &bl2u_hash + } + } + } +}; + +/* NS_BL2U */ +static const auth_img_desc_t ns_bl2u_image = { + .img_id = NS_BL2U_IMAGE_ID, + .img_type = IMG_RAW, + .parent = &fwu_cert, + .img_auth_methods = (const auth_method_desc_t[AUTH_METHOD_NUM]) { + [0] = { + .type = AUTH_METHOD_HASH, + .param.hash = { + .data = &raw_data, + .hash = &ns_bl2u_hash + } + } + } +}; + +static const auth_img_desc_t * const cot_desc[] = { + [TRUSTED_BOOT_FW_CERT_ID] = &trusted_boot_fw_cert, + [BL2_IMAGE_ID] = &bl2_image, + [TB_FW_CONFIG_ID] = &tb_fw_config, + [FW_CONFIG_ID] = &fw_config, + [FWU_CERT_ID] = &fwu_cert, + [SCP_BL2U_IMAGE_ID] = &scp_bl2u_image, + [BL2U_IMAGE_ID] = &bl2u_image, + [NS_BL2U_IMAGE_ID] = &ns_bl2u_image +}; + +REGISTER_COT(cot_desc); diff --git a/drivers/auth/dualroot/cot.c b/drivers/auth/dualroot/cot.c deleted file mode 100644 index c89930c1..00000000 --- a/drivers/auth/dualroot/cot.c +++ /dev/null @@ -1,962 +0,0 @@ -/* - * Copyright (c) 2020-2023, Arm Limited. All rights reserved. - * - * SPDX-License-Identifier: BSD-3-Clause - */ - -#include <stddef.h> - -#include <mbedtls/version.h> - -#include <common/tbbr/cot_def.h> -#include <drivers/auth/auth_mod.h> - -#include <tools_share/dualroot_oid.h> - -#include <platform_def.h> - -/* - * Allocate static buffers to store the authentication parameters extracted from - * the certificates. - */ -static unsigned char fw_config_hash_buf[HASH_DER_LEN]; -static unsigned char tb_fw_hash_buf[HASH_DER_LEN]; -static unsigned char tb_fw_config_hash_buf[HASH_DER_LEN]; -static unsigned char hw_config_hash_buf[HASH_DER_LEN]; -static unsigned char scp_fw_hash_buf[HASH_DER_LEN]; -static unsigned char nt_world_bl_hash_buf[HASH_DER_LEN]; - -#ifdef IMAGE_BL2 -static unsigned char soc_fw_hash_buf[HASH_DER_LEN]; -static unsigned char tos_fw_hash_buf[HASH_DER_LEN]; -static unsigned char tos_fw_extra1_hash_buf[HASH_DER_LEN]; -static unsigned char tos_fw_extra2_hash_buf[HASH_DER_LEN]; -static unsigned char soc_fw_config_hash_buf[HASH_DER_LEN]; -static unsigned char tos_fw_config_hash_buf[HASH_DER_LEN]; -static unsigned char nt_fw_config_hash_buf[HASH_DER_LEN]; -#if defined(SPD_spmd) -static unsigned char sp_pkg_hash_buf[MAX_SP_IDS][HASH_DER_LEN]; -#endif /* SPD_spmd */ - -static unsigned char trusted_world_pk_buf[PK_DER_LEN]; -static unsigned char content_pk_buf[PK_DER_LEN]; -#endif - -/* - * Parameter type descriptors. - */ -static auth_param_type_desc_t trusted_nv_ctr = AUTH_PARAM_TYPE_DESC( - AUTH_PARAM_NV_CTR, TRUSTED_FW_NVCOUNTER_OID); -static auth_param_type_desc_t subject_pk = AUTH_PARAM_TYPE_DESC( - AUTH_PARAM_PUB_KEY, 0); -static auth_param_type_desc_t sig = AUTH_PARAM_TYPE_DESC( - AUTH_PARAM_SIG, 0); -static auth_param_type_desc_t sig_alg = AUTH_PARAM_TYPE_DESC( - AUTH_PARAM_SIG_ALG, 0); -static auth_param_type_desc_t raw_data = AUTH_PARAM_TYPE_DESC( - AUTH_PARAM_RAW_DATA, 0); - -static auth_param_type_desc_t tb_fw_hash = AUTH_PARAM_TYPE_DESC( - AUTH_PARAM_HASH, TRUSTED_BOOT_FW_HASH_OID); -static auth_param_type_desc_t tb_fw_config_hash = AUTH_PARAM_TYPE_DESC( - AUTH_PARAM_HASH, TRUSTED_BOOT_FW_CONFIG_HASH_OID); -static auth_param_type_desc_t hw_config_hash = AUTH_PARAM_TYPE_DESC( - AUTH_PARAM_HASH, HW_CONFIG_HASH_OID); -static auth_param_type_desc_t fw_config_hash = AUTH_PARAM_TYPE_DESC( - AUTH_PARAM_HASH, FW_CONFIG_HASH_OID); -#ifdef IMAGE_BL1 -static auth_param_type_desc_t scp_bl2u_hash = AUTH_PARAM_TYPE_DESC( - AUTH_PARAM_HASH, SCP_FWU_CFG_HASH_OID); -static auth_param_type_desc_t bl2u_hash = AUTH_PARAM_TYPE_DESC( - AUTH_PARAM_HASH, AP_FWU_CFG_HASH_OID); -static auth_param_type_desc_t ns_bl2u_hash = AUTH_PARAM_TYPE_DESC( - AUTH_PARAM_HASH, FWU_HASH_OID); -#endif /* IMAGE_BL1 */ - -#ifdef IMAGE_BL2 -static auth_param_type_desc_t non_trusted_nv_ctr = AUTH_PARAM_TYPE_DESC( - AUTH_PARAM_NV_CTR, NON_TRUSTED_FW_NVCOUNTER_OID); - -static auth_param_type_desc_t trusted_world_pk = AUTH_PARAM_TYPE_DESC( - AUTH_PARAM_PUB_KEY, TRUSTED_WORLD_PK_OID); -static auth_param_type_desc_t scp_fw_content_pk = AUTH_PARAM_TYPE_DESC( - AUTH_PARAM_PUB_KEY, SCP_FW_CONTENT_CERT_PK_OID); -static auth_param_type_desc_t soc_fw_content_pk = AUTH_PARAM_TYPE_DESC( - AUTH_PARAM_PUB_KEY, SOC_FW_CONTENT_CERT_PK_OID); -static auth_param_type_desc_t tos_fw_content_pk = AUTH_PARAM_TYPE_DESC( - AUTH_PARAM_PUB_KEY, TRUSTED_OS_FW_CONTENT_CERT_PK_OID); -static auth_param_type_desc_t prot_pk = AUTH_PARAM_TYPE_DESC( - AUTH_PARAM_PUB_KEY, PROT_PK_OID); - -static auth_param_type_desc_t scp_fw_hash = AUTH_PARAM_TYPE_DESC( - AUTH_PARAM_HASH, SCP_FW_HASH_OID); -static auth_param_type_desc_t soc_fw_hash = AUTH_PARAM_TYPE_DESC( - AUTH_PARAM_HASH, SOC_AP_FW_HASH_OID); -static auth_param_type_desc_t soc_fw_config_hash = AUTH_PARAM_TYPE_DESC( - AUTH_PARAM_HASH, SOC_FW_CONFIG_HASH_OID); -static auth_param_type_desc_t tos_fw_hash = AUTH_PARAM_TYPE_DESC( - AUTH_PARAM_HASH, TRUSTED_OS_FW_HASH_OID); -static auth_param_type_desc_t tos_fw_config_hash = AUTH_PARAM_TYPE_DESC( - AUTH_PARAM_HASH, TRUSTED_OS_FW_CONFIG_HASH_OID); -static auth_param_type_desc_t tos_fw_extra1_hash = AUTH_PARAM_TYPE_DESC( - AUTH_PARAM_HASH, TRUSTED_OS_FW_EXTRA1_HASH_OID); -static auth_param_type_desc_t tos_fw_extra2_hash = AUTH_PARAM_TYPE_DESC( - AUTH_PARAM_HASH, TRUSTED_OS_FW_EXTRA2_HASH_OID); -static auth_param_type_desc_t nt_world_bl_hash = AUTH_PARAM_TYPE_DESC( - AUTH_PARAM_HASH, NON_TRUSTED_WORLD_BOOTLOADER_HASH_OID); -static auth_param_type_desc_t nt_fw_config_hash = AUTH_PARAM_TYPE_DESC( - AUTH_PARAM_HASH, NON_TRUSTED_FW_CONFIG_HASH_OID); -#if defined(SPD_spmd) -static auth_param_type_desc_t sp_pkg1_hash = AUTH_PARAM_TYPE_DESC( - AUTH_PARAM_HASH, SP_PKG1_HASH_OID); -static auth_param_type_desc_t sp_pkg2_hash = AUTH_PARAM_TYPE_DESC( - AUTH_PARAM_HASH, SP_PKG2_HASH_OID); -static auth_param_type_desc_t sp_pkg3_hash = AUTH_PARAM_TYPE_DESC( - AUTH_PARAM_HASH, SP_PKG3_HASH_OID); -static auth_param_type_desc_t sp_pkg4_hash = AUTH_PARAM_TYPE_DESC( - AUTH_PARAM_HASH, SP_PKG4_HASH_OID); -static auth_param_type_desc_t sp_pkg5_hash = AUTH_PARAM_TYPE_DESC( - AUTH_PARAM_HASH, SP_PKG5_HASH_OID); -static auth_param_type_desc_t sp_pkg6_hash = AUTH_PARAM_TYPE_DESC( - AUTH_PARAM_HASH, SP_PKG6_HASH_OID); -static auth_param_type_desc_t sp_pkg7_hash = AUTH_PARAM_TYPE_DESC( - AUTH_PARAM_HASH, SP_PKG7_HASH_OID); -static auth_param_type_desc_t sp_pkg8_hash = AUTH_PARAM_TYPE_DESC( - AUTH_PARAM_HASH, SP_PKG8_HASH_OID); -#endif /* SPD_spmd */ -#endif /* IMAGE_BL2 */ - - -/* BL2 */ -static const auth_img_desc_t trusted_boot_fw_cert = { - .img_id = TRUSTED_BOOT_FW_CERT_ID, - .img_type = IMG_CERT, - .parent = NULL, - .img_auth_methods = (const auth_method_desc_t[AUTH_METHOD_NUM]) { - [0] = { - .type = AUTH_METHOD_SIG, - .param.sig = { - .pk = &subject_pk, - .sig = &sig, - .alg = &sig_alg, - .data = &raw_data - } - }, - [1] = { - .type = AUTH_METHOD_NV_CTR, - .param.nv_ctr = { - .cert_nv_ctr = &trusted_nv_ctr, - .plat_nv_ctr = &trusted_nv_ctr - } - } - }, - .authenticated_data = (const auth_param_desc_t[COT_MAX_VERIFIED_PARAMS]) { - [0] = { - .type_desc = &tb_fw_hash, - .data = { - .ptr = (void *)tb_fw_hash_buf, - .len = (unsigned int)HASH_DER_LEN - } - }, - [1] = { - .type_desc = &tb_fw_config_hash, - .data = { - .ptr = (void *)tb_fw_config_hash_buf, - .len = (unsigned int)HASH_DER_LEN - } - }, - [2] = { - .type_desc = &hw_config_hash, - .data = { - .ptr = (void *)hw_config_hash_buf, - .len = (unsigned int)HASH_DER_LEN - } - }, - [3] = { - .type_desc = &fw_config_hash, - .data = { - .ptr = (void *)fw_config_hash_buf, - .len = (unsigned int)HASH_DER_LEN - } - } - } -}; - -#ifdef IMAGE_BL1 -static const auth_img_desc_t bl2_image = { - .img_id = BL2_IMAGE_ID, - .img_type = IMG_RAW, - .parent = &trusted_boot_fw_cert, - .img_auth_methods = (const auth_method_desc_t[AUTH_METHOD_NUM]) { - [0] = { - .type = AUTH_METHOD_HASH, - .param.hash = { - .data = &raw_data, - .hash = &tb_fw_hash - } - } - } -}; -#endif /* IMAGE_BL1 */ - -/* HW Config */ -static const auth_img_desc_t hw_config = { - .img_id = HW_CONFIG_ID, - .img_type = IMG_RAW, - .parent = &trusted_boot_fw_cert, - .img_auth_methods = (const auth_method_desc_t[AUTH_METHOD_NUM]) { - [0] = { - .type = AUTH_METHOD_HASH, - .param.hash = { - .data = &raw_data, - .hash = &hw_config_hash - } - } - } -}; - -/* TB FW Config */ -#ifdef IMAGE_BL1 -static const auth_img_desc_t tb_fw_config = { - .img_id = TB_FW_CONFIG_ID, - .img_type = IMG_RAW, - .parent = &trusted_boot_fw_cert, - .img_auth_methods = (const auth_method_desc_t[AUTH_METHOD_NUM]) { - [0] = { - .type = AUTH_METHOD_HASH, - .param.hash = { - .data = &raw_data, - .hash = &tb_fw_config_hash - } - } - } -}; - -static const auth_img_desc_t fw_config = { - .img_id = FW_CONFIG_ID, - .img_type = IMG_RAW, - .parent = &trusted_boot_fw_cert, - .img_auth_methods = (const auth_method_desc_t[AUTH_METHOD_NUM]) { - [0] = { - .type = AUTH_METHOD_HASH, - .param.hash = { - .data = &raw_data, - .hash = &fw_config_hash - } - } - } -}; - -#endif /* IMAGE_BL1 */ - -#ifdef IMAGE_BL2 -/* Trusted key certificate */ -static const auth_img_desc_t trusted_key_cert = { - .img_id = TRUSTED_KEY_CERT_ID, - .img_type = IMG_CERT, - .parent = NULL, - .img_auth_methods = (const auth_method_desc_t[AUTH_METHOD_NUM]) { - [0] = { - .type = AUTH_METHOD_SIG, - .param.sig = { - .pk = &subject_pk, - .sig = &sig, - .alg = &sig_alg, - .data = &raw_data - } - }, - [1] = { - .type = AUTH_METHOD_NV_CTR, - .param.nv_ctr = { - .cert_nv_ctr = &trusted_nv_ctr, - .plat_nv_ctr = &trusted_nv_ctr - } - } - }, - .authenticated_data = (const auth_param_desc_t[COT_MAX_VERIFIED_PARAMS]) { - [0] = { - .type_desc = &trusted_world_pk, - .data = { - .ptr = (void *)trusted_world_pk_buf, - .len = (unsigned int)PK_DER_LEN - } - }, - } -}; - -/* SCP Firmware */ -static const auth_img_desc_t scp_fw_key_cert = { - .img_id = SCP_FW_KEY_CERT_ID, - .img_type = IMG_CERT, - .parent = &trusted_key_cert, - .img_auth_methods = (const auth_method_desc_t[AUTH_METHOD_NUM]) { - [0] = { - .type = AUTH_METHOD_SIG, - .param.sig = { - .pk = &trusted_world_pk, - .sig = &sig, - .alg = &sig_alg, - .data = &raw_data - } - }, - [1] = { - .type = AUTH_METHOD_NV_CTR, - .param.nv_ctr = { - .cert_nv_ctr = &trusted_nv_ctr, - .plat_nv_ctr = &trusted_nv_ctr - } - } - }, - .authenticated_data = (const auth_param_desc_t[COT_MAX_VERIFIED_PARAMS]) { - [0] = { - .type_desc = &scp_fw_content_pk, - .data = { - .ptr = (void *)content_pk_buf, - .len = (unsigned int)PK_DER_LEN - } - } - } -}; - -static const auth_img_desc_t scp_fw_content_cert = { - .img_id = SCP_FW_CONTENT_CERT_ID, - .img_type = IMG_CERT, - .parent = &scp_fw_key_cert, - .img_auth_methods = (const auth_method_desc_t[AUTH_METHOD_NUM]) { - [0] = { - .type = AUTH_METHOD_SIG, - .param.sig = { - .pk = &scp_fw_content_pk, - .sig = &sig, - .alg = &sig_alg, - .data = &raw_data - } - }, - [1] = { - .type = AUTH_METHOD_NV_CTR, - .param.nv_ctr = { - .cert_nv_ctr = &trusted_nv_ctr, - .plat_nv_ctr = &trusted_nv_ctr - } - } - }, - .authenticated_data = (const auth_param_desc_t[COT_MAX_VERIFIED_PARAMS]) { - [0] = { - .type_desc = &scp_fw_hash, - .data = { - .ptr = (void *)scp_fw_hash_buf, - .len = (unsigned int)HASH_DER_LEN - } - } - } -}; - -static const auth_img_desc_t scp_bl2_image = { - .img_id = SCP_BL2_IMAGE_ID, - .img_type = IMG_RAW, - .parent = &scp_fw_content_cert, - .img_auth_methods = (const auth_method_desc_t[AUTH_METHOD_NUM]) { - [0] = { - .type = AUTH_METHOD_HASH, - .param.hash = { - .data = &raw_data, - .hash = &scp_fw_hash - } - } - } -}; - -/* SoC Firmware */ -static const auth_img_desc_t soc_fw_key_cert = { - .img_id = SOC_FW_KEY_CERT_ID, - .img_type = IMG_CERT, - .parent = &trusted_key_cert, - .img_auth_methods = (const auth_method_desc_t[AUTH_METHOD_NUM]) { - [0] = { - .type = AUTH_METHOD_SIG, - .param.sig = { - .pk = &trusted_world_pk, - .sig = &sig, - .alg = &sig_alg, - .data = &raw_data - } - }, - [1] = { - .type = AUTH_METHOD_NV_CTR, - .param.nv_ctr = { - .cert_nv_ctr = &trusted_nv_ctr, - .plat_nv_ctr = &trusted_nv_ctr - } - } - }, - .authenticated_data = (const auth_param_desc_t[COT_MAX_VERIFIED_PARAMS]) { - [0] = { - .type_desc = &soc_fw_content_pk, - .data = { - .ptr = (void *)content_pk_buf, - .len = (unsigned int)PK_DER_LEN - } - } - } -}; - -static const auth_img_desc_t soc_fw_content_cert = { - .img_id = SOC_FW_CONTENT_CERT_ID, - .img_type = IMG_CERT, - .parent = &soc_fw_key_cert, - .img_auth_methods = (const auth_method_desc_t[AUTH_METHOD_NUM]) { - [0] = { - .type = AUTH_METHOD_SIG, - .param.sig = { - .pk = &soc_fw_content_pk, - .sig = &sig, - .alg = &sig_alg, - .data = &raw_data - } - }, - [1] = { - .type = AUTH_METHOD_NV_CTR, - .param.nv_ctr = { - .cert_nv_ctr = &trusted_nv_ctr, - .plat_nv_ctr = &trusted_nv_ctr - } - } - }, - .authenticated_data = (const auth_param_desc_t[COT_MAX_VERIFIED_PARAMS]) { - [0] = { - .type_desc = &soc_fw_hash, - .data = { - .ptr = (void *)soc_fw_hash_buf, - .len = (unsigned int)HASH_DER_LEN - } - }, - [1] = { - .type_desc = &soc_fw_config_hash, - .data = { - .ptr = (void *)soc_fw_config_hash_buf, - .len = (unsigned int)HASH_DER_LEN - } - } - } -}; - -static const auth_img_desc_t bl31_image = { - .img_id = BL31_IMAGE_ID, - .img_type = IMG_RAW, - .parent = &soc_fw_content_cert, - .img_auth_methods = (const auth_method_desc_t[AUTH_METHOD_NUM]) { - [0] = { - .type = AUTH_METHOD_HASH, - .param.hash = { - .data = &raw_data, - .hash = &soc_fw_hash - } - } - } -}; - -/* SOC FW Config */ -static const auth_img_desc_t soc_fw_config = { - .img_id = SOC_FW_CONFIG_ID, - .img_type = IMG_RAW, - .parent = &soc_fw_content_cert, - .img_auth_methods = (const auth_method_desc_t[AUTH_METHOD_NUM]) { - [0] = { - .type = AUTH_METHOD_HASH, - .param.hash = { - .data = &raw_data, - .hash = &soc_fw_config_hash - } - } - } -}; - -/* Trusted OS Firmware */ -static const auth_img_desc_t trusted_os_fw_key_cert = { - .img_id = TRUSTED_OS_FW_KEY_CERT_ID, - .img_type = IMG_CERT, - .parent = &trusted_key_cert, - .img_auth_methods = (const auth_method_desc_t[AUTH_METHOD_NUM]) { - [0] = { - .type = AUTH_METHOD_SIG, - .param.sig = { - .pk = &trusted_world_pk, - .sig = &sig, - .alg = &sig_alg, - .data = &raw_data - } - }, - [1] = { - .type = AUTH_METHOD_NV_CTR, - .param.nv_ctr = { - .cert_nv_ctr = &trusted_nv_ctr, - .plat_nv_ctr = &trusted_nv_ctr - } - } - }, - .authenticated_data = (const auth_param_desc_t[COT_MAX_VERIFIED_PARAMS]) { - [0] = { - .type_desc = &tos_fw_content_pk, - .data = { - .ptr = (void *)content_pk_buf, - .len = (unsigned int)PK_DER_LEN - } - } - } -}; - -static const auth_img_desc_t trusted_os_fw_content_cert = { - .img_id = TRUSTED_OS_FW_CONTENT_CERT_ID, - .img_type = IMG_CERT, - .parent = &trusted_os_fw_key_cert, - .img_auth_methods = (const auth_method_desc_t[AUTH_METHOD_NUM]) { - [0] = { - .type = AUTH_METHOD_SIG, - .param.sig = { - .pk = &tos_fw_content_pk, - .sig = &sig, - .alg = &sig_alg, - .data = &raw_data - } - }, - [1] = { - .type = AUTH_METHOD_NV_CTR, - .param.nv_ctr = { - .cert_nv_ctr = &trusted_nv_ctr, - .plat_nv_ctr = &trusted_nv_ctr - } - } - }, - .authenticated_data = (const auth_param_desc_t[COT_MAX_VERIFIED_PARAMS]) { - [0] = { - .type_desc = &tos_fw_hash, - .data = { - .ptr = (void *)tos_fw_hash_buf, - .len = (unsigned int)HASH_DER_LEN - } - }, - [1] = { - .type_desc = &tos_fw_extra1_hash, - .data = { - .ptr = (void *)tos_fw_extra1_hash_buf, - .len = (unsigned int)HASH_DER_LEN - } - }, - [2] = { - .type_desc = &tos_fw_extra2_hash, - .data = { - .ptr = (void *)tos_fw_extra2_hash_buf, - .len = (unsigned int)HASH_DER_LEN - } - }, - [3] = { - .type_desc = &tos_fw_config_hash, - .data = { - .ptr = (void *)tos_fw_config_hash_buf, - .len = (unsigned int)HASH_DER_LEN - } - } - } -}; - -static const auth_img_desc_t bl32_image = { - .img_id = BL32_IMAGE_ID, - .img_type = IMG_RAW, - .parent = &trusted_os_fw_content_cert, - .img_auth_methods = (const auth_method_desc_t[AUTH_METHOD_NUM]) { - [0] = { - .type = AUTH_METHOD_HASH, - .param.hash = { - .data = &raw_data, - .hash = &tos_fw_hash - } - } - } -}; - -static const auth_img_desc_t bl32_extra1_image = { - .img_id = BL32_EXTRA1_IMAGE_ID, - .img_type = IMG_RAW, - .parent = &trusted_os_fw_content_cert, - .img_auth_methods = (const auth_method_desc_t[AUTH_METHOD_NUM]) { - [0] = { - .type = AUTH_METHOD_HASH, - .param.hash = { - .data = &raw_data, - .hash = &tos_fw_extra1_hash - } - } - } -}; - -static const auth_img_desc_t bl32_extra2_image = { - .img_id = BL32_EXTRA2_IMAGE_ID, - .img_type = IMG_RAW, - .parent = &trusted_os_fw_content_cert, - .img_auth_methods = (const auth_method_desc_t[AUTH_METHOD_NUM]) { - [0] = { - .type = AUTH_METHOD_HASH, - .param.hash = { - .data = &raw_data, - .hash = &tos_fw_extra2_hash - } - } - } -}; - -/* TOS FW Config */ -static const auth_img_desc_t tos_fw_config = { - .img_id = TOS_FW_CONFIG_ID, - .img_type = IMG_RAW, - .parent = &trusted_os_fw_content_cert, - .img_auth_methods = (const auth_method_desc_t[AUTH_METHOD_NUM]) { - [0] = { - .type = AUTH_METHOD_HASH, - .param.hash = { - .data = &raw_data, - .hash = &tos_fw_config_hash - } - } - } -}; - -/* Non-Trusted Firmware */ -static const auth_img_desc_t non_trusted_fw_content_cert = { - .img_id = NON_TRUSTED_FW_CONTENT_CERT_ID, - .img_type = IMG_CERT, - .parent = NULL, /* Root certificate. */ - .img_auth_methods = (const auth_method_desc_t[AUTH_METHOD_NUM]) { - [0] = { - .type = AUTH_METHOD_SIG, - .param.sig = { - .pk = &prot_pk, - .sig = &sig, - .alg = &sig_alg, - .data = &raw_data - } - }, - [1] = { - .type = AUTH_METHOD_NV_CTR, - .param.nv_ctr = { - .cert_nv_ctr = &non_trusted_nv_ctr, - .plat_nv_ctr = &non_trusted_nv_ctr - } - } - }, - .authenticated_data = (const auth_param_desc_t[COT_MAX_VERIFIED_PARAMS]) { - [0] = { - .type_desc = &nt_world_bl_hash, - .data = { - .ptr = (void *)nt_world_bl_hash_buf, - .len = (unsigned int)HASH_DER_LEN - } - }, - [1] = { - .type_desc = &nt_fw_config_hash, - .data = { - .ptr = (void *)nt_fw_config_hash_buf, - .len = (unsigned int)HASH_DER_LEN - } - } - } -}; - -static const auth_img_desc_t bl33_image = { - .img_id = BL33_IMAGE_ID, - .img_type = IMG_RAW, - .parent = &non_trusted_fw_content_cert, - .img_auth_methods = (const auth_method_desc_t[AUTH_METHOD_NUM]) { - [0] = { - .type = AUTH_METHOD_HASH, - .param.hash = { - .data = &raw_data, - .hash = &nt_world_bl_hash - } - } - } -}; - -/* NT FW Config */ -static const auth_img_desc_t nt_fw_config = { - .img_id = NT_FW_CONFIG_ID, - .img_type = IMG_RAW, - .parent = &non_trusted_fw_content_cert, - .img_auth_methods = (const auth_method_desc_t[AUTH_METHOD_NUM]) { - [0] = { - .type = AUTH_METHOD_HASH, - .param.hash = { - .data = &raw_data, - .hash = &nt_fw_config_hash - } - } - } -}; - -/* - * Secure Partitions - */ -#if defined(SPD_spmd) -static const auth_img_desc_t sip_sp_content_cert = { - .img_id = SIP_SP_CONTENT_CERT_ID, - .img_type = IMG_CERT, - .parent = &trusted_key_cert, - .img_auth_methods = (const auth_method_desc_t[AUTH_METHOD_NUM]) { - [0] = { - .type = AUTH_METHOD_SIG, - .param.sig = { - .pk = &trusted_world_pk, - .sig = &sig, - .alg = &sig_alg, - .data = &raw_data - } - }, - [1] = { - .type = AUTH_METHOD_NV_CTR, - .param.nv_ctr = { - .cert_nv_ctr = &trusted_nv_ctr, - .plat_nv_ctr = &trusted_nv_ctr - } - } - }, - .authenticated_data = (const auth_param_desc_t[COT_MAX_VERIFIED_PARAMS]) { - [0] = { - .type_desc = &sp_pkg1_hash, - .data = { - .ptr = (void *)sp_pkg_hash_buf[0], - .len = (unsigned int)HASH_DER_LEN - } - }, - [1] = { - .type_desc = &sp_pkg2_hash, - .data = { - .ptr = (void *)sp_pkg_hash_buf[1], - .len = (unsigned int)HASH_DER_LEN - } - }, - [2] = { - .type_desc = &sp_pkg3_hash, - .data = { - .ptr = (void *)sp_pkg_hash_buf[2], - .len = (unsigned int)HASH_DER_LEN - } - }, - [3] = { - .type_desc = &sp_pkg4_hash, - .data = { - .ptr = (void *)sp_pkg_hash_buf[3], - .len = (unsigned int)HASH_DER_LEN - } - } - } -}; - -DEFINE_SIP_SP_PKG(1); -DEFINE_SIP_SP_PKG(2); -DEFINE_SIP_SP_PKG(3); -DEFINE_SIP_SP_PKG(4); - -static const auth_img_desc_t plat_sp_content_cert = { - .img_id = PLAT_SP_CONTENT_CERT_ID, - .img_type = IMG_CERT, - .parent = NULL, - .img_auth_methods = (const auth_method_desc_t[AUTH_METHOD_NUM]) { - [0] = { - .type = AUTH_METHOD_SIG, - .param.sig = { - .pk = &prot_pk, - .sig = &sig, - .alg = &sig_alg, - .data = &raw_data - } - }, - [1] = { - .type = AUTH_METHOD_NV_CTR, - .param.nv_ctr = { - .cert_nv_ctr = &non_trusted_nv_ctr, - .plat_nv_ctr = &non_trusted_nv_ctr - } - } - }, - .authenticated_data = (const auth_param_desc_t[COT_MAX_VERIFIED_PARAMS]) { - [0] = { - .type_desc = &sp_pkg5_hash, - .data = { - .ptr = (void *)sp_pkg_hash_buf[4], - .len = (unsigned int)HASH_DER_LEN - } - }, - [1] = { - .type_desc = &sp_pkg6_hash, - .data = { - .ptr = (void *)sp_pkg_hash_buf[5], - .len = (unsigned int)HASH_DER_LEN - } - }, - [2] = { - .type_desc = &sp_pkg7_hash, - .data = { - .ptr = (void *)sp_pkg_hash_buf[6], - .len = (unsigned int)HASH_DER_LEN - } - }, - [3] = { - .type_desc = &sp_pkg8_hash, - .data = { - .ptr = (void *)sp_pkg_hash_buf[7], - .len = (unsigned int)HASH_DER_LEN - } - } - } -}; - -DEFINE_PLAT_SP_PKG(5); -DEFINE_PLAT_SP_PKG(6); -DEFINE_PLAT_SP_PKG(7); -DEFINE_PLAT_SP_PKG(8); -#endif /* SPD_spmd */ - -#else /* IMAGE_BL2 */ - -/* FWU auth descriptor */ -static const auth_img_desc_t fwu_cert = { - .img_id = FWU_CERT_ID, - .img_type = IMG_CERT, - .parent = NULL, - .img_auth_methods = (const auth_method_desc_t[AUTH_METHOD_NUM]) { - [0] = { - .type = AUTH_METHOD_SIG, - .param.sig = { - .pk = &subject_pk, - .sig = &sig, - .alg = &sig_alg, - .data = &raw_data - } - } - }, - .authenticated_data = (const auth_param_desc_t[COT_MAX_VERIFIED_PARAMS]) { - [0] = { - .type_desc = &scp_bl2u_hash, - .data = { - .ptr = (void *)scp_fw_hash_buf, - .len = (unsigned int)HASH_DER_LEN - } - }, - [1] = { - .type_desc = &bl2u_hash, - .data = { - .ptr = (void *)tb_fw_hash_buf, - .len = (unsigned int)HASH_DER_LEN - } - }, - [2] = { - .type_desc = &ns_bl2u_hash, - .data = { - .ptr = (void *)nt_world_bl_hash_buf, - .len = (unsigned int)HASH_DER_LEN - } - } - } -}; - -/* SCP_BL2U */ -static const auth_img_desc_t scp_bl2u_image = { - .img_id = SCP_BL2U_IMAGE_ID, - .img_type = IMG_RAW, - .parent = &fwu_cert, - .img_auth_methods = (const auth_method_desc_t[AUTH_METHOD_NUM]) { - [0] = { - .type = AUTH_METHOD_HASH, - .param.hash = { - .data = &raw_data, - .hash = &scp_bl2u_hash - } - } - } -}; - -/* BL2U */ -static const auth_img_desc_t bl2u_image = { - .img_id = BL2U_IMAGE_ID, - .img_type = IMG_RAW, - .parent = &fwu_cert, - .img_auth_methods = (const auth_method_desc_t[AUTH_METHOD_NUM]) { - [0] = { - .type = AUTH_METHOD_HASH, - .param.hash = { - .data = &raw_data, - .hash = &bl2u_hash - } - } - } -}; - -/* NS_BL2U */ -static const auth_img_desc_t ns_bl2u_image = { - .img_id = NS_BL2U_IMAGE_ID, - .img_type = IMG_RAW, - .parent = &fwu_cert, - .img_auth_methods = (const auth_method_desc_t[AUTH_METHOD_NUM]) { - [0] = { - .type = AUTH_METHOD_HASH, - .param.hash = { - .data = &raw_data, - .hash = &ns_bl2u_hash - } - } - } -}; -#endif /* IMAGE_BL2 */ - -/* - * Chain of trust definition - */ -#ifdef IMAGE_BL1 -static const auth_img_desc_t * const cot_desc[] = { - [TRUSTED_BOOT_FW_CERT_ID] = &trusted_boot_fw_cert, - [BL2_IMAGE_ID] = &bl2_image, - [HW_CONFIG_ID] = &hw_config, - [TB_FW_CONFIG_ID] = &tb_fw_config, - [FW_CONFIG_ID] = &fw_config, - [FWU_CERT_ID] = &fwu_cert, - [SCP_BL2U_IMAGE_ID] = &scp_bl2u_image, - [BL2U_IMAGE_ID] = &bl2u_image, - [NS_BL2U_IMAGE_ID] = &ns_bl2u_image -}; -#else /* IMAGE_BL2 */ -static const auth_img_desc_t * const cot_desc[] = { - [TRUSTED_BOOT_FW_CERT_ID] = &trusted_boot_fw_cert, - [HW_CONFIG_ID] = &hw_config, - [TRUSTED_KEY_CERT_ID] = &trusted_key_cert, - [SCP_FW_KEY_CERT_ID] = &scp_fw_key_cert, - [SCP_FW_CONTENT_CERT_ID] = &scp_fw_content_cert, - [SCP_BL2_IMAGE_ID] = &scp_bl2_image, - [SOC_FW_KEY_CERT_ID] = &soc_fw_key_cert, - [SOC_FW_CONTENT_CERT_ID] = &soc_fw_content_cert, - [BL31_IMAGE_ID] = &bl31_image, - [SOC_FW_CONFIG_ID] = &soc_fw_config, - [TRUSTED_OS_FW_KEY_CERT_ID] = &trusted_os_fw_key_cert, - [TRUSTED_OS_FW_CONTENT_CERT_ID] = &trusted_os_fw_content_cert, - [BL32_IMAGE_ID] = &bl32_image, - [BL32_EXTRA1_IMAGE_ID] = &bl32_extra1_image, - [BL32_EXTRA2_IMAGE_ID] = &bl32_extra2_image, - [TOS_FW_CONFIG_ID] = &tos_fw_config, - [NON_TRUSTED_FW_CONTENT_CERT_ID] = &non_trusted_fw_content_cert, - [BL33_IMAGE_ID] = &bl33_image, - [NT_FW_CONFIG_ID] = &nt_fw_config, -#if defined(SPD_spmd) - [SIP_SP_CONTENT_CERT_ID] = &sip_sp_content_cert, - [PLAT_SP_CONTENT_CERT_ID] = &plat_sp_content_cert, - [SP_PKG1_ID] = &sp_pkg1, - [SP_PKG2_ID] = &sp_pkg2, - [SP_PKG3_ID] = &sp_pkg3, - [SP_PKG4_ID] = &sp_pkg4, - [SP_PKG5_ID] = &sp_pkg5, - [SP_PKG6_ID] = &sp_pkg6, - [SP_PKG7_ID] = &sp_pkg7, - [SP_PKG8_ID] = &sp_pkg8, -#endif -}; -#endif - -/* Register the CoT in the authentication module */ -REGISTER_COT(cot_desc); diff --git a/drivers/auth/mbedtls/mbedtls_common.mk b/drivers/auth/mbedtls/mbedtls_common.mk index a2c64303..765491e9 100644 --- a/drivers/auth/mbedtls/mbedtls_common.mk +++ b/drivers/auth/mbedtls/mbedtls_common.mk @@ -1,5 +1,5 @@ # -# Copyright (c) 2015-2023, Arm Limited. All rights reserved. +# Copyright (c) 2015-2024, Arm Limited. All rights reserved. # # SPDX-License-Identifier: BSD-3-Clause # @@ -19,16 +19,15 @@ MBEDTLS_MAJOR=$(shell grep -hP "define MBEDTLS_VERSION_MAJOR" ${MBEDTLS_DIR}/inc MBEDTLS_MINOR=$(shell grep -hP "define MBEDTLS_VERSION_MINOR" ${MBEDTLS_DIR}/include/mbedtls/*.h | grep -oe '\([0-9.]*\)') $(info MBEDTLS_VERSION_MAJOR is [${MBEDTLS_MAJOR}] MBEDTLS_VERSION_MINOR is [${MBEDTLS_MINOR}]) +ifneq (${MBEDTLS_MAJOR}, 3) + $(error Error: TF-A only supports MbedTLS versions > 3.x) +endif + # Specify mbed TLS configuration file -ifeq (${MBEDTLS_MAJOR}, 2) - $(info Deprecation Notice: Please migrate to Mbedtls version 3.x (refer to TF-A documentation for the exact version number)) - MBEDTLS_CONFIG_FILE ?= "<drivers/auth/mbedtls/mbedtls_config-2.h>" -else ifeq (${MBEDTLS_MAJOR}, 3) - ifeq (${PSA_CRYPTO},1) - MBEDTLS_CONFIG_FILE ?= "<drivers/auth/mbedtls/psa_mbedtls_config.h>" - else - MBEDTLS_CONFIG_FILE ?= "<drivers/auth/mbedtls/mbedtls_config-3.h>" - endif +ifeq (${PSA_CRYPTO},1) + MBEDTLS_CONFIG_FILE ?= "<drivers/auth/mbedtls/psa_mbedtls_config.h>" +else + MBEDTLS_CONFIG_FILE ?= "<drivers/auth/mbedtls/mbedtls_config-3.h>" endif $(eval $(call add_define,MBEDTLS_CONFIG_FILE)) @@ -47,9 +46,11 @@ LIBMBEDTLS_SRCS += $(addprefix ${MBEDTLS_DIR}/library/, \ platform.c \ platform_util.c \ bignum.c \ + bignum_core.c \ gcm.c \ md.c \ pk.c \ + pk_ecc.c \ pk_wrap.c \ pkparse.c \ pkwrite.c \ @@ -59,38 +60,22 @@ LIBMBEDTLS_SRCS += $(addprefix ${MBEDTLS_DIR}/library/, \ ecp_curves.c \ ecp.c \ rsa.c \ + rsa_alt_helpers.c \ x509.c \ x509_crt.c \ ) -ifeq (${MBEDTLS_MAJOR}, 2) - LIBMBEDTLS_SRCS += $(addprefix ${MBEDTLS_DIR}/library/, \ - rsa_internal.c \ - ) -else ifeq (${MBEDTLS_MAJOR}, 3) - LIBMBEDTLS_SRCS += $(addprefix ${MBEDTLS_DIR}/library/, \ - bignum_core.c \ - rsa_alt_helpers.c \ - hash_info.c \ - ) - - # Currently on Mbedtls-3 there is outstanding bug due to usage - # of redundant declaration[1], So disable redundant-decls - # compilation flag to avoid compilation error when compiling with - # Mbedtls-3. - # [1]: https://github.com/Mbed-TLS/mbedtls/issues/6910 - LIBMBEDTLS_CFLAGS += -Wno-error=redundant-decls -endif - ifeq (${PSA_CRYPTO},1) LIBMBEDTLS_SRCS += $(addprefix ${MBEDTLS_DIR}/library/, \ psa_crypto.c \ psa_crypto_client.c \ - psa_crypto_driver_wrappers.c \ psa_crypto_hash.c \ psa_crypto_rsa.c \ psa_crypto_ecp.c \ psa_crypto_slot_management.c \ + psa_crypto_aead.c \ + psa_crypto_cipher.c \ + psa_util.c \ ) endif @@ -134,6 +119,14 @@ else TF_MBEDTLS_HASH_ALG_ID := TF_MBEDTLS_SHA256 endif +ifeq (${MBOOT_EL_HASH_ALG}, sha256) + $(eval $(call add_define,TF_MBEDTLS_MBOOT_USE_SHA256)) +else ifeq (${MBOOT_EL_HASH_ALG}, sha384) + $(eval $(call add_define,TF_MBEDTLS_MBOOT_USE_SHA384)) +else ifeq (${MBOOT_EL_HASH_ALG}, sha512) + $(eval $(call add_define,TF_MBEDTLS_MBOOT_USE_SHA512)) +endif + ifeq (${TF_MBEDTLS_KEY_ALG},ecdsa) TF_MBEDTLS_KEY_ALG_ID := TF_MBEDTLS_ECDSA else ifeq (${TF_MBEDTLS_KEY_ALG},rsa) diff --git a/drivers/auth/mbedtls/mbedtls_crypto.c b/drivers/auth/mbedtls/mbedtls_crypto.c index 230cec9d..8fe426bb 100644 --- a/drivers/auth/mbedtls/mbedtls_crypto.c +++ b/drivers/auth/mbedtls/mbedtls_crypto.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015-2023, Arm Limited and Contributors. All rights reserved. + * Copyright (c) 2015-2024, Arm Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -65,6 +65,18 @@ static void init(void) #if CRYPTO_SUPPORT == CRYPTO_AUTH_VERIFY_ONLY || \ CRYPTO_SUPPORT == CRYPTO_AUTH_VERIFY_AND_HASH_CALC + + +/* + * NOTE: This has been made internal in mbedtls 3.6.0 and the mbedtls team has + * advised that it's better to copy out the declaration than it would be to + * update to 3.5.2, where this function is exposed. + */ +int mbedtls_x509_get_sig_alg(const mbedtls_x509_buf *sig_oid, + const mbedtls_x509_buf *sig_params, + mbedtls_md_type_t *md_alg, + mbedtls_pk_type_t *pk_alg, + void **sig_opts); /* * Verify a signature. * @@ -263,6 +275,7 @@ static int calc_hash(enum crypto_md_algo md_algo, void *data_ptr, unsigned char output[CRYPTO_MD_MAX_SIZE]) { const mbedtls_md_info_t *md_info; + int rc; md_info = mbedtls_md_info_from_type(md_type(md_algo)); if (md_info == NULL) { @@ -274,7 +287,12 @@ static int calc_hash(enum crypto_md_algo md_algo, void *data_ptr, * 'output' hash buffer pointer considering its size is always * bigger than or equal to MBEDTLS_MD_MAX_SIZE. */ - return mbedtls_md(md_info, data_ptr, data_len, output); + rc = mbedtls_md(md_info, data_ptr, data_len, output); + if (rc != 0) { + return CRYPTO_ERR_HASH; + } + + return CRYPTO_SUCCESS; } #endif /* CRYPTO_SUPPORT == CRYPTO_HASH_CALC_ONLY || \ CRYPTO_SUPPORT == CRYPTO_AUTH_VERIFY_AND_HASH_CALC */ diff --git a/drivers/auth/mbedtls/mbedtls_psa_crypto.c b/drivers/auth/mbedtls/mbedtls_psa_crypto.c index 5891acf3..0e4b57e2 100644 --- a/drivers/auth/mbedtls/mbedtls_psa_crypto.c +++ b/drivers/auth/mbedtls/mbedtls_psa_crypto.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023, Arm Limited. All rights reserved. + * Copyright (c) 2023-2024, Arm Limited. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -9,12 +9,11 @@ #include <string.h> /* mbed TLS headers */ -#include <mbedtls/gcm.h> #include <mbedtls/md.h> #include <mbedtls/memory_buffer_alloc.h> #include <mbedtls/oid.h> #include <mbedtls/platform.h> -#include <mbedtls/version.h> +#include <mbedtls/psa_util.h> #include <mbedtls/x509.h> #include <psa/crypto.h> #include <psa/crypto_platform.h> @@ -28,8 +27,10 @@ #define LIB_NAME "mbed TLS PSA" -/* Maximum length of R_S pair in the ECDSA signature in bytes */ -#define MAX_ECDSA_R_S_PAIR_LEN 64U +/* Minimum required size for a buffer containing a raw EC signature when using + * a maximum curve size of 384 bits. + * This is calculated as 2 * (384 / 8). */ +#define ECDSA_SIG_BUFFER_SIZE 96U /* Size of ASN.1 length and tag in bytes*/ #define SIZE_OF_ASN1_LEN 1U @@ -49,16 +50,6 @@ CASSERT(CRYPTO_MD_MAX_SIZE >= MBEDTLS_MD_MAX_SIZE, * CRYPTO_SUPPORT == CRYPTO_AUTH_VERIFY_AND_HASH_CALC */ -static inline psa_algorithm_t mbedtls_md_psa_alg_from_type( - mbedtls_md_type_t md_type) -{ - assert((md_type == MBEDTLS_MD_SHA256) || - (md_type == MBEDTLS_MD_SHA384) || - (md_type == MBEDTLS_MD_SHA512)); - - return PSA_ALG_CATEGORY_HASH | (psa_algorithm_t) (md_type + 0x5); -} - /* * AlgorithmIdentifier ::= SEQUENCE { * algorithm OBJECT IDENTIFIER, @@ -113,184 +104,82 @@ static void init(void) #if CRYPTO_SUPPORT == CRYPTO_AUTH_VERIFY_ONLY || \ CRYPTO_SUPPORT == CRYPTO_AUTH_VERIFY_AND_HASH_CALC -static void construct_psa_key_alg_and_type(mbedtls_pk_type_t pk_alg, - mbedtls_md_type_t md_alg, - psa_ecc_family_t psa_ecc_family, - psa_algorithm_t *psa_alg, - psa_key_type_t *psa_key_type) -{ - psa_algorithm_t psa_md_alg = mbedtls_md_psa_alg_from_type(md_alg); - - switch (pk_alg) { - case MBEDTLS_PK_RSASSA_PSS: - *psa_alg = PSA_ALG_RSA_PSS(psa_md_alg); - *psa_key_type = PSA_KEY_TYPE_RSA_PUBLIC_KEY; - break; - case MBEDTLS_PK_ECDSA: - *psa_alg = PSA_ALG_ECDSA(psa_md_alg); - *psa_key_type = PSA_KEY_TYPE_ECC_PUBLIC_KEY(psa_ecc_family); - break; - default: - *psa_alg = PSA_ALG_NONE; - *psa_key_type = PSA_KEY_TYPE_NONE; - break; - } -} - - -#if TF_MBEDTLS_KEY_ALG_ID == TF_MBEDTLS_ECDSA || \ -TF_MBEDTLS_KEY_ALG_ID == TF_MBEDTLS_RSA_AND_ECDSA - /* - * This is a helper function to detect padding byte (if the MSB bit of the - * first data byte is set to 1, for example 0x80) and on detection, ignore the - * padded byte(0x00) and increase the buffer pointer beyond padded byte and - * decrease the length of the buffer by 1. - * - * On Success returns 0, error otherwise. - **/ -static inline int ignore_asn1_int_padding_byte(unsigned char **buf_start, - size_t *buf_len) -{ - unsigned char *local_buf = *buf_start; - - /* Check for negative number */ - if ((local_buf[0] & 0x80U) != 0U) { - return -1; - } - - if ((local_buf[0] == 0U) && (local_buf[1] > 0x7FU) && - (*buf_len > 1U)) { - *buf_start = &local_buf[1]; - (*buf_len)--; - } - - return 0; -} + * NOTE: This has been made internal in mbedtls 3.6.0 and the mbedtls team has + * advised that it's better to copy out the declaration than it would be to + * update to 3.5.2, where this function is exposed. + */ +int mbedtls_x509_get_sig_alg(const mbedtls_x509_buf *sig_oid, + const mbedtls_x509_buf *sig_params, + mbedtls_md_type_t *md_alg, + mbedtls_pk_type_t *pk_alg, + void **sig_opts); /* - * This is a helper function that gets a pointer to the encoded ECDSA publicKey - * and its length (as per RFC5280) and returns corresponding decoded publicKey - * and its length. As well, it retrieves the family of ECC key in the PSA - * format. - * - * This function returns error(CRYPTO_ERR_SIGNATURE) on ASN.1 parsing failure, - * otherwise success(0). - **/ -static int get_ecdsa_pkinfo_from_asn1(unsigned char **pk_start, - unsigned int *pk_len, - psa_ecc_family_t *psa_ecc_family) + * This is a helper function which parses a SignatureAlgorithm OID. + * It extracts the pk algorithm and constructs a psa_algorithm_t object + * to be used by PSA calls. + */ +static int construct_psa_alg(void *sig_alg, unsigned int sig_alg_len, + mbedtls_pk_type_t *pk_alg, psa_algorithm_t *psa_alg) { - mbedtls_asn1_buf alg_oid, alg_params; - mbedtls_ecp_group_id grp_id; int rc; - unsigned char *pk_end; - size_t len; - size_t curve_bits; - unsigned char *pk_ptr = *pk_start; + mbedtls_md_type_t md_alg; + void *sig_opts = NULL; + mbedtls_asn1_buf sig_alg_oid, params; + unsigned char *p = (unsigned char *) sig_alg; + unsigned char *end = (unsigned char *) sig_alg + sig_alg_len; - pk_end = pk_ptr + *pk_len; - rc = mbedtls_asn1_get_tag(&pk_ptr, pk_end, &len, - MBEDTLS_ASN1_CONSTRUCTED | - MBEDTLS_ASN1_SEQUENCE); + rc = mbedtls_asn1_get_alg(&p, end, &sig_alg_oid, ¶ms); if (rc != 0) { - return CRYPTO_ERR_SIGNATURE; + rc = CRYPTO_ERR_SIGNATURE; + goto end; } - pk_end = pk_ptr + len; - rc = mbedtls_asn1_get_alg(&pk_ptr, pk_end, &alg_oid, &alg_params); + rc = mbedtls_x509_get_sig_alg(&sig_alg_oid, ¶ms, &md_alg, pk_alg, &sig_opts); if (rc != 0) { - return CRYPTO_ERR_SIGNATURE; + rc = CRYPTO_ERR_SIGNATURE; + goto end; } - if (alg_params.tag == MBEDTLS_ASN1_OID) { - if (mbedtls_oid_get_ec_grp(&alg_params, &grp_id) != 0) { - return CRYPTO_ERR_SIGNATURE; - } - *psa_ecc_family = mbedtls_ecc_group_to_psa(grp_id, - &curve_bits); - } else { - return CRYPTO_ERR_SIGNATURE; - } + psa_algorithm_t psa_md_alg = mbedtls_md_psa_alg_from_type(md_alg); - pk_end = pk_ptr + len - (alg_oid.len + alg_params.len + - 2 * (SIZE_OF_ASN1_LEN + SIZE_OF_ASN1_TAG)); - rc = mbedtls_asn1_get_bitstring_null(&pk_ptr, pk_end, &len); - if (rc != 0) { - return CRYPTO_ERR_SIGNATURE; + switch (*pk_alg) { + case MBEDTLS_PK_RSASSA_PSS: + *psa_alg = PSA_ALG_RSA_PSS(psa_md_alg); + rc = CRYPTO_SUCCESS; + break; + case MBEDTLS_PK_ECDSA: + *psa_alg = PSA_ALG_ECDSA(psa_md_alg); + rc = CRYPTO_SUCCESS; + break; + default: + *psa_alg = PSA_ALG_NONE; + rc = CRYPTO_ERR_SIGNATURE; + break; } - *pk_start = pk_ptr; - *pk_len = len; - +end: + mbedtls_free(sig_opts); return rc; } /* - * Ecdsa-Sig-Value ::= SEQUENCE { - * r INTEGER, - * s INTEGER - * } - * - * This helper function that gets a pointer to the encoded ECDSA signature and - * its length (as per RFC5280) and returns corresponding decoded signature - * (R_S pair) and its size. - * - * This function returns error(CRYPTO_ERR_SIGNATURE) on ASN.1 parsing failure, - * otherwise success(0). - **/ -static int get_ecdsa_signature_from_asn1(unsigned char *sig_ptr, - size_t *sig_len, - unsigned char *r_s_pair) + * Helper functions for mbedtls PK contexts. + */ +static void initialize_pk_context(mbedtls_pk_context *pk, bool *pk_initialized) { - int rc; - unsigned char *sig_end; - size_t len, r_len, s_len; - - sig_end = sig_ptr + *sig_len; - rc = mbedtls_asn1_get_tag(&sig_ptr, sig_end, &len, - MBEDTLS_ASN1_CONSTRUCTED | - MBEDTLS_ASN1_SEQUENCE); - if (rc != 0) { - return CRYPTO_ERR_SIGNATURE; - } - - sig_end = sig_ptr + len; - rc = mbedtls_asn1_get_tag(&sig_ptr, sig_end, &r_len, - MBEDTLS_ASN1_INTEGER); - if (rc != 0) { - return CRYPTO_ERR_SIGNATURE; - } - - if (ignore_asn1_int_padding_byte(&sig_ptr, &r_len) != 0) { - return CRYPTO_ERR_SIGNATURE; - } - - (void)memcpy((void *)&r_s_pair[0], (const void *)sig_ptr, r_len); - - sig_ptr = sig_ptr + r_len; - sig_end = sig_ptr + len - (r_len + (SIZE_OF_ASN1_LEN + - SIZE_OF_ASN1_TAG)); - rc = mbedtls_asn1_get_tag(&sig_ptr, sig_end, &s_len, - MBEDTLS_ASN1_INTEGER); - if (rc != 0) { - return CRYPTO_ERR_SIGNATURE; - } + mbedtls_pk_init(pk); + *pk_initialized = true; +} - if (ignore_asn1_int_padding_byte(&sig_ptr, &s_len) != 0) { - return CRYPTO_ERR_SIGNATURE; +static void cleanup_pk_context(mbedtls_pk_context *pk, bool *pk_initialized) +{ + if (*pk_initialized) { + mbedtls_pk_free(pk); + *pk_initialized = false; } - - (void)memcpy((void *)&r_s_pair[r_len], (const void *)sig_ptr, s_len); - - *sig_len = s_len + r_len; - - return 0; } -#endif /* - * TF_MBEDTLS_KEY_ALG_ID == TF_MBEDTLS_ECDSA || \ - * TF_MBEDTLS_KEY_ALG_ID == TF_MBEDTLS_RSA_AND_ECDSA - **/ /* * Verify a signature. @@ -303,125 +192,99 @@ static int verify_signature(void *data_ptr, unsigned int data_len, void *sig_alg, unsigned int sig_alg_len, void *pk_ptr, unsigned int pk_len) { - mbedtls_asn1_buf sig_oid, sig_params; - mbedtls_asn1_buf signature; - mbedtls_md_type_t md_alg; - mbedtls_pk_type_t pk_alg; - int rc; - void *sig_opts = NULL; unsigned char *p, *end; + mbedtls_pk_context pk; + bool pk_initialized = false; + int rc = CRYPTO_ERR_SIGNATURE; + psa_status_t psa_status = PSA_ERROR_CORRUPTION_DETECTED; + psa_key_attributes_t psa_key_attr = PSA_KEY_ATTRIBUTES_INIT; + psa_key_id_t psa_key_id; + mbedtls_pk_type_t pk_alg; + psa_algorithm_t psa_alg; + __unused unsigned char reformatted_sig[ECDSA_SIG_BUFFER_SIZE] = {0}; unsigned char *local_sig_ptr; size_t local_sig_len; - psa_ecc_family_t psa_ecc_family = 0U; - __unused unsigned char reformatted_sig[MAX_ECDSA_R_S_PAIR_LEN] = {0}; - /* construct PSA key algo and type */ - psa_status_t status = PSA_SUCCESS; - psa_key_attributes_t psa_key_attr = PSA_KEY_ATTRIBUTES_INIT; - psa_key_id_t psa_key_id = PSA_KEY_ID_NULL; - psa_key_type_t psa_key_type; - psa_algorithm_t psa_alg; + /* Load the key into the PSA key store. */ + initialize_pk_context(&pk, &pk_initialized); - /* Get pointers to signature OID and parameters */ - p = (unsigned char *)sig_alg; - end = (unsigned char *)(p + sig_alg_len); - rc = mbedtls_asn1_get_alg(&p, end, &sig_oid, &sig_params); + p = (unsigned char *) pk_ptr; + end = p + pk_len; + rc = mbedtls_pk_parse_subpubkey(&p, end, &pk); if (rc != 0) { - return CRYPTO_ERR_SIGNATURE; + rc = CRYPTO_ERR_SIGNATURE; + goto end2; } - /* Get the actual signature algorithm (MD + PK) */ - rc = mbedtls_x509_get_sig_alg(&sig_oid, &sig_params, &md_alg, &pk_alg, &sig_opts); + rc = mbedtls_pk_get_psa_attributes(&pk, PSA_KEY_USAGE_VERIFY_MESSAGE, &psa_key_attr); if (rc != 0) { - return CRYPTO_ERR_SIGNATURE; + rc = CRYPTO_ERR_SIGNATURE; + goto end2; } - /* Get the signature (bitstring) */ - p = (unsigned char *)sig_ptr; - end = (unsigned char *)(p + sig_len); - signature.tag = *p; - rc = mbedtls_asn1_get_bitstring_null(&p, end, &signature.len); - if ((rc != 0) || ((size_t)(end - p) != signature.len)) { + rc = construct_psa_alg(sig_alg, sig_alg_len, &pk_alg, &psa_alg); + if (rc != CRYPTO_SUCCESS) { + goto end2; + } + psa_set_key_algorithm(&psa_key_attr, psa_alg); + + rc = mbedtls_pk_import_into_psa(&pk, &psa_key_attr, &psa_key_id); + if (rc != 0) { rc = CRYPTO_ERR_SIGNATURE; goto end2; } + /* Optimize mbedtls heap usage by freeing the pk context now. */ + cleanup_pk_context(&pk, &pk_initialized); + + /* Extract the signature from sig_ptr. */ + p = (unsigned char *) sig_ptr; + end = p + sig_len; + rc = mbedtls_asn1_get_bitstring_null(&p, end, &local_sig_len); + if (rc != 0) { + rc = CRYPTO_ERR_SIGNATURE; + goto end1; + } local_sig_ptr = p; - local_sig_len = signature.len; #if TF_MBEDTLS_KEY_ALG_ID == TF_MBEDTLS_ECDSA || \ TF_MBEDTLS_KEY_ALG_ID == TF_MBEDTLS_RSA_AND_ECDSA if (pk_alg == MBEDTLS_PK_ECDSA) { - rc = get_ecdsa_signature_from_asn1(local_sig_ptr, - &local_sig_len, - reformatted_sig); - if (rc != 0) { - goto end2; - } + /* Convert the DER ASN.1 signature to raw format. */ + size_t key_bits = psa_get_key_bits(&psa_key_attr); - local_sig_ptr = reformatted_sig; - - rc = get_ecdsa_pkinfo_from_asn1((unsigned char **)&pk_ptr, - &pk_len, - &psa_ecc_family); + rc = mbedtls_ecdsa_der_to_raw(key_bits, p, local_sig_len, + reformatted_sig, ECDSA_SIG_BUFFER_SIZE, + &local_sig_len); if (rc != 0) { - goto end2; + rc = CRYPTO_ERR_SIGNATURE; + goto end1; } + local_sig_ptr = reformatted_sig; } #endif /* * TF_MBEDTLS_KEY_ALG_ID == TF_MBEDTLS_ECDSA || \ * TF_MBEDTLS_KEY_ALG_ID == TF_MBEDTLS_RSA_AND_ECDSA **/ - /* Convert this pk_alg and md_alg to PSA key type and key algorithm */ - construct_psa_key_alg_and_type(pk_alg, md_alg, psa_ecc_family, - &psa_alg, &psa_key_type); - - - if ((psa_alg == PSA_ALG_NONE) || (psa_key_type == PSA_KEY_TYPE_NONE)) { - rc = CRYPTO_ERR_SIGNATURE; - goto end2; - } - - /* filled-in key_attributes */ - psa_set_key_algorithm(&psa_key_attr, psa_alg); - psa_set_key_type(&psa_key_attr, psa_key_type); - psa_set_key_usage_flags(&psa_key_attr, PSA_KEY_USAGE_VERIFY_MESSAGE); - - /* Get the key_id using import API */ - status = psa_import_key(&psa_key_attr, - pk_ptr, - (size_t)pk_len, - &psa_key_id); - - if (status != PSA_SUCCESS) { - rc = CRYPTO_ERR_SIGNATURE; - goto end2; - } - - /* - * Hash calculation and Signature verification of the given data payload - * is wrapped under the psa_verify_message function. - */ - status = psa_verify_message(psa_key_id, psa_alg, + /* Verify the signature. */ + psa_status = psa_verify_message(psa_key_id, psa_alg, data_ptr, data_len, local_sig_ptr, local_sig_len); - - if (status != PSA_SUCCESS) { + if (psa_status == PSA_SUCCESS) { + /* The signature has been successfully verified. */ + rc = CRYPTO_SUCCESS; + } else { rc = CRYPTO_ERR_SIGNATURE; - goto end1; } - /* Signature verification success */ - rc = CRYPTO_SUCCESS; - end1: - /* - * Destroy the key if it is created successfully - */ + /* Destroy the key from the PSA subsystem. */ psa_destroy_key(psa_key_id); end2: - mbedtls_free(sig_opts); + /* Free the pk context, if it is initialized. */ + cleanup_pk_context(&pk, &pk_initialized); + return rc; } @@ -570,78 +433,61 @@ static int aes_gcm_decrypt(void *data_ptr, size_t len, const void *key, unsigned int iv_len, const void *tag, unsigned int tag_len) { - mbedtls_gcm_context ctx; - mbedtls_cipher_id_t cipher = MBEDTLS_CIPHER_ID_AES; + mbedtls_svc_key_id_t key_id = MBEDTLS_SVC_KEY_ID_INIT; + psa_aead_operation_t operation = PSA_AEAD_OPERATION_INIT; + psa_key_attributes_t attributes = PSA_KEY_ATTRIBUTES_INIT; + psa_status_t psa_status = PSA_ERROR_GENERIC_ERROR; unsigned char buf[DEC_OP_BUF_SIZE]; - unsigned char tag_buf[CRYPTO_MAX_TAG_SIZE]; unsigned char *pt = data_ptr; size_t dec_len; - int diff, i, rc; - size_t output_length __unused; + size_t output_length; - mbedtls_gcm_init(&ctx); + /* Load the key into the PSA key store. */ + psa_set_key_usage_flags(&attributes, PSA_KEY_USAGE_DECRYPT); + psa_set_key_algorithm(&attributes, PSA_ALG_GCM); + psa_set_key_type(&attributes, PSA_KEY_TYPE_AES); - rc = mbedtls_gcm_setkey(&ctx, cipher, key, key_len * 8); - if (rc != 0) { - rc = CRYPTO_ERR_DECRYPTION; - goto exit_gcm; + psa_status = psa_import_key(&attributes, key, key_len, &key_id); + if (psa_status != PSA_SUCCESS) { + return CRYPTO_ERR_DECRYPTION; } -#if (MBEDTLS_VERSION_MAJOR < 3) - rc = mbedtls_gcm_starts(&ctx, MBEDTLS_GCM_DECRYPT, iv, iv_len, NULL, 0); -#else - rc = mbedtls_gcm_starts(&ctx, MBEDTLS_GCM_DECRYPT, iv, iv_len); -#endif - if (rc != 0) { - rc = CRYPTO_ERR_DECRYPTION; - goto exit_gcm; + /* Perform the decryption. */ + psa_status = psa_aead_decrypt_setup(&operation, key_id, PSA_ALG_GCM); + if (psa_status != PSA_SUCCESS) { + goto err; + } + + psa_status = psa_aead_set_nonce(&operation, iv, iv_len); + if (psa_status != PSA_SUCCESS) { + goto err; } while (len > 0) { dec_len = MIN(sizeof(buf), len); -#if (MBEDTLS_VERSION_MAJOR < 3) - rc = mbedtls_gcm_update(&ctx, dec_len, pt, buf); -#else - rc = mbedtls_gcm_update(&ctx, pt, dec_len, buf, sizeof(buf), &output_length); -#endif - - if (rc != 0) { - rc = CRYPTO_ERR_DECRYPTION; - goto exit_gcm; + psa_status = psa_aead_update(&operation, pt, dec_len, buf, + sizeof(buf), &output_length); + if (psa_status != PSA_SUCCESS) { + goto err; } - memcpy(pt, buf, dec_len); - pt += dec_len; + memcpy(pt, buf, output_length); + pt += output_length; len -= dec_len; } -#if (MBEDTLS_VERSION_MAJOR < 3) - rc = mbedtls_gcm_finish(&ctx, tag_buf, sizeof(tag_buf)); -#else - rc = mbedtls_gcm_finish(&ctx, NULL, 0, &output_length, tag_buf, sizeof(tag_buf)); -#endif - - if (rc != 0) { - rc = CRYPTO_ERR_DECRYPTION; - goto exit_gcm; + /* Verify the tag. */ + psa_status = psa_aead_verify(&operation, NULL, 0, &output_length, tag, tag_len); + if (psa_status == PSA_SUCCESS) { + psa_destroy_key(key_id); + return CRYPTO_SUCCESS; } - /* Check tag in "constant-time" */ - for (diff = 0, i = 0; i < tag_len; i++) - diff |= ((const unsigned char *)tag)[i] ^ tag_buf[i]; - - if (diff != 0) { - rc = CRYPTO_ERR_DECRYPTION; - goto exit_gcm; - } - - /* GCM decryption success */ - rc = CRYPTO_SUCCESS; - -exit_gcm: - mbedtls_gcm_free(&ctx); - return rc; +err: + psa_aead_abort(&operation); + psa_destroy_key(key_id); + return CRYPTO_ERR_DECRYPTION; } /* diff --git a/drivers/auth/tbbr/tbbr_cot_bl1.c b/drivers/auth/tbbr/tbbr_cot_bl1.c index 21942b49..8bab6e87 100644 --- a/drivers/auth/tbbr/tbbr_cot_bl1.c +++ b/drivers/auth/tbbr/tbbr_cot_bl1.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015-2023, Arm Limited and Contributors. All rights reserved. + * Copyright (c) 2015-2024, Arm Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -173,7 +173,6 @@ static const auth_img_desc_t fw_config = { static const auth_img_desc_t * const cot_desc[] = { [TRUSTED_BOOT_FW_CERT_ID] = &trusted_boot_fw_cert, [BL2_IMAGE_ID] = &bl2_image, - [HW_CONFIG_ID] = &hw_config, [TB_FW_CONFIG_ID] = &tb_fw_config, [FW_CONFIG_ID] = &fw_config, [FWU_CERT_ID] = &fwu_cert, diff --git a/drivers/auth/tbbr/tbbr_cot_bl2.c b/drivers/auth/tbbr/tbbr_cot_bl2.c index ce2aa7e2..8dccaece 100644 --- a/drivers/auth/tbbr/tbbr_cot_bl2.c +++ b/drivers/auth/tbbr/tbbr_cot_bl2.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015-2023, Arm Limited and Contributors. All rights reserved. + * Copyright (c) 2015-2024, Arm Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -84,6 +84,22 @@ static auth_param_type_desc_t sp_pkg8_hash = AUTH_PARAM_TYPE_DESC( AUTH_PARAM_HASH, SP_PKG8_HASH_OID); #endif /* SPD_spmd */ +/* HW Config */ +static const auth_img_desc_t hw_config = { + .img_id = HW_CONFIG_ID, + .img_type = IMG_RAW, + .parent = &trusted_boot_fw_cert, + .img_auth_methods = (const auth_method_desc_t[AUTH_METHOD_NUM]) { + [0] = { + .type = AUTH_METHOD_HASH, + .param.hash = { + .data = &raw_data, + .hash = &hw_config_hash + } + } + } +}; + /* * Trusted key certificate */ diff --git a/drivers/auth/tbbr/tbbr_cot_common.c b/drivers/auth/tbbr/tbbr_cot_common.c index 8c372488..619b241c 100644 --- a/drivers/auth/tbbr/tbbr_cot_common.c +++ b/drivers/auth/tbbr/tbbr_cot_common.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015-2023, Arm Limited and Contributors. All rights reserved. + * Copyright (c) 2015-2024, Arm Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -52,7 +52,7 @@ auth_param_type_desc_t tb_fw_config_hash = AUTH_PARAM_TYPE_DESC( AUTH_PARAM_HASH, TRUSTED_BOOT_FW_CONFIG_HASH_OID); auth_param_type_desc_t fw_config_hash = AUTH_PARAM_TYPE_DESC( AUTH_PARAM_HASH, FW_CONFIG_HASH_OID); -static auth_param_type_desc_t hw_config_hash = AUTH_PARAM_TYPE_DESC( +auth_param_type_desc_t hw_config_hash = AUTH_PARAM_TYPE_DESC( AUTH_PARAM_HASH, HW_CONFIG_HASH_OID); /* trusted_boot_fw_cert */ @@ -109,19 +109,3 @@ const auth_img_desc_t trusted_boot_fw_cert = { } } }; - -/* HW Config */ -const auth_img_desc_t hw_config = { - .img_id = HW_CONFIG_ID, - .img_type = IMG_RAW, - .parent = &trusted_boot_fw_cert, - .img_auth_methods = (const auth_method_desc_t[AUTH_METHOD_NUM]) { - [0] = { - .type = AUTH_METHOD_HASH, - .param.hash = { - .data = &raw_data, - .hash = &hw_config_hash - } - } - } -}; diff --git a/drivers/cadence/emmc/cdns_sdmmc.c b/drivers/cadence/emmc/cdns_sdmmc.c index d2cd4d6a..892d3330 100644 --- a/drivers/cadence/emmc/cdns_sdmmc.c +++ b/drivers/cadence/emmc/cdns_sdmmc.c @@ -1,5 +1,6 @@ /* * Copyright (c) 2022-2023, Intel Corporation. All rights reserved. + * Copyright (c) 2024, Altera Corporation. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -18,35 +19,6 @@ #include <lib/mmio.h> #include <lib/utils.h> -/* Card busy and present */ -#define CARD_BUSY 1 -#define CARD_NOT_BUSY 0 - -/* 500 ms delay to read the RINST register */ -#define DELAY_MS_SRS_READ 500 -#define DELAY_RES 10 - -/* SRS12 error mask */ -#define SRS12_ERR_MASK 0xFFFF8000 - -/* Check DV dfi_init val=0 */ -#define IO_MASK_END_DATA 0x0 - -/* Check DV dfi_init val=2; DDR Mode */ -#define IO_MASK_END_DATA_DDR 0x2 -#define IO_MASK_START_DATA 0x0 -#define DATA_SELECT_OE_END_DATA 0x1 - -#define TIMEOUT 100000 - -/* General define */ -#define SDHC_REG_MASK UINT_MAX -#define SD_HOST_BLOCK_SIZE 0x200 -#define DTCVVAL_DEFAULT_VAL 0xE -#define CDMMC_DMA_MAX_BUFFER_SIZE 64*1024 -#define CDNSMMC_ADDRESS_MASK U(0x0f) -#define CONFIG_CDNS_DESC_COUNT 8 - void cdns_init(void); int cdns_send_cmd(struct mmc_cmd *cmd); int cdns_set_ios(unsigned int clk, unsigned int width); @@ -62,7 +34,8 @@ const struct mmc_ops cdns_sdmmc_ops = { .read = cdns_read, .write = cdns_write, }; - +void sd_host_adma_prepare(struct cdns_idmac_desc *desc_ptr, uintptr_t buf, + size_t size); struct cdns_sdmmc_params cdns_params; struct cdns_sdmmc_combo_phy sdmmc_combo_phy_reg; struct cdns_sdmmc_sdhc sdmmc_sdhc_reg; @@ -92,42 +65,19 @@ int cdns_wait_ics(uint16_t timeout, uint32_t cdn_srs_res) return 0; } -int cdns_busy(void) -{ - unsigned int data; - - data = mmio_read_32(MMC_REG_BASE + SDHC_CDNS_SRS09); - return (data & STATUS_DATA_BUSY) ? CARD_BUSY : CARD_NOT_BUSY; -} - -int cdns_vol_reset(void) -{ - /* Reset embedded card */ - mmio_write_32((MMC_REG_BASE + SDHC_CDNS_SRS10), (7 << SDMMC_CDN_BVS) | (1 << SDMMC_CDN_BP)); - udelay(250); - mmio_write_32((MMC_REG_BASE + SDHC_CDNS_SRS10), (7 << SDMMC_CDN_BVS) | (0 << SDMMC_CDN_BP)); - udelay(500); - - /* Turn on supply voltage */ - /* BVS = 7, BP = 1, BP2 only in UHS2 mode */ - mmio_write_32((MMC_REG_BASE + SDHC_CDNS_SRS10), (7 << SDMMC_CDN_BVS) | (1 << SDMMC_CDN_BP)); - udelay(250); - return 0; -} - void cdns_set_sdmmc_var(struct cdns_sdmmc_combo_phy *combo_phy_reg, - struct cdns_sdmmc_sdhc *sdhc_reg) + struct cdns_sdmmc_sdhc *sdhc_reg) { /* Values are taken by the reference of cadence IP documents */ combo_phy_reg->cp_clk_wr_delay = 0; combo_phy_reg->cp_clk_wrdqs_delay = 0; - combo_phy_reg->cp_data_select_oe_end = 0; + combo_phy_reg->cp_data_select_oe_end = 1; combo_phy_reg->cp_dll_bypass_mode = 1; combo_phy_reg->cp_dll_locked_mode = 0; - combo_phy_reg->cp_dll_start_point = 0; + combo_phy_reg->cp_dll_start_point = 254; combo_phy_reg->cp_gate_cfg_always_on = 1; combo_phy_reg->cp_io_mask_always_on = 0; - combo_phy_reg->cp_io_mask_end = 0; + combo_phy_reg->cp_io_mask_end = 5; combo_phy_reg->cp_io_mask_start = 0; combo_phy_reg->cp_rd_del_sel = 52; combo_phy_reg->cp_read_dqs_cmd_delay = 0; @@ -142,38 +92,58 @@ void cdns_set_sdmmc_var(struct cdns_sdmmc_combo_phy *combo_phy_reg, sdhc_reg->sdhc_extended_rd_mode = 1; sdhc_reg->sdhc_extended_wr_mode = 1; - sdhc_reg->sdhc_hcsdclkadj = 0; + sdhc_reg->sdhc_hcsdclkadj = 3; sdhc_reg->sdhc_idelay_val = 0; sdhc_reg->sdhc_rdcmd_en = 1; sdhc_reg->sdhc_rddata_en = 1; - sdhc_reg->sdhc_rw_compensate = 9; + sdhc_reg->sdhc_rw_compensate = 10; sdhc_reg->sdhc_sdcfsh = 0; - sdhc_reg->sdhc_sdcfsl = 1; + sdhc_reg->sdhc_sdcfsl = 0; sdhc_reg->sdhc_wrcmd0_dly = 1; sdhc_reg->sdhc_wrcmd0_sdclk_dly = 0; sdhc_reg->sdhc_wrcmd1_dly = 0; sdhc_reg->sdhc_wrcmd1_sdclk_dly = 0; - sdhc_reg->sdhc_wrdata0_dly = 1; + sdhc_reg->sdhc_wrdata0_dly = 0; sdhc_reg->sdhc_wrdata0_sdclk_dly = 0; sdhc_reg->sdhc_wrdata1_dly = 0; sdhc_reg->sdhc_wrdata1_sdclk_dly = 0; } -static int cdns_program_phy_reg(struct cdns_sdmmc_combo_phy *combo_phy_reg, - struct cdns_sdmmc_sdhc *sdhc_reg) +int cdns_program_phy_reg(struct cdns_sdmmc_combo_phy *combo_phy_reg, + struct cdns_sdmmc_sdhc *sdhc_reg) { uint32_t value = 0; int ret = 0; + uint32_t timeout = 0; + + /* HRS00 - Software Reset */ + mmio_write_32((cdns_params.reg_base + SDHC_CDNS_HRS00), SDHC_CDNS_HRS00_SWR); + + /* Waiting for SDHC_CDNS_HRS00_SWR reset */ + timeout = TIMEOUT; + do { + udelay(250); + if (--timeout <= 0) { + NOTICE(" SDHC Software Reset failed!!!\n"); + panic(); + } + } while (((mmio_read_32(cdns_params.reg_base + SDHC_CDNS_HRS00) & + SDHC_CDNS_HRS00_SWR) == 1)); + + /* Step 1, switch on DLL_RESET */ + value = mmio_read_32(cdns_params.reg_base + SDHC_CDNS_HRS09); + value &= ~SDHC_PHY_SW_RESET; + mmio_write_32(cdns_params.reg_base + SDHC_CDNS_HRS09, value); /* program PHY_DQS_TIMING_REG */ value = (CP_USE_EXT_LPBK_DQS(combo_phy_reg->cp_use_ext_lpbk_dqs)) | (CP_USE_LPBK_DQS(combo_phy_reg->cp_use_lpbk_dqs)) | (CP_USE_PHONY_DQS(combo_phy_reg->cp_use_phony_dqs)) | (CP_USE_PHONY_DQS_CMD(combo_phy_reg->cp_use_phony_dqs_cmd)); - ret = cdns_sdmmc_write_phy_reg(MMC_REG_BASE + SDHC_CDNS_HRS04, - COMBO_PHY_REG + PHY_DQS_TIMING_REG, MMC_REG_BASE + - SDHC_CDNS_HRS05, value); - if (ret != 0) { + ret = cdns_sdmmc_write_phy_reg(cdns_params.reg_base + SDHC_CDNS_HRS04, + COMBO_PHY_REG + PHY_DQS_TIMING_REG, + cdns_params.reg_base + SDHC_CDNS_HRS05, value); + if (ret != 0U) { return ret; } @@ -183,73 +153,90 @@ static int cdns_program_phy_reg(struct cdns_sdmmc_combo_phy *combo_phy_reg, (CP_RD_DEL_SEL(combo_phy_reg->cp_rd_del_sel)) | (CP_UNDERRUN_SUPPRESS(combo_phy_reg->cp_underrun_suppress)) | (CP_GATE_CFG_ALWAYS_ON(combo_phy_reg->cp_gate_cfg_always_on)); - ret = cdns_sdmmc_write_phy_reg(MMC_REG_BASE + SDHC_CDNS_HRS04, - COMBO_PHY_REG + PHY_GATE_LPBK_CTRL_REG, MMC_REG_BASE + - SDHC_CDNS_HRS05, value); - if (ret != 0) { - return ret; + ret = cdns_sdmmc_write_phy_reg(cdns_params.reg_base + SDHC_CDNS_HRS04, + COMBO_PHY_REG + PHY_GATE_LPBK_CTRL_REG, + cdns_params.reg_base + SDHC_CDNS_HRS05, value); + if (ret != 0U) { + return -ret; } /* program PHY_DLL_MASTER_CTRL_REG */ - value = (CP_DLL_BYPASS_MODE(combo_phy_reg->cp_dll_bypass_mode)) - | (CP_DLL_START_POINT(combo_phy_reg->cp_dll_start_point)); - ret = cdns_sdmmc_write_phy_reg(MMC_REG_BASE + SDHC_CDNS_HRS04, - COMBO_PHY_REG + PHY_DLL_MASTER_CTRL_REG, MMC_REG_BASE - + SDHC_CDNS_HRS05, value); - if (ret != 0) { + value = (CP_DLL_BYPASS_MODE(combo_phy_reg->cp_dll_bypass_mode)) | (2 << 20) | + (CP_DLL_START_POINT(combo_phy_reg->cp_dll_start_point)); + ret = cdns_sdmmc_write_phy_reg(cdns_params.reg_base + SDHC_CDNS_HRS04, + COMBO_PHY_REG + PHY_DLL_MASTER_CTRL_REG, + cdns_params.reg_base + SDHC_CDNS_HRS05, value); + if (ret != 0U) { return ret; } /* program PHY_DLL_SLAVE_CTRL_REG */ - value = (CP_READ_DQS_CMD_DELAY(combo_phy_reg->cp_read_dqs_cmd_delay)) - | (CP_CLK_WRDQS_DELAY(combo_phy_reg->cp_clk_wrdqs_delay)) - | (CP_CLK_WR_DELAY(combo_phy_reg->cp_clk_wr_delay)) - | (CP_READ_DQS_DELAY(combo_phy_reg->cp_read_dqs_delay)); - ret = cdns_sdmmc_write_phy_reg(MMC_REG_BASE + SDHC_CDNS_HRS04, - COMBO_PHY_REG + PHY_DLL_SLAVE_CTRL_REG, MMC_REG_BASE - + SDHC_CDNS_HRS05, value); - if (ret != 0) { + value = (CP_READ_DQS_CMD_DELAY(combo_phy_reg->cp_read_dqs_cmd_delay)) | + (CP_CLK_WRDQS_DELAY(combo_phy_reg->cp_clk_wrdqs_delay)) | + (CP_CLK_WR_DELAY(combo_phy_reg->cp_clk_wr_delay)) | + (CP_READ_DQS_DELAY(combo_phy_reg->cp_read_dqs_delay)); + ret = cdns_sdmmc_write_phy_reg(cdns_params.reg_base + SDHC_CDNS_HRS04, + COMBO_PHY_REG + PHY_DLL_SLAVE_CTRL_REG, + cdns_params.reg_base + SDHC_CDNS_HRS05, value); + if (ret != 0U) { return ret; } /* program PHY_CTRL_REG */ - mmio_write_32(MMC_REG_BASE + SDHC_CDNS_HRS04, COMBO_PHY_REG - + PHY_CTRL_REG); - value = mmio_read_32(MMC_REG_BASE + SDHC_CDNS_HRS05); + mmio_write_32(cdns_params.reg_base + SDHC_CDNS_HRS04, COMBO_PHY_REG + PHY_CTRL_REG); + value = mmio_read_32(cdns_params.reg_base + SDHC_CDNS_HRS05); /* phony_dqs_timing=0 */ value &= ~(CP_PHONY_DQS_TIMING_MASK << CP_PHONY_DQS_TIMING_SHIFT); - mmio_write_32(MMC_REG_BASE + SDHC_CDNS_HRS05, value); + mmio_write_32(cdns_params.reg_base + SDHC_CDNS_HRS05, value); /* switch off DLL_RESET */ do { - value = mmio_read_32(MMC_REG_BASE + SDHC_CDNS_HRS09); + value = mmio_read_32(cdns_params.reg_base + SDHC_CDNS_HRS09); value |= SDHC_PHY_SW_RESET; - mmio_write_32(MMC_REG_BASE + SDHC_CDNS_HRS09, value); - value = mmio_read_32(MMC_REG_BASE + SDHC_CDNS_HRS09); + mmio_write_32(cdns_params.reg_base + SDHC_CDNS_HRS09, value); + value = mmio_read_32(cdns_params.reg_base + SDHC_CDNS_HRS09); /* polling PHY_INIT_COMPLETE */ } while ((value & SDHC_PHY_INIT_COMPLETE) != SDHC_PHY_INIT_COMPLETE); /* program PHY_DQ_TIMING_REG */ - combo_phy_reg->cp_io_mask_end = 0U; - value = (CP_IO_MASK_ALWAYS_ON(combo_phy_reg->cp_io_mask_always_on)) - | (CP_IO_MASK_END(combo_phy_reg->cp_io_mask_end)) - | (CP_IO_MASK_START(combo_phy_reg->cp_io_mask_start)) - | (CP_DATA_SELECT_OE_END(combo_phy_reg->cp_data_select_oe_end)); - - ret = cdns_sdmmc_write_phy_reg(MMC_REG_BASE + SDHC_CDNS_HRS04, - COMBO_PHY_REG + PHY_DQ_TIMING_REG, MMC_REG_BASE - + SDHC_CDNS_HRS05, value); - if (ret != 0) { + value = (CP_IO_MASK_ALWAYS_ON(combo_phy_reg->cp_io_mask_always_on)) | + (CP_IO_MASK_END(combo_phy_reg->cp_io_mask_end)) | + (CP_IO_MASK_START(combo_phy_reg->cp_io_mask_start)) | + (CP_DATA_SELECT_OE_END(combo_phy_reg->cp_data_select_oe_end)); + + ret = cdns_sdmmc_write_phy_reg(cdns_params.reg_base + SDHC_CDNS_HRS04, + COMBO_PHY_REG + PHY_DQ_TIMING_REG, + cdns_params.reg_base + SDHC_CDNS_HRS05, value); + if (ret != 0U) { return ret; } + + value = mmio_read_32(cdns_params.reg_base + SDHC_CDNS_HRS09); + value |= (HRS_09_EXTENDED_RD_MODE | HRS_09_EXTENDED_WR_MODE | + HRS_09_RDCMD_EN | HRS_09_RDDATA_EN); + mmio_write_32(cdns_params.reg_base + SDHC_CDNS_HRS09, value); + + value = 0; + value = SDHC_HCSDCLKADJ(HRS_10_HCSDCLKADJ_VAL); + mmio_write_32(cdns_params.reg_base + SDHC_CDNS_HRS10, value); + + value = 0; + mmio_write_32(cdns_params.reg_base + SDHC_CDNS_HRS16, value); + + value = (10 << 16); + mmio_write_32(cdns_params.reg_base + SDHC_CDNS_HRS07, value); + return 0; } int cdns_read(int lba, uintptr_t buf, size_t size) { - inv_dcache_range(buf, size); + return 0; +} +int cdns_write(int lba, uintptr_t buf, size_t size) +{ return 0; } @@ -260,120 +247,79 @@ void cdns_init(void) int cdns_prepare(int dma_start_addr, uintptr_t dma_buff, size_t size) { - data_cmd = true; - struct cdns_idmac_desc *desc; - uint32_t desc_cnt, i; - uint64_t desc_base; - + struct cdns_idmac_desc *cdns_desc_data; assert(((dma_buff & CDNSMMC_ADDRESS_MASK) == 0) && - (cdns_params.desc_size > 0) && - ((MMC_REG_BASE & MMC_BLOCK_MASK) == 0) && - ((cdns_params.desc_base & MMC_BLOCK_MASK) == 0) && - ((cdns_params.desc_size & MMC_BLOCK_MASK) == 0)); - - flush_dcache_range(dma_buff, size); - - desc_cnt = (size + (CDMMC_DMA_MAX_BUFFER_SIZE) - 1) / (CDMMC_DMA_MAX_BUFFER_SIZE); - assert(desc_cnt * sizeof(struct cdns_idmac_desc) < cdns_params.desc_size); + (cdns_params.desc_size > 0)); - if (desc_cnt > CONFIG_CDNS_DESC_COUNT) { - ERROR("Requested data transfer length %ld is greater than configured length %d", - size, (CONFIG_CDNS_DESC_COUNT * CDMMC_DMA_MAX_BUFFER_SIZE)); - return -EINVAL; - } - - desc = (struct cdns_idmac_desc *)cdns_params.desc_base; - desc_base = (uint64_t)desc; - i = 0; - - while ((i + 1) < desc_cnt) { - desc->attr = ADMA_DESC_ATTR_VALID | ADMA_DESC_TRANSFER_DATA; - desc->reserved = 0; - desc->len = MAX_64KB_PAGE; - desc->addr_lo = (dma_buff & UINT_MAX) + (CDMMC_DMA_MAX_BUFFER_SIZE * i); -#if CONFIG_DMA_ADDR_T_64BIT == 1 - desc->addr_hi = (dma_buff >> 32) & 0xffffffff; -#endif - size -= CDMMC_DMA_MAX_BUFFER_SIZE; - desc++; - i++; - } + cdns_desc_data = (struct cdns_idmac_desc *)cdns_params.desc_base; + sd_host_adma_prepare(cdns_desc_data, dma_buff, size); - desc->attr = ADMA_DESC_ATTR_VALID | ADMA_DESC_TRANSFER_DATA | - ADMA_DESC_ATTR_END; - desc->reserved = 0; - desc->len = size; -#if CONFIG_DMA_ADDR_T_64BIT == 1 - desc->addr_lo = (dma_buff & UINT_MAX) + (CDMMC_DMA_MAX_BUFFER_SIZE * i); - desc->addr_hi = (dma_buff >> 32) & UINT_MAX; -#else - desc->addr_lo = (dma_buff & UINT_MAX); -#endif - - mmio_write_32(MMC_REG_BASE + SDHC_CDNS_SRS22, (uint32_t)desc_base); - mmio_write_32(MMC_REG_BASE + SDHC_CDNS_SRS23, (uint32_t)(desc_base >> 32)); - flush_dcache_range(cdns_params.desc_base, - desc_cnt * CDMMC_DMA_MAX_BUFFER_SIZE); - - mmio_write_32(MMC_REG_BASE + SDHC_CDNS_SRS01, - ((512 << BLOCK_SIZE) | ((size/512) << BLK_COUNT_CT) | SDMA_BUF)); return 0; } -static void cdns_host_set_clk(int clk) +void cdns_host_set_clk(uint32_t clk) { uint32_t ret = 0; uint32_t sdclkfsval = 0; - uint32_t dtcvval = DTCVVAL_DEFAULT_VAL; + uint32_t dtcvval = 0xE; - sdclkfsval = (cdns_params.clk_rate / 2000) / clk; - mmio_write_32(MMC_REG_BASE + SDHC_CDNS_SRS11, 0); - mmio_write_32(MMC_REG_BASE + SDHC_CDNS_SRS11, (dtcvval << SDMMC_CDN_DTCV) | - (sdclkfsval << SDMMC_CDN_SDCLKFS) | (1 << SDMMC_CDN_ICE)); + sdclkfsval = (SD_HOST_CLK / 2) / clk; + mmio_write_32(cdns_params.reg_base + SDHC_CDNS_SRS11, 0); + mmio_write_32(cdns_params.reg_base + SDHC_CDNS_SRS11, + (dtcvval << SDMMC_CDN_DTCV) | (sdclkfsval << SDMMC_CDN_SDCLKFS) | + (1 << SDMMC_CDN_ICE)); - ret = cdns_wait_ics(5000, MMC_REG_BASE + SDHC_CDNS_SRS11); - if (ret != 0U) { - ERROR("Waiting SDMMC_CDN_ICS timeout"); + ret = cdns_wait_ics(5000, cdns_params.reg_base + SDHC_CDNS_SRS11); + if (ret != 0) { + ERROR("Waiting ICS timeout"); } - /* Enable DLL reset */ - mmio_write_32(MMC_REG_BASE + SDHC_CDNS_HRS09, mmio_read_32(MMC_REG_BASE + SDHC_CDNS_HRS09) & - ~SDHC_DLL_RESET_MASK); + mmio_write_32(cdns_params.reg_base + SDHC_CDNS_HRS09, + mmio_read_32(cdns_params.reg_base + SDHC_CDNS_HRS09) & ~0x00000001); /* Set extended_wr_mode */ - mmio_write_32(MMC_REG_BASE + SDHC_CDNS_HRS09, (mmio_read_32(MMC_REG_BASE + SDHC_CDNS_HRS09) - & SDHC_EXTENDED_WR_MODE_MASK) | (1 << EXTENDED_WR_MODE)); + mmio_write_32(cdns_params.reg_base + SDHC_CDNS_HRS09, + (mmio_read_32(cdns_params.reg_base + SDHC_CDNS_HRS09) & 0xFFFFFFF7) | + (1 << EXTENDED_WR_MODE)); /* Release DLL reset */ - mmio_write_32(MMC_REG_BASE + SDHC_CDNS_HRS09, mmio_read_32(MMC_REG_BASE - + SDHC_CDNS_HRS09) | 1); - mmio_write_32(MMC_REG_BASE + SDHC_CDNS_HRS09, mmio_read_32(MMC_REG_BASE - + SDHC_CDNS_HRS09) | (3 << RDCMD_EN)); + mmio_write_32(cdns_params.reg_base + SDHC_CDNS_HRS09, + mmio_read_32(cdns_params.reg_base + SDHC_CDNS_HRS09) | PHY_SW_RESET_EN); + mmio_write_32(cdns_params.reg_base + SDHC_CDNS_HRS09, + mmio_read_32(cdns_params.reg_base + SDHC_CDNS_HRS09) | RDCMD_EN); do { - mmio_read_32(MMC_REG_BASE + SDHC_CDNS_HRS09); - } while (~mmio_read_32(MMC_REG_BASE + SDHC_CDNS_HRS09) & (1 << 1)); - - mmio_write_32(MMC_REG_BASE + SDHC_CDNS_SRS11, (dtcvval << SDMMC_CDN_DTCV) | - (sdclkfsval << SDMMC_CDN_SDCLKFS) | (1 << SDMMC_CDN_ICE) | (1 << SDMMC_CDN_SDCE)); - mmio_write_32(MMC_REG_BASE + SDHC_CDNS_SRS13, UINT_MAX); + mmio_read_32(cdns_params.reg_base + SDHC_CDNS_HRS09); + } while (~mmio_read_32(cdns_params.reg_base + SDHC_CDNS_HRS09) & + (PHY_INIT_COMPLETE_BIT)); + + mmio_write_32(cdns_params.reg_base + SDHC_CDNS_SRS11, (dtcvval << SDMMC_CDN_DTCV) | + (sdclkfsval << SDMMC_CDN_SDCLKFS) | (1 << SDMMC_CDN_ICE) | + (1 << SDMMC_CDN_SDCE)); + mmio_write_32(cdns_params.reg_base + SDHC_CDNS_SRS13, 0xFFFFFFFF); } int cdns_set_ios(unsigned int clk, unsigned int width) { + uint32_t _status = 0; + _status = mmio_read_32(cdns_params.reg_base + SDHC_CDNS_SRS10); switch (width) { case MMC_BUS_WIDTH_1: - mmio_write_32((MMC_REG_BASE + SDHC_CDNS_SRS10), LEDC_OFF); + _status &= ~(BIT4); break; + case MMC_BUS_WIDTH_4: - mmio_write_32((MMC_REG_BASE + SDHC_CDNS_SRS10), DTW_4BIT); + _status |= BIT4; break; + case MMC_BUS_WIDTH_8: - mmio_write_32((MMC_REG_BASE + SDHC_CDNS_SRS10), EDTW_8BIT); + _status |= BIT8; break; + default: assert(0); break; } + mmio_write_32((cdns_params.reg_base + SDHC_CDNS_SRS10), _status); cdns_host_set_clk(clk); return 0; @@ -388,6 +334,7 @@ int cdns_sdmmc_write_sd_host_reg(uint32_t addr, uint32_t data) value |= data; mmio_write_32(addr, value); value = mmio_read_32(addr); + if (value != data) { ERROR("SD host address is not set properly\n"); return -ENXIO; @@ -396,429 +343,403 @@ int cdns_sdmmc_write_sd_host_reg(uint32_t addr, uint32_t data) return 0; } -int cdns_write(int lba, uintptr_t buf, size_t size) -{ - return 0; -} -static int cdns_init_hrs_io(struct cdns_sdmmc_combo_phy *combo_phy_reg, - struct cdns_sdmmc_sdhc *sdhc_reg) + +void sd_host_oper_mode(enum sd_opr_modes opr_mode) { - uint32_t value = 0; - int ret = 0; - /* program HRS09, register 42 */ - value = (SDHC_RDDATA_EN(sdhc_reg->sdhc_rddata_en)) - | (SDHC_RDCMD_EN(sdhc_reg->sdhc_rdcmd_en)) - | (SDHC_EXTENDED_WR_MODE(sdhc_reg->sdhc_extended_wr_mode)) - | (SDHC_EXTENDED_RD_MODE(sdhc_reg->sdhc_extended_rd_mode)); - ret = cdns_sdmmc_write_sd_host_reg(MMC_REG_BASE + SDHC_CDNS_HRS09, value); - if (ret != 0) { - ERROR("Program HRS09 failed"); - return ret; - } + uint32_t reg = 0; - /* program HRS10, register 43 */ - value = (SDHC_HCSDCLKADJ(sdhc_reg->sdhc_hcsdclkadj)); - ret = cdns_sdmmc_write_sd_host_reg(MMC_REG_BASE + SDHC_CDNS_HRS10, value); - if (ret != 0) { - ERROR("Program HRS10 failed"); - return ret; - } + switch (opr_mode) { + case SD_HOST_OPR_MODE_HV4E_0_SDMA_32: + reg = mmio_read_32(cdns_params.reg_base + SDHC_CDNS_SRS10); + reg &= ~(DMA_SEL_BIT); + mmio_write_32(cdns_params.reg_base + SDHC_CDNS_SRS10, reg); + reg = mmio_read_32(cdns_params.reg_base + SDHC_CDNS_SRS15); + reg &= ~(HV4E | BIT_AD_64); + mmio_write_32(cdns_params.reg_base + SDHC_CDNS_SRS15, reg); + break; - /* program HRS16, register 48 */ - value = (SDHC_WRDATA1_SDCLK_DLY(sdhc_reg->sdhc_wrdata1_sdclk_dly)) - | (SDHC_WRDATA0_SDCLK_DLY(sdhc_reg->sdhc_wrdata0_sdclk_dly)) - | (SDHC_WRCMD1_SDCLK_DLY(sdhc_reg->sdhc_wrcmd1_sdclk_dly)) - | (SDHC_WRCMD0_SDCLK_DLY(sdhc_reg->sdhc_wrcmd0_sdclk_dly)) - | (SDHC_WRDATA1_DLY(sdhc_reg->sdhc_wrdata1_dly)) - | (SDHC_WRDATA0_DLY(sdhc_reg->sdhc_wrdata0_dly)) - | (SDHC_WRCMD1_DLY(sdhc_reg->sdhc_wrcmd1_dly)) - | (SDHC_WRCMD0_DLY(sdhc_reg->sdhc_wrcmd0_dly)); - ret = cdns_sdmmc_write_sd_host_reg(MMC_REG_BASE + SDHC_CDNS_HRS16, value); - if (ret != 0) { - ERROR("Program HRS16 failed"); - return ret; - } + case SD_HOST_OPR_MODE_HV4E_1_SDMA_32: + reg = mmio_read_32(cdns_params.reg_base + SDHC_CDNS_SRS10); + reg &= ~(DMA_SEL_BIT); + mmio_write_32(cdns_params.reg_base + SDHC_CDNS_SRS10, reg); + reg = mmio_read_32(cdns_params.reg_base + SDHC_CDNS_SRS15); + reg &= ~(HV4E | BIT_AD_64); + reg |= (HV4E); + mmio_write_32(cdns_params.reg_base + SDHC_CDNS_SRS15, reg); + break; - /* program HRS07, register 40 */ - value = (SDHC_RW_COMPENSATE(sdhc_reg->sdhc_rw_compensate)) - | (SDHC_IDELAY_VAL(sdhc_reg->sdhc_idelay_val)); - ret = cdns_sdmmc_write_sd_host_reg(MMC_REG_BASE + SDHC_CDNS_HRS07, value); - if (ret != 0) { - ERROR("Program HRS07 failed"); - return ret; - } + case SD_HOST_OPR_MODE_HV4E_1_SDMA_64: + reg = mmio_read_32(cdns_params.reg_base + SDHC_CDNS_SRS10); + reg &= ~(DMA_SEL_BIT); + mmio_write_32(cdns_params.reg_base + SDHC_CDNS_SRS10, reg); + reg = mmio_read_32(cdns_params.reg_base + SDHC_CDNS_SRS15); + reg |= (HV4E | BIT_AD_64); + mmio_write_32(cdns_params.reg_base + SDHC_CDNS_SRS15, reg); + break; + + case SD_HOST_OPR_MODE_HV4E_0_ADMA_32: + reg = mmio_read_32(cdns_params.reg_base + SDHC_CDNS_SRS10); + reg &= ~(DMA_SEL_BIT); + reg |= DMA_SEL_BIT_2; + mmio_write_32(cdns_params.reg_base + SDHC_CDNS_SRS10, reg); + reg = mmio_read_32(cdns_params.reg_base + SDHC_CDNS_SRS15); + reg &= ~(HV4E | BIT_AD_64); + mmio_write_32(cdns_params.reg_base + SDHC_CDNS_SRS15, reg); + break; - return ret; + case SD_HOST_OPR_MODE_HV4E_0_ADMA_64: + reg = mmio_read_32(cdns_params.reg_base + SDHC_CDNS_SRS10); + reg &= ~(DMA_SEL_BIT); + reg |= DMA_SEL_BIT_3; + mmio_write_32(cdns_params.reg_base + SDHC_CDNS_SRS10, reg); + reg = mmio_read_32(cdns_params.reg_base + SDHC_CDNS_SRS15); + reg &= ~(HV4E | BIT_AD_64); + reg |= BIT_AD_64; + mmio_write_32(cdns_params.reg_base + SDHC_CDNS_SRS15, reg); + break; + + case SD_HOST_OPR_MODE_HV4E_1_ADMA_32: + reg = mmio_read_32(cdns_params.reg_base + SDHC_CDNS_SRS10); + reg &= ~(DMA_SEL_BIT); + reg |= DMA_SEL_BIT_2; + mmio_write_32(cdns_params.reg_base + SDHC_CDNS_SRS10, reg); + reg = mmio_read_32(cdns_params.reg_base + SDHC_CDNS_SRS15); + reg &= ~(HV4E | BIT_AD_64); + reg |= HV4E; + mmio_write_32(cdns_params.reg_base + SDHC_CDNS_SRS15, reg); + break; + + case SD_HOST_OPR_MODE_HV4E_1_ADMA_64: + reg = mmio_read_32(cdns_params.reg_base + SDHC_CDNS_SRS10); + reg &= ~(DMA_SEL_BIT); + reg |= DMA_SEL_BIT_2; + mmio_write_32(cdns_params.reg_base + SDHC_CDNS_SRS10, reg); + reg = mmio_read_32(cdns_params.reg_base + SDHC_CDNS_SRS15); + reg |= (HV4E | BIT_AD_64); + mmio_write_32(cdns_params.reg_base + SDHC_CDNS_SRS15, reg); + break; + } } -static int cdns_hc_set_clk(struct cdns_sdmmc_params *cdn_sdmmc_dev_mode_params) +void card_reset(bool power_enable) { - uint32_t ret = 0; - uint32_t dtcvval, sdclkfsval; - - dtcvval = DTC_VAL; - sdclkfsval = 0; - - if ((cdn_sdmmc_dev_mode_params->cdn_sdmmc_dev_mode == SD_DS) || - (cdn_sdmmc_dev_mode_params->cdn_sdmmc_dev_mode == SD_UHS_SDR12) || - (cdn_sdmmc_dev_mode_params->cdn_sdmmc_dev_mode == EMMC_SDR_BC)) { - sdclkfsval = 4; - } else if ((cdn_sdmmc_dev_mode_params->cdn_sdmmc_dev_mode == SD_HS) || - (cdn_sdmmc_dev_mode_params->cdn_sdmmc_dev_mode == SD_UHS_SDR25) || - (cdn_sdmmc_dev_mode_params->cdn_sdmmc_dev_mode == SD_UHS_DDR50) || - (cdn_sdmmc_dev_mode_params->cdn_sdmmc_dev_mode == EMMC_SDR)) { - sdclkfsval = 2; - } else if ((cdn_sdmmc_dev_mode_params->cdn_sdmmc_dev_mode == SD_UHS_SDR50) || - (cdn_sdmmc_dev_mode_params->cdn_sdmmc_dev_mode == EMMC_DDR) || - (cdn_sdmmc_dev_mode_params->cdn_sdmmc_dev_mode == EMMC_HS400) || - (cdn_sdmmc_dev_mode_params->cdn_sdmmc_dev_mode == EMMC_HS400es)) { - sdclkfsval = 1; - } else if ((cdn_sdmmc_dev_mode_params->cdn_sdmmc_dev_mode == SD_UHS_SDR104) || - (cdn_sdmmc_dev_mode_params->cdn_sdmmc_dev_mode == EMMC_HS200)) { - sdclkfsval = 0; - } + uint32_t reg_value = 0; - mmio_write_32(MMC_REG_BASE + SDHC_CDNS_SRS11, 0); - mmio_write_32(MMC_REG_BASE + SDHC_CDNS_SRS11, (dtcvval << SDMMC_CDN_DTCV) | - (sdclkfsval << SDMMC_CDN_SDCLKFS) | (1 << SDMMC_CDN_ICE)); - ret = cdns_wait_ics(5000, MMC_REG_BASE + SDHC_CDNS_SRS11); - if (ret != 0U) { - ERROR("Waiting SDMMC_CDN_ICS timeout"); - return ret; + /* Reading SRS10 value before writing */ + reg_value = mmio_read_32(cdns_params.reg_base + SDHC_CDNS_SRS10); + + if (power_enable == true) { + reg_value &= ~((7 << SDMMC_CDN_BVS) | (1 << SDMMC_CDN_BP)); + reg_value = ((1 << SDMMC_CDN_BVS) | (1 << SDMMC_CDN_BP)); + } else { + reg_value &= ~((7 << SDMMC_CDN_BVS) | (1 << SDMMC_CDN_BP)); } + mmio_write_32(cdns_params.reg_base + SDHC_CDNS_SRS10, reg_value); +} - /* Enable DLL reset */ - mmio_write_32((MMC_REG_BASE + SDHC_CDNS_HRS09), mmio_read_32(MMC_REG_BASE - + SDHC_CDNS_HRS09) & ~SDHC_DLL_RESET_MASK); - /* Set extended_wr_mode */ - mmio_write_32((MMC_REG_BASE + SDHC_CDNS_HRS09), - (mmio_read_32(MMC_REG_BASE + SDHC_CDNS_HRS09) & SDHC_EXTENDED_WR_MODE_MASK) | - (1 << EXTENDED_WR_MODE)); - /* Release DLL reset */ - mmio_write_32(MMC_REG_BASE + SDHC_CDNS_HRS09, mmio_read_32(MMC_REG_BASE - + SDHC_CDNS_HRS09) | 1); - mmio_write_32(MMC_REG_BASE + SDHC_CDNS_HRS09, mmio_read_32(MMC_REG_BASE - + SDHC_CDNS_HRS09) | (3 << RDCMD_EN)); - do { - mmio_read_32(MMC_REG_BASE + SDHC_CDNS_HRS09); - } while (~mmio_read_32(MMC_REG_BASE + SDHC_CDNS_HRS09) & (1 << 1)); +void high_speed_enable(bool mode) +{ - mmio_write_32(MMC_REG_BASE + SDHC_CDNS_SRS11, (dtcvval << SDMMC_CDN_DTCV) | - (sdclkfsval << SDMMC_CDN_SDCLKFS) | (1 << SDMMC_CDN_ICE) | (1 << SDMMC_CDN_SDCE)); + uint32_t reg_value = 0; + /* Reading SRS10 value before writing */ + reg_value = mmio_read_32(cdns_params.reg_base + SDHC_CDNS_SRS10); - mmio_write_32(MMC_REG_BASE + SDHC_CDNS_SRS13, UINT_MAX); - return 0; + if (mode == true) { + reg_value |= HS_EN; + } else { + reg_value &= ~HS_EN; + } + + mmio_write_32(cdns_params.reg_base + SDHC_CDNS_SRS10, reg_value); } int cdns_reset(void) { - uint32_t data = 0; + volatile uint32_t data = 0; uint32_t count = 0; - uint32_t value = 0; - - value = mmio_read_32(MMC_REG_BASE + SDHC_CDNS_SRS11); - value &= ~(0xFFFF); - value |= 0x0; - mmio_write_32(MMC_REG_BASE + SDHC_CDNS_SRS11, value); - udelay(500); /* Software reset */ - mmio_write_32(MMC_REG_BASE + SDHC_CDNS_HRS00, 1); + mmio_write_32(cdns_params.reg_base + SDHC_CDNS_SRS11, SRS11_SRFA); /* Wait status command response ready */ do { - data = mmio_read_32(MMC_REG_BASE + SDHC_CDNS_HRS00); + data = mmio_read_32(cdns_params.reg_base + SDHC_CDNS_HRS00); count++; - if (count >= 5000) { + if (count >= CDNS_TIMEOUT) { return -ETIMEDOUT; } - /* Wait for HRS00.SWR */ - } while ((data & 1) == 1); - - /* Step 1, switch on DLL_RESET */ - value = mmio_read_32(MMC_REG_BASE + SDHC_CDNS_HRS09); - value &= ~SDHC_PHY_SW_RESET; - mmio_write_32(MMC_REG_BASE + SDHC_CDNS_HRS09, value); + /* Wait for SRS11 */ + } while (((SRS11_SRFA_CHK(data)) & 1) == 1); return 0; } +void sdmmc_host_init(bool uhs2_enable) +{ + uint32_t timeout; + + /* SRS11 - Host Control default value set */ + mmio_write_32(cdns_params.reg_base + SDHC_CDNS_SRS11, 0x0); + + /* Waiting for detect card */ + timeout = TIMEOUT; + do { + udelay(250); + if (--timeout <= 0) { + NOTICE(" SDHC Card Detecion failed!!!\n"); + panic(); + } + } while (((mmio_read_32(cdns_params.reg_base + SDHC_CDNS_SRS09) & CHECK_CARD) == 0)); + + /* UHS2 Host setting */ + if (uhs2_enable == true) { + /** need to implement*/ + } + + /* Card reset */ + + card_reset(1); + udelay(2500); + card_reset(0); + udelay(2500); + card_reset(1); + udelay(2500); + + /* Enable Interrupt Flags*/ + mmio_write_32((cdns_params.reg_base + SDHC_CDNS_SRS13), ~0); + high_speed_enable(true); +} + int cdns_sd_host_init(struct cdns_sdmmc_combo_phy *mmc_combo_phy_reg, -struct cdns_sdmmc_sdhc *mmc_sdhc_reg) + struct cdns_sdmmc_sdhc *mmc_sdhc_reg) { int ret = 0; ret = cdns_reset(); - if (ret != 0) { + if (ret != 0U) { ERROR("Program phy reg init failed"); return ret; } ret = cdns_program_phy_reg(&sdmmc_combo_phy_reg, &sdmmc_sdhc_reg); - if (ret != 0) { + if (ret != 0U) { ERROR("Program phy reg init failed"); return ret; } + sdmmc_host_init(0); + cdns_host_set_clk(100000); - ret = cdns_init_hrs_io(&sdmmc_combo_phy_reg, &sdmmc_sdhc_reg); - if (ret != 0) { - ERROR("Program init for HRS reg is failed"); - return ret; - } - - ret = cdns_sd_card_detect(); - if (ret != 0) { - ERROR("SD card does not detect"); - return ret; - } - - ret = cdns_vol_reset(); - if (ret != 0) { - ERROR("eMMC card reset failed"); - return ret; - } - - ret = cdns_hc_set_clk(&cdns_params); - if (ret != 0) { - ERROR("hc set clk failed"); - return ret; - } + sd_host_oper_mode(SD_HOST_OPR_MODE_HV4E_0_ADMA_64); return 0; } -void cdns_srs10_value_toggle(uint8_t write_val, uint8_t prev_val) -{ - uint32_t data_op = 0U; - - data_op = mmio_read_32(MMC_REG_BASE + SDHC_CDNS_SRS10); - mmio_write_32(MMC_REG_BASE + SDHC_CDNS_SRS10, (data_op & (prev_val << 0))); - mmio_read_32(MMC_REG_BASE + SDHC_CDNS_SRS10); - mmio_write_32(MMC_REG_BASE + SDHC_CDNS_SRS10, data_op | (write_val << 0)); -} - -void cdns_srs11_srs15_config(uint32_t srs11_val, uint32_t srs15_val) -{ - uint32_t data = 0U; - - data = mmio_read_32(MMC_REG_BASE + SDHC_CDNS_SRS11); - mmio_write_32(MMC_REG_BASE + SDHC_CDNS_SRS11, (data | srs11_val)); - data = mmio_read_32(MMC_REG_BASE + SDHC_CDNS_SRS15); - mmio_write_32(MMC_REG_BASE + SDHC_CDNS_SRS15, (data | srs15_val)); -} - int cdns_send_cmd(struct mmc_cmd *cmd) { - uint32_t op = 0, ret = 0; - uint8_t write_value = 0, prev_val = 0; - uint32_t value; - int32_t timeout; - uint32_t cmd_indx; - uint32_t status = 0, srs15_val = 0, srs11_val = 0; + uint32_t cmd_flags = 0; + uint32_t timeout = 0; uint32_t status_check = 0; + uint32_t mode = 0; + uint32_t status; assert(cmd); - cmd_indx = (cmd->cmd_idx) << COM_IDX; - - if (data_cmd) { - switch (cmd->cmd_idx) { - case SD_SWITCH: - op = DATA_PRESENT; - write_value = ADMA2_32 | DT_WIDTH; - prev_val = ADMA2_32 | DT_WIDTH; - cdns_srs10_value_toggle(write_value, prev_val); - srs11_val = READ_CLK | SDMMC_CDN_ICE | SDMMC_CDN_ICS | SDMMC_CDN_SDCE; - srs15_val = BIT_AD_64 | HV4E | V18SE; - cdns_srs11_srs15_config(srs11_val, srs15_val); - break; - - case SD_WRITE_SINGLE_BLOCK: - case SD_READ_SINGLE_BLOCK: - op = DATA_PRESENT; - write_value = ADMA2_32 | HS_EN | DT_WIDTH | LEDC; - prev_val = ADMA2_32 | HS_EN | DT_WIDTH; - cdns_srs10_value_toggle(write_value, prev_val); - srs15_val = PVE | BIT_AD_64 | HV4E | SDR104_MODE | V18SE; - srs11_val = READ_CLK | SDMMC_CDN_ICE | SDMMC_CDN_ICS | SDMMC_CDN_SDCE; - cdns_srs11_srs15_config(srs11_val, srs15_val); - mmio_write_32(MMC_REG_BASE + SDHC_CDNS_SRS00, SAAR); - break; - - case SD_WRITE_MULTIPLE_BLOCK: - case SD_READ_MULTIPLE_BLOCK: - op = DATA_PRESENT | AUTO_CMD_EN | MULTI_BLK_READ; - write_value = ADMA2_32 | HS_EN | DT_WIDTH | LEDC; - prev_val = ADMA2_32 | HS_EN | DT_WIDTH; - cdns_srs10_value_toggle(write_value, prev_val); - srs15_val = PVE | BIT_AD_64 | HV4E | SDR104_MODE | V18SE; - srs11_val = READ_CLK | SDMMC_CDN_ICE | SDMMC_CDN_ICS | SDMMC_CDN_SDCE; - cdns_srs11_srs15_config(srs11_val, srs15_val); - mmio_write_32(MMC_REG_BASE + SDHC_CDNS_SRS00, SAAR); - break; - - case SD_APP_SEND_SCR: - op = DATA_PRESENT; - write_value = ADMA2_32 | LEDC; - prev_val = LEDC; - cdns_srs10_value_toggle(write_value, prev_val); - srs15_val = BIT_AD_64 | HV4E | V18SE; - srs11_val = READ_CLK | SDMMC_CDN_ICE | SDMMC_CDN_ICS | SDMMC_CDN_SDCE; - cdns_srs11_srs15_config(srs11_val, srs15_val); - break; - - case SD_SEND_IF_COND: - op = DATA_PRESENT | CMD_IDX_CHK_ENABLE; - write_value = LEDC; - prev_val = 0x0; - cdns_srs10_value_toggle(write_value, prev_val); - srs15_val = HV4E; - srs11_val = READ_CLK | SDMMC_CDN_ICE | SDMMC_CDN_ICS | SDMMC_CDN_SDCE; - cdns_srs11_srs15_config(srs11_val, srs15_val); - break; - - default: - write_value = LEDC; - prev_val = 0x0; - cdns_srs10_value_toggle(write_value, prev_val); - op = 0; - break; - } - } else { - switch (cmd->cmd_idx) { - case SD_GO_IDLE_STATE: - write_value = LEDC; - prev_val = 0x0; - cdns_srs10_value_toggle(write_value, prev_val); - srs15_val = HV4E; - srs11_val = SDMMC_CDN_ICE | SDMMC_CDN_ICS | SDMMC_CDN_SDCE; - cdns_srs11_srs15_config(srs11_val, srs15_val); - break; - - case SD_ALL_SEND_CID: - write_value = LEDC; - prev_val = 0x0; - cdns_srs10_value_toggle(write_value, prev_val); - srs15_val = HV4E | V18SE; - srs11_val = SDMMC_CDN_ICE | SDMMC_CDN_ICS | SDMMC_CDN_SDCE; - cdns_srs11_srs15_config(srs11_val, srs15_val); - break; - - case SD_SEND_IF_COND: - op = CMD_IDX_CHK_ENABLE; - write_value = LEDC; - prev_val = 0x0; - cdns_srs10_value_toggle(write_value, prev_val); - srs15_val = HV4E; - srs11_val = READ_CLK | SDMMC_CDN_ICE | SDMMC_CDN_ICS | SDMMC_CDN_SDCE; - cdns_srs11_srs15_config(srs11_val, srs15_val); - break; - - case SD_STOP_TRANSMISSION: - op = CMD_STOP_ABORT_CMD; - break; - - case SD_SEND_STATUS: - break; - - case 1: - cmd->cmd_arg = 0; - break; - - case SD_SELECT_CARD: - op = MULTI_BLK_READ; - break; - - case SD_APP_CMD: - default: - write_value = LEDC; - prev_val = 0x0; - cdns_srs10_value_toggle(write_value, prev_val); - op = 0; - break; - } - } - - switch (cmd->resp_type) { - case MMC_RESPONSE_NONE: - op |= CMD_READ | MULTI_BLK_READ | DMA_ENABLED | BLK_CNT_EN; - break; - - case MMC_RESPONSE_R2: - op |= CMD_READ | MULTI_BLK_READ | DMA_ENABLED | BLK_CNT_EN | - RES_TYPE_SEL_136 | CMD_CHECK_RESP_CRC; - break; - case MMC_RESPONSE_R3: - op |= CMD_READ | MULTI_BLK_READ | DMA_ENABLED | BLK_CNT_EN | - RES_TYPE_SEL_48; - break; + cmd_flags = CDNS_HOST_CMD_INHIBIT | CDNS_HOST_DATA_INHIBIT; - case MMC_RESPONSE_R1: - if ((cmd->cmd_idx == SD_WRITE_SINGLE_BLOCK) || (cmd->cmd_idx - == SD_WRITE_MULTIPLE_BLOCK)) { - op |= DMA_ENABLED | BLK_CNT_EN | RES_TYPE_SEL_48 - | CMD_CHECK_RESP_CRC | CMD_IDX_CHK_ENABLE; - } else { - op |= DMA_ENABLED | BLK_CNT_EN | CMD_READ | RES_TYPE_SEL_48 - | CMD_CHECK_RESP_CRC | CMD_IDX_CHK_ENABLE; - } - break; - - default: - op |= DMA_ENABLED | BLK_CNT_EN | CMD_READ | MULTI_BLK_READ | - RES_TYPE_SEL_48 | CMD_CHECK_RESP_CRC | CMD_IDX_CHK_ENABLE; - break; + if ((cmd->cmd_idx == SD_STOP_TRANSMISSION) && (!data_cmd)) { + cmd_flags &= ~CDNS_HOST_DATA_INHIBIT; } timeout = TIMEOUT; do { udelay(100); - ret = cdns_busy(); if (--timeout <= 0) { udelay(50); + NOTICE("Timeout occur data and cmd line %x\n", + mmio_read_32(cdns_params.reg_base + SDHC_CDNS_SRS09)); panic(); } - } while (ret); + } while ((mmio_read_32(cdns_params.reg_base + SDHC_CDNS_SRS09) & (cmd_flags))); + + mmio_write_32(cdns_params.reg_base + SDHC_CDNS_SRS12, 0xFFFFFFFF); + cmd_flags = 0; + cmd_flags = (cmd->cmd_idx) << COM_IDX; + + if ((cmd->resp_type & MMC_RSP_136) != 0) { + cmd_flags |= RES_TYPE_SEL_136; + } else if (((cmd->resp_type & MMC_RSP_48) != 0) && + ((cmd->resp_type & MMC_RSP_BUSY) != 0)) { + cmd_flags |= RES_TYPE_SEL_48_B; + } else if ((cmd->resp_type & MMC_RSP_48) != 0) { + cmd_flags |= RES_TYPE_SEL_48; + } else { + cmd_flags &= ~RES_TYPE_SEL_NO; + } - mmio_write_32(MMC_REG_BASE + SDHC_CDNS_SRS12, UINT_MAX); + if ((cmd->resp_type & MMC_RSP_CRC) != 0) { + cmd_flags |= CMD_CHECK_RESP_CRC; + } - mmio_write_32(MMC_REG_BASE + SDHC_CDNS_SRS02, cmd->cmd_arg); - mmio_write_32(MMC_REG_BASE + SDHC_CDNS_SRS14, 0x00000000); - if (cmd_indx == 1) - mmio_write_32(MMC_REG_BASE + SDHC_CDNS_SRS03, SDHC_CDNS_SRS03_VALUE); - else - mmio_write_32(MMC_REG_BASE + SDHC_CDNS_SRS03, op | cmd_indx); + if ((cmd->resp_type & MMC_RSP_CMD_IDX) != 0) { + cmd_flags |= CMD_IDX_CHK_ENABLE; + } - timeout = TIMEOUT; - do { - udelay(500); - value = mmio_read_32(MMC_REG_BASE + SDHC_CDNS_SRS12); - } while (((value & (INT_CMD_DONE | ERROR_INT)) == 0) && (timeout-- > 0)); + if ((cmd->cmd_idx == MMC_ACMD(51)) || (cmd->cmd_idx == MMC_CMD(17)) || + (cmd->cmd_idx == MMC_CMD(18)) || (cmd->cmd_idx == MMC_CMD(24)) || + (cmd->cmd_idx == MMC_CMD(25))) { + mmio_write_8((cdns_params.reg_base + DTCV_OFFSET), DTCV_VAL); + cmd_flags |= DATA_PRESENT; + mode |= BLK_CNT_EN; + + mode |= (DMA_ENABLED); + if ((cmd->cmd_idx == SD_WRITE_MULTIPLE_BLOCK) || + (cmd->cmd_idx == SD_READ_MULTIPLE_BLOCK)) { + mode |= (MULTI_BLK_READ); + } else { + mode &= ~(MULTI_BLK_READ); + } + if ((cmd->cmd_idx == SD_WRITE_MULTIPLE_BLOCK) || + (cmd->cmd_idx == SD_WRITE_SINGLE_BLOCK)) { + mode &= ~CMD_READ; + } else { + mode |= CMD_READ; + } + mmio_write_16(cdns_params.reg_base + SDHC_CDNS_SRS03, mode); + + } else { + mmio_write_8((cdns_params.reg_base + DTCV_OFFSET), DTCV_VAL); + } + + mmio_write_32(cdns_params.reg_base + SDHC_CDNS_SRS02, cmd->cmd_arg); + mmio_write_16((cdns_params.reg_base + CICE_OFFSET), + SDHCI_MAKE_CMD(cmd->cmd_idx, cmd_flags)); timeout = TIMEOUT; - if (data_cmd) { - data_cmd = false; - do { - udelay(250); - } while (((value & TRAN_COMP) == 0) && (timeout-- > 0)); - } + do { + udelay(CDNS_TIMEOUT); + status = mmio_read_32(cdns_params.reg_base + SDHC_CDNS_SRS12); + } while (((status & (INT_CMD_DONE | ERROR_INT)) == 0) && (timeout-- > 0)); - status_check = value & SRS12_ERR_MASK; + mmio_write_32(cdns_params.reg_base + SDHC_CDNS_SRS12, (SRS_12_CC_EN)); + status_check = mmio_read_32(cdns_params.reg_base + SDHC_CDNS_SRS12) & 0xffff8000; if (status_check != 0U) { - ERROR("SD host controller send command failed, SRS12 = %x", status); + timeout = TIMEOUT; + ERROR("SD host controller send command failed, SRS12 = %x", status_check); return -1; } - if ((op & RES_TYPE_SEL_48) || (op & RES_TYPE_SEL_136)) { - cmd->resp_data[0] = mmio_read_32(MMC_REG_BASE + SDHC_CDNS_SRS04); - if (op & RES_TYPE_SEL_136) { - cmd->resp_data[1] = mmio_read_32(MMC_REG_BASE + SDHC_CDNS_SRS05); - cmd->resp_data[2] = mmio_read_32(MMC_REG_BASE + SDHC_CDNS_SRS06); - cmd->resp_data[3] = mmio_read_32(MMC_REG_BASE + SDHC_CDNS_SRS07); + if (!((cmd_flags & RES_TYPE_SEL_NO) == 0)) { + cmd->resp_data[0] = mmio_read_32(cdns_params.reg_base + SDHC_CDNS_SRS04); + if ((cmd_flags & RES_TYPE_SEL_NO) == RES_TYPE_SEL_136) { + cmd->resp_data[1] = mmio_read_32(cdns_params.reg_base + SDHC_CDNS_SRS05); + cmd->resp_data[2] = mmio_read_32(cdns_params.reg_base + SDHC_CDNS_SRS06); + cmd->resp_data[3] = mmio_read_32(cdns_params.reg_base + SDHC_CDNS_SRS07); + /* 136-bit: RTS=01b, Response field R[127:8] - RESP3[23:0], + * RESP2[31:0], RESP1[31:0], RESP0[31:0] + * Subsystem expects 128 bits response but cadence SDHC sends + * 120 bits response from R[127:8]. Bits manupulation to address + * the correct responses for the 136 bit response type. + */ + cmd->resp_data[3] = ((cmd->resp_data[3] << 8) | + ((cmd->resp_data[2] >> 24) & + CDNS_CSD_BYTE_MASK)); + cmd->resp_data[2] = ((cmd->resp_data[2] << 8) | + ((cmd->resp_data[1] >> 24) & + CDNS_CSD_BYTE_MASK)); + cmd->resp_data[1] = ((cmd->resp_data[1] << 8) | + ((cmd->resp_data[0] >> 24) & + CDNS_CSD_BYTE_MASK)); + cmd->resp_data[0] = (cmd->resp_data[0] << 8); } } + mmio_write_32(cdns_params.reg_base + SDHC_CDNS_SRS12, (SRS_12_CC_EN)); + return 0; } + +void sd_host_adma_prepare(struct cdns_idmac_desc *desc_ptr, uint64_t buf, + size_t size) +{ + uint32_t full_desc_cnt = 0; + uint32_t non_full_desc_cnt = 0; + uint64_t desc_address; + uint32_t block_count; + uint32_t transfer_block_size; + + full_desc_cnt = (size / PAGE_BUFFER_LEN); + non_full_desc_cnt = (size % PAGE_BUFFER_LEN); + for (int i = 0; i < full_desc_cnt; i++) { + desc_ptr->attr = (ADMA_DESC_TRANSFER_DATA | ADMA_DESC_ATTR_VALID); + desc_ptr->len = 0; // 0 means 64kb page size it will take + desc_ptr->addr_lo = 0; +#if CONFIG_DMA_ADDR_T_64BIT == 1 + desc_ptr->addr_hi = (uint32_t)((buf >> 32) & 0xffffffff); +#endif + if (non_full_desc_cnt == 0) { + desc_ptr->attr |= (ADMA_DESC_ATTR_END); + } + buf += PAGE_BUFFER_LEN; + } + + if (non_full_desc_cnt != 0) { + desc_ptr->attr = + (ADMA_DESC_TRANSFER_DATA | ADMA_DESC_ATTR_END | ADMA_DESC_ATTR_VALID); + desc_ptr->addr_lo = buf & 0xffffffff; + desc_ptr->len = size; +#if CONFIG_DMA_ADDR_T_64BIT == 1 + desc_ptr->addr_hi = (uint32_t)((buf >> 32) & 0xffffffff); +#endif + desc_address = (uint64_t)desc_ptr; + if (size > MMC_MAX_BLOCK_LEN) { + transfer_block_size = MMC_MAX_BLOCK_LEN; + } else { + transfer_block_size = size; + } + + block_count = (size / transfer_block_size); + mmio_write_32(cdns_params.reg_base + SDHC_CDNS_SRS01, + ((transfer_block_size << BLOCK_SIZE) | SDMA_BUF | + (block_count << BLK_COUNT_CT))); + mmio_write_32(cdns_params.reg_base + SDHC_CDNS_SRS22, + (uint32_t)desc_address & 0xFFFFFFFF); + mmio_write_32(cdns_params.reg_base + SDHC_CDNS_SRS23, + (uint32_t)(desc_address >> 32 & 0xFFFFFFFF)); + } +} + +int cdns_mmc_init(struct cdns_sdmmc_params *params, + struct mmc_device_info *info) +{ + + int result = 0; + + assert((params != NULL) && + ((params->reg_base & MMC_BLOCK_MASK) == 0) && + ((params->desc_size & MMC_BLOCK_MASK) == 0) && + ((params->reg_pinmux & MMC_BLOCK_MASK) == 0) && + ((params->reg_phy & MMC_BLOCK_MASK) == 0) && + (params->desc_size > 0) && + (params->clk_rate > 0) && + ((params->bus_width == MMC_BUS_WIDTH_1) || + (params->bus_width == MMC_BUS_WIDTH_4) || + (params->bus_width == MMC_BUS_WIDTH_8))); + + memcpy(&cdns_params, params, sizeof(struct cdns_sdmmc_params)); + + cdns_set_sdmmc_var(&sdmmc_combo_phy_reg, &sdmmc_sdhc_reg); + result = cdns_sd_host_init(&sdmmc_combo_phy_reg, &sdmmc_sdhc_reg); + if (result < 0) { + return result; + } + + cdns_params.cdn_sdmmc_dev_type = info->mmc_dev_type; + cdns_params.cdn_sdmmc_dev_mode = SD_DS; + + result = mmc_init(&cdns_sdmmc_ops, params->clk_rate, params->bus_width, + params->flags, info); + + return result; +} diff --git a/drivers/cadence/nand/cdns_nand.c b/drivers/cadence/nand/cdns_nand.c index 5a662626..20147d07 100644 --- a/drivers/cadence/nand/cdns_nand.c +++ b/drivers/cadence/nand/cdns_nand.c @@ -20,8 +20,12 @@ /* NAND flash device information struct */ static cnf_dev_info_t dev_info; -/* Scratch buffers for read and write operations */ -static uint8_t scratch_buff[PLATFORM_MTD_MAX_PAGE_SIZE]; +/* + * Scratch buffers for read and write operations + * DMA transfer of Cadence NAND expects data 8 bytes aligned + * to be written to register + */ +static uint8_t scratch_buff[PLATFORM_MTD_MAX_PAGE_SIZE] __aligned(8); /* Wait for controller to be in idle state */ static inline void cdns_nand_wait_idle(void) @@ -111,7 +115,8 @@ int cdns_nand_reset(uint8_t thread_id) cdns_nand_wait_thread_ready(thread_id); /* Select memory */ - mmio_write_32(CNF_CMDREG(CMD_REG4), (CNF_DEF_DEVICE << CNF_CMDREG4_MEM)); + mmio_write_32(CNF_CMDREG(CMD_REG4), + (CNF_DEF_DEVICE << CNF_CMDREG4_MEM)); /* Issue reset command */ uint32_t reg = (CNF_WORK_MODE_PIO << CNF_CMDREG0_CT); @@ -150,21 +155,19 @@ static void cdns_nand_set_opr_mode(uint8_t opr_mode) /* Async mode timing settings */ mmio_write_32(CNF_MINICTRL(ASYNC_TOGGLE_TIMINGS), - (2 << CNF_ASYNC_TIMINGS_TRH) | - (4 << CNF_ASYNC_TIMINGS_TRP) | - (2 << CNF_ASYNC_TIMINGS_TWH) | - (4 << CNF_ASYNC_TIMINGS_TWP)); + (2 << CNF_ASYNC_TIMINGS_TRH) | + (4 << CNF_ASYNC_TIMINGS_TRP) | + (2 << CNF_ASYNC_TIMINGS_TWH) | + (4 << CNF_ASYNC_TIMINGS_TWP)); /* Set extended read and write mode */ reg |= (1 << CNF_DLL_PHY_EXT_RD_MODE); reg |= (1 << CNF_DLL_PHY_EXT_WR_MODE); /* Set operation work mode in common settings */ - uint32_t data = mmio_read_32(CNF_MINICTRL(CMN_SETTINGS)); - - data |= (CNF_OPR_WORK_MODE_SDR << CNF_CMN_SETTINGS_OPR); - mmio_write_32(CNF_MINICTRL(CMN_SETTINGS), data); - + mmio_clrsetbits_32(CNF_MINICTRL(CMN_SETTINGS), + CNF_CMN_SETTINGS_OPR_MASK, + CNF_OPR_WORK_MODE_SDR); } else if (opr_mode == CNF_OPR_WORK_MODE_NVDDR) { ; /* ToDo: add DDR mode settings also once available on SIMICS */ } else { @@ -189,13 +192,13 @@ static void cdns_nand_transfer_config(void) /* DMA burst select */ mmio_write_32(CNF_CTRLCFG(DMA_SETTINGS), - (CNF_DMA_BURST_SIZE_MAX << CNF_DMA_SETTINGS_BURST) | - (1 << CNF_DMA_SETTINGS_OTE)); + (CNF_DMA_BURST_SIZE_MAX << CNF_DMA_SETTINGS_BURST) | + (1 << CNF_DMA_SETTINGS_OTE)); /* Enable pre-fetching for 1K */ mmio_write_32(CNF_CTRLCFG(FIFO_TLEVEL), - (CNF_DMA_PREFETCH_SIZE << CNF_FIFO_TLEVEL_POS) | - (CNF_DMA_PREFETCH_SIZE << CNF_FIFO_TLEVEL_DMA_SIZE)); + (CNF_DMA_PREFETCH_SIZE << CNF_FIFO_TLEVEL_POS) | + (CNF_DMA_PREFETCH_SIZE << CNF_FIFO_TLEVEL_DMA_SIZE)); /* Select access type */ mmio_write_32(CNF_CTRLCFG(MULTIPLANE_CFG), 0); @@ -235,12 +238,13 @@ static int cdns_nand_update_dev_info(void) /* Calculate block size and total device size */ dev_info.block_size = (dev_info.npages_per_block * dev_info.page_size); - dev_info.total_size = (dev_info.block_size * dev_info.nblocks_per_lun * - dev_info.nluns); + dev_info.total_size = ((unsigned long long)dev_info.block_size * + (unsigned long long)dev_info.nblocks_per_lun * + dev_info.nluns); - VERBOSE("CNF params: page %d, spare %d, block %d, total %lld\n", - dev_info.page_size, dev_info.spare_size, - dev_info.block_size, dev_info.total_size); + VERBOSE("CNF params: page_size %d, spare_size %d, block_size %u, total_size %llu\n", + dev_info.page_size, dev_info.spare_size, + dev_info.block_size, dev_info.total_size); return 0; } @@ -323,25 +327,44 @@ int cdns_nand_init_mtd(unsigned long long *size, unsigned int *erase_size) return 0; } +static uint32_t cdns_nand_get_row_address(uint32_t page, uint32_t block) +{ + uint32_t row_address = 0U; + uint32_t req_bits = 0U; + + /* The device info is not populated yet. */ + if (dev_info.npages_per_block == 0U) + return 0; + + for (uint32_t i = 0U; i < sizeof(uint32_t) * 8; i++) { + if ((1U << i) & dev_info.npages_per_block) + req_bits = i; + } + + row_address = ((page & GENMASK_32((req_bits - 1), 0)) | + (block << req_bits)); + + return row_address; +} + /* NAND Flash page read */ static int cdns_nand_read_page(uint32_t block, uint32_t page, uintptr_t buffer) { + /* Wait for thread to be ready */ cdns_nand_wait_thread_ready(CNF_DEF_TRD); /* Select device */ mmio_write_32(CNF_CMDREG(CMD_REG4), - (CNF_DEF_DEVICE << CNF_CMDREG4_MEM)); + (CNF_DEF_DEVICE << CNF_CMDREG4_MEM)); /* Set host memory address for DMA transfers */ - mmio_write_32(CNF_CMDREG(CMD_REG2), (buffer & 0xFFFF)); - mmio_write_32(CNF_CMDREG(CMD_REG3), ((buffer >> 32) & 0xFFFF)); + mmio_write_32(CNF_CMDREG(CMD_REG2), (buffer & UINT32_MAX)); + mmio_write_32(CNF_CMDREG(CMD_REG3), ((buffer >> 32) & UINT32_MAX)); /* Set row address */ - uint32_t row_address = 0U; - - row_address |= ((page & 0x3F) | (block << 6)); - mmio_write_32(CNF_CMDREG(CMD_REG1), row_address); + mmio_write_32(CNF_CMDREG(CMD_REG1), + cdns_nand_get_row_address(page, block)); /* Page read command */ uint32_t reg = (CNF_WORK_MODE_PIO << CNF_CMDREG0_CT); @@ -375,8 +398,8 @@ int cdns_nand_read(unsigned int offset, uintptr_t buffer, size_t length, uint32_t page = 0U; int result = 0; - VERBOSE("CNF: block %u-%u, page_start %u, len %zu, offset %u\n", - block, end_block, page_start, length, offset); + INFO("CNF: %s: block %u-%u, page_start %u, len %zu, offset %u\n", + __func__, block, end_block, page_start, length, offset); if ((offset >= dev_info.total_size) || (offset + length-1 >= dev_info.total_size) || @@ -392,7 +415,7 @@ int cdns_nand_read(unsigned int offset, uintptr_t buffer, size_t length, if ((start_offset != 0U) || (length < dev_info.page_size)) { /* Partial page read */ result = cdns_nand_read_page(block, page, - (uintptr_t)scratch_buff); + (uintptr_t)scratch_buff); if (result != 0) { return result; } diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c index 4cbc0f70..3e87efcb 100644 --- a/drivers/clk/clk.c +++ b/drivers/clk/clk.c @@ -34,6 +34,20 @@ unsigned long clk_get_rate(unsigned long id) return ops->get_rate(id); } +int clk_set_rate(unsigned long id, unsigned long rate, unsigned long *orate) +{ + unsigned long lrate; + + assert((ops != NULL) && (ops->set_rate != NULL)); + + if (orate != NULL) { + return ops->set_rate(id, rate, orate); + } + + /* In case the caller is not interested in the output rate */ + return ops->set_rate(id, rate, &lrate); +} + int clk_get_parent(unsigned long id) { assert((ops != NULL) && (ops->get_parent != NULL)); @@ -41,6 +55,13 @@ int clk_get_parent(unsigned long id) return ops->get_parent(id); } +int clk_set_parent(unsigned long id, unsigned long parent_id) +{ + assert((ops != NULL) && (ops->set_parent != NULL)); + + return ops->set_parent(id, parent_id); +} + bool clk_is_enabled(unsigned long id) { assert((ops != NULL) && (ops->is_enabled != NULL)); diff --git a/drivers/delay_timer/delay_timer.c b/drivers/delay_timer/delay_timer.c index a3fd7bfe..bdbbbf6a 100644 --- a/drivers/delay_timer/delay_timer.c +++ b/drivers/delay_timer/delay_timer.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015-2019, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2015-2024, Arm Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -80,3 +80,25 @@ void timer_init(const timer_ops_t *ops_ptr) timer_ops = ops_ptr; } + +/*********************************************************** + * Initialize the timer in us + ***********************************************************/ +uint64_t timeout_init_us(uint32_t usec) +{ + assert(timer_ops != NULL); + assert(timer_ops->timeout_init_us != NULL); + + return timer_ops->timeout_init_us(usec); +} + +/*********************************************************** + * check the given timeout elapsed or not. + ***********************************************************/ +bool timeout_elapsed(uint64_t cnt) +{ + assert(timer_ops != NULL); + assert(timer_ops->timeout_elapsed != NULL); + + return timer_ops->timeout_elapsed(cnt); +} diff --git a/drivers/delay_timer/generic_delay_timer.c b/drivers/delay_timer/generic_delay_timer.c index ca522e05..0407e385 100644 --- a/drivers/delay_timer/generic_delay_timer.c +++ b/drivers/delay_timer/generic_delay_timer.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016-2019, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2016-2024, Arm Limited and Contributors. All rights reserved. * Copyright (c) 2020, NVIDIA Corporation. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause @@ -18,7 +18,26 @@ static timer_ops_t ops; -static uint32_t get_timer_value(void) +static uint64_t timeout_cnt_us2cnt(uint32_t us) +{ + return ((uint64_t)us * (uint64_t)read_cntfrq_el0()) / 1000000ULL; +} + +static uint64_t generic_delay_timeout_init_us(uint32_t us) +{ + uint64_t cnt = timeout_cnt_us2cnt(us); + + cnt += read_cntpct_el0(); + + return cnt; +} + +static bool generic_delay_timeout_elapsed(uint64_t expire_cnt) +{ + return read_cntpct_el0() > expire_cnt; +} + +static uint32_t generic_delay_get_timer_value(void) { /* * Generic delay timer implementation expects the timer to be a down @@ -31,9 +50,11 @@ static uint32_t get_timer_value(void) void generic_delay_timer_init_args(uint32_t mult, uint32_t div) { - ops.get_timer_value = get_timer_value; + ops.get_timer_value = generic_delay_get_timer_value; ops.clk_mult = mult; ops.clk_div = div; + ops.timeout_init_us = generic_delay_timeout_init_us; + ops.timeout_elapsed = generic_delay_timeout_elapsed; timer_init(&ops); @@ -59,4 +80,3 @@ void generic_delay_timer_init(void) generic_delay_timer_init_args(mult, div); } - diff --git a/drivers/fwu/fwu.c b/drivers/fwu/fwu.c index ff432be8..b6f06e0a 100644 --- a/drivers/fwu/fwu.c +++ b/drivers/fwu/fwu.c @@ -24,6 +24,17 @@ CASSERT((offsetof(struct fwu_metadata, crc_32) == 0), crc_32_must_be_first_member_of_structure); +/* + * Ensure that the NR_OF_FW_BANKS selected by the platform is not + * zero and not greater than the maximum number of banks allowed + * by the specification. + */ +CASSERT((NR_OF_FW_BANKS > 0) && (NR_OF_FW_BANKS <= NR_OF_MAX_FW_BANKS), + assert_fwu_num_banks_invalid_value); + +#define FWU_METADATA_VERSION 2U +#define FWU_FW_STORE_DESC_OFFSET 0x20U + static struct fwu_metadata metadata; static bool is_metadata_initialized __unused; @@ -51,16 +62,54 @@ static int fwu_metadata_crc_check(void) /******************************************************************************* * Check the sanity of FWU metadata. * - * return -1 on error, otherwise 0 + * return -EINVAL on error, otherwise 0 ******************************************************************************/ static int fwu_metadata_sanity_check(void) { - /* ToDo: add more conditions for sanity check */ - if ((metadata.active_index >= NR_OF_FW_BANKS) || - (metadata.previous_active_index >= NR_OF_FW_BANKS)) { - return -1; + if (metadata.version != FWU_METADATA_VERSION) { + WARN("Incorrect FWU Metadata version of %u\n", + metadata.version); + return -EINVAL; + } + + if (metadata.active_index >= NR_OF_FW_BANKS) { + WARN("Active Index value(%u) greater than the configured value(%d)", + metadata.active_index, NR_OF_FW_BANKS); + return -EINVAL; + } + + if (metadata.previous_active_index >= NR_OF_FW_BANKS) { + WARN("Previous Active Index value(%u) greater than the configured value(%d)", + metadata.previous_active_index, NR_OF_FW_BANKS); + return -EINVAL; } +#if PSA_FWU_METADATA_FW_STORE_DESC + if (metadata.fw_desc.num_banks != NR_OF_FW_BANKS) { + WARN("Number of Banks(%u) in FWU Metadata different from the configured value(%d)", + metadata.fw_desc.num_banks, NR_OF_FW_BANKS); + return -EINVAL; + } + + if (metadata.fw_desc.num_images != NR_OF_IMAGES_IN_FW_BANK) { + WARN("Number of Images(%u) in FWU Metadata different from the configured value(%d)", + metadata.fw_desc.num_images, NR_OF_IMAGES_IN_FW_BANK); + return -EINVAL; + } + + if (metadata.desc_offset != FWU_FW_STORE_DESC_OFFSET) { + WARN("Descriptor Offset(0x%x) in the FWU Metadata not equal to 0x20\n", + metadata.desc_offset); + return -EINVAL; + } +#else + if (metadata.desc_offset != 0U) { + WARN("Descriptor offset has non zero value of 0x%x\n", + metadata.desc_offset); + return -EINVAL; + } +#endif + return 0; } @@ -133,28 +182,80 @@ exit: } /******************************************************************************* - * The system runs in the trial run state if any of the images in the active - * firmware bank has not been accepted yet. + * Check for an alternate bank for the platform to boot from. This function will + * mostly be called whenever the count of the number of times a platform boots + * in the Trial State exceeds a pre-set limit. + * The function first checks if the platform can boot from the previously active + * bank. If not, it tries to find another bank in the accepted state. + * And finally, if both the checks fail, as a last resort, it tries to find + * a valid bank. * - * Returns true if the system is running in the trial state. + * Returns the index of a bank to boot, else returns invalid index + * INVALID_BOOT_IDX. ******************************************************************************/ -bool fwu_is_trial_run_state(void) +uint32_t fwu_get_alternate_boot_bank(void) { - bool trial_run = false; + uint32_t i; - assert(is_metadata_initialized); + /* First check if the previously active bank can be used */ + if (metadata.bank_state[metadata.previous_active_index] == + FWU_BANK_STATE_ACCEPTED) { + return metadata.previous_active_index; + } + + /* Now check for any other bank in the accepted state */ + for (i = 0U; i < NR_OF_FW_BANKS; i++) { + if (i == metadata.active_index || + i == metadata.previous_active_index) { + continue; + } + + if (metadata.bank_state[i] == FWU_BANK_STATE_ACCEPTED) { + return i; + } + } + + /* + * No accepted bank found. Now try booting from a valid bank. + * Give priority to the previous active bank. + */ + if (metadata.bank_state[metadata.previous_active_index] == + FWU_BANK_STATE_VALID) { + return metadata.previous_active_index; + } - for (unsigned int i = 0U; i < NR_OF_IMAGES_IN_FW_BANK; i++) { - struct fwu_image_entry *entry = &metadata.img_entry[i]; - struct fwu_image_properties *img_props = - &entry->img_props[metadata.active_index]; - if (img_props->accepted == 0) { - trial_run = true; - break; + for (i = 0U; i < NR_OF_FW_BANKS; i++) { + if (i == metadata.active_index || + i == metadata.previous_active_index) { + continue; + } + + if (metadata.bank_state[i] == FWU_BANK_STATE_VALID) { + return i; } } - return trial_run; + return INVALID_BOOT_IDX; +} + +/******************************************************************************* + * The platform can be in one of Valid, Invalid or Accepted states. + * + * Invalid - One or more images in the bank are corrupted, or partially + * overwritten. The bank is not to be used for booting. + * + * Valid - All images of the bank are valid but at least one image has not + * been accepted. This implies that the platform is in Trial State. + * + * Accepted - All images of the bank are valid and accepted. + * + * Returns the state of the current active bank + ******************************************************************************/ +uint32_t fwu_get_active_bank_state(void) +{ + assert(is_metadata_initialized); + + return metadata.bank_state[metadata.active_index]; } const struct fwu_metadata *fwu_get_metadata(void) diff --git a/drivers/measured_boot/rse/dice_prot_env.c b/drivers/measured_boot/rse/dice_prot_env.c new file mode 100644 index 00000000..dad30b2c --- /dev/null +++ b/drivers/measured_boot/rse/dice_prot_env.c @@ -0,0 +1,194 @@ +/* + * Copyright (c) 2024, Arm Limited. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <assert.h> +#include <stdint.h> +#include <string.h> + +#include <psa/crypto_types.h> +#include <psa/crypto_values.h> + +#include <common/debug.h> +#include <drivers/auth/crypto_mod.h> +#include <drivers/measured_boot/rse/dice_prot_env.h> +#include <lib/cassert.h> +#include <lib/psa/dice_protection_environment.h> + +#include <platform_def.h> + +#define DPE_ALG_SHA512 0 +#define DPE_ALG_SHA384 1 +#define DPE_ALG_SHA256 2 + +#if DPE_ALG_ID == DPE_ALG_SHA512 +#define CRYPTO_MD_ID CRYPTO_MD_SHA512 +#define PSA_CRYPTO_MD_ID PSA_ALG_SHA_512 +#elif DPE_ALG_ID == DPE_ALG_SHA384 +#define CRYPTO_MD_ID CRYPTO_MD_SHA384 +#define PSA_CRYPTO_MD_ID PSA_ALG_SHA_384 +#elif DPE_ALG_ID == DPE_ALG_SHA256 +#define CRYPTO_MD_ID CRYPTO_MD_SHA256 +#define PSA_CRYPTO_MD_ID PSA_ALG_SHA_256 +#else +# error Invalid DPE hash algorithm. +#endif /* DPE_ALG_ID */ + +/* Ensure that computed hash values fits into the DiceInputValues structure */ +CASSERT(DICE_HASH_SIZE >= DPE_DIGEST_SIZE, + assert_digest_size_bigger_than_allocated_buffer); + +static int initial_context_handle; + +static void map_metadata_to_dice_inputs(struct dpe_metadata *metadata, + DiceInputValues *dice_inputs) +{ + /* Hash of the content certificate signing key (public part) */ + memcpy(dice_inputs->authority_hash, metadata->signer_id, + DPE_DIGEST_SIZE); + + /* SW type string identifier */ + assert(metadata->sw_type_size < DICE_CODE_DESCRIPTOR_MAX_SIZE); + dice_inputs->code_descriptor = metadata->sw_type; + dice_inputs->code_descriptor_size = metadata->sw_type_size; +} + +void dpe_init(struct dpe_metadata *metadata) +{ + assert(metadata != NULL); + + /* Init the non-const members of the metadata structure */ + while (metadata->id != DPE_INVALID_ID) { + /* Terminating 0 character is not needed due to CBOR encoding */ + metadata->sw_type_size = + strlen((const char *)&metadata->sw_type); + metadata++; + } + + plat_dpe_get_context_handle(&initial_context_handle); +} + +int dpe_measure_and_record(struct dpe_metadata *metadata, + uintptr_t data_base, uint32_t data_size, + uint32_t data_id) +{ + static int current_context_handle; + DiceInputValues dice_inputs = { 0 }; + int new_parent_context_handle; + int new_context_handle; + dpe_error_t ret; + int rc; + + assert(metadata != NULL); + + /* Get the metadata associated with this image. */ + while ((metadata->id != DPE_INVALID_ID) && (metadata->id != data_id)) { + metadata++; + } + + /* If image is not present in metadata array then skip */ + if (metadata->id == DPE_INVALID_ID) { + return 0; + } + + /* Calculate hash */ + rc = crypto_mod_calc_hash(CRYPTO_MD_ID, + (void *)data_base, data_size, + dice_inputs.code_hash); + if (rc != 0) { + return rc; + } + + map_metadata_to_dice_inputs(metadata, &dice_inputs); + + /* Only at the first call */ + if (current_context_handle == 0) { + current_context_handle = initial_context_handle; + } + + VERBOSE("Calling dpe_derive_context, image_id: %d\n", metadata->id); + ret = dpe_derive_context(current_context_handle, + metadata->cert_id, + metadata->retain_parent_context, + metadata->allow_new_context_to_derive, + metadata->create_certificate, + &dice_inputs, + metadata->target_locality, + false, /* return_certificate */ + true, /* allow_new_context_to_export */ + false, /* export_cdi */ + &new_context_handle, + &new_parent_context_handle, + NULL, 0, NULL, /* new_certificate_* */ + NULL, 0, NULL); /* exported_cdi_* */ + if (ret == DPE_NO_ERROR) { + current_context_handle = new_parent_context_handle; + if (metadata->allow_new_context_to_derive == true) { + /* Share new_context_handle with child component: + * e.g: BL2, BL33. + */ + VERBOSE("Share new_context_handle with child: 0x%x\n", + new_context_handle); + plat_dpe_share_context_handle(&new_context_handle, + &new_parent_context_handle); + } + } else { + ERROR("dpe_derive_context failed: %d\n", ret); + } + + return (ret == DPE_NO_ERROR) ? 0 : -1; +} + +int dpe_set_signer_id(struct dpe_metadata *metadata, + const void *pk_oid, + const void *pk_ptr, + size_t pk_len) +{ + unsigned char hash_data[CRYPTO_MD_MAX_SIZE]; + int rc; + bool hash_calc_done = false; + + assert(metadata != NULL); + + /* + * Do an exhaustive search over the platform metadata to find + * all images whose key OID matches the one passed in argument. + * + * Note that it is not an error if do not get any matches. + * The platform may decide not to measure all of the images + * in the system. + */ + while (metadata->id != DPE_INVALID_ID) { + /* Get the metadata associated with this key-oid */ + if (metadata->pk_oid == pk_oid) { + if (hash_calc_done == false) { + /* Calculate public key hash */ + rc = crypto_mod_calc_hash(CRYPTO_MD_ID, + (void *)pk_ptr, + pk_len, hash_data); + if (rc != 0) { + return rc; + } + + hash_calc_done = true; + } + + /* + * Fill the signer-ID field with the newly/already + * computed hash of the public key and update its + * signer ID size field with compile-time decided + * digest size. + */ + (void)memcpy(metadata->signer_id, + hash_data, + DPE_DIGEST_SIZE); + metadata->signer_id_size = DPE_DIGEST_SIZE; + } + + metadata++; + } + + return 0; +} diff --git a/drivers/measured_boot/rse/dice_prot_env.mk b/drivers/measured_boot/rse/dice_prot_env.mk new file mode 100644 index 00000000..7c833076 --- /dev/null +++ b/drivers/measured_boot/rse/dice_prot_env.mk @@ -0,0 +1,29 @@ +# +# Copyright (c) 2024, Arm Limited. All rights reserved. +# +# SPDX-License-Identifier: BSD-3-Clause +# + +# Hash algorithm for DICE Protection Environment +# SHA-256 (or stronger) is required. +DPE_HASH_ALG := sha256 + +ifeq (${DPE_HASH_ALG}, sha512) + DPE_ALG_ID := DPE_ALG_SHA512 + DPE_DIGEST_SIZE := 64U +else ifeq (${DPE_HASH_ALG}, sha384) + DPE_ALG_ID := DPE_ALG_SHA384 + DPE_DIGEST_SIZE := 48U +else + DPE_ALG_ID := DPE_ALG_SHA256 + DPE_DIGEST_SIZE := 32U +endif #DPE_HASH_ALG + +# Set definitions for DICE Protection Environment +$(eval $(call add_defines,\ + $(sort \ + DPE_ALG_ID \ + DPE_DIGEST_SIZE \ +))) + +DPE_SOURCES += drivers/measured_boot/rse/dice_prot_env.c diff --git a/drivers/measured_boot/rse/qcbor.mk b/drivers/measured_boot/rse/qcbor.mk new file mode 100644 index 00000000..2146e5d2 --- /dev/null +++ b/drivers/measured_boot/rse/qcbor.mk @@ -0,0 +1,23 @@ +# +# Copyright (c) 2024, Arm Limited. All rights reserved. +# +# SPDX-License-Identifier: BSD-3-Clause +# + +# TF-A was tested with v1.2 version of QCBOR + +ifeq (${QCBOR_DIR},) + $(error Error: QCBOR_DIR not set) +endif + +QCBOR_SOURCES += ${QCBOR_DIR}/src/qcbor_encode.c \ + ${QCBOR_DIR}/src/qcbor_decode.c \ + ${QCBOR_DIR}/src/UsefulBuf.c + +QCBOR_INCLUDES += ${QCBOR_DIR}/inc + +# Floating point numbers are not used, so disable the support. +# This reduces the library size as well. +$(eval $(call add_define,QCBOR_DISABLE_FLOAT_HW_USE)) +$(eval $(call add_define,USEFULBUF_DISABLE_ALL_FLOAT)) +$(eval $(call add_define,QCBOR_DISABLE_PREFERRED_FLOAT)) diff --git a/drivers/measured_boot/rss/rss_measured_boot.c b/drivers/measured_boot/rse/rse_measured_boot.c similarity index 86% rename from drivers/measured_boot/rss/rss_measured_boot.c rename to drivers/measured_boot/rse/rse_measured_boot.c index 258aa8d4..5337c3de 100644 --- a/drivers/measured_boot/rss/rss_measured_boot.c +++ b/drivers/measured_boot/rse/rse_measured_boot.c @@ -9,7 +9,7 @@ #include <common/debug.h> #include <drivers/auth/crypto_mod.h> -#include <drivers/measured_boot/rss/rss_measured_boot.h> +#include <drivers/measured_boot/rse/rse_measured_boot.h> #include <lib/psa/measured_boot.h> #include <psa/crypto_types.h> #include <psa/crypto_values.h> @@ -46,12 +46,12 @@ static bool null_arr(const uint8_t *signer_id, size_t signer_id_size) #endif /* ENABLE_ASSERTIONS */ /* Functions' declarations */ -void rss_measured_boot_init(struct rss_mboot_metadata *metadata_ptr) +void rse_measured_boot_init(struct rse_mboot_metadata *metadata_ptr) { assert(metadata_ptr != NULL); /* Init the non-const members of the metadata structure */ - while (metadata_ptr->id != RSS_MBOOT_INVALID_ID) { + while (metadata_ptr->id != RSE_MBOOT_INVALID_ID) { assert(null_arr(metadata_ptr->signer_id, MBOOT_DIGEST_SIZE)); metadata_ptr->sw_type_size = strlen((const char *)&metadata_ptr->sw_type) + 1; @@ -59,7 +59,7 @@ void rss_measured_boot_init(struct rss_mboot_metadata *metadata_ptr) } } -int rss_mboot_measure_and_record(struct rss_mboot_metadata *metadata_ptr, +int rse_mboot_measure_and_record(struct rse_mboot_metadata *metadata_ptr, uintptr_t data_base, uint32_t data_size, uint32_t data_id) { @@ -70,13 +70,13 @@ int rss_mboot_measure_and_record(struct rss_mboot_metadata *metadata_ptr, assert(metadata_ptr != NULL); /* Get the metadata associated with this image. */ - while ((metadata_ptr->id != RSS_MBOOT_INVALID_ID) && + while ((metadata_ptr->id != RSE_MBOOT_INVALID_ID) && (metadata_ptr->id != data_id)) { metadata_ptr++; } /* If image is not present in metadata array then skip */ - if (metadata_ptr->id == RSS_MBOOT_INVALID_ID) { + if (metadata_ptr->id == RSE_MBOOT_INVALID_ID) { return 0; } @@ -87,7 +87,7 @@ int rss_mboot_measure_and_record(struct rss_mboot_metadata *metadata_ptr, return rc; } - ret = rss_measured_boot_extend_measurement( + ret = rse_measured_boot_extend_measurement( metadata_ptr->slot, metadata_ptr->signer_id, metadata_ptr->signer_id_size, @@ -106,7 +106,7 @@ int rss_mboot_measure_and_record(struct rss_mboot_metadata *metadata_ptr, return 0; } -int rss_mboot_set_signer_id(struct rss_mboot_metadata *metadata_ptr, +int rse_mboot_set_signer_id(struct rse_mboot_metadata *metadata_ptr, const void *pk_oid, const void *pk_ptr, size_t pk_len) @@ -125,10 +125,10 @@ int rss_mboot_set_signer_id(struct rss_mboot_metadata *metadata_ptr, * The platform may decide not to measure all of the images * in the system. */ - while (metadata_ptr->id != RSS_MBOOT_INVALID_ID) { + while (metadata_ptr->id != RSE_MBOOT_INVALID_ID) { /* Get the metadata associated with this key-oid */ if (metadata_ptr->pk_oid == pk_oid) { - if (!hash_calc_done) { + if (hash_calc_done == false) { /* Calculate public key hash */ rc = crypto_mod_calc_hash(CRYPTO_MD_ID, (void *)pk_ptr, diff --git a/drivers/measured_boot/rss/rss_measured_boot.mk b/drivers/measured_boot/rse/rse_measured_boot.mk similarity index 65% rename from drivers/measured_boot/rss/rss_measured_boot.mk rename to drivers/measured_boot/rse/rse_measured_boot.mk index 18ee8361..1bd971f3 100644 --- a/drivers/measured_boot/rss/rss_measured_boot.mk +++ b/drivers/measured_boot/rse/rse_measured_boot.mk @@ -6,27 +6,27 @@ # Hash algorithm for measured boot # SHA-256 (or stronger) is required. -MBOOT_RSS_HASH_ALG := sha256 +MBOOT_RSE_HASH_ALG := sha256 -ifeq (${MBOOT_RSS_HASH_ALG}, sha512) +ifeq (${MBOOT_RSE_HASH_ALG}, sha512) MBOOT_ALG_ID := MBOOT_ALG_SHA512 MBOOT_DIGEST_SIZE := 64U -else ifeq (${MBOOT_RSS_HASH_ALG}, sha384) +else ifeq (${MBOOT_RSE_HASH_ALG}, sha384) MBOOT_ALG_ID := MBOOT_ALG_SHA384 MBOOT_DIGEST_SIZE := 48U else MBOOT_ALG_ID := MBOOT_ALG_SHA256 MBOOT_DIGEST_SIZE := 32U -endif #MBOOT_RSS_HASH_ALG +endif #MBOOT_RSE_HASH_ALG # Set definitions for Measured Boot driver. $(eval $(call add_defines,\ $(sort \ MBOOT_ALG_ID \ MBOOT_DIGEST_SIZE \ - MBOOT_RSS_BACKEND \ + MBOOT_RSE_BACKEND \ ))) -MEASURED_BOOT_SRC_DIR := drivers/measured_boot/rss/ +MEASURED_BOOT_SRC_DIR := drivers/measured_boot/rse/ -MEASURED_BOOT_SOURCES += ${MEASURED_BOOT_SRC_DIR}rss_measured_boot.c +MEASURED_BOOT_SOURCES += ${MEASURED_BOOT_SRC_DIR}rse_measured_boot.c diff --git a/drivers/nxp/auth/csf_hdr_parser/csf_hdr.mk b/drivers/nxp/auth/csf_hdr_parser/csf_hdr.mk index 1af51f80..12e22e94 100644 --- a/drivers/nxp/auth/csf_hdr_parser/csf_hdr.mk +++ b/drivers/nxp/auth/csf_hdr_parser/csf_hdr.mk @@ -26,8 +26,8 @@ endif # CST_BL31 define CST_BL31_RULE $(1): $(2) - @echo " Generating CSF Header for $$@ $$<" - $(Q)$(CST_DIR)/create_hdr_esbc --in $(2) --out $(1) --app_off ${CSF_HDR_SZ} \ + $(s)echo " Generating CSF Header for $$@ $$<" + $(q)$(CST_DIR)/create_hdr_esbc --in $(2) --out $(1) --app_off ${CSF_HDR_SZ} \ --app $(2) ${BL31_INPUT_FILE} endef @@ -36,8 +36,8 @@ CST_BL31_SUFFIX := .cst # CST_BL32 define CST_BL32_RULE $(1): $(2) - @echo " Generating CSF Header for $$@ $$<" - $(Q)$(CST_DIR)/create_hdr_esbc --in $(2) --out $(1) --app_off ${CSF_HDR_SZ} \ + $(s)echo " Generating CSF Header for $$@ $$<" + $(q)$(CST_DIR)/create_hdr_esbc --in $(2) --out $(1) --app_off ${CSF_HDR_SZ} \ --app $(2) ${BL32_INPUT_FILE} endef @@ -46,8 +46,8 @@ CST_BL32_SUFFIX := .cst # CST_BL33 define CST_BL33_RULE $(1): $(2) - @echo " Generating CSF Header for $$@ $$<" - $(Q)$(CST_DIR)/create_hdr_esbc --in $(2) --out $(1) --app_off ${CSF_HDR_SZ} \ + $(s)echo " Generating CSF Header for $$@ $$<" + $(q)$(CST_DIR)/create_hdr_esbc --in $(2) --out $(1) --app_off ${CSF_HDR_SZ} \ --app $(2) ${BL33_INPUT_FILE} endef @@ -56,8 +56,8 @@ CST_BL33_SUFFIX := .cst # CST_SCP_BL2 define CST_SCP_BL2_RULE $(1): $(2) - @echo " Generating CSF Header for $$@ $$<" - $(Q)$(CST_DIR)/create_hdr_esbc --in $(2) --out $(1) --app_off ${CSF_HDR_SZ} \ + $(s)echo " Generating CSF Header for $$@ $$<" + $(q)$(CST_DIR)/create_hdr_esbc --in $(2) --out $(1) --app_off ${CSF_HDR_SZ} \ --app $(2) ${FUSE_INPUT_FILE} endef diff --git a/drivers/nxp/clk/s32cc/include/s32cc-clk-regs.h b/drivers/nxp/clk/s32cc/include/s32cc-clk-regs.h new file mode 100644 index 00000000..e54d5813 --- /dev/null +++ b/drivers/nxp/clk/s32cc/include/s32cc-clk-regs.h @@ -0,0 +1,115 @@ +// SPDX-License-Identifier: BSD-3-Clause +/* + * Copyright 2020-2021, 2023-2024 NXP + */ +#ifndef S32CC_CLK_REGS_H +#define S32CC_CLK_REGS_H + +#include <lib/utils_def.h> + +#define FXOSC_BASE_ADDR (0x40050000UL) +#define ARMPLL_BASE_ADDR (0x40038000UL) +#define PERIPHPLL_BASE_ADDR (0x4003C000UL) +#define ARM_DFS_BASE_ADDR (0x40054000UL) +#define CGM0_BASE_ADDR (0x40030000UL) +#define CGM1_BASE_ADDR (0x40034000UL) +#define DDRPLL_BASE_ADDR (0x40044000UL) +#define MC_ME_BASE_ADDR (0x40088000UL) +#define MC_RGM_BASE_ADDR (0x40078000UL) +#define RDC_BASE_ADDR (0x40080000UL) +#define MC_CGM5_BASE_ADDR (0x40068000UL) + +/* FXOSC */ +#define FXOSC_CTRL(FXOSC) ((FXOSC) + 0x0UL) +#define FXOSC_CTRL_OSC_BYP BIT_32(31U) +#define FXOSC_CTRL_COMP_EN BIT_32(24U) +#define FXOSC_CTRL_EOCV_OFFSET 16U +#define FXOSC_CTRL_EOCV_MASK GENMASK_32(23U, FXOSC_CTRL_EOCV_OFFSET) +#define FXOSC_CTRL_EOCV(VAL) (FXOSC_CTRL_EOCV_MASK & \ + ((uint32_t)(VAL) << FXOSC_CTRL_EOCV_OFFSET)) +#define FXOSC_CTRL_GM_SEL_OFFSET 4U +#define FXOSC_CTRL_GM_SEL_MASK GENMASK_32(7U, FXOSC_CTRL_GM_SEL_OFFSET) +#define FXOSC_CTRL_GM_SEL(VAL) (FXOSC_CTRL_GM_SEL_MASK & \ + ((uint32_t)(VAL) << FXOSC_CTRL_GM_SEL_OFFSET)) +#define FXOSC_CTRL_OSCON BIT_32(0U) + +#define FXOSC_STAT(FXOSC) ((FXOSC) + 0x4UL) +#define FXOSC_STAT_OSC_STAT BIT_32(31U) + +/* PLL */ +#define PLLDIG_PLLCR(PLL) ((PLL) + 0x0UL) +#define PLLDIG_PLLCR_PLLPD BIT_32(31U) + +#define PLLDIG_PLLSR(PLL) ((PLL) + 0x4UL) +#define PLLDIG_PLLSR_LOCK BIT_32(2U) + +#define PLLDIG_PLLDV(PLL) ((PLL) + 0x8UL) +#define PLLDIG_PLLDV_RDIV_OFFSET 12U +#define PLLDIG_PLLDV_RDIV_MASK GENMASK_32(14U, PLLDIG_PLLDV_RDIV_OFFSET) +#define PLLDIG_PLLDV_RDIV_SET(VAL) (PLLDIG_PLLDV_RDIV_MASK & \ + ((VAL) << PLLDIG_PLLDV_RDIV_OFFSET)) +#define PLLDIG_PLLDV_MFI_MASK GENMASK_32(7U, 0U) +#define PLLDIG_PLLDV_MFI(DIV) (PLLDIG_PLLDV_MFI_MASK & (DIV)) + +#define PLLDIG_PLLFD(PLL) ((PLL) + 0x10UL) +#define PLLDIG_PLLFD_SMDEN BIT_32(30U) +#define PLLDIG_PLLFD_MFN_MASK GENMASK_32(14U, 0U) +#define PLLDIG_PLLFD_MFN_SET(VAL) (PLLDIG_PLLFD_MFN_MASK & (VAL)) + +#define PLLDIG_PLLCLKMUX(PLL) ((PLL) + 0x20UL) + +#define PLLDIG_PLLODIV(PLL, N) ((PLL) + 0x80UL + ((N) * 0x4UL)) +#define PLLDIG_PLLODIV_DE BIT_32(31U) +#define PLLDIG_PLLODIV_DIV_OFFSET 16U +#define PLLDIG_PLLODIV_DIV_MASK GENMASK_32(23U, PLLDIG_PLLODIV_DIV_OFFSET) +#define PLLDIG_PLLODIV_DIV(VAL) (((VAL) & PLLDIG_PLLODIV_DIV_MASK) >> \ + PLLDIG_PLLODIV_DIV_OFFSET) +#define PLLDIG_PLLODIV_DIV_SET(VAL) (PLLDIG_PLLODIV_DIV_MASK & ((VAL) << \ + PLLDIG_PLLODIV_DIV_OFFSET)) + +/* MMC_CGM */ +#define CGM_MUXn_CSC(CGM_ADDR, MUX) ((CGM_ADDR) + 0x300UL + ((MUX) * 0x40UL)) +#define MC_CGM_MUXn_CSC_SELCTL_OFFSET 24U +#define MC_CGM_MUXn_CSC_SELCTL_MASK GENMASK_32(29U, MC_CGM_MUXn_CSC_SELCTL_OFFSET) +#define MC_CGM_MUXn_CSC_SELCTL(val) (MC_CGM_MUXn_CSC_SELCTL_MASK & ((val) \ + << MC_CGM_MUXn_CSC_SELCTL_OFFSET)) +#define MC_CGM_MUXn_CSC_CLK_SW BIT_32(2U) +#define MC_CGM_MUXn_CSC_SAFE_SW BIT_32(3U) + +#define CGM_MUXn_CSS(CGM_ADDR, MUX) ((CGM_ADDR) + 0x304UL + ((MUX) * 0x40UL)) +#define MC_CGM_MUXn_CSS_SELSTAT_OFFSET 24U +#define MC_CGM_MUXn_CSS_SELSTAT_MASK GENMASK_32(29U, MC_CGM_MUXn_CSS_SELSTAT_OFFSET) +#define MC_CGM_MUXn_CSS_SELSTAT(css) ((MC_CGM_MUXn_CSS_SELSTAT_MASK & (css))\ + >> MC_CGM_MUXn_CSS_SELSTAT_OFFSET) +#define MC_CGM_MUXn_CSS_SWTRG(css) ((MC_CGM_MUXn_CSS_SWTRG_MASK & (css)) \ + >> MC_CGM_MUXn_CSS_SWTRG_OFFSET) +#define MC_CGM_MUXn_CSS_SWTRG_OFFSET 17U +#define MC_CGM_MUXn_CSS_SWTRG_MASK GENMASK_32(19U, MC_CGM_MUXn_CSS_SWTRG_OFFSET) +#define MC_CGM_MUXn_CSS_SWTRG_SUCCESS 0x1U +#define MC_CGM_MUXn_CSS_SWTRG_SAFE_CLK 0x4U +#define MC_CGM_MUXn_CSS_SWTRG_SAFE_CLK_INACTIVE 0x5U +#define MC_CGM_MUXn_CSS_SWIP BIT_32(16U) +#define MC_CGM_MUXn_CSS_SAFE_SW BIT_32(3U) + +/* DFS */ +#define DFS_PORTSR(DFS_ADDR) ((DFS_ADDR) + 0xCUL) +#define DFS_PORTOLSR(DFS_ADDR) ((DFS_ADDR) + 0x10UL) +#define DFS_PORTOLSR_LOL(N) (BIT_32(N) & GENMASK_32(5U, 0U)) +#define DFS_PORTRESET(DFS_ADDR) ((DFS_ADDR) + 0x14UL) +#define DFS_PORTRESET_MASK GENMASK_32(5U, 0U) +#define DFS_PORTRESET_SET(VAL) (((VAL) & DFS_PORTRESET_MASK)) + +#define DFS_CTL(DFS_ADDR) ((DFS_ADDR) + 0x18UL) +#define DFS_CTL_RESET BIT_32(1U) + +#define DFS_DVPORTn(DFS_ADDR, PORT) ((DFS_ADDR) + 0x1CUL + ((PORT) * 0x4UL)) +#define DFS_DVPORTn_MFI_MASK GENMASK_32(15U, 8U) +#define DFS_DVPORTn_MFI_SHIFT 8U +#define DFS_DVPORTn_MFN_MASK GENMASK_32(7U, 0U) +#define DFS_DVPORTn_MFN_SHIFT 0U +#define DFS_DVPORTn_MFI(MFI) (((MFI) & DFS_DVPORTn_MFI_MASK) >> DFS_DVPORTn_MFI_SHIFT) +#define DFS_DVPORTn_MFN(MFN) (((MFN) & DFS_DVPORTn_MFN_MASK) >> DFS_DVPORTn_MFN_SHIFT) +#define DFS_DVPORTn_MFI_SET(VAL) (((VAL) << DFS_DVPORTn_MFI_SHIFT) & DFS_DVPORTn_MFI_MASK) +#define DFS_DVPORTn_MFN_SET(VAL) (((VAL) << DFS_DVPORTn_MFN_SHIFT) & DFS_DVPORTn_MFN_MASK) + +#endif /* S32CC_CLK_REGS_H */ diff --git a/drivers/nxp/clk/s32cc/include/s32cc-mc-me.h b/drivers/nxp/clk/s32cc/include/s32cc-mc-me.h new file mode 100644 index 00000000..8249fc55 --- /dev/null +++ b/drivers/nxp/clk/s32cc/include/s32cc-mc-me.h @@ -0,0 +1,16 @@ +// SPDX-License-Identifier: BSD-3-Clause +/* + * Copyright 2020-2021, 2023-2024 NXP + */ +#ifndef S32CC_MC_ME_H +#define S32CC_MC_ME_H + +#include <stdbool.h> +#include <stdint.h> + +int mc_me_enable_partition(uintptr_t mc_me, uintptr_t mc_rgm, uintptr_t rdc, + uint32_t part); +void mc_me_enable_part_cofb(uintptr_t mc_me, uint32_t partition_n, uint32_t block, + bool check_status); + +#endif /* S32CC_MC_ME_H */ diff --git a/drivers/nxp/clk/s32cc/include/s32cc-mc-rgm.h b/drivers/nxp/clk/s32cc/include/s32cc-mc-rgm.h new file mode 100644 index 00000000..d6234daf --- /dev/null +++ b/drivers/nxp/clk/s32cc/include/s32cc-mc-rgm.h @@ -0,0 +1,14 @@ +// SPDX-License-Identifier: BSD-3-Clause +/* + * Copyright 2020-2021, 2023-2024 NXP + */ +#ifndef S32CC_MC_RGM_H +#define S32CC_MC_RGM_H + +#include <stdint.h> + +void mc_rgm_periph_reset(uintptr_t rgm, uint32_t part, uint32_t value); +void mc_rgm_release_part(uintptr_t rgm, uint32_t part); +void mc_rgm_wait_part_deassert(uintptr_t rgm, uint32_t part); + +#endif /* MC_RGM_H */ diff --git a/drivers/nxp/clk/s32cc/mc_me.c b/drivers/nxp/clk/s32cc/mc_me.c new file mode 100644 index 00000000..04d04253 --- /dev/null +++ b/drivers/nxp/clk/s32cc/mc_me.c @@ -0,0 +1,173 @@ +/* + * Copyright 2024 NXP + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <errno.h> +#include <inttypes.h> +#include <stdbool.h> + +#include <common/debug.h> +#include <lib/mmio.h> +#include <lib/utils_def.h> +#include <s32cc-mc-me.h> +#include <s32cc-mc-rgm.h> + +#define MC_ME_MAX_PARTITIONS (4U) + +#define MC_ME_CTL_KEY(MC_ME) ((MC_ME) + 0x0UL) +#define MC_ME_CTL_KEY_KEY (0x5AF0U) +#define MC_ME_CTL_KEY_INVERTEDKEY (0xA50FU) + +#define MC_ME_PRTN_N(MC_ME, PART) ((MC_ME) + 0x100UL + ((PART) * 0x200UL)) +#define MC_ME_PRTN_N_PCONF(MC_ME, PART) (MC_ME_PRTN_N(MC_ME, PART)) +#define MC_ME_PRTN_N_PCE BIT_32(0) +#define MC_ME_PRTN_N_OSSE BIT_32(2) +#define MC_ME_PRTN_N_PUPD(MC_ME, PART) (MC_ME_PRTN_N(MC_ME, PART) + 0x4UL) +#define MC_ME_PRTN_N_PCUD BIT_32(0) +#define MC_ME_PRTN_N_OSSUD BIT_32(2) +#define MC_ME_PRTN_N_STAT(MC_ME, PART) (MC_ME_PRTN_N(MC_ME, PART) + 0x8UL) +#define MC_ME_PRTN_N_PCS BIT_32(0) +#define MC_ME_PRTN_N_COFB0_STAT(MC_ME, PART) \ + (MC_ME_PRTN_N(MC_ME, PART) + 0x10UL) +#define MC_ME_PRTN_N_COFB0_CLKEN(MC_ME, PART) \ + (MC_ME_PRTN_N(MC_ME, PART) + 0x30UL) +#define MC_ME_PRTN_N_REQ(PART) BIT_32(PART) + +#define RDC_RD_CTRL(RDC, PART) ((RDC) + ((PART) * 0x4UL)) +#define RDC_CTRL_UNLOCK BIT_32(31) +#define RDC_RD_INTERCONNECT_DISABLE BIT_32(3) + +#define RDC_RD_N_STATUS(RDC, PART) ((RDC) + ((PART) * 0x4UL) + 0x80UL) +#define RDC_RD_INTERCONNECT_DISABLE_STAT \ + BIT_32(4) + +static bool is_interconnect_disabled(uintptr_t rdc, uint32_t part) +{ + return ((mmio_read_32(RDC_RD_N_STATUS(rdc, part)) & + RDC_RD_INTERCONNECT_DISABLE_STAT) != 0U); +} + +static void enable_interconnect(uintptr_t rdc, uint32_t part) +{ + /* Unlock RDC register write */ + mmio_setbits_32(RDC_RD_CTRL(rdc, part), RDC_CTRL_UNLOCK); + + /* Clear corresponding RDC_RD_INTERCONNECT bit */ + mmio_clrbits_32(RDC_RD_CTRL(rdc, part), RDC_RD_INTERCONNECT_DISABLE); + + /* Wait until the interface gets enabled */ + while (is_interconnect_disabled(rdc, part)) { + } + + /* Lock RDC register write */ + mmio_clrbits_32(RDC_RD_CTRL(rdc, part), RDC_CTRL_UNLOCK); +} + +static int mc_me_check_partition_nb_valid(uint32_t part) +{ + if (part >= MC_ME_MAX_PARTITIONS) { + ERROR("Invalid partition %" PRIu32 "\n", part); + return -EINVAL; + } + + return 0; +} + +static void part_pconf_write_pce(uintptr_t mc_me, uint32_t pce_bit, + uint32_t part) +{ + mmio_clrsetbits_32(MC_ME_PRTN_N_PCONF(mc_me, part), MC_ME_PRTN_N_PCE, + pce_bit & MC_ME_PRTN_N_PCE); +} + +static void mc_me_apply_hw_changes(uintptr_t mc_me) +{ + mmio_write_32(MC_ME_CTL_KEY(mc_me), MC_ME_CTL_KEY_KEY); + mmio_write_32(MC_ME_CTL_KEY(mc_me), MC_ME_CTL_KEY_INVERTEDKEY); +} + +static void part_pupd_update_and_wait(uintptr_t mc_me, uint32_t part, + uint32_t mask) +{ + uint32_t pconf, stat; + + mmio_setbits_32(MC_ME_PRTN_N_PUPD(mc_me, part), mask); + + mc_me_apply_hw_changes(mc_me); + + /* wait for the updates to apply */ + pconf = mmio_read_32(MC_ME_PRTN_N_PCONF(mc_me, part)); + do { + stat = mmio_read_32(MC_ME_PRTN_N_STAT(mc_me, part)); + } while ((stat & mask) != (pconf & mask)); +} + +static void part_pconf_write_osse(uintptr_t mc_me, uint32_t osse_bit, + uint32_t part) +{ + mmio_clrsetbits_32(MC_ME_PRTN_N_PCONF(mc_me, part), MC_ME_PRTN_N_OSSE, + (osse_bit & MC_ME_PRTN_N_OSSE)); +} + +int mc_me_enable_partition(uintptr_t mc_me, uintptr_t mc_rgm, uintptr_t rdc, + uint32_t part) +{ + uint32_t part_stat; + int ret; + + /* Partition 0 is already enabled by BootROM */ + if (part == 0U) { + return 0; + } + + ret = mc_me_check_partition_nb_valid(part); + if (ret != 0) { + return ret; + } + + /* Enable a partition only if it's disabled */ + part_stat = mmio_read_32(MC_ME_PRTN_N_STAT(mc_me, part)); + if ((MC_ME_PRTN_N_PCS & part_stat) != 0U) { + return 0; + } + + part_pconf_write_pce(mc_me, MC_ME_PRTN_N_PCE, part); + part_pupd_update_and_wait(mc_me, part, MC_ME_PRTN_N_PCUD); + + enable_interconnect(rdc, part); + + /* Release partition reset */ + mc_rgm_release_part(mc_rgm, part); + + /* Clear OSSE bit */ + part_pconf_write_osse(mc_me, 0, part); + + part_pupd_update_and_wait(mc_me, part, MC_ME_PRTN_N_OSSUD); + + mc_rgm_wait_part_deassert(mc_rgm, part); + + return 0; +} + +void mc_me_enable_part_cofb(uintptr_t mc_me, uint32_t partition_n, uint32_t block, + bool check_status) +{ + uint32_t block_mask = MC_ME_PRTN_N_REQ(block); + uintptr_t cofb_stat_addr; + + mmio_setbits_32(MC_ME_PRTN_N_COFB0_CLKEN(mc_me, partition_n), + block_mask); + + mmio_setbits_32(MC_ME_PRTN_N_PCONF(mc_me, partition_n), + MC_ME_PRTN_N_PCE); + + part_pupd_update_and_wait(mc_me, partition_n, MC_ME_PRTN_N_PCUD); + + cofb_stat_addr = MC_ME_PRTN_N_COFB0_STAT(mc_me, partition_n); + if (check_status) { + while ((mmio_read_32(cofb_stat_addr) & block_mask) == 0U) { + } + } +} diff --git a/drivers/nxp/clk/s32cc/mc_rgm.c b/drivers/nxp/clk/s32cc/mc_rgm.c new file mode 100644 index 00000000..c66b0139 --- /dev/null +++ b/drivers/nxp/clk/s32cc/mc_rgm.c @@ -0,0 +1,84 @@ +/* + * Copyright 2023-2024 NXP + * + * SPDX-License-Identifier: BSD-3-Clause + */ +#include <lib/mmio.h> +#include <lib/utils_def.h> +#include <s32cc-mc-rgm.h> + +#define MC_RGM_PRST(RGM, PER) ((RGM) + 0x40UL + ((PER) * 0x8UL)) +#define MC_RGM_PRST_PERIPH_N_RST(PER) BIT_32(PER) +#define MC_RGM_PSTAT(RGM, PER) ((RGM) + 0x140UL + ((PER) * 0x8UL)) +#define MC_RGM_PSTAT_PERIPH(PER) BIT_32(PER) + +/* ERR051700 + * Releasing more than one Software Resettable Domain (SRD) + * from reset simultaneously, by clearing the corresponding + * peripheral MC_RGM_PRSTn[PERIPH_x_RST] reset control may + * cause a false setting of the Fault Collection and + * Control Unit (FCCU) Non-Critical Fault (NCF) flag + * corresponding to a Memory-Test-Repair (MTR) Error + */ +#if (ERRATA_S32_051700 == 1) +void mc_rgm_periph_reset(uintptr_t rgm, uint32_t part, uint32_t value) +{ + uint32_t current_bit_checked, i; + uint32_t current_regs, mask; + int bit_index; + + current_regs = mmio_read_32(MC_RGM_PRST(rgm, part)); + /* Create a mask with all changed bits */ + mask = current_regs ^ value; + + while (mask != 0U) { + bit_index = __builtin_ffs(mask); + if (bit_index < 1) { + break; + } + + i = (uint32_t)bit_index - 1U; + current_bit_checked = BIT_32(i); + + /* Check if we assert or de-assert. + * Also wait for completion. + */ + if ((value & current_bit_checked) != 0U) { + mmio_setbits_32(MC_RGM_PRST(rgm, part), + current_bit_checked); + while ((mmio_read_32(MC_RGM_PRST(rgm, part)) & + current_bit_checked) == 0U) + ; + } else { + mmio_clrbits_32(MC_RGM_PRST(rgm, part), + current_bit_checked); + while ((mmio_read_32(MC_RGM_PRST(rgm, part)) & + current_bit_checked) != 0U) + ; + } + + mask &= ~current_bit_checked; + } +} +#else /* ERRATA_S32_051700 */ +void mc_rgm_periph_reset(uintptr_t rgm, uint32_t part, uint32_t value) +{ + mmio_write_32(MC_RGM_PRST(rgm, part), value); +} +#endif /* ERRATA_S32_051700 */ + +void mc_rgm_release_part(uintptr_t rgm, uint32_t part) +{ + uint32_t reg; + + reg = mmio_read_32(MC_RGM_PRST(rgm, part)); + reg &= ~MC_RGM_PRST_PERIPH_N_RST(0); + mc_rgm_periph_reset(rgm, part, reg); +} + +void mc_rgm_wait_part_deassert(uintptr_t rgm, uint32_t part) +{ + while ((mmio_read_32(MC_RGM_PSTAT(rgm, part)) & + MC_RGM_PSTAT_PERIPH(0)) != 0U) { + } +} diff --git a/drivers/nxp/clk/s32cc/s32cc_clk.mk b/drivers/nxp/clk/s32cc/s32cc_clk.mk new file mode 100644 index 00000000..602179e3 --- /dev/null +++ b/drivers/nxp/clk/s32cc/s32cc_clk.mk @@ -0,0 +1,22 @@ +# +# Copyright 2024 NXP +# +# SPDX-License-Identifier: BSD-3-Clause +# + +PLAT_INCLUDES += \ + -I${PLAT_DRIVERS_INCLUDE_PATH}/clk/s32cc \ + -I${PLAT_DRIVERS_PATH}/clk/s32cc/include \ + +CLK_SOURCES := \ + ${PLAT_DRIVERS_PATH}/clk/s32cc/mc_rgm.c \ + ${PLAT_DRIVERS_PATH}/clk/s32cc/mc_me.c \ + ${PLAT_DRIVERS_PATH}/clk/s32cc/s32cc_clk_drv.c \ + ${PLAT_DRIVERS_PATH}/clk/s32cc/s32cc_clk_modules.c \ + ${PLAT_DRIVERS_PATH}/clk/s32cc/s32cc_clk_utils.c \ + ${PLAT_DRIVERS_PATH}/clk/s32cc/s32cc_early_clks.c \ + drivers/clk/clk.c \ + +ifeq (${BL_COMM_CLK_NEEDED},yes) +BL2_SOURCES += ${CLK_SOURCES} +endif diff --git a/drivers/nxp/clk/s32cc/s32cc_clk_drv.c b/drivers/nxp/clk/s32cc/s32cc_clk_drv.c new file mode 100644 index 00000000..9b576075 --- /dev/null +++ b/drivers/nxp/clk/s32cc/s32cc_clk_drv.c @@ -0,0 +1,1481 @@ +/* + * Copyright 2024 NXP + * + * SPDX-License-Identifier: BSD-3-Clause + */ +#include <errno.h> +#include <common/debug.h> +#include <drivers/clk.h> +#include <lib/mmio.h> +#include <s32cc-clk-ids.h> +#include <s32cc-clk-modules.h> +#include <s32cc-clk-regs.h> +#include <s32cc-clk-utils.h> +#include <s32cc-mc-me.h> + +#define MAX_STACK_DEPTH (40U) + +/* This is used for floating-point precision calculations. */ +#define FP_PRECISION (100000000UL) + +struct s32cc_clk_drv { + uintptr_t fxosc_base; + uintptr_t armpll_base; + uintptr_t periphpll_base; + uintptr_t armdfs_base; + uintptr_t cgm0_base; + uintptr_t cgm1_base; + uintptr_t cgm5_base; + uintptr_t ddrpll_base; + uintptr_t mc_me; + uintptr_t mc_rgm; + uintptr_t rdc; +}; + +static int update_stack_depth(unsigned int *depth) +{ + if (*depth == 0U) { + return -ENOMEM; + } + + (*depth)--; + return 0; +} + +static struct s32cc_clk_drv *get_drv(void) +{ + static struct s32cc_clk_drv driver = { + .fxosc_base = FXOSC_BASE_ADDR, + .armpll_base = ARMPLL_BASE_ADDR, + .periphpll_base = PERIPHPLL_BASE_ADDR, + .armdfs_base = ARM_DFS_BASE_ADDR, + .cgm0_base = CGM0_BASE_ADDR, + .cgm1_base = CGM1_BASE_ADDR, + .cgm5_base = MC_CGM5_BASE_ADDR, + .ddrpll_base = DDRPLL_BASE_ADDR, + .mc_me = MC_ME_BASE_ADDR, + .mc_rgm = MC_RGM_BASE_ADDR, + .rdc = RDC_BASE_ADDR, + }; + + return &driver; +} + +static int enable_module(struct s32cc_clk_obj *module, + const struct s32cc_clk_drv *drv, + unsigned int depth); + +static struct s32cc_clk_obj *get_clk_parent(const struct s32cc_clk_obj *module) +{ + const struct s32cc_clk *clk = s32cc_obj2clk(module); + + if (clk->module != NULL) { + return clk->module; + } + + if (clk->pclock != NULL) { + return &clk->pclock->desc; + } + + return NULL; +} + +static int get_base_addr(enum s32cc_clk_source id, const struct s32cc_clk_drv *drv, + uintptr_t *base) +{ + int ret = 0; + + switch (id) { + case S32CC_FXOSC: + *base = drv->fxosc_base; + break; + case S32CC_ARM_PLL: + *base = drv->armpll_base; + break; + case S32CC_PERIPH_PLL: + *base = drv->periphpll_base; + break; + case S32CC_DDR_PLL: + *base = drv->ddrpll_base; + break; + case S32CC_ARM_DFS: + *base = drv->armdfs_base; + break; + case S32CC_CGM0: + *base = drv->cgm0_base; + break; + case S32CC_CGM1: + *base = drv->cgm1_base; + break; + case S32CC_CGM5: + *base = drv->cgm5_base; + break; + case S32CC_FIRC: + break; + case S32CC_SIRC: + break; + default: + ret = -EINVAL; + break; + } + + if (ret != 0) { + ERROR("Unknown clock source id: %u\n", id); + } + + return ret; +} + +static void enable_fxosc(const struct s32cc_clk_drv *drv) +{ + uintptr_t fxosc_base = drv->fxosc_base; + uint32_t ctrl; + + ctrl = mmio_read_32(FXOSC_CTRL(fxosc_base)); + if ((ctrl & FXOSC_CTRL_OSCON) != U(0)) { + return; + } + + ctrl = FXOSC_CTRL_COMP_EN; + ctrl &= ~FXOSC_CTRL_OSC_BYP; + ctrl |= FXOSC_CTRL_EOCV(0x1); + ctrl |= FXOSC_CTRL_GM_SEL(0x7); + mmio_write_32(FXOSC_CTRL(fxosc_base), ctrl); + + /* Switch ON the crystal oscillator. */ + mmio_setbits_32(FXOSC_CTRL(fxosc_base), FXOSC_CTRL_OSCON); + + /* Wait until the clock is stable. */ + while ((mmio_read_32(FXOSC_STAT(fxosc_base)) & FXOSC_STAT_OSC_STAT) == U(0)) { + } +} + +static int enable_osc(struct s32cc_clk_obj *module, + const struct s32cc_clk_drv *drv, + unsigned int depth) +{ + const struct s32cc_osc *osc = s32cc_obj2osc(module); + unsigned int ldepth = depth; + int ret = 0; + + ret = update_stack_depth(&ldepth); + if (ret != 0) { + return ret; + } + + switch (osc->source) { + case S32CC_FXOSC: + enable_fxosc(drv); + break; + /* FIRC and SIRC oscillators are enabled by default */ + case S32CC_FIRC: + break; + case S32CC_SIRC: + break; + default: + ERROR("Invalid oscillator %d\n", osc->source); + ret = -EINVAL; + break; + }; + + return ret; +} + +static struct s32cc_clk_obj *get_pll_parent(const struct s32cc_clk_obj *module) +{ + const struct s32cc_pll *pll = s32cc_obj2pll(module); + + if (pll->source == NULL) { + ERROR("Failed to identify PLL's parent\n"); + } + + return pll->source; +} + +static int get_pll_mfi_mfn(unsigned long pll_vco, unsigned long ref_freq, + uint32_t *mfi, uint32_t *mfn) + +{ + unsigned long vco; + unsigned long mfn64; + + /* FRAC-N mode */ + *mfi = (uint32_t)(pll_vco / ref_freq); + + /* MFN formula : (double)(pll_vco % ref_freq) / ref_freq * 18432.0 */ + mfn64 = pll_vco % ref_freq; + mfn64 *= FP_PRECISION; + mfn64 /= ref_freq; + mfn64 *= 18432UL; + mfn64 /= FP_PRECISION; + + if (mfn64 > UINT32_MAX) { + return -EINVAL; + } + + *mfn = (uint32_t)mfn64; + + vco = ((unsigned long)*mfn * FP_PRECISION) / 18432UL; + vco += (unsigned long)*mfi * FP_PRECISION; + vco *= ref_freq; + vco /= FP_PRECISION; + + if (vco != pll_vco) { + ERROR("Failed to find MFI and MFN settings for PLL freq %lu. Nearest freq = %lu\n", + pll_vco, vco); + return -EINVAL; + } + + return 0; +} + +static struct s32cc_clkmux *get_pll_mux(const struct s32cc_pll *pll) +{ + const struct s32cc_clk_obj *source = pll->source; + const struct s32cc_clk *clk; + + if (source == NULL) { + ERROR("Failed to identify PLL's parent\n"); + return NULL; + } + + if (source->type != s32cc_clk_t) { + ERROR("The parent of the PLL isn't a clock\n"); + return NULL; + } + + clk = s32cc_obj2clk(source); + + if (clk->module == NULL) { + ERROR("The clock isn't connected to a module\n"); + return NULL; + } + + source = clk->module; + + if ((source->type != s32cc_clkmux_t) && + (source->type != s32cc_shared_clkmux_t)) { + ERROR("The parent of the PLL isn't a MUX\n"); + return NULL; + } + + return s32cc_obj2clkmux(source); +} + +static void disable_odiv(uintptr_t pll_addr, uint32_t div_index) +{ + mmio_clrbits_32(PLLDIG_PLLODIV(pll_addr, div_index), PLLDIG_PLLODIV_DE); +} + +static void enable_odiv(uintptr_t pll_addr, uint32_t div_index) +{ + mmio_setbits_32(PLLDIG_PLLODIV(pll_addr, div_index), PLLDIG_PLLODIV_DE); +} + +static void disable_odivs(uintptr_t pll_addr, uint32_t ndivs) +{ + uint32_t i; + + for (i = 0; i < ndivs; i++) { + disable_odiv(pll_addr, i); + } +} + +static void enable_pll_hw(uintptr_t pll_addr) +{ + /* Enable the PLL. */ + mmio_write_32(PLLDIG_PLLCR(pll_addr), 0x0); + + /* Poll until PLL acquires lock. */ + while ((mmio_read_32(PLLDIG_PLLSR(pll_addr)) & PLLDIG_PLLSR_LOCK) == 0U) { + } +} + +static void disable_pll_hw(uintptr_t pll_addr) +{ + mmio_write_32(PLLDIG_PLLCR(pll_addr), PLLDIG_PLLCR_PLLPD); +} + +static int program_pll(const struct s32cc_pll *pll, uintptr_t pll_addr, + const struct s32cc_clk_drv *drv, uint32_t sclk_id, + unsigned long sclk_freq) +{ + uint32_t rdiv = 1, mfi, mfn; + int ret; + + ret = get_pll_mfi_mfn(pll->vco_freq, sclk_freq, &mfi, &mfn); + if (ret != 0) { + return -EINVAL; + } + + /* Disable ODIVs*/ + disable_odivs(pll_addr, pll->ndividers); + + /* Disable PLL */ + disable_pll_hw(pll_addr); + + /* Program PLLCLKMUX */ + mmio_write_32(PLLDIG_PLLCLKMUX(pll_addr), sclk_id); + + /* Program VCO */ + mmio_clrsetbits_32(PLLDIG_PLLDV(pll_addr), + PLLDIG_PLLDV_RDIV_MASK | PLLDIG_PLLDV_MFI_MASK, + PLLDIG_PLLDV_RDIV_SET(rdiv) | PLLDIG_PLLDV_MFI(mfi)); + + mmio_write_32(PLLDIG_PLLFD(pll_addr), + PLLDIG_PLLFD_MFN_SET(mfn) | PLLDIG_PLLFD_SMDEN); + + enable_pll_hw(pll_addr); + + return ret; +} + +static int enable_pll(struct s32cc_clk_obj *module, + const struct s32cc_clk_drv *drv, + unsigned int depth) +{ + const struct s32cc_pll *pll = s32cc_obj2pll(module); + const struct s32cc_clkmux *mux; + uintptr_t pll_addr = UL(0x0); + unsigned int ldepth = depth; + unsigned long sclk_freq; + uint32_t sclk_id; + int ret; + + ret = update_stack_depth(&ldepth); + if (ret != 0) { + return ret; + } + + mux = get_pll_mux(pll); + if (mux == NULL) { + return -EINVAL; + } + + if (pll->instance != mux->module) { + ERROR("MUX type is not in sync with PLL ID\n"); + return -EINVAL; + } + + ret = get_base_addr(pll->instance, drv, &pll_addr); + if (ret != 0) { + ERROR("Failed to detect PLL instance\n"); + return ret; + } + + switch (mux->source_id) { + case S32CC_CLK_FIRC: + sclk_freq = 48U * MHZ; + sclk_id = 0; + break; + case S32CC_CLK_FXOSC: + sclk_freq = 40U * MHZ; + sclk_id = 1; + break; + default: + ERROR("Invalid source selection for PLL 0x%lx\n", + pll_addr); + return -EINVAL; + }; + + return program_pll(pll, pll_addr, drv, sclk_id, sclk_freq); +} + +static inline struct s32cc_pll *get_div_pll(const struct s32cc_pll_out_div *pdiv) +{ + const struct s32cc_clk_obj *parent; + + parent = pdiv->parent; + if (parent == NULL) { + ERROR("Failed to identify PLL divider's parent\n"); + return NULL; + } + + if (parent->type != s32cc_pll_t) { + ERROR("The parent of the divider is not a PLL instance\n"); + return NULL; + } + + return s32cc_obj2pll(parent); +} + +static void config_pll_out_div(uintptr_t pll_addr, uint32_t div_index, uint32_t dc) +{ + uint32_t pllodiv; + uint32_t pdiv; + + pllodiv = mmio_read_32(PLLDIG_PLLODIV(pll_addr, div_index)); + pdiv = PLLDIG_PLLODIV_DIV(pllodiv); + + if (((pdiv + 1U) == dc) && ((pllodiv & PLLDIG_PLLODIV_DE) != 0U)) { + return; + } + + if ((pllodiv & PLLDIG_PLLODIV_DE) != 0U) { + disable_odiv(pll_addr, div_index); + } + + pllodiv = PLLDIG_PLLODIV_DIV_SET(dc - 1U); + mmio_write_32(PLLDIG_PLLODIV(pll_addr, div_index), pllodiv); + + enable_odiv(pll_addr, div_index); +} + +static struct s32cc_clk_obj *get_pll_div_parent(const struct s32cc_clk_obj *module) +{ + const struct s32cc_pll_out_div *pdiv = s32cc_obj2plldiv(module); + + if (pdiv->parent == NULL) { + ERROR("Failed to identify PLL DIV's parent\n"); + } + + return pdiv->parent; +} + +static int enable_pll_div(struct s32cc_clk_obj *module, + const struct s32cc_clk_drv *drv, + unsigned int depth) +{ + const struct s32cc_pll_out_div *pdiv = s32cc_obj2plldiv(module); + uintptr_t pll_addr = 0x0ULL; + unsigned int ldepth = depth; + const struct s32cc_pll *pll; + uint32_t dc; + int ret; + + ret = update_stack_depth(&ldepth); + if (ret != 0) { + return ret; + } + + pll = get_div_pll(pdiv); + if (pll == NULL) { + ERROR("The parent of the PLL DIV is invalid\n"); + return 0; + } + + ret = get_base_addr(pll->instance, drv, &pll_addr); + if (ret != 0) { + ERROR("Failed to detect PLL instance\n"); + return -EINVAL; + } + + dc = (uint32_t)(pll->vco_freq / pdiv->freq); + + config_pll_out_div(pll_addr, pdiv->index, dc); + + return 0; +} + +static int cgm_mux_clk_config(uintptr_t cgm_addr, uint32_t mux, uint32_t source, + bool safe_clk) +{ + uint32_t css, csc; + + css = mmio_read_32(CGM_MUXn_CSS(cgm_addr, mux)); + + /* Already configured */ + if ((MC_CGM_MUXn_CSS_SELSTAT(css) == source) && + (MC_CGM_MUXn_CSS_SWTRG(css) == MC_CGM_MUXn_CSS_SWTRG_SUCCESS) && + ((css & MC_CGM_MUXn_CSS_SWIP) == 0U) && !safe_clk) { + return 0; + } + + /* Ongoing clock switch? */ + while ((mmio_read_32(CGM_MUXn_CSS(cgm_addr, mux)) & + MC_CGM_MUXn_CSS_SWIP) != 0U) { + } + + csc = mmio_read_32(CGM_MUXn_CSC(cgm_addr, mux)); + + /* Clear previous source. */ + csc &= ~(MC_CGM_MUXn_CSC_SELCTL_MASK); + + if (!safe_clk) { + /* Select the clock source and trigger the clock switch. */ + csc |= MC_CGM_MUXn_CSC_SELCTL(source) | MC_CGM_MUXn_CSC_CLK_SW; + } else { + /* Switch to safe clock */ + csc |= MC_CGM_MUXn_CSC_SAFE_SW; + } + + mmio_write_32(CGM_MUXn_CSC(cgm_addr, mux), csc); + + /* Wait for configuration bit to auto-clear. */ + while ((mmio_read_32(CGM_MUXn_CSC(cgm_addr, mux)) & + MC_CGM_MUXn_CSC_CLK_SW) != 0U) { + } + + /* Is the clock switch completed? */ + while ((mmio_read_32(CGM_MUXn_CSS(cgm_addr, mux)) & + MC_CGM_MUXn_CSS_SWIP) != 0U) { + } + + /* + * Check if the switch succeeded. + * Check switch trigger cause and the source. + */ + css = mmio_read_32(CGM_MUXn_CSS(cgm_addr, mux)); + if (!safe_clk) { + if ((MC_CGM_MUXn_CSS_SWTRG(css) == MC_CGM_MUXn_CSS_SWTRG_SUCCESS) && + (MC_CGM_MUXn_CSS_SELSTAT(css) == source)) { + return 0; + } + + ERROR("Failed to change the source of mux %" PRIu32 " to %" PRIu32 " (CGM=%lu)\n", + mux, source, cgm_addr); + } else { + if (((MC_CGM_MUXn_CSS_SWTRG(css) == MC_CGM_MUXn_CSS_SWTRG_SAFE_CLK) || + (MC_CGM_MUXn_CSS_SWTRG(css) == MC_CGM_MUXn_CSS_SWTRG_SAFE_CLK_INACTIVE)) && + ((MC_CGM_MUXn_CSS_SAFE_SW & css) != 0U)) { + return 0; + } + + ERROR("The switch of mux %" PRIu32 " (CGM=%lu) to safe clock failed\n", + mux, cgm_addr); + } + + return -EINVAL; +} + +static int enable_cgm_mux(const struct s32cc_clkmux *mux, + const struct s32cc_clk_drv *drv) +{ + uintptr_t cgm_addr = UL(0x0); + uint32_t mux_hw_clk; + int ret; + + ret = get_base_addr(mux->module, drv, &cgm_addr); + if (ret != 0) { + return ret; + } + + mux_hw_clk = (uint32_t)S32CC_CLK_ID(mux->source_id); + + return cgm_mux_clk_config(cgm_addr, mux->index, + mux_hw_clk, false); +} + +static struct s32cc_clk_obj *get_mux_parent(const struct s32cc_clk_obj *module) +{ + const struct s32cc_clkmux *mux = s32cc_obj2clkmux(module); + struct s32cc_clk *clk; + + if (mux == NULL) { + return NULL; + } + + clk = s32cc_get_arch_clk(mux->source_id); + if (clk == NULL) { + ERROR("Invalid parent (%lu) for mux %" PRIu8 "\n", + mux->source_id, mux->index); + return NULL; + } + + return &clk->desc; +} + +static int enable_mux(struct s32cc_clk_obj *module, + const struct s32cc_clk_drv *drv, + unsigned int depth) +{ + const struct s32cc_clkmux *mux = s32cc_obj2clkmux(module); + unsigned int ldepth = depth; + const struct s32cc_clk *clk; + int ret = 0; + + ret = update_stack_depth(&ldepth); + if (ret != 0) { + return ret; + } + + if (mux == NULL) { + return -EINVAL; + } + + clk = s32cc_get_arch_clk(mux->source_id); + if (clk == NULL) { + ERROR("Invalid parent (%lu) for mux %" PRIu8 "\n", + mux->source_id, mux->index); + return -EINVAL; + } + + switch (mux->module) { + /* PLL mux will be enabled by PLL setup */ + case S32CC_ARM_PLL: + case S32CC_PERIPH_PLL: + case S32CC_DDR_PLL: + break; + case S32CC_CGM1: + ret = enable_cgm_mux(mux, drv); + break; + case S32CC_CGM0: + ret = enable_cgm_mux(mux, drv); + break; + case S32CC_CGM5: + ret = enable_cgm_mux(mux, drv); + break; + default: + ERROR("Unknown mux parent type: %d\n", mux->module); + ret = -EINVAL; + break; + }; + + return ret; +} + +static struct s32cc_clk_obj *get_dfs_parent(const struct s32cc_clk_obj *module) +{ + const struct s32cc_dfs *dfs = s32cc_obj2dfs(module); + + if (dfs->parent == NULL) { + ERROR("Failed to identify DFS's parent\n"); + } + + return dfs->parent; +} + +static int enable_dfs(struct s32cc_clk_obj *module, + const struct s32cc_clk_drv *drv, + unsigned int depth) +{ + unsigned int ldepth = depth; + int ret = 0; + + ret = update_stack_depth(&ldepth); + if (ret != 0) { + return ret; + } + + return 0; +} + +static struct s32cc_dfs *get_div_dfs(const struct s32cc_dfs_div *dfs_div) +{ + const struct s32cc_clk_obj *parent = dfs_div->parent; + + if (parent->type != s32cc_dfs_t) { + ERROR("DFS DIV doesn't have a DFS as parent\n"); + return NULL; + } + + return s32cc_obj2dfs(parent); +} + +static struct s32cc_pll *dfsdiv2pll(const struct s32cc_dfs_div *dfs_div) +{ + const struct s32cc_clk_obj *parent; + const struct s32cc_dfs *dfs; + + dfs = get_div_dfs(dfs_div); + if (dfs == NULL) { + return NULL; + } + + parent = dfs->parent; + if (parent->type != s32cc_pll_t) { + return NULL; + } + + return s32cc_obj2pll(parent); +} + +static int get_dfs_mfi_mfn(unsigned long dfs_freq, const struct s32cc_dfs_div *dfs_div, + uint32_t *mfi, uint32_t *mfn) +{ + uint64_t factor64, tmp64, ofreq; + uint32_t factor32; + + unsigned long in = dfs_freq; + unsigned long out = dfs_div->freq; + + /** + * factor = (IN / OUT) / 2 + * MFI = integer(factor) + * MFN = (factor - MFI) * 36 + */ + factor64 = ((((uint64_t)in) * FP_PRECISION) / ((uint64_t)out)) / 2ULL; + tmp64 = factor64 / FP_PRECISION; + if (tmp64 > UINT32_MAX) { + return -EINVAL; + } + + factor32 = (uint32_t)tmp64; + *mfi = factor32; + + tmp64 = ((factor64 - ((uint64_t)*mfi * FP_PRECISION)) * 36UL) / FP_PRECISION; + if (tmp64 > UINT32_MAX) { + return -EINVAL; + } + + *mfn = (uint32_t)tmp64; + + /* div_freq = in / (2 * (*mfi + *mfn / 36.0)) */ + factor64 = (((uint64_t)*mfn) * FP_PRECISION) / 36ULL; + factor64 += ((uint64_t)*mfi) * FP_PRECISION; + factor64 *= 2ULL; + ofreq = (((uint64_t)in) * FP_PRECISION) / factor64; + + if (ofreq != dfs_div->freq) { + ERROR("Failed to find MFI and MFN settings for DFS DIV freq %lu\n", + dfs_div->freq); + ERROR("Nearest freq = %" PRIx64 "\n", ofreq); + return -EINVAL; + } + + return 0; +} + +static int init_dfs_port(uintptr_t dfs_addr, uint32_t port, + uint32_t mfi, uint32_t mfn) +{ + uint32_t portsr, portolsr; + uint32_t mask, old_mfi, old_mfn; + uint32_t dvport; + bool init_dfs; + + dvport = mmio_read_32(DFS_DVPORTn(dfs_addr, port)); + + old_mfi = DFS_DVPORTn_MFI(dvport); + old_mfn = DFS_DVPORTn_MFN(dvport); + + portsr = mmio_read_32(DFS_PORTSR(dfs_addr)); + portolsr = mmio_read_32(DFS_PORTOLSR(dfs_addr)); + + /* Skip configuration if it's not needed */ + if (((portsr & BIT_32(port)) != 0U) && + ((portolsr & BIT_32(port)) == 0U) && + (mfi == old_mfi) && (mfn == old_mfn)) { + return 0; + } + + init_dfs = (portsr == 0U); + + if (init_dfs) { + mask = DFS_PORTRESET_MASK; + } else { + mask = DFS_PORTRESET_SET(BIT_32(port)); + } + + mmio_write_32(DFS_PORTOLSR(dfs_addr), mask); + mmio_write_32(DFS_PORTRESET(dfs_addr), mask); + + while ((mmio_read_32(DFS_PORTSR(dfs_addr)) & mask) != 0U) { + } + + if (init_dfs) { + mmio_write_32(DFS_CTL(dfs_addr), DFS_CTL_RESET); + } + + mmio_write_32(DFS_DVPORTn(dfs_addr, port), + DFS_DVPORTn_MFI_SET(mfi) | DFS_DVPORTn_MFN_SET(mfn)); + + if (init_dfs) { + /* DFS clk enable programming */ + mmio_clrbits_32(DFS_CTL(dfs_addr), DFS_CTL_RESET); + } + + mmio_clrbits_32(DFS_PORTRESET(dfs_addr), BIT_32(port)); + + while ((mmio_read_32(DFS_PORTSR(dfs_addr)) & BIT_32(port)) != BIT_32(port)) { + } + + portolsr = mmio_read_32(DFS_PORTOLSR(dfs_addr)); + if ((portolsr & DFS_PORTOLSR_LOL(port)) != 0U) { + ERROR("Failed to lock DFS divider\n"); + return -EINVAL; + } + + return 0; +} + +static struct s32cc_clk_obj * +get_dfs_div_parent(const struct s32cc_clk_obj *module) +{ + const struct s32cc_dfs_div *dfs_div = s32cc_obj2dfsdiv(module); + + if (dfs_div->parent == NULL) { + ERROR("Failed to identify DFS divider's parent\n"); + } + + return dfs_div->parent; +} + +static int enable_dfs_div(struct s32cc_clk_obj *module, + const struct s32cc_clk_drv *drv, + unsigned int depth) +{ + const struct s32cc_dfs_div *dfs_div = s32cc_obj2dfsdiv(module); + unsigned int ldepth = depth; + const struct s32cc_pll *pll; + const struct s32cc_dfs *dfs; + uintptr_t dfs_addr = 0UL; + uint32_t mfi, mfn; + int ret = 0; + + ret = update_stack_depth(&ldepth); + if (ret != 0) { + return ret; + } + + dfs = get_div_dfs(dfs_div); + if (dfs == NULL) { + return -EINVAL; + } + + pll = dfsdiv2pll(dfs_div); + if (pll == NULL) { + ERROR("Failed to identify DFS divider's parent\n"); + return -EINVAL; + } + + ret = get_base_addr(dfs->instance, drv, &dfs_addr); + if ((ret != 0) || (dfs_addr == 0UL)) { + return -EINVAL; + } + + ret = get_dfs_mfi_mfn(pll->vco_freq, dfs_div, &mfi, &mfn); + if (ret != 0) { + return -EINVAL; + } + + return init_dfs_port(dfs_addr, dfs_div->index, mfi, mfn); +} + +typedef int (*enable_clk_t)(struct s32cc_clk_obj *module, + const struct s32cc_clk_drv *drv, + unsigned int depth); + +static int enable_part(struct s32cc_clk_obj *module, + const struct s32cc_clk_drv *drv, + unsigned int depth) +{ + const struct s32cc_part *part = s32cc_obj2part(module); + uint32_t part_no = part->partition_id; + + if ((drv->mc_me == 0UL) || (drv->mc_rgm == 0UL) || (drv->rdc == 0UL)) { + return -EINVAL; + } + + return mc_me_enable_partition(drv->mc_me, drv->mc_rgm, drv->rdc, part_no); +} + +static int enable_part_block(struct s32cc_clk_obj *module, + const struct s32cc_clk_drv *drv, + unsigned int depth) +{ + const struct s32cc_part_block *block = s32cc_obj2partblock(module); + const struct s32cc_part *part = block->part; + uint32_t part_no = part->partition_id; + unsigned int ldepth = depth; + uint32_t cofb; + int ret; + + ret = update_stack_depth(&ldepth); + if (ret != 0) { + return ret; + } + + if ((block->block >= s32cc_part_block0) && + (block->block <= s32cc_part_block15)) { + cofb = (uint32_t)block->block - (uint32_t)s32cc_part_block0; + mc_me_enable_part_cofb(drv->mc_me, part_no, cofb, block->status); + } else { + ERROR("Unknown partition block type: %d\n", block->block); + return -EINVAL; + } + + return 0; +} + +static struct s32cc_clk_obj * +get_part_block_parent(const struct s32cc_clk_obj *module) +{ + const struct s32cc_part_block *block = s32cc_obj2partblock(module); + + return &block->part->desc; +} + +static int enable_module_with_refcount(struct s32cc_clk_obj *module, + const struct s32cc_clk_drv *drv, + unsigned int depth); + +static int enable_part_block_link(struct s32cc_clk_obj *module, + const struct s32cc_clk_drv *drv, + unsigned int depth) +{ + const struct s32cc_part_block_link *link = s32cc_obj2partblocklink(module); + struct s32cc_part_block *block = link->block; + unsigned int ldepth = depth; + int ret; + + ret = update_stack_depth(&ldepth); + if (ret != 0) { + return ret; + } + + /* Move the enablement algorithm to partition tree */ + return enable_module_with_refcount(&block->desc, drv, ldepth); +} + +static struct s32cc_clk_obj * +get_part_block_link_parent(const struct s32cc_clk_obj *module) +{ + const struct s32cc_part_block_link *link = s32cc_obj2partblocklink(module); + + return link->parent; +} + +static int no_enable(struct s32cc_clk_obj *module, + const struct s32cc_clk_drv *drv, + unsigned int depth) +{ + return 0; +} + +static int exec_cb_with_refcount(enable_clk_t en_cb, struct s32cc_clk_obj *mod, + const struct s32cc_clk_drv *drv, bool leaf_node, + unsigned int depth) +{ + unsigned int ldepth = depth; + int ret = 0; + + if (mod == NULL) { + return 0; + } + + ret = update_stack_depth(&ldepth); + if (ret != 0) { + return ret; + } + + /* Refcount will be updated as part of the recursivity */ + if (leaf_node) { + return en_cb(mod, drv, ldepth); + } + + if (mod->refcount == 0U) { + ret = en_cb(mod, drv, ldepth); + } + + if (ret == 0) { + mod->refcount++; + } + + return ret; +} + +static struct s32cc_clk_obj *get_module_parent(const struct s32cc_clk_obj *module); + +static int enable_module(struct s32cc_clk_obj *module, + const struct s32cc_clk_drv *drv, + unsigned int depth) +{ + struct s32cc_clk_obj *parent = get_module_parent(module); + static const enable_clk_t enable_clbs[12] = { + [s32cc_clk_t] = no_enable, + [s32cc_osc_t] = enable_osc, + [s32cc_pll_t] = enable_pll, + [s32cc_pll_out_div_t] = enable_pll_div, + [s32cc_clkmux_t] = enable_mux, + [s32cc_shared_clkmux_t] = enable_mux, + [s32cc_dfs_t] = enable_dfs, + [s32cc_dfs_div_t] = enable_dfs_div, + [s32cc_part_t] = enable_part, + [s32cc_part_block_t] = enable_part_block, + [s32cc_part_block_link_t] = enable_part_block_link, + }; + unsigned int ldepth = depth; + uint32_t index; + int ret = 0; + + ret = update_stack_depth(&ldepth); + if (ret != 0) { + return ret; + } + + if (drv == NULL) { + return -EINVAL; + } + + index = (uint32_t)module->type; + + if (index >= ARRAY_SIZE(enable_clbs)) { + ERROR("Undefined module type: %d\n", module->type); + return -EINVAL; + } + + if (enable_clbs[index] == NULL) { + ERROR("Undefined callback for the clock type: %d\n", + module->type); + return -EINVAL; + } + + parent = get_module_parent(module); + + ret = exec_cb_with_refcount(enable_module, parent, drv, + false, ldepth); + if (ret != 0) { + return ret; + } + + ret = exec_cb_with_refcount(enable_clbs[index], module, drv, + true, ldepth); + if (ret != 0) { + return ret; + } + + return ret; +} + +static int enable_module_with_refcount(struct s32cc_clk_obj *module, + const struct s32cc_clk_drv *drv, + unsigned int depth) +{ + return exec_cb_with_refcount(enable_module, module, drv, false, depth); +} + +static int s32cc_clk_enable(unsigned long id) +{ + const struct s32cc_clk_drv *drv = get_drv(); + unsigned int depth = MAX_STACK_DEPTH; + struct s32cc_clk *clk; + + clk = s32cc_get_arch_clk(id); + if (clk == NULL) { + return -EINVAL; + } + + return enable_module_with_refcount(&clk->desc, drv, depth); +} + +static void s32cc_clk_disable(unsigned long id) +{ +} + +static bool s32cc_clk_is_enabled(unsigned long id) +{ + return false; +} + +static unsigned long s32cc_clk_get_rate(unsigned long id) +{ + return 0; +} + +static int set_module_rate(const struct s32cc_clk_obj *module, + unsigned long rate, unsigned long *orate, + unsigned int *depth); + +static int set_osc_freq(const struct s32cc_clk_obj *module, unsigned long rate, + unsigned long *orate, unsigned int *depth) +{ + struct s32cc_osc *osc = s32cc_obj2osc(module); + int ret; + + ret = update_stack_depth(depth); + if (ret != 0) { + return ret; + } + + if ((osc->freq != 0UL) && (rate != osc->freq)) { + ERROR("Already initialized oscillator. freq = %lu\n", + osc->freq); + return -EINVAL; + } + + osc->freq = rate; + *orate = osc->freq; + + return 0; +} + +static int set_clk_freq(const struct s32cc_clk_obj *module, unsigned long rate, + unsigned long *orate, unsigned int *depth) +{ + const struct s32cc_clk *clk = s32cc_obj2clk(module); + int ret; + + ret = update_stack_depth(depth); + if (ret != 0) { + return ret; + } + + if ((clk->min_freq != 0UL) && (clk->max_freq != 0UL) && + ((rate < clk->min_freq) || (rate > clk->max_freq))) { + ERROR("%lu frequency is out of the allowed range: [%lu:%lu]\n", + rate, clk->min_freq, clk->max_freq); + return -EINVAL; + } + + if (clk->module != NULL) { + return set_module_rate(clk->module, rate, orate, depth); + } + + if (clk->pclock != NULL) { + return set_clk_freq(&clk->pclock->desc, rate, orate, depth); + } + + return -EINVAL; +} + +static int set_pll_freq(const struct s32cc_clk_obj *module, unsigned long rate, + unsigned long *orate, unsigned int *depth) +{ + struct s32cc_pll *pll = s32cc_obj2pll(module); + int ret; + + ret = update_stack_depth(depth); + if (ret != 0) { + return ret; + } + + if ((pll->vco_freq != 0UL) && (pll->vco_freq != rate)) { + ERROR("PLL frequency was already set\n"); + return -EINVAL; + } + + pll->vco_freq = rate; + *orate = pll->vco_freq; + + return 0; +} + +static int set_pll_div_freq(const struct s32cc_clk_obj *module, unsigned long rate, + unsigned long *orate, unsigned int *depth) +{ + struct s32cc_pll_out_div *pdiv = s32cc_obj2plldiv(module); + const struct s32cc_pll *pll; + unsigned long prate, dc; + int ret; + + ret = update_stack_depth(depth); + if (ret != 0) { + return ret; + } + + if (pdiv->parent == NULL) { + ERROR("Failed to identify PLL divider's parent\n"); + return -EINVAL; + } + + pll = s32cc_obj2pll(pdiv->parent); + if (pll == NULL) { + ERROR("The parent of the PLL DIV is invalid\n"); + return -EINVAL; + } + + prate = pll->vco_freq; + + /** + * The PLL is not initialized yet, so let's take a risk + * and accept the proposed rate. + */ + if (prate == 0UL) { + pdiv->freq = rate; + *orate = rate; + return 0; + } + + /* Decline in case the rate cannot fit PLL's requirements. */ + dc = prate / rate; + if ((prate / dc) != rate) { + return -EINVAL; + } + + pdiv->freq = rate; + *orate = pdiv->freq; + + return 0; +} + +static int set_fixed_div_freq(const struct s32cc_clk_obj *module, unsigned long rate, + unsigned long *orate, unsigned int *depth) +{ + const struct s32cc_fixed_div *fdiv = s32cc_obj2fixeddiv(module); + int ret; + + ret = update_stack_depth(depth); + if (ret != 0) { + return ret; + } + + if (fdiv->parent == NULL) { + ERROR("The divider doesn't have a valid parent\b"); + return -EINVAL; + } + + ret = set_module_rate(fdiv->parent, rate * fdiv->rate_div, orate, depth); + + /* Update the output rate based on the parent's rate */ + *orate /= fdiv->rate_div; + + return ret; +} + +static int set_mux_freq(const struct s32cc_clk_obj *module, unsigned long rate, + unsigned long *orate, unsigned int *depth) +{ + const struct s32cc_clkmux *mux = s32cc_obj2clkmux(module); + const struct s32cc_clk *clk = s32cc_get_arch_clk(mux->source_id); + int ret; + + ret = update_stack_depth(depth); + if (ret != 0) { + return ret; + } + + if (clk == NULL) { + ERROR("Mux (id:%" PRIu8 ") without a valid source (%lu)\n", + mux->index, mux->source_id); + return -EINVAL; + } + + return set_module_rate(&clk->desc, rate, orate, depth); +} + +static int set_dfs_div_freq(const struct s32cc_clk_obj *module, unsigned long rate, + unsigned long *orate, unsigned int *depth) +{ + struct s32cc_dfs_div *dfs_div = s32cc_obj2dfsdiv(module); + const struct s32cc_dfs *dfs; + int ret; + + ret = update_stack_depth(depth); + if (ret != 0) { + return ret; + } + + if (dfs_div->parent == NULL) { + ERROR("Failed to identify DFS divider's parent\n"); + return -EINVAL; + } + + /* Sanity check */ + dfs = s32cc_obj2dfs(dfs_div->parent); + if (dfs->parent == NULL) { + ERROR("Failed to identify DFS's parent\n"); + return -EINVAL; + } + + if ((dfs_div->freq != 0U) && (dfs_div->freq != rate)) { + ERROR("DFS DIV frequency was already set to %lu\n", + dfs_div->freq); + return -EINVAL; + } + + dfs_div->freq = rate; + *orate = rate; + + return ret; +} + +static int set_module_rate(const struct s32cc_clk_obj *module, + unsigned long rate, unsigned long *orate, + unsigned int *depth) +{ + int ret = 0; + + ret = update_stack_depth(depth); + if (ret != 0) { + return ret; + } + + ret = -EINVAL; + + switch (module->type) { + case s32cc_clk_t: + ret = set_clk_freq(module, rate, orate, depth); + break; + case s32cc_osc_t: + ret = set_osc_freq(module, rate, orate, depth); + break; + case s32cc_pll_t: + ret = set_pll_freq(module, rate, orate, depth); + break; + case s32cc_pll_out_div_t: + ret = set_pll_div_freq(module, rate, orate, depth); + break; + case s32cc_fixed_div_t: + ret = set_fixed_div_freq(module, rate, orate, depth); + break; + case s32cc_clkmux_t: + ret = set_mux_freq(module, rate, orate, depth); + break; + case s32cc_shared_clkmux_t: + ret = set_mux_freq(module, rate, orate, depth); + break; + case s32cc_dfs_t: + ERROR("Setting the frequency of a DFS is not allowed!"); + break; + case s32cc_dfs_div_t: + ret = set_dfs_div_freq(module, rate, orate, depth); + break; + default: + break; + } + + return ret; +} + +static int s32cc_clk_set_rate(unsigned long id, unsigned long rate, + unsigned long *orate) +{ + unsigned int depth = MAX_STACK_DEPTH; + const struct s32cc_clk *clk; + int ret; + + clk = s32cc_get_arch_clk(id); + if (clk == NULL) { + return -EINVAL; + } + + ret = set_module_rate(&clk->desc, rate, orate, &depth); + if (ret != 0) { + ERROR("Failed to set frequency (%lu MHz) for clock %lu\n", + rate, id); + } + + return ret; +} + +static struct s32cc_clk_obj *get_no_parent(const struct s32cc_clk_obj *module) +{ + return NULL; +} + +typedef struct s32cc_clk_obj *(*get_parent_clb_t)(const struct s32cc_clk_obj *clk_obj); + +static struct s32cc_clk_obj *get_module_parent(const struct s32cc_clk_obj *module) +{ + static const get_parent_clb_t parents_clbs[12] = { + [s32cc_clk_t] = get_clk_parent, + [s32cc_osc_t] = get_no_parent, + [s32cc_pll_t] = get_pll_parent, + [s32cc_pll_out_div_t] = get_pll_div_parent, + [s32cc_clkmux_t] = get_mux_parent, + [s32cc_shared_clkmux_t] = get_mux_parent, + [s32cc_dfs_t] = get_dfs_parent, + [s32cc_dfs_div_t] = get_dfs_div_parent, + [s32cc_part_t] = get_no_parent, + [s32cc_part_block_t] = get_part_block_parent, + [s32cc_part_block_link_t] = get_part_block_link_parent, + }; + uint32_t index; + + if (module == NULL) { + return NULL; + } + + index = (uint32_t)module->type; + + if (index >= ARRAY_SIZE(parents_clbs)) { + ERROR("Undefined module type: %d\n", module->type); + return NULL; + } + + if (parents_clbs[index] == NULL) { + ERROR("Undefined parent getter for type: %d\n", module->type); + return NULL; + } + + return parents_clbs[index](module); +} + +static int s32cc_clk_get_parent(unsigned long id) +{ + struct s32cc_clk *parent_clk; + const struct s32cc_clk_obj *parent; + const struct s32cc_clk *clk; + unsigned long parent_id; + int ret; + + clk = s32cc_get_arch_clk(id); + if (clk == NULL) { + return -EINVAL; + } + + parent = get_module_parent(clk->module); + if (parent == NULL) { + return -EINVAL; + } + + parent_clk = s32cc_obj2clk(parent); + if (parent_clk == NULL) { + return -EINVAL; + } + + ret = s32cc_get_clk_id(parent_clk, &parent_id); + if (ret != 0) { + return ret; + } + + if (parent_id > (unsigned long)INT_MAX) { + return -E2BIG; + } + + return (int)parent_id; +} + +static int s32cc_clk_set_parent(unsigned long id, unsigned long parent_id) +{ + const struct s32cc_clk *parent; + const struct s32cc_clk *clk; + bool valid_source = false; + struct s32cc_clkmux *mux; + uint8_t i; + + clk = s32cc_get_arch_clk(id); + if (clk == NULL) { + return -EINVAL; + } + + parent = s32cc_get_arch_clk(parent_id); + if (parent == NULL) { + return -EINVAL; + } + + if (!is_s32cc_clk_mux(clk)) { + ERROR("Clock %lu is not a mux\n", id); + return -EINVAL; + } + + mux = s32cc_clk2mux(clk); + if (mux == NULL) { + ERROR("Failed to cast clock %lu to clock mux\n", id); + return -EINVAL; + } + + for (i = 0; i < mux->nclks; i++) { + if (mux->clkids[i] == parent_id) { + valid_source = true; + break; + } + } + + if (!valid_source) { + ERROR("Clock %lu is not a valid clock for mux %lu\n", + parent_id, id); + return -EINVAL; + } + + mux->source_id = parent_id; + + return 0; +} + +void s32cc_clk_register_drv(void) +{ + static const struct clk_ops s32cc_clk_ops = { + .enable = s32cc_clk_enable, + .disable = s32cc_clk_disable, + .is_enabled = s32cc_clk_is_enabled, + .get_rate = s32cc_clk_get_rate, + .set_rate = s32cc_clk_set_rate, + .get_parent = s32cc_clk_get_parent, + .set_parent = s32cc_clk_set_parent, + }; + + clk_register(&s32cc_clk_ops); +} + diff --git a/drivers/nxp/clk/s32cc/s32cc_clk_modules.c b/drivers/nxp/clk/s32cc/s32cc_clk_modules.c new file mode 100644 index 00000000..71055abc --- /dev/null +++ b/drivers/nxp/clk/s32cc/s32cc_clk_modules.c @@ -0,0 +1,257 @@ +/* + * Copyright 2020-2024 NXP + * + * SPDX-License-Identifier: BSD-3-Clause + */ +#include <s32cc-clk-ids.h> +#include <s32cc-clk-modules.h> +#include <s32cc-clk-utils.h> + +#define S32CC_A53_MIN_FREQ (48UL * MHZ) +#define S32CC_A53_MAX_FREQ (1000UL * MHZ) + +/* Partitions */ +static struct s32cc_part part0 = S32CC_PART(0); + +/* Oscillators */ +static struct s32cc_osc fxosc = + S32CC_OSC_INIT(S32CC_FXOSC); +static struct s32cc_clk fxosc_clk = + S32CC_MODULE_CLK(fxosc); + +static struct s32cc_osc firc = + S32CC_OSC_INIT(S32CC_FIRC); +static struct s32cc_clk firc_clk = + S32CC_MODULE_CLK(firc); + +static struct s32cc_osc sirc = + S32CC_OSC_INIT(S32CC_SIRC); +static struct s32cc_clk sirc_clk = + S32CC_MODULE_CLK(sirc); + +/* ARM PLL */ +static struct s32cc_clkmux arm_pll_mux = + S32CC_CLKMUX_INIT(S32CC_ARM_PLL, 0, 2, + S32CC_CLK_FIRC, + S32CC_CLK_FXOSC, 0, 0, 0); +static struct s32cc_clk arm_pll_mux_clk = + S32CC_MODULE_CLK(arm_pll_mux); +static struct s32cc_pll armpll = + S32CC_PLL_INIT(arm_pll_mux_clk, S32CC_ARM_PLL, 2); +static struct s32cc_clk arm_pll_vco_clk = + S32CC_FREQ_MODULE_CLK(armpll, 1400 * MHZ, 2000 * MHZ); + +static struct s32cc_pll_out_div arm_pll_phi0_div = + S32CC_PLL_OUT_DIV_INIT(armpll, 0); +static struct s32cc_clk arm_pll_phi0_clk = + S32CC_FREQ_MODULE_CLK(arm_pll_phi0_div, 0, GHZ); + +/* ARM DFS */ +static struct s32cc_dfs armdfs = + S32CC_DFS_INIT(armpll, S32CC_ARM_DFS); +static struct s32cc_dfs_div arm_dfs1_div = + S32CC_DFS_DIV_INIT(armdfs, 0); +static struct s32cc_clk arm_dfs1_clk = + S32CC_FREQ_MODULE_CLK(arm_dfs1_div, 0, 800 * MHZ); + +/* MC_CGM0 */ +static struct s32cc_clkmux cgm0_mux0 = + S32CC_SHARED_CLKMUX_INIT(S32CC_CGM0, 0, 2, + S32CC_CLK_FIRC, + S32CC_CLK_ARM_PLL_DFS1, 0, 0, 0); +static struct s32cc_clk cgm0_mux0_clk = S32CC_MODULE_CLK(cgm0_mux0); + +static struct s32cc_clkmux cgm0_mux8 = + S32CC_SHARED_CLKMUX_INIT(S32CC_CGM0, 8, 3, + S32CC_CLK_FIRC, + S32CC_CLK_PERIPH_PLL_PHI3, + S32CC_CLK_FXOSC, 0, 0); +static struct s32cc_clk cgm0_mux8_clk = S32CC_MODULE_CLK(cgm0_mux8); + +/* XBAR */ +static struct s32cc_clk xbar_2x_clk = + S32CC_CHILD_CLK(cgm0_mux0_clk, 48 * MHZ, 800 * MHZ); +static struct s32cc_fixed_div xbar_div2 = + S32CC_FIXED_DIV_INIT(cgm0_mux0_clk, 2); +static struct s32cc_clk xbar_clk = + S32CC_FREQ_MODULE_CLK(xbar_div2, 24 * MHZ, 400 * MHZ); +static struct s32cc_fixed_div xbar_div4 = + S32CC_FIXED_DIV_INIT(cgm0_mux0_clk, 4); +static struct s32cc_clk xbar_div2_clk = + S32CC_FREQ_MODULE_CLK(xbar_div4, 12 * MHZ, 200 * MHZ); +static struct s32cc_fixed_div xbar_div6 = + S32CC_FIXED_DIV_INIT(cgm0_mux0_clk, 6); +static struct s32cc_clk xbar_div3_clk = + S32CC_FREQ_MODULE_CLK(xbar_div6, 8 * MHZ, 133333333); +static struct s32cc_fixed_div xbar_div8 = + S32CC_FIXED_DIV_INIT(cgm0_mux0_clk, 8); +static struct s32cc_clk xbar_div4_clk = + S32CC_FREQ_MODULE_CLK(xbar_div8, 6 * MHZ, 100 * MHZ); +static struct s32cc_fixed_div xbar_div12 = + S32CC_FIXED_DIV_INIT(cgm0_mux0_clk, 12); +static struct s32cc_clk xbar_div6_clk = + S32CC_FREQ_MODULE_CLK(xbar_div12, 4 * MHZ, 66666666); + +/* Linflex */ +static struct s32cc_clk linflex_baud_clk = + S32CC_CHILD_CLK(cgm0_mux8_clk, 19200, 133333333); +static struct s32cc_fixed_div linflex_div = + S32CC_FIXED_DIV_INIT(linflex_baud_clk, 2); +static struct s32cc_clk linflex_clk = + S32CC_FREQ_MODULE_CLK(linflex_div, 9600, 66666666); + +/* MC_CGM1 */ +static struct s32cc_clkmux cgm1_mux0 = + S32CC_SHARED_CLKMUX_INIT(S32CC_CGM1, 0, 3, + S32CC_CLK_FIRC, + S32CC_CLK_ARM_PLL_PHI0, + S32CC_CLK_ARM_PLL_DFS2, 0, 0); +static struct s32cc_clk cgm1_mux0_clk = S32CC_MODULE_CLK(cgm1_mux0); + +/* A53_CORE */ +static struct s32cc_clk a53_core_clk = + S32CC_FREQ_MODULE_CLK(cgm1_mux0_clk, S32CC_A53_MIN_FREQ, + S32CC_A53_MAX_FREQ); +/* A53_CORE_DIV2 */ +static struct s32cc_fixed_div a53_core_div2 = + S32CC_FIXED_DIV_INIT(cgm1_mux0_clk, 2); +static struct s32cc_clk a53_core_div2_clk = + S32CC_FREQ_MODULE_CLK(a53_core_div2, S32CC_A53_MIN_FREQ / 2, + S32CC_A53_MAX_FREQ / 2); +/* A53_CORE_DIV10 */ +static struct s32cc_fixed_div a53_core_div10 = + S32CC_FIXED_DIV_INIT(cgm1_mux0_clk, 10); +static struct s32cc_clk a53_core_div10_clk = + S32CC_FREQ_MODULE_CLK(a53_core_div10, S32CC_A53_MIN_FREQ / 10, + S32CC_A53_MAX_FREQ / 10); + +/* PERIPH PLL */ +static struct s32cc_clkmux periph_pll_mux = + S32CC_CLKMUX_INIT(S32CC_PERIPH_PLL, 0, 2, + S32CC_CLK_FIRC, + S32CC_CLK_FXOSC, 0, 0, 0); +static struct s32cc_clk periph_pll_mux_clk = + S32CC_MODULE_CLK(periph_pll_mux); +static struct s32cc_pll periphpll = + S32CC_PLL_INIT(periph_pll_mux_clk, S32CC_PERIPH_PLL, 2); +static struct s32cc_clk periph_pll_vco_clk = + S32CC_FREQ_MODULE_CLK(periphpll, 1300 * MHZ, 2 * GHZ); + +static struct s32cc_pll_out_div periph_pll_phi3_div = + S32CC_PLL_OUT_DIV_INIT(periphpll, 3); +static struct s32cc_clk periph_pll_phi3_clk = + S32CC_FREQ_MODULE_CLK(periph_pll_phi3_div, 0, 133333333); + +/* DDR PLL */ +static struct s32cc_clkmux ddr_pll_mux = + S32CC_CLKMUX_INIT(S32CC_DDR_PLL, 0, 2, + S32CC_CLK_FIRC, + S32CC_CLK_FXOSC, 0, 0, 0); +static struct s32cc_clk ddr_pll_mux_clk = + S32CC_MODULE_CLK(ddr_pll_mux); +static struct s32cc_pll ddrpll = + S32CC_PLL_INIT(ddr_pll_mux_clk, S32CC_DDR_PLL, 1); +static struct s32cc_clk ddr_pll_vco_clk = + S32CC_FREQ_MODULE_CLK(ddrpll, 1300 * MHZ, 1600 * MHZ); + +static struct s32cc_pll_out_div ddr_pll_phi0_div = + S32CC_PLL_OUT_DIV_INIT(ddrpll, 0); +static struct s32cc_clk ddr_pll_phi0_clk = + S32CC_FREQ_MODULE_CLK(ddr_pll_phi0_div, 0, 800 * MHZ); + +/* MC_CGM5 */ +static struct s32cc_clkmux cgm5_mux0 = + S32CC_SHARED_CLKMUX_INIT(S32CC_CGM5, 0, 2, + S32CC_CLK_FIRC, + S32CC_CLK_DDR_PLL_PHI0, + 0, 0, 0); +static struct s32cc_clk cgm5_mux0_clk = S32CC_MODULE_CLK(cgm5_mux0); + +/* DDR clock */ +static struct s32cc_part_block part0_block1 = + S32CC_PART_BLOCK(&part0, s32cc_part_block1); +static struct s32cc_part_block_link ddr_block_link = + S32CC_PART_BLOCK_LINK(cgm5_mux0_clk, &part0_block1); +static struct s32cc_clk ddr_clk = + S32CC_FREQ_MODULE_CLK(ddr_block_link, 0, 800 * MHZ); + +static struct s32cc_clk *s32cc_hw_clk_list[37] = { + /* Oscillators */ + [S32CC_CLK_ID(S32CC_CLK_FIRC)] = &firc_clk, + [S32CC_CLK_ID(S32CC_CLK_SIRC)] = &sirc_clk, + [S32CC_CLK_ID(S32CC_CLK_FXOSC)] = &fxosc_clk, + /* ARM PLL */ + [S32CC_CLK_ID(S32CC_CLK_ARM_PLL_PHI0)] = &arm_pll_phi0_clk, + /* ARM DFS */ + [S32CC_CLK_ID(S32CC_CLK_ARM_PLL_DFS1)] = &arm_dfs1_clk, + /* PERIPH PLL */ + [S32CC_CLK_ID(S32CC_CLK_PERIPH_PLL_PHI3)] = &periph_pll_phi3_clk, + /* DDR PLL */ + [S32CC_CLK_ID(S32CC_CLK_DDR_PLL_PHI0)] = &ddr_pll_phi0_clk, +}; + +static struct s32cc_clk_array s32cc_hw_clocks = { + .type_mask = S32CC_CLK_TYPE(S32CC_CLK_FIRC), + .clks = &s32cc_hw_clk_list[0], + .n_clks = ARRAY_SIZE(s32cc_hw_clk_list), +}; + +static struct s32cc_clk *s32cc_arch_clk_list[22] = { + /* ARM PLL */ + [S32CC_CLK_ID(S32CC_CLK_ARM_PLL_MUX)] = &arm_pll_mux_clk, + [S32CC_CLK_ID(S32CC_CLK_ARM_PLL_VCO)] = &arm_pll_vco_clk, + /* PERIPH PLL */ + [S32CC_CLK_ID(S32CC_CLK_PERIPH_PLL_MUX)] = &periph_pll_mux_clk, + [S32CC_CLK_ID(S32CC_CLK_PERIPH_PLL_VCO)] = &periph_pll_vco_clk, + /* MC_CGM0 */ + [S32CC_CLK_ID(S32CC_CLK_MC_CGM0_MUX0)] = &cgm0_mux0_clk, + [S32CC_CLK_ID(S32CC_CLK_MC_CGM0_MUX8)] = &cgm0_mux8_clk, + /* XBAR */ + [S32CC_CLK_ID(S32CC_CLK_XBAR_2X)] = &xbar_2x_clk, + [S32CC_CLK_ID(S32CC_CLK_XBAR)] = &xbar_clk, + [S32CC_CLK_ID(S32CC_CLK_XBAR_DIV2)] = &xbar_div2_clk, + [S32CC_CLK_ID(S32CC_CLK_XBAR_DIV3)] = &xbar_div3_clk, + [S32CC_CLK_ID(S32CC_CLK_XBAR_DIV4)] = &xbar_div4_clk, + [S32CC_CLK_ID(S32CC_CLK_XBAR_DIV6)] = &xbar_div6_clk, + /* MC_CGM1 */ + [S32CC_CLK_ID(S32CC_CLK_MC_CGM1_MUX0)] = &cgm1_mux0_clk, + /* A53 */ + [S32CC_CLK_ID(S32CC_CLK_A53_CORE)] = &a53_core_clk, + [S32CC_CLK_ID(S32CC_CLK_A53_CORE_DIV2)] = &a53_core_div2_clk, + [S32CC_CLK_ID(S32CC_CLK_A53_CORE_DIV10)] = &a53_core_div10_clk, + /* Linflex */ + [S32CC_CLK_ID(S32CC_CLK_LINFLEX)] = &linflex_clk, + [S32CC_CLK_ID(S32CC_CLK_LINFLEX_BAUD)] = &linflex_baud_clk, + /* DDR PLL */ + [S32CC_CLK_ID(S32CC_CLK_DDR_PLL_MUX)] = &ddr_pll_mux_clk, + [S32CC_CLK_ID(S32CC_CLK_DDR_PLL_VCO)] = &ddr_pll_vco_clk, + /* MC_CGM5 */ + [S32CC_CLK_ID(S32CC_CLK_MC_CGM5_MUX0)] = &cgm5_mux0_clk, + /* DDR */ + [S32CC_CLK_ID(S32CC_CLK_DDR)] = &ddr_clk, +}; + +static struct s32cc_clk_array s32cc_arch_clocks = { + .type_mask = S32CC_CLK_TYPE(S32CC_CLK_ARM_PLL_MUX), + .clks = &s32cc_arch_clk_list[0], + .n_clks = ARRAY_SIZE(s32cc_arch_clk_list), +}; + +static const struct s32cc_clk_array *s32cc_clk_table[2] = { + &s32cc_hw_clocks, + &s32cc_arch_clocks, +}; + +struct s32cc_clk *s32cc_get_arch_clk(unsigned long id) +{ + return s32cc_get_clk_from_table(s32cc_clk_table, + ARRAY_SIZE(s32cc_clk_table), + id); +} + +int s32cc_get_clk_id(const struct s32cc_clk *clk, unsigned long *id) +{ + return s32cc_get_id_from_table(s32cc_clk_table, + ARRAY_SIZE(s32cc_clk_table), + clk, id); +} diff --git a/drivers/nxp/clk/s32cc/s32cc_clk_utils.c b/drivers/nxp/clk/s32cc/s32cc_clk_utils.c new file mode 100644 index 00000000..0e75054e --- /dev/null +++ b/drivers/nxp/clk/s32cc/s32cc_clk_utils.c @@ -0,0 +1,65 @@ +/* + * Copyright 2024 NXP + * + * SPDX-License-Identifier: BSD-3-Clause + */ +#include <errno.h> +#include <s32cc-clk-ids.h> +#include <s32cc-clk-utils.h> + +static struct s32cc_clk *s32cc_clk_get_from_array(const struct s32cc_clk_array *arr, + unsigned long clk_id) +{ + unsigned long type, id; + + type = S32CC_CLK_TYPE(clk_id); + + if (type != arr->type_mask) { + return NULL; + } + + id = S32CC_CLK_ID(clk_id); + + if (id >= arr->n_clks) { + return NULL; + } + + return arr->clks[id]; +} + +struct s32cc_clk *s32cc_get_clk_from_table(const struct s32cc_clk_array *const *clk_arr, + size_t size, + unsigned long clk_id) +{ + struct s32cc_clk *clk; + size_t i; + + for (i = 0; i < size; i++) { + clk = s32cc_clk_get_from_array(clk_arr[i], clk_id); + if (clk != NULL) { + return clk; + } + } + + return NULL; +} + +int s32cc_get_id_from_table(const struct s32cc_clk_array *const *clk_arr, + size_t size, const struct s32cc_clk *clk, + unsigned long *clk_index) +{ + size_t i, j; + + for (i = 0; i < size; i++) { + for (j = 0; j < clk_arr[i]->n_clks; j++) { + if (clk_arr[i]->clks[j] != clk) { + continue; + } + + *clk_index = S32CC_CLK(clk_arr[i]->type_mask, j); + return 0; + } + } + + return -EINVAL; +} diff --git a/drivers/nxp/clk/s32cc/s32cc_early_clks.c b/drivers/nxp/clk/s32cc/s32cc_early_clks.c new file mode 100644 index 00000000..02b9df93 --- /dev/null +++ b/drivers/nxp/clk/s32cc/s32cc_early_clks.c @@ -0,0 +1,230 @@ +/* + * Copyright 2024 NXP + * + * SPDX-License-Identifier: BSD-3-Clause + */ +#include <drivers/clk.h> +#include <platform_def.h> +#include <s32cc-clk-drv.h> +#include <s32cc-clk-ids.h> +#include <s32cc-clk-utils.h> + +#define S32CC_FXOSC_FREQ (40U * MHZ) +#define S32CC_ARM_PLL_VCO_FREQ (2U * GHZ) +#define S32CC_ARM_PLL_PHI0_FREQ (1U * GHZ) +#define S32CC_A53_FREQ (1U * GHZ) +#define S32CC_XBAR_2X_FREQ (800U * MHZ) +#define S32CC_PERIPH_PLL_VCO_FREQ (2U * GHZ) +#define S32CC_PERIPH_PLL_PHI3_FREQ UART_CLOCK_HZ +#define S32CC_DDR_PLL_VCO_FREQ (1600U * MHZ) +#define S32CC_DDR_PLL_PHI0_FREQ (800U * MHZ) + +static int setup_fxosc(void) +{ + int ret; + + ret = clk_set_rate(S32CC_CLK_FXOSC, S32CC_FXOSC_FREQ, NULL); + if (ret != 0) { + return ret; + } + + return ret; +} + +static int setup_arm_pll(void) +{ + int ret; + + ret = clk_set_parent(S32CC_CLK_ARM_PLL_MUX, S32CC_CLK_FXOSC); + if (ret != 0) { + return ret; + } + + ret = clk_set_rate(S32CC_CLK_ARM_PLL_VCO, S32CC_ARM_PLL_VCO_FREQ, NULL); + if (ret != 0) { + return ret; + } + + ret = clk_set_rate(S32CC_CLK_ARM_PLL_PHI0, S32CC_ARM_PLL_PHI0_FREQ, NULL); + if (ret != 0) { + return ret; + } + + return ret; +} + +static int setup_periph_pll(void) +{ + int ret; + + ret = clk_set_parent(S32CC_CLK_PERIPH_PLL_MUX, S32CC_CLK_FXOSC); + if (ret != 0) { + return ret; + } + + ret = clk_set_rate(S32CC_CLK_PERIPH_PLL_VCO, S32CC_PERIPH_PLL_VCO_FREQ, NULL); + if (ret != 0) { + return ret; + } + + ret = clk_set_rate(S32CC_CLK_PERIPH_PLL_PHI3, S32CC_PERIPH_PLL_PHI3_FREQ, NULL); + if (ret != 0) { + return ret; + } + + return ret; +} + +static int enable_a53_clk(void) +{ + int ret; + + ret = clk_set_parent(S32CC_CLK_MC_CGM1_MUX0, S32CC_CLK_ARM_PLL_PHI0); + if (ret != 0) { + return ret; + } + + ret = clk_set_rate(S32CC_CLK_A53_CORE, S32CC_A53_FREQ, NULL); + if (ret != 0) { + return ret; + } + + ret = clk_enable(S32CC_CLK_A53_CORE); + if (ret != 0) { + return ret; + } + + return ret; +} + +static int enable_xbar_clk(void) +{ + int ret; + + ret = clk_set_parent(S32CC_CLK_MC_CGM0_MUX0, S32CC_CLK_ARM_PLL_DFS1); + if (ret != 0) { + return ret; + } + + ret = clk_set_rate(S32CC_CLK_XBAR_2X, S32CC_XBAR_2X_FREQ, NULL); + if (ret != 0) { + return ret; + } + + ret = clk_enable(S32CC_CLK_ARM_PLL_DFS1); + if (ret != 0) { + return ret; + } + + ret = clk_enable(S32CC_CLK_XBAR_2X); + if (ret != 0) { + return ret; + } + + return ret; +} + +static int enable_uart_clk(void) +{ + int ret; + + ret = clk_set_parent(S32CC_CLK_MC_CGM0_MUX8, S32CC_CLK_PERIPH_PLL_PHI3); + if (ret != 0) { + return ret; + } + + ret = clk_enable(S32CC_CLK_LINFLEX_BAUD); + if (ret != 0) { + return ret; + } + + return ret; +} + +static int setup_ddr_pll(void) +{ + int ret; + + ret = clk_set_parent(S32CC_CLK_DDR_PLL_MUX, S32CC_CLK_FXOSC); + if (ret != 0) { + return ret; + } + + ret = clk_set_rate(S32CC_CLK_DDR_PLL_VCO, S32CC_DDR_PLL_VCO_FREQ, NULL); + if (ret != 0) { + return ret; + } + + ret = clk_set_rate(S32CC_CLK_DDR_PLL_PHI0, S32CC_DDR_PLL_PHI0_FREQ, NULL); + if (ret != 0) { + return ret; + } + + return ret; +} + +static int enable_ddr_clk(void) +{ + int ret; + + ret = clk_set_parent(S32CC_CLK_MC_CGM5_MUX0, S32CC_CLK_DDR_PLL_PHI0); + if (ret != 0) { + return ret; + } + + ret = clk_enable(S32CC_CLK_DDR); + if (ret != 0) { + return ret; + } + + return ret; +} + +int s32cc_init_early_clks(void) +{ + int ret; + + s32cc_clk_register_drv(); + + ret = setup_fxosc(); + if (ret != 0) { + return ret; + } + + ret = setup_arm_pll(); + if (ret != 0) { + return ret; + } + + ret = enable_a53_clk(); + if (ret != 0) { + return ret; + } + + ret = enable_xbar_clk(); + if (ret != 0) { + return ret; + } + + ret = setup_periph_pll(); + if (ret != 0) { + return ret; + } + + ret = enable_uart_clk(); + if (ret != 0) { + return ret; + } + + ret = setup_ddr_pll(); + if (ret != 0) { + return ret; + } + + ret = enable_ddr_clk(); + if (ret != 0) { + return ret; + } + + return ret; +} diff --git a/drivers/nxp/console/console.mk b/drivers/nxp/console/console.mk index 6174650d..5f3c6e34 100644 --- a/drivers/nxp/console/console.mk +++ b/drivers/nxp/console/console.mk @@ -1,5 +1,5 @@ # -# Copyright 2021 NXP +# Copyright 2021-2024 NXP # # SPDX-License-Identifier: BSD-3-Clause # @@ -27,9 +27,14 @@ else ifeq ($(CONSOLE), PL011) CONSOLE_SOURCES := drivers/arm/pl011/aarch64/pl011_console.S \ ${PLAT_DRIVERS_PATH}/console/console_pl011.c +else +ifeq ($(CONSOLE), LINFLEX) +CONSOLE_SOURCES := ${PLAT_DRIVERS_PATH}/console/linflex_console.S else $(error -> CONSOLE not set!) endif + +endif endif ifeq (${BL_COMM_CONSOLE_NEEDED},yes) diff --git a/drivers/nxp/console/linflex_console.S b/drivers/nxp/console/linflex_console.S new file mode 100644 index 00000000..d8c10ef3 --- /dev/null +++ b/drivers/nxp/console/linflex_console.S @@ -0,0 +1,299 @@ +/* + * Copyright 2024 NXP + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <lib/libc/errno.h> + +#include <asm_macros.S> +#include <console_macros.S> +#include <lib/utils_def.h> + +#define LDIV_MULTIPLIER U(16) + +#define LINFLEX_LINCR1 (0x0) +#define LINCR1_INIT BIT_32(0) +#define LINCR1_MME BIT_32(4) + +#define LINFLEX_LINSR (0x8) +#define LINSR_LINS_INITMODE (0x00001000) +#define LINSR_LINS_RX_TX_MODE (0x00008000) +#define LINSR_LINS_MASK (0x0000F000) + +#define LINFLEX_UARTCR (0x10) +#define UARTCR_ROSE BIT_32(23) + +#define LINFLEX_UARTSR (0x14) +#define LINFLEX_LINIBRR (0x28) +#define LINFLEX_LINFBRR (0x24) +#define LINFLEX_BDRL (0x38) +#define LINFLEX_UARTPTO (0x50) + +#define UARTCR_UART BIT_32(0) +#define UARTCR_WL0 BIT_32(1) +#define UARTCR_PC0 BIT_32(3) +#define UARTCR_TXEN BIT_32(4) +#define UARTCR_RXEN BIT_32(5) +#define UARTCR_PC1 BIT_32(6) +#define UARTCR_TFBM BIT_32(8) +#define UARTCR_RFBM BIT_32(9) +#define UARTCR_OSR_SHIFT U(24) +#define UARTCR_OSR_WIDTH U(4) + +#define UARTSR_DTF BIT_32(1) + +/* + * "core" functions are low-level implementations that do not require + * writable memory and are thus safe to call in BL1 crash context. + */ +.globl console_linflex_core_init +.globl console_linflex_core_putc +.globl console_linflex_core_flush + +.globl console_linflex_register +.globl console_linflex_putc +.globl console_linflex_flush + +/** + * uint32_t get_ldiv_mult(uintptr_t baseaddr, uint32_t clock, + * uint32_t baud, console_t *console,); + * + * Clobber list : x0 - x6 + * Out x4: LDIV multiplier + */ +func get_ldiv_mult + ldr w4, [x0, LINFLEX_UARTCR] + mov w5, w4 + + /* Prepare choices in w5 and w6 */ + ubfx x5, x5, #UARTCR_OSR_SHIFT, #UARTCR_OSR_WIDTH + mov w6, #LDIV_MULTIPLIER + + and w4, w4, #UARTCR_ROSE + cmp w4, #0x0 + csel w4, w5, w6, ne + ret +endfunc get_ldiv_mult + +/* + * void linflex_set_brg(uintptr_t baseaddr, uint32_t clock + * uint32_t baud, console_t *console); + * + * Clobber list : x0 - x7, x13 + */ +func linflex_set_brg + mov x13, x30 + bl get_ldiv_mult + mov x30, x13 + + /* (x4) dividr = baudrate * ldiv_mult */ + mul x4, x4, x2 + /* (x5) divisr = clock rate */ + mov x5, x1 + /* (x6) ibr = divisr / dividr */ + udiv x6, x5, x4 + /* (x7) fbr = divisr % dividr */ + msub x7, x6, x4, x5 + /* fbr *= 16 / dividr */ + lsl x7, x7, #4 + udiv x7, x7, x4 + /* fbr &= 0xf */ + and w7, w7, #0xf + str w6, [x0, LINFLEX_LINIBRR] + str w7, [x0, LINFLEX_LINFBRR] + ret +endfunc linflex_set_brg + +/** + * int console_linflex_core_init(uintptr_t baseaddr, uint32_t clock, + * uint32_t baud); + * + * In: x0 - Linflex base address + * x1 - clock frequency + * x2 - baudrate + * Out: x0 - 1 on success, 0 on error + * Clobber list : x0 - x7, x13 - x14 + */ +func console_linflex_core_init + /* Set master mode and init mode */ + mov w4, #(LINCR1_INIT) + str w4, [x0, LINFLEX_LINCR1] + mov w4, #(LINCR1_MME | LINCR1_INIT) + str w4, [x0, LINFLEX_LINCR1] + + /* wait for init mode entry */ +wait_init_entry: + ldr w4, [x0, LINFLEX_LINSR] + and w4, w4, #LINSR_LINS_MASK + cmp w4, #LINSR_LINS_INITMODE + b.ne wait_init_entry + + /* Set UART bit */ + mov w4, #UARTCR_UART + str w4, [x0, LINFLEX_UARTCR] + + mov x14, x30 + bl linflex_set_brg + mov x30, x14 + + /* Set preset timeout register value. */ + mov w4, #0xf + str w4, [x0, LINFLEX_UARTPTO] + + /* 8-bit data, no parity, Tx/Rx enabled, UART mode */ + mov w4, #(UARTCR_PC1 | UARTCR_RXEN | UARTCR_TXEN | UARTCR_PC0 | \ + UARTCR_WL0 | UARTCR_UART | UARTCR_RFBM | UARTCR_TFBM) + str w4, [x0, LINFLEX_UARTCR] + + /* End init mode */ + ldr w4, [x0, LINFLEX_LINCR1] + bic w4, w4, #LINCR1_INIT + str w4, [x0, LINFLEX_LINCR1] + ret +endfunc console_linflex_core_init + +/** + * int console_linflex_register(uintptr_t baseaddr, uint32_t clock, + * uint32_t clock, uint32_t baud); + * + * Function to initialize and register the console. + * The caller needs to pass an empty console_linflex_t + * structure in which *MUST* be allocated in + * persistent memory (e.g. a global or static local + * variable, *NOT* on the stack). + * In: x0 - Linflex base address + * x1 - clock frequency + * x2 - baudrate + * x3 - pointer to empty console_t structure + * Out: x0 - 1 on success, 0 on error + * Clobber list : x0 - x7, x13 - x15 + */ +func console_linflex_register + mov x15, x30 + bl console_linflex_core_init + mov x30, x15 + + /* Populate the base address */ + str x0, [x3, #CONSOLE_T_BASE] + + mov x0, x3 + finish_console_register linflex, putc=1, getc=0, flush=1 +endfunc console_linflex_register + +/** + * int console_linflex_core_flush(uintptr_t baseaddr); + * + * Loop while the TX fifo is not empty, depending on the selected UART mode. + * + * In: x0 - Linflex base address + * Clobber list : x0 - x1 + */ +func console_linflex_core_flush +wait_rx_tx: + ldr w1, [x0, LINFLEX_LINSR] + and w1, w1, #LINSR_LINS_MASK + cmp w1, #LINSR_LINS_RX_TX_MODE + b.eq wait_rx_tx + + mov x0, #0 + ret +endfunc console_linflex_core_flush + +/** + * int console_linflex_core_putc(int c, uintptr_t baseaddr); + + * Out: w0 - printed character on success, < 0 on error. + * Clobber list : x0 - x3 + */ +func console_linflex_core_putc + cbz x1, putc_error + + cmp w0, #'\n' + b.ne print_char + + /* Print '\r\n' for each '\n' */ + mov x0, #'\r' + mov x14, x30 + bl console_linflex_core_putc + mov x30, x14 + mov x0, #'\n' + +print_char: + ldr w2, [x1, LINFLEX_UARTCR] + and w2, w2, #UARTCR_TFBM + cmp w2, #0x0 + b.eq buffer_mode + +fifo_mode: + /* UART is in FIFO mode */ + ldr w2, [x1, LINFLEX_UARTSR] + and w2, w2, #UARTSR_DTF + cmp w2, #0 + b.ne fifo_mode + + strb w0, [x1, LINFLEX_BDRL] + b no_error + +buffer_mode: + strb w0, [x1, LINFLEX_BDRL] + +buffer_loop: + ldr w2, [x1, LINFLEX_UARTSR] + and w3, w2, #UARTSR_DTF + cmp w3, #0 + b.eq buffer_loop + + /** + * In Buffer Mode the DTFTFF bit of UARTSR register + * has to be set in software + */ + mov w2, #UARTSR_DTF + str w2, [x1, LINFLEX_UARTSR] + +no_error: + mov x0, #0 + ret + +putc_error: + mov x0, #-EINVAL + ret +endfunc console_linflex_core_putc + +/** + * int console_linflex_putc(int c, console_t *console); + * + * Function to output a character over the console. It + * returns the character printed on success or -EINVAL on error. + * In : w0 - character to be printed + * x1 - pointer to console_t struct + * Out: w0 - printed character on success, < 0 on error. + * Clobber list : x0 - x3, x15 + */ +func console_linflex_putc + cbz x1, putc_error + ldr x1, [x1, #CONSOLE_T_BASE] + + b console_linflex_core_putc +puct_error: + mov x0, #-EINVAL + ret +endfunc console_linflex_putc + +/** + * int console_linflex_flush(console_t *console); + * + * Function to wait for the TX FIFO to be cleared. + * In : x0 - pointer to console_t struct + * Out: x0 - return -1 on error else return 0. + * Clobber list : x0 - x1 + */ +func console_linflex_flush + cbz x0, flush_error + ldr x0, [x0, #CONSOLE_T_BASE] + + b console_linflex_core_flush +flush_error: + mov x0, #-EINVAL + ret +endfunc console_linflex_flush diff --git a/drivers/nxp/ddr/phy-gen2/ddrphy.mk b/drivers/nxp/ddr/phy-gen2/ddrphy.mk index ba5c7742..a09a278a 100644 --- a/drivers/nxp/ddr/phy-gen2/ddrphy.mk +++ b/drivers/nxp/ddr/phy-gen2/ddrphy.mk @@ -12,9 +12,9 @@ DDR_PHY_C = DDR_PHY_H = $(DDR_PHY_C): $(DDR_PHY_H) $(COMMON_HDRS) src - @cp -r "$(DDR_PHY_PATH)/$@" "$(SRC_DIR)/$@" + $(q)cp -r "$(DDR_PHY_PATH)/$@" "$(SRC_DIR)/$@" $(DDR_PHY_H): src - @cp -r "$(DDR_PHY_PATH)/$@" "$(SRC_DIR)/$@" + $(q)cp -r "$(DDR_PHY_PATH)/$@" "$(SRC_DIR)/$@" #------------------------------------------------ diff --git a/drivers/nxp/drivers.mk b/drivers/nxp/drivers.mk index d77e9854..761571d3 100644 --- a/drivers/nxp/drivers.mk +++ b/drivers/nxp/drivers.mk @@ -97,3 +97,7 @@ endif ifeq (${IFC_NAND_NEEDED},yes) include ${PLAT_DRIVERS_PATH}/ifc/nand/ifc_nand.mk endif + +ifeq (${CLK_NEEDED},yes) +include ${PLAT_DRIVERS_PATH}/clk/s32cc/s32cc_clk.mk +endif diff --git a/drivers/nxp/gpio/nxp_gpio.c b/drivers/nxp/gpio/nxp_gpio.c index 28c9db97..b477b796 100644 --- a/drivers/nxp/gpio/nxp_gpio.c +++ b/drivers/nxp/gpio/nxp_gpio.c @@ -28,8 +28,8 @@ int set_gpio_bit(uint32_t *gpio_base_addr, ERROR("GPIO is not initialized.\n"); return GPIO_FAILURE; } - - gpdir = gpio_base_addr + GPDIR_REG_OFFSET; + /* Divide by 4 since we're operating on 32-bit pointer addresses. */ + gpdir = gpio_base_addr + (GPDIR_REG_OFFSET >> 2); gpdat = gpio_base_addr + (GPDAT_REG_OFFSET >> 2); /* @@ -67,8 +67,9 @@ int clr_gpio_bit(uint32_t *gpio_base_addr, uint32_t bit_num) return GPIO_FAILURE; } - gpdir = gpio_base_addr + GPDIR_REG_OFFSET; - gpdat = gpio_base_addr + GPDAT_REG_OFFSET; + /* Divide by 4 since we're operating on 32-bit pointer addresses. */ + gpdir = gpio_base_addr + (GPDIR_REG_OFFSET >> 2); + gpdat = gpio_base_addr + (GPDAT_REG_OFFSET >> 2); /* * Reset the corresponding bit in direction and data register diff --git a/drivers/partition/partition.c b/drivers/partition/partition.c index c60820df..c4f74935 100644 --- a/drivers/partition/partition.c +++ b/drivers/partition/partition.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016-2023, Arm Limited and Contributors. All rights reserved. + * Copyright (c) 2016-2024, Arm Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -50,7 +50,7 @@ static int load_mbr_header(uintptr_t image_handle, mbr_entry_t *mbr_entry) { size_t bytes_read; int result; - mbr_entry_t *tmp; + mbr_entry_t tmp; assert(mbr_entry != NULL); /* MBR partition table is in LBA0. */ @@ -73,19 +73,19 @@ static int load_mbr_header(uintptr_t image_handle, mbr_entry_t *mbr_entry) return -ENOENT; } - tmp = (mbr_entry_t *)(&mbr_sector[MBR_PRIMARY_ENTRY_OFFSET]); + memcpy(&tmp, mbr_sector + MBR_PRIMARY_ENTRY_OFFSET, sizeof(tmp)); - if (tmp->first_lba != 1) { + if (tmp.first_lba != 1) { VERBOSE("MBR header may have an invalid first LBA\n"); return -EINVAL; } - if ((tmp->sector_nums == 0) || (tmp->sector_nums == UINT32_MAX)) { + if ((tmp.sector_nums == 0) || (tmp.sector_nums == UINT32_MAX)) { VERBOSE("MBR header entry has an invalid number of sectors\n"); return -EINVAL; } - memcpy(mbr_entry, tmp, sizeof(mbr_entry_t)); + memcpy(mbr_entry, &tmp, sizeof(mbr_entry_t)); return 0; } @@ -94,9 +94,8 @@ static int load_mbr_header(uintptr_t image_handle, mbr_entry_t *mbr_entry) * If partition numbers could be found, check & update it. */ static int load_gpt_header(uintptr_t image_handle, size_t header_offset, - unsigned long long *part_lba) + gpt_header_t *header) { - gpt_header_t header; size_t bytes_read; int result; uint32_t header_crc, calc_crc; @@ -107,7 +106,7 @@ static int load_gpt_header(uintptr_t image_handle, size_t header_offset, header_offset); return result; } - result = io_read(image_handle, (uintptr_t)&header, + result = io_read(image_handle, (uintptr_t)header, sizeof(gpt_header_t), &bytes_read); if ((result != 0) || (sizeof(gpt_header_t) != bytes_read)) { VERBOSE("GPT header read error(%i) or read mismatch occurred," @@ -115,8 +114,8 @@ static int load_gpt_header(uintptr_t image_handle, size_t header_offset, sizeof(gpt_header_t), bytes_read); return result; } - if (memcmp(header.signature, GPT_SIGNATURE, - sizeof(header.signature)) != 0) { + if (memcmp(header->signature, GPT_SIGNATURE, + sizeof(header->signature)) != 0) { VERBOSE("GPT header signature failure\n"); return -EINVAL; } @@ -126,25 +125,24 @@ static int load_gpt_header(uintptr_t image_handle, size_t header_offset, * computed by setting this field to 0, and computing the * 32-bit CRC for HeaderSize bytes. */ - header_crc = header.header_crc; - header.header_crc = 0U; + header_crc = header->header_crc; + header->header_crc = 0U; - calc_crc = tf_crc32(0U, (uint8_t *)&header, sizeof(gpt_header_t)); + calc_crc = tf_crc32(0U, (uint8_t *)header, sizeof(gpt_header_t)); if (header_crc != calc_crc) { ERROR("Invalid GPT Header CRC: Expected 0x%x but got 0x%x.\n", header_crc, calc_crc); return -EINVAL; } - header.header_crc = header_crc; + header->header_crc = header_crc; /* partition numbers can't exceed PLAT_PARTITION_MAX_ENTRIES */ - list.entry_count = header.list_num; + list.entry_count = header->list_num; if (list.entry_count > PLAT_PARTITION_MAX_ENTRIES) { list.entry_count = PLAT_PARTITION_MAX_ENTRIES; } - *part_lba = header.part_lba; return 0; } @@ -192,11 +190,11 @@ static int load_mbr_entry(uintptr_t image_handle, mbr_entry_t *mbr_entry, static int load_mbr_entries(uintptr_t image_handle) { mbr_entry_t mbr_entry; - int i; + unsigned int i; list.entry_count = MBR_PRIMARY_ENTRY_NUMBER; - for (i = 0; i < list.entry_count; i++) { + for (i = 0U; i < list.entry_count; i++) { load_mbr_entry(image_handle, &mbr_entry, i); list.list[i].start = mbr_entry.first_lba * 512; list.list[i].length = mbr_entry.sector_nums * 512; @@ -231,12 +229,13 @@ static int load_gpt_entry(uintptr_t image_handle, gpt_entry_t *entry) * Retrieve each entry in the partition table, parse the data from each * entry and store them in the list of partition table entries. */ -static int load_partition_gpt(uintptr_t image_handle, - unsigned long long part_lba) +static int load_partition_gpt(uintptr_t image_handle, gpt_header_t header) { - const signed long long gpt_entry_offset = LBA(part_lba); + const signed long long gpt_entry_offset = LBA(header.part_lba); gpt_entry_t entry; - int result, i; + int result; + unsigned int i; + uint32_t calc_crc = 0U; result = io_seek(image_handle, IO_SEEK_SET, gpt_entry_offset); if (result != 0) { @@ -245,23 +244,36 @@ static int load_partition_gpt(uintptr_t image_handle, return result; } - for (i = 0; i < list.entry_count; i++) { + for (i = 0U; i < list.entry_count; i++) { result = load_gpt_entry(image_handle, &entry); if (result != 0) { - VERBOSE("Failed to load gpt entry data(%i) error is (%i)\n", + VERBOSE("Failed to load gpt entry data(%u) error is (%i)\n", i, result); return result; } result = parse_gpt_entry(&entry, &list.list[i]); if (result != 0) { + result = io_seek(image_handle, IO_SEEK_SET, + (gpt_entry_offset + (i * sizeof(gpt_entry_t)))); + if (result != 0) { + VERBOSE("Failed to seek (%i)\n", result); + return result; + } break; } + + /* + * Calculate CRC of Partition entry array to compare with CRC + * value in header + */ + calc_crc = tf_crc32(calc_crc, (uint8_t *)&entry, sizeof(gpt_entry_t)); } if (i == 0) { VERBOSE("No Valid GPT Entries found\n"); return -EINVAL; } + /* * Only records the valid partition number that is loaded from * partition table. @@ -269,6 +281,29 @@ static int load_partition_gpt(uintptr_t image_handle, list.entry_count = i; dump_entries(list.entry_count); + /* + * If there are less valid entries than the possible number of entries + * from the header, continue to load the partition entry table to + * calculate the full CRC in order to check against the partition CRC + * from the header for validation. + */ + for (; i < header.list_num; i++) { + result = load_gpt_entry(image_handle, &entry); + if (result != 0) { + VERBOSE("Failed to load gpt entry data(%u) error is (%i)\n", + i, result); + return result; + } + + calc_crc = tf_crc32(calc_crc, (uint8_t *)&entry, sizeof(gpt_entry_t)); + } + + if (header.part_crc != calc_crc) { + ERROR("Invalid GPT Partition Array Entry CRC: Expected 0x%x" + " but got 0x%x.\n", header.part_crc, calc_crc); + return -EINVAL; + } + return 0; } @@ -279,7 +314,7 @@ static int load_partition_gpt(uintptr_t image_handle, static int load_backup_gpt(unsigned int image_id, unsigned int sector_nums) { int result; - unsigned long long part_lba = 0; + gpt_header_t header; size_t gpt_header_offset; uintptr_t dev_handle, image_spec, image_handle; io_block_spec_t *block_spec; @@ -316,8 +351,8 @@ static int load_backup_gpt(unsigned int image_id, unsigned int sector_nums) INFO("Trying to retrieve back-up GPT header\n"); /* Last block is backup-GPT header, after the end of GPT entries */ gpt_header_offset = LBA(part_num_entries); - result = load_gpt_header(image_handle, gpt_header_offset, &part_lba); - if ((result != 0) || (part_lba == 0)) { + result = load_gpt_header(image_handle, gpt_header_offset, &header); + if ((result != 0) || (header.part_lba == 0)) { ERROR("Failed to retrieve Backup GPT header," "Partition maybe corrupted\n"); goto out; @@ -327,7 +362,8 @@ static int load_backup_gpt(unsigned int image_id, unsigned int sector_nums) * Note we mapped last 33 blocks(LBA-33), first block here starts with * entries while last block was header. */ - result = load_partition_gpt(image_handle, 0); + header.part_lba = 0; + result = load_partition_gpt(image_handle, header); out: io_close(image_handle); @@ -342,19 +378,19 @@ out: static int load_primary_gpt(uintptr_t image_handle, unsigned int first_lba) { int result; - unsigned long long part_lba; size_t gpt_header_offset; + gpt_header_t header; /* Try to load Primary GPT header from LBA1 */ gpt_header_offset = LBA(first_lba); - result = load_gpt_header(image_handle, gpt_header_offset, &part_lba); - if ((result != 0) || (part_lba == 0)) { + result = load_gpt_header(image_handle, gpt_header_offset, &header); + if ((result != 0) || (header.part_lba == 0)) { VERBOSE("Failed to retrieve Primary GPT header," "trying to retrieve back-up GPT header\n"); return result; } - return load_partition_gpt(image_handle, part_lba); + return load_partition_gpt(image_handle, header); } /* @@ -405,9 +441,9 @@ out: */ const partition_entry_t *get_partition_entry(const char *name) { - int i; + unsigned int i; - for (i = 0; i < list.entry_count; i++) { + for (i = 0U; i < list.entry_count; i++) { if (strcmp(name, list.list[i].name) == 0) { return &list.list[i]; } @@ -416,14 +452,15 @@ const partition_entry_t *get_partition_entry(const char *name) } /* - * Try retrieving a partition table entry based on the GUID. + * Try retrieving a partition table entry based on the partition type GUID. */ -const partition_entry_t *get_partition_entry_by_type(const uuid_t *type_uuid) +const partition_entry_t *get_partition_entry_by_type( + const struct efi_guid *type_guid) { - int i; + unsigned int i; - for (i = 0; i < list.entry_count; i++) { - if (guidcmp(type_uuid, &list.list[i].type_guid) == 0) { + for (i = 0U; i < list.entry_count; i++) { + if (guidcmp(type_guid, &list.list[i].type_guid) == 0) { return &list.list[i]; } } @@ -432,14 +469,15 @@ const partition_entry_t *get_partition_entry_by_type(const uuid_t *type_uuid) } /* - * Try retrieving a partition table entry based on the UUID. + * Try retrieving a partition table entry based on the unique partition GUID. */ -const partition_entry_t *get_partition_entry_by_uuid(const uuid_t *part_uuid) +const partition_entry_t *get_partition_entry_by_guid( + const struct efi_guid *part_guid) { - int i; + unsigned int i; - for (i = 0; i < list.entry_count; i++) { - if (guidcmp(part_uuid, &list.list[i].part_guid) == 0) { + for (i = 0U; i < list.entry_count; i++) { + if (guidcmp(part_guid, &list.list[i].part_guid) == 0) { return &list.list[i]; } } diff --git a/drivers/renesas/common/io/io_rcar.c b/drivers/renesas/common/io/io_rcar.c index 45ef386a..66662c11 100644 --- a/drivers/renesas/common/io/io_rcar.c +++ b/drivers/renesas/common/io/io_rcar.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015-2021, Renesas Electronics Corporation. All rights reserved. + * Copyright (c) 2015-2023, Renesas Electronics Corporation. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -84,6 +84,29 @@ typedef struct { #define RCAR_COUNT_LOAD_BL33 (2U) #define RCAR_COUNT_LOAD_BL33X (3U) +#define CHECK_IMAGE_AREA_CNT (7U) +#define BOOT_BL2_ADDR (0xE6304000U) +#define BOOT_BL2_LENGTH (0x19000U) + +typedef struct { + uintptr_t dest; + uintptr_t length; +} addr_loaded_t; + +static addr_loaded_t addr_loaded[CHECK_IMAGE_AREA_CNT] = { + [0] = {BOOT_BL2_ADDR, BOOT_BL2_LENGTH}, + [1] = {BL31_BASE, RCAR_TRUSTED_SRAM_SIZE}, +#ifndef SPD_NONE + [2] = {BL32_BASE, BL32_SIZE} +#endif +}; + +#ifndef SPD_NONE +static uint32_t addr_loaded_cnt = 3; +#else +static uint32_t addr_loaded_cnt = 2; +#endif + static const plat_rcar_name_offset_t name_offset[] = { {BL31_IMAGE_ID, 0U, RCAR_ATTR_SET_ALL(0, 0, 0)}, @@ -244,8 +267,16 @@ void rcar_read_certificate(uint64_t cert, uint32_t *len, uintptr_t *dst) dstl = cert + RCAR_CERT_INFO_DST_OFFSET; break; } + val = mmio_read_32(size); + if (val > (UINT32_MAX / 4)) { + ERROR("BL2: %s[%d] uint32 overflow!\n", + __func__, __LINE__); + *dst = 0; + *len = 0; + return; + } - *len = mmio_read_32(size) * 4U; + *len = val * 4U; dsth = dstl + 4U; *dst = ((uintptr_t) mmio_read_32(dsth) << 32) + ((uintptr_t) mmio_read_32(dstl)); @@ -253,7 +284,14 @@ void rcar_read_certificate(uint64_t cert, uint32_t *len, uintptr_t *dst) } size = cert + RCAR_CERT_INFO_SIZE_OFFSET; - *len = mmio_read_32(size) * 4U; + val = mmio_read_32(size); + if (val > (UINT32_MAX / 4)) { + ERROR("BL2: %s[%d] uint32 overflow!\n", __func__, __LINE__); + *dst = 0; + *len = 0; + return; + } + *len = val * 4U; dstl = cert + RCAR_CERT_INFO_DST_OFFSET; dsth = dstl + 4U; *dst = ((uintptr_t) mmio_read_32(dsth) << 32) + @@ -266,17 +304,18 @@ static int32_t check_load_area(uintptr_t dst, uintptr_t len) uintptr_t dram_start, dram_end; uintptr_t prot_start, prot_end; int32_t result = IO_SUCCESS; + int n; - dram_start = legacy ? DRAM1_BASE : DRAM_40BIT_BASE; + dram_start = legacy ? DRAM1_NS_BASE : DRAM_40BIT_BASE; - dram_end = legacy ? DRAM1_BASE + DRAM1_SIZE : + dram_end = legacy ? DRAM1_NS_BASE + DRAM1_NS_SIZE : DRAM_40BIT_BASE + DRAM_40BIT_SIZE; prot_start = legacy ? DRAM_PROTECTED_BASE : DRAM_40BIT_PROTECTED_BASE; prot_end = prot_start + DRAM_PROTECTED_SIZE; - if (dst < dram_start || dst > dram_end - len) { + if (dst < dram_start || len > dram_end || dst > dram_end - len) { ERROR("BL2: dst address is on the protected area.\n"); result = IO_FAIL; goto done; @@ -286,12 +325,54 @@ static int32_t check_load_area(uintptr_t dst, uintptr_t len) if (dst >= prot_start && dst < prot_end) { ERROR("BL2: dst address is on the protected area.\n"); result = IO_FAIL; + goto done; + } + + if (len > prot_start || (dst < prot_start && dst > prot_start - len)) { + ERROR("BL2: %s[%d] loaded data is on the protected area.\n", + __func__, __LINE__); + result = IO_FAIL; + goto done; } - if (dst < prot_start && dst > prot_start - len) { - ERROR("BL2: loaded data is on the protected area.\n"); + if (addr_loaded_cnt >= CHECK_IMAGE_AREA_CNT) { + ERROR("BL2: max loadable non secure images reached\n"); result = IO_FAIL; + goto done; + } + + addr_loaded[addr_loaded_cnt].dest = dst; + addr_loaded[addr_loaded_cnt].length = len; + for (n = 0; n < addr_loaded_cnt; n++) { + /* + * Check if next image invades a previous loaded image + * + * IMAGE n: area from previous image: dest| IMAGE n |length + * IMAGE n+1: area from next image: dst | IMAGE n |len + * + * 1. check: + * | IMAGE n | + * | IMAGE n+1 | + * 2. check: + * | IMAGE n | + * | IMAGE n+1 | + * 3. check: + * | IMAGE n | + * | IMAGE n+1 | + */ + if (((dst >= addr_loaded[n].dest) && + (dst <= addr_loaded[n].dest + addr_loaded[n].length)) || + ((dst + len >= addr_loaded[n].dest) && + (dst + len <= addr_loaded[n].dest + addr_loaded[n].length)) || + ((dst <= addr_loaded[n].dest) && + (dst + len >= addr_loaded[n].dest + addr_loaded[n].length))) { + ERROR("BL2: next image overlap a previous image area.\n"); + result = IO_FAIL; + goto done; + } } + addr_loaded_cnt++; + done: if (result == IO_FAIL) { ERROR("BL2: Out of range : dst=0x%lx len=0x%lx\n", dst, len); @@ -435,17 +516,17 @@ static int32_t rcar_dev_init(io_dev_info_t *dev_info, const uintptr_t name) #endif rcar_image_number = header[0]; - for (i = 0; i < rcar_image_number + 2; i++) { - rcar_image_header[i] = header[i * 2 + 1]; - rcar_image_header_prttn[i] = header[i * 2 + 2]; - } - if (rcar_image_number == 0 || rcar_image_number > RCAR_MAX_BL3X_IMAGE) { WARN("Firmware Image Package header check failed.\n"); rc = IO_FAIL; goto error; } + for (i = 0; i < rcar_image_number + 2; i++) { + rcar_image_header[i] = header[i * 2 + 1]; + rcar_image_header_prttn[i] = header[i * 2 + 2]; + } + rc = io_seek(handle, IO_SEEK_SET, offset + RCAR_SECTOR6_CERT_OFFSET); if (rc != IO_SUCCESS) { WARN("Firmware Image Package header failed to seek cert\n"); @@ -517,13 +598,6 @@ static int32_t rcar_file_open(io_dev_info_t *info, const uintptr_t file_spec, rcar_read_certificate((uint64_t) cert, &len, &dst); - /* Baylibre: HACK */ - if (spec->offset == BL31_IMAGE_ID && len < RCAR_TRUSTED_SRAM_SIZE) { - WARN("%s,%s\n", "r-car ignoring the BL31 size from certificate", - "using RCAR_TRUSTED_SRAM_SIZE instead"); - len = RCAR_TRUSTED_SRAM_SIZE; - } - current_file.partition = partition; current_file.no_load = noload; current_file.offset = offset; diff --git a/drivers/renesas/rcar/qos/D3/qos_init_d3.c b/drivers/renesas/rcar/qos/D3/qos_init_d3.c index b96e822f..8e1ebcb4 100644 --- a/drivers/renesas/rcar/qos/D3/qos_init_d3.c +++ b/drivers/renesas/rcar/qos/D3/qos_init_d3.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015-2019, Renesas Electronics Corporation. All rights reserved. + * Copyright (c) 2015-2024, Renesas Electronics Corporation. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -18,7 +18,7 @@ struct rcar_gen3_dbsc_qos_settings d3_qos[] = { /* BUFCAM settings */ - { DBSC_DBCAM0CNF1, 0x00043218 }, + { DBSC_DBCAM0CNF1, 0x00048218U }, { DBSC_DBCAM0CNF2, 0x000000F4 }, { DBSC_DBSCHCNT0, 0x000F0037 }, { DBSC_DBSCHSZ0, 0x00000001 }, diff --git a/drivers/renesas/rcar/qos/E3/qos_init_e3_v10.c b/drivers/renesas/rcar/qos/E3/qos_init_e3_v10.c index 6f4c66cb..1931dd1b 100644 --- a/drivers/renesas/rcar/qos/E3/qos_init_e3_v10.c +++ b/drivers/renesas/rcar/qos/E3/qos_init_e3_v10.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018-2019, Renesas Electronics Corporation. All rights reserved. + * Copyright (c) 2018-2024, Renesas Electronics Corporation. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -28,7 +28,7 @@ struct rcar_gen3_dbsc_qos_settings e3_qos[] = { /* BUFCAM settings */ - { DBSC_DBCAM0CNF1, 0x00043218 }, + { DBSC_DBCAM0CNF1, 0x00048218U }, { DBSC_DBCAM0CNF2, 0x000000F4 }, { DBSC_DBSCHCNT0, 0x000F0037 }, { DBSC_DBSCHSZ0, 0x00000001 }, diff --git a/drivers/renesas/rcar/qos/H3/qos_init_h3_v11.c b/drivers/renesas/rcar/qos/H3/qos_init_h3_v11.c index 329bcb82..6d933131 100644 --- a/drivers/renesas/rcar/qos/H3/qos_init_h3_v11.c +++ b/drivers/renesas/rcar/qos/H3/qos_init_h3_v11.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015-2019, Renesas Electronics Corporation. All rights reserved. + * Copyright (c) 2015-2024, Renesas Electronics Corporation. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -21,7 +21,7 @@ struct rcar_gen3_dbsc_qos_settings h3_v11_qos[] = { /* BUFCAM settings */ /* DBSC_DBCAM0CNF0 not set */ - { DBSC_DBCAM0CNF1, 0x00044218 }, + { DBSC_DBCAM0CNF1, 0x00048218U }, { DBSC_DBCAM0CNF2, 0x000000F4 }, /* DBSC_DBCAM0CNF3 not set */ { DBSC_DBSCHCNT0, 0x080F0037 }, diff --git a/drivers/renesas/rcar/qos/H3/qos_init_h3_v20.c b/drivers/renesas/rcar/qos/H3/qos_init_h3_v20.c index c20ab086..f44da872 100644 --- a/drivers/renesas/rcar/qos/H3/qos_init_h3_v20.c +++ b/drivers/renesas/rcar/qos/H3/qos_init_h3_v20.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015-2019, Renesas Electronics Corporation. All rights reserved. + * Copyright (c) 2015-2024, Renesas Electronics Corporation. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -63,7 +63,7 @@ struct rcar_gen3_dbsc_qos_settings h3_v20_qos[] = { /* BUFCAM settings */ - { DBSC_DBCAM0CNF1, 0x00043218U }, + { DBSC_DBCAM0CNF1, 0x00048218U }, { DBSC_DBCAM0CNF2, 0x000000F4U }, { DBSC_DBCAM0CNF3, 0x00000000U }, { DBSC_DBSCHCNT0, 0x000F0037U }, diff --git a/drivers/renesas/rcar/qos/H3/qos_init_h3_v30.c b/drivers/renesas/rcar/qos/H3/qos_init_h3_v30.c index 1fe6182b..867d9e07 100644 --- a/drivers/renesas/rcar/qos/H3/qos_init_h3_v30.c +++ b/drivers/renesas/rcar/qos/H3/qos_init_h3_v30.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018-2019, Renesas Electronics Corporation. All rights reserved. + * Copyright (c) 2018-2024, Renesas Electronics Corporation. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -62,7 +62,7 @@ struct rcar_gen3_dbsc_qos_settings h3_v30_qos[] = { /* BUFCAM settings */ - { DBSC_DBCAM0CNF1, 0x00043218U }, + { DBSC_DBCAM0CNF1, 0x00048218U }, { DBSC_DBCAM0CNF2, 0x000000F4U }, { DBSC_DBCAM0CNF3, 0x00000000U }, { DBSC_DBSCHCNT0, 0x000F0037U }, diff --git a/drivers/renesas/rcar/qos/H3/qos_init_h3n_v30.c b/drivers/renesas/rcar/qos/H3/qos_init_h3n_v30.c index f1ee41b9..d758dbf5 100644 --- a/drivers/renesas/rcar/qos/H3/qos_init_h3n_v30.c +++ b/drivers/renesas/rcar/qos/H3/qos_init_h3n_v30.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018-2019, Renesas Electronics Corporation. All rights reserved. + * Copyright (c) 2018-2024, Renesas Electronics Corporation. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -62,7 +62,7 @@ struct rcar_gen3_dbsc_qos_settings h3n_v30_qos[] = { /* BUFCAM settings */ - { DBSC_DBCAM0CNF1, 0x00043218U }, + { DBSC_DBCAM0CNF1, 0x00048218U }, { DBSC_DBCAM0CNF2, 0x000000F4U }, { DBSC_DBCAM0CNF3, 0x00000000U }, { DBSC_DBSCHCNT0, 0x000F0037U }, diff --git a/drivers/renesas/rcar/qos/M3/qos_init_m3_v10.c b/drivers/renesas/rcar/qos/M3/qos_init_m3_v10.c index a8264cb2..d096d012 100644 --- a/drivers/renesas/rcar/qos/M3/qos_init_m3_v10.c +++ b/drivers/renesas/rcar/qos/M3/qos_init_m3_v10.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015-2019, Renesas Electronics Corporation. All rights reserved. + * Copyright (c) 2015-2024, Renesas Electronics Corporation. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -19,7 +19,7 @@ struct rcar_gen3_dbsc_qos_settings m3_v10_qos[] = { /* BUFCAM settings */ /* DBSC_DBCAM0CNF0 not set */ - { DBSC_DBCAM0CNF1, 0x00043218 }, + { DBSC_DBCAM0CNF1, 0x00048218U }, { DBSC_DBCAM0CNF2, 0x000000F4 }, { DBSC_DBCAM0CNF3, 0x00000000 }, { DBSC_DBSCHCNT0, 0x080F0037 }, diff --git a/drivers/renesas/rcar/qos/M3/qos_init_m3_v11.c b/drivers/renesas/rcar/qos/M3/qos_init_m3_v11.c index 22fd83a9..640fe803 100644 --- a/drivers/renesas/rcar/qos/M3/qos_init_m3_v11.c +++ b/drivers/renesas/rcar/qos/M3/qos_init_m3_v11.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2019, Renesas Electronics Corporation. All rights reserved. + * Copyright (c) 2017-2024, Renesas Electronics Corporation. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -62,7 +62,7 @@ struct rcar_gen3_dbsc_qos_settings m3_v11_qos[] = { /* BUFCAM settings */ - { DBSC_DBCAM0CNF1, 0x00043218 }, + { DBSC_DBCAM0CNF1, 0x00048218U }, { DBSC_DBCAM0CNF2, 0x000000F4 }, { DBSC_DBCAM0CNF3, 0x00000000 }, { DBSC_DBSCHCNT0, 0x000F0037 }, diff --git a/drivers/renesas/rcar/qos/M3/qos_init_m3_v30.c b/drivers/renesas/rcar/qos/M3/qos_init_m3_v30.c index 43d21d71..f5ca4b66 100644 --- a/drivers/renesas/rcar/qos/M3/qos_init_m3_v30.c +++ b/drivers/renesas/rcar/qos/M3/qos_init_m3_v30.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, Renesas Electronics Corporation. All rights reserved. + * Copyright (c) 2019-2024, Renesas Electronics Corporation. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -62,7 +62,7 @@ struct rcar_gen3_dbsc_qos_settings m3_v30_qos[] = { /* BUFCAM settings */ - { DBSC_DBCAM0CNF1, 0x00043218 }, + { DBSC_DBCAM0CNF1, 0x00048218U }, { DBSC_DBCAM0CNF2, 0x000000F4 }, { DBSC_DBCAM0CNF3, 0x00000000 }, { DBSC_DBSCHCNT0, 0x000F0037 }, diff --git a/drivers/renesas/rcar/qos/M3N/qos_init_m3n_v10.c b/drivers/renesas/rcar/qos/M3N/qos_init_m3n_v10.c index 446340bb..95c6ac9a 100644 --- a/drivers/renesas/rcar/qos/M3N/qos_init_m3n_v10.c +++ b/drivers/renesas/rcar/qos/M3N/qos_init_m3n_v10.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2019, Renesas Electronics Corporation. All rights reserved. + * Copyright (c) 2017-2024, Renesas Electronics Corporation. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -60,7 +60,7 @@ struct rcar_gen3_dbsc_qos_settings m3n_v10_qos[] = { /* BUFCAM settings */ - { DBSC_DBCAM0CNF1, 0x00043218 }, + { DBSC_DBCAM0CNF1, 0x00048218U }, { DBSC_DBCAM0CNF2, 0x000000F4 }, { DBSC_DBSCHCNT0, 0x000F0037 }, { DBSC_DBSCHSZ0, 0x00000001 }, diff --git a/drivers/renesas/rcar/qos/V3M/qos_init_v3m.c b/drivers/renesas/rcar/qos/V3M/qos_init_v3m.c index 076876cc..4e1734cd 100644 --- a/drivers/renesas/rcar/qos/V3M/qos_init_v3m.c +++ b/drivers/renesas/rcar/qos/V3M/qos_init_v3m.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015-2019, Renesas Electronics Corporation + * Copyright (c) 2015-2024, Renesas Electronics Corporation * All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause @@ -19,7 +19,7 @@ struct rcar_gen3_dbsc_qos_settings v3m_qos[] = { /* BUFCAM settings */ - { DBSC_DBCAM0CNF1, 0x00044218 }, + { DBSC_DBCAM0CNF1, 0x00048218U }, { DBSC_DBCAM0CNF2, 0x000000F4 }, { DBSC_DBSCHCNT0, 0x080F003F }, { DBSC_DBSCHCNT1, 0x00001010 }, diff --git a/drivers/rpi3/gpio/rpi3_gpio.c b/drivers/rpi3/gpio/rpi3_gpio.c index 55a8832c..460afe17 100644 --- a/drivers/rpi3/gpio/rpi3_gpio.c +++ b/drivers/rpi3/gpio/rpi3_gpio.c @@ -1,6 +1,7 @@ /* * Copyright (c) 2019, Linaro Limited * Copyright (c) 2019, Ying-Chun Liu (PaulLiu) <paul.liu@linaro.org> + * Copyright (c) 2024, Arm Limited. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -120,7 +121,7 @@ static void rpi3_gpio_set_value(int gpio, int value) int regN = gpio / 32; int shift = gpio % 32; uintptr_t reg_set = reg_base + RPI3_GPIO_GPSET(regN); - uintptr_t reg_clr = reg_base + RPI3_GPIO_GPSET(regN); + uintptr_t reg_clr = reg_base + RPI3_GPIO_GPCLR(regN); switch (value) { case GPIO_LEVEL_LOW: diff --git a/drivers/rpi3/rng/rpi3_rng.c b/drivers/rpi3/rng/rpi3_rng.c index b6bf0052..16733e15 100644 --- a/drivers/rpi3/rng/rpi3_rng.c +++ b/drivers/rpi3/rng/rpi3_rng.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2018-2024, Arm Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -11,6 +11,19 @@ #include <rpi_hw.h> +#define RPI3_RNG_CTRL_OFFSET ULL(0x00000000) +#define RPI3_RNG_STATUS_OFFSET ULL(0x00000004) +#define RPI3_RNG_DATA_OFFSET ULL(0x00000008) +#define RPI3_RNG_INT_MASK_OFFSET ULL(0x00000010) +/* Enable/disable RNG */ +#define RPI3_RNG_CTRL_ENABLE U(0x1) +#define RPI3_RNG_CTRL_DISABLE U(0x0) +/* Number of currently available words */ +#define RPI3_RNG_STATUS_NUM_WORDS_SHIFT U(24) +#define RPI3_RNG_STATUS_NUM_WORDS_MASK U(0xFF) +/* Value to mask interrupts caused by the RNG */ +#define RPI3_RNG_INT_MASK_DISABLE U(0x1) + /* Initial amount of values to discard */ #define RNG_WARMUP_COUNT U(0x40000) diff --git a/drivers/scmi-msg/common.h b/drivers/scmi-msg/common.h index 62f3087d..6b186d07 100644 --- a/drivers/scmi-msg/common.h +++ b/drivers/scmi-msg/common.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: BSD-3-Clause */ /* - * Copyright (c) 2015-2019, Arm Limited and Contributors. All rights reserved. + * Copyright (c) 2015-2024, Arm Limited and Contributors. All rights reserved. * Copyright (c) 2019-2020, Linaro Limited */ #ifndef SCMI_MSG_COMMON_H @@ -15,6 +15,7 @@ #include "clock.h" #include "power_domain.h" #include "reset_domain.h" +#include "sensor.h" #define SCMI_VERSION 0x20000U #define SCMI_IMPL_VERSION 0U @@ -118,6 +119,13 @@ scmi_msg_handler_t scmi_msg_get_rstd_handler(struct scmi_msg *msg); */ scmi_msg_handler_t scmi_msg_get_pd_handler(struct scmi_msg *msg); +/* + * scmi_msg_get_sensor_handler - Return a handler for a sensor message + * @msg - message to process + * Return a function handler for the message or NULL + */ +scmi_msg_handler_t scmi_msg_get_sensor_handler(struct scmi_msg *msg); + /* * Process Read, process and write response for input SCMI message * diff --git a/drivers/scmi-msg/entry.c b/drivers/scmi-msg/entry.c index 399115c6..5ac68e1e 100644 --- a/drivers/scmi-msg/entry.c +++ b/drivers/scmi-msg/entry.c @@ -15,6 +15,7 @@ #pragma weak scmi_msg_get_rstd_handler #pragma weak scmi_msg_get_pd_handler #pragma weak scmi_msg_get_voltage_handler +#pragma weak scmi_msg_get_sensor_handler scmi_msg_handler_t scmi_msg_get_clock_handler(struct scmi_msg *msg __unused) { @@ -36,6 +37,11 @@ scmi_msg_handler_t scmi_msg_get_voltage_handler(struct scmi_msg *msg __unused) return NULL; } +scmi_msg_handler_t scmi_msg_get_sensor_handler(struct scmi_msg *msg __unused) +{ + return NULL; +} + void scmi_status_response(struct scmi_msg *msg, int32_t status) { assert(msg->out && msg->out_size >= sizeof(int32_t)); @@ -75,6 +81,9 @@ void scmi_process_message(struct scmi_msg *msg) case SCMI_PROTOCOL_ID_POWER_DOMAIN: handler = scmi_msg_get_pd_handler(msg); break; + case SCMI_PROTOCOL_ID_SENSOR: + handler = scmi_msg_get_sensor_handler(msg); + break; default: break; } diff --git a/drivers/scmi-msg/sensor.c b/drivers/scmi-msg/sensor.c new file mode 100644 index 00000000..a47018d8 --- /dev/null +++ b/drivers/scmi-msg/sensor.c @@ -0,0 +1,277 @@ +// SPDX-License-Identifier: BSD-3-Clause +/* + * Copyright 2021-2024 NXP + */ + +#include <cdefs.h> +#include <string.h> + +#include "common.h" + +#include <drivers/scmi-msg.h> +#include <drivers/scmi.h> +#include <lib/utils_def.h> + +static bool message_id_is_supported(size_t message_id); + +uint16_t plat_scmi_sensor_count(unsigned int agent_id __unused) +{ + if (sensor_ops.sensor_count != NULL) { + return sensor_ops.sensor_count(agent_id); + } + + return 0U; +} + +uint8_t plat_scmi_sensor_max_requests(unsigned int agent_id __unused) +{ + if (sensor_ops.sensor_max_request != NULL) { + return sensor_ops.sensor_max_request(agent_id); + } + + return 0U; +} + +uint32_t plat_scmi_sensor_reg(unsigned int agent_id __unused, + unsigned int *addr) +{ + if (sensor_ops.get_sensor_req != NULL) { + return sensor_ops.get_sensor_req(agent_id, addr); + } + + return 0U; +} + +int32_t plat_scmi_sensor_reading_get(uint32_t agent_id __unused, + uint16_t sensor_id __unused, + uint32_t *val __unused) +{ + if (sensor_ops.sensor_reading_get != NULL) { + return sensor_ops.sensor_reading_get(agent_id, sensor_id, val); + } + + return 0; +} + +uint32_t plat_scmi_sensor_description_get(uint32_t agent_id __unused, + uint16_t desc_index __unused, + struct scmi_sensor_desc *desc __unused) +{ + if (sensor_ops.sensor_description_get != NULL) { + return sensor_ops.sensor_description_get(agent_id, desc_index, desc); + } + + return 0U; +} + +uint32_t plat_scmi_sensor_update_interval(uint32_t agent_id __unused, + uint16_t sensor_id __unused) +{ + if (sensor_ops.sensor_update_interval != NULL) { + return sensor_ops.sensor_update_interval(agent_id, sensor_id); + } + + return 0U; +} + +uint32_t plat_scmi_sensor_state(uint32_t agent_id __unused, + uint16_t sensor_id __unused) +{ + if (sensor_ops.sensor_state != NULL) { + return sensor_ops.sensor_state(agent_id, sensor_id); + } + + return 0U; +} + +uint32_t plat_scmi_sensor_timestamped(uint32_t agent_id __unused, + uint16_t sensor_id __unused) +{ + if (sensor_ops.sensor_timestamped != NULL) { + return sensor_ops.sensor_timestamped(agent_id, sensor_id); + } + + return 0U; +} + +static void report_version(struct scmi_msg *msg) +{ + struct scmi_protocol_version_p2a return_values = { + .status = SCMI_SUCCESS, + .version = SCMI_PROTOCOL_VERSION_SENSOR, + }; + + if (msg->in_size != 0U) { + scmi_status_response(msg, SCMI_PROTOCOL_ERROR); + return; + } + + scmi_write_response(msg, &return_values, sizeof(return_values)); +} + +static void report_attributes(struct scmi_msg *msg) +{ + unsigned int addr[2]; + unsigned int len; + + struct scmi_protocol_attributes_p2a_sensor return_values = { + .status = SCMI_SUCCESS, + }; + + if (msg->in_size != 0U) { + scmi_status_response(msg, SCMI_PROTOCOL_ERROR); + return; + } + + return_values.num_sensors = plat_scmi_sensor_count(msg->agent_id); + return_values.max_reqs = plat_scmi_sensor_max_requests(msg->agent_id); + len = plat_scmi_sensor_reg(msg->agent_id, addr); + if (len != 0U) { + return_values.sensor_reg_low = addr[0]; + return_values.sensor_reg_high = addr[1]; + return_values.sensor_reg_len = len; + } + + scmi_write_response(msg, &return_values, sizeof(return_values)); +} + +static void report_message_attributes(struct scmi_msg *msg) +{ + struct scmi_protocol_message_attributes_a2p *in_args = (void *)msg->in; + struct scmi_protocol_message_attributes_p2a return_values = { + .status = SCMI_SUCCESS, + /* For this protocol, attributes shall be zero */ + .attributes = 0U, + }; + + if (msg->in_size != sizeof(*in_args)) { + scmi_status_response(msg, SCMI_PROTOCOL_ERROR); + return; + } + + if (!message_id_is_supported(in_args->message_id)) { + scmi_status_response(msg, SCMI_NOT_FOUND); + return; + } + + scmi_write_response(msg, &return_values, sizeof(return_values)); +} + +static void scmi_sensor_description_get(struct scmi_msg *msg) +{ + const struct scmi_sensor_description_get_a2p *in_args = (void *)msg->in; + struct scmi_sensor_description_get_p2a return_values = { + .status = SCMI_SUCCESS, + }; + struct scmi_sensor_desc desc; + unsigned int desc_index = 0U; + unsigned int num_sensor_flags; + + if (msg->in_size != sizeof(*in_args)) { + scmi_status_response(msg, SCMI_PROTOCOL_ERROR); + return; + } + + desc_index = SPECULATION_SAFE_VALUE(in_args->desc_index); + + num_sensor_flags = plat_scmi_sensor_description_get(msg->agent_id, desc_index, + &desc); + return_values.num_sensor_flags = num_sensor_flags; + + memcpy(msg->out, &return_values, sizeof(return_values)); + memcpy(msg->out + sizeof(return_values), &desc, sizeof(desc)); + msg->out_size_out = sizeof(return_values) + sizeof(struct scmi_sensor_desc); +} + +static void scmi_sensor_config_get(struct scmi_msg *msg) +{ + const struct scmi_sensor_config_get_a2p *in_args = (void *)msg->in; + struct scmi_sensor_config_get_p2a return_values = { + .status = SCMI_SUCCESS, + }; + unsigned int sensor_id = 0U; + uint32_t update_interval, state, timestamped; + + if (msg->in_size != sizeof(*in_args)) { + scmi_status_response(msg, SCMI_PROTOCOL_ERROR); + return; + } + + sensor_id = SPECULATION_SAFE_VALUE(in_args->sensor_id); + + if (sensor_id >= plat_scmi_sensor_count(msg->agent_id)) { + scmi_status_response(msg, SCMI_INVALID_PARAMETERS); + return; + } + + update_interval = plat_scmi_sensor_update_interval(msg->agent_id, sensor_id); + state = plat_scmi_sensor_state(msg->agent_id, sensor_id); + timestamped = plat_scmi_sensor_timestamped(msg->agent_id, sensor_id); + return_values.sensor_config = (update_interval << 11) | (timestamped << 1) | state; + + scmi_write_response(msg, &return_values, sizeof(return_values)); +} + +static void scmi_sensor_reading_get(struct scmi_msg *msg) +{ + const struct scmi_sensor_reading_get_a2p *in_args = (void *)msg->in; + struct scmi_sensor_reading_get_p2a return_values = { + .status = SCMI_SUCCESS, + }; + unsigned int sensor_id = 0U; + int32_t ret; + + if (msg->in_size != sizeof(*in_args)) { + scmi_status_response(msg, SCMI_PROTOCOL_ERROR); + return; + } + + sensor_id = SPECULATION_SAFE_VALUE(in_args->sensor_id); + + if (sensor_id >= plat_scmi_sensor_count(msg->agent_id)) { + scmi_status_response(msg, SCMI_INVALID_PARAMETERS); + return; + } + + ret = plat_scmi_sensor_reading_get(msg->agent_id, sensor_id, + (uint32_t *)&return_values.val); + if (ret) { + scmi_status_response(msg, SCMI_HARDWARE_ERROR); + return; + } + + scmi_write_response(msg, &return_values, sizeof(return_values)); +} + +static void scmi_sensor_list_update_intervals(struct scmi_msg *msg) +{ + /* TODO */ + scmi_status_response(msg, SCMI_NOT_SUPPORTED); +} + +static const scmi_msg_handler_t scmi_sensor_handler_table[SCMI_SENSOR_MAX] = { + [SCMI_PROTOCOL_VERSION] = report_version, + [SCMI_PROTOCOL_ATTRIBUTES] = report_attributes, + [SCMI_PROTOCOL_MESSAGE_ATTRIBUTES] = report_message_attributes, + [SCMI_SENSOR_DESCRIPTION_GET] = scmi_sensor_description_get, + [SCMI_SENSOR_CONFIG_GET] = scmi_sensor_config_get, + [SCMI_SENSOR_LIST_UPDATE_INTERVALS] = scmi_sensor_list_update_intervals, + [SCMI_SENSOR_READING_GET] = scmi_sensor_reading_get, +}; + +static bool message_id_is_supported(size_t message_id) +{ + return scmi_sensor_handler_table[message_id] != NULL; +} + +scmi_msg_handler_t scmi_msg_get_sensor_handler(struct scmi_msg *msg) +{ + unsigned int message_id = SPECULATION_SAFE_VALUE(msg->message_id); + + if (!message_id_is_supported(message_id)) { + VERBOSE("pd handle not found %u\n", msg->message_id); + return NULL; + } + + return scmi_sensor_handler_table[message_id]; +} diff --git a/drivers/scmi-msg/sensor.h b/drivers/scmi-msg/sensor.h new file mode 100644 index 00000000..28cbb1ed --- /dev/null +++ b/drivers/scmi-msg/sensor.h @@ -0,0 +1,125 @@ +/* SPDX-License-Identifier: BSD-3-Clause */ +/* + * Copyright 2023-2024 NXP + */ + +#ifndef SCMI_MSG_SENSOR_H +#define SCMI_MSG_SENSOR_H + +#include <stdint.h> + +#include <lib/utils_def.h> + +#define SCMI_PROTOCOL_VERSION_SENSOR 0x20000U + +/* + * Identifiers of the SCMI SENSOR Protocol commands + */ +enum scmi_sensor_command_id { + SCMI_SENSOR_DESCRIPTION_GET = 0x003, + SCMI_SENSOR_TRIP_POINT_NOTIFY = 0x004, + SCMI_SENSOR_TRIP_POINT_CONFIG = 0x005, + SCMI_SENSOR_READING_GET = 0x006, + SCMI_SENSOR_AXIS_DESCRIPTION_GET = 0x007, + SCMI_SENSOR_LIST_UPDATE_INTERVALS = 0x008, + SCMI_SENSOR_CONFIG_GET = 0x009, + SCMI_SENSOR_CONFIG_SET = 0x00A, + SCMI_SENSOR_CONTINUOUS_UPDATE_NOTIFY = 0x00B, + SCMI_SENSOR_MAX = 0x00C, +}; + +/* Protocol attributes */ +struct scmi_protocol_attributes_p2a_sensor { + int32_t status; + int16_t num_sensors; + uint8_t max_reqs; + uint8_t res; + uint32_t sensor_reg_low; + uint32_t sensor_reg_high; + uint32_t sensor_reg_len; +}; + +#define SCMI_SENSOR_NAME_LENGTH_MAX 16U + +struct scmi_sensor_desc { + uint32_t id; + uint32_t attr_low; + uint32_t attr_high; + uint8_t name[SCMI_SENSOR_NAME_LENGTH_MAX]; + uint32_t power; + uint32_t resolution; + int32_t min_range_low; + int32_t min_range_high; + int32_t max_range_low; + int32_t max_range_high; +}; + +struct scmi_sensor_description_get_a2p { + uint32_t desc_index; +}; + +struct scmi_sensor_description_get_p2a { + int32_t status; + uint32_t num_sensor_flags; +}; + +struct scmi_sensor_config_get_a2p { + uint32_t sensor_id; +}; + +struct scmi_sensor_config_get_p2a { + int32_t status; + uint32_t sensor_config; +}; + +/* + * Sensor Reading Get + */ +struct scmi_sensor_reading_get_a2p { + uint32_t sensor_id; + uint32_t flags; +}; + +struct scmi_sensor_val { + uint32_t value_low; + uint32_t value_high; + uint32_t timestap_low; + uint32_t timestap_high; +}; + +struct scmi_sensor_reading_get_p2a { + int32_t status; + struct scmi_sensor_val val; +}; + +typedef struct { + uint16_t (*sensor_count)(unsigned int agent_id); + uint8_t (*sensor_max_request)(unsigned int agent_id); + uint32_t (*get_sensor_req)(unsigned int agent_id, unsigned int *addr); + int32_t (*sensor_reading_get)(uint32_t agent_id, uint16_t sensor_id, + uint32_t *val); + uint32_t (*sensor_description_get)(unsigned int agent_id, uint16_t sensor_id, + struct scmi_sensor_desc *desc); + uint32_t (*sensor_update_interval)(uint32_t agent_id, uint16_t sensor_id); + uint32_t (*sensor_state)(uint32_t agent_id, uint16_t sensor_id); + uint16_t (*sensor_timestamped)(uint32_t agent_id, uint16_t sensor_id); +} plat_scmi_sensor_ops_t; + +#define REGISTER_SCMI_SENSOR_OPS(_sensor_count, _sensor_max_request, \ + _get_sensor_req, _sensor_reading_get, \ + _sensor_description_get, _sensor_update_interval, \ + _sensor_state, _sensor_timestamped) \ + const plat_scmi_sensor_ops_t sensor_ops = { \ + .sensor_count = _sensor_count, \ + .sensor_max_request = _sensor_max_request, \ + .get_sensor_req = _get_sensor_req, \ + .sensor_reading_get = _sensor_reading_get, \ + .sensor_description_get = _sensor_description_get, \ + .sensor_update_interval = _sensor_update_interval, \ + .sensor_state = _sensor_state, \ + .sensor_timestamped = _sensor_timestamped, \ + } + +extern const plat_scmi_sensor_ops_t sensor_ops; + +#endif /* SCMI_MSG_SENSOR_H */ diff --git a/drivers/st/bsec/bsec2.c b/drivers/st/bsec/bsec2.c index 68d3a5b8..db07d1c5 100644 --- a/drivers/st/bsec/bsec2.c +++ b/drivers/st/bsec/bsec2.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2022, STMicroelectronics - All Rights Reserved + * Copyright (c) 2017-2024, STMicroelectronics - All Rights Reserved * * SPDX-License-Identifier: BSD-3-Clause */ @@ -21,15 +21,26 @@ #define BSEC_IP_VERSION_2_0 U(0x20) #define BSEC_IP_ID_2 U(0x100032) +/* + * IP configuration + */ +#define BSEC_OTP_MASK GENMASK(4, 0) +#define BSEC_OTP_BANK_SHIFT 5 +#define BSEC_TIMEOUT_VALUE U(0xFFFF) + #define OTP_ACCESS_SIZE (round_up(OTP_MAX_SIZE, __WORD_BIT) / __WORD_BIT) -static uint32_t otp_nsec_access[OTP_ACCESS_SIZE] __unused; +static uint32_t otp_nsec_access[OTP_ACCESS_SIZE] __maybe_unused; +static uint32_t bsec_shadow_register(uint32_t otp); static uint32_t bsec_power_safmem(bool power); +static uint32_t bsec_get_version(void); +static uint32_t bsec_get_id(void); +static uint32_t bsec_get_status(void); +static uint32_t bsec_read_permanent_lock(uint32_t otp, bool *value); /* BSEC access protection */ static spinlock_t bsec_spinlock; -static uintptr_t bsec_base; static void bsec_lock(void) { @@ -47,7 +58,7 @@ static void bsec_unlock(void) static bool is_otp_invalid_mode(void) { - bool ret = ((bsec_get_status() & BSEC_MODE_INVALID) == BSEC_MODE_INVALID); + bool ret = ((bsec_get_status() & BSEC_OTP_STATUS_INVALID) == BSEC_OTP_STATUS_INVALID); if (ret) { ERROR("OTP mode is OTP-INVALID\n"); @@ -155,15 +166,17 @@ static void bsec_late_init(void) struct dt_node_info bsec_info; if (fdt_get_address(&fdt) == 0) { + EARLY_ERROR("%s: DT not found\n", __func__); panic(); } node = bsec_get_dt_node(&bsec_info); if (node < 0) { + EARLY_ERROR("%s: BSEC node not found\n", __func__); panic(); } - assert(bsec_base == bsec_info.base); + assert(bsec_info.base == BSEC_BASE); bsec_dt_otp_nsec_access(fdt, node); } @@ -177,6 +190,11 @@ static uint32_t otp_bank_offset(uint32_t otp) sizeof(uint32_t); } +static uint32_t otp_bit_mask(uint32_t otp) +{ + return BIT(otp & BSEC_OTP_MASK); +} + /* * bsec_check_error: check BSEC error status. * otp: OTP number. @@ -186,10 +204,10 @@ static uint32_t otp_bank_offset(uint32_t otp) */ static uint32_t bsec_check_error(uint32_t otp, bool check_disturbed) { - uint32_t bit = BIT(otp & BSEC_OTP_MASK); + uint32_t bit = otp_bit_mask(otp); uint32_t bank = otp_bank_offset(otp); - if ((mmio_read_32(bsec_base + BSEC_ERROR_OFF + bank) & bit) != 0U) { + if ((mmio_read_32(BSEC_BASE + BSEC_ERROR_OFF + bank) & bit) != 0U) { return BSEC_ERROR; } @@ -197,7 +215,7 @@ static uint32_t bsec_check_error(uint32_t otp, bool check_disturbed) return BSEC_OK; } - if ((mmio_read_32(bsec_base + BSEC_DISTURBED_OFF + bank) & bit) != 0U) { + if ((mmio_read_32(BSEC_BASE + BSEC_DISTURBED_OFF + bank) & bit) != 0U) { return BSEC_DISTURBED; } @@ -210,15 +228,21 @@ static uint32_t bsec_check_error(uint32_t otp, bool check_disturbed) */ uint32_t bsec_probe(void) { - bsec_base = BSEC_BASE; + uint32_t version; + uint32_t id; if (is_otp_invalid_mode()) { + EARLY_ERROR("%s: otp_invalid_mod\n", __func__); return BSEC_ERROR; } - if ((((bsec_get_version() & BSEC_IPVR_MSK) != BSEC_IP_VERSION_1_1) && - ((bsec_get_version() & BSEC_IPVR_MSK) != BSEC_IP_VERSION_2_0)) || - (bsec_get_id() != BSEC_IP_ID_2)) { + version = bsec_get_version(); + id = bsec_get_id(); + + if (((version != BSEC_IP_VERSION_1_1) && + (version != BSEC_IP_VERSION_2_0)) || + (id != BSEC_IP_ID_2)) { + EARLY_ERROR("%s: version = 0x%x, id = 0x%x\n", __func__, version, id); panic(); } @@ -228,103 +252,12 @@ uint32_t bsec_probe(void) return BSEC_OK; } -/* - * bsec_get_base: return BSEC base address. - */ -uint32_t bsec_get_base(void) -{ - return bsec_base; -} - -/* - * bsec_set_config: enable and configure BSEC. - * cfg: pointer to param structure used to set register. - * return value: BSEC_OK if no error. - */ -uint32_t bsec_set_config(struct bsec_config *cfg) -{ - uint32_t value; - uint32_t result; - - if (is_otp_invalid_mode()) { - return BSEC_ERROR; - } - - value = ((((uint32_t)cfg->freq << BSEC_CONF_FRQ_SHIFT) & - BSEC_CONF_FRQ_MASK) | - (((uint32_t)cfg->pulse_width << BSEC_CONF_PRG_WIDTH_SHIFT) & - BSEC_CONF_PRG_WIDTH_MASK) | - (((uint32_t)cfg->tread << BSEC_CONF_TREAD_SHIFT) & - BSEC_CONF_TREAD_MASK)); - - bsec_lock(); - - mmio_write_32(bsec_base + BSEC_OTP_CONF_OFF, value); - - bsec_unlock(); - - result = bsec_power_safmem((bool)cfg->power & - BSEC_CONF_POWER_UP_MASK); - if (result != BSEC_OK) { - return result; - } - - value = ((((uint32_t)cfg->upper_otp_lock << UPPER_OTP_LOCK_SHIFT) & - UPPER_OTP_LOCK_MASK) | - (((uint32_t)cfg->den_lock << DENREG_LOCK_SHIFT) & - DENREG_LOCK_MASK) | - (((uint32_t)cfg->prog_lock << GPLOCK_LOCK_SHIFT) & - GPLOCK_LOCK_MASK)); - - bsec_lock(); - - mmio_write_32(bsec_base + BSEC_OTP_LOCK_OFF, value); - - bsec_unlock(); - - return BSEC_OK; -} - -/* - * bsec_get_config: return config parameters set in BSEC registers. - * cfg: config param return. - * return value: BSEC_OK if no error. - */ -uint32_t bsec_get_config(struct bsec_config *cfg) -{ - uint32_t value; - - if (cfg == NULL) { - return BSEC_INVALID_PARAM; - } - - value = mmio_read_32(bsec_base + BSEC_OTP_CONF_OFF); - cfg->power = (uint8_t)((value & BSEC_CONF_POWER_UP_MASK) >> - BSEC_CONF_POWER_UP_SHIFT); - cfg->freq = (uint8_t)((value & BSEC_CONF_FRQ_MASK) >> - BSEC_CONF_FRQ_SHIFT); - cfg->pulse_width = (uint8_t)((value & BSEC_CONF_PRG_WIDTH_MASK) >> - BSEC_CONF_PRG_WIDTH_SHIFT); - cfg->tread = (uint8_t)((value & BSEC_CONF_TREAD_MASK) >> - BSEC_CONF_TREAD_SHIFT); - - value = mmio_read_32(bsec_base + BSEC_OTP_LOCK_OFF); - cfg->upper_otp_lock = (uint8_t)((value & UPPER_OTP_LOCK_MASK) >> - UPPER_OTP_LOCK_SHIFT); - cfg->den_lock = (uint8_t)((value & DENREG_LOCK_MASK) >> - DENREG_LOCK_SHIFT); - cfg->prog_lock = (uint8_t)((value & GPLOCK_LOCK_MASK) >> - GPLOCK_LOCK_SHIFT); - - return BSEC_OK; -} - /* * bsec_shadow_register: copy SAFMEM OTP to BSEC data. * otp: OTP number. * return value: BSEC_OK if no error. */ -uint32_t bsec_shadow_register(uint32_t otp) +static uint32_t bsec_shadow_register(uint32_t otp) { uint32_t result; bool value; @@ -345,7 +278,7 @@ uint32_t bsec_shadow_register(uint32_t otp) otp); } - if ((bsec_get_status() & BSEC_MODE_PWR_MASK) == 0U) { + if ((bsec_get_status() & BSEC_OTP_STATUS_PWRON) == 0U) { result = bsec_power_safmem(true); if (result != BSEC_OK) { @@ -357,9 +290,9 @@ uint32_t bsec_shadow_register(uint32_t otp) bsec_lock(); - mmio_write_32(bsec_base + BSEC_OTP_CTRL_OFF, otp | BSEC_READ); + mmio_write_32(BSEC_BASE + BSEC_OTP_CTRL_OFF, otp | BSEC_READ); - while ((bsec_get_status() & BSEC_MODE_BUSY_MASK) != 0U) { + while ((bsec_get_status() & BSEC_OTP_STATUS_BUSY) != 0U) { ; } @@ -392,7 +325,7 @@ uint32_t bsec_read_otp(uint32_t *val, uint32_t otp) return BSEC_INVALID_PARAM; } - *val = mmio_read_32(bsec_base + BSEC_OTP_DATA_OFF + + *val = mmio_read_32(BSEC_BASE + BSEC_OTP_DATA_OFF + (otp * sizeof(uint32_t))); return BSEC_OK; @@ -427,7 +360,7 @@ uint32_t bsec_write_otp(uint32_t val, uint32_t otp) /* Ensure integrity of each register access sequence */ bsec_lock(); - mmio_write_32(bsec_base + BSEC_OTP_DATA_OFF + + mmio_write_32(BSEC_BASE + BSEC_OTP_DATA_OFF + (otp * sizeof(uint32_t)), val); bsec_unlock(); @@ -470,12 +403,11 @@ uint32_t bsec_program_otp(uint32_t val, uint32_t otp) return BSEC_PROG_FAIL; } - if ((mmio_read_32(bsec_base + BSEC_OTP_LOCK_OFF) & - BIT(BSEC_LOCK_PROGRAM)) != 0U) { + if ((mmio_read_32(BSEC_BASE + BSEC_OTP_LOCK_OFF) & GPLOCK_LOCK_MASK) != 0U) { WARN("BSEC: GPLOCK activated, prog will be ignored\n"); } - if ((bsec_get_status() & BSEC_MODE_PWR_MASK) == 0U) { + if ((bsec_get_status() & BSEC_OTP_STATUS_PWRON) == 0U) { result = bsec_power_safmem(true); if (result != BSEC_OK) { @@ -487,15 +419,15 @@ uint32_t bsec_program_otp(uint32_t val, uint32_t otp) bsec_lock(); - mmio_write_32(bsec_base + BSEC_OTP_WRDATA_OFF, val); + mmio_write_32(BSEC_BASE + BSEC_OTP_WRDATA_OFF, val); - mmio_write_32(bsec_base + BSEC_OTP_CTRL_OFF, otp | BSEC_WRITE); + mmio_write_32(BSEC_BASE + BSEC_OTP_CTRL_OFF, otp | BSEC_WRITE); - while ((bsec_get_status() & BSEC_MODE_BUSY_MASK) != 0U) { + while ((bsec_get_status() & BSEC_OTP_STATUS_BUSY) != 0U) { ; } - if ((bsec_get_status() & BSEC_MODE_PROGFAIL_MASK) != 0U) { + if ((bsec_get_status() & BSEC_OTP_STATUS_PROGFAIL) != 0U) { result = BSEC_PROG_FAIL; } else { result = bsec_check_error(otp, true); @@ -517,6 +449,7 @@ uint32_t bsec_program_otp(uint32_t val, uint32_t otp) * otp: OTP number. * return value: BSEC_OK if no error. */ +#if defined(IMAGE_BL32) uint32_t bsec_permanent_lock_otp(uint32_t otp) { uint32_t result; @@ -532,7 +465,7 @@ uint32_t bsec_permanent_lock_otp(uint32_t otp) return BSEC_INVALID_PARAM; } - if ((bsec_get_status() & BSEC_MODE_PWR_MASK) == 0U) { + if ((bsec_get_status() & BSEC_OTP_STATUS_PWRON) == 0U) { result = bsec_power_safmem(true); if (result != BSEC_OK) { @@ -554,16 +487,16 @@ uint32_t bsec_permanent_lock_otp(uint32_t otp) bsec_lock(); - mmio_write_32(bsec_base + BSEC_OTP_WRDATA_OFF, data); + mmio_write_32(BSEC_BASE + BSEC_OTP_WRDATA_OFF, data); - mmio_write_32(bsec_base + BSEC_OTP_CTRL_OFF, + mmio_write_32(BSEC_BASE + BSEC_OTP_CTRL_OFF, addr | BSEC_WRITE | BSEC_LOCK); - while ((bsec_get_status() & BSEC_MODE_BUSY_MASK) != 0U) { + while ((bsec_get_status() & BSEC_OTP_STATUS_BUSY) != 0U) { ; } - if ((bsec_get_status() & BSEC_MODE_PROGFAIL_MASK) != 0U) { + if ((bsec_get_status() & BSEC_OTP_STATUS_PROGFAIL) != 0U) { result = BSEC_PROG_FAIL; } else { result = bsec_check_error(otp, false); @@ -579,30 +512,14 @@ uint32_t bsec_permanent_lock_otp(uint32_t otp) return result; } - -/* - * bsec_write_debug_conf: write value in debug feature. - * to enable/disable debug service. - * val: value to write. - * return value: none. - */ -void bsec_write_debug_conf(uint32_t val) -{ - if (is_otp_invalid_mode()) { - return; - } - - bsec_lock(); - mmio_write_32(bsec_base + BSEC_DEN_OFF, val & BSEC_DEN_ALL_MSK); - bsec_unlock(); -} +#endif /* * bsec_read_debug_conf: return debug configuration register value. */ uint32_t bsec_read_debug_conf(void) { - return mmio_read_32(bsec_base + BSEC_DEN_OFF); + return mmio_read_32(BSEC_BASE + BSEC_DEN_OFF); } /* @@ -618,59 +535,35 @@ void bsec_write_scratch(uint32_t val) } bsec_lock(); - mmio_write_32(bsec_base + BSEC_SCRATCH_OFF, val); + mmio_write_32(BSEC_BASE + BSEC_SCRATCH_OFF, val); bsec_unlock(); #else mmio_write_32(BSEC_BASE + BSEC_SCRATCH_OFF, val); #endif } -/* - * bsec_read_scratch: return scratch register value. - */ -uint32_t bsec_read_scratch(void) -{ - return mmio_read_32(bsec_base + BSEC_SCRATCH_OFF); -} - /* * bsec_get_status: return status register value. */ -uint32_t bsec_get_status(void) -{ - return mmio_read_32(bsec_base + BSEC_OTP_STATUS_OFF); -} - -/* - * bsec_get_hw_conf: return hardware configuration register value. - */ -uint32_t bsec_get_hw_conf(void) +static uint32_t bsec_get_status(void) { - return mmio_read_32(bsec_base + BSEC_IPHW_CFG_OFF); + return mmio_read_32(BSEC_BASE + BSEC_OTP_STATUS_OFF); } /* * bsec_get_version: return BSEC version register value. */ -uint32_t bsec_get_version(void) +static uint32_t bsec_get_version(void) { - return mmio_read_32(bsec_base + BSEC_IPVR_OFF); + return mmio_read_32(BSEC_BASE + BSEC_IPVR_OFF) & BSEC_IPVR_MSK; } /* * bsec_get_id: return BSEC ID register value. */ -uint32_t bsec_get_id(void) +static uint32_t bsec_get_id(void) { - return mmio_read_32(bsec_base + BSEC_IP_ID_OFF); -} - -/* - * bsec_get_magic_id: return BSEC magic number register value. - */ -uint32_t bsec_get_magic_id(void) -{ - return mmio_read_32(bsec_base + BSEC_IP_MAGIC_ID_OFF); + return mmio_read_32(BSEC_BASE + BSEC_IP_ID_OFF); } /* @@ -681,7 +574,7 @@ uint32_t bsec_get_magic_id(void) uint32_t bsec_set_sr_lock(uint32_t otp) { uint32_t bank = otp_bank_offset(otp); - uint32_t otp_mask = BIT(otp & BSEC_OTP_MASK); + uint32_t otp_mask = otp_bit_mask(otp); if (is_otp_invalid_mode()) { return BSEC_ERROR; @@ -692,7 +585,7 @@ uint32_t bsec_set_sr_lock(uint32_t otp) } bsec_lock(); - mmio_write_32(bsec_base + BSEC_SRLOCK_OFF + bank, otp_mask); + mmio_write_32(BSEC_BASE + BSEC_SRLOCK_OFF + bank, otp_mask); bsec_unlock(); return BSEC_OK; @@ -707,14 +600,14 @@ uint32_t bsec_set_sr_lock(uint32_t otp) uint32_t bsec_read_sr_lock(uint32_t otp, bool *value) { uint32_t bank = otp_bank_offset(otp); - uint32_t otp_mask = BIT(otp & BSEC_OTP_MASK); + uint32_t otp_mask = otp_bit_mask(otp); uint32_t bank_value; if (otp > STM32MP1_OTP_MAX_ID) { return BSEC_INVALID_PARAM; } - bank_value = mmio_read_32(bsec_base + BSEC_SRLOCK_OFF + bank); + bank_value = mmio_read_32(BSEC_BASE + BSEC_SRLOCK_OFF + bank); *value = ((bank_value & otp_mask) != 0U); @@ -729,7 +622,7 @@ uint32_t bsec_read_sr_lock(uint32_t otp, bool *value) uint32_t bsec_set_sw_lock(uint32_t otp) { uint32_t bank = otp_bank_offset(otp); - uint32_t otp_mask = BIT(otp & BSEC_OTP_MASK); + uint32_t otp_mask = otp_bit_mask(otp); if (is_otp_invalid_mode()) { return BSEC_ERROR; @@ -740,7 +633,7 @@ uint32_t bsec_set_sw_lock(uint32_t otp) } bsec_lock(); - mmio_write_32(bsec_base + BSEC_SWLOCK_OFF + bank, otp_mask); + mmio_write_32(BSEC_BASE + BSEC_SWLOCK_OFF + bank, otp_mask); bsec_unlock(); return BSEC_OK; @@ -762,7 +655,7 @@ uint32_t bsec_read_sw_lock(uint32_t otp, bool *value) return BSEC_INVALID_PARAM; } - bank_value = mmio_read_32(bsec_base + BSEC_SWLOCK_OFF + bank); + bank_value = mmio_read_32(BSEC_BASE + BSEC_SWLOCK_OFF + bank); *value = ((bank_value & otp_mask) != 0U); @@ -777,7 +670,7 @@ uint32_t bsec_read_sw_lock(uint32_t otp, bool *value) uint32_t bsec_set_sp_lock(uint32_t otp) { uint32_t bank = otp_bank_offset(otp); - uint32_t otp_mask = BIT(otp & BSEC_OTP_MASK); + uint32_t otp_mask = otp_bit_mask(otp); if (is_otp_invalid_mode()) { return BSEC_ERROR; @@ -788,7 +681,7 @@ uint32_t bsec_set_sp_lock(uint32_t otp) } bsec_lock(); - mmio_write_32(bsec_base + BSEC_SPLOCK_OFF + bank, otp_mask); + mmio_write_32(BSEC_BASE + BSEC_SPLOCK_OFF + bank, otp_mask); bsec_unlock(); return BSEC_OK; @@ -810,7 +703,7 @@ uint32_t bsec_read_sp_lock(uint32_t otp, bool *value) return BSEC_INVALID_PARAM; } - bank_value = mmio_read_32(bsec_base + BSEC_SPLOCK_OFF + bank); + bank_value = mmio_read_32(BSEC_BASE + BSEC_SPLOCK_OFF + bank); *value = ((bank_value & otp_mask) != 0U); @@ -823,53 +716,23 @@ uint32_t bsec_read_sp_lock(uint32_t otp, bool *value) * value: read value (true or false). * return value: BSEC_OK if no error. */ -uint32_t bsec_read_permanent_lock(uint32_t otp, bool *value) +static uint32_t bsec_read_permanent_lock(uint32_t otp, bool *value) { uint32_t bank = otp_bank_offset(otp); - uint32_t otp_mask = BIT(otp & BSEC_OTP_MASK); + uint32_t otp_mask = otp_bit_mask(otp); uint32_t bank_value; if (otp > STM32MP1_OTP_MAX_ID) { return BSEC_INVALID_PARAM; } - bank_value = mmio_read_32(bsec_base + BSEC_WRLOCK_OFF + bank); + bank_value = mmio_read_32(BSEC_BASE + BSEC_WRLOCK_OFF + bank); *value = ((bank_value & otp_mask) != 0U); return BSEC_OK; } -/* - * bsec_otp_lock: Lock Upper OTP or Global Programming or Debug Enable. - * service: Service to lock, see header file. - * return value: BSEC_OK if no error. - */ -uint32_t bsec_otp_lock(uint32_t service) -{ - uintptr_t reg = bsec_base + BSEC_OTP_LOCK_OFF; - - if (is_otp_invalid_mode()) { - return BSEC_ERROR; - } - - switch (service) { - case BSEC_LOCK_UPPER_OTP: - mmio_write_32(reg, BIT(BSEC_LOCK_UPPER_OTP)); - break; - case BSEC_LOCK_DEBUG: - mmio_write_32(reg, BIT(BSEC_LOCK_DEBUG)); - break; - case BSEC_LOCK_PROGRAM: - mmio_write_32(reg, BIT(BSEC_LOCK_PROGRAM)); - break; - default: - return BSEC_INVALID_PARAM; - } - - return BSEC_OK; -} - /* * bsec_power_safmem: Activate or deactivate SAFMEM power. * power: true to power up, false to power down. @@ -882,7 +745,7 @@ static uint32_t bsec_power_safmem(bool power) bsec_lock(); - register_val = mmio_read_32(bsec_base + BSEC_OTP_CONF_OFF); + register_val = mmio_read_32(BSEC_BASE + BSEC_OTP_CONF_OFF); if (power) { register_val |= BSEC_CONF_POWER_UP_MASK; @@ -890,15 +753,15 @@ static uint32_t bsec_power_safmem(bool power) register_val &= ~BSEC_CONF_POWER_UP_MASK; } - mmio_write_32(bsec_base + BSEC_OTP_CONF_OFF, register_val); + mmio_write_32(BSEC_BASE + BSEC_OTP_CONF_OFF, register_val); if (power) { - while (((bsec_get_status() & BSEC_MODE_PWR_MASK) == 0U) && + while (((bsec_get_status() & BSEC_OTP_STATUS_PWRON) == 0U) && (timeout != 0U)) { timeout--; } } else { - while (((bsec_get_status() & BSEC_MODE_PWR_MASK) != 0U) && + while (((bsec_get_status() & BSEC_OTP_STATUS_PWRON) != 0U) && (timeout != 0U)) { timeout--; } @@ -915,28 +778,29 @@ static uint32_t bsec_power_safmem(bool power) /* * bsec_shadow_read_otp: Load OTP from SAFMEM and provide its value. - * otp_value: read value. - * word: OTP number. + * val: read value. + * otp: OTP number. * return value: BSEC_OK if no error. */ -uint32_t bsec_shadow_read_otp(uint32_t *otp_value, uint32_t word) +uint32_t bsec_shadow_read_otp(uint32_t *val, uint32_t otp) { uint32_t result; - result = bsec_shadow_register(word); + result = bsec_shadow_register(otp); if (result != BSEC_OK) { - ERROR("BSEC: %u Shadowing Error %u\n", word, result); + ERROR("BSEC: %u Shadowing Error %u\n", otp, result); return result; } - result = bsec_read_otp(otp_value, word); + result = bsec_read_otp(val, otp); if (result != BSEC_OK) { - ERROR("BSEC: %u Read Error %u\n", word, result); + ERROR("BSEC: %u Read Error %u\n", otp, result); } return result; } +#if defined(IMAGE_BL32) /* * bsec_check_nsec_access_rights: check non-secure access rights to target OTP. * otp: OTP number. @@ -944,7 +808,6 @@ uint32_t bsec_shadow_read_otp(uint32_t *otp_value, uint32_t word) */ uint32_t bsec_check_nsec_access_rights(uint32_t otp) { -#if defined(IMAGE_BL32) if (otp > STM32MP1_OTP_MAX_ID) { return BSEC_INVALID_PARAM; } @@ -954,8 +817,33 @@ uint32_t bsec_check_nsec_access_rights(uint32_t otp) return BSEC_ERROR; } } -#endif return BSEC_OK; } +#endif + +uint32_t bsec_get_secure_state(void) +{ + uint32_t status = bsec_get_status(); + uint32_t result = BSEC_STATE_INVALID; + uint32_t otp_enc_id __maybe_unused; + uint32_t otp_bit_len __maybe_unused; + int res __maybe_unused; + if ((status & BSEC_OTP_STATUS_INVALID) != 0U) { + result = BSEC_STATE_INVALID; + } else { + if ((status & BSEC_OTP_STATUS_SECURE) != 0U) { + if (stm32mp_check_closed_device() == STM32MP_CHIP_SEC_CLOSED) { + result = BSEC_STATE_SEC_CLOSED; + } else { + result = BSEC_STATE_SEC_OPEN; + } + } else { + /* OTP modes OPEN1 and OPEN2 are not supported */ + result = BSEC_STATE_INVALID; + } + } + + return result; +} diff --git a/drivers/st/bsec/bsec3.c b/drivers/st/bsec/bsec3.c new file mode 100644 index 00000000..3fdaf16f --- /dev/null +++ b/drivers/st/bsec/bsec3.c @@ -0,0 +1,533 @@ +/* + * Copyright (c) 2024, STMicroelectronics - All Rights Reserved + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <assert.h> +#include <limits.h> + +#include <arch_helpers.h> +#include <common/debug.h> +#include <drivers/st/bsec.h> +#include <drivers/st/bsec3_reg.h> +#include <drivers/st/stm32mp_reset.h> +#include <lib/mmio.h> +#include <lib/spinlock.h> +#include <libfdt.h> + +#include <platform_def.h> + +#define BSEC_IP_VERSION_1_0 U(0x10) +#define BSEC_IP_ID_3 U(0x100033) + +#define MAX_NB_TRIES U(3) + +/* + * IP configuration + */ +#define BSEC_OTP_MASK GENMASK_32(4, 0) +#define BSEC_OTP_BANK_SHIFT U(5) +#define BSEC_TIMEOUT_VALUE U(0x800000) /* ~7sec @1.2GHz */ + +/* Magic use to indicated valid SHADOW = 'B' 'S' 'E' 'C' */ +#define BSEC_MAGIC U(0x42534543) + +#define OTP_MAX_SIZE (STM32MP2_OTP_MAX_ID + U(1)) + +struct bsec_shadow { + uint32_t magic; + uint32_t state; + uint32_t value[OTP_MAX_SIZE]; + uint32_t status[OTP_MAX_SIZE]; +}; + +static uint32_t otp_bank(uint32_t otp) +{ + if (otp > STM32MP2_OTP_MAX_ID) { + panic(); + } + + return (otp & ~BSEC_OTP_MASK) >> BSEC_OTP_BANK_SHIFT; +} + +static uint32_t otp_bit_mask(uint32_t otp) +{ + return BIT(otp & BSEC_OTP_MASK); +} + +/* + * bsec_get_status: return status register value. + */ +static uint32_t bsec_get_status(void) +{ + return mmio_read_32(BSEC_BASE + BSEC_OTPSR); +} + +/* + * bsec_get_version: return BSEC version. + */ +static uint32_t bsec_get_version(void) +{ + return mmio_read_32(BSEC_BASE + BSEC_VERR) & BSEC_VERR_MASK; +} + +/* + * bsec_get_id: return BSEC ID. + */ +static uint32_t bsec_get_id(void) +{ + return mmio_read_32(BSEC_BASE + BSEC_IPIDR); +} + +static bool is_fuse_shadowed(uint32_t otp) +{ + uint32_t bank = otp_bank(otp); + uint32_t otp_mask = otp_bit_mask(otp); + uint32_t bank_value; + + bank_value = mmio_read_32(BSEC_BASE + BSEC_SFSR(bank)); + + if ((bank_value & otp_mask) != 0U) { + return true; + } + + return false; +} + +static void poll_otp_status_busy(void) +{ + uint32_t timeout = BSEC_TIMEOUT_VALUE; + + while (((bsec_get_status() & BSEC_OTPSR_BUSY) != 0U) && (timeout != 0U)) { + timeout--; + } + + if ((bsec_get_status() & BSEC_OTPSR_BUSY) != 0U) { + ERROR("BSEC timeout\n"); + panic(); + } +} + +static uint32_t check_read_error(uint32_t otp) +{ + uint32_t status = bsec_get_status(); + + if ((status & BSEC_OTPSR_SECF) != 0U) { + VERBOSE("BSEC read %u single error correction detected\n", otp); + } + + if ((status & BSEC_OTPSR_PPLF) != 0U) { + VERBOSE("BSEC read %u permanent programming lock detected.\n", otp); + } + + if ((status & BSEC_OTPSR_PPLMF) != 0U) { + ERROR("BSEC read %u error 0x%x\n", otp, status); + return BSEC_ERROR; + } + + if ((status & (BSEC_OTPSR_DISTURBF | BSEC_OTPSR_DEDF | BSEC_OTPSR_AMEF)) != 0U) { + ERROR("BSEC read %u error 0x%x with invalid FVR\n", otp, status); + return BSEC_RETRY; + } + + return BSEC_OK; +} + +static uint32_t check_program_error(uint32_t otp) +{ + uint32_t status = bsec_get_status(); + + if ((status & BSEC_OTPSR_PROGFAIL) != 0U) { + ERROR("BSEC program %u error 0x%x\n", otp, status); + return BSEC_RETRY; + } + + return BSEC_OK; +} + +static void check_reset_error(void) +{ + uint32_t status = bsec_get_status(); + + /* check initial status reporting */ + if ((status & BSEC_OTPSR_BUSY) != 0U) { + VERBOSE("BSEC reset and busy when OTPSR read\n"); + } + if ((status & BSEC_OTPSR_HIDEUP) != 0U) { + VERBOSE("BSEC upper fuse are not accessible (HIDEUP)\n"); + } + if ((status & BSEC_OTPSR_OTPSEC) != 0U) { + VERBOSE("BSEC reset single error correction detected\n"); + } + if ((status & BSEC_OTPSR_OTPNVIR) == 0U) { + VERBOSE("BSEC reset first fuse word 0 is detected zero\n"); + } + if ((status & BSEC_OTPSR_OTPERR) != 0U) { + ERROR("BSEC reset critical error 0x%x\n", status); + panic(); + } + if ((status & BSEC_OTPSR_FUSEOK) != BSEC_OTPSR_FUSEOK) { + ERROR("BSEC reset critical error 0x%x\n", status); + panic(); + } +} + +static bool is_bsec_write_locked(void) +{ + return (mmio_read_32(BSEC_BASE + BSEC_LOCKR) & BSEC_LOCKR_GWLOCK_MASK) != 0U; +} + +/* + * bsec_probe: initialize BSEC driver. + * return value: BSEC_OK if no error. + */ +uint32_t bsec_probe(void) +{ + uint32_t version = bsec_get_version(); + uint32_t id = bsec_get_id(); + + if ((version != BSEC_IP_VERSION_1_0) || (id != BSEC_IP_ID_3)) { + EARLY_ERROR("%s: version = 0x%x, id = 0x%x\n", __func__, version, id); + panic(); + } + + check_reset_error(); + + return BSEC_OK; +} + +/* + * bsec_shadow_register: copy SAFMEM OTP to BSEC data. + * otp: OTP number. + * return value: BSEC_OK if no error. + */ +static uint32_t bsec_shadow_register(uint32_t otp) +{ + uint32_t result; + uint32_t i; + bool value; + + result = bsec_read_sr_lock(otp, &value); + if (result != BSEC_OK) { + WARN("BSEC: %u Sticky-read bit read Error %u\n", otp, result); + } else if (value) { + VERBOSE("BSEC: OTP %u is locked and will not be refreshed\n", otp); + } + + for (i = 0U; i < MAX_NB_TRIES; i++) { + mmio_write_32(BSEC_BASE + BSEC_OTPCR, otp); + + poll_otp_status_busy(); + + result = check_read_error(otp); + if (result != BSEC_RETRY) { + break; + } + } + + return result; +} + +/* + * bsec_write_otp: write a value in shadow OTP. + * val: value to program. + * otp: OTP number. + * return value: BSEC_OK if no error. + */ +uint32_t bsec_write_otp(uint32_t val, uint32_t otp) +{ + bool state; + uint32_t result; + + if (otp > STM32MP2_OTP_MAX_ID) { + panic(); + } + + if (!is_fuse_shadowed(otp)) { + return BSEC_ERROR; + } + + if (is_bsec_write_locked()) { + return BSEC_WRITE_LOCKED; + } + + result = bsec_read_sw_lock(otp, &state); + if (result != BSEC_OK) { + WARN("Shadow register is SW locked\n"); + return result; + } + + mmio_write_32(BSEC_BASE + BSEC_FVR(otp), val); + + return BSEC_OK; +} + +/* + * bsec_program_otp: program a bit in SAFMEM after the prog. + * The OTP data is not refreshed. + * val: value to program. + * otp: OTP number. + * return value: BSEC_OK if no error. + */ +uint32_t bsec_program_otp(uint32_t val, uint32_t otp) +{ + uint32_t result; + uint32_t i; + bool value; + + if (otp > STM32MP2_OTP_MAX_ID) { + panic(); + } + + if (is_bsec_write_locked() == true) { + return BSEC_WRITE_LOCKED; + } + + result = bsec_read_sp_lock(otp, &value); + if (result != BSEC_OK) { + WARN("BSEC: %u Sticky-prog bit read Error %u\n", otp, result); + } else if (value) { + WARN("BSEC: OTP locked, prog will be ignored\n"); + return BSEC_WRITE_LOCKED; + } + + mmio_write_32(BSEC_BASE + BSEC_WDR, val); + + for (i = 0U; i < MAX_NB_TRIES; i++) { + mmio_write_32(BSEC_BASE + BSEC_OTPCR, otp | BSEC_OTPCR_PROG); + + poll_otp_status_busy(); + + result = check_program_error(otp); + if (result != BSEC_RETRY) { + break; + } + } + + return result; +} + +/* + * bsec_read_debug_conf: read debug configuration. + */ +uint32_t bsec_read_debug_conf(void) +{ + return mmio_read_32(BSEC_BASE + BSEC_DENR); +} + +static uint32_t bsec_lock_register_set(uint32_t offset, uint32_t mask) +{ + uint32_t value = mmio_read_32(BSEC_BASE + offset); + + /* The lock is already set */ + if ((value & mask) != 0U) { + return BSEC_OK; + } + + if (is_bsec_write_locked()) { + return BSEC_WRITE_LOCKED; + } + + value |= mask; + + mmio_write_32(BSEC_BASE + offset, value); + + return BSEC_OK; +} + +static bool bsec_lock_register_get(uint32_t offset, uint32_t mask) +{ + uint32_t value = mmio_read_32(BSEC_BASE + offset); + + return (value & mask) != 0U; +} + +/* + * bsec_set_sr_lock: set shadow-read lock. + * otp: OTP number. + * return value: BSEC_OK if no error. + */ +uint32_t bsec_set_sr_lock(uint32_t otp) +{ + uint32_t bank = otp_bank(otp); + uint32_t otp_mask = otp_bit_mask(otp); + + if (otp > STM32MP2_OTP_MAX_ID) { + panic(); + } + + return bsec_lock_register_set(BSEC_SRLOCK(bank), otp_mask); +} + +/* + * bsec_read_sr_lock: read shadow-read lock. + * otp: OTP number. + * value: read value (true or false). + * return value: BSEC_OK if no error. + */ +uint32_t bsec_read_sr_lock(uint32_t otp, bool *value) +{ + uint32_t bank = otp_bank(otp); + uint32_t otp_mask = otp_bit_mask(otp); + + assert(value != NULL); + if (otp > STM32MP2_OTP_MAX_ID) { + panic(); + } + + *value = bsec_lock_register_get(BSEC_SRLOCK(bank), otp_mask); + + return BSEC_OK; +} + +/* + * bsec_set_sw_lock: set shadow-write lock. + * otp: OTP number. + * return value: BSEC_OK if no error. + */ +uint32_t bsec_set_sw_lock(uint32_t otp) +{ + uint32_t bank = otp_bank(otp); + uint32_t otp_mask = otp_bit_mask(otp); + + if (otp > STM32MP2_OTP_MAX_ID) { + panic(); + } + + return bsec_lock_register_set(BSEC_SWLOCK(bank), otp_mask); +} + +/* + * bsec_read_sw_lock: read shadow-write lock. + * otp: OTP number. + * value: read value (true or false). + * return value: BSEC_OK if no error. + */ +uint32_t bsec_read_sw_lock(uint32_t otp, bool *value) +{ + uint32_t bank = otp_bank(otp); + uint32_t otp_mask = otp_bit_mask(otp); + + assert(value != NULL); + if (otp > STM32MP2_OTP_MAX_ID) { + panic(); + } + + *value = bsec_lock_register_get(BSEC_SWLOCK(bank), otp_mask); + + return BSEC_OK; +} + +/* + * bsec_set_sp_lock: set shadow-program lock. + * otp: OTP number. + * return value: BSEC_OK if no error. + */ +uint32_t bsec_set_sp_lock(uint32_t otp) +{ + uint32_t bank = otp_bank(otp); + uint32_t otp_mask = otp_bit_mask(otp); + + if (otp > STM32MP2_OTP_MAX_ID) { + panic(); + } + + return bsec_lock_register_set(BSEC_SPLOCK(bank), otp_mask); +} + +/* + * bsec_read_sp_lock: read shadow-program lock. + * otp: OTP number. + * value: read value (true or false). + * return value: BSEC_OK if no error. + */ +uint32_t bsec_read_sp_lock(uint32_t otp, bool *value) +{ + uint32_t bank = otp_bank(otp); + uint32_t otp_mask = otp_bit_mask(otp); + + assert(value != NULL); + if (otp > STM32MP2_OTP_MAX_ID) { + panic(); + } + + *value = bsec_lock_register_get(BSEC_SPLOCK(bank), otp_mask); + + return BSEC_OK; +} + +/* + * bsec_get_secure_state: read state in BSEC status register. + * return: secure state + */ +uint32_t bsec_get_secure_state(void) +{ + uint32_t state = BSEC_STATE_INVALID; + uint32_t status = bsec_get_status(); + uint32_t bsec_sr = mmio_read_32(BSEC_BASE + BSEC_SR); + + if ((status & BSEC_OTPSR_FUSEOK) == BSEC_OTPSR_FUSEOK) { + /* NVSTATE is only valid if FUSEOK */ + uint32_t nvstates = (bsec_sr & BSEC_SR_NVSTATE_MASK) >> BSEC_SR_NVSTATE_SHIFT; + + if (nvstates == BSEC_SR_NVSTATE_OPEN) { + state = BSEC_STATE_SEC_OPEN; + } else if (nvstates == BSEC_SR_NVSTATE_CLOSED) { + state = BSEC_STATE_SEC_CLOSED; + } else { + VERBOSE("%s nvstates = %u\n", __func__, nvstates); + } + } + + return state; +} + +/* + * bsec_shadow_read_otp: Load OTP from SAFMEM and provide its value + * val: read value. + * otp: OTP number. + * return value: BSEC_OK if no error. + */ +uint32_t bsec_shadow_read_otp(uint32_t *val, uint32_t otp) +{ + assert(val != NULL); + if (otp > STM32MP2_OTP_MAX_ID) { + panic(); + } + + *val = 0U; + + if (is_bsec_write_locked()) { + return BSEC_WRITE_LOCKED; + } + + if (!is_fuse_shadowed(otp)) { + uint32_t result = bsec_shadow_register(otp); + + if (result != BSEC_OK) { + ERROR("BSEC: %u Shadowing Error %u\n", otp, result); + return result; + } + } + + *val = mmio_read_32(BSEC_BASE + BSEC_FVR(otp)); + + return BSEC_OK; +} + +/* + * bsec_read_otp: read an OTP data value. + * val: read value. + * otp: OTP number. + * return value: BSEC_OK if no error. + */ +uint32_t bsec_read_otp(uint32_t *val, uint32_t otp) +{ + assert(val != NULL); + if (otp > STM32MP2_OTP_MAX_ID) { + panic(); + } + + return bsec_shadow_read_otp(val, otp); +} diff --git a/drivers/st/clk/clk-stm32-core.c b/drivers/st/clk/clk-stm32-core.c index 9fe8c8cc..6787d504 100644 --- a/drivers/st/clk/clk-stm32-core.c +++ b/drivers/st/clk/clk-stm32-core.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2022, STMicroelectronics - All Rights Reserved + * Copyright (C) 2022-2024, STMicroelectronics - All Rights Reserved * * SPDX-License-Identifier: GPL-2.0+ OR BSD-3-Clause */ @@ -28,7 +28,7 @@ struct stm32_clk_priv *clk_stm32_get_priv(void) return stm32_clock_data; } -static void stm32mp1_clk_lock(struct spinlock *lock) +static void _clk_lock(struct spinlock *lock) { if (stm32mp_lock_available()) { /* Assume interrupts are masked */ @@ -36,21 +36,21 @@ static void stm32mp1_clk_lock(struct spinlock *lock) } } -static void stm32mp1_clk_unlock(struct spinlock *lock) +static void _clk_unlock(struct spinlock *lock) { if (stm32mp_lock_available()) { spin_unlock(lock); } } -void stm32mp1_clk_rcc_regs_lock(void) +void clk_stm32_rcc_regs_lock(void) { - stm32mp1_clk_lock(®_lock); + _clk_lock(®_lock); } -void stm32mp1_clk_rcc_regs_unlock(void) +void clk_stm32_rcc_regs_unlock(void) { - stm32mp1_clk_unlock(®_lock); + _clk_unlock(®_lock); } #define TIMEOUT_US_1S U(1000000) @@ -224,6 +224,15 @@ const struct clk_stm32 *_clk_get(struct stm32_clk_priv *priv, int id) return NULL; } +static const struct stm32_clk_ops *_clk_get_ops(struct stm32_clk_priv *priv, int id) +{ + const struct clk_stm32 *clk = _clk_get(priv, id); + + assert(clk->ops != NO_OPS); + + return priv->ops_array[clk->ops]; +} + #define clk_div_mask(_width) GENMASK(((_width) - 1U), 0U) static unsigned int _get_table_div(const struct clk_div_table *table, @@ -377,7 +386,7 @@ int _clk_stm32_set_parent_by_index(struct stm32_clk_priv *priv, int clk, int sel int _clk_stm32_get_parent(struct stm32_clk_priv *priv, int clk_id) { - const struct clk_stm32 *clk = _clk_get(priv, clk_id); + const struct stm32_clk_ops *ops = _clk_get_ops(priv, clk_id); const struct parent_cfg *parent; uint16_t mux_id; int sel; @@ -394,8 +403,8 @@ int _clk_stm32_get_parent(struct stm32_clk_priv *priv, int clk_id) mux_id &= MUX_PARENT_MASK; parent = &priv->parents[mux_id]; - if (clk->ops->get_parent != NULL) { - sel = clk->ops->get_parent(priv, clk_id); + if (ops->get_parent != NULL) { + sel = ops->get_parent(priv, clk_id); } else { sel = clk_mux_get_parent(priv, mux_id); } @@ -464,7 +473,7 @@ int clk_get_index(struct stm32_clk_priv *priv, unsigned long binding_id) unsigned long _clk_stm32_get_rate(struct stm32_clk_priv *priv, int id) { - const struct clk_stm32 *clk = _clk_get(priv, id); + const struct stm32_clk_ops *ops = _clk_get_ops(priv, id); int parent; if ((unsigned int)id >= priv->num) { @@ -476,14 +485,14 @@ unsigned long _clk_stm32_get_rate(struct stm32_clk_priv *priv, int id) return 0UL; } - if (clk->ops->recalc_rate != NULL) { + if (ops->recalc_rate != NULL) { unsigned long prate = 0UL; if (parent != CLK_IS_ROOT) { prate = _clk_stm32_get_rate(priv, parent); } - return clk->ops->recalc_rate(priv, id, prate); + return ops->recalc_rate(priv, id, prate); } if (parent == CLK_IS_ROOT) { @@ -520,10 +529,10 @@ bool _stm32_clk_is_flags(struct stm32_clk_priv *priv, int id, uint8_t flag) int clk_stm32_enable_call_ops(struct stm32_clk_priv *priv, uint16_t id) { - const struct clk_stm32 *clk = _clk_get(priv, id); + const struct stm32_clk_ops *ops = _clk_get_ops(priv, id); - if (clk->ops->enable != NULL) { - clk->ops->enable(priv, id); + if (ops->enable != NULL) { + ops->enable(priv, id); } return 0; @@ -550,7 +559,7 @@ static int _clk_stm32_enable_core(struct stm32_clk_priv *priv, int id) priv->gate_refcounts[id]++; - if (priv->gate_refcounts[id] == UINT_MAX) { + if (priv->gate_refcounts[id] == UINT8_MAX) { ERROR("%s: %d max enable count !", __func__, id); panic(); } @@ -562,19 +571,19 @@ int _clk_stm32_enable(struct stm32_clk_priv *priv, int id) { int ret; - stm32mp1_clk_lock(&refcount_lock); + _clk_lock(&refcount_lock); ret = _clk_stm32_enable_core(priv, id); - stm32mp1_clk_unlock(&refcount_lock); + _clk_unlock(&refcount_lock); return ret; } void clk_stm32_disable_call_ops(struct stm32_clk_priv *priv, uint16_t id) { - const struct clk_stm32 *clk = _clk_get(priv, id); + const struct stm32_clk_ops *ops = _clk_get_ops(priv, id); - if (clk->ops->disable != NULL) { - clk->ops->disable(priv, id); + if (ops->disable != NULL) { + ops->disable(priv, id); } } @@ -610,19 +619,19 @@ static void _clk_stm32_disable_core(struct stm32_clk_priv *priv, int id) void _clk_stm32_disable(struct stm32_clk_priv *priv, int id) { - stm32mp1_clk_lock(&refcount_lock); + _clk_lock(&refcount_lock); _clk_stm32_disable_core(priv, id); - stm32mp1_clk_unlock(&refcount_lock); + _clk_unlock(&refcount_lock); } bool _clk_stm32_is_enabled(struct stm32_clk_priv *priv, int id) { - const struct clk_stm32 *clk = _clk_get(priv, id); + const struct stm32_clk_ops *ops = _clk_get_ops(priv, id); - if (clk->ops->is_enabled != NULL) { - return clk->ops->is_enabled(priv, id); + if (ops->is_enabled != NULL) { + return ops->is_enabled(priv, id); } return priv->gate_refcounts[id]; @@ -957,6 +966,10 @@ int clk_stm32_osc_gate_enable(struct stm32_clk_priv *priv, int id) { struct clk_oscillator_data *osc_data = clk_oscillator_get_data(priv, id); + if (osc_data->frequency == 0UL) { + return 0; + } + _clk_stm32_gate_enable(priv, osc_data->gate_id); if (_clk_stm32_gate_wait_ready(priv, osc_data->gate_rdy_id, true) != 0U) { @@ -971,6 +984,10 @@ void clk_stm32_osc_gate_disable(struct stm32_clk_priv *priv, int id) { struct clk_oscillator_data *osc_data = clk_oscillator_get_data(priv, id); + if (osc_data->frequency == 0UL) { + return; + } + _clk_stm32_gate_disable(priv, osc_data->gate_id); if (_clk_stm32_gate_wait_ready(priv, osc_data->gate_rdy_id, false) != 0U) { @@ -1073,12 +1090,10 @@ int clk_stm32_init(struct stm32_clk_priv *priv, uintptr_t base) priv->base = base; for (i = 0U; i < priv->num; i++) { - const struct clk_stm32 *clk = _clk_get(priv, i); - - assert(clk->ops != NULL); + const struct stm32_clk_ops *ops = _clk_get_ops(priv, i); - if (clk->ops->init != NULL) { - clk->ops->init(priv, i); + if (ops->init != NULL) { + ops->init(priv, i); } } diff --git a/drivers/st/clk/clk-stm32-core.h b/drivers/st/clk/clk-stm32-core.h index 8bfb5134..bfb5f119 100644 --- a/drivers/st/clk/clk-stm32-core.h +++ b/drivers/st/clk/clk-stm32-core.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2022, STMicroelectronics - All Rights Reserved + * Copyright (C) 2022-2024, STMicroelectronics - All Rights Reserved * * SPDX-License-Identifier: GPL-2.0+ OR BSD-3-Clause */ @@ -21,23 +21,23 @@ struct gate_cfg { }; struct clk_div_table { - unsigned int val; - unsigned int div; + uint16_t val; + uint16_t div; }; struct div_cfg { + const struct clk_div_table *table; uint16_t offset; uint8_t shift; uint8_t width; uint8_t flags; uint8_t bitrdy; - const struct clk_div_table *table; }; struct parent_cfg { - uint8_t num_parents; const uint16_t *id_parents; struct mux_cfg *mux; + uint8_t num_parents; }; struct stm32_clk_priv; @@ -56,9 +56,9 @@ struct stm32_clk_ops { struct clk_stm32 { uint16_t binding; uint16_t parent; + uint8_t ops; uint8_t flags; void *clock_cfg; - const struct stm32_clk_ops *ops; }; struct stm32_clk_priv { @@ -73,8 +73,9 @@ struct stm32_clk_priv { const uint32_t nb_div; struct clk_oscillator_data *osci_data; const uint32_t nb_osci_data; - uint32_t *gate_refcounts; + uint8_t *gate_refcounts; void *pdata; + const struct stm32_clk_ops **ops_array; }; struct stm32_clk_bypass { @@ -97,18 +98,14 @@ struct stm32_clk_drive { struct clk_oscillator_data { const char *name; - uint16_t id_clk; - unsigned long frequency; - uint16_t gate_id; - uint16_t gate_rdy_id; struct stm32_clk_bypass *bypass; struct stm32_clk_css *css; struct stm32_clk_drive *drive; -}; + unsigned long frequency; + uint16_t id_clk; + uint16_t gate_id; + uint16_t gate_rdy_id; -struct clk_fixed_rate { - const char *name; - unsigned long fixed_rate; }; struct clk_gate_cfg { @@ -144,6 +141,9 @@ struct clk_gate_cfg { #define MASK_WIDTH_SHIFT(_width, _shift) \ GENMASK(((_width) + (_shift) - 1U), (_shift)) +void clk_stm32_rcc_regs_lock(void); +void clk_stm32_rcc_regs_unlock(void); + int clk_stm32_init(struct stm32_clk_priv *priv, uintptr_t base); void clk_stm32_enable_critical_clocks(void); @@ -218,7 +218,7 @@ void clk_stm32_display_clock_info(void); #endif struct clk_stm32_div_cfg { - int id; + uint8_t id; }; #define STM32_DIV(idx, _binding, _parent, _flags, _div_id) \ @@ -229,11 +229,11 @@ struct clk_stm32_div_cfg { .clock_cfg = &(struct clk_stm32_div_cfg){\ .id = (_div_id),\ },\ - .ops = &clk_stm32_divider_ops,\ + .ops = STM32_DIVIDER_OPS,\ } struct clk_stm32_gate_cfg { - int id; + uint8_t id; }; #define STM32_GATE(idx, _binding, _parent, _flags, _gate_id) \ @@ -244,12 +244,12 @@ struct clk_stm32_gate_cfg { .clock_cfg = &(struct clk_stm32_gate_cfg){\ .id = (_gate_id),\ },\ - .ops = &clk_stm32_gate_ops,\ + .ops = STM32_GATE_OPS,\ } struct fixed_factor_cfg { - unsigned int mult; - unsigned int div; + uint8_t mult; + uint8_t div; }; unsigned long fixed_factor_recalc_rate(struct stm32_clk_priv *priv, @@ -263,7 +263,7 @@ unsigned long fixed_factor_recalc_rate(struct stm32_clk_priv *priv, .mult = (_mult),\ .div = (_div),\ },\ - .ops = &clk_fixed_factor_ops,\ + .ops = FIXED_FACTOR_OPS,\ } #define GATE(idx, _binding, _parent, _flags, _offset, _bit_idx) \ @@ -275,7 +275,7 @@ unsigned long fixed_factor_recalc_rate(struct stm32_clk_priv *priv, .offset = (_offset),\ .bit_idx = (_bit_idx),\ },\ - .ops = &clk_gate_ops,\ + .ops = GATE_OPS,\ } #define STM32_MUX(idx, _binding, _mux_id, _flags) \ @@ -284,7 +284,7 @@ unsigned long fixed_factor_recalc_rate(struct stm32_clk_priv *priv, .parent = (MUX(_mux_id)),\ .flags = (_flags),\ .clock_cfg = NULL,\ - .ops = (&clk_mux_ops),\ + .ops = STM32_MUX_OPS\ } struct clk_timer_cfg { @@ -301,7 +301,7 @@ struct clk_timer_cfg { .apbdiv = (_apbdiv),\ .timpre = (_timpre),\ },\ - .ops = &clk_timer_ops,\ + .ops = STM32_TIMER_OPS,\ } struct clk_stm32_fixed_rate_cfg { @@ -315,7 +315,7 @@ struct clk_stm32_fixed_rate_cfg { .clock_cfg = &(struct clk_stm32_fixed_rate_cfg){\ .rate = (_rate),\ },\ - .ops = &clk_stm32_fixed_rate_ops,\ + .ops = STM32_FIXED_RATE_OPS,\ } #define BYPASS(_offset, _bit_byp, _bit_digbyp) &(struct stm32_clk_bypass){\ @@ -355,7 +355,7 @@ int clk_stm32_osc_gate_enable(struct stm32_clk_priv *priv, int id); void clk_stm32_osc_gate_disable(struct stm32_clk_priv *priv, int id); struct stm32_osc_cfg { - int osc_id; + uint8_t osc_id; }; #define CLK_OSC(idx, _idx, _parent, _osc_id) \ @@ -366,7 +366,7 @@ struct stm32_osc_cfg { .clock_cfg = &(struct stm32_osc_cfg){\ .osc_id = (_osc_id),\ },\ - .ops = &clk_stm32_osc_ops,\ + .ops = STM32_OSC_OPS,\ } #define CLK_OSC_FIXED(idx, _idx, _parent, _osc_id) \ @@ -377,7 +377,7 @@ struct stm32_osc_cfg { .clock_cfg = &(struct stm32_osc_cfg){\ .osc_id = (_osc_id),\ },\ - .ops = &clk_stm32_osc_nogate_ops,\ + .ops = STM32_OSC_NOGATE_OPS,\ } extern const struct stm32_clk_ops clk_mux_ops; @@ -390,4 +390,19 @@ extern const struct stm32_clk_ops clk_stm32_fixed_rate_ops; extern const struct stm32_clk_ops clk_stm32_osc_ops; extern const struct stm32_clk_ops clk_stm32_osc_nogate_ops; +enum { + NO_OPS, + FIXED_FACTOR_OPS, + GATE_OPS, + STM32_MUX_OPS, + STM32_DIVIDER_OPS, + STM32_GATE_OPS, + STM32_TIMER_OPS, + STM32_FIXED_RATE_OPS, + STM32_OSC_OPS, + STM32_OSC_NOGATE_OPS, + + STM32_LAST_OPS +}; + #endif /* CLK_STM32_CORE_H */ diff --git a/drivers/st/clk/clk-stm32mp13.c b/drivers/st/clk/clk-stm32mp13.c index 01d17640..fd620492 100644 --- a/drivers/st/clk/clk-stm32mp13.c +++ b/drivers/st/clk/clk-stm32mp13.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2022-2023, STMicroelectronics - All Rights Reserved + * Copyright (C) 2022-2024, STMicroelectronics - All Rights Reserved * * SPDX-License-Identifier: GPL-2.0+ OR BSD-3-Clause */ @@ -186,65 +186,7 @@ enum stm32_clock { _MCE, _FMC_K, _QSPI_K, -#if defined(IMAGE_BL32) - _LTDC, - _DMA1, - _DMA2, - _MDMA, - _ETH1MAC, - _USBH, - _TIM2, - _TIM3, - _TIM4, - _TIM5, - _TIM6, - _TIM7, - _LPTIM1_K, - _SPI2_K, - _SPI3_K, - _SPDIF_K, - _TIM1, - _TIM8, - _SPI1_K, - _SAI1_K, - _SAI2_K, - _DFSDM, - _FDCAN_K, - _TIM13, - _TIM14, - _TIM16, - _TIM17, - _SPI4_K, - _SPI5_K, - _I2C1_K, - _I2C2_K, - _ADFSDM, - _LPTIM2_K, - _LPTIM3_K, - _LPTIM4_K, - _LPTIM5_K, - _VREF, - _DTS, - _PMBCTRL, - _HDP, - _STGENRO, - _DCMIPP_K, - _DMAMUX1, - _DMAMUX2, - _DMA3, - _ADC1_K, - _ADC2_K, - _TSC, - _AXIMC, - _ETH1CK, - _ETH1TX, - _ETH1RX, - _CRC1, - _ETH2CK, - _ETH2TX, - _ETH2RX, - _ETH2MAC, -#endif + CK_LAST }; @@ -947,7 +889,7 @@ static bool pll4_bootrom; #endif /* RCC clock device driver private */ -static unsigned int refcounts_mp13[CK_LAST]; +static uint8_t refcounts_mp13[CK_LAST]; static const struct stm32_clk_pll *clk_st32_pll_data(unsigned int idx); @@ -1007,6 +949,11 @@ static void stm32_enable_oscillator_lse(struct stm32_clk_priv *priv) return; } + /* Do not reconfigure LSE if already enabled */ + if (_clk_stm32_gate_is_enabled(priv, osc_data->gate_id)) { + return; + } + clk_oscillator_set_bypass(priv, _CK_LSE, digbyp, bypass); clk_oscillator_set_drive(priv, _CK_LSE, drive); @@ -1027,8 +974,8 @@ static int stm32mp1_set_hsidiv(uint8_t hsidiv) timeout = timeout_init_us(HSIDIV_TIMEOUT); while ((mmio_read_32(address) & RCC_OCRDYR_HSIDIVRDY) == 0U) { if (timeout_elapsed(timeout)) { - ERROR("HSIDIV failed @ 0x%lx: 0x%x\n", - address, mmio_read_32(address)); + EARLY_ERROR("HSIDIV failed @ 0x%lx: 0x%x\n", + address, mmio_read_32(address)); return -ETIMEDOUT; } } @@ -1050,7 +997,7 @@ static int stm32mp1_hsidiv(unsigned long hsifreq) } if (hsidiv == 4U) { - ERROR("Invalid clk-hsi frequency\n"); + EARLY_ERROR("Invalid clk-hsi frequency\n"); return -EINVAL; } @@ -1292,7 +1239,7 @@ static void clk_stm32_pll_config_vco(struct stm32_clk_priv *priv, uint32_t value = 0; if (clk_stm32_pll_compute_cfgr1(priv, pll, vco, &value) != 0) { - ERROR("Invalid Vref clock !\n"); + EARLY_ERROR("Invalid Vref clock !\n"); panic(); } @@ -1352,6 +1299,123 @@ static inline struct stm32_pll_dt_cfg *clk_stm32_pll_get_pdata(int pll_idx) return &pdata->pll[pll_idx]; } +/* Define characteristic for PLL1 : PLL_2000 */ +#define POST_DIVM_MIN 8000000U +#define POST_DIVM_MAX 16000000U +#define DIVM_MIN 0U +#define DIVM_MAX 63U +#define DIVN_MIN 24U +#define DIVN_MAX 99U +#define DIVP_MIN 0U +#define DIVP_MAX 127U +#define FRAC_MAX 8192U +#define VCO_MIN 992000000U +#define VCO_MAX 2000000000U + +static int clk_compute_pll1_settings(uint32_t freq_khz) +{ + struct stm32_clk_priv *priv = clk_stm32_get_priv(); + struct stm32_pll_dt_cfg *pll1 = clk_stm32_pll_get_pdata(_PLL1); + struct stm32_pll_dt_cfg *pll2 = clk_stm32_pll_get_pdata(_PLL2); + unsigned long long best_diff = ULLONG_MAX; + unsigned int divm; + unsigned long input_freq = 0UL; + uint32_t src = pll2->vco.src; + + /* PLL1 share the same clock source than PLL2 */ + switch (src) { + case CLK_PLL12_HSI: + input_freq = _clk_stm32_get_rate(priv, _CK_HSI); + break; + case CLK_PLL12_HSE: + input_freq = _clk_stm32_get_rate(priv, _CK_HSE); + break; + default: + break; + } + + if (input_freq == 0UL) { + panic(); + } + + /* Following parameters have always the same value */ + pll1->output.output[PLL_CFG_Q] = 0U; + pll1->output.output[PLL_CFG_R] = 0U; + + for (divm = (DIVM_MAX + 1U); divm != DIVM_MIN; divm--) { + unsigned long post_divm = input_freq / divm; + unsigned int divp; + + if ((post_divm < POST_DIVM_MIN) || (post_divm > POST_DIVM_MAX)) { + continue; + } + + for (divp = DIVP_MIN; divp <= DIVP_MAX; divp++) { + unsigned long long output_freq = freq_khz * 1000ULL; + unsigned long long freq; + unsigned long long divn; + unsigned long long frac; + unsigned int i; + + freq = output_freq * divm * (divp + 1U); + + divn = (freq / input_freq) - 1U; + if ((divn < DIVN_MIN) || (divn > DIVN_MAX)) { + continue; + } + + frac = ((freq * FRAC_MAX) / input_freq) - ((divn + 1U) * FRAC_MAX); + + /* 2 loops to refine the fractional part */ + for (i = 2U; i != 0U; i--) { + unsigned long long diff; + unsigned long long vco; + + if (frac > FRAC_MAX) { + break; + } + + vco = (post_divm * (divn + 1U)) + ((post_divm * frac) / FRAC_MAX); + + if ((vco < (VCO_MIN / 2U)) || (vco > (VCO_MAX / 2U))) { + frac++; + continue; + } + + freq = vco / (divp + 1U); + if (output_freq < freq) { + diff = freq - output_freq; + } else { + diff = output_freq - freq; + } + + if (diff < best_diff) { + pll1->vco.src = src; + pll1->vco.status = RCC_PLLNCR_DIVPEN | RCC_PLLNCR_PLLON; + pll1->vco.div_mn[PLL_CFG_M] = divm - 1U; + pll1->vco.div_mn[PLL_CFG_N] = (uint32_t)divn; + pll1->vco.frac = (uint32_t)frac; + pll1->output.output[PLL_CFG_P] = divp; + + if (diff == 0U) { + return 0; + } + + best_diff = diff; + } + + frac++; + } + } + } + + if (best_diff == ULLONG_MAX) { + return -EINVAL; + } + + return 0; +} + static bool _clk_stm32_pll_is_enabled(struct stm32_clk_priv *priv, const struct stm32_clk_pll *pll) { uintptr_t pll_base = priv->base + pll->reg_pllxcr; @@ -1388,8 +1452,8 @@ static int _clk_stm32_pll_wait_ready_on(struct stm32_clk_priv *priv, /* Wait PLL lock */ while ((mmio_read_32(pll_base) & RCC_PLLNCR_PLLRDY) == 0U) { if (timeout_elapsed(timeout)) { - ERROR("%d clock start failed @ 0x%x: 0x%x\n", - pll->clk_id, pll->reg_pllxcr, mmio_read_32(pll_base)); + ERROR("PLL%d start failed @ 0x%x: 0x%x\n", + pll->clk_id - _CK_PLL1 + 1, pll->reg_pllxcr, mmio_read_32(pll_base)); return -EINVAL; } } @@ -1406,8 +1470,8 @@ static int _clk_stm32_pll_wait_ready_off(struct stm32_clk_priv *priv, /* Wait PLL lock */ while ((mmio_read_32(pll_base) & RCC_PLLNCR_PLLRDY) != 0U) { if (timeout_elapsed(timeout)) { - ERROR("%d clock stop failed @ 0x%x: 0x%x\n", - pll->clk_id, pll->reg_pllxcr, mmio_read_32(pll_base)); + ERROR("PLL%d stop failed @ 0x%x: 0x%x\n", + pll->clk_id - _CK_PLL1 + 1, pll->reg_pllxcr, mmio_read_32(pll_base)); return -EINVAL; } } @@ -1629,7 +1693,7 @@ static const struct stm32_clk_pll *clk_st32_pll_data(unsigned int idx) } struct stm32_pll_cfg { - int pll_id; + uint8_t pll_id; }; static unsigned long clk_stm32_pll_recalc_rate(struct stm32_clk_priv *priv, int id, @@ -1711,12 +1775,12 @@ static const struct stm32_clk_ops clk_stm32_pll_ops = { .clock_cfg = &(struct stm32_pll_cfg) {\ .pll_id = _pll_id,\ },\ - .ops = &clk_stm32_pll_ops,\ + .ops = STM32_PLL_OPS,\ } struct clk_stm32_composite_cfg { - int gate_id; - int div_id; + uint8_t gate_id; + uint8_t div_id; }; static unsigned long clk_stm32_composite_recalc_rate(struct stm32_clk_priv *priv, @@ -1768,9 +1832,32 @@ static const struct stm32_clk_ops clk_stm32_composite_ops = { .gate_id = (_gate_id),\ .div_id = (_div_id),\ },\ - .ops = &clk_stm32_composite_ops,\ + .ops = STM32_COMPOSITE_OPS,\ } +enum { + STM32_PLL_OPS = STM32_LAST_OPS, + STM32_COMPOSITE_OPS, + + MP13_LAST_OPS +}; + +static const struct stm32_clk_ops *ops_array_mp13[MP13_LAST_OPS] = { + [NO_OPS] = NULL, + [FIXED_FACTOR_OPS] = &clk_fixed_factor_ops, + [GATE_OPS] = &clk_gate_ops, + [STM32_MUX_OPS] = &clk_mux_ops, + [STM32_DIVIDER_OPS] = &clk_stm32_divider_ops, + [STM32_GATE_OPS] = &clk_stm32_gate_ops, + [STM32_TIMER_OPS] = &clk_timer_ops, + [STM32_FIXED_RATE_OPS] = &clk_stm32_fixed_rate_ops, + [STM32_OSC_OPS] = &clk_stm32_osc_ops, + [STM32_OSC_NOGATE_OPS] = &clk_stm32_osc_nogate_ops, + + [STM32_PLL_OPS] = &clk_stm32_pll_ops, + [STM32_COMPOSITE_OPS] = &clk_stm32_composite_ops +}; + static const struct clk_stm32 stm32mp13_clk[CK_LAST] = { /* ROOT CLOCKS */ CLK_FIXED_RATE(_CK_OFF, _NO_ID, 0), @@ -1882,7 +1969,6 @@ static const struct clk_stm32 stm32mp13_clk[CK_LAST] = { STM32_GATE(_SDMMC2_K, SDMMC2_K, MUX(MUX_SDMMC2), 0, GATE_SDMMC2), STM32_GATE(_DBGCK, CK_DBG, _CKAXI, 0, GATE_DBGCK), -/* TODO: CHECK CLOCK FOR BL2/BL32 AND IF ONLY FOR TEST OR NOT */ STM32_GATE(_USART3_K, USART3_K, MUX(MUX_UART35), 0, GATE_USART3), STM32_GATE(_UART4_K, UART4_K, MUX(MUX_UART4), 0, GATE_UART4), STM32_GATE(_UART5_K, UART5_K, MUX(MUX_UART35), 0, GATE_UART5), @@ -1896,61 +1982,6 @@ static const struct clk_stm32 stm32mp13_clk[CK_LAST] = { STM32_COMPOSITE(_MCO1_K, CK_MCO1, MUX(MUX_MCO1), 0, GATE_MCO1, DIV_MCO1), STM32_COMPOSITE(_MCO2_K, CK_MCO2, MUX(MUX_MCO2), 0, GATE_MCO2, DIV_MCO2), STM32_COMPOSITE(_TRACECK, CK_TRACE, _CKAXI, 0, GATE_TRACECK, DIV_TRACE), - -#if defined(IMAGE_BL32) - STM32_GATE(_TIM2, TIM2_K, _CKTIMG1, 0, GATE_TIM2), - STM32_GATE(_TIM3, TIM3_K, _CKTIMG1, 0, GATE_TIM3), - STM32_GATE(_TIM4, TIM4_K, _CKTIMG1, 0, GATE_TIM4), - STM32_GATE(_TIM5, TIM5_K, _CKTIMG1, 0, GATE_TIM5), - STM32_GATE(_TIM6, TIM6_K, _CKTIMG1, 0, GATE_TIM6), - STM32_GATE(_TIM7, TIM7_K, _CKTIMG1, 0, GATE_TIM7), - STM32_GATE(_TIM13, TIM13_K, _CKTIMG3, 0, GATE_TIM13), - STM32_GATE(_TIM14, TIM14_K, _CKTIMG3, 0, GATE_TIM14), - STM32_GATE(_LPTIM1_K, LPTIM1_K, MUX(MUX_LPTIM1), 0, GATE_LPTIM1), - STM32_GATE(_SPI2_K, SPI2_K, MUX(MUX_SPI23), 0, GATE_SPI2), - STM32_GATE(_SPI3_K, SPI3_K, MUX(MUX_SPI23), 0, GATE_SPI3), - STM32_GATE(_SPDIF_K, SPDIF_K, MUX(MUX_SPDIF), 0, GATE_SPDIF), - STM32_GATE(_TIM1, TIM1_K, _CKTIMG2, 0, GATE_TIM1), - STM32_GATE(_TIM8, TIM8_K, _CKTIMG2, 0, GATE_TIM8), - STM32_GATE(_TIM16, TIM16_K, _CKTIMG3, 0, GATE_TIM16), - STM32_GATE(_TIM17, TIM17_K, _CKTIMG3, 0, GATE_TIM17), - STM32_GATE(_SPI1_K, SPI1_K, MUX(MUX_SPI1), 0, GATE_SPI1), - STM32_GATE(_SPI4_K, SPI4_K, MUX(MUX_SPI4), 0, GATE_SPI4), - STM32_GATE(_SPI5_K, SPI5_K, MUX(MUX_SPI5), 0, GATE_SPI5), - STM32_GATE(_SAI1_K, SAI1_K, MUX(MUX_SAI1), 0, GATE_SAI1), - STM32_GATE(_SAI2_K, SAI2_K, MUX(MUX_SAI2), 0, GATE_SAI2), - STM32_GATE(_DFSDM, DFSDM_K, MUX(MUX_SAI1), 0, GATE_DFSDM), - STM32_GATE(_FDCAN_K, FDCAN_K, MUX(MUX_FDCAN), 0, GATE_FDCAN), - STM32_GATE(_USBH, USBH, _CKAXI, 0, GATE_USBH), - STM32_GATE(_I2C1_K, I2C1_K, MUX(MUX_I2C12), 0, GATE_I2C1), - STM32_GATE(_I2C2_K, I2C2_K, MUX(MUX_I2C12), 0, GATE_I2C2), - STM32_GATE(_ADFSDM, ADFSDM_K, MUX(MUX_SAI1), 0, GATE_ADFSDM), - STM32_GATE(_LPTIM2_K, LPTIM2_K, MUX(MUX_LPTIM2), 0, GATE_LPTIM2), - STM32_GATE(_LPTIM3_K, LPTIM3_K, MUX(MUX_LPTIM3), 0, GATE_LPTIM3), - STM32_GATE(_LPTIM4_K, LPTIM4_K, MUX(MUX_LPTIM45), 0, GATE_LPTIM4), - STM32_GATE(_LPTIM5_K, LPTIM5_K, MUX(MUX_LPTIM45), 0, GATE_LPTIM5), - STM32_GATE(_VREF, VREF, _PCLK3, 0, GATE_VREF), - STM32_GATE(_DTS, TMPSENS, _PCLK3, 0, GATE_DTS), - STM32_GATE(_PMBCTRL, PMBCTRL, _PCLK3, 0, GATE_HDP), - STM32_GATE(_HDP, HDP, _PCLK3, 0, GATE_PMBCTRL), - STM32_GATE(_STGENRO, STGENRO, _PCLK4, 0, GATE_DCMIPP), - STM32_GATE(_DCMIPP_K, DCMIPP_K, MUX(MUX_DCMIPP), 0, GATE_DCMIPP), - STM32_GATE(_DMAMUX1, DMAMUX1, _CKAXI, 0, GATE_DMAMUX1), - STM32_GATE(_DMAMUX2, DMAMUX2, _CKAXI, 0, GATE_DMAMUX2), - STM32_GATE(_DMA3, DMA3, _CKAXI, 0, GATE_DMAMUX2), - STM32_GATE(_ADC1_K, ADC1_K, MUX(MUX_ADC1), 0, GATE_ADC1), - STM32_GATE(_ADC2_K, ADC2_K, MUX(MUX_ADC2), 0, GATE_ADC2), - STM32_GATE(_TSC, TSC, _CKAXI, 0, GATE_TSC), - STM32_GATE(_AXIMC, AXIMC, _CKAXI, 0, GATE_AXIMC), - STM32_GATE(_CRC1, CRC1, _CKAXI, 0, GATE_ETH1TX), - STM32_GATE(_ETH1CK, ETH1CK_K, MUX(MUX_ETH1), 0, GATE_ETH1CK), - STM32_GATE(_ETH1TX, ETH1TX, _CKAXI, 0, GATE_ETH1TX), - STM32_GATE(_ETH1RX, ETH1RX, _CKAXI, 0, GATE_ETH1RX), - STM32_GATE(_ETH2CK, ETH2CK_K, MUX(MUX_ETH2), 0, GATE_ETH2CK), - STM32_GATE(_ETH2TX, ETH2TX, _CKAXI, 0, GATE_ETH2TX), - STM32_GATE(_ETH2RX, ETH2RX, _CKAXI, 0, GATE_ETH2RX), - STM32_GATE(_ETH2MAC, ETH2MAC, _CKAXI, 0, GATE_ETH2MAC), -#endif }; static struct stm32_pll_dt_cfg mp13_pll[_PLL_NB]; @@ -1986,6 +2017,7 @@ static struct stm32_clk_priv stm32mp13_clock_data = { .nb_osci_data = ARRAY_SIZE(stm32mp13_osc_data), .gate_refcounts = refcounts_mp13, .pdata = &stm32mp13_clock_pdata, + .ops_array = ops_array_mp13, }; static int stm32mp1_init_clock_tree(void) @@ -2072,8 +2104,6 @@ static int stm32mp1_init_clock_tree(void) return 0; } -#define LSEDRV_MEDIUM_HIGH 2 - static int clk_stm32_parse_oscillator_fdt(void *fdt, int node, const char *name, struct stm32_osci_dt_cfg *osci) { @@ -2241,7 +2271,8 @@ static int stm32_clk_parse_fdt_all_pll(void *fdt, int node, struct stm32_clk_pla { size_t i = 0U; - for (i = _PLL1; i < pdata->npll; i++) { + /* PLL1 is not configurable with device tree */ + for (i = _PLL2; i < pdata->npll; i++) { struct stm32_pll_dt_cfg *pll = &pdata->pll[i]; char name[RCC_PLL_NAME_SIZE]; int subnode = 0; @@ -2301,32 +2332,47 @@ static int stm32_clk_parse_fdt(struct stm32_clk_platdata *pdata) return 0; } -int stm32mp1_clk_init(void) +void stm32mp1_clk_rcc_regs_lock(void) { - return 0; + clk_stm32_rcc_regs_lock(); } -int stm32mp1_clk_probe(void) +void stm32mp1_clk_rcc_regs_unlock(void) +{ + clk_stm32_rcc_regs_unlock(); +} + +int stm32mp1_clk_init(void) { - uintptr_t base = RCC_BASE; int ret; - ret = stm32_clk_parse_fdt(&stm32mp13_clock_pdata); + /* compute the PLL1 settings, not read in device tree */ + ret = clk_compute_pll1_settings(PLL1_NOMINAL_FREQ_IN_KHZ); if (ret != 0) { return ret; } - ret = clk_stm32_init(&stm32mp13_clock_data, base); + ret = stm32mp1_init_clock_tree(); if (ret != 0) { return ret; } - ret = stm32mp1_init_clock_tree(); + clk_stm32_enable_critical_clocks(); + + return 0; +} + +int stm32mp1_clk_probe(void) +{ + uintptr_t base = RCC_BASE; + int ret; + + ret = stm32_clk_parse_fdt(&stm32mp13_clock_pdata); if (ret != 0) { return ret; } - clk_stm32_enable_critical_clocks(); + ret = clk_stm32_init(&stm32mp13_clock_data, base); - return 0; + return ret; } diff --git a/drivers/st/clk/clk-stm32mp2.c b/drivers/st/clk/clk-stm32mp2.c new file mode 100644 index 00000000..12839f1a --- /dev/null +++ b/drivers/st/clk/clk-stm32mp2.c @@ -0,0 +1,2357 @@ +/* + * Copyright (C) 2024, STMicroelectronics - All Rights Reserved + * + * SPDX-License-Identifier: GPL-2.0+ OR BSD-3-Clause + */ + +#include <assert.h> +#include <errno.h> +#include <limits.h> +#include <stdint.h> + +#include "clk-stm32-core.h" +#include <common/fdt_wrappers.h> +#include <drivers/clk.h> +#include <drivers/delay_timer.h> +#include <drivers/generic_delay_timer.h> +#include <drivers/st/stm32mp2_clk.h> +#include <drivers/st/stm32mp_clkfunc.h> +#include <lib/mmio.h> +#include <lib/spinlock.h> +#include <lib/utils_def.h> +#include <libfdt.h> + +#include <platform_def.h> + +struct stm32_osci_dt_cfg { + unsigned long freq; + uint32_t drive; + bool bypass; + bool digbyp; + bool css; +}; + +struct stm32_pll_dt_cfg { + uint32_t src; + uint32_t frac; + uint32_t cfg[PLLCFG_NB]; + uint32_t csg[PLLCSG_NB]; + bool csg_enabled; + bool enabled; +}; + +struct stm32_clk_platdata { + uintptr_t rcc_base; + uint32_t nosci; + struct stm32_osci_dt_cfg *osci; + uint32_t npll; + struct stm32_pll_dt_cfg *pll; + uint32_t nflexgen; + uint32_t *flexgen; + uint32_t nbusclk; + uint32_t *busclk; + uint32_t nkernelclk; + uint32_t *kernelclk; +}; + +/* A35 Sub-System which manages its own PLL (PLL1) */ +#define A35_SS_CHGCLKREQ 0x0000 +#define A35_SS_PLL_FREQ1 0x0080 +#define A35_SS_PLL_FREQ2 0x0090 +#define A35_SS_PLL_ENABLE 0x00a0 + +#define A35_SS_CHGCLKREQ_ARM_CHGCLKREQ BIT(0) +#define A35_SS_CHGCLKREQ_ARM_CHGCLKACK BIT(1) + +#define A35_SS_PLL_FREQ1_FBDIV_MASK GENMASK(11, 0) +#define A35_SS_PLL_FREQ1_FBDIV_SHIFT 0 +#define A35_SS_PLL_FREQ1_REFDIV_MASK GENMASK(21, 16) +#define A35_SS_PLL_FREQ1_REFDIV_SHIFT 16 + +#define A35_SS_PLL_FREQ2_POSTDIV1_MASK GENMASK(2, 0) +#define A35_SS_PLL_FREQ2_POSTDIV1_SHIFT 0 +#define A35_SS_PLL_FREQ2_POSTDIV2_MASK GENMASK(5, 3) +#define A35_SS_PLL_FREQ2_POSTDIV2_SHIFT 3 + +#define A35_SS_PLL_ENABLE_PD BIT(0) +#define A35_SS_PLL_ENABLE_LOCKP BIT(1) +#define A35_SS_PLL_ENABLE_NRESET_SWPLL_FF BIT(2) + +#define TIMEOUT_US_200MS U(200000) +#define TIMEOUT_US_1S U(1000000) + +#define PLLRDY_TIMEOUT TIMEOUT_US_200MS +#define CLKSRC_TIMEOUT TIMEOUT_US_200MS +#define CLKDIV_TIMEOUT TIMEOUT_US_200MS +#define OSCRDY_TIMEOUT TIMEOUT_US_1S + +/* PLL minimal frequencies for clock sources */ +#define PLL_REFCLK_MIN UL(5000000) +#define PLL_FRAC_REFCLK_MIN UL(10000000) + +#define XBAR_CHANNEL_NB 64 + +/* Warning, should be start to 1 */ +enum clock { + _CK_0_MHZ, + + /* ROOT CLOCKS */ + _CK_HSI, + _CK_HSE, + _CK_MSI, + _CK_LSI, + _CK_LSE, + _I2SCKIN, + _SPDIFSYMB, + _CK_PLL1, + _CK_PLL2, + _CK_PLL3, + _CK_PLL4, + _CK_PLL5, + _CK_PLL6, + _CK_PLL7, + _CK_PLL8, + _CK_HSE_RTC, + _CK_RTCCK, + _CK_ICN_HS_MCU, + _CK_ICN_SDMMC, + _CK_ICN_DDR, + _CK_ICN_HSL, + _CK_ICN_NIC, + _CK_ICN_LS_MCU, + _CK_FLEXGEN_07, + _CK_FLEXGEN_08, + _CK_FLEXGEN_09, + _CK_FLEXGEN_10, + _CK_FLEXGEN_11, + _CK_FLEXGEN_12, + _CK_FLEXGEN_13, + _CK_FLEXGEN_14, + _CK_FLEXGEN_15, + _CK_FLEXGEN_16, + _CK_FLEXGEN_17, + _CK_FLEXGEN_18, + _CK_FLEXGEN_19, + _CK_FLEXGEN_20, + _CK_FLEXGEN_21, + _CK_FLEXGEN_22, + _CK_FLEXGEN_23, + _CK_FLEXGEN_24, + _CK_FLEXGEN_25, + _CK_FLEXGEN_26, + _CK_FLEXGEN_27, + _CK_FLEXGEN_28, + _CK_FLEXGEN_29, + _CK_FLEXGEN_30, + _CK_FLEXGEN_31, + _CK_FLEXGEN_32, + _CK_FLEXGEN_33, + _CK_FLEXGEN_34, + _CK_FLEXGEN_35, + _CK_FLEXGEN_36, + _CK_FLEXGEN_37, + _CK_FLEXGEN_38, + _CK_FLEXGEN_39, + _CK_FLEXGEN_40, + _CK_FLEXGEN_41, + _CK_FLEXGEN_42, + _CK_FLEXGEN_43, + _CK_FLEXGEN_44, + _CK_FLEXGEN_45, + _CK_FLEXGEN_46, + _CK_FLEXGEN_47, + _CK_FLEXGEN_48, + _CK_FLEXGEN_49, + _CK_FLEXGEN_50, + _CK_FLEXGEN_51, + _CK_FLEXGEN_52, + _CK_FLEXGEN_53, + _CK_FLEXGEN_54, + _CK_FLEXGEN_55, + _CK_FLEXGEN_56, + _CK_FLEXGEN_57, + _CK_FLEXGEN_58, + _CK_FLEXGEN_59, + _CK_FLEXGEN_60, + _CK_FLEXGEN_61, + _CK_FLEXGEN_62, + _CK_FLEXGEN_63, + _CK_ICN_APB1, + _CK_ICN_APB2, + _CK_ICN_APB3, + _CK_ICN_APB4, + _CK_ICN_APBDBG, + _CK_BKPSRAM, + _CK_BSEC, + _CK_CRC, + _CK_CRYP1, + _CK_CRYP2, + _CK_DDR, + _CK_DDRCAPB, + _CK_DDRCP, + _CK_DDRPHYC, + _CK_FMC, + _CK_GPIOA, + _CK_GPIOB, + _CK_GPIOC, + _CK_GPIOD, + _CK_GPIOE, + _CK_GPIOF, + _CK_GPIOG, + _CK_GPIOH, + _CK_GPIOI, + _CK_GPIOJ, + _CK_GPIOK, + _CK_GPIOZ, + _CK_HASH, + _CK_I2C1, + _CK_I2C2, + _CK_I2C3, + _CK_I2C4, + _CK_I2C5, + _CK_I2C6, + _CK_I2C7, + _CK_I2C8, + _CK_IWDG1, + _CK_IWDG2, + _CK_OSPI1, + _CK_OSPI2, + _CK_OSPIIOM, + _CK_PKA, + _CK_RETRAM, + _CK_RNG, + _CK_RTC, + _CK_SAES, + _CK_SDMMC1, + _CK_SDMMC2, + _CK_SRAM1, + _CK_SRAM2, + _CK_STGEN, + _CK_SYSCPU1, + _CK_SYSRAM, + _CK_UART4, + _CK_UART5, + _CK_UART7, + _CK_UART8, + _CK_UART9, + _CK_USART1, + _CK_USART2, + _CK_USART3, + _CK_USART6, + _CK_USB2EHCI, + _CK_USB2OHCI, + _CK_USB2PHY1, + _CK_USB2PHY2, + _CK_USB3DR, + _CK_USB3PCIEPHY, + _CK_USBTC, + + CK_LAST +}; + +static const uint16_t muxsel_src[] = { + _CK_HSI, _CK_HSE, _CK_MSI, _CK_0_MHZ +}; + +static const uint16_t xbarsel_src[] = { + _CK_PLL4, _CK_PLL5, _CK_PLL6, _CK_PLL7, _CK_PLL8, + _CK_HSI, _CK_HSE, _CK_MSI, _CK_HSI, _CK_HSE, _CK_MSI, + _SPDIFSYMB, _I2SCKIN, _CK_LSI, _CK_LSE +}; + +static const uint16_t rtc_src[] = { + _CK_0_MHZ, _CK_LSE, _CK_LSI, _CK_HSE_RTC +}; + +static const uint16_t usb2phy1_src[] = { + _CK_FLEXGEN_57, _CK_HSE +}; + +static const uint16_t usb2phy2_src[] = { + _CK_FLEXGEN_58, _CK_HSE +}; + +static const uint16_t usb3pciphy_src[] = { + _CK_FLEXGEN_34, _CK_HSE +}; + +static const uint16_t d3per_src[] = { + _CK_MSI, _CK_LSI, _CK_LSE +}; + +#define MUX_CONF(id, src, _offset, _shift, _witdh)[id] = {\ + .id_parents = src,\ + .num_parents = ARRAY_SIZE(src),\ + .mux = &(struct mux_cfg) {\ + .offset = (_offset),\ + .shift = (_shift),\ + .width = (_witdh),\ + .bitrdy = UINT8_MAX,\ + },\ +} + +static const struct parent_cfg parent_mp25[] = { + MUX_CONF(MUX_MUXSEL0, muxsel_src, RCC_MUXSELCFGR, 0, 2), + MUX_CONF(MUX_MUXSEL1, muxsel_src, RCC_MUXSELCFGR, 4, 2), + MUX_CONF(MUX_MUXSEL2, muxsel_src, RCC_MUXSELCFGR, 8, 2), + MUX_CONF(MUX_MUXSEL3, muxsel_src, RCC_MUXSELCFGR, 12, 2), + MUX_CONF(MUX_MUXSEL4, muxsel_src, RCC_MUXSELCFGR, 16, 2), + MUX_CONF(MUX_MUXSEL5, muxsel_src, RCC_MUXSELCFGR, 20, 2), + MUX_CONF(MUX_MUXSEL6, muxsel_src, RCC_MUXSELCFGR, 24, 2), + MUX_CONF(MUX_MUXSEL7, muxsel_src, RCC_MUXSELCFGR, 28, 2), + MUX_CONF(MUX_XBARSEL, xbarsel_src, RCC_XBAR0CFGR, 0, 4), + MUX_CONF(MUX_RTC, rtc_src, RCC_BDCR, 16, 2), + MUX_CONF(MUX_USB2PHY1, usb2phy1_src, RCC_USB2PHY1CFGR, 15, 1), + MUX_CONF(MUX_USB2PHY2, usb2phy2_src, RCC_USB2PHY2CFGR, 15, 1), + MUX_CONF(MUX_USB3PCIEPHY, usb3pciphy_src, RCC_USB3PCIEPHYCFGR, 15, 1), + MUX_CONF(MUX_D3PER, d3per_src, RCC_D3DCR, 16, 2), +}; + +/* GATES */ +enum enum_gate_cfg { + GATE_ZERO, /* reserved for no gate */ + GATE_LSE, + GATE_RTCCK, + GATE_LSI, + GATE_HSI, + GATE_MSI, + GATE_HSE, + GATE_LSI_RDY, + GATE_MSI_RDY, + GATE_LSE_RDY, + GATE_HSE_RDY, + GATE_HSI_RDY, + GATE_SYSRAM, + GATE_RETRAM, + GATE_SRAM1, + GATE_SRAM2, + + GATE_DDRPHYC, + GATE_SYSCPU1, + GATE_CRC, + GATE_OSPIIOM, + GATE_BKPSRAM, + GATE_HASH, + GATE_RNG, + GATE_CRYP1, + GATE_CRYP2, + GATE_SAES, + GATE_PKA, + + GATE_GPIOA, + GATE_GPIOB, + GATE_GPIOC, + GATE_GPIOD, + GATE_GPIOE, + GATE_GPIOF, + GATE_GPIOG, + GATE_GPIOH, + GATE_GPIOI, + GATE_GPIOJ, + GATE_GPIOK, + GATE_GPIOZ, + GATE_RTC, + + GATE_DDRCP, + + /* WARNING 2 CLOCKS FOR ONE GATE */ + GATE_USB2OHCI, + GATE_USB2EHCI, + + GATE_USB3DR, + + GATE_BSEC, + GATE_IWDG1, + GATE_IWDG2, + + GATE_DDRCAPB, + GATE_DDR, + + GATE_USART2, + GATE_UART4, + GATE_USART3, + GATE_UART5, + GATE_I2C1, + GATE_I2C2, + GATE_I2C3, + GATE_I2C5, + GATE_I2C4, + GATE_I2C6, + GATE_I2C7, + GATE_USART1, + GATE_USART6, + GATE_UART7, + GATE_UART8, + GATE_UART9, + GATE_STGEN, + GATE_USB3PCIEPHY, + GATE_USBTC, + GATE_I2C8, + GATE_OSPI1, + GATE_OSPI2, + GATE_FMC, + GATE_SDMMC1, + GATE_SDMMC2, + GATE_USB2PHY1, + GATE_USB2PHY2, + LAST_GATE +}; + +#define GATE_CFG(id, _offset, _bit_idx, _offset_clr)[id] = {\ + .offset = (_offset),\ + .bit_idx = (_bit_idx),\ + .set_clr = (_offset_clr),\ +} + +static const struct gate_cfg gates_mp25[LAST_GATE] = { + GATE_CFG(GATE_LSE, RCC_BDCR, 0, 0), + GATE_CFG(GATE_LSI, RCC_BDCR, 9, 0), + GATE_CFG(GATE_RTCCK, RCC_BDCR, 20, 0), + GATE_CFG(GATE_HSI, RCC_OCENSETR, 0, 1), + GATE_CFG(GATE_HSE, RCC_OCENSETR, 8, 1), + GATE_CFG(GATE_MSI, RCC_D3DCR, 0, 0), + + GATE_CFG(GATE_LSI_RDY, RCC_BDCR, 10, 0), + GATE_CFG(GATE_LSE_RDY, RCC_BDCR, 2, 0), + GATE_CFG(GATE_MSI_RDY, RCC_D3DCR, 2, 0), + GATE_CFG(GATE_HSE_RDY, RCC_OCRDYR, 8, 0), + GATE_CFG(GATE_HSI_RDY, RCC_OCRDYR, 0, 0), + GATE_CFG(GATE_SYSRAM, RCC_SYSRAMCFGR, 1, 0), + GATE_CFG(GATE_RETRAM, RCC_RETRAMCFGR, 1, 0), + GATE_CFG(GATE_SRAM1, RCC_SRAM1CFGR, 1, 0), + GATE_CFG(GATE_SRAM2, RCC_SRAM2CFGR, 1, 0), + GATE_CFG(GATE_DDRPHYC, RCC_DDRPHYCAPBCFGR, 1, 0), + GATE_CFG(GATE_SYSCPU1, RCC_SYSCPU1CFGR, 1, 0), + GATE_CFG(GATE_CRC, RCC_CRCCFGR, 1, 0), + GATE_CFG(GATE_OSPIIOM, RCC_OSPIIOMCFGR, 1, 0), + GATE_CFG(GATE_BKPSRAM, RCC_BKPSRAMCFGR, 1, 0), + GATE_CFG(GATE_HASH, RCC_HASHCFGR, 1, 0), + GATE_CFG(GATE_RNG, RCC_RNGCFGR, 1, 0), + GATE_CFG(GATE_CRYP1, RCC_CRYP1CFGR, 1, 0), + GATE_CFG(GATE_CRYP2, RCC_CRYP2CFGR, 1, 0), + GATE_CFG(GATE_SAES, RCC_SAESCFGR, 1, 0), + GATE_CFG(GATE_PKA, RCC_PKACFGR, 1, 0), + GATE_CFG(GATE_GPIOA, RCC_GPIOACFGR, 1, 0), + GATE_CFG(GATE_GPIOB, RCC_GPIOBCFGR, 1, 0), + GATE_CFG(GATE_GPIOC, RCC_GPIOCCFGR, 1, 0), + GATE_CFG(GATE_GPIOD, RCC_GPIODCFGR, 1, 0), + GATE_CFG(GATE_GPIOE, RCC_GPIOECFGR, 1, 0), + GATE_CFG(GATE_GPIOF, RCC_GPIOFCFGR, 1, 0), + GATE_CFG(GATE_GPIOG, RCC_GPIOGCFGR, 1, 0), + GATE_CFG(GATE_GPIOH, RCC_GPIOHCFGR, 1, 0), + GATE_CFG(GATE_GPIOI, RCC_GPIOICFGR, 1, 0), + GATE_CFG(GATE_GPIOJ, RCC_GPIOJCFGR, 1, 0), + GATE_CFG(GATE_GPIOK, RCC_GPIOKCFGR, 1, 0), + GATE_CFG(GATE_GPIOZ, RCC_GPIOZCFGR, 1, 0), + GATE_CFG(GATE_RTC, RCC_RTCCFGR, 1, 0), + GATE_CFG(GATE_DDRCP, RCC_DDRCPCFGR, 1, 0), + + /* WARNING 2 CLOCKS FOR ONE GATE */ + GATE_CFG(GATE_USB2OHCI, RCC_USB2CFGR, 1, 0), + GATE_CFG(GATE_USB2EHCI, RCC_USB2CFGR, 1, 0), + GATE_CFG(GATE_USB3DR, RCC_USB3DRCFGR, 1, 0), + GATE_CFG(GATE_BSEC, RCC_BSECCFGR, 1, 0), + GATE_CFG(GATE_IWDG1, RCC_IWDG1CFGR, 1, 0), + GATE_CFG(GATE_IWDG2, RCC_IWDG2CFGR, 1, 0), + GATE_CFG(GATE_DDRCAPB, RCC_DDRCAPBCFGR, 1, 0), + GATE_CFG(GATE_DDR, RCC_DDRCFGR, 1, 0), + GATE_CFG(GATE_USART2, RCC_USART2CFGR, 1, 0), + GATE_CFG(GATE_UART4, RCC_UART4CFGR, 1, 0), + GATE_CFG(GATE_USART3, RCC_USART3CFGR, 1, 0), + GATE_CFG(GATE_UART5, RCC_UART5CFGR, 1, 0), + GATE_CFG(GATE_I2C1, RCC_I2C1CFGR, 1, 0), + GATE_CFG(GATE_I2C2, RCC_I2C2CFGR, 1, 0), + GATE_CFG(GATE_I2C3, RCC_I2C3CFGR, 1, 0), + GATE_CFG(GATE_I2C5, RCC_I2C5CFGR, 1, 0), + GATE_CFG(GATE_I2C4, RCC_I2C4CFGR, 1, 0), + GATE_CFG(GATE_I2C6, RCC_I2C6CFGR, 1, 0), + GATE_CFG(GATE_I2C7, RCC_I2C7CFGR, 1, 0), + GATE_CFG(GATE_USART1, RCC_USART1CFGR, 1, 0), + GATE_CFG(GATE_USART6, RCC_USART6CFGR, 1, 0), + GATE_CFG(GATE_UART7, RCC_UART7CFGR, 1, 0), + GATE_CFG(GATE_UART8, RCC_UART8CFGR, 1, 0), + GATE_CFG(GATE_UART9, RCC_UART9CFGR, 1, 0), + GATE_CFG(GATE_STGEN, RCC_STGENCFGR, 1, 0), + GATE_CFG(GATE_USB3PCIEPHY, RCC_USB3PCIEPHYCFGR, 1, 0), + GATE_CFG(GATE_USBTC, RCC_USBTCCFGR, 1, 0), + GATE_CFG(GATE_I2C8, RCC_I2C8CFGR, 1, 0), + GATE_CFG(GATE_OSPI1, RCC_OSPI1CFGR, 1, 0), + GATE_CFG(GATE_OSPI2, RCC_OSPI2CFGR, 1, 0), + GATE_CFG(GATE_FMC, RCC_FMCCFGR, 1, 0), + GATE_CFG(GATE_SDMMC1, RCC_SDMMC1CFGR, 1, 0), + GATE_CFG(GATE_SDMMC2, RCC_SDMMC2CFGR, 1, 0), + GATE_CFG(GATE_USB2PHY1, RCC_USB2PHY1CFGR, 1, 0), + GATE_CFG(GATE_USB2PHY2, RCC_USB2PHY2CFGR, 1, 0), +}; + +static const struct clk_div_table apb_div_table[] = { + { 0, 1 }, { 1, 2 }, { 2, 4 }, { 3, 8 }, { 4, 16 }, + { 5, 16 }, { 6, 16 }, { 7, 16 }, { 0 }, +}; + +#undef DIV_CFG +#define DIV_CFG(id, _offset, _shift, _width, _flags, _table, _bitrdy)[id] = {\ + .offset = _offset,\ + .shift = _shift,\ + .width = _width,\ + .flags = _flags,\ + .table = _table,\ + .bitrdy = _bitrdy,\ +} + +static const struct div_cfg dividers_mp25[] = { + DIV_CFG(DIV_APB1, RCC_APB1DIVR, 0, 3, 0, apb_div_table, 31), + DIV_CFG(DIV_APB2, RCC_APB2DIVR, 0, 3, 0, apb_div_table, 31), + DIV_CFG(DIV_APB3, RCC_APB3DIVR, 0, 3, 0, apb_div_table, 31), + DIV_CFG(DIV_APB4, RCC_APB4DIVR, 0, 3, 0, apb_div_table, 31), + DIV_CFG(DIV_APBDBG, RCC_APBDBGDIVR, 0, 3, 0, apb_div_table, 31), + DIV_CFG(DIV_LSMCU, RCC_LSMCUDIVR, 0, 1, 0, NULL, 31), + DIV_CFG(DIV_RTC, RCC_RTCDIVR, 0, 6, 0, NULL, 0), +}; + +enum stm32_osc { + OSC_HSI, + OSC_HSE, + OSC_MSI, + OSC_LSI, + OSC_LSE, + OSC_I2SCKIN, + OSC_SPDIFSYMB, + NB_OSCILLATOR +}; + +static struct clk_oscillator_data stm32mp25_osc_data[] = { + OSCILLATOR(OSC_HSI, _CK_HSI, "clk-hsi", GATE_HSI, GATE_HSI_RDY, + NULL, NULL, NULL), + + OSCILLATOR(OSC_LSI, _CK_LSI, "clk-lsi", GATE_LSI, GATE_LSI_RDY, + NULL, NULL, NULL), + + OSCILLATOR(OSC_MSI, _CK_MSI, "clk-msi", GATE_MSI, GATE_MSI_RDY, + NULL, NULL, NULL), + + OSCILLATOR(OSC_HSE, _CK_HSE, "clk-hse", GATE_HSE, GATE_HSE_RDY, + BYPASS(RCC_OCENSETR, 10, 7), + CSS(RCC_OCENSETR, 11), + NULL), + + OSCILLATOR(OSC_LSE, _CK_LSE, "clk-lse", GATE_LSE, GATE_LSE_RDY, + BYPASS(RCC_BDCR, 1, 3), + CSS(RCC_BDCR, 8), + DRIVE(RCC_BDCR, 4, 2, 2)), + + OSCILLATOR(OSC_I2SCKIN, _I2SCKIN, "i2s_ckin", NO_GATE, NO_GATE, + NULL, NULL, NULL), + + OSCILLATOR(OSC_SPDIFSYMB, _SPDIFSYMB, "spdif_symb", NO_GATE, NO_GATE, + NULL, NULL, NULL), +}; + +#ifdef IMAGE_BL2 +static const char *clk_stm32_get_oscillator_name(enum stm32_osc id) +{ + if (id < NB_OSCILLATOR) { + return stm32mp25_osc_data[id].name; + } + + return NULL; +} +#endif + +enum pll_id { + _PLL1, + _PLL2, + _PLL3, + _PLL4, + _PLL5, + _PLL6, + _PLL7, + _PLL8, + _PLL_NB +}; + +/* PLL configuration registers offsets from RCC_PLLxCFGR1 */ +#define RCC_OFFSET_PLLXCFGR1 0x00 +#define RCC_OFFSET_PLLXCFGR2 0x04 +#define RCC_OFFSET_PLLXCFGR3 0x08 +#define RCC_OFFSET_PLLXCFGR4 0x0C +#define RCC_OFFSET_PLLXCFGR5 0x10 +#define RCC_OFFSET_PLLXCFGR6 0x18 +#define RCC_OFFSET_PLLXCFGR7 0x1C + +struct stm32_clk_pll { + uint16_t clk_id; + uint16_t reg_pllxcfgr1; +}; + +#define CLK_PLL_CFG(_idx, _clk_id, _reg)\ + [(_idx)] = {\ + .clk_id = (_clk_id),\ + .reg_pllxcfgr1 = (_reg),\ + } + +static const struct stm32_clk_pll stm32mp25_clk_pll[_PLL_NB] = { + CLK_PLL_CFG(_PLL1, _CK_PLL1, A35_SS_CHGCLKREQ), + CLK_PLL_CFG(_PLL2, _CK_PLL2, RCC_PLL2CFGR1), + CLK_PLL_CFG(_PLL3, _CK_PLL3, RCC_PLL3CFGR1), + CLK_PLL_CFG(_PLL4, _CK_PLL4, RCC_PLL4CFGR1), + CLK_PLL_CFG(_PLL5, _CK_PLL5, RCC_PLL5CFGR1), + CLK_PLL_CFG(_PLL6, _CK_PLL6, RCC_PLL6CFGR1), + CLK_PLL_CFG(_PLL7, _CK_PLL7, RCC_PLL7CFGR1), + CLK_PLL_CFG(_PLL8, _CK_PLL8, RCC_PLL8CFGR1), +}; + +static const struct stm32_clk_pll *clk_stm32_pll_data(unsigned int idx) +{ + return &stm32mp25_clk_pll[idx]; +} + +static unsigned long clk_get_pll_fvco(struct stm32_clk_priv *priv, + const struct stm32_clk_pll *pll, + unsigned long prate) +{ + unsigned long refclk, fvco; + uint32_t fracin, fbdiv, refdiv; + uintptr_t pllxcfgr1 = priv->base + pll->reg_pllxcfgr1; + uintptr_t pllxcfgr2 = pllxcfgr1 + RCC_OFFSET_PLLXCFGR2; + uintptr_t pllxcfgr3 = pllxcfgr1 + RCC_OFFSET_PLLXCFGR3; + + refclk = prate; + + fracin = mmio_read_32(pllxcfgr3) & RCC_PLLxCFGR3_FRACIN_MASK; + fbdiv = (mmio_read_32(pllxcfgr2) & RCC_PLLxCFGR2_FBDIV_MASK) >> + RCC_PLLxCFGR2_FBDIV_SHIFT; + refdiv = mmio_read_32(pllxcfgr2) & RCC_PLLxCFGR2_FREFDIV_MASK; + + if (fracin != 0U) { + uint64_t numerator, denominator; + + numerator = ((uint64_t)fbdiv << 24) + fracin; + numerator = refclk * numerator; + denominator = (uint64_t)refdiv << 24; + fvco = (unsigned long)(numerator / denominator); + } else { + fvco = (unsigned long)(refclk * fbdiv / refdiv); + } + + return fvco; +} + +struct stm32_pll_cfg { + uint16_t pll_id; +}; + +static bool _clk_stm32_pll_is_enabled(struct stm32_clk_priv *priv, const struct stm32_clk_pll *pll) +{ + uintptr_t pllxcfgr1 = priv->base + pll->reg_pllxcfgr1; + + return ((mmio_read_32(pllxcfgr1) & RCC_PLLxCFGR1_PLLEN) != 0U); +} + +static void _clk_stm32_pll_set_on(struct stm32_clk_priv *priv, const struct stm32_clk_pll *pll) +{ + uintptr_t pllxcfgr1 = priv->base + pll->reg_pllxcfgr1; + + mmio_setbits_32(pllxcfgr1, RCC_PLLxCFGR1_PLLEN); +} + +static void _clk_stm32_pll_set_off(struct stm32_clk_priv *priv, const struct stm32_clk_pll *pll) +{ + uintptr_t pllxcfgr1 = priv->base + pll->reg_pllxcfgr1; + + /* Stop PLL */ + mmio_clrbits_32(pllxcfgr1, RCC_PLLxCFGR1_PLLEN); +} + +static int _clk_stm32_pll_wait_ready_on(struct stm32_clk_priv *priv, + const struct stm32_clk_pll *pll) +{ + uintptr_t pllxcfgr1 = priv->base + pll->reg_pllxcfgr1; + uint64_t timeout = timeout_init_us(PLLRDY_TIMEOUT); + + /* Wait PLL lock */ + while ((mmio_read_32(pllxcfgr1) & RCC_PLLxCFGR1_PLLRDY) == 0U) { + if (timeout_elapsed(timeout)) { + ERROR("PLL%d start failed @ 0x%x: 0x%x\n", + pll->clk_id - _CK_PLL1 + 1, pll->reg_pllxcfgr1, + mmio_read_32(pllxcfgr1)); + return -ETIMEDOUT; + } + } + + return 0; +} + +static int _clk_stm32_pll_wait_ready_off(struct stm32_clk_priv *priv, + const struct stm32_clk_pll *pll) +{ + uintptr_t pllxcfgr1 = priv->base + pll->reg_pllxcfgr1; + uint64_t timeout = timeout_init_us(PLLRDY_TIMEOUT); + + /* Wait PLL stopped */ + while ((mmio_read_32(pllxcfgr1) & RCC_PLLxCFGR1_PLLRDY) != 0U) { + if (timeout_elapsed(timeout)) { + ERROR("PLL%d stop failed @ 0x%lx: 0x%x\n", + pll->clk_id - _CK_PLL1 + 1, pllxcfgr1, mmio_read_32(pllxcfgr1)); + return -ETIMEDOUT; + } + } + + return 0; +} + +static int _clk_stm32_pll_enable(struct stm32_clk_priv *priv, const struct stm32_clk_pll *pll) +{ + if (_clk_stm32_pll_is_enabled(priv, pll)) { + return 0; + } + + _clk_stm32_pll_set_on(priv, pll); + + return _clk_stm32_pll_wait_ready_on(priv, pll); +} + +static void _clk_stm32_pll_disable(struct stm32_clk_priv *priv, const struct stm32_clk_pll *pll) +{ + if (!_clk_stm32_pll_is_enabled(priv, pll)) { + return; + } + + _clk_stm32_pll_set_off(priv, pll); + + _clk_stm32_pll_wait_ready_off(priv, pll); +} + +static bool clk_stm32_pll_is_enabled(struct stm32_clk_priv *priv, int id) +{ + const struct clk_stm32 *clk = _clk_get(priv, id); + struct stm32_pll_cfg *pll_cfg = clk->clock_cfg; + const struct stm32_clk_pll *pll = clk_stm32_pll_data(pll_cfg->pll_id); + + return _clk_stm32_pll_is_enabled(priv, pll); +} + +static int clk_stm32_pll_enable(struct stm32_clk_priv *priv, int id) +{ + const struct clk_stm32 *clk = _clk_get(priv, id); + struct stm32_pll_cfg *pll_cfg = clk->clock_cfg; + const struct stm32_clk_pll *pll = clk_stm32_pll_data(pll_cfg->pll_id); + + return _clk_stm32_pll_enable(priv, pll); +} + +static void clk_stm32_pll_disable(struct stm32_clk_priv *priv, int id) +{ + const struct clk_stm32 *clk = _clk_get(priv, id); + struct stm32_pll_cfg *pll_cfg = clk->clock_cfg; + const struct stm32_clk_pll *pll = clk_stm32_pll_data(pll_cfg->pll_id); + + _clk_stm32_pll_disable(priv, pll); +} + +static unsigned long clk_stm32_pll_recalc_rate(struct stm32_clk_priv *priv, int id, + unsigned long prate) +{ + const struct clk_stm32 *clk = _clk_get(priv, id); + struct stm32_pll_cfg *pll_cfg = clk->clock_cfg; + const struct stm32_clk_pll *pll = clk_stm32_pll_data(pll_cfg->pll_id); + uintptr_t pllxcfgr1 = priv->base + pll->reg_pllxcfgr1; + uintptr_t pllxcfgr4 = pllxcfgr1 + RCC_OFFSET_PLLXCFGR4; + uintptr_t pllxcfgr6 = pllxcfgr1 + RCC_OFFSET_PLLXCFGR6; + uintptr_t pllxcfgr7 = pllxcfgr1 + RCC_OFFSET_PLLXCFGR7; + unsigned long dfout; + uint32_t postdiv1, postdiv2; + + postdiv1 = mmio_read_32(pllxcfgr6) & RCC_PLLxCFGR6_POSTDIV1_MASK; + postdiv2 = mmio_read_32(pllxcfgr7) & RCC_PLLxCFGR7_POSTDIV2_MASK; + + if ((mmio_read_32(pllxcfgr4) & RCC_PLLxCFGR4_BYPASS) != 0U) { + dfout = prate; + } else { + if ((postdiv1 == 0U) || (postdiv2 == 0U)) { + dfout = prate; + } else { + dfout = clk_get_pll_fvco(priv, pll, prate) / (postdiv1 * postdiv2); + } + } + + return dfout; +} + +static const struct stm32_clk_ops clk_stm32_pll_ops = { + .recalc_rate = clk_stm32_pll_recalc_rate, + .enable = clk_stm32_pll_enable, + .disable = clk_stm32_pll_disable, + .is_enabled = clk_stm32_pll_is_enabled, +}; + +#define CLK_PLL(idx, _idx, _parent, _pll_id, _flags)[idx] = {\ + .binding = _idx,\ + .parent = _parent,\ + .flags = (_flags),\ + .clock_cfg = &(struct stm32_pll_cfg) {\ + .pll_id = _pll_id,\ + },\ + .ops = STM32_PLL_OPS,\ +} + +static unsigned long clk_get_pll1_fvco(unsigned long refclk) +{ + uintptr_t pll_freq1_reg = A35SSC_BASE + A35_SS_PLL_FREQ1; + uint32_t reg, fbdiv, refdiv; + + reg = mmio_read_32(pll_freq1_reg); + + fbdiv = (reg & A35_SS_PLL_FREQ1_FBDIV_MASK) >> A35_SS_PLL_FREQ1_FBDIV_SHIFT; + refdiv = (reg & A35_SS_PLL_FREQ1_REFDIV_MASK) >> A35_SS_PLL_FREQ1_REFDIV_SHIFT; + + return (unsigned long)(refclk * fbdiv / refdiv); +} + +static unsigned long clk_stm32_pll1_recalc_rate(struct stm32_clk_priv *priv, + int id, unsigned long prate) +{ + uintptr_t pll_freq2_reg = A35SSC_BASE + A35_SS_PLL_FREQ2; + uint32_t postdiv1, postdiv2; + unsigned long dfout; + + postdiv1 = (mmio_read_32(pll_freq2_reg) & A35_SS_PLL_FREQ2_POSTDIV1_MASK) >> + A35_SS_PLL_FREQ2_POSTDIV1_SHIFT; + postdiv2 = (mmio_read_32(pll_freq2_reg) & A35_SS_PLL_FREQ2_POSTDIV2_MASK) >> + A35_SS_PLL_FREQ2_POSTDIV2_SHIFT; + + if ((postdiv1 == 0U) || (postdiv2 == 0U)) { + dfout = prate; + } else { + dfout = clk_get_pll1_fvco(prate) / (postdiv1 * postdiv2); + } + + return dfout; +} + +static const struct stm32_clk_ops clk_stm32_pll1_ops = { + .recalc_rate = clk_stm32_pll1_recalc_rate, +}; + +#define CLK_PLL1(idx, _idx, _parent, _pll_id, _flags)[idx] = {\ + .binding = _idx,\ + .parent = _parent,\ + .flags = (_flags),\ + .clock_cfg = &(struct stm32_pll_cfg) {\ + .pll_id = _pll_id,\ + },\ + .ops = STM32_PLL1_OPS,\ +} + +struct stm32_clk_flexgen_cfg { + uint8_t id; +}; + +static unsigned long clk_flexgen_recalc(struct stm32_clk_priv *priv, int idx, + unsigned long prate) +{ + const struct clk_stm32 *clk = _clk_get(priv, idx); + struct stm32_clk_flexgen_cfg *cfg = clk->clock_cfg; + uintptr_t rcc_base = priv->base; + uint32_t prediv, findiv; + uint8_t channel = cfg->id; + unsigned long freq = prate; + + prediv = mmio_read_32(rcc_base + RCC_PREDIV0CFGR + (0x4U * channel)) & + RCC_PREDIVxCFGR_PREDIVx_MASK; + findiv = mmio_read_32(rcc_base + RCC_FINDIV0CFGR + (0x4U * channel)) & + RCC_FINDIVxCFGR_FINDIVx_MASK; + + if (freq == 0UL) { + return 0U; + } + + switch (prediv) { + case 0x0: + case 0x1: + case 0x3: + case 0x3FF: + break; + + default: + ERROR("Unsupported PREDIV value (%x)\n", prediv); + panic(); + break; + } + + freq /= (prediv + 1U); + freq /= (findiv + 1U); + + return freq; +} + +static int clk_flexgen_get_parent(struct stm32_clk_priv *priv, int idx) +{ + const struct clk_stm32 *clk = _clk_get(priv, idx); + struct stm32_clk_flexgen_cfg *cfg = clk->clock_cfg; + uint32_t sel; + uint32_t address; + uintptr_t rcc_base = priv->base; + + address = RCC_XBAR0CFGR + (cfg->id * 4); + + sel = mmio_read_32(rcc_base + address) & RCC_XBARxCFGR_XBARxSEL_MASK; + + return sel; +} + +static int clk_flexgen_gate_enable(struct stm32_clk_priv *priv, int idx) +{ + const struct clk_stm32 *clk = _clk_get(priv, idx); + struct stm32_clk_flexgen_cfg *cfg = clk->clock_cfg; + uintptr_t rcc_base = priv->base; + uint8_t channel = cfg->id; + + mmio_setbits_32(rcc_base + RCC_FINDIV0CFGR + (0x4U * channel), + RCC_FINDIVxCFGR_FINDIVxEN); + + return 0; +} + +static void clk_flexgen_gate_disable(struct stm32_clk_priv *priv, int id) +{ + const struct clk_stm32 *clk = _clk_get(priv, id); + struct stm32_clk_flexgen_cfg *cfg = clk->clock_cfg; + uintptr_t rcc_base = priv->base; + uint8_t channel = cfg->id; + + mmio_clrbits_32(rcc_base + RCC_FINDIV0CFGR + (0x4U * channel), + RCC_FINDIVxCFGR_FINDIVxEN); +} + +static bool clk_flexgen_gate_is_enabled(struct stm32_clk_priv *priv, int id) +{ + const struct clk_stm32 *clk = _clk_get(priv, id); + struct stm32_clk_flexgen_cfg *cfg = clk->clock_cfg; + uintptr_t rcc_base = priv->base; + uint8_t channel = cfg->id; + + return !!(mmio_read_32(rcc_base + RCC_FINDIV0CFGR + (0x4U * channel)) & + RCC_FINDIVxCFGR_FINDIVxEN); +} + +static const struct stm32_clk_ops clk_stm32_flexgen_ops = { + .recalc_rate = clk_flexgen_recalc, + .get_parent = clk_flexgen_get_parent, + .enable = clk_flexgen_gate_enable, + .disable = clk_flexgen_gate_disable, + .is_enabled = clk_flexgen_gate_is_enabled, +}; + +#define FLEXGEN(idx, _idx, _flags, _id)[idx] = {\ + .binding = _idx,\ + .parent = MUX(MUX_XBARSEL),\ + .flags = (_flags),\ + .clock_cfg = &(struct stm32_clk_flexgen_cfg) {\ + .id = _id,\ + },\ + .ops = STM32_FLEXGEN_OPS,\ +} + +#define RCC_0_MHZ UL(0) +#define RCC_4_MHZ UL(4000000) +#define RCC_16_MHZ UL(16000000) + +#ifdef IMAGE_BL2 +static int clk_stm32_osc_msi_set_rate(struct stm32_clk_priv *priv, int id, unsigned long rate, + unsigned long prate) +{ + uintptr_t address = priv->base + RCC_BDCR; + uint32_t mask = RCC_BDCR_MSIFREQSEL; + int ret = -1; + + switch (rate) { + case RCC_4_MHZ: + mmio_clrbits_32(address, mask); + ret = 0; + break; + + case RCC_16_MHZ: + mmio_setbits_32(address, mask); + ret = 0; + break; + + default: + break; + } + + return ret; +} +#endif /* IMAGE_BL2 */ + +static unsigned long clk_stm32_osc_msi_recalc_rate(struct stm32_clk_priv *priv, + int id __unused, + unsigned long prate __unused) +{ + uintptr_t address = priv->base + RCC_BDCR; + + if ((mmio_read_32(address) & RCC_BDCR_MSIFREQSEL) == 0U) { + return RCC_4_MHZ; + } else { + return RCC_16_MHZ; + } +} + +static const struct stm32_clk_ops clk_stm32_osc_msi_ops = { + .recalc_rate = clk_stm32_osc_msi_recalc_rate, + .is_enabled = clk_stm32_osc_gate_is_enabled, + .enable = clk_stm32_osc_gate_enable, + .disable = clk_stm32_osc_gate_disable, + .init = clk_stm32_osc_init, +}; + +#define CLK_OSC_MSI(idx, _idx, _parent, _osc_id) \ + [(idx)] = (struct clk_stm32){ \ + .binding = (_idx),\ + .parent = (_parent),\ + .flags = CLK_IS_CRITICAL,\ + .clock_cfg = &(struct stm32_osc_cfg){\ + .osc_id = (_osc_id),\ + },\ + .ops = STM32_OSC_MSI_OPS,\ + } + +static const struct stm32_clk_ops clk_stm32_rtc_ops = { + .enable = clk_stm32_gate_enable, + .disable = clk_stm32_gate_disable, + .is_enabled = clk_stm32_gate_is_enabled, +}; + +#define CLK_RTC(idx, _binding, _parent, _flags, _gate_id)[idx] = {\ + .binding = (_binding),\ + .parent = (_parent),\ + .flags = (_flags),\ + .clock_cfg = &(struct clk_stm32_gate_cfg) {\ + .id = (_gate_id),\ + },\ + .ops = STM32_RTC_OPS,\ +} + +enum { + STM32_PLL_OPS = STM32_LAST_OPS, + STM32_PLL1_OPS, + STM32_FLEXGEN_OPS, + STM32_OSC_MSI_OPS, + STM32_RTC_OPS, + + MP25_LAST_OPS +}; + +static const struct stm32_clk_ops *ops_array_mp25[MP25_LAST_OPS] = { + [NO_OPS] = NULL, + [FIXED_FACTOR_OPS] = &clk_fixed_factor_ops, + [GATE_OPS] = &clk_gate_ops, + [STM32_MUX_OPS] = &clk_mux_ops, + [STM32_DIVIDER_OPS] = &clk_stm32_divider_ops, + [STM32_GATE_OPS] = &clk_stm32_gate_ops, + [STM32_TIMER_OPS] = &clk_timer_ops, + [STM32_FIXED_RATE_OPS] = &clk_stm32_fixed_rate_ops, + [STM32_OSC_OPS] = &clk_stm32_osc_ops, + [STM32_OSC_NOGATE_OPS] = &clk_stm32_osc_nogate_ops, + + [STM32_PLL_OPS] = &clk_stm32_pll_ops, + [STM32_PLL1_OPS] = &clk_stm32_pll1_ops, + [STM32_FLEXGEN_OPS] = &clk_stm32_flexgen_ops, + [STM32_OSC_MSI_OPS] = &clk_stm32_osc_msi_ops, + [STM32_RTC_OPS] = &clk_stm32_rtc_ops +}; + +static const struct clk_stm32 stm32mp25_clk[CK_LAST] = { + CLK_FIXED_RATE(_CK_0_MHZ, _NO_ID, RCC_0_MHZ), + + /* ROOT CLOCKS */ + CLK_OSC(_CK_HSE, HSE_CK, CLK_IS_ROOT, OSC_HSE), + CLK_OSC(_CK_LSE, LSE_CK, CLK_IS_ROOT, OSC_LSE), + CLK_OSC(_CK_HSI, HSI_CK, CLK_IS_ROOT, OSC_HSI), + CLK_OSC(_CK_LSI, LSI_CK, CLK_IS_ROOT, OSC_LSI), + CLK_OSC_MSI(_CK_MSI, MSI_CK, CLK_IS_ROOT, OSC_MSI), + + CLK_OSC_FIXED(_I2SCKIN, _NO_ID, CLK_IS_ROOT, OSC_I2SCKIN), + CLK_OSC_FIXED(_SPDIFSYMB, _NO_ID, CLK_IS_ROOT, OSC_SPDIFSYMB), + + STM32_DIV(_CK_HSE_RTC, _NO_ID, _CK_HSE, 0, DIV_RTC), + + CLK_RTC(_CK_RTCCK, RTC_CK, MUX(MUX_RTC), 0, GATE_RTCCK), + + CLK_PLL1(_CK_PLL1, PLL1_CK, MUX(MUX_MUXSEL5), _PLL1, 0), + + CLK_PLL(_CK_PLL2, PLL2_CK, MUX(MUX_MUXSEL6), _PLL2, 0), + CLK_PLL(_CK_PLL3, PLL3_CK, MUX(MUX_MUXSEL7), _PLL3, 0), + CLK_PLL(_CK_PLL4, PLL4_CK, MUX(MUX_MUXSEL0), _PLL4, 0), + CLK_PLL(_CK_PLL5, PLL5_CK, MUX(MUX_MUXSEL1), _PLL5, 0), + CLK_PLL(_CK_PLL6, PLL6_CK, MUX(MUX_MUXSEL2), _PLL6, 0), + CLK_PLL(_CK_PLL7, PLL7_CK, MUX(MUX_MUXSEL3), _PLL7, 0), + CLK_PLL(_CK_PLL8, PLL8_CK, MUX(MUX_MUXSEL4), _PLL8, 0), + + FLEXGEN(_CK_ICN_HS_MCU, CK_ICN_HS_MCU, CLK_IS_CRITICAL, 0), + FLEXGEN(_CK_ICN_SDMMC, CK_ICN_SDMMC, CLK_IS_CRITICAL, 1), + FLEXGEN(_CK_ICN_DDR, CK_ICN_DDR, CLK_IS_CRITICAL, 2), + FLEXGEN(_CK_ICN_HSL, CK_ICN_HSL, CLK_IS_CRITICAL, 4), + FLEXGEN(_CK_ICN_NIC, CK_ICN_NIC, CLK_IS_CRITICAL, 5), + + STM32_DIV(_CK_ICN_LS_MCU, CK_ICN_LS_MCU, _CK_ICN_HS_MCU, 0, DIV_LSMCU), + + FLEXGEN(_CK_FLEXGEN_07, CK_FLEXGEN_07, 0, 7), + FLEXGEN(_CK_FLEXGEN_08, CK_FLEXGEN_08, 0, 8), + FLEXGEN(_CK_FLEXGEN_09, CK_FLEXGEN_09, 0, 9), + FLEXGEN(_CK_FLEXGEN_10, CK_FLEXGEN_10, 0, 10), + FLEXGEN(_CK_FLEXGEN_11, CK_FLEXGEN_11, 0, 11), + FLEXGEN(_CK_FLEXGEN_12, CK_FLEXGEN_12, 0, 12), + FLEXGEN(_CK_FLEXGEN_13, CK_FLEXGEN_13, 0, 13), + FLEXGEN(_CK_FLEXGEN_14, CK_FLEXGEN_14, 0, 14), + FLEXGEN(_CK_FLEXGEN_15, CK_FLEXGEN_15, 0, 15), + FLEXGEN(_CK_FLEXGEN_16, CK_FLEXGEN_16, 0, 16), + FLEXGEN(_CK_FLEXGEN_17, CK_FLEXGEN_17, 0, 17), + FLEXGEN(_CK_FLEXGEN_18, CK_FLEXGEN_18, 0, 18), + FLEXGEN(_CK_FLEXGEN_19, CK_FLEXGEN_19, 0, 19), + FLEXGEN(_CK_FLEXGEN_20, CK_FLEXGEN_20, 0, 20), + FLEXGEN(_CK_FLEXGEN_21, CK_FLEXGEN_21, 0, 21), + FLEXGEN(_CK_FLEXGEN_22, CK_FLEXGEN_22, 0, 22), + FLEXGEN(_CK_FLEXGEN_23, CK_FLEXGEN_23, 0, 23), + FLEXGEN(_CK_FLEXGEN_24, CK_FLEXGEN_24, 0, 24), + FLEXGEN(_CK_FLEXGEN_25, CK_FLEXGEN_25, 0, 25), + FLEXGEN(_CK_FLEXGEN_26, CK_FLEXGEN_26, 0, 26), + FLEXGEN(_CK_FLEXGEN_27, CK_FLEXGEN_27, 0, 27), + FLEXGEN(_CK_FLEXGEN_28, CK_FLEXGEN_28, 0, 28), + FLEXGEN(_CK_FLEXGEN_29, CK_FLEXGEN_29, 0, 29), + FLEXGEN(_CK_FLEXGEN_30, CK_FLEXGEN_30, 0, 30), + FLEXGEN(_CK_FLEXGEN_31, CK_FLEXGEN_31, 0, 31), + FLEXGEN(_CK_FLEXGEN_32, CK_FLEXGEN_32, 0, 32), + FLEXGEN(_CK_FLEXGEN_33, CK_FLEXGEN_33, 0, 33), + FLEXGEN(_CK_FLEXGEN_34, CK_FLEXGEN_34, 0, 34), + FLEXGEN(_CK_FLEXGEN_35, CK_FLEXGEN_35, 0, 35), + FLEXGEN(_CK_FLEXGEN_36, CK_FLEXGEN_36, 0, 36), + FLEXGEN(_CK_FLEXGEN_37, CK_FLEXGEN_37, 0, 37), + FLEXGEN(_CK_FLEXGEN_38, CK_FLEXGEN_38, 0, 38), + FLEXGEN(_CK_FLEXGEN_39, CK_FLEXGEN_39, 0, 39), + FLEXGEN(_CK_FLEXGEN_40, CK_FLEXGEN_40, 0, 40), + FLEXGEN(_CK_FLEXGEN_41, CK_FLEXGEN_41, 0, 41), + FLEXGEN(_CK_FLEXGEN_42, CK_FLEXGEN_42, 0, 42), + FLEXGEN(_CK_FLEXGEN_43, CK_FLEXGEN_43, 0, 43), + FLEXGEN(_CK_FLEXGEN_44, CK_FLEXGEN_44, 0, 44), + FLEXGEN(_CK_FLEXGEN_45, CK_FLEXGEN_45, 0, 45), + FLEXGEN(_CK_FLEXGEN_46, CK_FLEXGEN_46, 0, 46), + FLEXGEN(_CK_FLEXGEN_47, CK_FLEXGEN_47, 0, 47), + FLEXGEN(_CK_FLEXGEN_48, CK_FLEXGEN_48, 0, 48), + FLEXGEN(_CK_FLEXGEN_49, CK_FLEXGEN_49, 0, 49), + FLEXGEN(_CK_FLEXGEN_50, CK_FLEXGEN_50, 0, 50), + FLEXGEN(_CK_FLEXGEN_51, CK_FLEXGEN_51, 0, 51), + FLEXGEN(_CK_FLEXGEN_52, CK_FLEXGEN_52, 0, 52), + FLEXGEN(_CK_FLEXGEN_53, CK_FLEXGEN_53, 0, 53), + FLEXGEN(_CK_FLEXGEN_54, CK_FLEXGEN_54, 0, 54), + FLEXGEN(_CK_FLEXGEN_55, CK_FLEXGEN_55, 0, 55), + FLEXGEN(_CK_FLEXGEN_56, CK_FLEXGEN_56, 0, 56), + FLEXGEN(_CK_FLEXGEN_57, CK_FLEXGEN_57, 0, 57), + FLEXGEN(_CK_FLEXGEN_58, CK_FLEXGEN_58, 0, 58), + FLEXGEN(_CK_FLEXGEN_59, CK_FLEXGEN_59, 0, 59), + FLEXGEN(_CK_FLEXGEN_60, CK_FLEXGEN_60, 0, 60), + FLEXGEN(_CK_FLEXGEN_61, CK_FLEXGEN_61, 0, 61), + FLEXGEN(_CK_FLEXGEN_62, CK_FLEXGEN_62, 0, 62), + FLEXGEN(_CK_FLEXGEN_63, CK_FLEXGEN_63, 0, 63), + + STM32_DIV(_CK_ICN_APB1, CK_ICN_APB1, _CK_ICN_LS_MCU, 0, DIV_APB1), + STM32_DIV(_CK_ICN_APB2, CK_ICN_APB2, _CK_ICN_LS_MCU, 0, DIV_APB2), + STM32_DIV(_CK_ICN_APB3, CK_ICN_APB3, _CK_ICN_LS_MCU, 0, DIV_APB3), + STM32_DIV(_CK_ICN_APB4, CK_ICN_APB4, _CK_ICN_LS_MCU, 0, DIV_APB4), + STM32_DIV(_CK_ICN_APBDBG, CK_ICN_APBDBG, _CK_ICN_LS_MCU, 0, DIV_APBDBG), + + /* KERNEL CLOCK */ + STM32_GATE(_CK_SYSRAM, CK_BUS_SYSRAM, _CK_ICN_HS_MCU, 0, GATE_SYSRAM), + STM32_GATE(_CK_RETRAM, CK_BUS_RETRAM, _CK_ICN_HS_MCU, 0, GATE_RETRAM), + STM32_GATE(_CK_SRAM1, CK_BUS_SRAM1, _CK_ICN_HS_MCU, CLK_IS_CRITICAL, GATE_SRAM1), + STM32_GATE(_CK_SRAM2, CK_BUS_SRAM2, _CK_ICN_HS_MCU, CLK_IS_CRITICAL, GATE_SRAM2), + + STM32_GATE(_CK_DDRPHYC, CK_BUS_DDRPHYC, _CK_ICN_LS_MCU, 0, GATE_DDRPHYC), + STM32_GATE(_CK_SYSCPU1, CK_BUS_SYSCPU1, _CK_ICN_LS_MCU, 0, GATE_SYSCPU1), + STM32_GATE(_CK_CRC, CK_BUS_CRC, _CK_ICN_LS_MCU, 0, GATE_CRC), + STM32_GATE(_CK_OSPIIOM, CK_BUS_OSPIIOM, _CK_ICN_LS_MCU, 0, GATE_OSPIIOM), + STM32_GATE(_CK_BKPSRAM, CK_BUS_BKPSRAM, _CK_ICN_LS_MCU, 0, GATE_BKPSRAM), + STM32_GATE(_CK_HASH, CK_BUS_HASH, _CK_ICN_LS_MCU, 0, GATE_HASH), + STM32_GATE(_CK_RNG, CK_BUS_RNG, _CK_ICN_LS_MCU, 0, GATE_RNG), + STM32_GATE(_CK_CRYP1, CK_BUS_CRYP1, _CK_ICN_LS_MCU, 0, GATE_CRYP1), + STM32_GATE(_CK_CRYP2, CK_BUS_CRYP2, _CK_ICN_LS_MCU, 0, GATE_CRYP2), + STM32_GATE(_CK_SAES, CK_BUS_SAES, _CK_ICN_LS_MCU, 0, GATE_SAES), + STM32_GATE(_CK_PKA, CK_BUS_PKA, _CK_ICN_LS_MCU, 0, GATE_PKA), + + STM32_GATE(_CK_GPIOA, CK_BUS_GPIOA, _CK_ICN_LS_MCU, 0, GATE_GPIOA), + STM32_GATE(_CK_GPIOB, CK_BUS_GPIOB, _CK_ICN_LS_MCU, 0, GATE_GPIOB), + STM32_GATE(_CK_GPIOC, CK_BUS_GPIOC, _CK_ICN_LS_MCU, 0, GATE_GPIOC), + STM32_GATE(_CK_GPIOD, CK_BUS_GPIOD, _CK_ICN_LS_MCU, 0, GATE_GPIOD), + STM32_GATE(_CK_GPIOE, CK_BUS_GPIOE, _CK_ICN_LS_MCU, 0, GATE_GPIOE), + STM32_GATE(_CK_GPIOF, CK_BUS_GPIOF, _CK_ICN_LS_MCU, 0, GATE_GPIOF), + STM32_GATE(_CK_GPIOG, CK_BUS_GPIOG, _CK_ICN_LS_MCU, 0, GATE_GPIOG), + STM32_GATE(_CK_GPIOH, CK_BUS_GPIOH, _CK_ICN_LS_MCU, 0, GATE_GPIOH), + STM32_GATE(_CK_GPIOI, CK_BUS_GPIOI, _CK_ICN_LS_MCU, 0, GATE_GPIOI), + STM32_GATE(_CK_GPIOJ, CK_BUS_GPIOJ, _CK_ICN_LS_MCU, 0, GATE_GPIOJ), + STM32_GATE(_CK_GPIOK, CK_BUS_GPIOK, _CK_ICN_LS_MCU, 0, GATE_GPIOK), + STM32_GATE(_CK_GPIOZ, CK_BUS_GPIOZ, _CK_ICN_LS_MCU, 0, GATE_GPIOZ), + STM32_GATE(_CK_RTC, CK_BUS_RTC, _CK_ICN_LS_MCU, 0, GATE_RTC), + + STM32_GATE(_CK_DDRCP, CK_BUS_DDR, _CK_ICN_DDR, 0, GATE_DDRCP), + + /* WARNING 2 CLOCKS FOR ONE GATE */ + STM32_GATE(_CK_USB2OHCI, CK_BUS_USB2OHCI, _CK_ICN_HSL, 0, GATE_USB2OHCI), + STM32_GATE(_CK_USB2EHCI, CK_BUS_USB2EHCI, _CK_ICN_HSL, 0, GATE_USB2EHCI), + + STM32_GATE(_CK_USB3DR, CK_BUS_USB3DR, _CK_ICN_HSL, 0, GATE_USB3DR), + + STM32_GATE(_CK_BSEC, CK_BUS_BSEC, _CK_ICN_APB3, 0, GATE_BSEC), + STM32_GATE(_CK_IWDG1, CK_BUS_IWDG1, _CK_ICN_APB3, 0, GATE_IWDG1), + STM32_GATE(_CK_IWDG2, CK_BUS_IWDG2, _CK_ICN_APB3, 0, GATE_IWDG2), + + STM32_GATE(_CK_DDRCAPB, CK_BUS_DDRC, _CK_ICN_APB4, 0, GATE_DDRCAPB), + STM32_GATE(_CK_DDR, CK_BUS_DDRCFG, _CK_ICN_APB4, 0, GATE_DDR), + + STM32_GATE(_CK_USART2, CK_KER_USART2, _CK_FLEXGEN_08, 0, GATE_USART2), + STM32_GATE(_CK_UART4, CK_KER_UART4, _CK_FLEXGEN_08, 0, GATE_UART4), + STM32_GATE(_CK_USART3, CK_KER_USART3, _CK_FLEXGEN_09, 0, GATE_USART3), + STM32_GATE(_CK_UART5, CK_KER_UART5, _CK_FLEXGEN_09, 0, GATE_UART5), + STM32_GATE(_CK_I2C1, CK_KER_I2C1, _CK_FLEXGEN_12, 0, GATE_I2C1), + STM32_GATE(_CK_I2C2, CK_KER_I2C2, _CK_FLEXGEN_12, 0, GATE_I2C2), + STM32_GATE(_CK_I2C3, CK_KER_I2C3, _CK_FLEXGEN_13, 0, GATE_I2C3), + STM32_GATE(_CK_I2C5, CK_KER_I2C5, _CK_FLEXGEN_13, 0, GATE_I2C5), + STM32_GATE(_CK_I2C4, CK_KER_I2C4, _CK_FLEXGEN_14, 0, GATE_I2C4), + STM32_GATE(_CK_I2C6, CK_KER_I2C6, _CK_FLEXGEN_14, 0, GATE_I2C6), + STM32_GATE(_CK_I2C7, CK_KER_I2C7, _CK_FLEXGEN_15, 0, GATE_I2C7), + STM32_GATE(_CK_USART1, CK_KER_USART1, _CK_FLEXGEN_19, 0, GATE_USART1), + STM32_GATE(_CK_USART6, CK_KER_USART6, _CK_FLEXGEN_20, 0, GATE_USART6), + STM32_GATE(_CK_UART7, CK_KER_UART7, _CK_FLEXGEN_21, 0, GATE_UART7), + STM32_GATE(_CK_UART8, CK_KER_UART8, _CK_FLEXGEN_21, 0, GATE_UART8), + STM32_GATE(_CK_UART9, CK_KER_UART9, _CK_FLEXGEN_22, 0, GATE_UART9), + STM32_GATE(_CK_STGEN, CK_KER_STGEN, _CK_FLEXGEN_33, 0, GATE_STGEN), + STM32_GATE(_CK_USB3PCIEPHY, CK_KER_USB3PCIEPHY, _CK_FLEXGEN_34, 0, GATE_USB3PCIEPHY), + STM32_GATE(_CK_USBTC, CK_KER_USBTC, _CK_FLEXGEN_35, 0, GATE_USBTC), + STM32_GATE(_CK_I2C8, CK_KER_I2C8, _CK_FLEXGEN_38, 0, GATE_I2C8), + STM32_GATE(_CK_OSPI1, CK_KER_OSPI1, _CK_FLEXGEN_48, 0, GATE_OSPI1), + STM32_GATE(_CK_OSPI2, CK_KER_OSPI2, _CK_FLEXGEN_49, 0, GATE_OSPI2), + STM32_GATE(_CK_FMC, CK_KER_FMC, _CK_FLEXGEN_50, 0, GATE_FMC), + STM32_GATE(_CK_SDMMC1, CK_KER_SDMMC1, _CK_FLEXGEN_51, 0, GATE_SDMMC1), + STM32_GATE(_CK_SDMMC2, CK_KER_SDMMC2, _CK_FLEXGEN_52, 0, GATE_SDMMC2), + STM32_GATE(_CK_USB2PHY1, CK_KER_USB2PHY1, _CK_FLEXGEN_57, 0, GATE_USB2PHY1), + STM32_GATE(_CK_USB2PHY2, CK_KER_USB2PHY2, _CK_FLEXGEN_58, 0, GATE_USB2PHY2), +}; + +enum clksrc_id { + CLKSRC_CA35SS, + CLKSRC_PLL1, + CLKSRC_PLL2, + CLKSRC_PLL3, + CLKSRC_PLL4, + CLKSRC_PLL5, + CLKSRC_PLL6, + CLKSRC_PLL7, + CLKSRC_PLL8, + CLKSRC_XBAR_CHANNEL0, + CLKSRC_XBAR_CHANNEL1, + CLKSRC_XBAR_CHANNEL2, + CLKSRC_XBAR_CHANNEL3, + CLKSRC_XBAR_CHANNEL4, + CLKSRC_XBAR_CHANNEL5, + CLKSRC_XBAR_CHANNEL6, + CLKSRC_XBAR_CHANNEL7, + CLKSRC_XBAR_CHANNEL8, + CLKSRC_XBAR_CHANNEL9, + CLKSRC_XBAR_CHANNEL10, + CLKSRC_XBAR_CHANNEL11, + CLKSRC_XBAR_CHANNEL12, + CLKSRC_XBAR_CHANNEL13, + CLKSRC_XBAR_CHANNEL14, + CLKSRC_XBAR_CHANNEL15, + CLKSRC_XBAR_CHANNEL16, + CLKSRC_XBAR_CHANNEL17, + CLKSRC_XBAR_CHANNEL18, + CLKSRC_XBAR_CHANNEL19, + CLKSRC_XBAR_CHANNEL20, + CLKSRC_XBAR_CHANNEL21, + CLKSRC_XBAR_CHANNEL22, + CLKSRC_XBAR_CHANNEL23, + CLKSRC_XBAR_CHANNEL24, + CLKSRC_XBAR_CHANNEL25, + CLKSRC_XBAR_CHANNEL26, + CLKSRC_XBAR_CHANNEL27, + CLKSRC_XBAR_CHANNEL28, + CLKSRC_XBAR_CHANNEL29, + CLKSRC_XBAR_CHANNEL30, + CLKSRC_XBAR_CHANNEL31, + CLKSRC_XBAR_CHANNEL32, + CLKSRC_XBAR_CHANNEL33, + CLKSRC_XBAR_CHANNEL34, + CLKSRC_XBAR_CHANNEL35, + CLKSRC_XBAR_CHANNEL36, + CLKSRC_XBAR_CHANNEL37, + CLKSRC_XBAR_CHANNEL38, + CLKSRC_XBAR_CHANNEL39, + CLKSRC_XBAR_CHANNEL40, + CLKSRC_XBAR_CHANNEL41, + CLKSRC_XBAR_CHANNEL42, + CLKSRC_XBAR_CHANNEL43, + CLKSRC_XBAR_CHANNEL44, + CLKSRC_XBAR_CHANNEL45, + CLKSRC_XBAR_CHANNEL46, + CLKSRC_XBAR_CHANNEL47, + CLKSRC_XBAR_CHANNEL48, + CLKSRC_XBAR_CHANNEL49, + CLKSRC_XBAR_CHANNEL50, + CLKSRC_XBAR_CHANNEL51, + CLKSRC_XBAR_CHANNEL52, + CLKSRC_XBAR_CHANNEL53, + CLKSRC_XBAR_CHANNEL54, + CLKSRC_XBAR_CHANNEL55, + CLKSRC_XBAR_CHANNEL56, + CLKSRC_XBAR_CHANNEL57, + CLKSRC_XBAR_CHANNEL58, + CLKSRC_XBAR_CHANNEL59, + CLKSRC_XBAR_CHANNEL60, + CLKSRC_XBAR_CHANNEL61, + CLKSRC_XBAR_CHANNEL62, + CLKSRC_XBAR_CHANNEL63, + CLKSRC_RTC, + CLKSRC_MCO1, + CLKSRC_MCO2, + CLKSRC_NB +}; + +static void stm32mp2_a35_ss_on_hsi(void) +{ + uintptr_t a35_ss_address = A35SSC_BASE; + uintptr_t chgclkreq_reg = a35_ss_address + A35_SS_CHGCLKREQ; + uintptr_t pll_enable_reg = a35_ss_address + A35_SS_PLL_ENABLE; + uint64_t timeout; + + if ((mmio_read_32(chgclkreq_reg) & A35_SS_CHGCLKREQ_ARM_CHGCLKACK) == + A35_SS_CHGCLKREQ_ARM_CHGCLKACK) { + /* Nothing to do, clock source is already set on bypass clock */ + return; + } + + mmio_setbits_32(chgclkreq_reg, A35_SS_CHGCLKREQ_ARM_CHGCLKREQ); + + timeout = timeout_init_us(CLKSRC_TIMEOUT); + while ((mmio_read_32(chgclkreq_reg) & A35_SS_CHGCLKREQ_ARM_CHGCLKACK) != + A35_SS_CHGCLKREQ_ARM_CHGCLKACK) { + if (timeout_elapsed(timeout)) { + EARLY_ERROR("Cannot switch A35 to bypass clock\n"); + panic(); + } + } + + mmio_clrbits_32(pll_enable_reg, A35_SS_PLL_ENABLE_NRESET_SWPLL_FF); +} + +#ifdef IMAGE_BL2 +static void stm32mp2_clk_muxsel_on_hsi(struct stm32_clk_priv *priv) +{ + mmio_clrbits_32(priv->base + RCC_MUXSELCFGR, + RCC_MUXSELCFGR_MUXSEL0_MASK | + RCC_MUXSELCFGR_MUXSEL1_MASK | + RCC_MUXSELCFGR_MUXSEL2_MASK | + RCC_MUXSELCFGR_MUXSEL3_MASK | + RCC_MUXSELCFGR_MUXSEL4_MASK | + RCC_MUXSELCFGR_MUXSEL5_MASK | + RCC_MUXSELCFGR_MUXSEL6_MASK | + RCC_MUXSELCFGR_MUXSEL7_MASK); +} + +static void stm32mp2_clk_xbar_on_hsi(struct stm32_clk_priv *priv) +{ + uintptr_t xbar0cfgr = priv->base + RCC_XBAR0CFGR; + uint32_t i; + + for (i = 0; i < XBAR_CHANNEL_NB; i++) { + mmio_clrsetbits_32(xbar0cfgr + (0x4 * i), + RCC_XBAR0CFGR_XBAR0SEL_MASK, + XBAR_SRC_HSI); + } +} + +static int stm32mp2_a35_pll1_start(void) +{ + uintptr_t a35_ss_address = A35SSC_BASE; + uintptr_t pll_enable_reg = a35_ss_address + A35_SS_PLL_ENABLE; + uintptr_t chgclkreq_reg = a35_ss_address + A35_SS_CHGCLKREQ; + uint64_t timeout = timeout_init_us(PLLRDY_TIMEOUT); + + mmio_setbits_32(pll_enable_reg, A35_SS_PLL_ENABLE_PD); + + /* Wait PLL lock */ + while ((mmio_read_32(pll_enable_reg) & A35_SS_PLL_ENABLE_LOCKP) == 0U) { + if (timeout_elapsed(timeout)) { + EARLY_ERROR("PLL1 start failed @ 0x%lx: 0x%x\n", + pll_enable_reg, mmio_read_32(pll_enable_reg)); + return -ETIMEDOUT; + } + } + + /* De-assert reset on PLL output clock path */ + mmio_setbits_32(pll_enable_reg, A35_SS_PLL_ENABLE_NRESET_SWPLL_FF); + + /* Switch CPU clock to PLL clock */ + mmio_clrbits_32(chgclkreq_reg, A35_SS_CHGCLKREQ_ARM_CHGCLKREQ); + + /* Wait for clock change acknowledge */ + timeout = timeout_init_us(CLKSRC_TIMEOUT); + while ((mmio_read_32(chgclkreq_reg) & A35_SS_CHGCLKREQ_ARM_CHGCLKACK) != 0U) { + if (timeout_elapsed(timeout)) { + EARLY_ERROR("CA35SS switch to PLL1 failed @ 0x%lx: 0x%x\n", + chgclkreq_reg, mmio_read_32(chgclkreq_reg)); + return -ETIMEDOUT; + } + } + + return 0; +} + +static void stm32mp2_a35_pll1_config(uint32_t fbdiv, uint32_t refdiv, uint32_t postdiv1, + uint32_t postdiv2) +{ + uintptr_t a35_ss_address = A35SSC_BASE; + uintptr_t pll_freq1_reg = a35_ss_address + A35_SS_PLL_FREQ1; + uintptr_t pll_freq2_reg = a35_ss_address + A35_SS_PLL_FREQ2; + + mmio_clrsetbits_32(pll_freq1_reg, A35_SS_PLL_FREQ1_REFDIV_MASK, + (refdiv << A35_SS_PLL_FREQ1_REFDIV_SHIFT) & + A35_SS_PLL_FREQ1_REFDIV_MASK); + + mmio_clrsetbits_32(pll_freq1_reg, A35_SS_PLL_FREQ1_FBDIV_MASK, + (fbdiv << A35_SS_PLL_FREQ1_FBDIV_SHIFT) & + A35_SS_PLL_FREQ1_FBDIV_MASK); + + mmio_clrsetbits_32(pll_freq2_reg, A35_SS_PLL_FREQ2_POSTDIV1_MASK, + (postdiv1 << A35_SS_PLL_FREQ2_POSTDIV1_SHIFT) & + A35_SS_PLL_FREQ2_POSTDIV1_MASK); + + mmio_clrsetbits_32(pll_freq2_reg, A35_SS_PLL_FREQ2_POSTDIV2_MASK, + (postdiv2 << A35_SS_PLL_FREQ2_POSTDIV2_SHIFT) & + A35_SS_PLL_FREQ2_POSTDIV2_MASK); +} + +static int clk_stm32_pll_config_output(struct stm32_clk_priv *priv, + const struct stm32_clk_pll *pll, + uint32_t *pllcfg, + uint32_t fracv) +{ + uintptr_t pllxcfgr1 = priv->base + pll->reg_pllxcfgr1; + uintptr_t pllxcfgr2 = pllxcfgr1 + RCC_OFFSET_PLLXCFGR2; + uintptr_t pllxcfgr3 = pllxcfgr1 + RCC_OFFSET_PLLXCFGR3; + uintptr_t pllxcfgr4 = pllxcfgr1 + RCC_OFFSET_PLLXCFGR4; + uintptr_t pllxcfgr6 = pllxcfgr1 + RCC_OFFSET_PLLXCFGR6; + uintptr_t pllxcfgr7 = pllxcfgr1 + RCC_OFFSET_PLLXCFGR7; + unsigned long refclk; + + refclk = _clk_stm32_get_parent_rate(priv, pll->clk_id); + + if (fracv == 0U) { + /* PLL in integer mode */ + + /* + * No need to check max clock, as oscillator reference clocks + * will always be less than 1.2GHz + */ + if (refclk < PLL_REFCLK_MIN) { + panic(); + } + + mmio_clrbits_32(pllxcfgr3, RCC_PLLxCFGR3_FRACIN_MASK); + mmio_clrbits_32(pllxcfgr4, RCC_PLLxCFGR4_DSMEN); + mmio_clrbits_32(pllxcfgr3, RCC_PLLxCFGR3_DACEN); + mmio_setbits_32(pllxcfgr3, RCC_PLLxCFGR3_SSCGDIS); + mmio_setbits_32(pllxcfgr1, RCC_PLLxCFGR1_SSMODRST); + } else { + /* PLL in frac mode */ + + /* + * No need to check max clock, as oscillator reference clocks + * will always be less than 1.2GHz + */ + if (refclk < PLL_FRAC_REFCLK_MIN) { + panic(); + } + + mmio_clrsetbits_32(pllxcfgr3, RCC_PLLxCFGR3_FRACIN_MASK, + fracv & RCC_PLLxCFGR3_FRACIN_MASK); + mmio_setbits_32(pllxcfgr3, RCC_PLLxCFGR3_SSCGDIS); + mmio_setbits_32(pllxcfgr4, RCC_PLLxCFGR4_DSMEN); + } + + assert(pllcfg[REFDIV] != 0U); + + mmio_clrsetbits_32(pllxcfgr2, RCC_PLLxCFGR2_FBDIV_MASK, + (pllcfg[FBDIV] << RCC_PLLxCFGR2_FBDIV_SHIFT) & + RCC_PLLxCFGR2_FBDIV_MASK); + mmio_clrsetbits_32(pllxcfgr2, RCC_PLLxCFGR2_FREFDIV_MASK, + pllcfg[REFDIV] & RCC_PLLxCFGR2_FREFDIV_MASK); + mmio_clrsetbits_32(pllxcfgr6, RCC_PLLxCFGR6_POSTDIV1_MASK, + pllcfg[POSTDIV1] & RCC_PLLxCFGR6_POSTDIV1_MASK); + mmio_clrsetbits_32(pllxcfgr7, RCC_PLLxCFGR7_POSTDIV2_MASK, + pllcfg[POSTDIV2] & RCC_PLLxCFGR7_POSTDIV2_MASK); + + if ((pllcfg[POSTDIV1] == 0U) || (pllcfg[POSTDIV2] == 0U)) { + /* Bypass mode */ + mmio_setbits_32(pllxcfgr4, RCC_PLLxCFGR4_BYPASS); + mmio_clrbits_32(pllxcfgr4, RCC_PLLxCFGR4_FOUTPOSTDIVEN); + } else { + mmio_clrbits_32(pllxcfgr4, RCC_PLLxCFGR4_BYPASS); + mmio_setbits_32(pllxcfgr4, RCC_PLLxCFGR4_FOUTPOSTDIVEN); + } + + return 0; +} + +static void clk_stm32_pll_config_csg(struct stm32_clk_priv *priv, + const struct stm32_clk_pll *pll, + uint32_t *csg) +{ + uintptr_t pllxcfgr1 = priv->base + pll->reg_pllxcfgr1; + uintptr_t pllxcfgr3 = pllxcfgr1 + RCC_OFFSET_PLLXCFGR3; + uintptr_t pllxcfgr4 = pllxcfgr1 + RCC_OFFSET_PLLXCFGR4; + uintptr_t pllxcfgr5 = pllxcfgr1 + RCC_OFFSET_PLLXCFGR5; + + + mmio_clrsetbits_32(pllxcfgr5, RCC_PLLxCFGR5_DIVVAL_MASK, + csg[DIVVAL] & RCC_PLLxCFGR5_DIVVAL_MASK); + mmio_clrsetbits_32(pllxcfgr5, RCC_PLLxCFGR5_SPREAD_MASK, + (csg[SPREAD] << RCC_PLLxCFGR5_SPREAD_SHIFT) & + RCC_PLLxCFGR5_SPREAD_MASK); + + if (csg[DOWNSPREAD] != 0) { + mmio_setbits_32(pllxcfgr3, RCC_PLLxCFGR3_DOWNSPREAD); + } else { + mmio_clrbits_32(pllxcfgr3, RCC_PLLxCFGR3_DOWNSPREAD); + } + + mmio_clrbits_32(pllxcfgr3, RCC_PLLxCFGR3_SSCGDIS); + + mmio_clrbits_32(pllxcfgr1, RCC_PLLxCFGR1_PLLEN); + udelay(1); + + mmio_setbits_32(pllxcfgr4, RCC_PLLxCFGR4_DSMEN); + mmio_setbits_32(pllxcfgr3, RCC_PLLxCFGR3_DACEN); +} + +static int stm32_clk_configure_mux(struct stm32_clk_priv *priv, uint32_t data); + +static inline struct stm32_pll_dt_cfg *clk_stm32_pll_get_pdata(int pll_idx) +{ + struct stm32_clk_priv *priv = clk_stm32_get_priv(); + struct stm32_clk_platdata *pdata = priv->pdata; + + return &pdata->pll[pll_idx]; +} + +static int _clk_stm32_pll1_init(struct stm32_clk_priv *priv, int pll_idx, + struct stm32_pll_dt_cfg *pll_conf) +{ + const struct stm32_clk_pll *pll = clk_stm32_pll_data(pll_idx); + unsigned long refclk; + int ret = 0; + + stm32mp2_a35_ss_on_hsi(); + + ret = stm32_clk_configure_mux(priv, pll_conf->src); + if (ret != 0) { + panic(); + } + + refclk = _clk_stm32_get_parent_rate(priv, pll->clk_id); + + /* + * No need to check max clock, as oscillator reference clocks will + * always be less than 1.2 GHz + */ + if (refclk < PLL_REFCLK_MIN) { + EARLY_ERROR("%s: %d\n", __func__, __LINE__); + panic(); + } + + stm32mp2_a35_pll1_config(pll_conf->cfg[FBDIV], pll_conf->cfg[REFDIV], + pll_conf->cfg[POSTDIV1], pll_conf->cfg[POSTDIV2]); + + ret = stm32mp2_a35_pll1_start(); + if (ret != 0) { + panic(); + } + + return 0; +} + +static int clk_stm32_pll_wait_mux_ready(struct stm32_clk_priv *priv, + const struct stm32_clk_pll *pll) +{ + uintptr_t pllxcfgr1 = priv->base + pll->reg_pllxcfgr1; + uint64_t timeout = timeout_init_us(CLKSRC_TIMEOUT); + + while ((mmio_read_32(pllxcfgr1) & RCC_PLLxCFGR1_CKREFST) != + RCC_PLLxCFGR1_CKREFST) { + if (timeout_elapsed(timeout)) { + EARLY_ERROR("PLL%d ref clock not started\n", pll->clk_id - _CK_PLL1 + 1); + return -ETIMEDOUT; + } + } + + return 0; +} + +static int _clk_stm32_pll_init(struct stm32_clk_priv *priv, int pll_idx, + struct stm32_pll_dt_cfg *pll_conf) +{ + const struct stm32_clk_pll *pll = clk_stm32_pll_data(pll_idx); + uintptr_t pllxcfgr1 = priv->base + pll->reg_pllxcfgr1; + bool spread_spectrum = false; + int ret = 0; + + _clk_stm32_pll_disable(priv, pll); + + ret = stm32_clk_configure_mux(priv, pll_conf->src); + if (ret != 0) { + panic(); + } + + ret = clk_stm32_pll_wait_mux_ready(priv, pll); + if (ret != 0) { + panic(); + } + + ret = clk_stm32_pll_config_output(priv, pll, pll_conf->cfg, pll_conf->frac); + if (ret != 0) { + panic(); + } + + if (pll_conf->csg_enabled) { + clk_stm32_pll_config_csg(priv, pll, pll_conf->csg); + spread_spectrum = true; + } + + _clk_stm32_pll_enable(priv, pll); + + if (spread_spectrum) { + mmio_clrbits_32(pllxcfgr1, RCC_PLLxCFGR1_SSMODRST); + } + + return 0; +} + +static int clk_stm32_pll_init(struct stm32_clk_priv *priv, int pll_idx) +{ + struct stm32_pll_dt_cfg *pll_conf = clk_stm32_pll_get_pdata(pll_idx); + + if (pll_conf->enabled) { + if (pll_idx == _PLL1) { + return _clk_stm32_pll1_init(priv, pll_idx, pll_conf); + } else { + return _clk_stm32_pll_init(priv, pll_idx, pll_conf); + } + } + + return 0; +} + +static int stm32mp2_clk_pll_configure(struct stm32_clk_priv *priv) +{ + enum pll_id i; + int err; + + for (i = _PLL1; i < _PLL_NB; i++) { + err = clk_stm32_pll_init(priv, i); + if (err) { + return err; + } + } + + return 0; +} + +static int wait_predivsr(uint16_t channel) +{ + struct stm32_clk_priv *priv = clk_stm32_get_priv(); + uintptr_t rcc_base = priv->base; + uintptr_t previvsr; + uint32_t channel_bit; + uint64_t timeout; + + if (channel < __WORD_BIT) { + previvsr = rcc_base + RCC_PREDIVSR1; + channel_bit = BIT(channel); + } else { + previvsr = rcc_base + RCC_PREDIVSR2; + channel_bit = BIT(channel - __WORD_BIT); + } + + timeout = timeout_init_us(CLKDIV_TIMEOUT); + while ((mmio_read_32(previvsr) & channel_bit) != 0U) { + if (timeout_elapsed(timeout)) { + EARLY_ERROR("Pre divider status: %x\n", + mmio_read_32(previvsr)); + return -ETIMEDOUT; + } + } + + return 0; +} + +static int wait_findivsr(uint16_t channel) +{ + struct stm32_clk_priv *priv = clk_stm32_get_priv(); + uintptr_t rcc_base = priv->base; + uintptr_t finvivsr; + uint32_t channel_bit; + uint64_t timeout; + + if (channel < __WORD_BIT) { + finvivsr = rcc_base + RCC_FINDIVSR1; + channel_bit = BIT(channel); + } else { + finvivsr = rcc_base + RCC_FINDIVSR2; + channel_bit = BIT(channel - __WORD_BIT); + } + + timeout = timeout_init_us(CLKDIV_TIMEOUT); + while ((mmio_read_32(finvivsr) & channel_bit) != 0U) { + if (timeout_elapsed(timeout)) { + EARLY_ERROR("Final divider status: %x\n", + mmio_read_32(finvivsr)); + return -ETIMEDOUT; + } + } + + return 0; +} + +static int wait_xbar_sts(uint16_t channel) +{ + struct stm32_clk_priv *priv = clk_stm32_get_priv(); + uintptr_t rcc_base = priv->base; + uintptr_t xbar_cfgr = rcc_base + RCC_XBAR0CFGR + (0x4U * channel); + uint64_t timeout; + + timeout = timeout_init_us(CLKDIV_TIMEOUT); + while ((mmio_read_32(xbar_cfgr) & RCC_XBAR0CFGR_XBAR0STS) != 0U) { + if (timeout_elapsed(timeout)) { + EARLY_ERROR("XBAR%uCFGR: %x\n", channel, + mmio_read_32(xbar_cfgr)); + return -ETIMEDOUT; + } + } + + return 0; +} + +static void flexclkgen_config_channel(uint16_t channel, unsigned int clk_src, + unsigned int prediv, unsigned int findiv) +{ + struct stm32_clk_priv *priv = clk_stm32_get_priv(); + uintptr_t rcc_base = priv->base; + + if (wait_predivsr(channel) != 0) { + panic(); + } + + mmio_clrsetbits_32(rcc_base + RCC_PREDIV0CFGR + (0x4U * channel), + RCC_PREDIV0CFGR_PREDIV0_MASK, + prediv); + + if (wait_predivsr(channel) != 0) { + panic(); + } + + if (wait_findivsr(channel) != 0) { + panic(); + } + + mmio_clrsetbits_32(rcc_base + RCC_FINDIV0CFGR + (0x4U * channel), + RCC_FINDIV0CFGR_FINDIV0_MASK, + findiv); + + if (wait_findivsr(channel) != 0) { + panic(); + } + + if (wait_xbar_sts(channel) != 0) { + panic(); + } + + mmio_clrsetbits_32(rcc_base + RCC_XBAR0CFGR + (0x4U * channel), + RCC_XBARxCFGR_XBARxSEL_MASK, + clk_src); + mmio_setbits_32(rcc_base + RCC_XBAR0CFGR + (0x4U * channel), + RCC_XBARxCFGR_XBARxEN); + + if (wait_xbar_sts(channel) != 0) { + panic(); + } +} + +static int stm32mp2_clk_flexgen_configure(struct stm32_clk_priv *priv) +{ + struct stm32_clk_platdata *pdata = priv->pdata; + uint32_t i; + + for (i = 0U; i < pdata->nflexgen; i++) { + uint32_t val = pdata->flexgen[i]; + uint32_t cmd, cmd_data; + unsigned int channel, clk_src, pdiv, fdiv; + + cmd = (val & CMD_MASK) >> CMD_SHIFT; + cmd_data = val & ~CMD_MASK; + + if (cmd != CMD_FLEXGEN) { + continue; + } + + channel = (cmd_data & FLEX_ID_MASK) >> FLEX_ID_SHIFT; + clk_src = (cmd_data & FLEX_SEL_MASK) >> FLEX_SEL_SHIFT; + pdiv = (cmd_data & FLEX_PDIV_MASK) >> FLEX_PDIV_SHIFT; + fdiv = (cmd_data & FLEX_FDIV_MASK) >> FLEX_FDIV_SHIFT; + + switch (channel) { + case 33U: /* STGEN */ + break; + + default: + flexclkgen_config_channel(channel, clk_src, pdiv, fdiv); + break; + } + } + + return 0; +} + +static void stm32_enable_oscillator_hse(struct stm32_clk_priv *priv) +{ + struct stm32_clk_platdata *pdata = priv->pdata; + struct stm32_osci_dt_cfg *osci = &pdata->osci[OSC_HSE]; + bool digbyp = osci->digbyp; + bool bypass = osci->bypass; + bool css = osci->css; + + if (_clk_stm32_get_rate(priv, _CK_HSE) == 0U) { + return; + } + + clk_oscillator_set_bypass(priv, _CK_HSE, digbyp, bypass); + + _clk_stm32_enable(priv, _CK_HSE); + + clk_oscillator_set_css(priv, _CK_HSE, css); +} + +static void stm32_enable_oscillator_lse(struct stm32_clk_priv *priv) +{ + struct clk_oscillator_data *osc_data = clk_oscillator_get_data(priv, _CK_LSE); + struct stm32_clk_platdata *pdata = priv->pdata; + struct stm32_osci_dt_cfg *osci = &pdata->osci[OSC_LSE]; + bool digbyp = osci->digbyp; + bool bypass = osci->bypass; + uint8_t drive = osci->drive; + + if (_clk_stm32_get_rate(priv, _CK_LSE) == 0U) { + return; + } + + /* Do not reconfigure LSE if already enabled */ + if (_clk_stm32_gate_is_enabled(priv, osc_data->gate_id)) { + return; + } + + clk_oscillator_set_bypass(priv, _CK_LSE, digbyp, bypass); + + clk_oscillator_set_drive(priv, _CK_LSE, drive); + + _clk_stm32_gate_enable(priv, osc_data->gate_id); +} + +static int stm32mp2_clk_switch_to_hsi(struct stm32_clk_priv *priv) +{ + stm32mp2_a35_ss_on_hsi(); + stm32mp2_clk_muxsel_on_hsi(priv); + stm32mp2_clk_xbar_on_hsi(priv); + + return 0; +} + +static int stm32_clk_oscillators_wait_lse_ready(struct stm32_clk_priv *priv) +{ + int ret = 0; + + if (_clk_stm32_get_rate(priv, _CK_LSE) != 0U) { + ret = clk_oscillator_wait_ready_on(priv, _CK_LSE); + } + + return ret; +} + +static void stm32_enable_oscillator_msi(struct stm32_clk_priv *priv) +{ + struct stm32_clk_platdata *pdata = priv->pdata; + struct stm32_osci_dt_cfg *osci = &pdata->osci[OSC_MSI]; + int err; + + err = clk_stm32_osc_msi_set_rate(priv, _CK_MSI, osci->freq, 0); + if (err != 0) { + EARLY_ERROR("Invalid rate %lu MHz for MSI ! (4 or 16 only)\n", + osci->freq / 1000000U); + panic(); + } + + _clk_stm32_enable(priv, _CK_MSI); +} + +static void stm32_clk_oscillators_enable(struct stm32_clk_priv *priv) +{ + stm32_enable_oscillator_hse(priv); + stm32_enable_oscillator_lse(priv); + stm32_enable_oscillator_msi(priv); + _clk_stm32_enable(priv, _CK_LSI); +} + +static int stm32_clk_configure_div(struct stm32_clk_priv *priv, uint32_t data) +{ + int div_id = (data & DIV_ID_MASK) >> DIV_ID_SHIFT; + int div_n = (data & DIV_DIVN_MASK) >> DIV_DIVN_SHIFT; + + return clk_stm32_set_div(priv, div_id, div_n); +} + +static int stm32_clk_configure_mux(struct stm32_clk_priv *priv, uint32_t data) +{ + int mux_id = (data & MUX_ID_MASK) >> MUX_ID_SHIFT; + int sel = (data & MUX_SEL_MASK) >> MUX_SEL_SHIFT; + + return clk_mux_set_parent(priv, mux_id, sel); +} + +static int stm32_clk_configure_clk_get_binding_id(struct stm32_clk_priv *priv, uint32_t data) +{ + unsigned long binding_id = ((unsigned long)data & CLK_ID_MASK) >> CLK_ID_SHIFT; + + return clk_get_index(priv, binding_id); +} + +static int stm32_clk_configure_clk(struct stm32_clk_priv *priv, uint32_t data) +{ + int sel = (data & CLK_SEL_MASK) >> CLK_SEL_SHIFT; + bool enable = ((data & CLK_ON_MASK) >> CLK_ON_SHIFT) != 0U; + int clk_id = 0; + int ret = 0; + + clk_id = stm32_clk_configure_clk_get_binding_id(priv, data); + if (clk_id < 0) { + return clk_id; + } + + if (sel != CLK_NOMUX) { + ret = _clk_stm32_set_parent_by_index(priv, clk_id, sel); + if (ret != 0) { + return ret; + } + } + + if (enable) { + clk_stm32_enable_call_ops(priv, clk_id); + } else { + clk_stm32_disable_call_ops(priv, clk_id); + } + + return 0; +} + +static int stm32_clk_configure(struct stm32_clk_priv *priv, uint32_t val) +{ + uint32_t cmd = (val & CMD_MASK) >> CMD_SHIFT; + uint32_t cmd_data = val & ~CMD_MASK; + int ret = -1; + + switch (cmd) { + case CMD_DIV: + ret = stm32_clk_configure_div(priv, cmd_data); + break; + + case CMD_MUX: + ret = stm32_clk_configure_mux(priv, cmd_data); + break; + + case CMD_CLK: + ret = stm32_clk_configure_clk(priv, cmd_data); + break; + + default: + EARLY_ERROR("%s: cmd unknown ! : 0x%x\n", __func__, val); + break; + } + + return ret; +} + +static int stm32_clk_bus_configure(struct stm32_clk_priv *priv) +{ + struct stm32_clk_platdata *pdata = priv->pdata; + uint32_t i; + + for (i = 0; i < pdata->nbusclk; i++) { + int ret; + + ret = stm32_clk_configure(priv, pdata->busclk[i]); + if (ret != 0) { + return ret; + } + } + + return 0; +} + +static int stm32_clk_kernel_configure(struct stm32_clk_priv *priv) +{ + struct stm32_clk_platdata *pdata = priv->pdata; + uint32_t i; + + for (i = 0U; i < pdata->nkernelclk; i++) { + int ret; + + ret = stm32_clk_configure(priv, pdata->kernelclk[i]); + if (ret != 0) { + return ret; + } + } + + return 0; +} + +static int stm32mp2_init_clock_tree(void) +{ + struct stm32_clk_priv *priv = clk_stm32_get_priv(); + int ret; + + /* Set timer with STGEN without changing its clock source */ + stm32mp_stgen_restore_rate(); + generic_delay_timer_init(); + + stm32_clk_oscillators_enable(priv); + + /* Come back to HSI */ + ret = stm32mp2_clk_switch_to_hsi(priv); + if (ret != 0) { + panic(); + } + + ret = stm32mp2_clk_pll_configure(priv); + if (ret != 0) { + panic(); + } + + /* Wait LSE ready before to use it */ + ret = stm32_clk_oscillators_wait_lse_ready(priv); + if (ret != 0) { + panic(); + } + + ret = stm32mp2_clk_flexgen_configure(priv); + if (ret != 0) { + panic(); + } + + ret = stm32_clk_bus_configure(priv); + if (ret != 0) { + panic(); + } + + ret = stm32_clk_kernel_configure(priv); + if (ret != 0) { + panic(); + } + + return 0; +} + +static int clk_stm32_parse_oscillator_fdt(void *fdt, int node, const char *name, + struct stm32_osci_dt_cfg *osci) +{ + int subnode = 0; + + /* Default value oscillator not found, freq=0 */ + osci->freq = 0; + + fdt_for_each_subnode(subnode, fdt, node) { + const char *cchar = NULL; + const fdt32_t *cuint = NULL; + int ret = 0; + + cchar = fdt_get_name(fdt, subnode, &ret); + if (cchar == NULL) { + return ret; + } + + if (strncmp(cchar, name, (size_t)ret) || + fdt_get_status(subnode) == DT_DISABLED) { + continue; + } + + cuint = fdt_getprop(fdt, subnode, "clock-frequency", &ret); + if (cuint == NULL) { + return ret; + } + + osci->freq = fdt32_to_cpu(*cuint); + + if (fdt_getprop(fdt, subnode, "st,bypass", NULL) != NULL) { + osci->bypass = true; + } + + if (fdt_getprop(fdt, subnode, "st,digbypass", NULL) != NULL) { + osci->digbyp = true; + } + + if (fdt_getprop(fdt, subnode, "st,css", NULL) != NULL) { + osci->css = true; + } + + osci->drive = fdt_read_uint32_default(fdt, subnode, "st,drive", LSEDRV_MEDIUM_HIGH); + + return 0; + } + + return 0; +} + +static int stm32_clk_parse_fdt_all_oscillator(void *fdt, struct stm32_clk_platdata *pdata) +{ + int fdt_err = 0; + uint32_t i = 0; + int node = 0; + + node = fdt_path_offset(fdt, "/clocks"); + if (node < 0) { + return -FDT_ERR_NOTFOUND; + } + + for (i = 0; i < pdata->nosci; i++) { + const char *name = NULL; + + name = clk_stm32_get_oscillator_name((enum stm32_osc)i); + if (name == NULL) { + continue; + } + + fdt_err = clk_stm32_parse_oscillator_fdt(fdt, node, name, &pdata->osci[i]); + if (fdt_err < 0) { + panic(); + } + } + + return 0; +} + +static int clk_stm32_parse_pll_fdt(void *fdt, int subnode, struct stm32_pll_dt_cfg *pll) +{ + const fdt32_t *cuint = NULL; + int subnode_pll = 0; + uint32_t val = 0; + int err = 0; + + cuint = fdt_getprop(fdt, subnode, "st,pll", NULL); + if (!cuint) { + return -FDT_ERR_NOTFOUND; + } + + subnode_pll = fdt_node_offset_by_phandle(fdt, fdt32_to_cpu(*cuint)); + if (subnode_pll < 0) { + return -FDT_ERR_NOTFOUND; + } + + err = fdt_read_uint32_array(fdt, subnode_pll, "cfg", (int)PLLCFG_NB, pll->cfg); + if (err != 0) { + return err; + } + + err = fdt_read_uint32_array(fdt, subnode_pll, "csg", (int)PLLCSG_NB, pll->csg); + + pll->csg_enabled = (err == 0); + + if (err == -FDT_ERR_NOTFOUND) { + err = 0; + } + + if (err != 0) { + return err; + } + + pll->enabled = true; + + pll->frac = fdt_read_uint32_default(fdt, subnode_pll, "frac", 0); + + pll->src = UINT32_MAX; + + err = fdt_read_uint32(fdt, subnode_pll, "src", &val); + if (err == 0) { + pll->src = val; + } + + return 0; +} + +#define RCC_PLL_NAME_SIZE 12 + +static int stm32_clk_parse_fdt_all_pll(void *fdt, int node, struct stm32_clk_platdata *pdata) +{ + unsigned int i = 0; + + for (i = _PLL1; i < pdata->npll; i++) { + struct stm32_pll_dt_cfg *pll = pdata->pll + i; + char name[RCC_PLL_NAME_SIZE]; + int subnode = 0; + int err = 0; + + snprintf(name, sizeof(name), "st,pll-%u", i + 1); + + subnode = fdt_subnode_offset(fdt, node, name); + if (!fdt_check_node(subnode)) { + continue; + } + + err = clk_stm32_parse_pll_fdt(fdt, subnode, pll); + if (err != 0) { + panic(); + } + } + + return 0; +} + +static int stm32_clk_parse_fdt(struct stm32_clk_platdata *pdata) +{ + void *fdt = NULL; + int node; + int err; + + if (fdt_get_address(&fdt) == 0) { + return -ENOENT; + } + + node = fdt_node_offset_by_compatible(fdt, -1, DT_RCC_CLK_COMPAT); + if (node < 0) { + panic(); + } + + err = stm32_clk_parse_fdt_all_oscillator(fdt, pdata); + if (err != 0) { + return err; + } + + err = stm32_clk_parse_fdt_all_pll(fdt, node, pdata); + if (err != 0) { + return err; + } + + err = stm32_clk_parse_fdt_by_name(fdt, node, "st,busclk", pdata->busclk, &pdata->nbusclk); + if (err != 0) { + return err; + } + + err = stm32_clk_parse_fdt_by_name(fdt, node, "st,flexgen", pdata->flexgen, + &pdata->nflexgen); + if (err != 0) { + return err; + } + + err = stm32_clk_parse_fdt_by_name(fdt, node, "st,kerclk", pdata->kernelclk, + &pdata->nkernelclk); + if (err != 0) { + return err; + } + + return 0; +} +#endif /* IMAGE_BL2 */ + +static struct stm32_osci_dt_cfg mp25_osci[NB_OSCILLATOR]; + +static struct stm32_pll_dt_cfg mp25_pll[_PLL_NB]; + +#define DT_FLEXGEN_CLK_MAX 64 +static uint32_t mp25_flexgen[DT_FLEXGEN_CLK_MAX]; + +#define DT_BUS_CLK_MAX 6 +static uint32_t mp25_busclk[DT_BUS_CLK_MAX]; + +#define DT_KERNEL_CLK_MAX 20 +static uint32_t mp25_kernelclk[DT_KERNEL_CLK_MAX]; + +static struct stm32_clk_platdata stm32mp25_pdata = { + .osci = mp25_osci, + .nosci = NB_OSCILLATOR, + .pll = mp25_pll, + .npll = _PLL_NB, + .flexgen = mp25_flexgen, + .nflexgen = DT_FLEXGEN_CLK_MAX, + .busclk = mp25_busclk, + .nbusclk = DT_BUS_CLK_MAX, + .kernelclk = mp25_kernelclk, + .nkernelclk = DT_KERNEL_CLK_MAX, +}; + +static uint8_t refcounts_mp25[CK_LAST]; + +static struct stm32_clk_priv stm32mp25_clock_data = { + .base = RCC_BASE, + .num = ARRAY_SIZE(stm32mp25_clk), + .clks = stm32mp25_clk, + .parents = parent_mp25, + .nb_parents = ARRAY_SIZE(parent_mp25), + .gates = gates_mp25, + .nb_gates = ARRAY_SIZE(gates_mp25), + .div = dividers_mp25, + .nb_div = ARRAY_SIZE(dividers_mp25), + .osci_data = stm32mp25_osc_data, + .nb_osci_data = ARRAY_SIZE(stm32mp25_osc_data), + .gate_refcounts = refcounts_mp25, + .pdata = &stm32mp25_pdata, + .ops_array = ops_array_mp25, +}; + +int stm32mp2_clk_init(void) +{ + uintptr_t base = RCC_BASE; + int ret; + +#ifdef IMAGE_BL2 + ret = stm32_clk_parse_fdt(&stm32mp25_pdata); + if (ret != 0) { + return ret; + } +#endif + + ret = clk_stm32_init(&stm32mp25_clock_data, base); + if (ret != 0) { + return ret; + } + +#ifdef IMAGE_BL2 + ret = stm32mp2_init_clock_tree(); + if (ret != 0) { + return ret; + } + + clk_stm32_enable_critical_clocks(); +#endif + + return 0; +} + +int stm32mp2_pll1_disable(void) +{ +#ifdef IMAGE_BL2 + return -EPERM; +#else + uintptr_t a35_ss_address = A35SSC_BASE; + uintptr_t pll_enable_reg = a35_ss_address + A35_SS_PLL_ENABLE; + + stm32mp2_a35_ss_on_hsi(); + + mmio_clrbits_32(pll_enable_reg, A35_SS_PLL_ENABLE_PD); + + return 0; +#endif +} diff --git a/drivers/st/clk/stm32mp1_clk.c b/drivers/st/clk/stm32mp1_clk.c index c9c3c5f9..3d352afb 100644 --- a/drivers/st/clk/stm32mp1_clk.c +++ b/drivers/st/clk/stm32mp1_clk.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2018-2022, STMicroelectronics - All Rights Reserved + * Copyright (C) 2018-2024, STMicroelectronics - All Rights Reserved * * SPDX-License-Identifier: GPL-2.0+ OR BSD-3-Clause */ @@ -27,6 +27,71 @@ #include <platform_def.h> +enum stm32mp1_pllcfg { + PLLCFG_M, + PLLCFG_N, + PLL_DIV_MN_NB, + PLLCFG_P = PLL_DIV_MN_NB, + PLLCFG_Q, + PLLCFG_R, + PLLCFG_O, + PLLCFG_NB +}; + +#define PLL_DIV_MN_NB 2 +#define PLL_DIV_PQR_NB 3 + +enum stm32mp1_pllcsg { + PLLCSG_MOD_PER, + PLLCSG_INC_STEP, + PLLCSG_SSCG_MODE, + PLLCSG_NB +}; + +struct stm32_pll_dt_cfg { + bool status; + uint32_t src; + uint32_t cfg[PLLCFG_NB]; + uint32_t frac; + bool csg_enabled; + uint32_t csg[PLLCSG_NB]; +}; + +struct stm32_clk_platdata { + uint32_t npll; + struct stm32_pll_dt_cfg *pll; + uint32_t nclksrc; + uint32_t *clksrc; + uint32_t nclkdiv; + uint32_t *clkdiv; + bool lse_css; +}; + +struct stm32_clk_priv { + uintptr_t base; + const struct mux_cfg *parents; + const uint32_t nb_parents; + const struct div_cfg *div; + const uint32_t nb_div; + void *pdata; +}; + +static struct stm32_clk_priv *stm32_clock_data; + +static struct stm32_clk_priv *clk_stm32_get_priv(void) +{ + return stm32_clock_data; +} + +static int clk_stm32_init(struct stm32_clk_priv *priv, uintptr_t base) +{ + stm32_clock_data = priv; + + priv->base = base; + + return 0; +} + #define MAX_HSI_HZ 64000000 #define USB_PHY_48_MHZ 48000000 @@ -39,6 +104,199 @@ #define HSIDIV_TIMEOUT TIMEOUT_US_200MS #define OSCRDY_TIMEOUT TIMEOUT_US_1S +struct mux_cfg { + uint16_t offset; + uint8_t shift; + uint8_t width; + uint8_t bitrdy; +}; + +struct div_cfg { + uint16_t offset; + uint8_t shift; + uint8_t width; + uint8_t bitrdy; +}; + +#define DIV_NO_BIT_RDY UINT8_MAX + +#define DIV_CFG(_id, _offset, _shift, _width, _bitrdy)\ + [(_id)] = {\ + .offset = (_offset),\ + .shift = (_shift),\ + .width = (_width),\ + .bitrdy = (_bitrdy),\ + } + +static const struct div_cfg dividers_mp15[] = { + DIV_CFG(DIV_MPU, RCC_MPCKDIVR, 0, 4, 31), + DIV_CFG(DIV_AXI, RCC_AXIDIVR, 0, 3, 31), + DIV_CFG(DIV_MCU, RCC_MCUDIVR, 0, 4, 31), + DIV_CFG(DIV_APB1, RCC_APB1DIVR, 0, 3, 31), + DIV_CFG(DIV_APB2, RCC_APB2DIVR, 0, 3, 31), + DIV_CFG(DIV_APB3, RCC_APB3DIVR, 0, 3, 31), + DIV_CFG(DIV_APB4, RCC_APB4DIVR, 0, 3, 31), + DIV_CFG(DIV_APB5, RCC_APB5DIVR, 0, 3, 31), + DIV_CFG(DIV_RTC, RCC_RTCDIVR, 0, 6, DIV_NO_BIT_RDY), + DIV_CFG(DIV_MCO1, RCC_MCO1CFGR, 4, 4, DIV_NO_BIT_RDY), + DIV_CFG(DIV_MCO2, RCC_MCO2CFGR, 4, 4, DIV_NO_BIT_RDY), + DIV_CFG(DIV_TRACE, RCC_DBGCFGR, 0, 3, DIV_NO_BIT_RDY), + DIV_CFG(DIV_ETHPTP, RCC_ETHCKSELR, 4, 4, DIV_NO_BIT_RDY), +}; + +/* + * MUX CONFIG + */ + +#define MUX_NO_BIT_RDY UINT8_MAX + +#define MUXRDY_CFG(_id, _offset, _shift, _width, _bitrdy)\ + [(_id)] = {\ + .offset = (_offset),\ + .shift = (_shift),\ + .width = (_width),\ + .bitrdy = (_bitrdy),\ + } + +#define MUX_CFG(_id, _offset, _shift, _width)\ + MUXRDY_CFG(_id, _offset, _shift, _width, MUX_NO_BIT_RDY) + +static const struct mux_cfg parent_mp15[MUX_NB] = { + MUX_CFG(MUX_PLL12, RCC_RCK12SELR, 0, 2), + MUX_CFG(MUX_PLL3, RCC_RCK3SELR, 0, 2), + MUX_CFG(MUX_PLL4, RCC_RCK4SELR, 0, 2), + MUX_CFG(MUX_CKPER, RCC_CPERCKSELR, 0, 2), + MUXRDY_CFG(MUX_MPU, RCC_MPCKSELR, 0, 2, 31), + MUXRDY_CFG(MUX_AXI, RCC_ASSCKSELR, 0, 3, 31), + MUXRDY_CFG(MUX_MCU, RCC_MSSCKSELR, 0, 2, 31), + MUX_CFG(MUX_RTC, RCC_BDCR, 16, 2), + MUX_CFG(MUX_SDMMC12, RCC_SDMMC12CKSELR, 0, 3), + MUX_CFG(MUX_SPI2S23, RCC_SPI2S23CKSELR, 0, 3), + MUX_CFG(MUX_SPI45, RCC_SPI45CKSELR, 0, 3), + MUX_CFG(MUX_I2C12, RCC_I2C12CKSELR, 0, 3), + MUX_CFG(MUX_I2C35, RCC_I2C35CKSELR, 0, 3), + MUX_CFG(MUX_LPTIM23, RCC_LPTIM23CKSELR, 0, 3), + MUX_CFG(MUX_LPTIM45, RCC_LPTIM45CKSELR, 0, 3), + MUX_CFG(MUX_UART24, RCC_UART24CKSELR, 0, 3), + MUX_CFG(MUX_UART35, RCC_UART35CKSELR, 0, 3), + MUX_CFG(MUX_UART78, RCC_UART78CKSELR, 0, 3), + MUX_CFG(MUX_SAI1, RCC_SAI1CKSELR, 0, 3), + MUX_CFG(MUX_ETH, RCC_ETHCKSELR, 0, 2), + MUX_CFG(MUX_I2C46, RCC_I2C46CKSELR, 0, 3), + MUX_CFG(MUX_RNG2, RCC_RNG2CKSELR, 0, 2), + MUX_CFG(MUX_SDMMC3, RCC_SDMMC3CKSELR, 0, 3), + MUX_CFG(MUX_FMC, RCC_FMCCKSELR, 0, 2), + MUX_CFG(MUX_QSPI, RCC_QSPICKSELR, 0, 2), + MUX_CFG(MUX_USBPHY, RCC_USBCKSELR, 0, 2), + MUX_CFG(MUX_USBO, RCC_USBCKSELR, 4, 1), + MUX_CFG(MUX_SPDIF, RCC_SPDIFCKSELR, 0, 2), + MUX_CFG(MUX_SPI2S1, RCC_SPI2S1CKSELR, 0, 3), + MUX_CFG(MUX_CEC, RCC_CECCKSELR, 0, 2), + MUX_CFG(MUX_LPTIM1, RCC_LPTIM1CKSELR, 0, 3), + MUX_CFG(MUX_UART6, RCC_UART6CKSELR, 0, 3), + MUX_CFG(MUX_FDCAN, RCC_FDCANCKSELR, 0, 2), + MUX_CFG(MUX_SAI2, RCC_SAI2CKSELR, 0, 3), + MUX_CFG(MUX_SAI3, RCC_SAI3CKSELR, 0, 3), + MUX_CFG(MUX_SAI4, RCC_SAI4CKSELR, 0, 3), + MUX_CFG(MUX_ADC, RCC_ADCCKSELR, 0, 2), + MUX_CFG(MUX_DSI, RCC_DSICKSELR, 0, 1), + MUX_CFG(MUX_RNG1, RCC_RNG1CKSELR, 0, 2), + MUX_CFG(MUX_STGEN, RCC_STGENCKSELR, 0, 2), + MUX_CFG(MUX_UART1, RCC_UART1CKSELR, 0, 3), + MUX_CFG(MUX_SPI6, RCC_SPI6CKSELR, 0, 3), + MUX_CFG(MUX_MCO1, RCC_MCO1CFGR, 0, 3), + MUX_CFG(MUX_MCO2, RCC_MCO2CFGR, 0, 3), +}; + +#define MASK_WIDTH_SHIFT(_width, _shift) \ + GENMASK(((_width) + (_shift) - 1U), (_shift)) + +int clk_mux_get_parent(struct stm32_clk_priv *priv, uint32_t mux_id) +{ + const struct mux_cfg *mux; + uint32_t mask; + + if (mux_id >= priv->nb_parents) { + panic(); + } + + mux = &priv->parents[mux_id]; + + mask = MASK_WIDTH_SHIFT(mux->width, mux->shift); + + return (mmio_read_32(priv->base + mux->offset) & mask) >> mux->shift; +} + +static int clk_mux_set_parent(struct stm32_clk_priv *priv, uint16_t pid, uint8_t sel) +{ + const struct mux_cfg *mux = &priv->parents[pid]; + uintptr_t address = priv->base + mux->offset; + uint32_t mask; + uint64_t timeout; + + mask = MASK_WIDTH_SHIFT(mux->width, mux->shift); + + mmio_clrsetbits_32(address, mask, (sel << mux->shift) & mask); + + if (mux->bitrdy == MUX_NO_BIT_RDY) { + return 0; + } + + timeout = timeout_init_us(CLKSRC_TIMEOUT); + + mask = BIT(mux->bitrdy); + + while ((mmio_read_32(address) & mask) == 0U) { + if (timeout_elapsed(timeout)) { + return -ETIMEDOUT; + } + } + + return 0; +} + +static int stm32_clk_configure_mux(struct stm32_clk_priv *priv, uint32_t val) +{ + uint32_t data = val & CMD_DATA_MASK; + int mux = (data & MUX_ID_MASK) >> MUX_ID_SHIFT; + int sel = (data & MUX_SEL_MASK) >> MUX_SEL_SHIFT; + + return clk_mux_set_parent(priv, mux, sel); +} + +int clk_stm32_set_div(struct stm32_clk_priv *priv, uint32_t div_id, uint32_t value) +{ + const struct div_cfg *divider; + uintptr_t address; + uint64_t timeout; + uint32_t mask; + + if (div_id >= priv->nb_div) { + panic(); + } + + divider = &priv->div[div_id]; + address = priv->base + divider->offset; + + mask = MASK_WIDTH_SHIFT(divider->width, divider->shift); + mmio_clrsetbits_32(address, mask, (value << divider->shift) & mask); + + if (divider->bitrdy == DIV_NO_BIT_RDY) { + return 0; + } + + timeout = timeout_init_us(CLKSRC_TIMEOUT); + mask = BIT(divider->bitrdy); + + while ((mmio_read_32(address) & mask) == 0U) { + if (timeout_elapsed(timeout)) { + return -ETIMEDOUT; + } + } + + return 0; +} + const char *stm32mp_osc_node_label[NB_OSC] = { [_LSI] = "clk-lsi", [_LSE] = "clk-lse", @@ -206,23 +464,6 @@ enum stm32mp1_clkdiv_id { CLKDIV_NB }; -enum stm32mp1_pllcfg { - PLLCFG_M, - PLLCFG_N, - PLLCFG_P, - PLLCFG_Q, - PLLCFG_R, - PLLCFG_O, - PLLCFG_NB -}; - -enum stm32mp1_pllcsg { - PLLCSG_MOD_PER, - PLLCSG_INC_STEP, - PLLCSG_SSCG_MODE, - PLLCSG_NB -}; - enum stm32mp1_plltype { PLL_800, PLL_1600, @@ -537,7 +778,18 @@ static const struct stm32mp1_clk_sel stm32mp1_clk_sel[_PARENT_SEL_NB] = { }; /* Define characteristic of PLL according type */ -#define DIVN_MIN 24 +#define POST_DIVM_MIN 8000000U +#define POST_DIVM_MAX 16000000U +#define DIVM_MIN 0U +#define DIVM_MAX 63U +#define DIVN_MIN 24U +#define DIVN_MAX 99U +#define DIVP_MIN 0U +#define DIVP_MAX 127U +#define FRAC_MAX 8192U +#define VCO_MIN 800000000U +#define VCO_MAX 1600000000U + static const struct stm32mp1_pll stm32mp1_pll[PLL_TYPE_NB] = { [PLL_800] = { .refclk_min = 4, @@ -1327,6 +1579,11 @@ static void stm32mp1_lse_enable(bool bypass, bool digbyp, uint32_t lsedrv) uint32_t value; uintptr_t rcc_base = stm32mp_rcc_base(); + /* Do not reconfigure LSE if it is already ON */ + if ((mmio_read_32(rcc_base + RCC_BDCR) & RCC_BDCR_LSEON) == RCC_BDCR_LSEON) { + return; + } + if (digbyp) { mmio_setbits_32(rcc_base + RCC_BDCR, RCC_BDCR_DIGBYP); } @@ -1360,7 +1617,7 @@ static void stm32mp1_lse_enable(bool bypass, bool digbyp, uint32_t lsedrv) static void stm32mp1_lse_wait(void) { if (stm32mp1_osc_wait(true, RCC_BDCR, RCC_BDCR_LSERDY) != 0) { - VERBOSE("%s: failed\n", __func__); + EARLY_ERROR("%s: failed\n", __func__); } } @@ -1369,7 +1626,7 @@ static void stm32mp1_lsi_set(bool enable) stm32mp1_ls_osc_set(enable, RCC_RDLSICR, RCC_RDLSICR_LSION); if (stm32mp1_osc_wait(enable, RCC_RDLSICR, RCC_RDLSICR_LSIRDY) != 0) { - VERBOSE("%s: failed\n", __func__); + EARLY_ERROR("%s: failed\n", __func__); } } @@ -1387,7 +1644,7 @@ static void stm32mp1_hse_enable(bool bypass, bool digbyp, bool css) stm32mp1_hs_ocs_set(true, RCC_OCENR_HSEON); if (stm32mp1_osc_wait(true, RCC_OCRDYR, RCC_OCRDYR_HSERDY) != 0) { - VERBOSE("%s: failed\n", __func__); + EARLY_ERROR("%s: failed\n", __func__); } if (css) { @@ -1406,7 +1663,7 @@ static void stm32mp1_csi_set(bool enable) { stm32mp1_hs_ocs_set(enable, RCC_OCENR_CSION); if (stm32mp1_osc_wait(enable, RCC_OCRDYR, RCC_OCRDYR_CSIRDY) != 0) { - VERBOSE("%s: failed\n", __func__); + EARLY_ERROR("%s: failed\n", __func__); } } @@ -1414,7 +1671,7 @@ static void stm32mp1_hsi_set(bool enable) { stm32mp1_hs_ocs_set(enable, RCC_OCENR_HSION); if (stm32mp1_osc_wait(enable, RCC_OCRDYR, RCC_OCRDYR_HSIRDY) != 0) { - VERBOSE("%s: failed\n", __func__); + EARLY_ERROR("%s: failed\n", __func__); } } @@ -1454,7 +1711,7 @@ static int stm32mp1_hsidiv(unsigned long hsifreq) } if (hsidiv == 4U) { - ERROR("Invalid clk-hsi frequency\n"); + EARLY_ERROR("Invalid clk-hsi frequency\n"); return -1; } @@ -1467,7 +1724,7 @@ static int stm32mp1_hsidiv(unsigned long hsifreq) static bool stm32mp1_check_pll_conf(enum stm32mp1_pll_id pll_id, unsigned int clksrc, - uint32_t *pllcfg, int plloff) + uint32_t *pllcfg, uint32_t fracv) { const struct stm32mp1_clk_pll *pll = pll_ref(pll_id); uintptr_t rcc_base = stm32mp_rcc_base(); @@ -1476,8 +1733,7 @@ static bool stm32mp1_check_pll_conf(enum stm32mp1_pll_id pll_id, uintptr_t clksrc_address = rcc_base + (clksrc >> 4); unsigned long refclk; uint32_t ifrge = 0U; - uint32_t src, value, fracv = 0; - void *fdt; + uint32_t src, value; /* Check PLL output */ if (mmio_read_32(pllxcr) != RCC_PLLNCR_PLLON) { @@ -1516,10 +1772,6 @@ static bool stm32mp1_check_pll_conf(enum stm32mp1_pll_id pll_id, } /* Fractional configuration */ - if (fdt_get_address(&fdt) == 1) { - fracv = fdt_read_uint32_default(fdt, plloff, "frac", 0); - } - value = fracv << RCC_PLLNFRACR_FRACV_SHIFT; value |= RCC_PLLNFRACR_FRACLE; if (mmio_read_32(rcc_base + pll->pllxfracr) != value) { @@ -1561,8 +1813,8 @@ static int stm32mp1_pll_output(enum stm32mp1_pll_id pll_id, uint32_t output) /* Wait PLL lock */ while ((mmio_read_32(pllxcr) & RCC_PLLNCR_PLLRDY) == 0U) { if (timeout_elapsed(timeout)) { - ERROR("PLL%u start failed @ 0x%lx: 0x%x\n", - pll_id, pllxcr, mmio_read_32(pllxcr)); + EARLY_ERROR("PLL%u start failed @ 0x%lx: 0x%x\n", + pll_id, pllxcr, mmio_read_32(pllxcr)); return -ETIMEDOUT; } } @@ -1590,8 +1842,8 @@ static int stm32mp1_pll_stop(enum stm32mp1_pll_id pll_id) /* Wait PLL stopped */ while ((mmio_read_32(pllxcr) & RCC_PLLNCR_PLLRDY) != 0U) { if (timeout_elapsed(timeout)) { - ERROR("PLL%u stop failed @ 0x%lx: 0x%x\n", - pll_id, pllxcr, mmio_read_32(pllxcr)); + EARLY_ERROR("PLL%u stop failed @ 0x%lx: 0x%x\n", + pll_id, pllxcr, mmio_read_32(pllxcr)); return -ETIMEDOUT; } } @@ -1683,190 +1935,270 @@ static void stm32mp1_pll_csg(enum stm32mp1_pll_id pll_id, uint32_t *csg) RCC_PLLNCR_SSCG_CTRL); } -static int stm32mp1_set_clksrc(unsigned int clksrc) +static int clk_compute_pll1_settings(unsigned long input_freq, + uint32_t freq_khz, + uint32_t *pllcfg, uint32_t *fracv) { - uintptr_t clksrc_address = stm32mp_rcc_base() + (clksrc >> 4); - uint64_t timeout; + unsigned long long best_diff = ULLONG_MAX; + unsigned int divm; - mmio_clrsetbits_32(clksrc_address, RCC_SELR_SRC_MASK, - clksrc & RCC_SELR_SRC_MASK); + /* Following parameters have always the same value */ + pllcfg[PLLCFG_Q] = 0U; + pllcfg[PLLCFG_R] = 0U; + pllcfg[PLLCFG_O] = PQR(1, 0, 0); - timeout = timeout_init_us(CLKSRC_TIMEOUT); - while ((mmio_read_32(clksrc_address) & RCC_SELR_SRCRDY) == 0U) { - if (timeout_elapsed(timeout)) { - ERROR("CLKSRC %x start failed @ 0x%lx: 0x%x\n", clksrc, - clksrc_address, mmio_read_32(clksrc_address)); - return -ETIMEDOUT; + for (divm = (DIVM_MAX + 1U); divm != DIVM_MIN; divm--) { + unsigned long post_divm = input_freq / divm; + unsigned int divp; + + if ((post_divm < POST_DIVM_MIN) || (post_divm > POST_DIVM_MAX)) { + continue; } - } - return 0; -} + for (divp = DIVP_MIN; divp <= DIVP_MAX; divp++) { + unsigned long long output_freq = freq_khz * 1000ULL; + unsigned long long freq; + unsigned long long divn; + unsigned long long frac; + unsigned int i; -static int stm32mp1_set_clkdiv(unsigned int clkdiv, uintptr_t address) -{ - uint64_t timeout; + freq = output_freq * divm * (divp + 1U); - mmio_clrsetbits_32(address, RCC_DIVR_DIV_MASK, - clkdiv & RCC_DIVR_DIV_MASK); + divn = (freq / input_freq) - 1U; + if ((divn < DIVN_MIN) || (divn > DIVN_MAX)) { + continue; + } - timeout = timeout_init_us(CLKDIV_TIMEOUT); - while ((mmio_read_32(address) & RCC_DIVR_DIVRDY) == 0U) { - if (timeout_elapsed(timeout)) { - ERROR("CLKDIV %x start failed @ 0x%lx: 0x%x\n", - clkdiv, address, mmio_read_32(address)); - return -ETIMEDOUT; + frac = ((freq * FRAC_MAX) / input_freq) - ((divn + 1U) * FRAC_MAX); + + /* 2 loops to refine the fractional part */ + for (i = 2U; i != 0U; i--) { + unsigned long long diff; + unsigned long long vco; + + if (frac > FRAC_MAX) { + break; + } + + vco = (post_divm * (divn + 1U)) + ((post_divm * frac) / FRAC_MAX); + + if ((vco < (VCO_MIN / 2U)) || (vco > (VCO_MAX / 2U))) { + frac++; + continue; + } + + freq = vco / (divp + 1U); + if (output_freq < freq) { + diff = freq - output_freq; + } else { + diff = output_freq - freq; + } + + if (diff < best_diff) { + pllcfg[PLLCFG_M] = divm - 1U; + pllcfg[PLLCFG_N] = (uint32_t)divn; + pllcfg[PLLCFG_P] = divp; + *fracv = (uint32_t)frac; + + if (diff == 0U) { + return 0; + } + + best_diff = diff; + } + + frac++; + } } } + if (best_diff == ULLONG_MAX) { + return -EINVAL; + } + return 0; } -static void stm32mp1_mco_csg(uint32_t clksrc, uint32_t clkdiv) +static int clk_get_pll1_settings(uint32_t clksrc, uint32_t freq_khz, + uint32_t *pllcfg, uint32_t *fracv) { - uintptr_t clksrc_address = stm32mp_rcc_base() + (clksrc >> 4); + unsigned long input_freq = 0UL; - /* - * Binding clksrc : - * bit15-4 offset - * bit3: disable - * bit2-0: MCOSEL[2:0] - */ - if ((clksrc & 0x8U) != 0U) { - mmio_clrbits_32(clksrc_address, RCC_MCOCFG_MCOON); - } else { - mmio_clrsetbits_32(clksrc_address, - RCC_MCOCFG_MCOSRC_MASK, - clksrc & RCC_MCOCFG_MCOSRC_MASK); - mmio_clrsetbits_32(clksrc_address, - RCC_MCOCFG_MCODIV_MASK, - clkdiv << RCC_MCOCFG_MCODIV_SHIFT); - mmio_setbits_32(clksrc_address, RCC_MCOCFG_MCOON); + assert(pllcfg != NULL); + assert(fracv != NULL); + + switch (clksrc) { + case CLK_PLL12_HSI: + input_freq = stm32mp_clk_get_rate(CK_HSI); + break; + case CLK_PLL12_HSE: + input_freq = stm32mp_clk_get_rate(CK_HSE); + break; + default: + break; } + + if (input_freq == 0UL) { + panic(); + } + + return clk_compute_pll1_settings(input_freq, freq_khz, pllcfg, fracv); } -static void stm32mp1_set_rtcsrc(unsigned int clksrc, bool lse_css) +static int stm32_clk_dividers_configure(struct stm32_clk_priv *priv) { - uintptr_t address = stm32mp_rcc_base() + RCC_BDCR; + struct stm32_clk_platdata *pdata = priv->pdata; + uint32_t i; + + for (i = 0U; i < pdata->nclkdiv; i++) { + uint32_t div_id, div_n; + uint32_t val; + int ret; - if (((mmio_read_32(address) & RCC_BDCR_RTCCKEN) == 0U) || - (clksrc != (uint32_t)CLK_RTC_DISABLED)) { - mmio_clrsetbits_32(address, - RCC_BDCR_RTCSRC_MASK, - (clksrc & RCC_SELR_SRC_MASK) << RCC_BDCR_RTCSRC_SHIFT); + val = pdata->clkdiv[i] & CMD_DATA_MASK; + div_id = (val & DIV_ID_MASK) >> DIV_ID_SHIFT; + div_n = (val & DIV_DIVN_MASK) >> DIV_DIVN_SHIFT; - mmio_setbits_32(address, RCC_BDCR_RTCCKEN); + ret = clk_stm32_set_div(priv, div_id, div_n); + if (ret != 0) { + return ret; + } } - if (lse_css) { - mmio_setbits_32(address, RCC_BDCR_LSECSSON); + return 0; +} + +static int stm32_clk_configure_clk(struct stm32_clk_priv *priv, uint32_t data) +{ + uint32_t sel = (data & CLK_SEL_MASK) >> CLK_SEL_SHIFT; + uint32_t enable = (data & CLK_ON_MASK) >> CLK_ON_SHIFT; + unsigned long binding_id = ((unsigned long)data & CLK_ID_MASK) >> CLK_ID_SHIFT; + struct stm32_clk_platdata *pdata = priv->pdata; + + if (binding_id == RTC) { + uintptr_t address = stm32mp_rcc_base() + RCC_BDCR; + + if (((mmio_read_32(address) & RCC_BDCR_RTCCKEN) == 0U) || (enable != 0U)) { + mmio_clrsetbits_32(address, RCC_BDCR_RTCSRC_MASK, + (sel & RCC_SELR_SRC_MASK) << RCC_BDCR_RTCSRC_SHIFT); + + mmio_setbits_32(address, RCC_BDCR_RTCCKEN); + /* Configure LSE CSS */ + if (pdata->lse_css) { + mmio_setbits_32(priv->base + RCC_BDCR, RCC_BDCR_LSECSSON); + } + } } + + return 0; } -static void stm32mp1_pkcs_config(uint32_t pkcs) +static int stm32_clk_configure_by_addr_val(struct stm32_clk_priv *priv, + uint32_t data) { - uintptr_t address = stm32mp_rcc_base() + ((pkcs >> 4) & 0xFFFU); - uint32_t value = pkcs & 0xFU; - uint32_t mask = 0xFU; + uint32_t addr = data >> CLK_ADDR_SHIFT; + uint32_t val = data & CLK_ADDR_VAL_MASK; - if ((pkcs & BIT(31)) != 0U) { - mask <<= 4; - value <<= 4; - } + mmio_setbits_32(priv->base + addr, val); - mmio_clrsetbits_32(address, mask, value); + return 0; } -static int clk_get_pll_settings_from_dt(int plloff, unsigned int *pllcfg, - uint32_t *fracv, uint32_t *csg, - bool *csg_set) +static int stm32_clk_source_configure(struct stm32_clk_priv *priv) { - void *fdt; - int ret; + struct stm32_clk_platdata *pdata = priv->pdata; + bool ckper_disabled = false; + uint32_t i; + + for (i = 0U; i < pdata->nclksrc; i++) { + uint32_t val = pdata->clksrc[i]; + uint32_t cmd, cmd_data; + int ret; + + if (val & CMD_ADDR_BIT) { + ret = stm32_clk_configure_by_addr_val(priv, val & ~CMD_ADDR_BIT); + if (ret != 0) { + return ret; + } - if (fdt_get_address(&fdt) == 0) { - return -FDT_ERR_NOTFOUND; - } + continue; + } - ret = fdt_read_uint32_array(fdt, plloff, "cfg", (uint32_t)PLLCFG_NB, - pllcfg); - if (ret < 0) { - return -FDT_ERR_NOTFOUND; + if (val == (uint32_t)CLK_CKPER_DISABLED) { + ckper_disabled = true; + continue; + } + + cmd = (val & CMD_MASK) >> CMD_SHIFT; + cmd_data = val & ~CMD_MASK; + + switch (cmd) { + case CMD_MUX: + ret = stm32_clk_configure_mux(priv, cmd_data); + break; + + case CMD_CLK: + ret = stm32_clk_configure_clk(priv, cmd_data); + break; + default: + ret = -EINVAL; + break; + } + + if (ret != 0) { + return ret; + } } - *fracv = fdt_read_uint32_default(fdt, plloff, "frac", 0); + /* + * CKPER is source for some peripheral clocks + * (FMC-NAND / QPSI-NOR) and switching source is allowed + * only if previous clock is still ON + * => deactivate CKPER only after switching clock + */ + if (!ckper_disabled) { + return 0; + } - ret = fdt_read_uint32_array(fdt, plloff, "csg", (uint32_t)PLLCSG_NB, - csg); + return stm32_clk_configure_mux(priv, CLK_CKPER_DISABLED); +} - *csg_set = (ret == 0); +static int stm32mp1_pll_configure_src(struct stm32_clk_priv *priv, int pll_idx) +{ + struct stm32_clk_platdata *pdata = priv->pdata; + struct stm32_pll_dt_cfg *pll_conf = &pdata->pll[pll_idx]; - if (ret == -FDT_ERR_NOTFOUND) { - ret = 0; + if (!pll_conf->status) { + return 0; } - return ret; + return stm32_clk_configure_mux(priv, pll_conf->src); } int stm32mp1_clk_init(void) { - uintptr_t rcc_base = stm32mp_rcc_base(); - uint32_t pllfracv[_PLL_NB]; - uint32_t pllcsg[_PLL_NB][PLLCSG_NB]; - unsigned int clksrc[CLKSRC_NB]; - unsigned int clkdiv[CLKDIV_NB]; - unsigned int pllcfg[_PLL_NB][PLLCFG_NB]; - int plloff[_PLL_NB]; - int ret, len; + struct stm32_clk_priv *priv = clk_stm32_get_priv(); + struct stm32_clk_platdata *pdata = priv->pdata; + struct stm32_pll_dt_cfg *pll_conf = pdata->pll; + int ret; enum stm32mp1_pll_id i; - bool pllcsg_set[_PLL_NB]; - bool pllcfg_valid[_PLL_NB]; - bool lse_css = false; bool pll3_preserve = false; bool pll4_preserve = false; bool pll4_bootrom = false; - const fdt32_t *pkcs_cell; - void *fdt; int stgen_p = stm32mp1_clk_get_parent(STGEN_K); int usbphy_p = stm32mp1_clk_get_parent(USBPHY_K); + uint32_t usbreg_bootrom = 0U; - if (fdt_get_address(&fdt) == 0) { - return -FDT_ERR_NOTFOUND; - } - - ret = fdt_rcc_read_uint32_array("st,clksrc", (uint32_t)CLKSRC_NB, - clksrc); - if (ret < 0) { - return -FDT_ERR_NOTFOUND; - } - - ret = fdt_rcc_read_uint32_array("st,clkdiv", (uint32_t)CLKDIV_NB, - clkdiv); - if (ret < 0) { - return -FDT_ERR_NOTFOUND; - } - - for (i = (enum stm32mp1_pll_id)0; i < _PLL_NB; i++) { - char name[12]; - - snprintf(name, sizeof(name), "st,pll@%u", i); - plloff[i] = fdt_rcc_subnode_offset(name); - - pllcfg_valid[i] = fdt_check_node(plloff[i]); - if (!pllcfg_valid[i]) { - continue; - } - - ret = clk_get_pll_settings_from_dt(plloff[i], pllcfg[i], - &pllfracv[i], pllcsg[i], - &pllcsg_set[i]); + if (!pll_conf[_PLL1].status) { + ret = clk_get_pll1_settings(pll_conf[_PLL2].src, PLL1_NOMINAL_FREQ_IN_KHZ, + pll_conf[_PLL1].cfg, &pll_conf[_PLL1].frac); if (ret != 0) { return ret; } - } - stm32mp1_mco_csg(clksrc[CLKSRC_MCO1], clkdiv[CLKDIV_MCO1]); - stm32mp1_mco_csg(clksrc[CLKSRC_MCO2], clkdiv[CLKDIV_MCO2]); + pll_conf[_PLL1].status = true; + pll_conf[_PLL1].src = pll_conf[_PLL2].src; + } /* * Switch ON oscillator found in device-tree. @@ -1882,7 +2214,7 @@ int stm32mp1_clk_init(void) bypass = fdt_clk_read_bool(name, "st,bypass"); digbyp = fdt_clk_read_bool(name, "st,digbypass"); - lse_css = fdt_clk_read_bool(name, "st,css"); + pdata->lse_css = fdt_clk_read_bool(name, "st,css"); lsedrv = fdt_clk_read_uint32_default(name, "st,drive", LSEDRV_MEDIUM_HIGH); stm32mp1_lse_enable(bypass, digbyp, lsedrv); @@ -1903,36 +2235,28 @@ int stm32mp1_clk_init(void) stm32mp1_csi_set(true); /* Come back to HSI */ - ret = stm32mp1_set_clksrc(CLK_MPU_HSI); + ret = stm32_clk_configure_mux(priv, CLK_MPU_HSI); if (ret != 0) { return ret; } - ret = stm32mp1_set_clksrc(CLK_AXI_HSI); + ret = stm32_clk_configure_mux(priv, CLK_AXI_HSI); if (ret != 0) { return ret; } - ret = stm32mp1_set_clksrc(CLK_MCU_HSI); + ret = stm32_clk_configure_mux(priv, CLK_MCU_HSI); if (ret != 0) { return ret; } - - if ((mmio_read_32(rcc_base + RCC_MP_RSTSCLRR) & + if ((mmio_read_32(priv->base + RCC_MP_RSTSCLRR) & RCC_MP_RSTSCLRR_MPUP0RSTF) != 0) { - if (pllcfg_valid[_PLL3]) { - pll3_preserve = - stm32mp1_check_pll_conf(_PLL3, - clksrc[CLKSRC_PLL3], - pllcfg[_PLL3], - plloff[_PLL3]); - } - - if (pllcfg_valid[_PLL4]) { - pll4_preserve = - stm32mp1_check_pll_conf(_PLL4, - clksrc[CLKSRC_PLL4], - pllcfg[_PLL4], - plloff[_PLL4]); - } + pll3_preserve = stm32mp1_check_pll_conf(_PLL3, + pll_conf[_PLL3].src, + pll_conf[_PLL3].cfg, + pll_conf[_PLL3].frac); + pll4_preserve = stm32mp1_check_pll_conf(_PLL4, + pll_conf[_PLL4].src, + pll_conf[_PLL4].cfg, + pll_conf[_PLL4].frac); } /* Don't initialize PLL4, when used by BOOTROM */ if ((stm32mp_get_boot_itf_selected() == @@ -1964,58 +2288,27 @@ int stm32mp1_clk_init(void) stm32mp_stgen_config(stm32mp_clk_get_rate(STGEN_K)); } - /* Select DIV */ - /* No ready bit when MPUSRC != CLK_MPU_PLL1P_DIV, MPUDIV is disabled */ - mmio_write_32(rcc_base + RCC_MPCKDIVR, - clkdiv[CLKDIV_MPU] & RCC_DIVR_DIV_MASK); - ret = stm32mp1_set_clkdiv(clkdiv[CLKDIV_AXI], rcc_base + RCC_AXIDIVR); - if (ret != 0) { - return ret; - } - ret = stm32mp1_set_clkdiv(clkdiv[CLKDIV_APB4], rcc_base + RCC_APB4DIVR); - if (ret != 0) { - return ret; - } - ret = stm32mp1_set_clkdiv(clkdiv[CLKDIV_APB5], rcc_base + RCC_APB5DIVR); - if (ret != 0) { - return ret; - } - ret = stm32mp1_set_clkdiv(clkdiv[CLKDIV_MCU], rcc_base + RCC_MCUDIVR); - if (ret != 0) { - return ret; - } - ret = stm32mp1_set_clkdiv(clkdiv[CLKDIV_APB1], rcc_base + RCC_APB1DIVR); - if (ret != 0) { - return ret; - } - ret = stm32mp1_set_clkdiv(clkdiv[CLKDIV_APB2], rcc_base + RCC_APB2DIVR); - if (ret != 0) { - return ret; - } - ret = stm32mp1_set_clkdiv(clkdiv[CLKDIV_APB3], rcc_base + RCC_APB3DIVR); + /* Configure dividers */ + ret = stm32_clk_dividers_configure(priv); if (ret != 0) { return ret; } - /* No ready bit for RTC */ - mmio_write_32(rcc_base + RCC_RTCDIVR, - clkdiv[CLKDIV_RTC] & RCC_DIVR_DIV_MASK); - /* Configure PLLs source */ - ret = stm32mp1_set_clksrc(clksrc[CLKSRC_PLL12]); + ret = stm32mp1_pll_configure_src(priv, _PLL1); if (ret != 0) { return ret; } if (!pll3_preserve) { - ret = stm32mp1_set_clksrc(clksrc[CLKSRC_PLL3]); + ret = stm32mp1_pll_configure_src(priv, _PLL3); if (ret != 0) { return ret; } } if (!pll4_preserve) { - ret = stm32mp1_set_clksrc(clksrc[CLKSRC_PLL4]); + ret = stm32mp1_pll_configure_src(priv, _PLL4); if (ret != 0) { return ret; } @@ -2028,34 +2321,34 @@ int stm32mp1_clk_init(void) continue; } - if (!pllcfg_valid[i]) { + if (!pll_conf[i].status) { continue; } if ((i == _PLL4) && pll4_bootrom) { /* Set output divider if not done by the Bootrom */ - stm32mp1_pll_config_output(i, pllcfg[i]); + stm32mp1_pll_config_output(i, pll_conf[i].cfg); continue; } - ret = stm32mp1_pll_config(i, pllcfg[i], pllfracv[i]); + ret = stm32mp1_pll_config(i, pll_conf[i].cfg, pll_conf[i].frac); if (ret != 0) { return ret; } - if (pllcsg_set[i]) { - stm32mp1_pll_csg(i, pllcsg[i]); + if (pll_conf[i].csg_enabled) { + stm32mp1_pll_csg(i, pll_conf[i].csg); } stm32mp1_pll_start(i); } /* Wait and start PLLs output when ready */ for (i = (enum stm32mp1_pll_id)0; i < _PLL_NB; i++) { - if (!pllcfg_valid[i]) { + if (!pll_conf[i].status) { continue; } - ret = stm32mp1_pll_output(i, pllcfg[i][PLLCFG_O]); + ret = stm32mp1_pll_output(i, pll_conf[i].cfg[PLLCFG_O]); if (ret != 0) { return ret; } @@ -2065,69 +2358,32 @@ int stm32mp1_clk_init(void) stm32mp1_lse_wait(); } - /* Configure with expected clock source */ - ret = stm32mp1_set_clksrc(clksrc[CLKSRC_MPU]); - if (ret != 0) { - return ret; + if (pll4_bootrom) { + usbreg_bootrom = mmio_read_32(priv->base + RCC_USBCKSELR); } - ret = stm32mp1_set_clksrc(clksrc[CLKSRC_AXI]); - if (ret != 0) { - return ret; - } - ret = stm32mp1_set_clksrc(clksrc[CLKSRC_MCU]); + + /* Configure with expected clock source */ + ret = stm32_clk_source_configure(priv); if (ret != 0) { - return ret; + panic(); } - stm32mp1_set_rtcsrc(clksrc[CLKSRC_RTC], lse_css); - - /* Configure PKCK */ - pkcs_cell = fdt_rcc_read_prop("st,pkcs", &len); - if (pkcs_cell != NULL) { - bool ckper_disabled = false; - uint32_t j; - uint32_t usbreg_bootrom = 0U; - - if (pll4_bootrom) { - usbreg_bootrom = mmio_read_32(rcc_base + RCC_USBCKSELR); - } - - for (j = 0; j < ((uint32_t)len / sizeof(uint32_t)); j++) { - uint32_t pkcs = fdt32_to_cpu(pkcs_cell[j]); - if (pkcs == (uint32_t)CLK_CKPER_DISABLED) { - ckper_disabled = true; - continue; - } - stm32mp1_pkcs_config(pkcs); - } + if (pll4_bootrom) { + uint32_t usbreg_value, usbreg_mask; + const struct stm32mp1_clk_sel *sel; - /* - * CKPER is source for some peripheral clocks - * (FMC-NAND / QPSI-NOR) and switching source is allowed - * only if previous clock is still ON - * => deactivated CKPER only after switching clock - */ - if (ckper_disabled) { - stm32mp1_pkcs_config(CLK_CKPER_DISABLED); - } + sel = clk_sel_ref(_USBPHY_SEL); + usbreg_mask = (uint32_t)sel->msk << sel->src; + sel = clk_sel_ref(_USBO_SEL); + usbreg_mask |= (uint32_t)sel->msk << sel->src; - if (pll4_bootrom) { - uint32_t usbreg_value, usbreg_mask; - const struct stm32mp1_clk_sel *sel; - - sel = clk_sel_ref(_USBPHY_SEL); - usbreg_mask = (uint32_t)sel->msk << sel->src; - sel = clk_sel_ref(_USBO_SEL); - usbreg_mask |= (uint32_t)sel->msk << sel->src; - - usbreg_value = mmio_read_32(rcc_base + RCC_USBCKSELR) & - usbreg_mask; - usbreg_bootrom &= usbreg_mask; - if (usbreg_bootrom != usbreg_value) { - VERBOSE("forbidden new USB clk path\n"); - VERBOSE("vs bootrom on USB boot\n"); - return -FDT_ERR_BADVALUE; - } + usbreg_value = mmio_read_32(priv->base + RCC_USBCKSELR) & + usbreg_mask; + usbreg_bootrom &= usbreg_mask; + if (usbreg_bootrom != usbreg_value) { + EARLY_ERROR("forbidden new USB clk path\n"); + EARLY_ERROR("vs bootrom on USB boot\n"); + return -FDT_ERR_BADVALUE; } } @@ -2139,7 +2395,7 @@ int stm32mp1_clk_init(void) stm32mp_stgen_config(stm32mp_clk_get_rate(STGEN_K)); /* Software Self-Refresh mode (SSR) during DDR initilialization */ - mmio_clrsetbits_32(rcc_base + RCC_DDRITFCR, + mmio_clrsetbits_32(priv->base + RCC_DDRITFCR, RCC_DDRITFCR_DDRCKMOD_MASK, RCC_DDRITFCR_DDRCKMOD_SSR << RCC_DDRITFCR_DDRCKMOD_SHIFT); @@ -2326,6 +2582,17 @@ void stm32mp1_register_clock_parents_secure(unsigned long clock_id) } #endif /* STM32MP_SHARED_RESOURCES */ +void stm32mp1_clk_mcuss_protect(bool enable) +{ + uintptr_t rcc_base = stm32mp_rcc_base(); + + if (enable) { + mmio_setbits_32(rcc_base + RCC_TZCR, RCC_TZCR_MCKPROT); + } else { + mmio_clrbits_32(rcc_base + RCC_TZCR, RCC_TZCR_MCKPROT); + } +} + static void sync_earlyboot_clocks_state(void) { unsigned int idx; @@ -2355,8 +2622,199 @@ static const struct clk_ops stm32mp_clk_ops = { .get_parent = stm32mp1_clk_get_parent, }; +struct stm32_pll_dt_cfg mp15_pll[_PLL_NB]; +uint32_t mp15_clksrc[MUX_NB]; +uint32_t mp15_clkdiv[DIV_NB]; + +struct stm32_clk_platdata stm32mp15_clock_pdata = { + .pll = mp15_pll, + .npll = _PLL_NB, + .clksrc = mp15_clksrc, + .nclksrc = MUX_NB, + .clkdiv = mp15_clkdiv, + .nclkdiv = DIV_NB, +}; + +static struct stm32_clk_priv stm32mp15_clock_data = { + .base = RCC_BASE, + .parents = parent_mp15, + .nb_parents = ARRAY_SIZE(parent_mp15), + .div = dividers_mp15, + .nb_div = ARRAY_SIZE(dividers_mp15), + .pdata = &stm32mp15_clock_pdata, +}; + +static int stm32_clk_parse_fdt_by_name(void *fdt, int node, const char *name, + uint32_t *tab, uint32_t *nb) +{ + const fdt32_t *cell; + int len = 0; + uint32_t i; + + cell = fdt_getprop(fdt, node, name, &len); + if (cell == NULL) { + *nb = 0U; + return 0; + } + + for (i = 0U; i < ((uint32_t)len / sizeof(uint32_t)); i++) { + tab[i] = fdt32_to_cpu(cell[i]); + } + + *nb = (uint32_t)len / sizeof(uint32_t); + + return 0; +} + +#define RCC_PLL_NAME_SIZE 12 + +static int clk_stm32_load_vco_config(void *fdt, int subnode, struct stm32_pll_dt_cfg *pll) +{ + int err; + + err = fdt_read_uint32_array(fdt, subnode, "divmn", (int)PLL_DIV_MN_NB, &pll->cfg[PLLCFG_M]); + if (err != 0) { + return err; + } + + err = fdt_read_uint32_array(fdt, subnode, "csg", (int)PLLCSG_NB, pll->csg); + if (err == 0) { + pll->csg_enabled = true; + } else if (err == -FDT_ERR_NOTFOUND) { + pll->csg_enabled = false; + } else { + return err; + } + + pll->status = true; + + pll->frac = fdt_read_uint32_default(fdt, subnode, "frac", 0); + + pll->src = fdt_read_uint32_default(fdt, subnode, "src", UINT32_MAX); + + return 0; +} + +static int clk_stm32_load_output_config(void *fdt, int subnode, struct stm32_pll_dt_cfg *pll) +{ + int err; + + err = fdt_read_uint32_array(fdt, subnode, "st,pll_div_pqr", (int)PLL_DIV_PQR_NB, + &pll->cfg[PLLCFG_P]); + if (err != 0) { + return err; + } + + pll->cfg[PLLCFG_O] = PQR(1, 1, 1); + + return 0; +} + +static int clk_stm32_parse_pll_fdt(void *fdt, int subnode, struct stm32_pll_dt_cfg *pll) +{ + const fdt32_t *cuint; + int subnode_pll; + int subnode_vco; + int err; + + cuint = fdt_getprop(fdt, subnode, "st,pll", NULL); + if (cuint == NULL) { + /* Case of no pll is defined */ + return 0; + } + + subnode_pll = fdt_node_offset_by_phandle(fdt, fdt32_to_cpu(*cuint)); + if (subnode_pll < 0) { + return -FDT_ERR_NOTFOUND; + } + + cuint = fdt_getprop(fdt, subnode_pll, "st,pll_vco", NULL); + if (cuint == NULL) { + return -FDT_ERR_NOTFOUND; + } + + subnode_vco = fdt_node_offset_by_phandle(fdt, fdt32_to_cpu(*cuint)); + if (subnode_vco < 0) { + return -FDT_ERR_NOTFOUND; + } + + err = clk_stm32_load_vco_config(fdt, subnode_vco, pll); + if (err != 0) { + return err; + } + + err = clk_stm32_load_output_config(fdt, subnode_pll, pll); + if (err != 0) { + return err; + } + + return 0; +} + +static int stm32_clk_parse_fdt_all_pll(void *fdt, int node, struct stm32_clk_platdata *pdata) +{ + size_t i = 0U; + + for (i = _PLL1; i < pdata->npll; i++) { + struct stm32_pll_dt_cfg *pll = pdata->pll + i; + char name[RCC_PLL_NAME_SIZE]; + int subnode; + int err; + + snprintf(name, sizeof(name), "st,pll@%u", i); + + subnode = fdt_subnode_offset(fdt, node, name); + if (!fdt_check_node(subnode)) { + continue; + } + + err = clk_stm32_parse_pll_fdt(fdt, subnode, pll); + if (err != 0) { + panic(); + } + } + + return 0; +} + +static int stm32_clk_parse_fdt(struct stm32_clk_platdata *pdata) +{ + void *fdt = NULL; + int node; + uint32_t err; + + if (fdt_get_address(&fdt) == 0) { + return -ENOENT; + } + + node = fdt_node_offset_by_compatible(fdt, -1, DT_RCC_CLK_COMPAT); + if (node < 0) { + panic(); + } + + err = stm32_clk_parse_fdt_all_pll(fdt, node, pdata); + if (err != 0) { + return err; + } + + err = stm32_clk_parse_fdt_by_name(fdt, node, "st,clkdiv", pdata->clkdiv, &pdata->nclkdiv); + if (err != 0) { + return err; + } + + err = stm32_clk_parse_fdt_by_name(fdt, node, "st,clksrc", pdata->clksrc, &pdata->nclksrc); + if (err != 0) { + return err; + } + + return 0; +} + int stm32mp1_clk_probe(void) { + uintptr_t base = RCC_BASE; + int ret; + #if defined(IMAGE_BL32) if (!fdt_get_rcc_secure_state()) { mmio_write_32(stm32mp_rcc_base() + RCC_TZCR, 0U); @@ -2365,6 +2823,16 @@ int stm32mp1_clk_probe(void) stm32mp1_osc_init(); + ret = stm32_clk_parse_fdt(&stm32mp15_clock_pdata); + if (ret != 0) { + return ret; + } + + ret = clk_stm32_init(&stm32mp15_clock_data, base); + if (ret != 0) { + return ret; + } + sync_earlyboot_clocks_state(); clk_register(&stm32mp_clk_ops); diff --git a/drivers/st/clk/stm32mp_clkfunc.c b/drivers/st/clk/stm32mp_clkfunc.c index 379547fd..69922fd2 100644 --- a/drivers/st/clk/stm32mp_clkfunc.c +++ b/drivers/st/clk/stm32mp_clkfunc.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2023, STMicroelectronics - All Rights Reserved + * Copyright (c) 2017-2024, STMicroelectronics - All Rights Reserved * * SPDX-License-Identifier: BSD-3-Clause */ @@ -334,6 +334,19 @@ static void stgen_set_counter(unsigned long long counter) #endif } +/******************************************************************************* + * This function returns the STGEN counter value. + ******************************************************************************/ +static unsigned long long stm32mp_stgen_get_counter(void) +{ +#ifdef __aarch64__ + return mmio_read_64(STGEN_BASE + CNTCV_OFF); +#else + return (((unsigned long long)mmio_read_32(STGEN_BASE + CNTCVU_OFF) << 32) | + mmio_read_32(STGEN_BASE + CNTCVL_OFF)); +#endif +} + /******************************************************************************* * This function configures and restores the STGEN counter depending on the * connected clock. @@ -350,9 +363,11 @@ void stm32mp_stgen_config(unsigned long rate) } mmio_clrbits_32(STGEN_BASE + CNTCR_OFF, CNTCR_EN); - counter = stm32mp_stgen_get_counter() * rate / cntfid0; - stgen_set_counter(counter); + if (cntfid0 != 0U) { + counter = stm32mp_stgen_get_counter() * rate / cntfid0; + stgen_set_counter(counter); + } mmio_write_32(STGEN_BASE + CNTFID_OFF, rate); mmio_setbits_32(STGEN_BASE + CNTCR_OFF, CNTCR_EN); @@ -363,32 +378,13 @@ void stm32mp_stgen_config(unsigned long rate) } /******************************************************************************* - * This function returns the STGEN counter value. + * This function restores CPU generic timer rate from the STGEN clock rate. ******************************************************************************/ -unsigned long long stm32mp_stgen_get_counter(void) +void stm32mp_stgen_restore_rate(void) { -#ifdef __aarch64__ - return mmio_read_64(STGEN_BASE + CNTCV_OFF); -#else - return (((unsigned long long)mmio_read_32(STGEN_BASE + CNTCVU_OFF) << 32) | - mmio_read_32(STGEN_BASE + CNTCVL_OFF)); -#endif -} + unsigned long rate; -/******************************************************************************* - * This function restores the STGEN counter value. - * It takes a first input value as a counter backup value to be restored and a - * offset in ms to be added. - ******************************************************************************/ -void stm32mp_stgen_restore_counter(unsigned long long value, - unsigned long long offset_in_ms) -{ - unsigned long long cnt; + rate = mmio_read_32(STGEN_BASE + CNTFID_OFF); - cnt = value + ((offset_in_ms * - mmio_read_32(STGEN_BASE + CNTFID_OFF)) / 1000U); - - mmio_clrbits_32(STGEN_BASE + CNTCR_OFF, CNTCR_EN); - stgen_set_counter(cnt); - mmio_setbits_32(STGEN_BASE + CNTCR_OFF, CNTCR_EN); + write_cntfrq_el0((u_register_t)rate); } diff --git a/drivers/st/crypto/stm32_hash.c b/drivers/st/crypto/stm32_hash.c index e92f9800..bd493245 100644 --- a/drivers/st/crypto/stm32_hash.c +++ b/drivers/st/crypto/stm32_hash.c @@ -10,6 +10,7 @@ #include <arch_helpers.h> #include <common/debug.h> +#include <common/sha_common_macros.h> #include <drivers/clk.h> #include <drivers/delay_timer.h> #include <drivers/st/stm32_hash.h> @@ -62,15 +63,6 @@ #define HASH_STR_NBLW_MASK GENMASK(4, 0) #define HASH_STR_DCAL BIT(8) -#define MD5_DIGEST_SIZE 16U -#define SHA1_DIGEST_SIZE 20U -#define SHA224_DIGEST_SIZE 28U -#define SHA256_DIGEST_SIZE 32U -#define SHA384_DIGEST_SIZE 48U -#define SHA512_224_DIGEST_SIZE 28U -#define SHA512_256_DIGEST_SIZE 32U -#define SHA512_DIGEST_SIZE 64U - #define RESET_TIMEOUT_US_1MS 1000U #define HASH_TIMEOUT_US 10000U diff --git a/drivers/st/ddr/phy/firmware/include/mnpmusrammsgblock_ddr3.h b/drivers/st/ddr/phy/firmware/include/mnpmusrammsgblock_ddr3.h new file mode 100644 index 00000000..936b73c0 --- /dev/null +++ b/drivers/st/ddr/phy/firmware/include/mnpmusrammsgblock_ddr3.h @@ -0,0 +1,935 @@ +/* + * Copyright (C) 2021-2024, STMicroelectronics - All Rights Reserved + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef MNPMUSRAMMSGBLOCK_DDR3_H +#define MNPMUSRAMMSGBLOCK_DDR3_H + +/* + * DDR3U_1D training firmware message block structure + * + * Please refer to the Training Firmware App Note for futher information about + * the usage for Message Block. + */ +struct pmu_smb_ddr_1d { + uint8_t reserved00; /* + * Byte offset 0x00, CSR Addr 0x54000, Direction=In + * reserved00[0:4] RFU, must be zero + * + * reserved00[5] = Train vrefDAC0 During Read Deskew + * 0x1 = Read Deskew will begin by enabling and roughly + * training the phy's per-lane reference voltages. + * Training the vrefDACs CSRs will increase the maximum 1D + * training time by around half a millisecond, but will + * improve 1D training accuracy on systems with + * significant voltage-offsets between lane read eyes. + * 0x0 = Read Deskew will assume the messageblock's + * phyVref setting will work for all lanes. + * + * reserved00[6] = Enable High Effort WrDQ1D + * 0x1 = WrDQ1D will conditionally retry training at + * several extra RxClkDly Timings. This will increase the + * maximum 1D training time by up to 4 extra iterations of + * WrDQ1D. This is only required in systems that suffer + * from very large, asymmetric eye-collapse when receiving + * PRBS patterns. + * 0x0 = WrDQ1D assume rxClkDly values found by SI + * Friendly RdDqs1D will work for receiving PRBS patterns + * + * reserved00[7] = Optimize for the special hard macros in + * TSMC28. + * 0x1 = set if the phy being trained was manufactured in + * any TSMC28 process node. + * 0x0 = otherwise, when not training a TSMC28 phy, leave + * this field as 0. + */ + uint8_t msgmisc; /* + * Byte offset 0x01, CSR Addr 0x54000, Direction=In + * Contains various global options for training. + * + * Bit fields: + * + * msgmisc[0] = MTESTEnable + * 0x1 = Pulse primary digital test output bump at the end + * of each major training stage. This enables observation + * of training stage completion by observing the digital + * test output. + * 0x0 = Do not pulse primary digital test output bump + * + * msgmisc[1] = SimulationOnlyReset + * 0x1 = Verilog only simulation option to shorten + * duration of DRAM reset pulse length to 1ns. + * Must never be set to 1 in silicon. + * 0x0 = Use reset pulse length specified by JEDEC + * standard. + * + * msgmisc[2] = SimulationOnlyTraining + * 0x1 = Verilog only simulation option to shorten the + * duration of the training steps by performing fewer + * iterations. + * Must never be set to 1 in silicon. + * 0x0 = Use standard training duration. + * + * msgmisc[3] = RFU, must be zero + * + * msgmisc[4] = Suppress streaming messages, including + * assertions, regardless of hdtctrl setting. + * Stage Completion messages, as well as training completion + * and error messages are still sent depending on hdtctrl + * setting. + * + * msgmisc[5] = PerByteMaxRdLat + * 0x1 = Each DBYTE will return dfi_rddata_valid at the + * lowest possible latency. This may result in unaligned + * data between bytes to be returned to the DFI. + * 0x0 = Every DBYTE will return dfi_rddata_valid + * simultaneously. This will ensure that data bytes will + * return aligned accesses to the DFI. + * + * msgmisc[6] = PartialRank (DDR3 UDIMM and DDR4 UDIMM only, + * otherwise RFU, must be zero) + * 0x1 = Support rank populated with a subset of byte, but + * where even-odd pair of rank support all the byte + * 0x0 = All rank populated with all the byte (tyical + * configuration) + * + * msgmisc[7] RFU, must be zero + * + * Notes: + * + * - SimulationOnlyReset and SimulationOnlyTraining can be + * used to speed up simulation run times, and must never + * be used in real silicon. Some VIPs may have checks on + * DRAM reset parameters that may need to be disabled when + * using SimulationOnlyReset. + */ + uint16_t pmurevision; /* + * Byte offset 0x02, CSR Addr 0x54001, Direction=Out + * PMU firmware revision ID + * After training is run, this address will contain the + * revision ID of the firmware. + * Please reference this revision ID when filing support + * cases. + */ + uint8_t pstate; /* + * Byte offset 0x04, CSR Addr 0x54002, Direction=In + * Must be set to the target pstate to be trained + * 0x0 = pstate 0 + * 0x1 = pstate 1 + * 0x2 = pstate 2 + * 0x3 = pstate 3 + * All other encodings are reserved + */ + uint8_t pllbypassen; /* + * Byte offset 0x05, CSR Addr 0x54002, Direction=In + * Set according to whether target pstate uses PHY PLL + * bypass + * 0x0 = PHY PLL is enabled for target pstate + * 0x1 = PHY PLL is bypassed for target pstate + */ + uint16_t dramfreq; /* + * Byte offset 0x06, CSR Addr 0x54003, Direction=In + * DDR data rate for the target pstate in units of MT/s. + * For example enter 0x0640 for DDR1600. + */ + uint8_t dfifreqratio; /* + * Byte offset 0x08, CSR Addr 0x54004, Direction=In + * Frequency ratio betwen DfiCtlClk and SDRAM memclk. + * 0x1 = 1:1 + * 0x2 = 1:2 + * 0x4 = 1:4 + */ + uint8_t bpznresval; /* + * Byte offset 0x09, CSR Addr 0x54004, Direction=In + * Overwrite the value of precision resistor connected to + * Phy BP_ZN + * 0x00 = Do not program. Use current CSR value. + * 0xf0 = 240 Ohm + * 0x78 = 120 Ohm + * 0x28 = 40 Ohm + * All other values are reserved. + * It is recommended to set this to 0x00. + */ + uint8_t phyodtimpedance; /* + * Byte offset 0x0a, CSR Addr 0x54005, Direction=In + * Must be programmed to the termination impedance in ohms + * used by PHY during reads. + * + * 0x0 = Firmware skips programming (must be manually + * programmed by user prior to training start) + * + * See PHY databook for legal termination impedance values. + * + * For digital simulation, any legal value can be used. For + * silicon, the users must determine the correct value + * through SI simulation or other methods. + */ + uint8_t phydrvimpedance; /* + * Byte offset 0x0b, CSR Addr 0x54005, Direction=In + * Must be programmed to the driver impedance in ohms used + * by PHY during writes for all DBYTE drivers + * (DQ/DM/DBI/DQS). + * + * 0x0 = Firmware skips programming (must be manually + * programmed by user prior to training start) + * + * See PHY databook for legal R_on driver impedance values. + * + * For digital simulation, any value can be used that is not + * Hi-Z. For silicon, the users must determine the correct + * value through SI simulation or other methods. + */ + uint8_t phyvref; /* + * Byte offset 0x0c, CSR Addr 0x54006, Direction=In + * Must be programmed with the Vref level to be used by the + * PHY during reads + * + * The units of this field are a percentage of VDDQ + * according to the following equation: + * + * Receiver Vref = VDDQ*phyvref[6:0]/128 + * + * For example to set Vref at 0.75*VDDQ, set this field to + * 0x60. + * + * For digital simulation, any legal value can be used. For + * silicon, the users must calculate the analytical Vref by + * using the impedances, terminations, and series resistance + * present in the system. + */ + uint8_t dramtype; /* + * Byte offset 0x0d, CSR Addr 0x54006, Direction=In + * Module Type: + * 0x01 = DDR3 unbuffered + * 0x02 = Reserved + * 0x03 = Reserved + * 0x04 = Reserved + * 0x05 = Reserved + */ + uint8_t disableddbyte; /* + * Byte offset 0x0e, CSR Addr 0x54007, Direction=In + * Bitmap to indicate which Dbyte are not connected (for + * DByte 0 to 7): + * Set disableddbyte[i] to 1 only to specify that DByte is + * not need to be trained (DByte 8 can be disabled via + * enableddqs setting) + */ + uint8_t enableddqs; /* + * Byte offset 0x0f, CSR Addr 0x54007, Direction=In + * Total number of DQ bits enabled in PHY + */ + uint8_t cspresent; /* + * Byte offset 0x10, CSR Addr 0x54008, Direction=In + * Indicates presence of DRAM at each chip select for PHY. + * Each bit corresponds to a logical CS. + * + * If the bit is set to 1, the CS is connected to DRAM. + * If the bit is set to 0, the CS is not connected to DRAM. + * + * cspresent[0] = CS0 is populated with DRAM + * cspresent[1] = CS1 is populated with DRAM + * cspresent[2] = CS2 is populated with DRAM + * cspresent[3] = CS3 is populated with DRAM + * cspresent[7:4] = Reserved (must be programmed to 0) + */ + uint8_t cspresentd0; /* + * Byte offset 0x11, CSR Addr 0x54008, Direction=In + * The CS signals from field cspresent that are routed to + * DIMM connector 0 + */ + uint8_t cspresentd1; /* + * Byte offset 0x12, CSR Addr 0x54009, Direction=In + * The CS signals from field cspresent that are routed to + * DIMM connector 1 + */ + uint8_t addrmirror; /* + * Byte offset 0x13, CSR Addr 0x54009, Direction=In + * Corresponds to CS[3:0] + * 1 = Address Mirror. + * 0 = No Address Mirror. + */ + uint8_t cstestfail; /* + * Byte offset 0x14, CSR Addr 0x5400a, Direction=Out + * This field will be set if training fails on any rank. + * 0x0 = No failures + * non-zero = one or more ranks failed training + */ + uint8_t phycfg; /* + * Byte offset 0x15, CSR Addr 0x5400a, Direction=In + * Additional mode bits. + * + * Bit fields: + * [0] SlowAccessMode: + * 1 = 2T Address Timing. + * 0 = 1T Address Timing. + * [7-1] RFU, must be zero + * + * WARNING: In case of DDR4 Geardown Mode (mr3[A3] == 1), + * phycfg[0] must be 0. + */ + uint16_t sequencectrl; /* + * Byte offset 0x16, CSR Addr 0x5400b, Direction=In + * Controls the training steps to be run. Each bit + * corresponds to a training step. + * + * If the bit is set to 1, the training step will run. + * If the bit is set to 0, the training step will be + * skipped. + * + * Training step to bit mapping: + * sequencectrl[0] = Run DevInit - Device/phy + * initialization. Should always be set. + * sequencectrl[1] = Run WrLvl - Write leveling + * sequencectrl[2] = Run RxEn - Read gate training + * sequencectrl[3] = Run RdDQS1D - 1d read dqs training + * sequencectrl[4] = Run WrDQ1D - 1d write dq training + * sequencectrl[5] = RFU, must be zero + * sequencectrl[6] = RFU, must be zero + * sequencectrl[7] = RFU, must be zero + * sequencectrl[8] = Run RdDeskew - Per lane read dq deskew + * training + * sequencectrl[9] = Run MxRdLat - Max read latency training + * sequencectrl[10] = RFU, must be zero + * sequencectrl[11] = RFU, must be zero + * sequencectrl[12] = RFU, must be zero + * sequencectrl[13] = RFU, must be zero + * sequencectrl[15-14] = RFU, must be zero + */ + uint8_t hdtctrl; /* + * Byte offset 0x18, CSR Addr 0x5400c, Direction=In + * To control the total number of debug messages, a + * verbosity subfield (hdtctrl, Hardware Debug Trace + * Control) exists in the message block. Every message has a + * verbosity level associated with it, and as the hdtctrl + * value is increased, less important s messages stop being + * sent through the mailboxes. The meanings of several major + * hdtctrl thresholds are explained below: + * + * 0x04 = Maximal debug messages (e.g., Eye contours) + * 0x05 = Detailed debug messages (e.g. Eye delays) + * 0x0A = Coarse debug messages (e.g. rank information) + * 0xC8 = Stage completion + * 0xC9 = Assertion messages + * 0xFF = Firmware completion messages only + */ + uint8_t reserved19; /* Byte offset 0x19, CSR Addr 0x5400c, Direction=N/A */ + uint8_t reserved1a; /* Byte offset 0x1a, CSR Addr 0x5400d, Direction=N/A */ + uint8_t share2dvrefresult; /* + * Byte offset 0x1b, CSR Addr 0x5400d, Direction=In + * Bitmap that designates the phy's vref source for every + * pstate + * If share2dvrefresult[x] = 0, then after 2D training, + * pstate x will continue using the phyVref provided in + * pstate x's 1D messageblock. + * If share2dvrefresult[x] = 1, then after 2D training, + * pstate x will use the per-lane VrefDAC0/1 CSRs trained by + * 2d training. + */ + uint8_t reserved1c; /* Byte offset 0x1c, CSR Addr 0x5400e, Direction=N/A */ + uint8_t reserved1d; /* Byte offset 0x1d, CSR Addr 0x5400e, Direction=N/A */ + uint8_t reserved1e; /* + * Byte offset 0x1e, CSR Addr 0x5400f, Direction=In + * Input for constraining the range of vref(DQ) values + * training will collect data for, usually reducing training + * time. However, too large of a voltage range may cause + * longer 2D training times while too small of a voltage + * range may truncate passing regions. When in doubt, leave + * this field set to 0. + * Used by 2D training in: Rd2D, Wr2D + * + * reserved1E[0-3]: Rd2D Voltage Range + * 0 = Training will search all phy vref(DQ) settings + * 1 = limit to +/-2 %VDDQ from phyVref + * 2 = limit to +/-4 %VDDQ from phyVref + * . . . + * 15 = limit to +/-30% VDDQ from phyVref + * + * reserved1E[4-7]: Wr2D Voltage Range + * 0 = Training will search all dram vref(DQ) settings + * 1 = limit to +/-2 %VDDQ from mr6 + * 2 = limit to +/-4 %VDDQ from mr6 + * . . . + * 15 = limit to +/-30% VDDQ from mr6 + */ + uint8_t reserved1f; /* + * Byte offset 0x1f, CSR Addr 0x5400f, Direction=In + * Extended training option: + * + * reserved1F[1:0]: Configured RxClkDly offset try during + * WrDq1D high-effort (i.e., when reserved00[6] is set) + * 0: -8, +8, -16, +16 + * 1: -4, +4, -8, +8, -12, +12, -16, +16 + * 2: -2, +2, -4, +4, -6, +6, -8, +8 + * 3: -2, +2, -4, +4, -6, +6, -8, +8, -10, +10, -12, +12, + * -14, +14, -16, +16 + * + * reserved1F[2]: When set, execute again WrDq1D after + * RdDqs1D PRBS + * + * reserved1F[3]: When set redo RdDeskew with PRBS after + * (first) WrDqs1D + * + * reserved1F[7:4]: This field is reserved and must be + * programmed to 0x00. + */ + uint8_t reserved20; /* + * Byte offset 0x20, CSR Addr 0x54010, Direction=In + * This field is reserved and must be programmed to 0x00, + * excepted for Reserved: + * Reserved MREP assume raising edge is found when + * reserved20[3:0]+3 consecutive 1 are received during MREP + * fine delay swept; reserved20[6:0] thus permits to + * increase tolerance for noisy system. And if reserved20[7] + * is set, MREP training is failing if no raising edge is + * found (otherwise the raising edge is assume close to + * delay 0). + */ + uint8_t reserved21; /* + * Byte offset 0x21, CSR Addr 0x54010, Direction=In + * This field is reserved and must be programmed to 0x00, + * excepted for Reserved: + * Reserved DWL assume raising edge is found when + * reserved21[3:0]+3 consecutive 1 are received during DWL + * fine delay swept; reserved21[6:0] thus permits to + * increase tolerance for noisy system. And if reserved21[7] + * is set, DWL training is failing if no raising edge is + * found (otherwise the raising edge is assume close to + * delay 0). + */ + uint16_t phyconfigoverride; /* + * Byte offset 0x22, CSR Addr 0x54011, Direction=In + * Override PhyConfig csr. + * 0x0: Use hardware csr value for PhyConfing + * (recommended) + * Other values: Use value for PhyConfig instead of + * Hardware value. + */ + uint8_t dfimrlmargin; /* + * Byte offset 0x24, CSR Addr 0x54012, Direction=In + * Margin added to smallest passing trained DFI Max Read + * Latency value, in units of DFI clocks. Recommended to be + * >= 1. + */ + int8_t cdd_rr_3_2; /* + * Byte offset 0x25, CSR Addr 0x54012, Direction=Out + * This is a signed integer value. + * Read to read critical delay difference from cs 3 to cs 2. + */ + int8_t cdd_rr_3_1; /* + * Byte offset 0x26, CSR Addr 0x54013, Direction=Out + * This is a signed integer value. + * Read to read critical delay difference from cs 3 to cs 1. + */ + int8_t cdd_rr_3_0; /* + * Byte offset 0x27, CSR Addr 0x54013, Direction=Out + * This is a signed integer value. + * Read to read critical delay difference from cs 3 to cs 0. + */ + int8_t cdd_rr_2_3; /* + * Byte offset 0x28, CSR Addr 0x54014, Direction=Out + * This is a signed integer value. + * Read to read critical delay difference from cs 2 to cs 3. + */ + int8_t cdd_rr_2_1; /* + * Byte offset 0x29, CSR Addr 0x54014, Direction=Out + * This is a signed integer value. + * Read to read critical delay difference from cs 2 to cs 1. + */ + int8_t cdd_rr_2_0; /* + * Byte offset 0x2a, CSR Addr 0x54015, Direction=Out + * This is a signed integer value. + * Read to read critical delay difference from cs 2 to cs 0. + */ + int8_t cdd_rr_1_3; /* + * Byte offset 0x2b, CSR Addr 0x54015, Direction=Out + * This is a signed integer value. + * Read to read critical delay difference from cs 1 to cs 3. + */ + int8_t cdd_rr_1_2; /* + * Byte offset 0x2c, CSR Addr 0x54016, Direction=Out + * This is a signed integer value. + * Read to read critical delay difference from cs 1 to cs 2. + */ + int8_t cdd_rr_1_0; /* + * Byte offset 0x2d, CSR Addr 0x54016, Direction=Out + * This is a signed integer value. + * Read to read critical delay difference from cs 1 to cs 0. + */ + int8_t cdd_rr_0_3; /* + * Byte offset 0x2e, CSR Addr 0x54017, Direction=Out + * This is a signed integer value. + * Read to read critical delay difference from cs 0 to cs 3. + */ + int8_t cdd_rr_0_2; /* + * Byte offset 0x2f, CSR Addr 0x54017, Direction=Out + * This is a signed integer value. + * Read to read critical delay difference from cs 0 to cs 2. + */ + int8_t cdd_rr_0_1; /* + * Byte offset 0x30, CSR Addr 0x54018, Direction=Out + * This is a signed integer value. + * Read to read critical delay difference from cs 0 to cs 1. + */ + int8_t cdd_ww_3_2; /* + * Byte offset 0x31, CSR Addr 0x54018, Direction=Out + * This is a signed integer value. + * Write to write critical delay difference from cs 3 to cs + * 2. + */ + int8_t cdd_ww_3_1; /* + * Byte offset 0x32, CSR Addr 0x54019, Direction=Out + * This is a signed integer value. + * Write to write critical delay difference from cs 3 to cs + * 1. + */ + int8_t cdd_ww_3_0; /* + * Byte offset 0x33, CSR Addr 0x54019, Direction=Out + * This is a signed integer value. + * Write to write critical delay difference from cs 3 to cs + * 0. + */ + int8_t cdd_ww_2_3; /* + * Byte offset 0x34, CSR Addr 0x5401a, Direction=Out + * This is a signed integer value. + * Write to write critical delay difference from cs 2 to cs + * 3. + */ + int8_t cdd_ww_2_1; /* + * Byte offset 0x35, CSR Addr 0x5401a, Direction=Out + * This is a signed integer value. + * Write to write critical delay difference from cs 2 to cs + * 1. + */ + int8_t cdd_ww_2_0; /* + * Byte offset 0x36, CSR Addr 0x5401b, Direction=Out + * This is a signed integer value. + * Write to write critical delay difference from cs 2 to cs + * 0. + */ + int8_t cdd_ww_1_3; /* + * Byte offset 0x37, CSR Addr 0x5401b, Direction=Out + * This is a signed integer value. + * Write to write critical delay difference from cs 1 to cs + * 3. + */ + int8_t cdd_ww_1_2; /* + * Byte offset 0x38, CSR Addr 0x5401c, Direction=Out + * This is a signed integer value. + * Write to write critical delay difference from cs 1 to cs + * 2. + */ + int8_t cdd_ww_1_0; /* + * Byte offset 0x39, CSR Addr 0x5401c, Direction=Out + * This is a signed integer value. + * Write to write critical delay difference from cs 1 to cs + * 0. + */ + int8_t cdd_ww_0_3; /* + * Byte offset 0x3a, CSR Addr 0x5401d, Direction=Out + * This is a signed integer value. + * Write to write critical delay difference from cs 0 to cs + * 3. + */ + int8_t cdd_ww_0_2; /* + * Byte offset 0x3b, CSR Addr 0x5401d, Direction=Out + * This is a signed integer value. + * Write to write critical delay difference from cs 0 to cs + * 2. + */ + int8_t cdd_ww_0_1; /* + * Byte offset 0x3c, CSR Addr 0x5401e, Direction=Out + * This is a signed integer value. + * Write to write critical delay difference from cs 0 to cs + * 1. + */ + int8_t cdd_rw_3_3; /* + * Byte offset 0x3d, CSR Addr 0x5401e, Direction=Out + * This is a signed integer value. + * Read to write critical delay difference from cs 3 to + * cs 3. + */ + int8_t cdd_rw_3_2; /* + * Byte offset 0x3e, CSR Addr 0x5401f, Direction=Out + * This is a signed integer value. + * Read to write critical delay difference from cs 3 to + * cs 2. + */ + int8_t cdd_rw_3_1; /* + * Byte offset 0x3f, CSR Addr 0x5401f, Direction=Out + * This is a signed integer value. + * Read to write critical delay difference from cs 3 to + * cs 1. + */ + int8_t cdd_rw_3_0; /* + * Byte offset 0x40, CSR Addr 0x54020, Direction=Out + * This is a signed integer value. + * Read to write critical delay difference from cs 3 to + * cs 0. + */ + int8_t cdd_rw_2_3; /* + * Byte offset 0x41, CSR Addr 0x54020, Direction=Out + * This is a signed integer value. + * Read to write critical delay difference from cs 2 to + * cs 3. + */ + int8_t cdd_rw_2_2; /* + * Byte offset 0x42, CSR Addr 0x54021, Direction=Out + * This is a signed integer value. + * Read to write critical delay difference from cs 2 to + * cs 2. + */ + int8_t cdd_rw_2_1; /* + * Byte offset 0x43, CSR Addr 0x54021, Direction=Out + * This is a signed integer value. + * Read to write critical delay difference from cs 2 to + * cs 1. + */ + int8_t cdd_rw_2_0; /* + * Byte offset 0x44, CSR Addr 0x54022, Direction=Out + * This is a signed integer value. + * Read to write critical delay difference from cs 2 to + * cs 0. + */ + int8_t cdd_rw_1_3; /* + * Byte offset 0x45, CSR Addr 0x54022, Direction=Out + * This is a signed integer value. + * Read to write critical delay difference from cs 1 to + * cs 3. + */ + int8_t cdd_rw_1_2; /* + * Byte offset 0x46, CSR Addr 0x54023, Direction=Out + * This is a signed integer value. + * Read to write critical delay difference from cs 1 to + * cs 2. + */ + int8_t cdd_rw_1_1; /* + * Byte offset 0x47, CSR Addr 0x54023, Direction=Out + * This is a signed integer value. + * Read to write critical delay difference from cs 1 to + * cs 1. + */ + int8_t cdd_rw_1_0; /* + * Byte offset 0x48, CSR Addr 0x54024, Direction=Out + * This is a signed integer value. + * Read to write critical delay difference from cs 1 to + * cs 0. + */ + int8_t cdd_rw_0_3; /* + * Byte offset 0x49, CSR Addr 0x54024, Direction=Out + * This is a signed integer value. + * Read to write critical delay difference from cs 0 to + * cs 3. + */ + int8_t cdd_rw_0_2; /* + * Byte offset 0x4a, CSR Addr 0x54025, Direction=Out + * This is a signed integer value. + * Read to write critical delay difference from cs 0 to + * cs 2. + */ + int8_t cdd_rw_0_1; /* + * Byte offset 0x4b, CSR Addr 0x54025, Direction=Out + * This is a signed integer value. + * Read to write critical delay difference from cs 0 to + * cs 1. + */ + int8_t cdd_rw_0_0; /* + * Byte offset 0x4c, CSR Addr 0x54026, Direction=Out + * This is a signed integer value. + * Read to write critical delay difference from cs 0 to + * cs 0. + */ + int8_t cdd_wr_3_3; /* + * Byte offset 0x4d, CSR Addr 0x54026, Direction=Out + * This is a signed integer value. + * Write to read critical delay difference from cs 3 to + * cs 3. + */ + int8_t cdd_wr_3_2; /* + * Byte offset 0x4e, CSR Addr 0x54027, Direction=Out + * This is a signed integer value. + * Write to read critical delay difference from cs 3 to + * cs 2. + */ + int8_t cdd_wr_3_1; /* + * Byte offset 0x4f, CSR Addr 0x54027, Direction=Out + * This is a signed integer value. + * Write to read critical delay difference from cs 3 to + * cs 1. + */ + int8_t cdd_wr_3_0; /* + * Byte offset 0x50, CSR Addr 0x54028, Direction=Out + * This is a signed integer value. + * Write to read critical delay difference from cs 3 to + * cs 0. + */ + int8_t cdd_wr_2_3; /* + * Byte offset 0x51, CSR Addr 0x54028, Direction=Out + * This is a signed integer value. + * Write to read critical delay difference from cs 2 to + * cs 3. + */ + int8_t cdd_wr_2_2; /* + * Byte offset 0x52, CSR Addr 0x54029, Direction=Out + * This is a signed integer value. + * Write to read critical delay difference from cs 2 to + * cs 2. + */ + int8_t cdd_wr_2_1; /* + * Byte offset 0x53, CSR Addr 0x54029, Direction=Out + * This is a signed integer value. + * Write to read critical delay difference from cs 2 to + * cs 1. + */ + int8_t cdd_wr_2_0; /* + * Byte offset 0x54, CSR Addr 0x5402a, Direction=Out + * This is a signed integer value. + * Write to read critical delay difference from cs 2 to + * cs 0. + */ + int8_t cdd_wr_1_3; /* + * Byte offset 0x55, CSR Addr 0x5402a, Direction=Out + * This is a signed integer value. + * Write to read critical delay difference from cs 1 to + * cs 3. + */ + int8_t cdd_wr_1_2; /* + * Byte offset 0x56, CSR Addr 0x5402b, Direction=Out + * This is a signed integer value. + * Write to read critical delay difference from cs 1 to + * cs 2. + */ + int8_t cdd_wr_1_1; /* + * Byte offset 0x57, CSR Addr 0x5402b, Direction=Out + * This is a signed integer value. + * Write to read critical delay difference from cs 1 to + * cs 1. + */ + int8_t cdd_wr_1_0; /* + * Byte offset 0x58, CSR Addr 0x5402c, Direction=Out + * This is a signed integer value. + * Write to read critical delay difference from cs 1 to + * cs 0. + */ + int8_t cdd_wr_0_3; /* + * Byte offset 0x59, CSR Addr 0x5402c, Direction=Out + * This is a signed integer value. + * Write to read critical delay difference from cs 0 to + * cs 3. + */ + int8_t cdd_wr_0_2; /* + * Byte offset 0x5a, CSR Addr 0x5402d, Direction=Out + * This is a signed integer value. + * Write to read critical delay difference from cs 0 to + * cs 2. + */ + int8_t cdd_wr_0_1; /* + * Byte offset 0x5b, CSR Addr 0x5402d, Direction=Out + * This is a signed integer value. + * Write to read critical delay difference from cs 0 to + * cs 1. + */ + int8_t cdd_wr_0_0; /* + * Byte offset 0x5c, CSR Addr 0x5402e, Direction=Out + * This is a signed integer value. + * Write to read critical delay difference from cs 0 to + * cs 0. + */ + uint8_t reserved5d; /* + * Byte offset 0x5d, CSR Addr 0x5402e, Direction=In + * This field is reserved and must be programmed to 0x00, + * excepted for DDR4: + * By default, if this parameter is 0, the offset applied at + * the end of DDR4 RxEn training resulting in the trained + * RxEnDly is 3/8 of the RX preamble width; if reserved5D is + * non zero, this offset is used instead (in fine step). + */ + uint16_t mr0; /* + * Byte offset 0x5e, CSR Addr 0x5402f, Direction=In + * Value of DDR mode register mr0 for all ranks for current + * pstate. + */ + uint16_t mr1; /* + * Byte offset 0x60, CSR Addr 0x54030, Direction=In + * Value of DDR mode register mr1 for all ranks for current + * pstate. + */ + uint16_t mr2; /* + * Byte offset 0x62, CSR Addr 0x54031, Direction=In + * Value of DDR mode register mr2 for all ranks for current + * pstate. + */ + uint8_t reserved64; /* + * Byte offset 0x64, CSR Addr 0x54032, Direction=In + * Reserved64[0] = protect memory reset + * 0x0 = dfi_reset_n cannot control CP_MEMRESET_L to + * devices after training. (Default value) + * 0x1 = dfi_reset_n can control CP_MEMRESET_L to + * devices after training. + * + * Reserved64[7:1] RFU, must be zero + */ + uint8_t reserved65; /* + * Byte offset 0x65, CSR Addr 0x54032, Direction=N/A + * This field is reserved and must be programmed to 0x00. + */ + uint8_t reserved66; /* + * Byte offset 0x66, CSR Addr 0x54033, Direction=N/A + * This field is reserved and must be programmed to 0x00. + */ + uint8_t reserved67; /* + * Byte offset 0x67, CSR Addr 0x54033, Direction=N/A + * This field is reserved and must be programmed to 0x00. + */ + uint8_t reserved68; /* + * Byte offset 0x68, CSR Addr 0x54034, Direction=N/A + * This field is reserved and must be programmed to 0x00. + */ + uint8_t reserved69; /* + * Byte offset 0x69, CSR Addr 0x54034, Direction=N/A + * This field is reserved and must be programmed to 0x00. + */ + uint8_t reserved6a; /* + * Byte offset 0x6a, CSR Addr 0x54035, Direction=N/A + * This field is reserved and must be programmed to 0x00. + */ + uint8_t reserved6b; /* + * Byte offset 0x6b, CSR Addr 0x54035, Direction=N/A + * This field is reserved and must be programmed to 0x00. + */ + uint8_t reserved6c; /* + * Byte offset 0x6c, CSR Addr 0x54036, Direction=N/A + * This field is reserved and must be programmed to 0x00. + */ + uint8_t reserved6d; /* + * Byte offset 0x6d, CSR Addr 0x54036, Direction=N/A + * This field is reserved and must be programmed to 0x00. + */ + uint8_t reserved6e; /* + * Byte offset 0x6e, CSR Addr 0x54037, Direction=N/A + * This field is reserved and must be programmed to 0x00. + */ + uint8_t reserved6f; /* + * Byte offset 0x6f, CSR Addr 0x54037, Direction=N/A + * This field is reserved and must be programmed to 0x00. + */ + uint8_t reserved70; /* + * Byte offset 0x70, CSR Addr 0x54038, Direction=N/A + * This field is reserved and must be programmed to 0x00. + */ + uint8_t reserved71; /* + * Byte offset 0x71, CSR Addr 0x54038, Direction=N/A + * This field is reserved and must be programmed to 0x00. + */ + uint8_t reserved72; /* + * Byte offset 0x72, CSR Addr 0x54039, Direction=N/A + * This field is reserved and must be programmed to 0x00. + */ + uint8_t reserved73; /* + * Byte offset 0x73, CSR Addr 0x54039, Direction=N/A + * This field is reserved and must be programmed to 0x00. + */ + uint8_t acsmodtctrl0; /* + * Byte offset 0x74, CSR Addr 0x5403a, Direction=In + * Odt pattern for accesses targeting rank 0. [3:0] is used + * for write ODT [7:4] is used for read ODT + */ + uint8_t acsmodtctrl1; /* + * Byte offset 0x75, CSR Addr 0x5403a, Direction=In + * Odt pattern for accesses targeting rank 1. [3:0] is used + * for write ODT [7:4] is used for read ODT + */ + uint8_t acsmodtctrl2; /* + * Byte offset 0x76, CSR Addr 0x5403b, Direction=In + * Odt pattern for accesses targeting rank 2. [3:0] is used + * for write ODT [7:4] is used for read ODT + */ + uint8_t acsmodtctrl3; /* + * Byte offset 0x77, CSR Addr 0x5403b, Direction=In + * Odt pattern for accesses targeting rank 3. [3:0] is used + * for write ODT [7:4] is used for read ODT + */ + uint8_t acsmodtctrl4; /* + * Byte offset 0x78, CSR Addr 0x5403c, Direction=In + * This field is reserved and must be programmed to 0x00. + */ + uint8_t acsmodtctrl5; /* + * Byte offset 0x79, CSR Addr 0x5403c, Direction=In + * This field is reserved and must be programmed to 0x00. + */ + uint8_t acsmodtctrl6; /* + * Byte offset 0x7a, CSR Addr 0x5403d, Direction=In + * This field is reserved and must be programmed to 0x00. + */ + uint8_t acsmodtctrl7; /* + * Byte offset 0x7b, CSR Addr 0x5403d, Direction=In + * This field is reserved and must be programmed to 0x00. + */ + uint8_t reserved7c; /* + * Byte offset 0x7c, CSR Addr 0x5403e, Direction=N/A + * This field is reserved and must be programmed to 0x00. + */ + uint8_t reserved7d; /* + * Byte offset 0x7d, CSR Addr 0x5403e, Direction=N/A + * This field is reserved and must be programmed to 0x00. + */ + uint8_t reserved7e; /* + * Byte offset 0x7e, CSR Addr 0x5403f, Direction=N/A + * This field is reserved and must be programmed to 0x00. + */ + uint8_t reserved7f; /* + * Byte offset 0x7f, CSR Addr 0x5403f, Direction=N/A + * This field is reserved and must be programmed to 0x00. + */ + uint8_t reserved80; /* + * Byte offset 0x80, CSR Addr 0x54040, Direction=N/A + * This field is reserved and must be programmed to 0x00. + */ + uint8_t reserved81; /* + * Byte offset 0x81, CSR Addr 0x54040, Direction=N/A + * This field is reserved and must be programmed to 0x00. + */ + uint8_t reserved82; /* + * Byte offset 0x82, CSR Addr 0x54041, Direction=N/A + * This field is reserved and must be programmed to 0x00. + */ + uint8_t reserved83; /* + * Byte offset 0x83, CSR Addr 0x54041, Direction=N/A + * This field is reserved and must be programmed to 0x00. + */ + uint8_t reserved84; /* Byte offset 0x84, CSR Addr 0x54042, Direction=N/A */ + uint8_t reserved85; /* Byte offset 0x85, CSR Addr 0x54042, Direction=N/A */ + uint8_t reserved86; /* Byte offset 0x86, CSR Addr 0x54043, Direction=N/A */ + uint8_t reserved87; /* Byte offset 0x87, CSR Addr 0x54043, Direction=N/A */ + uint8_t reserved88; /* Byte offset 0x88, CSR Addr 0x54044, Direction=N/A */ + uint8_t reserved89; /* Byte offset 0x89, CSR Addr 0x54044, Direction=N/A */ + uint8_t reserved8a; /* Byte offset 0x8a, CSR Addr 0x54045, Direction=N/A */ + uint8_t reserved8b; /* Byte offset 0x8b, CSR Addr 0x54045, Direction=N/A */ + uint8_t reserved8c; /* Byte offset 0x8c, CSR Addr 0x54046, Direction=N/A */ + uint8_t reserved8d; /* Byte offset 0x8d, CSR Addr 0x54046, Direction=N/A */ + uint8_t reserved8e; /* Byte offset 0x8e, CSR Addr 0x54047, Direction=N/A */ + uint8_t reserved8f; /* Byte offset 0x8f, CSR Addr 0x54047, Direction=N/A */ + uint8_t reserved90; /* Byte offset 0x90, CSR Addr 0x54048, Direction=N/A */ + uint8_t reserved91; /* Byte offset 0x91, CSR Addr 0x54048, Direction=N/A */ + uint8_t reserved92; /* Byte offset 0x92, CSR Addr 0x54049, Direction=N/A */ + uint8_t reserved93; /* Byte offset 0x93, CSR Addr 0x54049, Direction=N/A */ + uint8_t reserved94; /* Byte offset 0x94, CSR Addr 0x5404a, Direction=N/A */ + uint8_t reserved95; /* Byte offset 0x95, CSR Addr 0x5404a, Direction=N/A */ + uint8_t reserved96; /* Byte offset 0x96, CSR Addr 0x5404b, Direction=N/A */ + uint8_t reserved97; /* Byte offset 0x97, CSR Addr 0x5404b, Direction=N/A */ + uint8_t reserved98; /* Byte offset 0x98, CSR Addr 0x5404c, Direction=N/A */ + uint8_t reserved99; /* Byte offset 0x99, CSR Addr 0x5404c, Direction=N/A */ + uint8_t reserved9a; /* Byte offset 0x9a, CSR Addr 0x5404d, Direction=N/A */ + uint8_t reserved9b; /* Byte offset 0x9b, CSR Addr 0x5404d, Direction=N/A */ + uint8_t reserved9c; /* Byte offset 0x9c, CSR Addr 0x5404e, Direction=N/A */ + uint8_t reserved9d; /* Byte offset 0x9d, CSR Addr 0x5404e, Direction=N/A */ + uint8_t reserved9e; /* Byte offset 0x9e, CSR Addr 0x5404f, Direction=N/A */ + uint8_t reserved9f; /* Byte offset 0x9f, CSR Addr 0x5404f, Direction=N/A */ + uint8_t reserveda0; /* Byte offset 0xa0, CSR Addr 0x54050, Direction=N/A */ + uint8_t reserveda1; /* Byte offset 0xa1, CSR Addr 0x54050, Direction=N/A */ + uint8_t reserveda2; /* Byte offset 0xa2, CSR Addr 0x54051, Direction=N/A */ + uint8_t reserveda3; /* Byte offset 0xa3, CSR Addr 0x54051, Direction=N/A */ +} __packed __aligned(2); + +#endif /* MNPMUSRAMMSGBLOCK_DDR3_H */ diff --git a/drivers/st/ddr/phy/firmware/include/mnpmusrammsgblock_ddr4.h b/drivers/st/ddr/phy/firmware/include/mnpmusrammsgblock_ddr4.h new file mode 100644 index 00000000..384650e4 --- /dev/null +++ b/drivers/st/ddr/phy/firmware/include/mnpmusrammsgblock_ddr4.h @@ -0,0 +1,2203 @@ +/* + * Copyright (C) 2021-2024, STMicroelectronics - All Rights Reserved + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef MNPMUSRAMMSGBLOCK_DDR4_H +#define MNPMUSRAMMSGBLOCK_DDR4_H + +/* DDR4U_1D training firmware message block structure + * + * Please refer to the Training Firmware App Note for futher information about + * the usage for Message Block. + */ +struct pmu_smb_ddr_1d { + uint8_t reserved00; /* + * Byte offset 0x00, CSR Addr 0x54000, Direction=In + * reserved00[0:4] RFU, must be zero + * + * reserved00[5] = Train vrefDAC0 During Read Deskew + * 0x1 = Read Deskew will begin by enabling and roughly + * training the phy's per-lane reference voltages. + * Training the vrefDACs CSRs will increase the maximum 1D + * training time by around half a millisecond, but will + * improve 1D training accuracy on systems with + * significant voltage-offsets between lane read eyes. + * 0x0 = Read Deskew will assume the messageblock's + * phyVref setting will work for all lanes. + * + * reserved00[6] = Enable High Effort WrDQ1D + * 0x1 = WrDQ1D will conditionally retry training at + * several extra RxClkDly Timings. This will increase the + * maximum 1D training time by up to 4 extra iterations of + * WrDQ1D. This is only required in systems that suffer + * from very large, asymmetric eye-collapse when receiving + * PRBS patterns. + * 0x0 = WrDQ1D assume rxClkDly values found by SI + * Friendly RdDqs1D will work for receiving PRBS patterns + * + * reserved00[7] = Optimize for the special hard macros in + * TSMC28. + * 0x1 = set if the phy being trained was manufactured in + * any TSMC28 process node. + * 0x0 = otherwise, when not training a TSMC28 phy, leave + * this field as 0. + */ + uint8_t msgmisc; /* + * Byte offset 0x01, CSR Addr 0x54000, Direction=In + * Contains various global options for training. + * + * Bit fields: + * + * msgmisc[0] = MTESTEnable + * 0x1 = Pulse primary digital test output bump at the end + * of each major training stage. This enables observation + * of training stage completion by observing the digital + * test output. + * 0x0 = Do not pulse primary digital test output bump + * + * msgmisc[1] = SimulationOnlyReset + * 0x1 = Verilog only simulation option to shorten + * duration of DRAM reset pulse length to 1ns. + * Must never be set to 1 in silicon. + * 0x0 = Use reset pulse length specified by JEDEC + * standard. + * + * msgmisc[2] = SimulationOnlyTraining + * 0x1 = Verilog only simulation option to shorten the + * duration of the training steps by performing fewer + * iterations. + * Must never be set to 1 in silicon. + * 0x0 = Use standard training duration. + * + * msgmisc[3] = RFU, must be zero + * 0x1 = Program user characterized Vref DQ values per + * DDR4 DRAM device. The message block vrefdqr*nib* fields + * must be populated with the desired per device Vref DQs + * when using this option. Note: this option is not + * applicable in 2D training because these values are + * explicitly trained in 2D. + * 0x0 = Program Vref DQ for all DDR4 devices with the + * single value provided in mr6 message block field + * + * msgmisc[4] = Suppress streaming messages, including + * assertions, regardless of hdtctrl setting. + * Stage Completion messages, as well as training completion + * and error messages are still sent depending on hdtctrl + * setting. + * + * msgmisc[5] = PerByteMaxRdLat + * 0x1 = Each DBYTE will return dfi_rddata_valid at the + * lowest possible latency. This may result in unaligned + * data between bytes to be returned to the DFI. + * 0x0 = Every DBYTE will return dfi_rddata_valid + * simultaneously. This will ensure that data bytes will + * return aligned accesses to the DFI. + * + * msgmisc[6] = PartialRank (DDR3 UDIMM and DDR4 UDIMM only, + * otherwise RFU, must be zero) + * 0x1 = Support rank populated with a subset of byte, but + * where even-odd pair of rank support all the byte + * 0x0 = All rank populated with all the byte (tyical + * configuration) + * + * msgmisc[7] RFU, must be zero + * + * Notes: + * + * - SimulationOnlyReset and SimulationOnlyTraining can be + * used to speed up simulation run times, and must never + * be used in real silicon. Some VIPs may have checks on + * DRAM reset parameters that may need to be disabled when + * using SimulationOnlyReset. + */ + uint16_t pmurevision; /* + * Byte offset 0x02, CSR Addr 0x54001, Direction=Out + * PMU firmware revision ID + * After training is run, this address will contain the + * revision ID of the firmware. + * Please reference this revision ID when filing support + * cases. + */ + uint8_t pstate; /* + * Byte offset 0x04, CSR Addr 0x54002, Direction=In + * Must be set to the target pstate to be trained + * 0x0 = pstate 0 + * 0x1 = pstate 1 + * 0x2 = pstate 2 + * 0x3 = pstate 3 + * All other encodings are reserved + */ + uint8_t pllbypassen; /* + * Byte offset 0x05, CSR Addr 0x54002, Direction=In + * Set according to whether target pstate uses PHY PLL + * bypass + * 0x0 = PHY PLL is enabled for target pstate + * 0x1 = PHY PLL is bypassed for target pstate + */ + uint16_t dramfreq; /* + * Byte offset 0x06, CSR Addr 0x54003, Direction=In + * DDR data rate for the target pstate in units of MT/s. + * For example enter 0x0640 for DDR1600. + */ + uint8_t dfifreqratio; /* + * Byte offset 0x08, CSR Addr 0x54004, Direction=In + * Frequency ratio betwen DfiCtlClk and SDRAM memclk. + * 0x1 = 1:1 + * 0x2 = 1:2 + * 0x4 = 1:4 + */ + uint8_t bpznresval; /* + * Byte offset 0x09, CSR Addr 0x54004, Direction=In + * Overwrite the value of precision resistor connected to + * Phy BP_ZN + * 0x00 = Do not program. Use current CSR value. + * 0xf0 = 240 Ohm + * 0x78 = 120 Ohm + * 0x28 = 40 Ohm + * All other values are reserved. + * It is recommended to set this to 0x00. + */ + uint8_t phyodtimpedance; /* + * Byte offset 0x0a, CSR Addr 0x54005, Direction=In + * Must be programmed to the termination impedance in ohms + * used by PHY during reads. + * + * 0x0 = Firmware skips programming (must be manually + * programmed by user prior to training start) + * + * See PHY databook for legal termination impedance values. + * + * For digital simulation, any legal value can be used. For + * silicon, the users must determine the correct value + * through SI simulation or other methods. + */ + uint8_t phydrvimpedance; /* + * Byte offset 0x0b, CSR Addr 0x54005, Direction=In + * Must be programmed to the driver impedance in ohms used + * by PHY during writes for all DBYTE drivers + * (DQ/DM/DBI/DQS). + * + * 0x0 = Firmware skips programming (must be manually + * programmed by user prior to training start) + * + * See PHY databook for legal R_on driver impedance values. + * + * For digital simulation, any value can be used that is not + * Hi-Z. For silicon, the users must determine the correct + * value through SI simulation or other methods. + */ + uint8_t phyvref; /* + * Byte offset 0x0c, CSR Addr 0x54006, Direction=In + * Must be programmed with the Vref level to be used by the + * PHY during reads + * + * The units of this field are a percentage of VDDQ + * according to the following equation: + * + * Receiver Vref = VDDQ*phyvref[6:0]/128 + * + * For example to set Vref at 0.75*VDDQ, set this field to + * 0x60. + * + * For digital simulation, any legal value can be used. For + * silicon, the users must calculate the analytical Vref by + * using the impedances, terminations, and series resistance + * present in the system. + */ + uint8_t dramtype; /* + * Byte offset 0x0d, CSR Addr 0x54006, Direction=In + * Module Type: + * 0x01 = Reserved + * 0x02 = DDR4 unbuffered + * 0x03 = Reserved + * 0x04 = Reserved + * 0x05 = Reserved + */ + uint8_t disableddbyte; /* + * Byte offset 0x0e, CSR Addr 0x54007, Direction=In + * Bitmap to indicate which Dbyte are not connected (for + * DByte 0 to 7): + * Set disableddbyte[i] to 1 only to specify that DByte is + * not need to be trained (DByte 8 can be disabled via + * enableddqs setting) + */ + uint8_t enableddqs; /* + * Byte offset 0x0f, CSR Addr 0x54007, Direction=In + * Total number of DQ bits enabled in PHY + */ + uint8_t cspresent; /* + * Byte offset 0x10, CSR Addr 0x54008, Direction=In + * Indicates presence of DRAM at each chip select for PHY. + * Each bit corresponds to a logical CS. + * + * If the bit is set to 1, the CS is connected to DRAM. + * If the bit is set to 0, the CS is not connected to DRAM. + * + * cspresent[0] = CS0 is populated with DRAM + * cspresent[1] = CS1 is populated with DRAM + * cspresent[2] = CS2 is populated with DRAM + * cspresent[3] = CS3 is populated with DRAM + * cspresent[7:4] = Reserved (must be programmed to 0) + */ + uint8_t cspresentd0; /* + * Byte offset 0x11, CSR Addr 0x54008, Direction=In + * The CS signals from field cspresent that are routed to + * DIMM connector 0 + */ + uint8_t cspresentd1; /* + * Byte offset 0x12, CSR Addr 0x54009, Direction=In + * The CS signals from field cspresent that are routed to + * DIMM connector 1 + */ + uint8_t addrmirror; /* + * Byte offset 0x13, CSR Addr 0x54009, Direction=In + * Corresponds to CS[3:0] + * 1 = Address Mirror. + * 0 = No Address Mirror. + */ + uint8_t cstestfail; /* + * Byte offset 0x14, CSR Addr 0x5400a, Direction=Out + * This field will be set if training fails on any rank. + * 0x0 = No failures + * non-zero = one or more ranks failed training + */ + uint8_t phycfg; /* + * Byte offset 0x15, CSR Addr 0x5400a, Direction=In + * Additional mode bits. + * + * Bit fields: + * [0] SlowAccessMode: + * 1 = 2T Address Timing. + * 0 = 1T Address Timing. + * [7-1] RFU, must be zero + * + * WARNING: In case of DDR4 Geardown Mode (mr3[A3] == 1), + * phycfg[0] must be 0. + */ + uint16_t sequencectrl; /* + * Byte offset 0x16, CSR Addr 0x5400b, Direction=In + * Controls the training steps to be run. Each bit + * corresponds to a training step. + * + * If the bit is set to 1, the training step will run. + * If the bit is set to 0, the training step will be + * skipped. + * + * Training step to bit mapping: + * sequencectrl[0] = Run DevInit - Device/phy + * initialization. Should always be set. + * sequencectrl[1] = Run WrLvl - Write leveling + * sequencectrl[2] = Run RxEn - Read gate training + * sequencectrl[3] = Run RdDQS1D - 1d read dqs training + * sequencectrl[4] = Run WrDQ1D - 1d write dq training + * sequencectrl[5] = RFU, must be zero + * sequencectrl[6] = RFU, must be zero + * sequencectrl[7] = RFU, must be zero + * sequencectrl[8] = Run RdDeskew - Per lane read dq deskew + * training + * sequencectrl[9] = Run MxRdLat - Max read latency training + * sequencectrl[10] = Run Reserved + * sequencectrl[11] = Run Reserved + * sequencectrl[12] = Run Reserved + * sequencectrl[13] = Run Reserved + * sequencectrl[15-14] = RFU, must be zero + */ + uint8_t hdtctrl; /* + * Byte offset 0x18, CSR Addr 0x5400c, Direction=In + * To control the total number of debug messages, a + * verbosity subfield (hdtctrl, Hardware Debug Trace + * Control) exists in the message block. Every message has a + * verbosity level associated with it, and as the hdtctrl + * value is increased, less important s messages stop being + * sent through the mailboxes. The meanings of several major + * hdtctrl thresholds are explained below: + * + * 0x04 = Maximal debug messages (e.g., Eye contours) + * 0x05 = Detailed debug messages (e.g. Eye delays) + * 0x0A = Coarse debug messages (e.g. rank information) + * 0xC8 = Stage completion + * 0xC9 = Assertion messages + * 0xFF = Firmware completion messages only + */ + uint8_t reserved19; /* Byte offset 0x19, CSR Addr 0x5400c, Direction=N/A */ + uint8_t reserved1a; /* Byte offset 0x1a, CSR Addr 0x5400d, Direction=N/A */ + uint8_t share2dvrefresult; /* + * Byte offset 0x1b, CSR Addr 0x5400d, Direction=In + * Bitmap that designates the phy's vref source for every + * pstate + * If share2dvrefresult[x] = 0, then after 2D training, + * pstate x will continue using the phyVref provided in + * pstate x's 1D messageblock. + * If share2dvrefresult[x] = 1, then after 2D training, + * pstate x will use the per-lane VrefDAC0/1 CSRs trained by + * 2d training. + */ + uint8_t reserved1c; /* Byte offset 0x1c, CSR Addr 0x5400e, Direction=N/A */ + uint8_t reserved1d; /* Byte offset 0x1d, CSR Addr 0x5400e, Direction=N/A */ + uint8_t reserved1e; /* + * Byte offset 0x1e, CSR Addr 0x5400f, Direction=In + * Input for constraining the range of vref(DQ) values + * training will collect data for, usually reducing training + * time. However, too large of a voltage range may cause + * longer 2D training times while too small of a voltage + * range may truncate passing regions. When in doubt, leave + * this field set to 0. + * Used by 2D training in: Rd2D, Wr2D + * + * reserved1E[0-3]: Rd2D Voltage Range + * 0 = Training will search all phy vref(DQ) settings + * 1 = limit to +/-2 %VDDQ from phyVref + * 2 = limit to +/-4 %VDDQ from phyVref + * . . . + * 15 = limit to +/-30% VDDQ from phyVref + * + * reserved1E[4-7]: Wr2D Voltage Range + * 0 = Training will search all dram vref(DQ) settings + * 1 = limit to +/-2 %VDDQ from mr6 + * 2 = limit to +/-4 %VDDQ from mr6 + * . . . + * 15 = limit to +/-30% VDDQ from mr6 + */ + uint8_t reserved1f; /* + * Byte offset 0x1f, CSR Addr 0x5400f, Direction=In + * Extended training option: + * + * reserved1F[1:0]: Configured RxClkDly offset try during + * WrDq1D high-effort (i.e., when reserved00[6] is set) + * 0: -8, +8, -16, +16 + * 1: -4, +4, -8, +8, -12, +12, -16, +16 + * 2: -2, +2, -4, +4, -6, +6, -8, +8 + * 3: -2, +2, -4, +4, -6, +6, -8, +8, -10, +10, -12, +12, + * -14, +14, -16, +16 + * + * reserved1F[2]: When set, execute again WrDq1D after + * RdDqs1D PRBS + * reserved1F[3]: When set redo RdDeskew with PRBS after + * (first) WrDqs1D + * reserved1F[7:4]: This field is reserved and must be + * programmed to 0x00. + */ + uint8_t reserved20; /* + * Byte offset 0x20, CSR Addr 0x54010, Direction=In + * This field is reserved and must be programmed to 0x00, + * excepted for Reserved: + * Reserved MREP assume raising edge is found when + * reserved20[3:0]+3 consecutive 1 are received during MREP + * fine delay swept; reserved20[6:0] thus permits to + * increase tolerance for noisy system. And if reserved20[7] + * is set, MREP training is failing if no raising edge is + * found (otherwise the raising edge is assume close to + * delay 0). + */ + uint8_t reserved21; /* + * Byte offset 0x21, CSR Addr 0x54010, Direction=In + * This field is reserved and must be programmed to 0x00, + * excepted for Reserved: + * Reserved DWL assume raising edge is found when + * reserved21[3:0]+3 consecutive 1 are received during DWL + * fine delay swept; reserved21[6:0] thus permits to + * increase tolerance for noisy system. And if reserved21[7] + * is set, DWL training is failing if no raising edge is + * found (otherwise the raising edge is assume close to + * delay 0). + */ + uint16_t phyconfigoverride; /* + * Byte offset 0x22, CSR Addr 0x54011, Direction=In + * Override PhyConfig csr. + * 0x0: Use hardware csr value for PhyConfing + * (recommended) + * Other values: Use value for PhyConfig instead of + * Hardware value. + */ + uint8_t dfimrlmargin; /* + * Byte offset 0x24, CSR Addr 0x54012, Direction=In + * Margin added to smallest passing trained DFI Max Read + * Latency value, in units of DFI clocks. Recommended to be + * >= 1. + */ + int8_t cdd_rr_3_2; /* + * Byte offset 0x25, CSR Addr 0x54012, Direction=Out + * This is a signed integer value. + * Read to read critical delay difference from cs 3 to cs 2. + */ + int8_t cdd_rr_3_1; /* + * Byte offset 0x26, CSR Addr 0x54013, Direction=Out + * This is a signed integer value. + * Read to read critical delay difference from cs 3 to cs 1. + */ + int8_t cdd_rr_3_0; /* + * Byte offset 0x27, CSR Addr 0x54013, Direction=Out + * This is a signed integer value. + * Read to read critical delay difference from cs 3 to cs 0. + */ + int8_t cdd_rr_2_3; /* + * Byte offset 0x28, CSR Addr 0x54014, Direction=Out + * This is a signed integer value. + * Read to read critical delay difference from cs 2 to cs 3. + */ + int8_t cdd_rr_2_1; /* + * Byte offset 0x29, CSR Addr 0x54014, Direction=Out + * This is a signed integer value. + * Read to read critical delay difference from cs 2 to cs 1. + */ + int8_t cdd_rr_2_0; /* + * Byte offset 0x2a, CSR Addr 0x54015, Direction=Out + * This is a signed integer value. + * Read to read critical delay difference from cs 2 to cs 0. + */ + int8_t cdd_rr_1_3; /* + * Byte offset 0x2b, CSR Addr 0x54015, Direction=Out + * This is a signed integer value. + * Read to read critical delay difference from cs 1 to cs 3. + */ + int8_t cdd_rr_1_2; /* + * Byte offset 0x2c, CSR Addr 0x54016, Direction=Out + * This is a signed integer value. + * Read to read critical delay difference from cs 1 to cs 2. + */ + int8_t cdd_rr_1_0; /* + * Byte offset 0x2d, CSR Addr 0x54016, Direction=Out + * This is a signed integer value. + * Read to read critical delay difference from cs 1 to cs 0. + */ + int8_t cdd_rr_0_3; /* + * Byte offset 0x2e, CSR Addr 0x54017, Direction=Out + * This is a signed integer value. + * Read to read critical delay difference from cs 0 to cs 3. + */ + int8_t cdd_rr_0_2; /* + * Byte offset 0x2f, CSR Addr 0x54017, Direction=Out + * This is a signed integer value. + * Read to read critical delay difference from cs 0 to cs 2. + */ + int8_t cdd_rr_0_1; /* + * Byte offset 0x30, CSR Addr 0x54018, Direction=Out + * This is a signed integer value. + * Read to read critical delay difference from cs 0 to cs 1. + */ + int8_t cdd_ww_3_2; /* + * Byte offset 0x31, CSR Addr 0x54018, Direction=Out + * This is a signed integer value. + * Write to write critical delay difference from cs 3 to cs + * 2. + */ + int8_t cdd_ww_3_1; /* + * Byte offset 0x32, CSR Addr 0x54019, Direction=Out + * This is a signed integer value. + * Write to write critical delay difference from cs 3 to cs + * 1. + */ + int8_t cdd_ww_3_0; /* + * Byte offset 0x33, CSR Addr 0x54019, Direction=Out + * This is a signed integer value. + * Write to write critical delay difference from cs 3 to cs + * 0. + */ + int8_t cdd_ww_2_3; /* + * Byte offset 0x34, CSR Addr 0x5401a, Direction=Out + * This is a signed integer value. + * Write to write critical delay difference from cs 2 to cs + * 3. + */ + int8_t cdd_ww_2_1; /* + * Byte offset 0x35, CSR Addr 0x5401a, Direction=Out + * This is a signed integer value. + * Write to write critical delay difference from cs 2 to cs + * 1. + */ + int8_t cdd_ww_2_0; /* + * Byte offset 0x36, CSR Addr 0x5401b, Direction=Out + * This is a signed integer value. + * Write to write critical delay difference from cs 2 to cs + * 0. + */ + int8_t cdd_ww_1_3; /* + * Byte offset 0x37, CSR Addr 0x5401b, Direction=Out + * This is a signed integer value. + * Write to write critical delay difference from cs 1 to cs + * 3. + */ + int8_t cdd_ww_1_2; /* + * Byte offset 0x38, CSR Addr 0x5401c, Direction=Out + * This is a signed integer value. + * Write to write critical delay difference from cs 1 to cs + * 2. + */ + int8_t cdd_ww_1_0; /* + * Byte offset 0x39, CSR Addr 0x5401c, Direction=Out + * This is a signed integer value. + * Write to write critical delay difference from cs 1 to cs + * 0. + */ + int8_t cdd_ww_0_3; /* + * Byte offset 0x3a, CSR Addr 0x5401d, Direction=Out + * This is a signed integer value. + * Write to write critical delay difference from cs 0 to cs + * 3. + */ + int8_t cdd_ww_0_2; /* + * Byte offset 0x3b, CSR Addr 0x5401d, Direction=Out + * This is a signed integer value. + * Write to write critical delay difference from cs 0 to cs + * 2. + */ + int8_t cdd_ww_0_1; /* + * Byte offset 0x3c, CSR Addr 0x5401e, Direction=Out + * This is a signed integer value. + * Write to write critical delay difference from cs 0 to cs + * 1. + */ + int8_t cdd_rw_3_3; /* + * Byte offset 0x3d, CSR Addr 0x5401e, Direction=Out + * This is a signed integer value. + * Read to write critical delay difference from cs 3 to + * cs 3. + */ + int8_t cdd_rw_3_2; /* + * Byte offset 0x3e, CSR Addr 0x5401f, Direction=Out + * This is a signed integer value. + * Read to write critical delay difference from cs 3 to + * cs 2. + */ + int8_t cdd_rw_3_1; /* + * Byte offset 0x3f, CSR Addr 0x5401f, Direction=Out + * This is a signed integer value. + * Read to write critical delay difference from cs 3 to + * cs 1. + */ + int8_t cdd_rw_3_0; /* + * Byte offset 0x40, CSR Addr 0x54020, Direction=Out + * This is a signed integer value. + * Read to write critical delay difference from cs 3 to + * cs 0. + */ + int8_t cdd_rw_2_3; /* + * Byte offset 0x41, CSR Addr 0x54020, Direction=Out + * This is a signed integer value. + * Read to write critical delay difference from cs 2 to + * cs 3. + */ + int8_t cdd_rw_2_2; /* + * Byte offset 0x42, CSR Addr 0x54021, Direction=Out + * This is a signed integer value. + * Read to write critical delay difference from cs 2 to + * cs 2. + */ + int8_t cdd_rw_2_1; /* + * Byte offset 0x43, CSR Addr 0x54021, Direction=Out + * This is a signed integer value. + * Read to write critical delay difference from cs 2 to + * cs 1. + */ + int8_t cdd_rw_2_0; /* + * Byte offset 0x44, CSR Addr 0x54022, Direction=Out + * This is a signed integer value. + * Read to write critical delay difference from cs 2 to + * cs 0. + */ + int8_t cdd_rw_1_3; /* + * Byte offset 0x45, CSR Addr 0x54022, Direction=Out + * This is a signed integer value. + * Read to write critical delay difference from cs 1 to + * cs 3. + */ + int8_t cdd_rw_1_2; /* + * Byte offset 0x46, CSR Addr 0x54023, Direction=Out + * This is a signed integer value. + * Read to write critical delay difference from cs 1 to + * cs 2. + */ + int8_t cdd_rw_1_1; /* + * Byte offset 0x47, CSR Addr 0x54023, Direction=Out + * This is a signed integer value. + * Read to write critical delay difference from cs 1 to + * cs 1. + */ + int8_t cdd_rw_1_0; /* + * Byte offset 0x48, CSR Addr 0x54024, Direction=Out + * This is a signed integer value. + * Read to write critical delay difference from cs 1 to + * cs 0. + */ + int8_t cdd_rw_0_3; /* + * Byte offset 0x49, CSR Addr 0x54024, Direction=Out + * This is a signed integer value. + * Read to write critical delay difference from cs 0 to + * cs 3. + */ + int8_t cdd_rw_0_2; /* + * Byte offset 0x4a, CSR Addr 0x54025, Direction=Out + * This is a signed integer value. + * Read to write critical delay difference from cs 0 to + * cs 2. + */ + int8_t cdd_rw_0_1; /* + * Byte offset 0x4b, CSR Addr 0x54025, Direction=Out + * This is a signed integer value. + * Read to write critical delay difference from cs 0 to + * cs 1. + */ + int8_t cdd_rw_0_0; /* + * Byte offset 0x4c, CSR Addr 0x54026, Direction=Out + * This is a signed integer value. + * Read to write critical delay difference from cs 0 to + * cs 0. + */ + int8_t cdd_wr_3_3; /* + * Byte offset 0x4d, CSR Addr 0x54026, Direction=Out + * This is a signed integer value. + * Write to read critical delay difference from cs 3 to + * cs 3. + */ + int8_t cdd_wr_3_2; /* + * Byte offset 0x4e, CSR Addr 0x54027, Direction=Out + * This is a signed integer value. + * Write to read critical delay difference from cs 3 to + * cs 2. + */ + int8_t cdd_wr_3_1; /* + * Byte offset 0x4f, CSR Addr 0x54027, Direction=Out + * This is a signed integer value. + * Write to read critical delay difference from cs 3 to + * cs 1. + */ + int8_t cdd_wr_3_0; /* + * Byte offset 0x50, CSR Addr 0x54028, Direction=Out + * This is a signed integer value. + * Write to read critical delay difference from cs 3 to + * cs 0. + */ + int8_t cdd_wr_2_3; /* + * Byte offset 0x51, CSR Addr 0x54028, Direction=Out + * This is a signed integer value. + * Write to read critical delay difference from cs 2 to + * cs 3. + */ + int8_t cdd_wr_2_2; /* + * Byte offset 0x52, CSR Addr 0x54029, Direction=Out + * This is a signed integer value. + * Write to read critical delay difference from cs 2 to + * cs 2. + */ + int8_t cdd_wr_2_1; /* + * Byte offset 0x53, CSR Addr 0x54029, Direction=Out + * This is a signed integer value. + * Write to read critical delay difference from cs 2 to + * cs 1. + */ + int8_t cdd_wr_2_0; /* + * Byte offset 0x54, CSR Addr 0x5402a, Direction=Out + * This is a signed integer value. + * Write to read critical delay difference from cs 2 to + * cs 0. + */ + int8_t cdd_wr_1_3; /* + * Byte offset 0x55, CSR Addr 0x5402a, Direction=Out + * This is a signed integer value. + * Write to read critical delay difference from cs 1 to + * cs 3. + */ + int8_t cdd_wr_1_2; /* + * Byte offset 0x56, CSR Addr 0x5402b, Direction=Out + * This is a signed integer value. + * Write to read critical delay difference from cs 1 to + * cs 2. + */ + int8_t cdd_wr_1_1; /* + * Byte offset 0x57, CSR Addr 0x5402b, Direction=Out + * This is a signed integer value. + * Write to read critical delay difference from cs 1 to + * cs 1. + */ + int8_t cdd_wr_1_0; /* + * Byte offset 0x58, CSR Addr 0x5402c, Direction=Out + * This is a signed integer value. + * Write to read critical delay difference from cs 1 to + * cs 0. + */ + int8_t cdd_wr_0_3; /* + * Byte offset 0x59, CSR Addr 0x5402c, Direction=Out + * This is a signed integer value. + * Write to read critical delay difference from cs 0 to + * cs 3. + */ + int8_t cdd_wr_0_2; /* + * Byte offset 0x5a, CSR Addr 0x5402d, Direction=Out + * This is a signed integer value. + * Write to read critical delay difference from cs 0 to + * cs 2. + */ + int8_t cdd_wr_0_1; /* + * Byte offset 0x5b, CSR Addr 0x5402d, Direction=Out + * This is a signed integer value. + * Write to read critical delay difference from cs 0 to + * cs 1. + */ + int8_t cdd_wr_0_0; /* + * Byte offset 0x5c, CSR Addr 0x5402e, Direction=Out + * This is a signed integer value. + * Write to read critical delay difference from cs 0 to + * cs 0. + */ + uint8_t reserved5d; /* + * Byte offset 0x5d, CSR Addr 0x5402e, Direction=In + * This field is reserved and must be programmed to 0x00, + * excepted for DDR4: + * By default, if this parameter is 0, the offset applied at + * the end of DDR4 RxEn training resulting in the trained + * RxEnDly is 3/8 of the RX preamble width; if reserved5D is + * non zero, this offset is used instead (in fine step). + */ + uint16_t mr0; /* + * Byte offset 0x5e, CSR Addr 0x5402f, Direction=In + * Value of DDR mode register mr0 for all ranks for current + * pstate. + */ + uint16_t mr1; /* + * Byte offset 0x60, CSR Addr 0x54030, Direction=In + * Value of DDR mode register mr1 for all ranks for current + * pstate. + */ + uint16_t mr2; /* + * Byte offset 0x62, CSR Addr 0x54031, Direction=In + * Value of DDR mode register mr2 for all ranks for current + * pstate. + */ + uint16_t mr3; /* + * Byte offset 0x64, CSR Addr 0x54032, Direction=In + * Value of DDR mode register mr3 for all ranks for current + * pstate. + */ + uint16_t mr4; /* + * Byte offset 0x66, CSR Addr 0x54033, Direction=In + * Value of DDR mode register mr4 for all ranks for current + * pstate. + */ + uint16_t mr5; /* + * Byte offset 0x68, CSR Addr 0x54034, Direction=In + * Value of DDR mode register mr5 for all ranks for current + * pstate. + */ + uint16_t mr6; /* + * Byte offset 0x6a, CSR Addr 0x54035, Direction=In + * Value of DDR mode register mr6 for all ranks for current + * pstate. Note: The initial VrefDq value and range must be + * set in A6:A0. + */ + uint8_t x16present; /* + * Byte offset 0x6c, CSR Addr 0x54036, Direction=In + * X16 device map. Corresponds to CS[3:0]. + * x16present[0] = CS0 is populated with X16 devices + * x16present[1] = CS1 is populated with X16 devices + * x16present[2] = CS2 is populated with X16 devices + * x16present[3] = CS3 is populated with X16 devices + * x16present[7:4] = Reserved (must be programmed to 0) + * + * Ranks may not contain mixed device types. + */ + uint8_t cssetupgddec; /* + * Byte offset 0x6d, CSR Addr 0x54036, Direction=In + * controls timing of chip select signals when DDR4 + * gear-down mode is active + * 0 - Leave delay of chip select timing group signal + * the same both before and after gear-down sync occurs + * 1 - Add 1UI of delay to chip select timing group + * signals when geardown-mode is active. This allows CS + * signals to have equal setup and hold time in gear-down + * mode + */ + uint16_t rtt_nom_wr_park0; /* + * Byte offset 0x6e, CSR Addr 0x54037, Direction=In + * Optional RTT_NOM, RTT_WR and RTT_PARK values for rank 0 + * DRAM: + * rtt_nom_wr_park0[0] = 1: Option is enable (otherwise, + * remaining bit fields are don't care) + * rtt_nom_wr_park0[5:3]: Optional RTT_NOM value to be used + * in mr1[10:8] for rank 0 + * rtt_nom_wr_park0[11:9]: Optional RTT_WR value to be used + * in mr2[11:9] for rank 0 + * rtt_nom_wr_park0[8:6]: Optional RTT_PARK value to be used + * in mr5[8:6] for rank 0 + */ + uint16_t rtt_nom_wr_park1; /* + * Byte offset 0x70, CSR Addr 0x54038, Direction=In + * Optional RTT_NOM, RTT_WR and RTT_PARK values for rank 1 + * DRAM: + * rtt_nom_wr_park1[0] = 1: Option is enable (otherwise, + * remaining bit fields are don't care) + * rtt_nom_wr_park1[5:3]: Optional RTT_NOM value to be used + * in mr1[10:8] for rank 1 + * rtt_nom_wr_park1[11:9]: Optional RTT_WR value to be used + * in mr2[11:9] for rank 1 + * rtt_nom_wr_park1[8:6]: Optional RTT_PARK value to be used + * in mr5[8:6] for rank 1 + */ + uint16_t rtt_nom_wr_park2; /* + * Byte offset 0x72, CSR Addr 0x54039, Direction=In + * Optional RTT_NOM, RTT_WR and RTT_PARK values for rank 2 + * DRAM: + * rtt_nom_wr_park2[0] = 1: Option is enable (otherwise, + * remaining bit fields are don't care) + * rtt_nom_wr_park2[5:3]: Optional RTT_NOM value to be used + * in mr1[10:8] for rank 2 + * rtt_nom_wr_park2[11:9]: Optional RTT_WR value to be used + * in mr2[11:9] for rank 2 + * rtt_nom_wr_park2[8:6]: Optional RTT_PARK value to be used + * in mr5[8:6] for rank 2 + */ + uint16_t rtt_nom_wr_park3; /* + * Byte offset 0x74, CSR Addr 0x5403a, Direction=In + * Optional RTT_NOM, RTT_WR and RTT_PARK values for rank 3 + * DRAM: + * rtt_nom_wr_park3[0] = 1: Option is enable (otherwise, + * remaining bit fields are don't care) + * rtt_nom_wr_park3[5:3]: Optional RTT_NOM value to be used + * in mr1[10:8] for rank 3 + * rtt_nom_wr_park3[11:9]: Optional RTT_WR value to be used + * in mr2[11:9] for rank 3 + * rtt_nom_wr_park3[8:6]: Optional RTT_PARK value to be used + * in mr5[8:6] for rank 3 + */ + uint16_t rtt_nom_wr_park4; /* + * Byte offset 0x76, CSR Addr 0x5403b, Direction=In + * Optional RTT_NOM, RTT_WR and RTT_PARK values for rank 4 + * DRAM: + * rtt_nom_wr_park4[0] = 1: Option is enable (otherwise, + * remaining bit fields are don't care) + * rtt_nom_wr_park4[5:3]: Optional RTT_NOM value to be used + * in mr1[10:8] for rank 4 + * rtt_nom_wr_park4[11:9]: Optional RTT_WR value to be used + * in mr2[11:9] for rank 4 + * rtt_nom_wr_park4[8:6]: Optional RTT_PARK value to be used + * in mr5[8:6] for rank 4 + */ + uint16_t rtt_nom_wr_park5; /* + * Byte offset 0x78, CSR Addr 0x5403c, Direction=In + * Optional RTT_NOM, RTT_WR and RTT_PARK values for rank 5 + * DRAM: + * rtt_nom_wr_park5[0] = 1: Option is enable (otherwise, + * remaining bit fields are don't care) + * rtt_nom_wr_park5[5:3]: Optional RTT_NOM value to be used + * in mr1[10:8] for rank 5 + * rtt_nom_wr_park5[11:9]: Optional RTT_WR value to be used + * in mr2[11:9] for rank 5 + * rtt_nom_wr_park5[8:6]: Optional RTT_PARK value to be used + * in mr5[8:6] for rank 5 + */ + uint16_t rtt_nom_wr_park6; /* + * Byte offset 0x7a, CSR Addr 0x5403d, Direction=In + * Optional RTT_NOM, RTT_WR and RTT_PARK values for rank 6 + * DRAM: + * rtt_nom_wr_park6[0] = 1: Option is enable (otherwise, + * remaining bit fields are don't care) + * rtt_nom_wr_park6[5:3]: Optional RTT_NOM value to be used + * in mr1[10:8] for rank 6 + * rtt_nom_wr_park6[11:9]: Optional RTT_WR value to be used + * in mr2[11:9] for rank 6 + * rtt_nom_wr_park6[8:6]: Optional RTT_PARK value to be used + * in mr5[8:6] for rank 6 + */ + uint16_t rtt_nom_wr_park7; /* + * Byte offset 0x7c, CSR Addr 0x5403e, Direction=In + * Optional RTT_NOM, RTT_WR and RTT_PARK values for rank 7 + * DRAM: + * rtt_nom_wr_park7[0] = 1: Option is enable (otherwise, + * remaining bit fields are don't care) + * rtt_nom_wr_park7[5:3]: Optional RTT_NOM value to be used + * in mr1[10:8] for rank 7 + * rtt_nom_wr_park7[11:9]: Optional RTT_WR value to be used + * in mr2[11:9] for rank 7 + * rtt_nom_wr_park7[8:6]: Optional RTT_PARK value to be used + * in mr5[8:6] for rank 7 + */ + uint8_t acsmodtctrl0; /* + * Byte offset 0x7e, CSR Addr 0x5403f, Direction=In + * Odt pattern for accesses targeting rank 0. [3:0] is used + * for write ODT [7:4] is used for read ODT + */ + uint8_t acsmodtctrl1; /* + * Byte offset 0x7f, CSR Addr 0x5403f, Direction=In + * Odt pattern for accesses targeting rank 1. [3:0] is used + * for write ODT [7:4] is used for read ODT + */ + uint8_t acsmodtctrl2; /* + * Byte offset 0x80, CSR Addr 0x54040, Direction=In + * Odt pattern for accesses targeting rank 2. [3:0] is used + * for write ODT [7:4] is used for read ODT + */ + uint8_t acsmodtctrl3; /* + * Byte offset 0x81, CSR Addr 0x54040, Direction=In + * Odt pattern for accesses targeting rank 3. [3:0] is used + * for write ODT [7:4] is used for read ODT + */ + uint8_t acsmodtctrl4; /* + * Byte offset 0x82, CSR Addr 0x54041, Direction=In + * Odt pattern for accesses targeting rank 4. [3:0] is used + * for write ODT [7:4] is used for read ODT + */ + uint8_t acsmodtctrl5; /* + * Byte offset 0x83, CSR Addr 0x54041, Direction=In + * Odt pattern for accesses targeting rank 5. [3:0] is used + * for write ODT [7:4] is used for read ODT + */ + uint8_t acsmodtctrl6; /* + * Byte offset 0x84, CSR Addr 0x54042, Direction=In + * Odt pattern for accesses targeting rank 6. [3:0] is used + * for write ODT [7:4] is used for read ODT + */ + uint8_t acsmodtctrl7; /* + * Byte offset 0x85, CSR Addr 0x54042, Direction=In + * Odt pattern for accesses targeting rank 7. [3:0] is used + * for write ODT [7:4] is used for read ODT + */ + uint8_t vrefdqr0nib0; /* + * Byte offset 0x86, CSR Addr 0x54043, Direction=InOut + * VrefDq for rank 0 nibble 0. Specifies mr6[6:0] + */ + uint8_t vrefdqr0nib1; /* + * Byte offset 0x87, CSR Addr 0x54043, Direction=InOut + * VrefDq for rank 0 nibble 1. Specifies mr6[6:0]. + * Identical to vrefdqr0nib0 for x8 or x16 devices. + */ + uint8_t vrefdqr0nib2; /* + * Byte offset 0x88, CSR Addr 0x54044, Direction=InOut + * VrefDq for rank 0 nibble 2. Specifies mr6[6:0]. + * Identical to vrefdqr0nib0 for x16 devices. + */ + uint8_t vrefdqr0nib3; /* + * Byte offset 0x89, CSR Addr 0x54044, Direction=InOut + * VrefDq for rank 0 nibble 3. Specifies mr6[6:0]. + * Identical to vrefdqr0nib0 for x16 devices, or + * vrefdqr0nib2 for x8 devices. + */ + uint8_t vrefdqr0nib4; /* + * Byte offset 0x8a, CSR Addr 0x54045, Direction=InOut + * VrefDq for rank 0 nibble 4. Specifies mr6[6:0] + */ + uint8_t vrefdqr0nib5; /* + * Byte offset 0x8b, CSR Addr 0x54045, Direction=InOut + * VrefDq for rank 0 nibble 5. Specifies mr6[6:0]. + * Identical to vrefdqr0nib4 for x8 or x16 devices. + */ + uint8_t vrefdqr0nib6; /* + * Byte offset 0x8c, CSR Addr 0x54046, Direction=InOut + * VrefDq for rank 0 nibble 6. Specifies mr6[6:0]. + * Identical to vrefdqr0nib4 for x16 devices. + */ + uint8_t vrefdqr0nib7; /* + * Byte offset 0x8d, CSR Addr 0x54046, Direction=InOut + * VrefDq for rank 0 nibble 7. Specifies mr6[6:0]. + * Identical to vrefdqr0nib4 for x16 devices, + * or vrefdqr0nib6 for x8 devices. + */ + uint8_t vrefdqr0nib8; /* + * Byte offset 0x8e, CSR Addr 0x54047, Direction=InOut + * VrefDq for rank 0 nibble 8. Specifies mr6[6:0] + */ + uint8_t vrefdqr0nib9; /* + * Byte offset 0x8f, CSR Addr 0x54047, Direction=InOut + * VrefDq for rank 0 nibble 9. Specifies mr6[6:0]. + * Identical to vrefdqr0nib8 for x8 or x16 devices. + */ + uint8_t vrefdqr0nib10; /* + * Byte offset 0x90, CSR Addr 0x54048, Direction=InOut + * VrefDq for rank 0 nibble 10. Specifies mr6[6:0]. + * Identical to vrefdqr0nib8 for x16 devices. + */ + uint8_t vrefdqr0nib11; /* + * Byte offset 0x91, CSR Addr 0x54048, Direction=InOut + * VrefDq for rank 0 nibble 11. Specifies mr6[6:0]. + * Identical to vrefdqr0nib8 for x16 devices, + * or vrefdqr0nib10 for x8 devices. + */ + uint8_t vrefdqr0nib12; /* + * Byte offset 0x92, CSR Addr 0x54049, Direction=InOut + * VrefDq for rank 0 nibble 12. Specifies mr6[6:0] + */ + uint8_t vrefdqr0nib13; /* + * Byte offset 0x93, CSR Addr 0x54049, Direction=InOut + * VrefDq for rank 0 nibble 13. Specifies mr6[6:0]. + * Identical to vrefdqr0nib12 for x8 or x16 devices. + */ + uint8_t vrefdqr0nib14; /* + * Byte offset 0x94, CSR Addr 0x5404a, Direction=InOut + * VrefDq for rank 0 nibble 14. Specifies mr6[6:0]. + * Identical to vrefdqr0nib12 for x16 devices. + */ + uint8_t vrefdqr0nib15; /* + * Byte offset 0x95, CSR Addr 0x5404a, Direction=InOut + * VrefDq for rank 0 nibble 15. Specifies mr6[6:0]. + * Identical to vrefdqr0nib12 for x16 devices, + * or vrefdqr0nib14 for x8 devices. + */ + uint8_t vrefdqr0nib16; /* + * Byte offset 0x96, CSR Addr 0x5404b, Direction=InOut + * VrefDq for rank 0 nibble 16. Specifies mr6[6:0] + */ + uint8_t vrefdqr0nib17; /* + * Byte offset 0x97, CSR Addr 0x5404b, Direction=InOut + * VrefDq for rank 0 nibble 17. Specifies mr6[6:0]. + * Identical to vrefdqr0nib16 for x8 or x16 devices. + */ + uint8_t vrefdqr0nib18; /* + * Byte offset 0x98, CSR Addr 0x5404c, Direction=InOut + * VrefDq for rank 0 nibble 18. Specifies mr6[6:0]. + * Identical to vrefdqr0nib16 for x16 devices. + */ + uint8_t vrefdqr0nib19; /* + * Byte offset 0x99, CSR Addr 0x5404c, Direction=InOut + * VrefDq for rank 0 nibble 19. Specifies mr6[6:0]. + * Identical to vrefdqr0nib16 for x16 devices, + * or vrefdqr0nib18 for x8 devices. + */ + uint8_t vrefdqr1nib0; /* + * Byte offset 0x9a, CSR Addr 0x5404d, Direction=InOut + * VrefDq for rank 1 nibble 0. Specifies mr6[6:0] + */ + uint8_t vrefdqr1nib1; /* + * Byte offset 0x9b, CSR Addr 0x5404d, Direction=InOut + * VrefDq for rank 1 nibble 1. Specifies mr6[6:0]. + * Identical to vrefdqr1nib0 for x8 or x16 devices. + */ + uint8_t vrefdqr1nib2; /* + * Byte offset 0x9c, CSR Addr 0x5404e, Direction=InOut + * VrefDq for rank 1 nibble 2. Specifies mr6[6:0]. + * Identical to vrefdqr1nib0 for x16 devices. + */ + uint8_t vrefdqr1nib3; /* + * Byte offset 0x9d, CSR Addr 0x5404e, Direction=InOut + * VrefDq for rank 1 nibble 3. Specifies mr6[6:0]. + * Identical to vrefdqr1nib0 for x16 devices, + * or vrefdqr1nib2 for x8 devices. + */ + uint8_t vrefdqr1nib4; /* + * Byte offset 0x9e, CSR Addr 0x5404f, Direction=InOut + * VrefDq for rank 1 nibble 4. Specifies mr6[6:0] + */ + uint8_t vrefdqr1nib5; /* + * Byte offset 0x9f, CSR Addr 0x5404f, Direction=InOut + * VrefDq for rank 1 nibble 5. Specifies mr6[6:0]. + * Identical to vrefdqr1nib4 for x8 or x16 devices. + */ + uint8_t vrefdqr1nib6; /* + * Byte offset 0xa0, CSR Addr 0x54050, Direction=InOut + * VrefDq for rank 1 nibble 6. Specifies mr6[6:0]. + * Identical to vrefdqr1nib4 for x16 devices. + */ + uint8_t vrefdqr1nib7; /* + * Byte offset 0xa1, CSR Addr 0x54050, Direction=InOut + * VrefDq for rank 1 nibble 7. Specifies mr6[6:0]. + * Identical to vrefdqr1nib4 for x16 devices, + * or vrefdqr1nib6 for x8 devices. + */ + uint8_t vrefdqr1nib8; /* + * Byte offset 0xa2, CSR Addr 0x54051, Direction=InOut + * VrefDq for rank 1 nibble 8. Specifies mr6[6:0] + */ + uint8_t vrefdqr1nib9; /* + * Byte offset 0xa3, CSR Addr 0x54051, Direction=InOut + * VrefDq for rank 1 nibble 9. Specifies mr6[6:0]. + * Identical to vrefdqr1nib8 for x8 or x16 devices. + */ + uint8_t vrefdqr1nib10; /* + * Byte offset 0xa4, CSR Addr 0x54052, Direction=InOut + * VrefDq for rank 1 nibble 10. Specifies mr6[6:0]. + * Identical to vrefdqr1nib8 for x16 devices. + */ + uint8_t vrefdqr1nib11; /* + * Byte offset 0xa5, CSR Addr 0x54052, Direction=InOut + * VrefDq for rank 1 nibble 11. Specifies mr6[6:0]. + * Identical to vrefdqr1nib8 for x16 devices, + * or vrefdqr1nib10 for x8 devices. + */ + uint8_t vrefdqr1nib12; /* + * Byte offset 0xa6, CSR Addr 0x54053, Direction=InOut + * VrefDq for rank 1 nibble 12. Specifies mr6[6:0] + */ + uint8_t vrefdqr1nib13; /* + * Byte offset 0xa7, CSR Addr 0x54053, Direction=InOut + * VrefDq for rank 1 nibble 13. Specifies mr6[6:0]. + * Identical to vrefdqr1nib12 for x8 or x16 devices. + */ + uint8_t vrefdqr1nib14; /* + * Byte offset 0xa8, CSR Addr 0x54054, Direction=InOut + * VrefDq for rank 1 nibble 14. Specifies mr6[6:0]. + * Identical to vrefdqr1nib12 for x16 devices. + */ + uint8_t vrefdqr1nib15; /* + * Byte offset 0xa9, CSR Addr 0x54054, Direction=InOut + * VrefDq for rank 1 nibble 15. Specifies mr6[6:0]. + * Identical to vrefdqr1nib12 for x16 devices, + * or vrefdqr1nib14 for x8 devices. + */ + uint8_t vrefdqr1nib16; /* + * Byte offset 0xaa, CSR Addr 0x54055, Direction=InOut + * VrefDq for rank 1 nibble 16. Specifies mr6[6:0] + */ + uint8_t vrefdqr1nib17; /* + * Byte offset 0xab, CSR Addr 0x54055, Direction=InOut + * VrefDq for rank 1 nibble 17. Specifies mr6[6:0]. + * Identical to vrefdqr1nib16 for x8 or x16 devices. + */ + uint8_t vrefdqr1nib18; /* + * Byte offset 0xac, CSR Addr 0x54056, Direction=InOut + * VrefDq for rank 1 nibble 18. Specifies mr6[6:0]. + * Identical to vrefdqr1nib16 for x16 devices. + */ + uint8_t vrefdqr1nib19; /* + * Byte offset 0xad, CSR Addr 0x54056, Direction=InOut + * VrefDq for rank 1 nibble 19. Specifies mr6[6:0]. + * Identical to vrefdqr1nib16 for x16 devices, + * or vrefdqr1nib18 for x8 devices. + */ + uint8_t vrefdqr2nib0; /* + * Byte offset 0xae, CSR Addr 0x54057, Direction=InOut + * VrefDq for rank 2 nibble 0. Specifies mr6[6:0] + */ + uint8_t vrefdqr2nib1; /* + * Byte offset 0xaf, CSR Addr 0x54057, Direction=InOut + * VrefDq for rank 2 nibble 1. Specifies mr6[6:0]. + * Identical to vrefdqr2nib0 for x8 or x16 devices. + */ + uint8_t vrefdqr2nib2; /* + * Byte offset 0xb0, CSR Addr 0x54058, Direction=InOut + * VrefDq for rank 2 nibble 2. Specifies mr6[6:0]. + * Identical to vrefdqr2nib0 for x16 devices. + */ + uint8_t vrefdqr2nib3; /* + * Byte offset 0xb1, CSR Addr 0x54058, Direction=InOut + * VrefDq for rank 2 nibble 3. Specifies mr6[6:0]. + * Identical to vrefdqr2nib0 for x16 devices, + * or vrefdqr2nib2 for x8 devices. + */ + uint8_t vrefdqr2nib4; /* + * Byte offset 0xb2, CSR Addr 0x54059, Direction=InOut + * VrefDq for rank 2 nibble 4. Specifies mr6[6:0] + */ + uint8_t vrefdqr2nib5; /* + * Byte offset 0xb3, CSR Addr 0x54059, Direction=InOut + * VrefDq for rank 2 nibble 5. Specifies mr6[6:0]. + * Identical to vrefdqr2nib4 for x8 or x16 devices. + */ + uint8_t vrefdqr2nib6; /* + * Byte offset 0xb4, CSR Addr 0x5405a, Direction=InOut + * VrefDq for rank 2 nibble 6. Specifies mr6[6:0]. + * Identical to vrefdqr2nib4 for x16 devices. + */ + uint8_t vrefdqr2nib7; /* + * Byte offset 0xb5, CSR Addr 0x5405a, Direction=InOut + * VrefDq for rank 2 nibble 7. Specifies mr6[6:0]. + * Identical to vrefdqr2nib4 for x16 devices, + * or vrefdqr2nib6 for x8 devices. + */ + uint8_t vrefdqr2nib8; /* + * Byte offset 0xb6, CSR Addr 0x5405b, Direction=InOut + * VrefDq for rank 2 nibble 8. Specifies mr6[6:0] + */ + uint8_t vrefdqr2nib9; /* + * Byte offset 0xb7, CSR Addr 0x5405b, Direction=InOut + * VrefDq for rank 2 nibble 9. Specifies mr6[6:0]. + * Identical to vrefdqr2nib8 for x8 or x16 devices. + */ + uint8_t vrefdqr2nib10; /* + * Byte offset 0xb8, CSR Addr 0x5405c, Direction=InOut + * VrefDq for rank 2 nibble 10. Specifies mr6[6:0]. + * Identical to vrefdqr2nib8 for x16 devices. + */ + uint8_t vrefdqr2nib11; /* + * Byte offset 0xb9, CSR Addr 0x5405c, Direction=InOut + * VrefDq for rank 2 nibble 11. Specifies mr6[6:0]. + * Identical to vrefdqr2nib8 for x16 devices, + * or vrefdqr2nib10 for x8 devices. + */ + uint8_t vrefdqr2nib12; /* + * Byte offset 0xba, CSR Addr 0x5405d, Direction=InOut + * VrefDq for rank 2 nibble 12. Specifies mr6[6:0] + */ + uint8_t vrefdqr2nib13; /* + * Byte offset 0xbb, CSR Addr 0x5405d, Direction=InOut + * VrefDq for rank 2 nibble 13. Specifies mr6[6:0]. + * Identical to vrefdqr2nib12 for x8 or x16 devices. + */ + uint8_t vrefdqr2nib14; /* + * Byte offset 0xbc, CSR Addr 0x5405e, Direction=InOut + * VrefDq for rank 2 nibble 14. Specifies mr6[6:0]. + * Identical to vrefdqr2nib12 for x16 devices. + */ + uint8_t vrefdqr2nib15; /* + * Byte offset 0xbd, CSR Addr 0x5405e, Direction=InOut + * VrefDq for rank 2 nibble 15. Specifies mr6[6:0]. + * Identical to vrefdqr2nib12 for x16 devices, + * or vrefdqr2nib14 for x8 devices. + */ + uint8_t vrefdqr2nib16; /* + * Byte offset 0xbe, CSR Addr 0x5405f, Direction=InOut + * VrefDq for rank 2 nibble 16. Specifies mr6[6:0] + */ + uint8_t vrefdqr2nib17; /* + * Byte offset 0xbf, CSR Addr 0x5405f, Direction=InOut + * VrefDq for rank 2 nibble 17. Specifies mr6[6:0]. + * Identical to vrefdqr2nib16 for x8 or x16 devices. + */ + uint8_t vrefdqr2nib18; /* + * Byte offset 0xc0, CSR Addr 0x54060, Direction=InOut + * VrefDq for rank 2 nibble 18. Specifies mr6[6:0]. + * Identical to vrefdqr2nib16 for x16 devices. + */ + uint8_t vrefdqr2nib19; /* + * Byte offset 0xc1, CSR Addr 0x54060, Direction=InOut + * VrefDq for rank 2 nibble 19. Specifies mr6[6:0]. + * Identical to vrefdqr2nib16 for x16 devices, + * or vrefdqr2nib18 for x8 devices. + */ + uint8_t vrefdqr3nib0; /* + * Byte offset 0xc2, CSR Addr 0x54061, Direction=InOut + * VrefDq for rank 3 nibble 0. Specifies mr6[6:0] + */ + uint8_t vrefdqr3nib1; /* + * Byte offset 0xc3, CSR Addr 0x54061, Direction=InOut + * VrefDq for rank 3 nibble 1. Specifies mr6[6:0]. + * Identical to vrefdqr3nib0 for x8 or x16 devices. + */ + uint8_t vrefdqr3nib2; /* + * Byte offset 0xc4, CSR Addr 0x54062, Direction=InOut + * VrefDq for rank 3 nibble 2. Specifies mr6[6:0]. + * Identical to vrefdqr3nib0 for x16 devices. + */ + uint8_t vrefdqr3nib3; /* + * Byte offset 0xc5, CSR Addr 0x54062, Direction=InOut + * VrefDq for rank 3 nibble 3. Specifies mr6[6:0]. + * Identical to vrefdqr3nib0 for x16 devices, + * or vrefdqr3nib2 for x8 devices. + */ + uint8_t vrefdqr3nib4; /* + * Byte offset 0xc6, CSR Addr 0x54063, Direction=InOut + * VrefDq for rank 3 nibble 4. Specifies mr6[6:0] + */ + uint8_t vrefdqr3nib5; /* + * Byte offset 0xc7, CSR Addr 0x54063, Direction=InOut + * VrefDq for rank 3 nibble 5. Specifies mr6[6:0]. + * Identical to vrefdqr3nib4 for x8 or x16 devices. + */ + uint8_t vrefdqr3nib6; /* + * Byte offset 0xc8, CSR Addr 0x54064, Direction=InOut + * VrefDq for rank 3 nibble 6. Specifies mr6[6:0]. + * Identical to vrefdqr3nib4 for x16 devices. + */ + uint8_t vrefdqr3nib7; /* + * Byte offset 0xc9, CSR Addr 0x54064, Direction=InOut + * VrefDq for rank 3 nibble 7. Specifies mr6[6:0]. + * Identical to vrefdqr3nib4 for x16 devices, + * or vrefdqr3nib6 for x8 devices. + */ + uint8_t vrefdqr3nib8; /* + * Byte offset 0xca, CSR Addr 0x54065, Direction=InOut + * VrefDq for rank 3 nibble 8. Specifies mr6[6:0] + */ + uint8_t vrefdqr3nib9; /* + * Byte offset 0xcb, CSR Addr 0x54065, Direction=InOut + * VrefDq for rank 3 nibble 9. Specifies mr6[6:0]. + * Identical to vrefdqr3nib8 for x8 or x16 devices. + */ + uint8_t vrefdqr3nib10; /* + * Byte offset 0xcc, CSR Addr 0x54066, Direction=InOut + * VrefDq for rank 3 nibble 10. Specifies mr6[6:0]. + * Identical to vrefdqr3nib8 for x16 devices. + */ + uint8_t vrefdqr3nib11; /* + * Byte offset 0xcd, CSR Addr 0x54066, Direction=InOut + * VrefDq for rank 3 nibble 11. Specifies mr6[6:0]. + * Identical to vrefdqr3nib8 for x16 devices, + * or vrefdqr3nib10 for x8 devices. + */ + uint8_t vrefdqr3nib12; /* + * Byte offset 0xce, CSR Addr 0x54067, Direction=InOut + * VrefDq for rank 3 nibble 12. Specifies mr6[6:0] + */ + uint8_t vrefdqr3nib13; /* + * Byte offset 0xcf, CSR Addr 0x54067, Direction=InOut + * VrefDq for rank 3 nibble 13. Specifies mr6[6:0]. + * Identical to vrefdqr3nib12 for x8 or x16 devices. + */ + uint8_t vrefdqr3nib14; /* + * Byte offset 0xd0, CSR Addr 0x54068, Direction=InOut + * VrefDq for rank 3 nibble 14. Specifies mr6[6:0]. + * Identical to vrefdqr3nib12 for x16 devices. + */ + uint8_t vrefdqr3nib15; /* + * Byte offset 0xd1, CSR Addr 0x54068, Direction=InOut + * VrefDq for rank 3 nibble 15. Specifies mr6[6:0]. + * Identical to vrefdqr3nib12 for x16 devices, + * or vrefdqr3nib14 for x8 devices. + */ + uint8_t vrefdqr3nib16; /* + * Byte offset 0xd2, CSR Addr 0x54069, Direction=InOut + * VrefDq for rank 3 nibble 16. Specifies mr6[6:0] + */ + uint8_t vrefdqr3nib17; /* + * Byte offset 0xd3, CSR Addr 0x54069, Direction=InOut + * VrefDq for rank 3 nibble 17. Specifies mr6[6:0]. + * Identical to vrefdqr3nib16 for x8 or x16 devices. + */ + uint8_t vrefdqr3nib18; /* + * Byte offset 0xd4, CSR Addr 0x5406a, Direction=InOut + * VrefDq for rank 3 nibble 18. Specifies mr6[6:0]. + * Identical to vrefdqr3nib16 for x16 devices. + */ + uint8_t vrefdqr3nib19; /* + * Byte offset 0xd5, CSR Addr 0x5406a, Direction=InOut + * VrefDq for rank 3 nibble 19. Specifies mr6[6:0]. + * Identical to vrefdqr3nib16 for x16 devices, + * or vrefdqr3nib18 for x8 devices. + */ + uint8_t reservedd6; /* Byte offset 0xd6, CSR Addr 0x5406b, Direction=N/A */ + uint8_t reservedd7; /* Byte offset 0xd7, CSR Addr 0x5406b, Direction=N/A */ + uint8_t reservedd8; /* Byte offset 0xd8, CSR Addr 0x5406c, Direction=N/A */ + uint8_t reservedd9; /* Byte offset 0xd9, CSR Addr 0x5406c, Direction=N/A */ + uint8_t reservedda; /* Byte offset 0xda, CSR Addr 0x5406d, Direction=N/A */ + uint8_t reserveddb; /* Byte offset 0xdb, CSR Addr 0x5406d, Direction=N/A */ + uint8_t reserveddc; /* Byte offset 0xdc, CSR Addr 0x5406e, Direction=N/A */ + uint8_t reserveddd; /* Byte offset 0xdd, CSR Addr 0x5406e, Direction=N/A */ + uint8_t reservedde; /* Byte offset 0xde, CSR Addr 0x5406f, Direction=N/A */ + uint8_t reserveddf; /* Byte offset 0xdf, CSR Addr 0x5406f, Direction=N/A */ + uint8_t reservede0; /* Byte offset 0xe0, CSR Addr 0x54070, Direction=N/A */ + uint8_t reservede1; /* Byte offset 0xe1, CSR Addr 0x54070, Direction=N/A */ + uint8_t reservede2; /* Byte offset 0xe2, CSR Addr 0x54071, Direction=N/A */ + uint8_t reservede3; /* Byte offset 0xe3, CSR Addr 0x54071, Direction=N/A */ + uint8_t reservede4; /* Byte offset 0xe4, CSR Addr 0x54072, Direction=N/A */ + uint8_t reservede5; /* Byte offset 0xe5, CSR Addr 0x54072, Direction=N/A */ + uint8_t reservede6; /* Byte offset 0xe6, CSR Addr 0x54073, Direction=N/A */ + uint8_t reservede7; /* Byte offset 0xe7, CSR Addr 0x54073, Direction=N/A */ + uint8_t reservede8; /* Byte offset 0xe8, CSR Addr 0x54074, Direction=N/A */ + uint8_t reservede9; /* Byte offset 0xe9, CSR Addr 0x54074, Direction=N/A */ + uint8_t reservedea; /* Byte offset 0xea, CSR Addr 0x54075, Direction=N/A */ + uint8_t reservedeb; /* Byte offset 0xeb, CSR Addr 0x54075, Direction=N/A */ + uint8_t reservedec; /* Byte offset 0xec, CSR Addr 0x54076, Direction=N/A */ + uint8_t reserveded; /* Byte offset 0xed, CSR Addr 0x54076, Direction=N/A */ + uint8_t reservedee; /* Byte offset 0xee, CSR Addr 0x54077, Direction=N/A */ + uint8_t reservedef; /* Byte offset 0xef, CSR Addr 0x54077, Direction=N/A */ + uint8_t reservedf0; /* Byte offset 0xf0, CSR Addr 0x54078, Direction=N/A */ + uint8_t reservedf1; /* Byte offset 0xf1, CSR Addr 0x54078, Direction=N/A */ + uint8_t reservedf2; /* Byte offset 0xf2, CSR Addr 0x54079, Direction=N/A */ + uint8_t reservedf3; /* Byte offset 0xf3, CSR Addr 0x54079, Direction=N/A */ + uint8_t reservedf4; /* Byte offset 0xf4, CSR Addr 0x5407a, Direction=N/A */ + uint8_t reservedf5; /* Byte offset 0xf5, CSR Addr 0x5407a, Direction=N/A */ + uint8_t reservedf6; /* Byte offset 0xf6, CSR Addr 0x5407b, Direction=N/A */ + uint8_t reservedf7; /* Byte offset 0xf7, CSR Addr 0x5407b, Direction=N/A */ + uint8_t reservedf8; /* Byte offset 0xf8, CSR Addr 0x5407c, Direction=N/A */ + uint8_t reservedf9; /* Byte offset 0xf9, CSR Addr 0x5407c, Direction=N/A */ + uint8_t reservedfa; /* Byte offset 0xfa, CSR Addr 0x5407d, Direction=N/A */ + uint8_t reservedfb; /* Byte offset 0xfb, CSR Addr 0x5407d, Direction=N/A */ + uint8_t reservedfc; /* Byte offset 0xfc, CSR Addr 0x5407e, Direction=N/A */ + uint8_t reservedfd; /* Byte offset 0xfd, CSR Addr 0x5407e, Direction=N/A */ + uint8_t reservedfe; /* Byte offset 0xfe, CSR Addr 0x5407f, Direction=N/A */ + uint8_t reservedff; /* Byte offset 0xff, CSR Addr 0x5407f, Direction=N/A */ + uint8_t reserved100; /* Byte offset 0x100, CSR Addr 0x54080, Direction=N/A */ + uint8_t reserved101; /* Byte offset 0x101, CSR Addr 0x54080, Direction=N/A */ + uint8_t reserved102; /* Byte offset 0x102, CSR Addr 0x54081, Direction=N/A */ + uint8_t reserved103; /* Byte offset 0x103, CSR Addr 0x54081, Direction=N/A */ + uint8_t reserved104; /* Byte offset 0x104, CSR Addr 0x54082, Direction=N/A */ + uint8_t reserved105; /* Byte offset 0x105, CSR Addr 0x54082, Direction=N/A */ + uint8_t reserved106; /* Byte offset 0x106, CSR Addr 0x54083, Direction=N/A */ + uint8_t reserved107; /* Byte offset 0x107, CSR Addr 0x54083, Direction=N/A */ + uint8_t reserved108; /* Byte offset 0x108, CSR Addr 0x54084, Direction=N/A */ + uint8_t reserved109; /* Byte offset 0x109, CSR Addr 0x54084, Direction=N/A */ + uint8_t reserved10a; /* Byte offset 0x10a, CSR Addr 0x54085, Direction=N/A */ + uint8_t reserved10b; /* Byte offset 0x10b, CSR Addr 0x54085, Direction=N/A */ + uint8_t reserved10c; /* Byte offset 0x10c, CSR Addr 0x54086, Direction=N/A */ + uint8_t reserved10d; /* Byte offset 0x10d, CSR Addr 0x54086, Direction=N/A */ + uint8_t reserved10e; /* Byte offset 0x10e, CSR Addr 0x54087, Direction=N/A */ + uint8_t reserved10f; /* Byte offset 0x10f, CSR Addr 0x54087, Direction=N/A */ + uint8_t reserved110; /* Byte offset 0x110, CSR Addr 0x54088, Direction=N/A */ + uint8_t reserved111; /* Byte offset 0x111, CSR Addr 0x54088, Direction=N/A */ + uint8_t reserved112; /* Byte offset 0x112, CSR Addr 0x54089, Direction=N/A */ + uint8_t reserved113; /* Byte offset 0x113, CSR Addr 0x54089, Direction=N/A */ + uint8_t reserved114; /* Byte offset 0x114, CSR Addr 0x5408a, Direction=N/A */ + uint8_t reserved115; /* Byte offset 0x115, CSR Addr 0x5408a, Direction=N/A */ + uint8_t reserved116; /* Byte offset 0x116, CSR Addr 0x5408b, Direction=N/A */ + uint8_t reserved117; /* Byte offset 0x117, CSR Addr 0x5408b, Direction=N/A */ + uint8_t reserved118; /* Byte offset 0x118, CSR Addr 0x5408c, Direction=N/A */ + uint8_t reserved119; /* Byte offset 0x119, CSR Addr 0x5408c, Direction=N/A */ + uint8_t reserved11a; /* Byte offset 0x11a, CSR Addr 0x5408d, Direction=N/A */ + uint8_t reserved11b; /* Byte offset 0x11b, CSR Addr 0x5408d, Direction=N/A */ + uint8_t reserved11c; /* Byte offset 0x11c, CSR Addr 0x5408e, Direction=N/A */ + uint8_t reserved11d; /* Byte offset 0x11d, CSR Addr 0x5408e, Direction=N/A */ + uint8_t reserved11e; /* Byte offset 0x11e, CSR Addr 0x5408f, Direction=N/A */ + uint8_t reserved11f; /* Byte offset 0x11f, CSR Addr 0x5408f, Direction=N/A */ + uint8_t reserved120; /* Byte offset 0x120, CSR Addr 0x54090, Direction=N/A */ + uint8_t reserved121; /* Byte offset 0x121, CSR Addr 0x54090, Direction=N/A */ + uint8_t reserved122; /* Byte offset 0x122, CSR Addr 0x54091, Direction=N/A */ + uint8_t reserved123; /* Byte offset 0x123, CSR Addr 0x54091, Direction=N/A */ + uint8_t reserved124; /* Byte offset 0x124, CSR Addr 0x54092, Direction=N/A */ + uint8_t reserved125; /* Byte offset 0x125, CSR Addr 0x54092, Direction=N/A */ + uint8_t reserved126; /* Byte offset 0x126, CSR Addr 0x54093, Direction=N/A */ + uint8_t reserved127; /* Byte offset 0x127, CSR Addr 0x54093, Direction=N/A */ + uint8_t reserved128; /* Byte offset 0x128, CSR Addr 0x54094, Direction=N/A */ + uint8_t reserved129; /* Byte offset 0x129, CSR Addr 0x54094, Direction=N/A */ + uint8_t reserved12a; /* Byte offset 0x12a, CSR Addr 0x54095, Direction=N/A */ + uint8_t reserved12b; /* Byte offset 0x12b, CSR Addr 0x54095, Direction=N/A */ + uint8_t reserved12c; /* Byte offset 0x12c, CSR Addr 0x54096, Direction=N/A */ + uint8_t reserved12d; /* Byte offset 0x12d, CSR Addr 0x54096, Direction=N/A */ + uint8_t reserved12e; /* Byte offset 0x12e, CSR Addr 0x54097, Direction=N/A */ + uint8_t reserved12f; /* Byte offset 0x12f, CSR Addr 0x54097, Direction=N/A */ + uint8_t reserved130; /* Byte offset 0x130, CSR Addr 0x54098, Direction=N/A */ + uint8_t reserved131; /* Byte offset 0x131, CSR Addr 0x54098, Direction=N/A */ + uint8_t reserved132; /* Byte offset 0x132, CSR Addr 0x54099, Direction=N/A */ + uint8_t reserved133; /* Byte offset 0x133, CSR Addr 0x54099, Direction=N/A */ + uint8_t reserved134; /* Byte offset 0x134, CSR Addr 0x5409a, Direction=N/A */ + uint8_t reserved135; /* Byte offset 0x135, CSR Addr 0x5409a, Direction=N/A */ + uint8_t reserved136; /* Byte offset 0x136, CSR Addr 0x5409b, Direction=N/A */ + uint8_t reserved137; /* Byte offset 0x137, CSR Addr 0x5409b, Direction=N/A */ + uint8_t reserved138; /* Byte offset 0x138, CSR Addr 0x5409c, Direction=N/A */ + uint8_t reserved139; /* Byte offset 0x139, CSR Addr 0x5409c, Direction=N/A */ + uint8_t reserved13a; /* Byte offset 0x13a, CSR Addr 0x5409d, Direction=N/A */ + uint8_t reserved13b; /* Byte offset 0x13b, CSR Addr 0x5409d, Direction=N/A */ + uint8_t reserved13c; /* Byte offset 0x13c, CSR Addr 0x5409e, Direction=N/A */ + uint8_t reserved13d; /* Byte offset 0x13d, CSR Addr 0x5409e, Direction=N/A */ + uint8_t reserved13e; /* Byte offset 0x13e, CSR Addr 0x5409f, Direction=N/A */ + uint8_t reserved13f; /* Byte offset 0x13f, CSR Addr 0x5409f, Direction=N/A */ + uint8_t reserved140; /* Byte offset 0x140, CSR Addr 0x540a0, Direction=N/A */ + uint8_t reserved141; /* Byte offset 0x141, CSR Addr 0x540a0, Direction=N/A */ + uint8_t reserved142; /* Byte offset 0x142, CSR Addr 0x540a1, Direction=N/A */ + uint8_t reserved143; /* Byte offset 0x143, CSR Addr 0x540a1, Direction=N/A */ + uint8_t reserved144; /* Byte offset 0x144, CSR Addr 0x540a2, Direction=N/A */ + uint8_t reserved145; /* Byte offset 0x145, CSR Addr 0x540a2, Direction=N/A */ + uint8_t reserved146; /* Byte offset 0x146, CSR Addr 0x540a3, Direction=N/A */ + uint8_t reserved147; /* Byte offset 0x147, CSR Addr 0x540a3, Direction=N/A */ + uint8_t reserved148; /* Byte offset 0x148, CSR Addr 0x540a4, Direction=N/A */ + uint8_t reserved149; /* Byte offset 0x149, CSR Addr 0x540a4, Direction=N/A */ + uint8_t reserved14a; /* Byte offset 0x14a, CSR Addr 0x540a5, Direction=N/A */ + uint8_t reserved14b; /* Byte offset 0x14b, CSR Addr 0x540a5, Direction=N/A */ + uint8_t reserved14c; /* Byte offset 0x14c, CSR Addr 0x540a6, Direction=N/A */ + uint8_t reserved14d; /* Byte offset 0x14d, CSR Addr 0x540a6, Direction=N/A */ + uint8_t reserved14e; /* Byte offset 0x14e, CSR Addr 0x540a7, Direction=N/A */ + uint8_t reserved14f; /* Byte offset 0x14f, CSR Addr 0x540a7, Direction=N/A */ + uint8_t reserved150; /* Byte offset 0x150, CSR Addr 0x540a8, Direction=N/A */ + uint8_t reserved151; /* Byte offset 0x151, CSR Addr 0x540a8, Direction=N/A */ + uint8_t reserved152; /* Byte offset 0x152, CSR Addr 0x540a9, Direction=N/A */ + uint8_t reserved153; /* Byte offset 0x153, CSR Addr 0x540a9, Direction=N/A */ + uint8_t reserved154; /* Byte offset 0x154, CSR Addr 0x540aa, Direction=N/A */ + uint8_t reserved155; /* Byte offset 0x155, CSR Addr 0x540aa, Direction=N/A */ + uint8_t reserved156; /* Byte offset 0x156, CSR Addr 0x540ab, Direction=N/A */ + uint8_t reserved157; /* Byte offset 0x157, CSR Addr 0x540ab, Direction=N/A */ + uint8_t reserved158; /* Byte offset 0x158, CSR Addr 0x540ac, Direction=N/A */ + uint8_t reserved159; /* Byte offset 0x159, CSR Addr 0x540ac, Direction=N/A */ + uint8_t reserved15a; /* Byte offset 0x15a, CSR Addr 0x540ad, Direction=N/A */ + uint8_t reserved15b; /* Byte offset 0x15b, CSR Addr 0x540ad, Direction=N/A */ + uint8_t reserved15c; /* Byte offset 0x15c, CSR Addr 0x540ae, Direction=N/A */ + uint8_t reserved15d; /* Byte offset 0x15d, CSR Addr 0x540ae, Direction=N/A */ + uint8_t reserved15e; /* Byte offset 0x15e, CSR Addr 0x540af, Direction=N/A */ + uint8_t reserved15f; /* Byte offset 0x15f, CSR Addr 0x540af, Direction=N/A */ + uint8_t reserved160; /* Byte offset 0x160, CSR Addr 0x540b0, Direction=N/A */ + uint8_t reserved161; /* Byte offset 0x161, CSR Addr 0x540b0, Direction=N/A */ + uint8_t reserved162; /* Byte offset 0x162, CSR Addr 0x540b1, Direction=N/A */ + uint8_t reserved163; /* Byte offset 0x163, CSR Addr 0x540b1, Direction=N/A */ + uint8_t reserved164; /* Byte offset 0x164, CSR Addr 0x540b2, Direction=N/A */ + uint8_t reserved165; /* Byte offset 0x165, CSR Addr 0x540b2, Direction=N/A */ + uint8_t reserved166; /* Byte offset 0x166, CSR Addr 0x540b3, Direction=N/A */ + uint8_t reserved167; /* Byte offset 0x167, CSR Addr 0x540b3, Direction=N/A */ + uint8_t reserved168; /* Byte offset 0x168, CSR Addr 0x540b4, Direction=N/A */ + uint8_t reserved169; /* Byte offset 0x169, CSR Addr 0x540b4, Direction=N/A */ + uint8_t reserved16a; /* Byte offset 0x16a, CSR Addr 0x540b5, Direction=N/A */ + uint8_t reserved16b; /* Byte offset 0x16b, CSR Addr 0x540b5, Direction=N/A */ + uint8_t reserved16c; /* Byte offset 0x16c, CSR Addr 0x540b6, Direction=N/A */ + uint8_t reserved16d; /* Byte offset 0x16d, CSR Addr 0x540b6, Direction=N/A */ + uint8_t reserved16e; /* Byte offset 0x16e, CSR Addr 0x540b7, Direction=N/A */ + uint8_t reserved16f; /* Byte offset 0x16f, CSR Addr 0x540b7, Direction=N/A */ + uint8_t reserved170; /* Byte offset 0x170, CSR Addr 0x540b8, Direction=N/A */ + uint8_t reserved171; /* Byte offset 0x171, CSR Addr 0x540b8, Direction=N/A */ + uint8_t reserved172; /* Byte offset 0x172, CSR Addr 0x540b9, Direction=N/A */ + uint8_t reserved173; /* Byte offset 0x173, CSR Addr 0x540b9, Direction=N/A */ + uint8_t reserved174; /* Byte offset 0x174, CSR Addr 0x540ba, Direction=N/A */ + uint8_t reserved175; /* Byte offset 0x175, CSR Addr 0x540ba, Direction=N/A */ + uint8_t reserved176; /* Byte offset 0x176, CSR Addr 0x540bb, Direction=N/A */ + uint8_t reserved177; /* Byte offset 0x177, CSR Addr 0x540bb, Direction=N/A */ + uint8_t reserved178; /* Byte offset 0x178, CSR Addr 0x540bc, Direction=N/A */ + uint8_t reserved179; /* Byte offset 0x179, CSR Addr 0x540bc, Direction=N/A */ + uint8_t reserved17a; /* Byte offset 0x17a, CSR Addr 0x540bd, Direction=N/A */ + uint8_t reserved17b; /* Byte offset 0x17b, CSR Addr 0x540bd, Direction=N/A */ + uint8_t reserved17c; /* Byte offset 0x17c, CSR Addr 0x540be, Direction=N/A */ + uint8_t reserved17d; /* Byte offset 0x17d, CSR Addr 0x540be, Direction=N/A */ + uint8_t reserved17e; /* Byte offset 0x17e, CSR Addr 0x540bf, Direction=N/A */ + uint8_t reserved17f; /* Byte offset 0x17f, CSR Addr 0x540bf, Direction=N/A */ + uint8_t reserved180; /* Byte offset 0x180, CSR Addr 0x540c0, Direction=N/A */ + uint8_t reserved181; /* Byte offset 0x181, CSR Addr 0x540c0, Direction=N/A */ + uint8_t reserved182; /* Byte offset 0x182, CSR Addr 0x540c1, Direction=N/A */ + uint8_t reserved183; /* Byte offset 0x183, CSR Addr 0x540c1, Direction=N/A */ + uint8_t reserved184; /* Byte offset 0x184, CSR Addr 0x540c2, Direction=N/A */ + uint8_t reserved185; /* Byte offset 0x185, CSR Addr 0x540c2, Direction=N/A */ + uint8_t reserved186; /* Byte offset 0x186, CSR Addr 0x540c3, Direction=N/A */ + uint8_t reserved187; /* Byte offset 0x187, CSR Addr 0x540c3, Direction=N/A */ + uint8_t reserved188; /* Byte offset 0x188, CSR Addr 0x540c4, Direction=N/A */ + uint8_t reserved189; /* Byte offset 0x189, CSR Addr 0x540c4, Direction=N/A */ + uint8_t reserved18a; /* Byte offset 0x18a, CSR Addr 0x540c5, Direction=N/A */ + uint8_t reserved18b; /* Byte offset 0x18b, CSR Addr 0x540c5, Direction=N/A */ + uint8_t reserved18c; /* Byte offset 0x18c, CSR Addr 0x540c6, Direction=N/A */ + uint8_t reserved18d; /* Byte offset 0x18d, CSR Addr 0x540c6, Direction=N/A */ + uint8_t reserved18e; /* Byte offset 0x18e, CSR Addr 0x540c7, Direction=N/A */ + uint8_t reserved18f; /* Byte offset 0x18f, CSR Addr 0x540c7, Direction=N/A */ + uint8_t reserved190; /* Byte offset 0x190, CSR Addr 0x540c8, Direction=N/A */ + uint8_t reserved191; /* Byte offset 0x191, CSR Addr 0x540c8, Direction=N/A */ + uint8_t reserved192; /* Byte offset 0x192, CSR Addr 0x540c9, Direction=N/A */ + uint8_t reserved193; /* Byte offset 0x193, CSR Addr 0x540c9, Direction=N/A */ + uint8_t reserved194; /* Byte offset 0x194, CSR Addr 0x540ca, Direction=N/A */ + uint8_t reserved195; /* Byte offset 0x195, CSR Addr 0x540ca, Direction=N/A */ + uint8_t reserved196; /* Byte offset 0x196, CSR Addr 0x540cb, Direction=N/A */ + uint8_t reserved197; /* Byte offset 0x197, CSR Addr 0x540cb, Direction=N/A */ + uint8_t reserved198; /* Byte offset 0x198, CSR Addr 0x540cc, Direction=N/A */ + uint8_t reserved199; /* Byte offset 0x199, CSR Addr 0x540cc, Direction=N/A */ + uint8_t reserved19a; /* Byte offset 0x19a, CSR Addr 0x540cd, Direction=N/A */ + uint8_t reserved19b; /* Byte offset 0x19b, CSR Addr 0x540cd, Direction=N/A */ + uint8_t reserved19c; /* Byte offset 0x19c, CSR Addr 0x540ce, Direction=N/A */ + uint8_t reserved19d; /* Byte offset 0x19d, CSR Addr 0x540ce, Direction=N/A */ + uint8_t reserved19e; /* Byte offset 0x19e, CSR Addr 0x540cf, Direction=N/A */ + uint8_t reserved19f; /* Byte offset 0x19f, CSR Addr 0x540cf, Direction=N/A */ + uint8_t reserved1a0; /* Byte offset 0x1a0, CSR Addr 0x540d0, Direction=N/A */ + uint8_t reserved1a1; /* Byte offset 0x1a1, CSR Addr 0x540d0, Direction=N/A */ + uint8_t reserved1a2; /* Byte offset 0x1a2, CSR Addr 0x540d1, Direction=N/A */ + uint8_t reserved1a3; /* Byte offset 0x1a3, CSR Addr 0x540d1, Direction=N/A */ + uint8_t reserved1a4; /* Byte offset 0x1a4, CSR Addr 0x540d2, Direction=N/A */ + uint8_t reserved1a5; /* Byte offset 0x1a5, CSR Addr 0x540d2, Direction=N/A */ + uint8_t reserved1a6; /* Byte offset 0x1a6, CSR Addr 0x540d3, Direction=N/A */ + uint8_t reserved1a7; /* Byte offset 0x1a7, CSR Addr 0x540d3, Direction=N/A */ + uint8_t reserved1a8; /* Byte offset 0x1a8, CSR Addr 0x540d4, Direction=N/A */ + uint8_t reserved1a9; /* Byte offset 0x1a9, CSR Addr 0x540d4, Direction=N/A */ + uint8_t reserved1aa; /* Byte offset 0x1aa, CSR Addr 0x540d5, Direction=N/A */ + uint8_t reserved1ab; /* Byte offset 0x1ab, CSR Addr 0x540d5, Direction=N/A */ + uint8_t reserved1ac; /* Byte offset 0x1ac, CSR Addr 0x540d6, Direction=N/A */ + uint8_t reserved1ad; /* Byte offset 0x1ad, CSR Addr 0x540d6, Direction=N/A */ + uint8_t reserved1ae; /* Byte offset 0x1ae, CSR Addr 0x540d7, Direction=N/A */ + uint8_t reserved1af; /* Byte offset 0x1af, CSR Addr 0x540d7, Direction=N/A */ + uint8_t reserved1b0; /* Byte offset 0x1b0, CSR Addr 0x540d8, Direction=N/A */ + uint8_t reserved1b1; /* Byte offset 0x1b1, CSR Addr 0x540d8, Direction=N/A */ + uint8_t reserved1b2; /* Byte offset 0x1b2, CSR Addr 0x540d9, Direction=N/A */ + uint8_t reserved1b3; /* Byte offset 0x1b3, CSR Addr 0x540d9, Direction=N/A */ + uint8_t reserved1b4; /* Byte offset 0x1b4, CSR Addr 0x540da, Direction=N/A */ + uint8_t reserved1b5; /* Byte offset 0x1b5, CSR Addr 0x540da, Direction=N/A */ + uint8_t reserved1b6; /* Byte offset 0x1b6, CSR Addr 0x540db, Direction=N/A */ + uint8_t reserved1b7; /* Byte offset 0x1b7, CSR Addr 0x540db, Direction=N/A */ + uint8_t reserved1b8; /* Byte offset 0x1b8, CSR Addr 0x540dc, Direction=N/A */ + uint8_t reserved1b9; /* Byte offset 0x1b9, CSR Addr 0x540dc, Direction=N/A */ + uint8_t reserved1ba; /* Byte offset 0x1ba, CSR Addr 0x540dd, Direction=N/A */ + uint8_t reserved1bb; /* Byte offset 0x1bb, CSR Addr 0x540dd, Direction=N/A */ + uint8_t reserved1bc; /* Byte offset 0x1bc, CSR Addr 0x540de, Direction=N/A */ + uint8_t reserved1bd; /* Byte offset 0x1bd, CSR Addr 0x540de, Direction=N/A */ + uint8_t reserved1be; /* Byte offset 0x1be, CSR Addr 0x540df, Direction=N/A */ + uint8_t reserved1bf; /* Byte offset 0x1bf, CSR Addr 0x540df, Direction=N/A */ + uint8_t reserved1c0; /* Byte offset 0x1c0, CSR Addr 0x540e0, Direction=N/A */ + uint8_t reserved1c1; /* Byte offset 0x1c1, CSR Addr 0x540e0, Direction=N/A */ + uint8_t reserved1c2; /* Byte offset 0x1c2, CSR Addr 0x540e1, Direction=N/A */ + uint8_t reserved1c3; /* Byte offset 0x1c3, CSR Addr 0x540e1, Direction=N/A */ + uint8_t reserved1c4; /* Byte offset 0x1c4, CSR Addr 0x540e2, Direction=N/A */ + uint8_t reserved1c5; /* Byte offset 0x1c5, CSR Addr 0x540e2, Direction=N/A */ + uint8_t reserved1c6; /* Byte offset 0x1c6, CSR Addr 0x540e3, Direction=N/A */ + uint8_t reserved1c7; /* Byte offset 0x1c7, CSR Addr 0x540e3, Direction=N/A */ + uint8_t reserved1c8; /* Byte offset 0x1c8, CSR Addr 0x540e4, Direction=N/A */ + uint8_t reserved1c9; /* Byte offset 0x1c9, CSR Addr 0x540e4, Direction=N/A */ + uint8_t reserved1ca; /* Byte offset 0x1ca, CSR Addr 0x540e5, Direction=N/A */ + uint8_t reserved1cb; /* Byte offset 0x1cb, CSR Addr 0x540e5, Direction=N/A */ + uint8_t reserved1cc; /* Byte offset 0x1cc, CSR Addr 0x540e6, Direction=N/A */ + uint8_t reserved1cd; /* Byte offset 0x1cd, CSR Addr 0x540e6, Direction=N/A */ + uint8_t reserved1ce; /* Byte offset 0x1ce, CSR Addr 0x540e7, Direction=N/A */ + uint8_t reserved1cf; /* Byte offset 0x1cf, CSR Addr 0x540e7, Direction=N/A */ + uint8_t reserved1d0; /* Byte offset 0x1d0, CSR Addr 0x540e8, Direction=N/A */ + uint8_t reserved1d1; /* Byte offset 0x1d1, CSR Addr 0x540e8, Direction=N/A */ + uint8_t reserved1d2; /* Byte offset 0x1d2, CSR Addr 0x540e9, Direction=N/A */ + uint8_t reserved1d3; /* Byte offset 0x1d3, CSR Addr 0x540e9, Direction=N/A */ + uint8_t reserved1d4; /* Byte offset 0x1d4, CSR Addr 0x540ea, Direction=N/A */ + uint8_t reserved1d5; /* Byte offset 0x1d5, CSR Addr 0x540ea, Direction=N/A */ + uint8_t reserved1d6; /* Byte offset 0x1d6, CSR Addr 0x540eb, Direction=N/A */ + uint8_t reserved1d7; /* Byte offset 0x1d7, CSR Addr 0x540eb, Direction=N/A */ + uint8_t reserved1d8; /* Byte offset 0x1d8, CSR Addr 0x540ec, Direction=N/A */ + uint8_t reserved1d9; /* Byte offset 0x1d9, CSR Addr 0x540ec, Direction=N/A */ + uint8_t reserved1da; /* Byte offset 0x1da, CSR Addr 0x540ed, Direction=N/A */ + uint8_t reserved1db; /* Byte offset 0x1db, CSR Addr 0x540ed, Direction=N/A */ + uint8_t reserved1dc; /* Byte offset 0x1dc, CSR Addr 0x540ee, Direction=N/A */ + uint8_t reserved1dd; /* Byte offset 0x1dd, CSR Addr 0x540ee, Direction=N/A */ + uint8_t reserved1de; /* Byte offset 0x1de, CSR Addr 0x540ef, Direction=N/A */ + uint8_t reserved1df; /* Byte offset 0x1df, CSR Addr 0x540ef, Direction=N/A */ + uint8_t reserved1e0; /* Byte offset 0x1e0, CSR Addr 0x540f0, Direction=N/A */ + uint8_t reserved1e1; /* Byte offset 0x1e1, CSR Addr 0x540f0, Direction=N/A */ + uint8_t reserved1e2; /* Byte offset 0x1e2, CSR Addr 0x540f1, Direction=N/A */ + uint8_t reserved1e3; /* Byte offset 0x1e3, CSR Addr 0x540f1, Direction=N/A */ + uint8_t reserved1e4; /* Byte offset 0x1e4, CSR Addr 0x540f2, Direction=N/A */ + uint8_t reserved1e5; /* Byte offset 0x1e5, CSR Addr 0x540f2, Direction=N/A */ + uint8_t reserved1e6; /* Byte offset 0x1e6, CSR Addr 0x540f3, Direction=N/A */ + uint8_t reserved1e7; /* Byte offset 0x1e7, CSR Addr 0x540f3, Direction=N/A */ + uint8_t reserved1e8; /* Byte offset 0x1e8, CSR Addr 0x540f4, Direction=N/A */ + uint8_t reserved1e9; /* Byte offset 0x1e9, CSR Addr 0x540f4, Direction=N/A */ + uint8_t reserved1ea; /* Byte offset 0x1ea, CSR Addr 0x540f5, Direction=N/A */ + uint8_t reserved1eb; /* Byte offset 0x1eb, CSR Addr 0x540f5, Direction=N/A */ + uint8_t reserved1ec; /* Byte offset 0x1ec, CSR Addr 0x540f6, Direction=N/A */ + uint8_t reserved1ed; /* Byte offset 0x1ed, CSR Addr 0x540f6, Direction=N/A */ + uint8_t reserved1ee; /* Byte offset 0x1ee, CSR Addr 0x540f7, Direction=N/A */ + uint8_t reserved1ef; /* Byte offset 0x1ef, CSR Addr 0x540f7, Direction=N/A */ + uint8_t reserved1f0; /* Byte offset 0x1f0, CSR Addr 0x540f8, Direction=N/A */ + uint8_t reserved1f1; /* Byte offset 0x1f1, CSR Addr 0x540f8, Direction=N/A */ + uint8_t reserved1f2; /* Byte offset 0x1f2, CSR Addr 0x540f9, Direction=N/A */ + uint8_t reserved1f3; /* Byte offset 0x1f3, CSR Addr 0x540f9, Direction=N/A */ + uint8_t reserved1f4; /* Byte offset 0x1f4, CSR Addr 0x540fa, Direction=N/A */ + uint8_t reserved1f5; /* Byte offset 0x1f5, CSR Addr 0x540fa, Direction=N/A */ + uint8_t reserved1f6; /* Byte offset 0x1f6, CSR Addr 0x540fb, Direction=N/A */ + uint8_t reserved1f7; /* Byte offset 0x1f7, CSR Addr 0x540fb, Direction=N/A */ + uint8_t reserved1f8; /* Byte offset 0x1f8, CSR Addr 0x540fc, Direction=N/A */ + uint8_t reserved1f9; /* Byte offset 0x1f9, CSR Addr 0x540fc, Direction=N/A */ + uint8_t reserved1fa; /* Byte offset 0x1fa, CSR Addr 0x540fd, Direction=N/A */ + uint8_t reserved1fb; /* Byte offset 0x1fb, CSR Addr 0x540fd, Direction=N/A */ + uint8_t reserved1fc; /* Byte offset 0x1fc, CSR Addr 0x540fe, Direction=N/A */ + uint8_t reserved1fd; /* Byte offset 0x1fd, CSR Addr 0x540fe, Direction=N/A */ + uint8_t reserved1fe; /* Byte offset 0x1fe, CSR Addr 0x540ff, Direction=N/A */ + uint8_t reserved1ff; /* Byte offset 0x1ff, CSR Addr 0x540ff, Direction=N/A */ + uint8_t reserved200; /* Byte offset 0x200, CSR Addr 0x54100, Direction=N/A */ + uint8_t reserved201; /* Byte offset 0x201, CSR Addr 0x54100, Direction=N/A */ + uint8_t reserved202; /* Byte offset 0x202, CSR Addr 0x54101, Direction=N/A */ + uint8_t reserved203; /* Byte offset 0x203, CSR Addr 0x54101, Direction=N/A */ + uint8_t reserved204; /* Byte offset 0x204, CSR Addr 0x54102, Direction=N/A */ + uint8_t reserved205; /* Byte offset 0x205, CSR Addr 0x54102, Direction=N/A */ + uint8_t reserved206; /* Byte offset 0x206, CSR Addr 0x54103, Direction=N/A */ + uint8_t reserved207; /* Byte offset 0x207, CSR Addr 0x54103, Direction=N/A */ + uint8_t reserved208; /* Byte offset 0x208, CSR Addr 0x54104, Direction=N/A */ + uint8_t reserved209; /* Byte offset 0x209, CSR Addr 0x54104, Direction=N/A */ + uint8_t reserved20a; /* Byte offset 0x20a, CSR Addr 0x54105, Direction=N/A */ + uint8_t reserved20b; /* Byte offset 0x20b, CSR Addr 0x54105, Direction=N/A */ + uint8_t reserved20c; /* Byte offset 0x20c, CSR Addr 0x54106, Direction=N/A */ + uint8_t reserved20d; /* Byte offset 0x20d, CSR Addr 0x54106, Direction=N/A */ + uint8_t reserved20e; /* Byte offset 0x20e, CSR Addr 0x54107, Direction=N/A */ + uint8_t reserved20f; /* Byte offset 0x20f, CSR Addr 0x54107, Direction=N/A */ + uint8_t reserved210; /* Byte offset 0x210, CSR Addr 0x54108, Direction=N/A */ + uint8_t reserved211; /* Byte offset 0x211, CSR Addr 0x54108, Direction=N/A */ + uint8_t reserved212; /* Byte offset 0x212, CSR Addr 0x54109, Direction=N/A */ + uint8_t reserved213; /* Byte offset 0x213, CSR Addr 0x54109, Direction=N/A */ + uint8_t reserved214; /* Byte offset 0x214, CSR Addr 0x5410a, Direction=N/A */ + uint8_t reserved215; /* Byte offset 0x215, CSR Addr 0x5410a, Direction=N/A */ + uint8_t reserved216; /* Byte offset 0x216, CSR Addr 0x5410b, Direction=N/A */ + uint8_t reserved217; /* Byte offset 0x217, CSR Addr 0x5410b, Direction=N/A */ + uint8_t reserved218; /* Byte offset 0x218, CSR Addr 0x5410c, Direction=N/A */ + uint8_t reserved219; /* Byte offset 0x219, CSR Addr 0x5410c, Direction=N/A */ + uint8_t reserved21a; /* Byte offset 0x21a, CSR Addr 0x5410d, Direction=N/A */ + uint8_t reserved21b; /* Byte offset 0x21b, CSR Addr 0x5410d, Direction=N/A */ + uint8_t reserved21c; /* Byte offset 0x21c, CSR Addr 0x5410e, Direction=N/A */ + uint8_t reserved21d; /* Byte offset 0x21d, CSR Addr 0x5410e, Direction=N/A */ + uint8_t reserved21e; /* Byte offset 0x21e, CSR Addr 0x5410f, Direction=N/A */ + uint8_t reserved21f; /* Byte offset 0x21f, CSR Addr 0x5410f, Direction=N/A */ + uint8_t reserved220; /* Byte offset 0x220, CSR Addr 0x54110, Direction=N/A */ + uint8_t reserved221; /* Byte offset 0x221, CSR Addr 0x54110, Direction=N/A */ + uint8_t reserved222; /* Byte offset 0x222, CSR Addr 0x54111, Direction=N/A */ + uint8_t reserved223; /* Byte offset 0x223, CSR Addr 0x54111, Direction=N/A */ + uint8_t reserved224; /* Byte offset 0x224, CSR Addr 0x54112, Direction=N/A */ + uint8_t reserved225; /* Byte offset 0x225, CSR Addr 0x54112, Direction=N/A */ + uint8_t reserved226; /* Byte offset 0x226, CSR Addr 0x54113, Direction=N/A */ + uint8_t reserved227; /* Byte offset 0x227, CSR Addr 0x54113, Direction=N/A */ + uint8_t reserved228; /* Byte offset 0x228, CSR Addr 0x54114, Direction=N/A */ + uint8_t reserved229; /* Byte offset 0x229, CSR Addr 0x54114, Direction=N/A */ + uint8_t reserved22a; /* Byte offset 0x22a, CSR Addr 0x54115, Direction=N/A */ + uint8_t reserved22b; /* Byte offset 0x22b, CSR Addr 0x54115, Direction=N/A */ + uint8_t reserved22c; /* Byte offset 0x22c, CSR Addr 0x54116, Direction=N/A */ + uint8_t reserved22d; /* Byte offset 0x22d, CSR Addr 0x54116, Direction=N/A */ + uint8_t reserved22e; /* Byte offset 0x22e, CSR Addr 0x54117, Direction=N/A */ + uint8_t reserved22f; /* Byte offset 0x22f, CSR Addr 0x54117, Direction=N/A */ + uint8_t reserved230; /* Byte offset 0x230, CSR Addr 0x54118, Direction=N/A */ + uint8_t reserved231; /* Byte offset 0x231, CSR Addr 0x54118, Direction=N/A */ + uint8_t reserved232; /* Byte offset 0x232, CSR Addr 0x54119, Direction=N/A */ + uint8_t reserved233; /* Byte offset 0x233, CSR Addr 0x54119, Direction=N/A */ + uint8_t reserved234; /* Byte offset 0x234, CSR Addr 0x5411a, Direction=N/A */ + uint8_t reserved235; /* Byte offset 0x235, CSR Addr 0x5411a, Direction=N/A */ + uint8_t reserved236; /* Byte offset 0x236, CSR Addr 0x5411b, Direction=N/A */ + uint8_t reserved237; /* Byte offset 0x237, CSR Addr 0x5411b, Direction=N/A */ + uint8_t reserved238; /* Byte offset 0x238, CSR Addr 0x5411c, Direction=N/A */ + uint8_t reserved239; /* Byte offset 0x239, CSR Addr 0x5411c, Direction=N/A */ + uint8_t reserved23a; /* Byte offset 0x23a, CSR Addr 0x5411d, Direction=N/A */ + uint8_t reserved23b; /* Byte offset 0x23b, CSR Addr 0x5411d, Direction=N/A */ + uint8_t reserved23c; /* Byte offset 0x23c, CSR Addr 0x5411e, Direction=N/A */ + uint8_t reserved23d; /* Byte offset 0x23d, CSR Addr 0x5411e, Direction=N/A */ + uint8_t reserved23e; /* Byte offset 0x23e, CSR Addr 0x5411f, Direction=N/A */ + uint8_t reserved23f; /* Byte offset 0x23f, CSR Addr 0x5411f, Direction=N/A */ + uint8_t reserved240; /* Byte offset 0x240, CSR Addr 0x54120, Direction=N/A */ + uint8_t reserved241; /* Byte offset 0x241, CSR Addr 0x54120, Direction=N/A */ + uint8_t reserved242; /* Byte offset 0x242, CSR Addr 0x54121, Direction=N/A */ + uint8_t reserved243; /* Byte offset 0x243, CSR Addr 0x54121, Direction=N/A */ + uint8_t reserved244; /* Byte offset 0x244, CSR Addr 0x54122, Direction=N/A */ + uint8_t reserved245; /* Byte offset 0x245, CSR Addr 0x54122, Direction=N/A */ + uint8_t reserved246; /* Byte offset 0x246, CSR Addr 0x54123, Direction=N/A */ + uint8_t reserved247; /* Byte offset 0x247, CSR Addr 0x54123, Direction=N/A */ + uint8_t reserved248; /* Byte offset 0x248, CSR Addr 0x54124, Direction=N/A */ + uint8_t reserved249; /* Byte offset 0x249, CSR Addr 0x54124, Direction=N/A */ + uint8_t reserved24a; /* Byte offset 0x24a, CSR Addr 0x54125, Direction=N/A */ + uint8_t reserved24b; /* Byte offset 0x24b, CSR Addr 0x54125, Direction=N/A */ + uint8_t reserved24c; /* Byte offset 0x24c, CSR Addr 0x54126, Direction=N/A */ + uint8_t reserved24d; /* Byte offset 0x24d, CSR Addr 0x54126, Direction=N/A */ + uint8_t reserved24e; /* Byte offset 0x24e, CSR Addr 0x54127, Direction=N/A */ + uint8_t reserved24f; /* Byte offset 0x24f, CSR Addr 0x54127, Direction=N/A */ + uint8_t reserved250; /* Byte offset 0x250, CSR Addr 0x54128, Direction=N/A */ + uint8_t reserved251; /* Byte offset 0x251, CSR Addr 0x54128, Direction=N/A */ + uint8_t reserved252; /* Byte offset 0x252, CSR Addr 0x54129, Direction=N/A */ + uint8_t reserved253; /* Byte offset 0x253, CSR Addr 0x54129, Direction=N/A */ + uint8_t reserved254; /* Byte offset 0x254, CSR Addr 0x5412a, Direction=N/A */ + uint8_t reserved255; /* Byte offset 0x255, CSR Addr 0x5412a, Direction=N/A */ + uint8_t reserved256; /* Byte offset 0x256, CSR Addr 0x5412b, Direction=N/A */ + uint8_t reserved257; /* Byte offset 0x257, CSR Addr 0x5412b, Direction=N/A */ + uint8_t reserved258; /* Byte offset 0x258, CSR Addr 0x5412c, Direction=N/A */ + uint8_t reserved259; /* Byte offset 0x259, CSR Addr 0x5412c, Direction=N/A */ + uint8_t reserved25a; /* Byte offset 0x25a, CSR Addr 0x5412d, Direction=N/A */ + uint8_t reserved25b; /* Byte offset 0x25b, CSR Addr 0x5412d, Direction=N/A */ + uint8_t reserved25c; /* Byte offset 0x25c, CSR Addr 0x5412e, Direction=N/A */ + uint8_t reserved25d; /* Byte offset 0x25d, CSR Addr 0x5412e, Direction=N/A */ + uint8_t reserved25e; /* Byte offset 0x25e, CSR Addr 0x5412f, Direction=N/A */ + uint8_t reserved25f; /* Byte offset 0x25f, CSR Addr 0x5412f, Direction=N/A */ + uint8_t reserved260; /* Byte offset 0x260, CSR Addr 0x54130, Direction=N/A */ + uint8_t reserved261; /* Byte offset 0x261, CSR Addr 0x54130, Direction=N/A */ + uint8_t reserved262; /* Byte offset 0x262, CSR Addr 0x54131, Direction=N/A */ + uint8_t reserved263; /* Byte offset 0x263, CSR Addr 0x54131, Direction=N/A */ + uint8_t reserved264; /* Byte offset 0x264, CSR Addr 0x54132, Direction=N/A */ + uint8_t reserved265; /* Byte offset 0x265, CSR Addr 0x54132, Direction=N/A */ + uint8_t reserved266; /* Byte offset 0x266, CSR Addr 0x54133, Direction=N/A */ + uint8_t reserved267; /* Byte offset 0x267, CSR Addr 0x54133, Direction=N/A */ + uint8_t reserved268; /* Byte offset 0x268, CSR Addr 0x54134, Direction=N/A */ + uint8_t reserved269; /* Byte offset 0x269, CSR Addr 0x54134, Direction=N/A */ + uint8_t reserved26a; /* Byte offset 0x26a, CSR Addr 0x54135, Direction=N/A */ + uint8_t reserved26b; /* Byte offset 0x26b, CSR Addr 0x54135, Direction=N/A */ + uint8_t reserved26c; /* Byte offset 0x26c, CSR Addr 0x54136, Direction=N/A */ + uint8_t reserved26d; /* Byte offset 0x26d, CSR Addr 0x54136, Direction=N/A */ + uint8_t reserved26e; /* Byte offset 0x26e, CSR Addr 0x54137, Direction=N/A */ + uint8_t reserved26f; /* Byte offset 0x26f, CSR Addr 0x54137, Direction=N/A */ + uint8_t reserved270; /* Byte offset 0x270, CSR Addr 0x54138, Direction=N/A */ + uint8_t reserved271; /* Byte offset 0x271, CSR Addr 0x54138, Direction=N/A */ + uint8_t reserved272; /* Byte offset 0x272, CSR Addr 0x54139, Direction=N/A */ + uint8_t reserved273; /* Byte offset 0x273, CSR Addr 0x54139, Direction=N/A */ + uint8_t reserved274; /* Byte offset 0x274, CSR Addr 0x5413a, Direction=N/A */ + uint8_t reserved275; /* Byte offset 0x275, CSR Addr 0x5413a, Direction=N/A */ + uint8_t reserved276; /* Byte offset 0x276, CSR Addr 0x5413b, Direction=N/A */ + uint8_t reserved277; /* Byte offset 0x277, CSR Addr 0x5413b, Direction=N/A */ + uint8_t reserved278; /* Byte offset 0x278, CSR Addr 0x5413c, Direction=N/A */ + uint8_t reserved279; /* Byte offset 0x279, CSR Addr 0x5413c, Direction=N/A */ + uint8_t reserved27a; /* Byte offset 0x27a, CSR Addr 0x5413d, Direction=N/A */ + uint8_t reserved27b; /* Byte offset 0x27b, CSR Addr 0x5413d, Direction=N/A */ + uint8_t reserved27c; /* Byte offset 0x27c, CSR Addr 0x5413e, Direction=N/A */ + uint8_t reserved27d; /* Byte offset 0x27d, CSR Addr 0x5413e, Direction=N/A */ + uint8_t reserved27e; /* Byte offset 0x27e, CSR Addr 0x5413f, Direction=N/A */ + uint8_t reserved27f; /* Byte offset 0x27f, CSR Addr 0x5413f, Direction=N/A */ + uint8_t reserved280; /* Byte offset 0x280, CSR Addr 0x54140, Direction=N/A */ + uint8_t reserved281; /* Byte offset 0x281, CSR Addr 0x54140, Direction=N/A */ + uint8_t reserved282; /* Byte offset 0x282, CSR Addr 0x54141, Direction=N/A */ + uint8_t reserved283; /* Byte offset 0x283, CSR Addr 0x54141, Direction=N/A */ + uint8_t reserved284; /* Byte offset 0x284, CSR Addr 0x54142, Direction=N/A */ + uint8_t reserved285; /* Byte offset 0x285, CSR Addr 0x54142, Direction=N/A */ + uint8_t reserved286; /* Byte offset 0x286, CSR Addr 0x54143, Direction=N/A */ + uint8_t reserved287; /* Byte offset 0x287, CSR Addr 0x54143, Direction=N/A */ + uint8_t reserved288; /* Byte offset 0x288, CSR Addr 0x54144, Direction=N/A */ + uint8_t reserved289; /* Byte offset 0x289, CSR Addr 0x54144, Direction=N/A */ + uint8_t reserved28a; /* Byte offset 0x28a, CSR Addr 0x54145, Direction=N/A */ + uint8_t reserved28b; /* Byte offset 0x28b, CSR Addr 0x54145, Direction=N/A */ + uint8_t reserved28c; /* Byte offset 0x28c, CSR Addr 0x54146, Direction=N/A */ + uint8_t reserved28d; /* Byte offset 0x28d, CSR Addr 0x54146, Direction=N/A */ + uint8_t reserved28e; /* Byte offset 0x28e, CSR Addr 0x54147, Direction=N/A */ + uint8_t reserved28f; /* Byte offset 0x28f, CSR Addr 0x54147, Direction=N/A */ + uint8_t reserved290; /* Byte offset 0x290, CSR Addr 0x54148, Direction=N/A */ + uint8_t reserved291; /* Byte offset 0x291, CSR Addr 0x54148, Direction=N/A */ + uint8_t reserved292; /* Byte offset 0x292, CSR Addr 0x54149, Direction=N/A */ + uint8_t reserved293; /* Byte offset 0x293, CSR Addr 0x54149, Direction=N/A */ + uint8_t reserved294; /* Byte offset 0x294, CSR Addr 0x5414a, Direction=N/A */ + uint8_t reserved295; /* Byte offset 0x295, CSR Addr 0x5414a, Direction=N/A */ + uint8_t reserved296; /* Byte offset 0x296, CSR Addr 0x5414b, Direction=N/A */ + uint8_t reserved297; /* Byte offset 0x297, CSR Addr 0x5414b, Direction=N/A */ + uint8_t reserved298; /* Byte offset 0x298, CSR Addr 0x5414c, Direction=N/A */ + uint8_t reserved299; /* Byte offset 0x299, CSR Addr 0x5414c, Direction=N/A */ + uint8_t reserved29a; /* Byte offset 0x29a, CSR Addr 0x5414d, Direction=N/A */ + uint8_t reserved29b; /* Byte offset 0x29b, CSR Addr 0x5414d, Direction=N/A */ + uint8_t reserved29c; /* Byte offset 0x29c, CSR Addr 0x5414e, Direction=N/A */ + uint8_t reserved29d; /* Byte offset 0x29d, CSR Addr 0x5414e, Direction=N/A */ + uint8_t reserved29e; /* Byte offset 0x29e, CSR Addr 0x5414f, Direction=N/A */ + uint8_t reserved29f; /* Byte offset 0x29f, CSR Addr 0x5414f, Direction=N/A */ + uint8_t reserved2a0; /* Byte offset 0x2a0, CSR Addr 0x54150, Direction=N/A */ + uint8_t reserved2a1; /* Byte offset 0x2a1, CSR Addr 0x54150, Direction=N/A */ + uint8_t reserved2a2; /* Byte offset 0x2a2, CSR Addr 0x54151, Direction=N/A */ + uint8_t reserved2a3; /* Byte offset 0x2a3, CSR Addr 0x54151, Direction=N/A */ + uint8_t reserved2a4; /* Byte offset 0x2a4, CSR Addr 0x54152, Direction=N/A */ + uint8_t reserved2a5; /* Byte offset 0x2a5, CSR Addr 0x54152, Direction=N/A */ + uint8_t reserved2a6; /* Byte offset 0x2a6, CSR Addr 0x54153, Direction=N/A */ + uint8_t reserved2a7; /* Byte offset 0x2a7, CSR Addr 0x54153, Direction=N/A */ + uint8_t reserved2a8; /* Byte offset 0x2a8, CSR Addr 0x54154, Direction=N/A */ + uint8_t reserved2a9; /* Byte offset 0x2a9, CSR Addr 0x54154, Direction=N/A */ + uint8_t reserved2aa; /* Byte offset 0x2aa, CSR Addr 0x54155, Direction=N/A */ + uint8_t reserved2ab; /* Byte offset 0x2ab, CSR Addr 0x54155, Direction=N/A */ + uint8_t reserved2ac; /* Byte offset 0x2ac, CSR Addr 0x54156, Direction=N/A */ + uint8_t reserved2ad; /* Byte offset 0x2ad, CSR Addr 0x54156, Direction=N/A */ + uint8_t reserved2ae; /* Byte offset 0x2ae, CSR Addr 0x54157, Direction=N/A */ + uint8_t reserved2af; /* Byte offset 0x2af, CSR Addr 0x54157, Direction=N/A */ + uint8_t reserved2b0; /* Byte offset 0x2b0, CSR Addr 0x54158, Direction=N/A */ + uint8_t reserved2b1; /* Byte offset 0x2b1, CSR Addr 0x54158, Direction=N/A */ + uint8_t reserved2b2; /* Byte offset 0x2b2, CSR Addr 0x54159, Direction=N/A */ + uint8_t reserved2b3; /* Byte offset 0x2b3, CSR Addr 0x54159, Direction=N/A */ + uint8_t reserved2b4; /* Byte offset 0x2b4, CSR Addr 0x5415a, Direction=N/A */ + uint8_t reserved2b5; /* Byte offset 0x2b5, CSR Addr 0x5415a, Direction=N/A */ + uint8_t reserved2b6; /* Byte offset 0x2b6, CSR Addr 0x5415b, Direction=N/A */ + uint8_t reserved2b7; /* Byte offset 0x2b7, CSR Addr 0x5415b, Direction=N/A */ + uint8_t reserved2b8; /* Byte offset 0x2b8, CSR Addr 0x5415c, Direction=N/A */ + uint8_t reserved2b9; /* Byte offset 0x2b9, CSR Addr 0x5415c, Direction=N/A */ + uint8_t reserved2ba; /* Byte offset 0x2ba, CSR Addr 0x5415d, Direction=N/A */ + uint8_t reserved2bb; /* Byte offset 0x2bb, CSR Addr 0x5415d, Direction=N/A */ + uint8_t reserved2bc; /* Byte offset 0x2bc, CSR Addr 0x5415e, Direction=N/A */ + uint8_t reserved2bd; /* Byte offset 0x2bd, CSR Addr 0x5415e, Direction=N/A */ + uint8_t reserved2be; /* Byte offset 0x2be, CSR Addr 0x5415f, Direction=N/A */ + uint8_t reserved2bf; /* Byte offset 0x2bf, CSR Addr 0x5415f, Direction=N/A */ + uint8_t reserved2c0; /* Byte offset 0x2c0, CSR Addr 0x54160, Direction=N/A */ + uint8_t reserved2c1; /* Byte offset 0x2c1, CSR Addr 0x54160, Direction=N/A */ + uint8_t reserved2c2; /* Byte offset 0x2c2, CSR Addr 0x54161, Direction=N/A */ + uint8_t reserved2c3; /* Byte offset 0x2c3, CSR Addr 0x54161, Direction=N/A */ + uint8_t reserved2c4; /* Byte offset 0x2c4, CSR Addr 0x54162, Direction=N/A */ + uint8_t reserved2c5; /* Byte offset 0x2c5, CSR Addr 0x54162, Direction=N/A */ + uint8_t reserved2c6; /* Byte offset 0x2c6, CSR Addr 0x54163, Direction=N/A */ + uint8_t reserved2c7; /* Byte offset 0x2c7, CSR Addr 0x54163, Direction=N/A */ + uint8_t reserved2c8; /* Byte offset 0x2c8, CSR Addr 0x54164, Direction=N/A */ + uint8_t reserved2c9; /* Byte offset 0x2c9, CSR Addr 0x54164, Direction=N/A */ + uint8_t reserved2ca; /* Byte offset 0x2ca, CSR Addr 0x54165, Direction=N/A */ + uint8_t reserved2cb; /* Byte offset 0x2cb, CSR Addr 0x54165, Direction=N/A */ + uint8_t reserved2cc; /* Byte offset 0x2cc, CSR Addr 0x54166, Direction=N/A */ + uint8_t reserved2cd; /* Byte offset 0x2cd, CSR Addr 0x54166, Direction=N/A */ + uint8_t reserved2ce; /* Byte offset 0x2ce, CSR Addr 0x54167, Direction=N/A */ + uint8_t reserved2cf; /* Byte offset 0x2cf, CSR Addr 0x54167, Direction=N/A */ + uint8_t reserved2d0; /* Byte offset 0x2d0, CSR Addr 0x54168, Direction=N/A */ + uint8_t reserved2d1; /* Byte offset 0x2d1, CSR Addr 0x54168, Direction=N/A */ + uint8_t reserved2d2; /* Byte offset 0x2d2, CSR Addr 0x54169, Direction=N/A */ + uint8_t reserved2d3; /* Byte offset 0x2d3, CSR Addr 0x54169, Direction=N/A */ + uint8_t reserved2d4; /* Byte offset 0x2d4, CSR Addr 0x5416a, Direction=N/A */ + uint8_t reserved2d5; /* Byte offset 0x2d5, CSR Addr 0x5416a, Direction=N/A */ + uint8_t reserved2d6; /* Byte offset 0x2d6, CSR Addr 0x5416b, Direction=N/A */ + uint8_t reserved2d7; /* Byte offset 0x2d7, CSR Addr 0x5416b, Direction=N/A */ + uint8_t reserved2d8; /* Byte offset 0x2d8, CSR Addr 0x5416c, Direction=N/A */ + uint8_t reserved2d9; /* Byte offset 0x2d9, CSR Addr 0x5416c, Direction=N/A */ + uint8_t reserved2da; /* Byte offset 0x2da, CSR Addr 0x5416d, Direction=N/A */ + uint8_t reserved2db; /* Byte offset 0x2db, CSR Addr 0x5416d, Direction=N/A */ + uint8_t reserved2dc; /* Byte offset 0x2dc, CSR Addr 0x5416e, Direction=N/A */ + uint8_t reserved2dd; /* Byte offset 0x2dd, CSR Addr 0x5416e, Direction=N/A */ + uint8_t reserved2de; /* Byte offset 0x2de, CSR Addr 0x5416f, Direction=N/A */ + uint8_t reserved2df; /* Byte offset 0x2df, CSR Addr 0x5416f, Direction=N/A */ + uint8_t reserved2e0; /* Byte offset 0x2e0, CSR Addr 0x54170, Direction=N/A */ + uint8_t reserved2e1; /* Byte offset 0x2e1, CSR Addr 0x54170, Direction=N/A */ + uint8_t reserved2e2; /* Byte offset 0x2e2, CSR Addr 0x54171, Direction=N/A */ + uint8_t reserved2e3; /* Byte offset 0x2e3, CSR Addr 0x54171, Direction=N/A */ + uint8_t reserved2e4; /* Byte offset 0x2e4, CSR Addr 0x54172, Direction=N/A */ + uint8_t reserved2e5; /* Byte offset 0x2e5, CSR Addr 0x54172, Direction=N/A */ + uint8_t reserved2e6; /* Byte offset 0x2e6, CSR Addr 0x54173, Direction=N/A */ + uint8_t reserved2e7; /* Byte offset 0x2e7, CSR Addr 0x54173, Direction=N/A */ + uint8_t reserved2e8; /* Byte offset 0x2e8, CSR Addr 0x54174, Direction=N/A */ + uint8_t reserved2e9; /* Byte offset 0x2e9, CSR Addr 0x54174, Direction=N/A */ + uint8_t reserved2ea; /* Byte offset 0x2ea, CSR Addr 0x54175, Direction=N/A */ + uint8_t reserved2eb; /* Byte offset 0x2eb, CSR Addr 0x54175, Direction=N/A */ + uint8_t reserved2ec; /* Byte offset 0x2ec, CSR Addr 0x54176, Direction=N/A */ + uint8_t reserved2ed; /* Byte offset 0x2ed, CSR Addr 0x54176, Direction=N/A */ + uint8_t reserved2ee; /* Byte offset 0x2ee, CSR Addr 0x54177, Direction=N/A */ + uint8_t reserved2ef; /* Byte offset 0x2ef, CSR Addr 0x54177, Direction=N/A */ + uint8_t reserved2f0; /* Byte offset 0x2f0, CSR Addr 0x54178, Direction=N/A */ + uint8_t reserved2f1; /* Byte offset 0x2f1, CSR Addr 0x54178, Direction=N/A */ + uint8_t reserved2f2; /* Byte offset 0x2f2, CSR Addr 0x54179, Direction=N/A */ + uint8_t reserved2f3; /* Byte offset 0x2f3, CSR Addr 0x54179, Direction=N/A */ + uint8_t reserved2f4; /* Byte offset 0x2f4, CSR Addr 0x5417a, Direction=N/A */ + uint8_t reserved2f5; /* Byte offset 0x2f5, CSR Addr 0x5417a, Direction=N/A */ + uint8_t reserved2f6; /* Byte offset 0x2f6, CSR Addr 0x5417b, Direction=N/A */ + uint8_t reserved2f7; /* Byte offset 0x2f7, CSR Addr 0x5417b, Direction=N/A */ + uint8_t reserved2f8; /* Byte offset 0x2f8, CSR Addr 0x5417c, Direction=N/A */ + uint8_t reserved2f9; /* Byte offset 0x2f9, CSR Addr 0x5417c, Direction=N/A */ + uint8_t reserved2fa; /* Byte offset 0x2fa, CSR Addr 0x5417d, Direction=N/A */ + uint8_t reserved2fb; /* Byte offset 0x2fb, CSR Addr 0x5417d, Direction=N/A */ + uint8_t reserved2fc; /* Byte offset 0x2fc, CSR Addr 0x5417e, Direction=N/A */ + uint8_t reserved2fd; /* Byte offset 0x2fd, CSR Addr 0x5417e, Direction=N/A */ + uint8_t reserved2fe; /* Byte offset 0x2fe, CSR Addr 0x5417f, Direction=N/A */ + uint8_t reserved2ff; /* Byte offset 0x2ff, CSR Addr 0x5417f, Direction=N/A */ + uint8_t reserved300; /* Byte offset 0x300, CSR Addr 0x54180, Direction=N/A */ + uint8_t reserved301; /* Byte offset 0x301, CSR Addr 0x54180, Direction=N/A */ + uint8_t reserved302; /* Byte offset 0x302, CSR Addr 0x54181, Direction=N/A */ + uint8_t reserved303; /* Byte offset 0x303, CSR Addr 0x54181, Direction=N/A */ + uint8_t reserved304; /* Byte offset 0x304, CSR Addr 0x54182, Direction=N/A */ + uint8_t reserved305; /* Byte offset 0x305, CSR Addr 0x54182, Direction=N/A */ + uint8_t reserved306; /* Byte offset 0x306, CSR Addr 0x54183, Direction=N/A */ + uint8_t reserved307; /* Byte offset 0x307, CSR Addr 0x54183, Direction=N/A */ + uint8_t reserved308; /* Byte offset 0x308, CSR Addr 0x54184, Direction=N/A */ + uint8_t reserved309; /* Byte offset 0x309, CSR Addr 0x54184, Direction=N/A */ + uint8_t reserved30a; /* Byte offset 0x30a, CSR Addr 0x54185, Direction=N/A */ + uint8_t reserved30b; /* Byte offset 0x30b, CSR Addr 0x54185, Direction=N/A */ + uint8_t reserved30c; /* Byte offset 0x30c, CSR Addr 0x54186, Direction=N/A */ + uint8_t reserved30d; /* Byte offset 0x30d, CSR Addr 0x54186, Direction=N/A */ + uint8_t reserved30e; /* Byte offset 0x30e, CSR Addr 0x54187, Direction=N/A */ + uint8_t reserved30f; /* Byte offset 0x30f, CSR Addr 0x54187, Direction=N/A */ + uint8_t reserved310; /* Byte offset 0x310, CSR Addr 0x54188, Direction=N/A */ + uint8_t reserved311; /* Byte offset 0x311, CSR Addr 0x54188, Direction=N/A */ + uint8_t reserved312; /* Byte offset 0x312, CSR Addr 0x54189, Direction=N/A */ + uint8_t reserved313; /* Byte offset 0x313, CSR Addr 0x54189, Direction=N/A */ + uint8_t reserved314; /* Byte offset 0x314, CSR Addr 0x5418a, Direction=N/A */ + uint8_t reserved315; /* Byte offset 0x315, CSR Addr 0x5418a, Direction=N/A */ + uint8_t reserved316; /* Byte offset 0x316, CSR Addr 0x5418b, Direction=N/A */ + uint8_t reserved317; /* Byte offset 0x317, CSR Addr 0x5418b, Direction=N/A */ + uint8_t reserved318; /* Byte offset 0x318, CSR Addr 0x5418c, Direction=N/A */ + uint8_t reserved319; /* Byte offset 0x319, CSR Addr 0x5418c, Direction=N/A */ + uint8_t reserved31a; /* Byte offset 0x31a, CSR Addr 0x5418d, Direction=N/A */ + uint8_t reserved31b; /* Byte offset 0x31b, CSR Addr 0x5418d, Direction=N/A */ + uint8_t reserved31c; /* Byte offset 0x31c, CSR Addr 0x5418e, Direction=N/A */ + uint8_t reserved31d; /* Byte offset 0x31d, CSR Addr 0x5418e, Direction=N/A */ + uint8_t reserved31e; /* Byte offset 0x31e, CSR Addr 0x5418f, Direction=N/A */ + uint8_t reserved31f; /* Byte offset 0x31f, CSR Addr 0x5418f, Direction=N/A */ + uint8_t reserved320; /* Byte offset 0x320, CSR Addr 0x54190, Direction=N/A */ + uint8_t reserved321; /* Byte offset 0x321, CSR Addr 0x54190, Direction=N/A */ + uint8_t reserved322; /* Byte offset 0x322, CSR Addr 0x54191, Direction=N/A */ + uint8_t reserved323; /* Byte offset 0x323, CSR Addr 0x54191, Direction=N/A */ + uint8_t reserved324; /* Byte offset 0x324, CSR Addr 0x54192, Direction=N/A */ + uint8_t reserved325; /* Byte offset 0x325, CSR Addr 0x54192, Direction=N/A */ + uint8_t reserved326; /* Byte offset 0x326, CSR Addr 0x54193, Direction=N/A */ + uint8_t reserved327; /* Byte offset 0x327, CSR Addr 0x54193, Direction=N/A */ + uint8_t reserved328; /* Byte offset 0x328, CSR Addr 0x54194, Direction=N/A */ + uint8_t reserved329; /* Byte offset 0x329, CSR Addr 0x54194, Direction=N/A */ + uint8_t reserved32a; /* Byte offset 0x32a, CSR Addr 0x54195, Direction=N/A */ + uint8_t reserved32b; /* Byte offset 0x32b, CSR Addr 0x54195, Direction=N/A */ + uint8_t reserved32c; /* Byte offset 0x32c, CSR Addr 0x54196, Direction=N/A */ + uint8_t reserved32d; /* Byte offset 0x32d, CSR Addr 0x54196, Direction=N/A */ + uint8_t reserved32e; /* Byte offset 0x32e, CSR Addr 0x54197, Direction=N/A */ + uint8_t reserved32f; /* Byte offset 0x32f, CSR Addr 0x54197, Direction=N/A */ + uint8_t reserved330; /* Byte offset 0x330, CSR Addr 0x54198, Direction=N/A */ + uint8_t reserved331; /* Byte offset 0x331, CSR Addr 0x54198, Direction=N/A */ + uint8_t reserved332; /* Byte offset 0x332, CSR Addr 0x54199, Direction=N/A */ + uint8_t reserved333; /* Byte offset 0x333, CSR Addr 0x54199, Direction=N/A */ + uint8_t reserved334; /* Byte offset 0x334, CSR Addr 0x5419a, Direction=N/A */ + uint8_t reserved335; /* Byte offset 0x335, CSR Addr 0x5419a, Direction=N/A */ + uint8_t reserved336; /* Byte offset 0x336, CSR Addr 0x5419b, Direction=N/A */ + uint8_t reserved337; /* Byte offset 0x337, CSR Addr 0x5419b, Direction=N/A */ + uint8_t reserved338; /* Byte offset 0x338, CSR Addr 0x5419c, Direction=N/A */ + uint8_t reserved339; /* Byte offset 0x339, CSR Addr 0x5419c, Direction=N/A */ + uint8_t reserved33a; /* Byte offset 0x33a, CSR Addr 0x5419d, Direction=N/A */ + uint8_t reserved33b; /* Byte offset 0x33b, CSR Addr 0x5419d, Direction=N/A */ + uint8_t reserved33c; /* Byte offset 0x33c, CSR Addr 0x5419e, Direction=N/A */ + uint8_t reserved33d; /* Byte offset 0x33d, CSR Addr 0x5419e, Direction=N/A */ + uint8_t reserved33e; /* Byte offset 0x33e, CSR Addr 0x5419f, Direction=N/A */ + uint8_t reserved33f; /* Byte offset 0x33f, CSR Addr 0x5419f, Direction=N/A */ + uint8_t reserved340; /* Byte offset 0x340, CSR Addr 0x541a0, Direction=N/A */ + uint8_t reserved341; /* Byte offset 0x341, CSR Addr 0x541a0, Direction=N/A */ + uint8_t reserved342; /* Byte offset 0x342, CSR Addr 0x541a1, Direction=N/A */ + uint8_t reserved343; /* Byte offset 0x343, CSR Addr 0x541a1, Direction=N/A */ + uint8_t reserved344; /* Byte offset 0x344, CSR Addr 0x541a2, Direction=N/A */ + uint8_t reserved345; /* Byte offset 0x345, CSR Addr 0x541a2, Direction=N/A */ + uint8_t reserved346; /* Byte offset 0x346, CSR Addr 0x541a3, Direction=N/A */ + uint8_t reserved347; /* Byte offset 0x347, CSR Addr 0x541a3, Direction=N/A */ + uint8_t reserved348; /* Byte offset 0x348, CSR Addr 0x541a4, Direction=N/A */ + uint8_t reserved349; /* Byte offset 0x349, CSR Addr 0x541a4, Direction=N/A */ + uint8_t reserved34a; /* Byte offset 0x34a, CSR Addr 0x541a5, Direction=N/A */ + uint8_t reserved34b; /* Byte offset 0x34b, CSR Addr 0x541a5, Direction=N/A */ + uint8_t reserved34c; /* Byte offset 0x34c, CSR Addr 0x541a6, Direction=N/A */ + uint8_t reserved34d; /* Byte offset 0x34d, CSR Addr 0x541a6, Direction=N/A */ + uint8_t reserved34e; /* Byte offset 0x34e, CSR Addr 0x541a7, Direction=N/A */ + uint8_t reserved34f; /* Byte offset 0x34f, CSR Addr 0x541a7, Direction=N/A */ + uint8_t reserved350; /* Byte offset 0x350, CSR Addr 0x541a8, Direction=N/A */ + uint8_t reserved351; /* Byte offset 0x351, CSR Addr 0x541a8, Direction=N/A */ + uint8_t reserved352; /* Byte offset 0x352, CSR Addr 0x541a9, Direction=N/A */ + uint8_t reserved353; /* Byte offset 0x353, CSR Addr 0x541a9, Direction=N/A */ + uint8_t reserved354; /* Byte offset 0x354, CSR Addr 0x541aa, Direction=N/A */ + uint8_t reserved355; /* Byte offset 0x355, CSR Addr 0x541aa, Direction=N/A */ + uint8_t reserved356; /* Byte offset 0x356, CSR Addr 0x541ab, Direction=N/A */ + uint8_t reserved357; /* Byte offset 0x357, CSR Addr 0x541ab, Direction=N/A */ + uint8_t reserved358; /* Byte offset 0x358, CSR Addr 0x541ac, Direction=N/A */ + uint8_t reserved359; /* Byte offset 0x359, CSR Addr 0x541ac, Direction=N/A */ + uint8_t reserved35a; /* Byte offset 0x35a, CSR Addr 0x541ad, Direction=N/A */ + uint8_t reserved35b; /* Byte offset 0x35b, CSR Addr 0x541ad, Direction=N/A */ + uint8_t reserved35c; /* Byte offset 0x35c, CSR Addr 0x541ae, Direction=N/A */ + uint8_t reserved35d; /* Byte offset 0x35d, CSR Addr 0x541ae, Direction=N/A */ + uint8_t reserved35e; /* Byte offset 0x35e, CSR Addr 0x541af, Direction=N/A */ + uint8_t reserved35f; /* Byte offset 0x35f, CSR Addr 0x541af, Direction=N/A */ + uint8_t reserved360; /* Byte offset 0x360, CSR Addr 0x541b0, Direction=N/A */ + uint8_t reserved361; /* Byte offset 0x361, CSR Addr 0x541b0, Direction=N/A */ + uint8_t reserved362; /* Byte offset 0x362, CSR Addr 0x541b1, Direction=N/A */ + uint8_t reserved363; /* Byte offset 0x363, CSR Addr 0x541b1, Direction=N/A */ + uint8_t reserved364; /* Byte offset 0x364, CSR Addr 0x541b2, Direction=N/A */ + uint8_t reserved365; /* Byte offset 0x365, CSR Addr 0x541b2, Direction=N/A */ + uint8_t reserved366; /* Byte offset 0x366, CSR Addr 0x541b3, Direction=N/A */ + uint8_t reserved367; /* Byte offset 0x367, CSR Addr 0x541b3, Direction=N/A */ + uint8_t reserved368; /* Byte offset 0x368, CSR Addr 0x541b4, Direction=N/A */ + uint8_t reserved369; /* Byte offset 0x369, CSR Addr 0x541b4, Direction=N/A */ + uint8_t reserved36a; /* Byte offset 0x36a, CSR Addr 0x541b5, Direction=N/A */ + uint8_t reserved36b; /* Byte offset 0x36b, CSR Addr 0x541b5, Direction=N/A */ + uint8_t reserved36c; /* Byte offset 0x36c, CSR Addr 0x541b6, Direction=N/A */ + uint8_t reserved36d; /* Byte offset 0x36d, CSR Addr 0x541b6, Direction=N/A */ + uint8_t reserved36e; /* Byte offset 0x36e, CSR Addr 0x541b7, Direction=N/A */ + uint8_t reserved36f; /* Byte offset 0x36f, CSR Addr 0x541b7, Direction=N/A */ + uint8_t reserved370; /* Byte offset 0x370, CSR Addr 0x541b8, Direction=N/A */ + uint8_t reserved371; /* Byte offset 0x371, CSR Addr 0x541b8, Direction=N/A */ + uint8_t reserved372; /* Byte offset 0x372, CSR Addr 0x541b9, Direction=N/A */ + uint8_t reserved373; /* Byte offset 0x373, CSR Addr 0x541b9, Direction=N/A */ + uint8_t reserved374; /* Byte offset 0x374, CSR Addr 0x541ba, Direction=N/A */ + uint8_t reserved375; /* Byte offset 0x375, CSR Addr 0x541ba, Direction=N/A */ + uint8_t reserved376; /* Byte offset 0x376, CSR Addr 0x541bb, Direction=N/A */ + uint8_t reserved377; /* Byte offset 0x377, CSR Addr 0x541bb, Direction=N/A */ + uint8_t reserved378; /* Byte offset 0x378, CSR Addr 0x541bc, Direction=N/A */ + uint8_t reserved379; /* Byte offset 0x379, CSR Addr 0x541bc, Direction=N/A */ + uint8_t reserved37a; /* Byte offset 0x37a, CSR Addr 0x541bd, Direction=N/A */ + uint8_t reserved37b; /* Byte offset 0x37b, CSR Addr 0x541bd, Direction=N/A */ + uint8_t reserved37c; /* Byte offset 0x37c, CSR Addr 0x541be, Direction=N/A */ + uint8_t reserved37d; /* Byte offset 0x37d, CSR Addr 0x541be, Direction=N/A */ + uint8_t reserved37e; /* Byte offset 0x37e, CSR Addr 0x541bf, Direction=N/A */ + uint8_t reserved37f; /* Byte offset 0x37f, CSR Addr 0x541bf, Direction=N/A */ + uint8_t reserved380; /* Byte offset 0x380, CSR Addr 0x541c0, Direction=N/A */ + uint8_t reserved381; /* Byte offset 0x381, CSR Addr 0x541c0, Direction=N/A */ + uint8_t reserved382; /* Byte offset 0x382, CSR Addr 0x541c1, Direction=N/A */ + uint8_t reserved383; /* Byte offset 0x383, CSR Addr 0x541c1, Direction=N/A */ + uint8_t reserved384; /* Byte offset 0x384, CSR Addr 0x541c2, Direction=N/A */ + uint8_t reserved385; /* Byte offset 0x385, CSR Addr 0x541c2, Direction=N/A */ + uint8_t reserved386; /* Byte offset 0x386, CSR Addr 0x541c3, Direction=N/A */ + uint8_t reserved387; /* Byte offset 0x387, CSR Addr 0x541c3, Direction=N/A */ + uint8_t reserved388; /* Byte offset 0x388, CSR Addr 0x541c4, Direction=N/A */ + uint8_t reserved389; /* Byte offset 0x389, CSR Addr 0x541c4, Direction=N/A */ + uint8_t reserved38a; /* Byte offset 0x38a, CSR Addr 0x541c5, Direction=N/A */ + uint8_t reserved38b; /* Byte offset 0x38b, CSR Addr 0x541c5, Direction=N/A */ + uint8_t reserved38c; /* Byte offset 0x38c, CSR Addr 0x541c6, Direction=N/A */ + uint8_t reserved38d; /* Byte offset 0x38d, CSR Addr 0x541c6, Direction=N/A */ + uint8_t reserved38e; /* Byte offset 0x38e, CSR Addr 0x541c7, Direction=N/A */ + uint8_t reserved38f; /* Byte offset 0x38f, CSR Addr 0x541c7, Direction=N/A */ + uint8_t reserved390; /* Byte offset 0x390, CSR Addr 0x541c8, Direction=N/A */ + uint8_t reserved391; /* Byte offset 0x391, CSR Addr 0x541c8, Direction=N/A */ + uint8_t reserved392; /* Byte offset 0x392, CSR Addr 0x541c9, Direction=N/A */ + uint8_t reserved393; /* Byte offset 0x393, CSR Addr 0x541c9, Direction=N/A */ + uint8_t reserved394; /* Byte offset 0x394, CSR Addr 0x541ca, Direction=N/A */ + uint8_t reserved395; /* Byte offset 0x395, CSR Addr 0x541ca, Direction=N/A */ + uint8_t reserved396; /* Byte offset 0x396, CSR Addr 0x541cb, Direction=N/A */ + uint8_t reserved397; /* Byte offset 0x397, CSR Addr 0x541cb, Direction=N/A */ + uint8_t reserved398; /* Byte offset 0x398, CSR Addr 0x541cc, Direction=N/A */ + uint8_t reserved399; /* Byte offset 0x399, CSR Addr 0x541cc, Direction=N/A */ + uint8_t reserved39a; /* Byte offset 0x39a, CSR Addr 0x541cd, Direction=N/A */ + uint8_t reserved39b; /* Byte offset 0x39b, CSR Addr 0x541cd, Direction=N/A */ + uint8_t reserved39c; /* Byte offset 0x39c, CSR Addr 0x541ce, Direction=N/A */ + uint8_t reserved39d; /* Byte offset 0x39d, CSR Addr 0x541ce, Direction=N/A */ + uint8_t reserved39e; /* Byte offset 0x39e, CSR Addr 0x541cf, Direction=N/A */ + uint8_t reserved39f; /* Byte offset 0x39f, CSR Addr 0x541cf, Direction=N/A */ + uint8_t reserved3a0; /* Byte offset 0x3a0, CSR Addr 0x541d0, Direction=N/A */ + uint8_t reserved3a1; /* Byte offset 0x3a1, CSR Addr 0x541d0, Direction=N/A */ + uint8_t reserved3a2; /* Byte offset 0x3a2, CSR Addr 0x541d1, Direction=N/A */ + uint8_t reserved3a3; /* Byte offset 0x3a3, CSR Addr 0x541d1, Direction=N/A */ + uint8_t reserved3a4; /* Byte offset 0x3a4, CSR Addr 0x541d2, Direction=N/A */ + uint8_t reserved3a5; /* Byte offset 0x3a5, CSR Addr 0x541d2, Direction=N/A */ + uint8_t reserved3a6; /* Byte offset 0x3a6, CSR Addr 0x541d3, Direction=N/A */ + uint8_t reserved3a7; /* Byte offset 0x3a7, CSR Addr 0x541d3, Direction=N/A */ + uint8_t reserved3a8; /* Byte offset 0x3a8, CSR Addr 0x541d4, Direction=N/A */ + uint8_t reserved3a9; /* Byte offset 0x3a9, CSR Addr 0x541d4, Direction=N/A */ + uint8_t reserved3aa; /* Byte offset 0x3aa, CSR Addr 0x541d5, Direction=N/A */ + uint8_t reserved3ab; /* Byte offset 0x3ab, CSR Addr 0x541d5, Direction=N/A */ + uint8_t reserved3ac; /* Byte offset 0x3ac, CSR Addr 0x541d6, Direction=N/A */ + uint8_t reserved3ad; /* Byte offset 0x3ad, CSR Addr 0x541d6, Direction=N/A */ + uint8_t reserved3ae; /* Byte offset 0x3ae, CSR Addr 0x541d7, Direction=N/A */ + uint8_t reserved3af; /* Byte offset 0x3af, CSR Addr 0x541d7, Direction=N/A */ + uint8_t reserved3b0; /* Byte offset 0x3b0, CSR Addr 0x541d8, Direction=N/A */ + uint8_t reserved3b1; /* Byte offset 0x3b1, CSR Addr 0x541d8, Direction=N/A */ + uint8_t reserved3b2; /* Byte offset 0x3b2, CSR Addr 0x541d9, Direction=N/A */ + uint8_t reserved3b3; /* Byte offset 0x3b3, CSR Addr 0x541d9, Direction=N/A */ + uint8_t reserved3b4; /* Byte offset 0x3b4, CSR Addr 0x541da, Direction=N/A */ + uint8_t reserved3b5; /* Byte offset 0x3b5, CSR Addr 0x541da, Direction=N/A */ + uint8_t reserved3b6; /* Byte offset 0x3b6, CSR Addr 0x541db, Direction=N/A */ + uint8_t reserved3b7; /* Byte offset 0x3b7, CSR Addr 0x541db, Direction=N/A */ + uint8_t reserved3b8; /* Byte offset 0x3b8, CSR Addr 0x541dc, Direction=N/A */ + uint8_t reserved3b9; /* Byte offset 0x3b9, CSR Addr 0x541dc, Direction=N/A */ + uint8_t reserved3ba; /* Byte offset 0x3ba, CSR Addr 0x541dd, Direction=N/A */ + uint8_t reserved3bb; /* Byte offset 0x3bb, CSR Addr 0x541dd, Direction=N/A */ + uint8_t reserved3bc; /* Byte offset 0x3bc, CSR Addr 0x541de, Direction=N/A */ + uint8_t reserved3bd; /* Byte offset 0x3bd, CSR Addr 0x541de, Direction=N/A */ + uint8_t reserved3be; /* Byte offset 0x3be, CSR Addr 0x541df, Direction=N/A */ + uint8_t reserved3bf; /* Byte offset 0x3bf, CSR Addr 0x541df, Direction=N/A */ + uint8_t reserved3c0; /* Byte offset 0x3c0, CSR Addr 0x541e0, Direction=N/A */ + uint8_t reserved3c1; /* Byte offset 0x3c1, CSR Addr 0x541e0, Direction=N/A */ + uint8_t reserved3c2; /* Byte offset 0x3c2, CSR Addr 0x541e1, Direction=N/A */ + uint8_t reserved3c3; /* Byte offset 0x3c3, CSR Addr 0x541e1, Direction=N/A */ + uint8_t reserved3c4; /* Byte offset 0x3c4, CSR Addr 0x541e2, Direction=N/A */ + uint8_t reserved3c5; /* Byte offset 0x3c5, CSR Addr 0x541e2, Direction=N/A */ + uint8_t reserved3c6; /* Byte offset 0x3c6, CSR Addr 0x541e3, Direction=N/A */ + uint8_t reserved3c7; /* Byte offset 0x3c7, CSR Addr 0x541e3, Direction=N/A */ + uint8_t reserved3c8; /* Byte offset 0x3c8, CSR Addr 0x541e4, Direction=N/A */ + uint8_t reserved3c9; /* Byte offset 0x3c9, CSR Addr 0x541e4, Direction=N/A */ + uint8_t reserved3ca; /* Byte offset 0x3ca, CSR Addr 0x541e5, Direction=N/A */ + uint8_t reserved3cb; /* Byte offset 0x3cb, CSR Addr 0x541e5, Direction=N/A */ + uint8_t reserved3cc; /* Byte offset 0x3cc, CSR Addr 0x541e6, Direction=N/A */ + uint8_t reserved3cd; /* Byte offset 0x3cd, CSR Addr 0x541e6, Direction=N/A */ + uint8_t reserved3ce; /* Byte offset 0x3ce, CSR Addr 0x541e7, Direction=N/A */ + uint8_t reserved3cf; /* Byte offset 0x3cf, CSR Addr 0x541e7, Direction=N/A */ + uint8_t reserved3d0; /* Byte offset 0x3d0, CSR Addr 0x541e8, Direction=N/A */ + uint8_t reserved3d1; /* Byte offset 0x3d1, CSR Addr 0x541e8, Direction=N/A */ + uint8_t reserved3d2; /* Byte offset 0x3d2, CSR Addr 0x541e9, Direction=N/A */ + uint8_t reserved3d3; /* Byte offset 0x3d3, CSR Addr 0x541e9, Direction=N/A */ + uint8_t reserved3d4; /* Byte offset 0x3d4, CSR Addr 0x541ea, Direction=N/A */ + uint8_t reserved3d5; /* Byte offset 0x3d5, CSR Addr 0x541ea, Direction=N/A */ + uint8_t reserved3d6; /* Byte offset 0x3d6, CSR Addr 0x541eb, Direction=N/A */ + uint8_t reserved3d7; /* Byte offset 0x3d7, CSR Addr 0x541eb, Direction=N/A */ + uint8_t reserved3d8; /* Byte offset 0x3d8, CSR Addr 0x541ec, Direction=N/A */ + uint8_t reserved3d9; /* Byte offset 0x3d9, CSR Addr 0x541ec, Direction=N/A */ + uint8_t reserved3da; /* Byte offset 0x3da, CSR Addr 0x541ed, Direction=N/A */ + uint8_t reserved3db; /* Byte offset 0x3db, CSR Addr 0x541ed, Direction=N/A */ + uint8_t reserved3dc; /* Byte offset 0x3dc, CSR Addr 0x541ee, Direction=N/A */ + uint8_t reserved3dd; /* Byte offset 0x3dd, CSR Addr 0x541ee, Direction=N/A */ + uint8_t reserved3de; /* Byte offset 0x3de, CSR Addr 0x541ef, Direction=N/A */ + uint8_t reserved3df; /* Byte offset 0x3df, CSR Addr 0x541ef, Direction=N/A */ + uint8_t reserved3e0; /* Byte offset 0x3e0, CSR Addr 0x541f0, Direction=N/A */ + uint8_t reserved3e1; /* Byte offset 0x3e1, CSR Addr 0x541f0, Direction=N/A */ + uint8_t reserved3e2; /* Byte offset 0x3e2, CSR Addr 0x541f1, Direction=N/A */ + uint8_t reserved3e3; /* Byte offset 0x3e3, CSR Addr 0x541f1, Direction=N/A */ + uint8_t reserved3e4; /* Byte offset 0x3e4, CSR Addr 0x541f2, Direction=N/A */ + uint8_t reserved3e5; /* Byte offset 0x3e5, CSR Addr 0x541f2, Direction=N/A */ + uint8_t reserved3e6; /* Byte offset 0x3e6, CSR Addr 0x541f3, Direction=N/A */ + uint8_t reserved3e7; /* Byte offset 0x3e7, CSR Addr 0x541f3, Direction=N/A */ + uint8_t reserved3e8; /* Byte offset 0x3e8, CSR Addr 0x541f4, Direction=N/A */ + uint8_t reserved3e9; /* Byte offset 0x3e9, CSR Addr 0x541f4, Direction=N/A */ + uint8_t reserved3ea; /* Byte offset 0x3ea, CSR Addr 0x541f5, Direction=N/A */ + uint8_t reserved3eb; /* Byte offset 0x3eb, CSR Addr 0x541f5, Direction=N/A */ + uint8_t reserved3ec; /* Byte offset 0x3ec, CSR Addr 0x541f6, Direction=N/A */ + uint8_t reserved3ed; /* Byte offset 0x3ed, CSR Addr 0x541f6, Direction=N/A */ + uint8_t reserved3ee; /* Byte offset 0x3ee, CSR Addr 0x541f7, Direction=N/A */ + uint8_t reserved3ef; /* Byte offset 0x3ef, CSR Addr 0x541f7, Direction=N/A */ + uint8_t reserved3f0; /* Byte offset 0x3f0, CSR Addr 0x541f8, Direction=N/A */ + uint8_t reserved3f1; /* Byte offset 0x3f1, CSR Addr 0x541f8, Direction=N/A */ + uint8_t reserved3f2; /* Byte offset 0x3f2, CSR Addr 0x541f9, Direction=N/A */ + uint8_t reserved3f3; /* Byte offset 0x3f3, CSR Addr 0x541f9, Direction=N/A */ + uint8_t reserved3f4; /* Byte offset 0x3f4, CSR Addr 0x541fa, Direction=N/A */ + uint8_t reserved3f5; /* Byte offset 0x3f5, CSR Addr 0x541fa, Direction=N/A */ + uint16_t alt_cas_l; /* + * Byte offset 0x3f6, CSR Addr 0x541fb, Direction=in + * This field must be populated if RdDBI is enabled + * (applicable when mr5[A12] == 1). + * RdDBI is dynamically disabled in certain training steps, + * and so the [RdDBI disabled] CAS Latency must be provided + * in this field. + * The required encoding is as follows: + * alt_cas_l[0] == 0: use value in mr0 + * alt_cas_l[0] == 1: use value in alt_cas_l, i.e., + * mr0{A[12],A[6],A[5],A[4],A[2]} = alt_cas_l[12,6,5,4,2] + * Other bits are ignored + */ + uint8_t alt_wcas_l; /* + * Byte offset 0x3f8, CSR Addr 0x541fc, Direction=In + * This field must be populated if 2tCK write preambles are + * enabled (applicable when mr4[A12] == 1). + * 2tCK write prambles are dynamically disabled in certain + * training steps, and so the [1tCK write preamble] WCAS + * Latency must be provided in this field. + * The required encoding is as follows: + * alt_wcas_l[0] == 0: use value in mr2 + * alt_wcas_l[0] == 1: use value in alt_wcas_l, i.e., + * mr2{A[5],A[4],A[3]} = alt_wcas_l[5,4,3] + * Other bits are ignored + */ + uint8_t d4misc; /* + * Byte offset 0x3f9, CSR Addr 0x541fc, Direction=In + * Contains various options for training DDR4 Devices. + * + * Bit fields: + * + * d4misc[7:5,2,1] RFU, must be zero + * + * d4misc[0] = protect memory reset + * 0x1 = dfi_reset_n cannot control BP_MEMRESERT_L to + * devices after training. + * 0x0 = dfi_resert_n can control BP_MEMRESERT_L to + * devices after training + * + * d4misc[3]: reserved + * + * d4misc[4]: DRAM reset mode + * 0x1 = Do not reset DRAM during devinit + * 0x0 = Reset DRAM during devinit + */ +} __packed __aligned(2); + +#endif /* MNPMUSRAMMSGBLOCK_DDR4_H */ diff --git a/drivers/st/ddr/phy/firmware/include/mnpmusrammsgblock_lpddr4.h b/drivers/st/ddr/phy/firmware/include/mnpmusrammsgblock_lpddr4.h new file mode 100644 index 00000000..fb1cd58b --- /dev/null +++ b/drivers/st/ddr/phy/firmware/include/mnpmusrammsgblock_lpddr4.h @@ -0,0 +1,925 @@ +/* + * Copyright (C) 2021-2024, STMicroelectronics - All Rights Reserved + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef MNPMUSRAMMSGBLOCK_LPDDR4_H +#define MNPMUSRAMMSGBLOCK_LPDDR4_H + +/* LPDDR4_1D training firmware message block structure + * + * Please refer to the Training Firmware App Note for futher information about + * the usage for Message Block. + */ +struct pmu_smb_ddr_1d { + uint8_t reserved00; /* + * Byte offset 0x00, CSR Addr 0x54000, Direction=In + * reserved00[0:4] RFU, must be zero + * + * reserved00[5] = Quick Rd2D during 1D Training + * 0x1 = Read Deskew will begin by enabling and quickly + * training the phy's per-lane reference voltages. + * Training the vrefDACs CSRs will increase the maximum 1D + * training time by around half a millisecond, but will + * improve 1D training accuracy on systems with + * significant voltage-offsets between lane read eyes. + * 0x0 = Read Deskew will assume the messageblock's + * phyVref setting is optimal for all lanes. + * + * reserved00[6] = Enable High Effort WrDQ1D + * 0x1 = WrDQ1D will conditionally retry training at + * several extra RxClkDly Timings. This will increase the + * maximum 1D training time by up to 4 extra iterations of + * WrDQ1D. This is only required in systems that suffer + * from very large, asymmetric eye-collapse when receiving + * PRBS patterns. + * 0x0 = WrDQ1D assume rxClkDly values found by SI + * Friendly RdDqs1D will work for receiving PRBS patterns + * + * reserved00[7] = Optimize for the special hard macros in + * TSMC28. + * 0x1 = set if the phy being trained was manufactured in + * any TSMC28 process node. + * 0x0 = otherwise, when not training a TSMC28 phy, leave + * this field as 0. + */ + uint8_t msgmisc; /* + * Byte offset 0x01, CSR Addr 0x54000, Direction=In + * Contains various global options for training. + * + * Bit fields: + * + * msgmisc[0] MTESTEnable + * 0x1 = Pulse primary digital test output bump at the end + * of each major training stage. This enables observation + * of training stage completion by observing the digital + * test output. + * 0x0 = Do not pulse primary digital test output bump + * + * msgmisc[1] SimulationOnlyReset + * 0x1 = Verilog only simulation option to shorten + * duration of DRAM reset pulse length to 1ns. + * Must never be set to 1 in silicon. + * 0x0 = Use reset pulse length specified by JEDEC + * standard. + * + * msgmisc[2] SimulationOnlyTraining + * 0x1 = Verilog only simulation option to shorten the + * duration of the training steps by performing fewer + * iterations. + * Must never be set to 1 in silicon. + * 0x0 = Use standard training duration. + * + * msgmisc[3] Disable Boot Clock + * 0x1 = Disable boot frequency clock when initializing + * DRAM. (not recommended) + * 0x0 = Use Boot Frequency Clock + * + * msgmisc[4] Suppress streaming messages, including + * assertions, regardless of hdtctrl setting. + * Stage Completion messages, as well as training completion + * and error messages are still sent depending on hdtctrl + * setting. + * + * msgmisc[5] PerByteMaxRdLat + * 0x1 = Each DBYTE will return dfi_rddata_valid at the + * lowest possible latency. This may result in unaligned + * data between bytes to be returned to the DFI. + * 0x0 = Every DBYTE will return dfi_rddata_valid + * simultaneously. This will ensure that data bytes will + * return aligned accesses to the DFI. + * + * msgmisc[7-6] RFU, must be zero + * + * Notes: + * + * - SimulationOnlyReset and SimulationOnlyTraining can be + * used to speed up simulation run times, and must never + * be used in real silicon. Some VIPs may have checks on + * DRAM reset parameters that may need to be disabled when + * using SimulationOnlyReset. + */ + uint16_t pmurevision; /* + * Byte offset 0x02, CSR Addr 0x54001, Direction=Out + * PMU firmware revision ID + * After training is run, this address will contain the + * revision ID of the firmware + */ + uint8_t pstate; /* + * Byte offset 0x04, CSR Addr 0x54002, Direction=In + * Must be set to the target pstate to be trained + * 0x0 = pstate 0 + * 0x1 = pstate 1 + * 0x2 = pstate 2 + * 0x3 = pstate 3 + * All other encodings are reserved + */ + uint8_t pllbypassen; /* + * Byte offset 0x05, CSR Addr 0x54002, Direction=In + * Set according to whether target pstate uses PHY PLL + * bypass + * 0x0 = PHY PLL is enabled for target pstate + * 0x1 = PHY PLL is bypassed for target pstate + */ + uint16_t dramfreq; /* + * Byte offset 0x06, CSR Addr 0x54003, Direction=In + * DDR data rate for the target pstate in units of MT/s. + * For example enter 0x0640 for DDR1600. + */ + uint8_t dfifreqratio; /* + * Byte offset 0x08, CSR Addr 0x54004, Direction=In + * Frequency ratio betwen DfiCtlClk and SDRAM memclk. + * 0x1 = 1:1 + * 0x2 = 1:2 + * 0x4 = 1:4 + */ + uint8_t bpznresval; /* + * Byte offset 0x09, CSR Addr 0x54004, Direction=In + * Overwrite the value of precision resistor connected to + * Phy BP_ZN + * 0x00 = Do not program. Use current CSR value. + * 0xf0 = 240 Ohm + * 0x78 = 120 Ohm + * 0x28 = 40 Ohm + * All other values are reserved. + * It is recommended to set this to 0x00. + */ + uint8_t phyodtimpedance; /* + * Byte offset 0x0a, CSR Addr 0x54005, Direction=In + * Must be programmed to the termination impedance in ohms + * used by PHY during reads. + * + * 0x0 = Firmware skips programming (must be manually + * programmed by user prior to training start) + * + * See PHY databook for legal termination impedance values. + * + * For digital simulation, any legal value can be used. For + * silicon, the users must determine the correct value + * through SI simulation or other methods. + */ + uint8_t phydrvimpedance; /* + * Byte offset 0x0b, CSR Addr 0x54005, Direction=In + * Must be programmed to the driver impedance in ohms used + * by PHY during writes for all DBYTE drivers + * (DQ/DM/DBI/DQS). + * + * 0x0 = Firmware skips programming (must be manually + * programmed by user prior to training start) + * + * See PHY databook for legal R_on driver impedance values. + * + * For digital simulation, any value can be used that is not + * Hi-Z. For silicon, the users must determine the correct + * value through SI simulation or other methods. + */ + uint8_t phyvref; /* + * Byte offset 0x0c, CSR Addr 0x54006, Direction=In + * Must be programmed with the Vref level to be used by the + * PHY during reads + * + * The units of this field are a percentage of VDDQ + * according to the following equation: + * + * Receiver Vref = VDDQ*phyvref[6:0]/128 + * + * For example to set Vref at 0.25*VDDQ, set this field to + * 0x20. + * + * For digital simulation, any legal value can be used. For + * silicon, the users must calculate the analytical Vref by + * using the impedances, terminations, and series resistance + * present in the system. + */ + uint8_t lp4misc; /* + * Byte offset 0x0d, CSR Addr 0x54006, Direction=In + * Lp4 specific options for training. + * + * Bit fields: + * + * lp4misc[0] Enable dfi_reset_n + * + * 0x0 = (Recommended) PHY internal registers control + * memreset during training, and also after training. + * dfi_reset_n cannot control the PHY BP_MEMRESET_L pin. + * + * 0x1 = Enables dfi_reset_n to control memreset after + * training. PHY Internal registers control memreset + * during training only. To ensure that no glitches occur + * on BP_MEMRESET at the end of training, The MC must + * drive dfi_reset_n=1'b1 _prior to starting training_ + * + * lp4misc[7-1] RFU, must be zero + */ + uint8_t reserved0e; /* + * Byte offset 0x0e, CSR Addr 0x54007, Direction=In + * Bit Field for enabling optional 2D training features + * that impact both Rx2D and Tx2D. + * + * reserved0E[0:3]: bitTimeControl + * input for the amount of data bits 2D writes/reads per DQ + * before deciding if any specific voltage and delay setting + * passes or fails. Every time this input increases by 1, + * the number of 2D data comparisons is doubled. The 2D run + * time will increase proportionally to the number of bit + * times requested per point. + * 0 = 288 bits per point (legacy behavior) + * 1 = 576 bits per point + * 2 = 1.125 kilobits per point + * . . . + * 15 = 9 megabits per point + * + * reserved0E[4]: Exhaustive2D + * 0 = 2D optimization assumes the optimal trained point + * is near the 1D trained point (legacy behavior) + * 1 = 2D optimization searches the entire passing region + * at the cost of run time. Recommended for optimal + * results any time the optimal trained point is expected + * to be near the edges of the eyes instead of near the 1D + * trained point. + * + * reserved0E[5]: Detect Vref Eye Truncation, ignored if + * eyeWeight2DControl == 0. + * 0 = 2D optimizes for the passing region it can measure. + * 1 = For every eye, 2D checks If the legal voltage range + * truncated the eye. If the true voltage margin cannot be + * measured, 2D will optimize heavily for delay margin + * instead of using incomplete voltage margin data. Eyes + * that are not truncated will still be optimized using + * user programmed weights. + * + * reserved0E[6]: eyeWeight2DControl + * 0 = Use 8 bit weights for Delay_Weight2D and + * Voltage_Weight2D and disable TrunkV behavior. + * 1 = Use 4 bit weights for Delay_weight2D and + * Voltage_Weight2D and enable TrunkV behavior. + * + * reserved0E[7]: RFU, must be 0 + */ + uint8_t cstestfail; /* + * Byte offset 0x0f, CSR Addr 0x54007, Direction=Out + * This field will be set if training fails on any rank. + * 0x0 = No failures + * non-zero = one or more ranks failed training + */ + uint16_t sequencectrl; /* + * Byte offset 0x10, CSR Addr 0x54008, Direction=In + * Controls the training steps to be run. Each bit + * corresponds to a training step. + * + * If the bit is set to 1, the training step will run. + * If the bit is set to 0, the training step will be + * skipped. + * + * Training step to bit mapping: + * sequencectrl[0] = Run DevInit - Device/phy + * initialization. Should always be set. + * sequencectrl[1] = Run WrLvl - Write leveling + * sequencectrl[2] = Run RxEn - Read gate training + * sequencectrl[3] = Run RdDQS1D - 1d read dqs training + * sequencectrl[4] = Run WrDQ1D - 1d write dq training + * sequencectrl[5] = RFU, must be zero + * sequencectrl[6] = RFU, must be zero + * sequencectrl[7] = RFU, must be zero + * sequencectrl[8] = Run RdDeskew - Per lane read dq deskew + * training + * sequencectrl[9] = Run MxRdLat - Max read latency training + * sequencectrl[11-10] = RFU, must be zero + * sequencectrl[12] = Run LPCA - CA Training + * sequencectrl[15-13] = RFU, must be zero + */ + uint8_t hdtctrl; /* + * Byte offset 0x12, CSR Addr 0x54009, Direction=In + * To control the total number of debug messages, a + * verbosity subfield (hdtctrl, Hardware Debug Trace + * Control) exists in the message block. Every message has a + * verbosity level associated with it, and as the hdtctrl + * value is increased, less important s messages stop being + * sent through the mailboxes. The meanings of several major + * hdtctrl thresholds are explained below: + * + * 0x04 = Maximal debug messages (e.g., Eye contours) + * 0x05 = Detailed debug messages (e.g. Eye delays) + * 0x0A = Coarse debug messages (e.g. rank information) + * 0xC8 = Stage completion + * 0xC9 = Assertion messages + * 0xFF = Firmware completion messages only + */ + uint8_t reserved13; /* + * Byte offset 0x13, CSR Addr 0x54009, Direction=In + * + * 0 = Default operation, unchanged. + * Others = RD DQ calibration Training steps are completed + * with user specified pattern. + */ + uint8_t reserved14; /* + * Byte offset 0x14, CSR Addr 0x5400a, Direction=In + * Configure rd2D search iteration from a starting seed + * point: + * + * reserved14[5:0]: If reserved14[6] is 0, Number of search + * iterations (if 0, then default is 20); otherwise if this + * value non zero, this value is used as a delta to filter + * out points during the averaging: when averaging over a + * dimension (delay or voltage), the points having a margin + * smaller than the max of the eye in this dimension by at + * least this delta value are filtered out. + * + * reserved14[6]: If set, instead of search, extract center + * using an averaging function over the eye surface area, + * where some points can be filtered out using + * reserved14[5:0] + * + * reserved14[7]: if set, start search with large step size, + * decreasing at each 4 iterations, down to 1 (do not care + * if reserved14[6] is set) + */ + uint8_t reserved15; /* + * Byte offset 0x15, CSR Addr 0x5400a, Direction=In + * Configure wr2D search iteration from a starting seed + * point: + * + * reserved15[5:0]: If reserved15[6] is 0, Number of search + * iterations (if 0, then default is 20); otherwise if this + * value non zero, this value is used as a delta to filter + * out points during the averaging: when averaging over a + * dimension (delay or voltage), the points having a margin + * smaller than the max of the eye in this dimension by at + * least this delta value are filtered out. + * + * reserved15[6]: If set, instead of search, extract center + * using an averaging function over the eye surface area, + * where some points can be filtered out using + * reserved15[5:0] + * + * reserved15[7]: if set, start search with large step size, + * decreasing at each 4 iterations, down to 1 (do not care + * if reserved15[6] is set) + */ + uint8_t dfimrlmargin; /* + * Byte offset 0x16, CSR Addr 0x5400b, Direction=In + * Margin added to smallest passing trained DFI Max Read + * Latency value, in units of DFI clocks. Recommended to be + * >= 1. + * + * This margin must include the maximum positive drift + * expected in tDQSCK over the target temperature and + * voltage range of the users system. + */ + uint8_t reserved17; /* + * Byte offset 0x17, CSR Addr 0x5400b, Direction=In + * Configure DB from which extra info is dump during 2D + * training when maximal debug is set: + * + * reserved17[3:0]: first DB + * + * reserved17[7:4]: number of DB, including first DB (if 0, + * no extra debug per DB is dump) + */ + uint8_t usebroadcastmr; /* + * Byte offset 0x18, CSR Addr 0x5400c, Direction=In + * Training firmware can optionally set per rank mode + * register values for DRAM partial array self-refresh + * features if desired. + * + * 0x0 = Use mr<1:4, 11:14, 16:17, 22, 24>_a0 for rank 0 + * channel A + * Use mr<1:4, 11:14, 16:17, 22, 24>_b0 for rank 0 + * channel B + * Use mr<1:4, 11:14, 16:17, 22, 24>_a1 for rank 1 + * channel A + * Use mr<1:4, 11:14, 16:17, 22, 24>_b1 for rank 1 + * channel B + * + * 0x1 = Use mr<1:4, 11:14, 16:17, 22, 24>_a0 setting for + * all channels/ranks + * + * It is recommended in most LPDDR4 system configurations + * to set this to 1. + * It is recommended in LPDDR4x system configurations to + * set this to 0. + */ + uint8_t lp4quickboot; /* + * Byte offset 0x19, CSR Addr 0x5400c, Direction=In + * Enable Quickboot. It must be set to 0x0 since Quickboot + * is only supported in dedicated Quickboot firmware. + */ + uint8_t reserved1a; /* + * Byte offset 0x1a, CSR Addr 0x5400d, Direction=In + * Input for constraining the range of vref(DQ) values + * training will collect data for, usually reducing training + * time. However, too large of a voltage range may cause + * longer 2D training times while too small of a voltage + * range may truncate passing regions. When in doubt, leave + * this field set to 0. + * Used by 2D stages: Rd2D, Wr2D + * + * reserved1A[0-3]: Rd2D Voltage Range + * 0 = Training will search all phy vref(DQ) settings + * 1 = limit to +/-2 %VDDQ from phyVref + * 2 = limit to +/-4 %VDDQ from phyVref + * . . . + * 15 = limit to +/-30% VDDQ from phyVref + * + * reserved1A[4-7]: Wr2D Voltage Range + * 0 = Training will search all dram vref(DQ) settings + * 1 = limit to +/-2 %VDDQ from mr14 + * 2 = limit to +/-4 %VDDQ from mr14 + * . . . + * 15 = limit to +/-30% VDDQ from mr14 + */ + uint8_t catrainopt; /* + * Byte offset 0x1b, CSR Addr 0x5400d, Direction=In + * CA training option bit field + * [0] CA VREF Training + * 1 = Enable CA VREF Training + * 0 = Disable CA VREF Training + * WARNING: catrainopt[0] must be set to the same value in + * 1D and 2D training. + * + * [1] Train terminated Rank only + * 1 = Only train terminated rank in CA training + * 0 = Train all ranks in CA training + * + * [2-7] RFU must be zero + */ + uint8_t x8mode; /* + * Byte offset 0x1c, CSR Addr 0x5400e, Direction=In + * X8 mode configuration: + * 0x0 = x16 configuration for all devices + * 0xF = x8 configuration for all devices + * All other values are RFU + */ + uint8_t reserved1d; /* Byte offset 0x1d, CSR Addr 0x5400e, Direction=N/A */ + uint8_t reserved1e; /* Byte offset 0x1e, CSR Addr 0x5400f, Direction=N/A */ + uint8_t share2dvrefresult; /* + * Byte offset 0x1f, CSR Addr 0x5400f, Direction=In + * Bitmap that designates the phy's vref source for every + * pstate + * If share2dvrefresult[x] = 0, then after 2D training, + * pstate x will continue using the phyVref provided in + * pstate x's 1D messageblock. + * If share2dvrefresult[x] = 1, then after 2D training, + * pstate x will use the per-lane VrefDAC0/1 CSRs trained by + * 2d training. + */ + uint8_t reserved20; /* Byte offset 0x20, CSR Addr 0x54010, Direction=N/A */ + uint8_t reserved21; /* Byte offset 0x21, CSR Addr 0x54010, Direction=N/A */ + uint16_t phyconfigoverride; /* + * Byte offset 0x22, CSR Addr 0x54011, Direction=In + * Override PhyConfig csr. + * 0x0: Use hardware csr value for PhyConfing + * (recommended) + * Other values: Use value for PhyConfig instead of + * Hardware value. + * + */ + uint8_t enableddqscha; /* + * Byte offset 0x24, CSR Addr 0x54012, Direction=In + * Total number of DQ bits enabled in PHY Channel A + */ + uint8_t cspresentcha; /* + * Byte offset 0x25, CSR Addr 0x54012, Direction=In + * Indicates presence of DRAM at each chip select for PHY + * channel A. + * 0x1 = CS0 is populated with DRAM + * 0x3 = CS0 and CS1 are populated with DRAM + * + * All other encodings are illegal + */ + int8_t cdd_cha_rr_1_0; /* + * Byte offset 0x26, CSR Addr 0x54013, Direction=Out + * This is a signed integer value. + * Read to read critical delay difference from cs 1 to cs 0 + * on Channel A. + */ + int8_t cdd_cha_rr_0_1; /* + * Byte offset 0x27, CSR Addr 0x54013, Direction=Out + * This is a signed integer value. + * Read to read critical delay difference from cs 0 to cs 1 + * on Channel A. + */ + int8_t cdd_cha_rw_1_1; /* + * Byte offset 0x28, CSR Addr 0x54014, Direction=Out + * This is a signed integer value. + * Read to write critical delay difference from cs 1 to cs 1 + * on Channel A. + */ + int8_t cdd_cha_rw_1_0; /* + * Byte offset 0x29, CSR Addr 0x54014, Direction=Out + * This is a signed integer value. + * Read to write critical delay difference from cs 1 to cs 0 + * on Channel A. + */ + int8_t cdd_cha_rw_0_1; /* + * Byte offset 0x2a, CSR Addr 0x54015, Direction=Out + * This is a signed integer value. + * Read to write critical delay difference from cs 0 to cs 1 + * on Channel A. + */ + int8_t cdd_cha_rw_0_0; /* + * Byte offset 0x2b, CSR Addr 0x54015, Direction=Out + * This is a signed integer value. + * Read to write critical delay difference from cs0 to cs 0 + * on Channel A. + */ + int8_t cdd_cha_wr_1_1; /* + * Byte offset 0x2c, CSR Addr 0x54016, Direction=Out + * This is a signed integer value. + * Write to read critical delay difference from cs 1 to cs 1 + * on Channel A. + */ + int8_t cdd_cha_wr_1_0; /* + * Byte offset 0x2d, CSR Addr 0x54016, Direction=Out + * This is a signed integer value. + * Write to read critical delay difference from cs 1 to cs 0 + * on Channel A. + */ + int8_t cdd_cha_wr_0_1; /* + * Byte offset 0x2e, CSR Addr 0x54017, Direction=Out + * This is a signed integer value. + * Write to read critical delay difference from cs 0 to cs 1 + * on Channel A. + */ + int8_t cdd_cha_wr_0_0; /* + * Byte offset 0x2f, CSR Addr 0x54017, Direction=Out + * This is a signed integer value. + * Write to read critical delay difference from cs 0 to cs 0 + * on Channel A. + */ + int8_t cdd_cha_ww_1_0; /* + * Byte offset 0x30, CSR Addr 0x54018, Direction=Out + * This is a signed integer value. + * Write to write critical delay difference from cs 1 to cs + * 0 on Channel A. + */ + int8_t cdd_cha_ww_0_1; /* + * Byte offset 0x31, CSR Addr 0x54018, Direction=Out + * This is a signed integer value. + * Write to write critical delay difference from cs 0 to cs + * 1 on Channel A. + */ + uint8_t mr1_a0; /* + * Byte offset 0x32, CSR Addr 0x54019, Direction=In + * Value to be programmed in DRAM Mode Register 1 + * {Channel A, Rank 0} + */ + uint8_t mr2_a0; /* + * Byte offset 0x33, CSR Addr 0x54019, Direction=In + * Value to be programmed in DRAM Mode Register 2 + * {Channel A, Rank 0} + */ + uint8_t mr3_a0; /* + * Byte offset 0x34, CSR Addr 0x5401a, Direction=In + * Value to be programmed in DRAM Mode Register 3 + * {Channel A, Rank 0} + */ + uint8_t mr4_a0; /* + * Byte offset 0x35, CSR Addr 0x5401a, Direction=In + * Value to be programmed in DRAM Mode Register 4 + * {Channel A, Rank 0} + */ + uint8_t mr11_a0; /* + * Byte offset 0x36, CSR Addr 0x5401b, Direction=In + * Value to be programmed in DRAM Mode Register 11 + * {Channel A, Rank 0} + */ + uint8_t mr12_a0; /* + * Byte offset 0x37, CSR Addr 0x5401b, Direction=In + * Value to be programmed in DRAM Mode Register 12 + * {Channel A, Rank 0} + */ + uint8_t mr13_a0; /* + * Byte offset 0x38, CSR Addr 0x5401c, Direction=In + * Value to be programmed in DRAM Mode Register 13 + * {Channel A, Rank 0} + */ + uint8_t mr14_a0; /* + * Byte offset 0x39, CSR Addr 0x5401c, Direction=In + * Value to be programmed in DRAM Mode Register 14 + * {Channel A, Rank 0} + */ + uint8_t mr16_a0; /* + * Byte offset 0x3a, CSR Addr 0x5401d, Direction=In + * Value to be programmed in DRAM Mode Register 16 + * {Channel A, Rank 0} + */ + uint8_t mr17_a0; /* + * Byte offset 0x3b, CSR Addr 0x5401d, Direction=In + * Value to be programmed in DRAM Mode Register 17 + * {Channel A, Rank 0} + */ + uint8_t mr22_a0; /* + * Byte offset 0x3c, CSR Addr 0x5401e, Direction=In + * Value to be programmed in DRAM Mode Register 22 + * {Channel A, Rank 0} + */ + uint8_t mr24_a0; /* + * Byte offset 0x3d, CSR Addr 0x5401e, Direction=In + * Value to be programmed in DRAM Mode Register 24 + * {Channel A, Rank 0} + */ + uint8_t mr1_a1; /* + * Byte offset 0x3e, CSR Addr 0x5401f, Direction=In + * Value to be programmed in DRAM Mode Register 1 + * {Channel A, Rank 1} + */ + uint8_t mr2_a1; /* + * Byte offset 0x3f, CSR Addr 0x5401f, Direction=In + * Value to be programmed in DRAM Mode Register 2 + * {Channel A, Rank 1} + */ + uint8_t mr3_a1; /* + * Byte offset 0x40, CSR Addr 0x54020, Direction=In + * Value to be programmed in DRAM Mode Register 3 + * {Channel A, Rank 1} + */ + uint8_t mr4_a1; /* + * Byte offset 0x41, CSR Addr 0x54020, Direction=In + * Value to be programmed in DRAM Mode Register 4 + * {Channel A, Rank 1} + */ + uint8_t mr11_a1; /* + * Byte offset 0x42, CSR Addr 0x54021, Direction=In + * Value to be programmed in DRAM Mode Register 11 + * {Channel A, Rank 1} + */ + uint8_t mr12_a1; /* + * Byte offset 0x43, CSR Addr 0x54021, Direction=In + * Value to be programmed in DRAM Mode Register 12 + * {Channel A, Rank 1} + */ + uint8_t mr13_a1; /* + * Byte offset 0x44, CSR Addr 0x54022, Direction=In + * Value to be programmed in DRAM Mode Register 13 + * {Channel A, Rank 1} + */ + uint8_t mr14_a1; /* + * Byte offset 0x45, CSR Addr 0x54022, Direction=In + * Value to be programmed in DRAM Mode Register 14 + * {Channel A, Rank 1} + */ + uint8_t mr16_a1; /* + * Byte offset 0x46, CSR Addr 0x54023, Direction=In + * Value to be programmed in DRAM Mode Register 16 + * {Channel A, Rank 1} + */ + uint8_t mr17_a1; /* + * Byte offset 0x47, CSR Addr 0x54023, Direction=In + * Value to be programmed in DRAM Mode Register 17 + * {Channel A, Rank 1} + */ + uint8_t mr22_a1; /* + * Byte offset 0x48, CSR Addr 0x54024, Direction=In + * Value to be programmed in DRAM Mode Register 22 + * {Channel A, Rank 1} + */ + uint8_t mr24_a1; /* + * Byte offset 0x49, CSR Addr 0x54024, Direction=In + * Value to be programmed in DRAM Mode Register 24 + * {Channel A, Rank 1} + */ + uint8_t caterminatingrankcha; /* Byte offset 0x4a, CSR Addr 0x54025, Direction=In + * Terminating Rank for CA bus on Channel A + * 0x0 = Rank 0 is terminating rank + * 0x1 = Rank 1 is terminating rank + */ + uint8_t reserved4b; /* Byte offset 0x4b, CSR Addr 0x54025, Direction=N/A */ + uint8_t reserved4c; /* Byte offset 0x4c, CSR Addr 0x54026, Direction=N/A */ + uint8_t reserved4d; /* Byte offset 0x4d, CSR Addr 0x54026, Direction=N/A */ + uint8_t reserved4e; /* Byte offset 0x4e, CSR Addr 0x54027, Direction=N/A */ + uint8_t reserved4f; /* Byte offset 0x4f, CSR Addr 0x54027, Direction=N/A */ + uint8_t reserved50; /* Byte offset 0x50, CSR Addr 0x54028, Direction=N/A */ + uint8_t reserved51; /* Byte offset 0x51, CSR Addr 0x54028, Direction=N/A */ + uint8_t reserved52; /* Byte offset 0x52, CSR Addr 0x54029, Direction=N/A */ + uint8_t reserved53; /* Byte offset 0x53, CSR Addr 0x54029, Direction=N/A */ + uint8_t reserved54; /* Byte offset 0x54, CSR Addr 0x5402a, Direction=N/A */ + uint8_t reserved55; /* Byte offset 0x55, CSR Addr 0x5402a, Direction=N/A */ + uint8_t reserved56; /* Byte offset 0x56, CSR Addr 0x5402b, Direction=N/A */ + uint8_t enableddqschb; /* + * Byte offset 0x57, CSR Addr 0x5402b, Direction=In + * Total number of DQ bits enabled in PHY Channel B + */ + uint8_t cspresentchb; /* + * Byte offset 0x58, CSR Addr 0x5402c, Direction=In + * Indicates presence of DRAM at each chip select for PHY + * channel B. + * 0x0 = No chip selects are populated with DRAM + * 0x1 = CS0 is populated with DRAM + * 0x3 = CS0 and CS1 are populated with DRAM + * + * All other encodings are illegal + */ + int8_t cdd_chb_rr_1_0; /* + * Byte offset 0x59, CSR Addr 0x5402c, Direction=Out + * This is a signed integer value. + * Read to read critical delay difference from cs 1 to cs 0 + * on Channel B. + */ + int8_t cdd_chb_rr_0_1; /* + * Byte offset 0x5a, CSR Addr 0x5402d, Direction=Out + * This is a signed integer value. + * Read to read critical delay difference from cs 0 to cs 1 + * on Channel B. + */ + int8_t cdd_chb_rw_1_1; /* + * Byte offset 0x5b, CSR Addr 0x5402d, Direction=Out + * This is a signed integer value. + * Read to write critical delay difference from cs 1 to cs 1 + * on Channel B. + */ + int8_t cdd_chb_rw_1_0; /* + * Byte offset 0x5c, CSR Addr 0x5402e, Direction=Out + * This is a signed integer value. + * Read to write critical delay difference from cs 1 to cs 0 + * on Channel B. + */ + int8_t cdd_chb_rw_0_1; /* + * Byte offset 0x5d, CSR Addr 0x5402e, Direction=Out + * This is a signed integer value. + * Read to write critical delay difference from cs 0 to cs 1 + * on Channel B. + */ + int8_t cdd_chb_rw_0_0; /* + * Byte offset 0x5e, CSR Addr 0x5402f, Direction=Out + * This is a signed integer value. + * Read to write critical delay difference from cs01 to cs 0 + * on Channel B. + */ + int8_t cdd_chb_wr_1_1; /* + * Byte offset 0x5f, CSR Addr 0x5402f, Direction=Out + * This is a signed integer value. + * Write to read critical delay difference from cs 1 to cs 1 + * on Channel B. + */ + int8_t cdd_chb_wr_1_0; /* + * Byte offset 0x60, CSR Addr 0x54030, Direction=Out + * This is a signed integer value. + * Write to read critical delay difference from cs 1 to cs 0 + * on Channel B. + */ + int8_t cdd_chb_wr_0_1; /* + * Byte offset 0x61, CSR Addr 0x54030, Direction=Out + * This is a signed integer value. + * Write to read critical delay difference from cs 0 to cs 1 + * on Channel B. + */ + int8_t cdd_chb_wr_0_0; /* + * Byte offset 0x62, CSR Addr 0x54031, Direction=Out + * This is a signed integer value. + * Write to read critical delay difference from cs 0 to cs 0 + * on Channel B. + */ + int8_t cdd_chb_ww_1_0; /* + * Byte offset 0x63, CSR Addr 0x54031, Direction=Out + * This is a signed integer value. + * Write to write critical delay difference from cs 1 to cs + * 0 on Channel B. + */ + int8_t cdd_chb_ww_0_1; /* + * Byte offset 0x64, CSR Addr 0x54032, Direction=Out + * This is a signed integer value. + * Write to write critical delay difference from cs 0 to cs + * 1 on Channel B. + */ + uint8_t mr1_b0; /* + * Byte offset 0x65, CSR Addr 0x54032, Direction=In + * Value to be programmed in DRAM Mode Register 1 + * {Channel B, Rank 0} + */ + uint8_t mr2_b0; /* + * Byte offset 0x66, CSR Addr 0x54033, Direction=In + * Value to be programmed in DRAM Mode Register 2 + * {Channel B, Rank 0} + */ + uint8_t mr3_b0; /* + * Byte offset 0x67, CSR Addr 0x54033, Direction=In + * Value to be programmed in DRAM Mode Register 3 + * {Channel B, Rank 0} + */ + uint8_t mr4_b0; /* + * Byte offset 0x68, CSR Addr 0x54034, Direction=In + * Value to be programmed in DRAM Mode Register 4 + * {Channel B, Rank 0} + */ + uint8_t mr11_b0; /* + * Byte offset 0x69, CSR Addr 0x54034, Direction=In + * Value to be programmed in DRAM Mode Register 11 + * {Channel B, Rank 0} + */ + uint8_t mr12_b0; /* + * Byte offset 0x6a, CSR Addr 0x54035, Direction=In + * Value to be programmed in DRAM Mode Register 12 + * {Channel B, Rank 0} + */ + uint8_t mr13_b0; /* + * Byte offset 0x6b, CSR Addr 0x54035, Direction=In + * Value to be programmed in DRAM Mode Register 13 + * {Channel B, Rank 0} + */ + uint8_t mr14_b0; /* + * Byte offset 0x6c, CSR Addr 0x54036, Direction=In + * Value to be programmed in DRAM Mode Register 14 + * {Channel B, Rank 0} + */ + uint8_t mr16_b0; /* + * Byte offset 0x6d, CSR Addr 0x54036, Direction=In + * Value to be programmed in DRAM Mode Register 16 + * {Channel B, Rank 0} + */ + uint8_t mr17_b0; /* + * Byte offset 0x6e, CSR Addr 0x54037, Direction=In + * Value to be programmed in DRAM Mode Register 17 + * {Channel B, Rank 0} + */ + uint8_t mr22_b0; /* + * Byte offset 0x6f, CSR Addr 0x54037, Direction=In + * Value to be programmed in DRAM Mode Register 22 + * {Channel B, Rank 0} + */ + uint8_t mr24_b0; /* + * Byte offset 0x70, CSR Addr 0x54038, Direction=In + * Value to be programmed in DRAM Mode Register 24 + * {Channel B, Rank 0} + */ + uint8_t mr1_b1; /* + * Byte offset 0x71, CSR Addr 0x54038, Direction=In + * Value to be programmed in DRAM Mode Register 1 + * {Channel B, Rank 1} + */ + uint8_t mr2_b1; /* + * Byte offset 0x72, CSR Addr 0x54039, Direction=In + * Value to be programmed in DRAM Mode Register 2 + * {Channel B, Rank 1} + */ + uint8_t mr3_b1; /* + * Byte offset 0x73, CSR Addr 0x54039, Direction=In + * Value to be programmed in DRAM Mode Register 3 + * {Channel B, Rank 1} + */ + uint8_t mr4_b1; /* + * Byte offset 0x74, CSR Addr 0x5403a, Direction=In + * Value to be programmed in DRAM Mode Register 4 + * {Channel B, Rank 1} + */ + uint8_t mr11_b1; /* + * Byte offset 0x75, CSR Addr 0x5403a, Direction=In + * Value to be programmed in DRAM Mode Register 11 + * {Channel B, Rank 1} + */ + uint8_t mr12_b1; /* + * Byte offset 0x76, CSR Addr 0x5403b, Direction=In + * Value to be programmed in DRAM Mode Register 12 + * {Channel B, Rank 1} + */ + uint8_t mr13_b1; /* + * Byte offset 0x77, CSR Addr 0x5403b, Direction=In + * Value to be programmed in DRAM Mode Register 13 + * {Channel B, Rank 1} + */ + uint8_t mr14_b1; /* + * Byte offset 0x78, CSR Addr 0x5403c, Direction=In + * Value to be programmed in DRAM Mode Register 14 + * {Channel B, Rank 1} + */ + uint8_t mr16_b1; /* + * Byte offset 0x79, CSR Addr 0x5403c, Direction=In + * Value to be programmed in DRAM Mode Register 16 + * {Channel B, Rank 1} + */ + uint8_t mr17_b1; /* + * Byte offset 0x7a, CSR Addr 0x5403d, Direction=In + * Value to be programmed in DRAM Mode Register 17 + * {Channel B, Rank 1} + */ + uint8_t mr22_b1; /* + * Byte offset 0x7b, CSR Addr 0x5403d, Direction=In + * Value to be programmed in DRAM Mode Register 22 + * {Channel B, Rank 1} + */ + uint8_t mr24_b1; /* + * Byte offset 0x7c, CSR Addr 0x5403e, Direction=In + * Value to be programmed in DRAM Mode Register 24 + * {Channel B, Rank 1} + */ + uint8_t caterminatingrankchb; /* Byte offset 0x7d, CSR Addr 0x5403e, Direction=In + * Terminating Rank for CA bus on Channel B + * 0x0 = Rank 0 is terminating rank + * 0x1 = Rank 1 is terminating rank + */ + uint8_t reserved7e; /* Byte offset 0x7e, CSR Addr 0x5403f, Direction=N/A */ + uint8_t reserved7f; /* Byte offset 0x7f, CSR Addr 0x5403f, Direction=N/A */ + uint8_t reserved80; /* Byte offset 0x80, CSR Addr 0x54040, Direction=N/A */ + uint8_t reserved81; /* Byte offset 0x81, CSR Addr 0x54040, Direction=N/A */ + uint8_t reserved82; /* Byte offset 0x82, CSR Addr 0x54041, Direction=N/A */ + uint8_t reserved83; /* Byte offset 0x83, CSR Addr 0x54041, Direction=N/A */ + uint8_t reserved84; /* Byte offset 0x84, CSR Addr 0x54042, Direction=N/A */ + uint8_t reserved85; /* Byte offset 0x85, CSR Addr 0x54042, Direction=N/A */ + uint8_t reserved86; /* Byte offset 0x86, CSR Addr 0x54043, Direction=N/A */ + uint8_t reserved87; /* Byte offset 0x87, CSR Addr 0x54043, Direction=N/A */ + uint8_t reserved88; /* Byte offset 0x88, CSR Addr 0x54044, Direction=N/A */ + uint8_t reserved89; /* Byte offset 0x89, CSR Addr 0x54044, Direction=N/A */ +} __packed __aligned(2); + +#endif /* MNPMUSRAMMSGBLOCK_LPDDR4_H */ diff --git a/drivers/st/ddr/phy/phyinit/include/ddrphy_csr_all_cdefines.h b/drivers/st/ddr/phy/phyinit/include/ddrphy_csr_all_cdefines.h new file mode 100644 index 00000000..99a8c4cc --- /dev/null +++ b/drivers/st/ddr/phy/phyinit/include/ddrphy_csr_all_cdefines.h @@ -0,0 +1,6944 @@ +/* + * Copyright (C) 2021-2024, STMicroelectronics - All Rights Reserved + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef DDRPHY_PHYINIT_CSR_ALL_DEFINES_H +#define DDRPHY_PHYINIT_CSR_ALL_DEFINES_H + +/* ANIBx register offsets */ +#define CSR_MTESTMUXSEL_ADDR 0x1AU +#define CSR_AFORCEDRVCONT_ADDR 0x27U +#define CSR_AFORCETRICONT_ADDR 0x28U +#define CSR_ATXIMPEDANCE_ADDR 0x43U +#define CSR_ATESTPRBSERR_ADDR 0x53U +#define CSR_ATXSLEWRATE_ADDR 0x55U +#define CSR_ATESTPRBSERRCNT_ADDR 0x56U +#define CSR_ATXDLY_ADDR 0x80U + +/* DBYTEx register offsets */ +#define CSR_DBYTEMISCMODE_ADDR 0x0U +#define CSR_TSMBYTE0_ADDR 0x1U +#define CSR_TRAININGPARAM_ADDR 0x2U +#define CSR_USEDQSENREPLICA_ADDR 0x3U +#define CSR_RXTRAINPATTERNENABLE_ADDR 0x10U +#define CSR_TSMBYTE1_ADDR 0x11U +#define CSR_TSMBYTE2_ADDR 0x12U +#define CSR_TSMBYTE3_ADDR 0x13U +#define CSR_TSMBYTE4_ADDR 0x14U +#define CSR_TESTMODECONFIG_ADDR 0x17U +#define CSR_TSMBYTE5_ADDR 0x18U +/* MTESTMUXSEL already defined in ANIBx section */ +#define CSR_DTSMTRAINMODECTRL_ADDR 0x1FU +#define CSR_DFIMRL_ADDR 0x20U +#define CSR_ASYNCDBYTEMODE_ADDR 0x24U +#define CSR_ASYNCDBYTETXEN_ADDR 0x26U +#define CSR_ASYNCDBYTETXDATA_ADDR 0x28U +#define CSR_ASYNCDBYTERXDATA_ADDR 0x2AU +#define CSR_VREFDAC1_ADDR 0x30U +#define CSR_TRAININGCNTR_ADDR 0x32U +#define CSR_VREFDAC0_ADDR 0x40U +#define CSR_TXIMPEDANCECTRL0_ADDR 0x41U +#define CSR_DQDQSRCVCNTRL_ADDR 0x43U +#define CSR_TXEQUALIZATIONMODE_ADDR 0x48U +#define CSR_TXIMPEDANCECTRL1_ADDR 0x49U +#define CSR_DQDQSRCVCNTRL1_ADDR 0x4AU +#define CSR_TXIMPEDANCECTRL2_ADDR 0x4BU +#define CSR_DQDQSRCVCNTRL2_ADDR 0x4CU +#define CSR_TXODTDRVSTREN_ADDR 0x4DU +#define CSR_RXFIFOCHECKSTATUS_ADDR 0x56U +#define CSR_RXFIFOCHECKERRVALUES_ADDR 0x57U +#define CSR_RXFIFOINFO_ADDR 0x58U +#define CSR_RXFIFOVISIBILITY_ADDR 0x59U +#define CSR_RXFIFOCONTENTSDQ3210_ADDR 0x5AU +#define CSR_RXFIFOCONTENTSDQ7654_ADDR 0x5BU +#define CSR_RXFIFOCONTENTSDBI_ADDR 0x5CU +#define CSR_TXSLEWRATE_ADDR 0x5FU +#define CSR_TRAININGINCDECDTSMEN_ADDR 0x62U +#define CSR_RXPBDLYTG0_ADDR 0x68U +#define CSR_RXPBDLYTG1_ADDR 0x69U +#define CSR_RXPBDLYTG2_ADDR 0x6AU +#define CSR_RXPBDLYTG3_ADDR 0x6BU +#define CSR_RXENDLYTG0_ADDR 0x80U +#define CSR_RXENDLYTG1_ADDR 0x81U +#define CSR_RXENDLYTG2_ADDR 0x82U +#define CSR_RXENDLYTG3_ADDR 0x83U +#define CSR_RXCLKDLYTG0_ADDR 0x8CU +#define CSR_RXCLKDLYTG1_ADDR 0x8DU +#define CSR_RXCLKDLYTG2_ADDR 0x8EU +#define CSR_RXCLKDLYTG3_ADDR 0x8FU +#define CSR_RXCLKCDLYTG0_ADDR 0x90U +#define CSR_RXCLKCDLYTG1_ADDR 0x91U +#define CSR_RXCLKCDLYTG2_ADDR 0x92U +#define CSR_RXCLKCDLYTG3_ADDR 0x93U +#define CSR_DQ0LNSEL_ADDR 0xA0U +#define CSR_DQ1LNSEL_ADDR 0xA1U +#define CSR_DQ2LNSEL_ADDR 0xA2U +#define CSR_DQ3LNSEL_ADDR 0xA3U +#define CSR_DQ4LNSEL_ADDR 0xA4U +#define CSR_DQ5LNSEL_ADDR 0xA5U +#define CSR_DQ6LNSEL_ADDR 0xA6U +#define CSR_DQ7LNSEL_ADDR 0xA7U +#define CSR_PPTCTLSTATIC_ADDR 0xAAU +#define CSR_PPTCTLDYN_ADDR 0xABU +#define CSR_PPTINFO_ADDR 0xACU +#define CSR_PPTRXENEVNT_ADDR 0xADU +#define CSR_PPTDQSCNTINVTRNTG0_ADDR 0xAEU +#define CSR_PPTDQSCNTINVTRNTG1_ADDR 0xAFU +#define CSR_DTSMBLANKINGCTRL_ADDR 0xB1U +#define CSR_TSM0_ADDR 0xB2U +#define CSR_TSM1_ADDR 0xB3U +#define CSR_TSM2_ADDR 0xB4U +#define CSR_TSM3_ADDR 0xB5U +#define CSR_TXCHKDATASELECTS_ADDR 0xB6U +#define CSR_DTSMUPTHLDXINGIND_ADDR 0xB7U +#define CSR_DTSMLOTHLDXINGIND_ADDR 0xB8U +#define CSR_DBYTEALLDTSMCTRL0_ADDR 0xB9U +#define CSR_DBYTEALLDTSMCTRL1_ADDR 0xBAU +#define CSR_DBYTEALLDTSMCTRL2_ADDR 0xBBU +#define CSR_TXDQDLYTG0_ADDR 0xC0U +#define CSR_TXDQDLYTG1_ADDR 0xC1U +#define CSR_TXDQDLYTG2_ADDR 0xC2U +#define CSR_TXDQDLYTG3_ADDR 0xC3U +#define CSR_TXDQSDLYTG0_ADDR 0xD0U +#define CSR_TXDQSDLYTG1_ADDR 0xD1U +#define CSR_TXDQSDLYTG2_ADDR 0xD2U +#define CSR_TXDQSDLYTG3_ADDR 0xD3U +#define CSR_DXLCDLSTATUS_ADDR 0xE4U + +/* MASTER0 register offsets */ +#define CSR_RXFIFOINIT_ADDR 0x0U +#define CSR_FORCECLKDISABLE_ADDR 0x1U +#define CSR_CLOCKINGCTRL_ADDR 0x2U +#define CSR_FORCEINTERNALUPDATE_ADDR 0x3U +#define CSR_PHYCONFIG_ADDR 0x4U +#define CSR_PGCR_ADDR 0x5U +#define CSR_TESTBUMPCNTRL1_ADDR 0x7U +#define CSR_CALUCLKINFO_ADDR 0x8U +#define CSR_TESTBUMPCNTRL_ADDR 0xAU +#define CSR_SEQ0BDLY0_ADDR 0xBU +#define CSR_SEQ0BDLY1_ADDR 0xCU +#define CSR_SEQ0BDLY2_ADDR 0xDU +#define CSR_SEQ0BDLY3_ADDR 0xEU +#define CSR_PHYALERTSTATUS_ADDR 0xFU +#define CSR_PPTTRAINSETUP_ADDR 0x10U +#define CSR_PPTTRAINSETUP2_ADDR 0x11U +#define CSR_ATESTMODE_ADDR 0x12U +#define CSR_TXCALBINP_ADDR 0x14U +#define CSR_TXCALBINN_ADDR 0x15U +#define CSR_TXCALPOVR_ADDR 0x16U +#define CSR_TXCALNOVR_ADDR 0x17U +#define CSR_DFIMODE_ADDR 0x18U +#define CSR_TRISTATEMODECA_ADDR 0x19U +/* MTESTMUXSEL already defined in ANIBx section */ +#define CSR_MTESTPGMINFO_ADDR 0x1BU +#define CSR_DYNPWRDNUP_ADDR 0x1CU +#define CSR_PMIENABLE_ADDR 0x1DU +#define CSR_PHYTID_ADDR 0x1EU +#define CSR_HWTMRL_ADDR 0x20U +#define CSR_DFIPHYUPD_ADDR 0x21U +#define CSR_PDAMRSWRITEMODE_ADDR 0x22U +#define CSR_DFIGEARDOWNCTL_ADDR 0x23U +#define CSR_DQSPREAMBLECONTROL_ADDR 0x24U +#define CSR_MASTERX4CONFIG_ADDR 0x25U +#define CSR_WRLEVBITS_ADDR 0x26U +#define CSR_ENABLECSMULTICAST_ADDR 0x27U +#define CSR_HWTLPCSMULTICAST_ADDR 0x28U +#define CSR_ACX4ANIBDIS_ADDR 0x2CU +#define CSR_DMIPINPRESENT_ADDR 0x2DU +#define CSR_ARDPTRINITVAL_ADDR 0x2EU +#define CSR_DB0LCDLCALPHDETOUT_ADDR 0x30U +#define CSR_DB1LCDLCALPHDETOUT_ADDR 0x31U +#define CSR_DB2LCDLCALPHDETOUT_ADDR 0x32U +#define CSR_DB3LCDLCALPHDETOUT_ADDR 0x33U +#define CSR_DB4LCDLCALPHDETOUT_ADDR 0x34U +#define CSR_DB5LCDLCALPHDETOUT_ADDR 0x35U +#define CSR_DB6LCDLCALPHDETOUT_ADDR 0x36U +#define CSR_DB7LCDLCALPHDETOUT_ADDR 0x37U +#define CSR_DB8LCDLCALPHDETOUT_ADDR 0x38U +#define CSR_DB9LCDLCALPHDETOUT_ADDR 0x39U +#define CSR_DBYTEDLLMODECNTRL_ADDR 0x3AU +#define CSR_DBYTERXENTRAIN_ADDR 0x3BU +#define CSR_ANLCDLCALPHDETOUT_ADDR 0x3FU +#define CSR_CALOFFSETS_ADDR 0x45U +#define CSR_SARINITVALS_ADDR 0x47U +#define CSR_CALPEXTOVR_ADDR 0x49U +#define CSR_CALCMPR5OVR_ADDR 0x4AU +#define CSR_CALNINTOVR_ADDR 0x4BU +#define CSR_CALDRVSTR0_ADDR 0x50U +#define CSR_PROCODTCTL_ADDR 0x55U +#define CSR_PROCODTTIMECTL_ADDR 0x56U +#define CSR_MEMALERTCONTROL_ADDR 0x5BU +#define CSR_MEMALERTCONTROL2_ADDR 0x5CU +#define CSR_MEMRESETL_ADDR 0x60U +#define CSR_PUBMODE_ADDR 0x6EU +#define CSR_MISCPHYSTATUS_ADDR 0x6FU +#define CSR_CORELOOPBACKSEL_ADDR 0x70U +#define CSR_DLLTRAINPARAM_ADDR 0x71U +#define CSR_HWTLPCSENA_ADDR 0x72U +#define CSR_HWTLPCSENB_ADDR 0x73U +#define CSR_HWTLPCSENBYPASS_ADDR 0x74U +#define CSR_DFICAMODE_ADDR 0x75U +#define CSR_HWTCACTL_ADDR 0x76U +#define CSR_HWTCAMODE_ADDR 0x77U +#define CSR_DLLCONTROL_ADDR 0x78U +#define CSR_PULSEDLLUPDATEPHASE_ADDR 0x79U +#define CSR_HWTCONTROLOVR0_ADDR 0x7AU +#define CSR_HWTCONTROLOVR1_ADDR 0x7BU +#define CSR_DLLGAINCTL_ADDR 0x7CU +#define CSR_DLLLOCKPARAM_ADDR 0x7DU +#define CSR_HWTCONTROLVAL0_ADDR 0x7EU +#define CSR_HWTCONTROLVAL1_ADDR 0x7FU +#define CSR_ACSMGLBLSTART_ADDR 0x81U +#define CSR_ACSMGLBLSGLSTPCTRL_ADDR 0x82U +#define CSR_LCDLCALPHASE_ADDR 0x84U +#define CSR_LCDLCALCTRL_ADDR 0x85U +#define CSR_CALRATE_ADDR 0x88U +#define CSR_CALZAP_ADDR 0x89U +#define CSR_PSTATE_ADDR 0x8BU +#define CSR_CALPREDRIVEROVERRIDE_ADDR 0x8CU +#define CSR_PLLOUTGATECONTROL_ADDR 0x8DU +#define CSR_UCMEMRESETCONTROL_ADDR 0x8FU +#define CSR_PORCONTROL_ADDR 0x90U +#define CSR_CALBUSY_ADDR 0x97U +#define CSR_CALMISC2_ADDR 0x98U +#define CSR_CALMISC_ADDR 0x9AU +#define CSR_CALVREFS_ADDR 0x9BU +#define CSR_CALCMPR5_ADDR 0x9CU +#define CSR_CALNINT_ADDR 0x9DU +#define CSR_CALPEXT_ADDR 0x9EU +#define CSR_CALCMPINVERT_ADDR 0xA8U +#define CSR_CALCMPANACNTRL_ADDR 0xAEU +#define CSR_DFIRDDATACSDESTMAP_ADDR 0xB0U +#define CSR_VREFINGLOBAL_ADDR 0xB2U +#define CSR_DFIWRDATACSDESTMAP_ADDR 0xB4U +#define CSR_MASUPDGOODCTR_ADDR 0xB5U +#define CSR_PHYUPD0GOODCTR_ADDR 0xB6U +#define CSR_PHYUPD1GOODCTR_ADDR 0xB7U +#define CSR_CTLUPD0GOODCTR_ADDR 0xB8U +#define CSR_CTLUPD1GOODCTR_ADDR 0xB9U +#define CSR_MASUPDFAILCTR_ADDR 0xBAU +#define CSR_PHYUPD0FAILCTR_ADDR 0xBBU +#define CSR_PHYUPD1FAILCTR_ADDR 0xBCU +#define CSR_PHYPERFCTRENABLE_ADDR 0xBDU +#define CSR_DFIWRRDDATACSCONFIG_ADDR 0xBEU +#define CSR_PLLPWRDN_ADDR 0xC3U +#define CSR_PLLRESET_ADDR 0xC4U +#define CSR_PLLCTRL2_ADDR 0xC5U +#define CSR_PLLCTRL0_ADDR 0xC6U +#define CSR_PLLCTRL1_ADDR 0xC7U +#define CSR_PLLTST_ADDR 0xC8U +#define CSR_PLLLOCKSTATUS_ADDR 0xC9U +#define CSR_PLLTESTMODE_ADDR 0xCAU +#define CSR_PLLCTRL3_ADDR 0xCBU +#define CSR_PLLCTRL4_ADDR 0xCCU +#define CSR_PLLENDOFCAL_ADDR 0xCDU +#define CSR_PLLSTANDBYEFF_ADDR 0xCEU +#define CSR_PLLDACVALOUT_ADDR 0xCFU +#define CSR_DLYTESTSEQ_ADDR 0xD0U +#define CSR_DLYTESTRINGSELDB_ADDR 0xD1U +#define CSR_DLYTESTRINGSELAC_ADDR 0xD2U +#define CSR_DLYTESTCNTDFICLKIV_ADDR 0xD3U +#define CSR_DLYTESTCNTDFICLK_ADDR 0xD4U +#define CSR_DLYTESTCNTRINGOSCDB0_ADDR 0xD5U +#define CSR_DLYTESTCNTRINGOSCDB1_ADDR 0xD6U +#define CSR_DLYTESTCNTRINGOSCDB2_ADDR 0xD7U +#define CSR_DLYTESTCNTRINGOSCDB3_ADDR 0xD8U +#define CSR_DLYTESTCNTRINGOSCDB4_ADDR 0xD9U +#define CSR_DLYTESTCNTRINGOSCDB5_ADDR 0xDAU +#define CSR_DLYTESTCNTRINGOSCDB6_ADDR 0xDBU +#define CSR_DLYTESTCNTRINGOSCDB7_ADDR 0xDCU +#define CSR_DLYTESTCNTRINGOSCDB8_ADDR 0xDDU +#define CSR_DLYTESTCNTRINGOSCDB9_ADDR 0xDEU +#define CSR_DLYTESTCNTRINGOSCAC_ADDR 0xDFU +#define CSR_MSTLCDLDBGCNTL_ADDR 0xE0U +#define CSR_MSTLCDL0DBGRES_ADDR 0xE1U +#define CSR_MSTLCDL1DBGRES_ADDR 0xE2U +#define CSR_LCDLDBGCNTL_ADDR 0xE3U +#define CSR_ACLCDLSTATUS_ADDR 0xE4U +#define CSR_CUSTPHYREV_ADDR 0xEDU +#define CSR_PHYREV_ADDR 0xEEU +#define CSR_LP3EXITSEQ0BSTARTVECTOR_ADDR 0xEFU +#define CSR_DFIFREQXLAT0_ADDR 0xF0U +#define CSR_DFIFREQXLAT1_ADDR 0xF1U +#define CSR_DFIFREQXLAT2_ADDR 0xF2U +#define CSR_DFIFREQXLAT3_ADDR 0xF3U +#define CSR_DFIFREQXLAT4_ADDR 0xF4U +#define CSR_DFIFREQXLAT5_ADDR 0xF5U +#define CSR_DFIFREQXLAT6_ADDR 0xF6U +#define CSR_DFIFREQXLAT7_ADDR 0xF7U +#define CSR_TXRDPTRINIT_ADDR 0xF8U +#define CSR_DFIINITCOMPLETE_ADDR 0xF9U +#define CSR_DFIFREQRATIO_ADDR 0xFAU +#define CSR_RXFIFOCHECKS_ADDR 0xFBU +#define CSR_MTESTDTOCTRL_ADDR 0xFFU +#define CSR_MAPCAA0TODFI_ADDR 0x100U +#define CSR_MAPCAA1TODFI_ADDR 0x101U +#define CSR_MAPCAA2TODFI_ADDR 0x102U +#define CSR_MAPCAA3TODFI_ADDR 0x103U +#define CSR_MAPCAA4TODFI_ADDR 0x104U +#define CSR_MAPCAA5TODFI_ADDR 0x105U +#define CSR_MAPCAA6TODFI_ADDR 0x106U +#define CSR_MAPCAA7TODFI_ADDR 0x107U +#define CSR_MAPCAA8TODFI_ADDR 0x108U +#define CSR_MAPCAA9TODFI_ADDR 0x109U +#define CSR_MAPCAB0TODFI_ADDR 0x110U +#define CSR_MAPCAB1TODFI_ADDR 0x111U +#define CSR_MAPCAB2TODFI_ADDR 0x112U +#define CSR_MAPCAB3TODFI_ADDR 0x113U +#define CSR_MAPCAB4TODFI_ADDR 0x114U +#define CSR_MAPCAB5TODFI_ADDR 0x115U +#define CSR_MAPCAB6TODFI_ADDR 0x116U +#define CSR_MAPCAB7TODFI_ADDR 0x117U +#define CSR_MAPCAB8TODFI_ADDR 0x118U +#define CSR_MAPCAB9TODFI_ADDR 0x119U +#define CSR_PHYINTERRUPTENABLE_ADDR 0x11BU +#define CSR_PHYINTERRUPTFWCONTROL_ADDR 0x11CU +#define CSR_PHYINTERRUPTMASK_ADDR 0x11DU +#define CSR_PHYINTERRUPTCLEAR_ADDR 0x11EU +#define CSR_PHYINTERRUPTSTATUS_ADDR 0x11FU +#define CSR_HWTSWIZZLEHWTADDRESS0_ADDR 0x120U +#define CSR_HWTSWIZZLEHWTADDRESS1_ADDR 0x121U +#define CSR_HWTSWIZZLEHWTADDRESS2_ADDR 0x122U +#define CSR_HWTSWIZZLEHWTADDRESS3_ADDR 0x123U +#define CSR_HWTSWIZZLEHWTADDRESS4_ADDR 0x124U +#define CSR_HWTSWIZZLEHWTADDRESS5_ADDR 0x125U +#define CSR_HWTSWIZZLEHWTADDRESS6_ADDR 0x126U +#define CSR_HWTSWIZZLEHWTADDRESS7_ADDR 0x127U +#define CSR_HWTSWIZZLEHWTADDRESS8_ADDR 0x128U +#define CSR_HWTSWIZZLEHWTADDRESS9_ADDR 0x129U +#define CSR_HWTSWIZZLEHWTADDRESS10_ADDR 0x12AU +#define CSR_HWTSWIZZLEHWTADDRESS11_ADDR 0x12BU +#define CSR_HWTSWIZZLEHWTADDRESS12_ADDR 0x12CU +#define CSR_HWTSWIZZLEHWTADDRESS13_ADDR 0x12DU +#define CSR_HWTSWIZZLEHWTADDRESS14_ADDR 0x12EU +#define CSR_HWTSWIZZLEHWTADDRESS15_ADDR 0x12FU +#define CSR_HWTSWIZZLEHWTADDRESS17_ADDR 0x130U +#define CSR_HWTSWIZZLEHWTACTN_ADDR 0x131U +#define CSR_HWTSWIZZLEHWTBANK0_ADDR 0x132U +#define CSR_HWTSWIZZLEHWTBANK1_ADDR 0x133U +#define CSR_HWTSWIZZLEHWTBANK2_ADDR 0x134U +#define CSR_HWTSWIZZLEHWTBG0_ADDR 0x135U +#define CSR_HWTSWIZZLEHWTBG1_ADDR 0x136U +#define CSR_HWTSWIZZLEHWTCASN_ADDR 0x137U +#define CSR_HWTSWIZZLEHWTRASN_ADDR 0x138U +#define CSR_HWTSWIZZLEHWTWEN_ADDR 0x139U +#define CSR_HWTSWIZZLEHWTPARITYIN_ADDR 0x13AU +#define CSR_DFIHANDSHAKEDELAYS0_ADDR 0x13CU +#define CSR_DFIHANDSHAKEDELAYS1_ADDR 0x13DU +#define CSR_REMOTEIMPCAL_ADDR 0x13EU +#define CSR_ACLOOPBACKCTL_ADDR 0x13FU + +/* ACSM0 register offsets */ +#define CSR_ACSMSEQ0X0_ADDR 0x0U +#define CSR_ACSMSEQ0X1_ADDR 0x1U +#define CSR_ACSMSEQ0X2_ADDR 0x2U +#define CSR_ACSMSEQ0X3_ADDR 0x3U +#define CSR_ACSMSEQ0X4_ADDR 0x4U +#define CSR_ACSMSEQ0X5_ADDR 0x5U +#define CSR_ACSMSEQ0X6_ADDR 0x6U +#define CSR_ACSMSEQ0X7_ADDR 0x7U +#define CSR_ACSMSEQ0X8_ADDR 0x8U +#define CSR_ACSMSEQ0X9_ADDR 0x9U +#define CSR_ACSMSEQ0X10_ADDR 0xAU +#define CSR_ACSMSEQ0X11_ADDR 0xBU +#define CSR_ACSMSEQ0X12_ADDR 0xCU +#define CSR_ACSMSEQ0X13_ADDR 0xDU +#define CSR_ACSMSEQ0X14_ADDR 0xEU +#define CSR_ACSMSEQ0X15_ADDR 0xFU +#define CSR_ACSMSEQ0X16_ADDR 0x10U +#define CSR_ACSMSEQ0X17_ADDR 0x11U +#define CSR_ACSMSEQ0X18_ADDR 0x12U +#define CSR_ACSMSEQ0X19_ADDR 0x13U +#define CSR_ACSMSEQ0X20_ADDR 0x14U +#define CSR_ACSMSEQ0X21_ADDR 0x15U +#define CSR_ACSMSEQ0X22_ADDR 0x16U +#define CSR_ACSMSEQ0X23_ADDR 0x17U +#define CSR_ACSMSEQ0X24_ADDR 0x18U +#define CSR_ACSMSEQ0X25_ADDR 0x19U +#define CSR_ACSMSEQ0X26_ADDR 0x1AU +#define CSR_ACSMSEQ0X27_ADDR 0x1BU +#define CSR_ACSMSEQ0X28_ADDR 0x1CU +#define CSR_ACSMSEQ0X29_ADDR 0x1DU +#define CSR_ACSMSEQ0X30_ADDR 0x1EU +#define CSR_ACSMSEQ0X31_ADDR 0x1FU +#define CSR_ACSMSEQ1X0_ADDR 0x20U +#define CSR_ACSMSEQ1X1_ADDR 0x21U +#define CSR_ACSMSEQ1X2_ADDR 0x22U +#define CSR_ACSMSEQ1X3_ADDR 0x23U +#define CSR_ACSMSEQ1X4_ADDR 0x24U +#define CSR_ACSMSEQ1X5_ADDR 0x25U +#define CSR_ACSMSEQ1X6_ADDR 0x26U +#define CSR_ACSMSEQ1X7_ADDR 0x27U +#define CSR_ACSMSEQ1X8_ADDR 0x28U +#define CSR_ACSMSEQ1X9_ADDR 0x29U +#define CSR_ACSMSEQ1X10_ADDR 0x2AU +#define CSR_ACSMSEQ1X11_ADDR 0x2BU +#define CSR_ACSMSEQ1X12_ADDR 0x2CU +#define CSR_ACSMSEQ1X13_ADDR 0x2DU +#define CSR_ACSMSEQ1X14_ADDR 0x2EU +#define CSR_ACSMSEQ1X15_ADDR 0x2FU +#define CSR_ACSMSEQ1X16_ADDR 0x30U +#define CSR_ACSMSEQ1X17_ADDR 0x31U +#define CSR_ACSMSEQ1X18_ADDR 0x32U +#define CSR_ACSMSEQ1X19_ADDR 0x33U +#define CSR_ACSMSEQ1X20_ADDR 0x34U +#define CSR_ACSMSEQ1X21_ADDR 0x35U +#define CSR_ACSMSEQ1X22_ADDR 0x36U +#define CSR_ACSMSEQ1X23_ADDR 0x37U +#define CSR_ACSMSEQ1X24_ADDR 0x38U +#define CSR_ACSMSEQ1X25_ADDR 0x39U +#define CSR_ACSMSEQ1X26_ADDR 0x3AU +#define CSR_ACSMSEQ1X27_ADDR 0x3BU +#define CSR_ACSMSEQ1X28_ADDR 0x3CU +#define CSR_ACSMSEQ1X29_ADDR 0x3DU +#define CSR_ACSMSEQ1X30_ADDR 0x3EU +#define CSR_ACSMSEQ1X31_ADDR 0x3FU +#define CSR_ACSMSEQ2X0_ADDR 0x40U +#define CSR_ACSMSEQ2X1_ADDR 0x41U +#define CSR_ACSMSEQ2X2_ADDR 0x42U +#define CSR_ACSMSEQ2X3_ADDR 0x43U +#define CSR_ACSMSEQ2X4_ADDR 0x44U +#define CSR_ACSMSEQ2X5_ADDR 0x45U +#define CSR_ACSMSEQ2X6_ADDR 0x46U +#define CSR_ACSMSEQ2X7_ADDR 0x47U +#define CSR_ACSMSEQ2X8_ADDR 0x48U +#define CSR_ACSMSEQ2X9_ADDR 0x49U +#define CSR_ACSMSEQ2X10_ADDR 0x4AU +#define CSR_ACSMSEQ2X11_ADDR 0x4BU +#define CSR_ACSMSEQ2X12_ADDR 0x4CU +#define CSR_ACSMSEQ2X13_ADDR 0x4DU +#define CSR_ACSMSEQ2X14_ADDR 0x4EU +#define CSR_ACSMSEQ2X15_ADDR 0x4FU +#define CSR_ACSMSEQ2X16_ADDR 0x50U +#define CSR_ACSMSEQ2X17_ADDR 0x51U +#define CSR_ACSMSEQ2X18_ADDR 0x52U +#define CSR_ACSMSEQ2X19_ADDR 0x53U +#define CSR_ACSMSEQ2X20_ADDR 0x54U +#define CSR_ACSMSEQ2X21_ADDR 0x55U +#define CSR_ACSMSEQ2X22_ADDR 0x56U +#define CSR_ACSMSEQ2X23_ADDR 0x57U +#define CSR_ACSMSEQ2X24_ADDR 0x58U +#define CSR_ACSMSEQ2X25_ADDR 0x59U +#define CSR_ACSMSEQ2X26_ADDR 0x5AU +#define CSR_ACSMSEQ2X27_ADDR 0x5BU +#define CSR_ACSMSEQ2X28_ADDR 0x5CU +#define CSR_ACSMSEQ2X29_ADDR 0x5DU +#define CSR_ACSMSEQ2X30_ADDR 0x5EU +#define CSR_ACSMSEQ2X31_ADDR 0x5FU +#define CSR_ACSMSEQ3X0_ADDR 0x60U +#define CSR_ACSMSEQ3X1_ADDR 0x61U +#define CSR_ACSMSEQ3X2_ADDR 0x62U +#define CSR_ACSMSEQ3X3_ADDR 0x63U +#define CSR_ACSMSEQ3X4_ADDR 0x64U +#define CSR_ACSMSEQ3X5_ADDR 0x65U +#define CSR_ACSMSEQ3X6_ADDR 0x66U +#define CSR_ACSMSEQ3X7_ADDR 0x67U +#define CSR_ACSMSEQ3X8_ADDR 0x68U +#define CSR_ACSMSEQ3X9_ADDR 0x69U +#define CSR_ACSMSEQ3X10_ADDR 0x6AU +#define CSR_ACSMSEQ3X11_ADDR 0x6BU +#define CSR_ACSMSEQ3X12_ADDR 0x6CU +#define CSR_ACSMSEQ3X13_ADDR 0x6DU +#define CSR_ACSMSEQ3X14_ADDR 0x6EU +#define CSR_ACSMSEQ3X15_ADDR 0x6FU +#define CSR_ACSMSEQ3X16_ADDR 0x70U +#define CSR_ACSMSEQ3X17_ADDR 0x71U +#define CSR_ACSMSEQ3X18_ADDR 0x72U +#define CSR_ACSMSEQ3X19_ADDR 0x73U +#define CSR_ACSMSEQ3X20_ADDR 0x74U +#define CSR_ACSMSEQ3X21_ADDR 0x75U +#define CSR_ACSMSEQ3X22_ADDR 0x76U +#define CSR_ACSMSEQ3X23_ADDR 0x77U +#define CSR_ACSMSEQ3X24_ADDR 0x78U +#define CSR_ACSMSEQ3X25_ADDR 0x79U +#define CSR_ACSMSEQ3X26_ADDR 0x7AU +#define CSR_ACSMSEQ3X27_ADDR 0x7BU +#define CSR_ACSMSEQ3X28_ADDR 0x7CU +#define CSR_ACSMSEQ3X29_ADDR 0x7DU +#define CSR_ACSMSEQ3X30_ADDR 0x7EU +#define CSR_ACSMSEQ3X31_ADDR 0x7FU +#define CSR_ACSMPLAYBACK0X0_ADDR 0x80U +#define CSR_ACSMPLAYBACK1X0_ADDR 0x81U +#define CSR_ACSMPLAYBACK0X1_ADDR 0x82U +#define CSR_ACSMPLAYBACK1X1_ADDR 0x83U +#define CSR_ACSMPLAYBACK0X2_ADDR 0x84U +#define CSR_ACSMPLAYBACK1X2_ADDR 0x85U +#define CSR_ACSMPLAYBACK0X3_ADDR 0x86U +#define CSR_ACSMPLAYBACK1X3_ADDR 0x87U +#define CSR_ACSMPLAYBACK0X4_ADDR 0x88U +#define CSR_ACSMPLAYBACK1X4_ADDR 0x89U +#define CSR_ACSMPLAYBACK0X5_ADDR 0x8AU +#define CSR_ACSMPLAYBACK1X5_ADDR 0x8BU +#define CSR_ACSMPLAYBACK0X6_ADDR 0x8CU +#define CSR_ACSMPLAYBACK1X6_ADDR 0x8DU +#define CSR_ACSMPLAYBACK0X7_ADDR 0x8EU +#define CSR_ACSMPLAYBACK1X7_ADDR 0x8FU +#define CSR_ACSMPSTATEOVREN_ADDR 0x90U +#define CSR_ACSMPSTATEOVRVAL_ADDR 0x91U +#define CSR_ACSMCTRL23_ADDR 0xC0U +#define CSR_ACSMCKEVAL_ADDR 0xC2U +#define CSR_LOWSPEEDCLOCKDIVIDER_ADDR 0xC8U +#define CSR_ACSMCSMAPCTRL0_ADDR 0xD0U +#define CSR_ACSMCSMAPCTRL1_ADDR 0xD1U +#define CSR_ACSMCSMAPCTRL2_ADDR 0xD2U +#define CSR_ACSMCSMAPCTRL3_ADDR 0xD3U +#define CSR_ACSMCSMAPCTRL4_ADDR 0xD4U +#define CSR_ACSMCSMAPCTRL5_ADDR 0xD5U +#define CSR_ACSMCSMAPCTRL6_ADDR 0xD6U +#define CSR_ACSMCSMAPCTRL7_ADDR 0xD7U +#define CSR_ACSMCSMAPCTRL8_ADDR 0xD8U +#define CSR_ACSMCSMAPCTRL9_ADDR 0xD9U +#define CSR_ACSMCSMAPCTRL10_ADDR 0xDAU +#define CSR_ACSMCSMAPCTRL11_ADDR 0xDBU +#define CSR_ACSMCSMAPCTRL12_ADDR 0xDCU +#define CSR_ACSMCSMAPCTRL13_ADDR 0xDDU +#define CSR_ACSMCSMAPCTRL14_ADDR 0xDEU +#define CSR_ACSMCSMAPCTRL15_ADDR 0xDFU +#define CSR_ACSMODTCTRL0_ADDR 0xE0U +#define CSR_ACSMODTCTRL1_ADDR 0xE1U +#define CSR_ACSMODTCTRL2_ADDR 0xE2U +#define CSR_ACSMODTCTRL3_ADDR 0xE3U +#define CSR_ACSMODTCTRL4_ADDR 0xE4U +#define CSR_ACSMODTCTRL5_ADDR 0xE5U +#define CSR_ACSMODTCTRL6_ADDR 0xE6U +#define CSR_ACSMODTCTRL7_ADDR 0xE7U +#define CSR_ACSMODTCTRL8_ADDR 0xE8U +#define CSR_ACSMCTRL16_ADDR 0xE9U +#define CSR_LOWSPEEDCLOCKSTOPVAL_ADDR 0xEAU +#define CSR_ACSMCTRL18_ADDR 0xEBU +#define CSR_ACSMCTRL19_ADDR 0xECU +#define CSR_ACSMCTRL20_ADDR 0xEDU +#define CSR_ACSMCTRL21_ADDR 0xEEU +#define CSR_ACSMCTRL22_ADDR 0xEFU +#define CSR_ACSMCTRL0_ADDR 0xF0U +#define CSR_ACSMCTRL1_ADDR 0xF1U +#define CSR_ACSMCTRL2_ADDR 0xF2U +#define CSR_ACSMCTRL3_ADDR 0xF3U +#define CSR_ACSMCTRL4_ADDR 0xF4U +#define CSR_ACSMCTRL5_ADDR 0xF5U +#define CSR_ACSMCTRL6_ADDR 0xF6U +#define CSR_ACSMCTRL7_ADDR 0xF7U +#define CSR_ACSMCTRL8_ADDR 0xF8U +#define CSR_ACSMCTRL9_ADDR 0xF9U +#define CSR_ACSMCTRL10_ADDR 0xFAU +#define CSR_ACSMCTRL11_ADDR 0xFBU +#define CSR_ACSMCTRL12_ADDR 0xFCU +#define CSR_ACSMCTRL13_ADDR 0xFDU +#define CSR_ACSMCTRL14_ADDR 0xFEU +#define CSR_ACSMCTRL15_ADDR 0xFFU + +/* PPGC0 register offsets */ +#define CSR_PPGCCTRL1_ADDR 0x11U +#define CSR_PPGCLANE2CRCINMAP0_ADDR 0x15U +#define CSR_PPGCLANE2CRCINMAP1_ADDR 0x16U +#define CSR_PRBSTAPDLY0_ADDR 0x24U +#define CSR_PRBSTAPDLY1_ADDR 0x25U +#define CSR_PRBSTAPDLY2_ADDR 0x26U +#define CSR_PRBSTAPDLY3_ADDR 0x27U +#define CSR_GENPRBSBYTE0_ADDR 0x30U +#define CSR_GENPRBSBYTE1_ADDR 0x31U +#define CSR_GENPRBSBYTE2_ADDR 0x32U +#define CSR_GENPRBSBYTE3_ADDR 0x33U +#define CSR_GENPRBSBYTE4_ADDR 0x34U +#define CSR_GENPRBSBYTE5_ADDR 0x35U +#define CSR_GENPRBSBYTE6_ADDR 0x36U +#define CSR_GENPRBSBYTE7_ADDR 0x37U +#define CSR_GENPRBSBYTE8_ADDR 0x38U +#define CSR_GENPRBSBYTE9_ADDR 0x39U +#define CSR_GENPRBSBYTE10_ADDR 0x3AU +#define CSR_GENPRBSBYTE11_ADDR 0x3BU +#define CSR_GENPRBSBYTE12_ADDR 0x3CU +#define CSR_GENPRBSBYTE13_ADDR 0x3DU +#define CSR_GENPRBSBYTE14_ADDR 0x3EU +#define CSR_GENPRBSBYTE15_ADDR 0x3FU +#define CSR_PRBSGENCTL_ADDR 0x60U +#define CSR_PRBSGENSTATELO_ADDR 0x61U +#define CSR_PRBSGENSTATEHI_ADDR 0x62U +#define CSR_PRBSCHKSTATELO_ADDR 0x63U +#define CSR_PRBSCHKSTATEHI_ADDR 0x64U +#define CSR_PRBSGENCTL1_ADDR 0x65U +#define CSR_PRBSGENCTL2_ADDR 0x66U + +/* INITENG0 register offsets */ +#define CSR_PRESEQUENCEREG0B0S0_ADDR 0x0U +#define CSR_PRESEQUENCEREG0B0S1_ADDR 0x1U +#define CSR_PRESEQUENCEREG0B0S2_ADDR 0x2U +#define CSR_PRESEQUENCEREG0B1S0_ADDR 0x3U +#define CSR_PRESEQUENCEREG0B1S1_ADDR 0x4U +#define CSR_PRESEQUENCEREG0B1S2_ADDR 0x5U +#define CSR_POSTSEQUENCEREG0B0S0_ADDR 0x6U +#define CSR_POSTSEQUENCEREG0B0S1_ADDR 0x7U +#define CSR_POSTSEQUENCEREG0B0S2_ADDR 0x8U +#define CSR_POSTSEQUENCEREG0B1S0_ADDR 0x9U +#define CSR_POSTSEQUENCEREG0B1S1_ADDR 0xAU +#define CSR_POSTSEQUENCEREG0B1S2_ADDR 0xBU +#define CSR_SEQ0BDISABLEFLAG0_ADDR 0xCU +#define CSR_SEQ0BDISABLEFLAG1_ADDR 0xDU +#define CSR_SEQ0BDISABLEFLAG2_ADDR 0xEU +#define CSR_SEQ0BDISABLEFLAG3_ADDR 0xFU +#define CSR_SEQ0BDISABLEFLAG4_ADDR 0x10U +#define CSR_SEQ0BDISABLEFLAG5_ADDR 0x11U +#define CSR_SEQ0BDISABLEFLAG6_ADDR 0x12U +#define CSR_SEQ0BDISABLEFLAG7_ADDR 0x13U +#define CSR_STARTVECTOR0B0_ADDR 0x17U +#define CSR_STARTVECTOR0B1_ADDR 0x18U +#define CSR_STARTVECTOR0B2_ADDR 0x19U +#define CSR_STARTVECTOR0B3_ADDR 0x1AU +#define CSR_STARTVECTOR0B4_ADDR 0x1BU +#define CSR_STARTVECTOR0B5_ADDR 0x1CU +#define CSR_STARTVECTOR0B6_ADDR 0x1DU +#define CSR_STARTVECTOR0B7_ADDR 0x1EU +#define CSR_STARTVECTOR0B8_ADDR 0x1FU +#define CSR_STARTVECTOR0B9_ADDR 0x20U +#define CSR_STARTVECTOR0B10_ADDR 0x21U +#define CSR_STARTVECTOR0B11_ADDR 0x22U +#define CSR_STARTVECTOR0B12_ADDR 0x23U +#define CSR_STARTVECTOR0B13_ADDR 0x24U +#define CSR_STARTVECTOR0B14_ADDR 0x25U +#define CSR_STARTVECTOR0B15_ADDR 0x26U +#define CSR_SEQ0BWAITCONDSEL_ADDR 0x27U +#define CSR_PHYINLP3_ADDR 0x28U +#define CSR_SEQUENCEREG0B0S0_ADDR 0x29U +#define CSR_SEQUENCEREG0B0S1_ADDR 0x2AU +#define CSR_SEQUENCEREG0B0S2_ADDR 0x2BU +#define CSR_SEQUENCEREG0B1S0_ADDR 0x2CU +#define CSR_SEQUENCEREG0B1S1_ADDR 0x2DU +#define CSR_SEQUENCEREG0B1S2_ADDR 0x2EU +#define CSR_SEQUENCEREG0B2S0_ADDR 0x2FU +#define CSR_SEQUENCEREG0B2S1_ADDR 0x30U +#define CSR_SEQUENCEREG0B2S2_ADDR 0x31U +#define CSR_SEQUENCEREG0B3S0_ADDR 0x32U +#define CSR_SEQUENCEREG0B3S1_ADDR 0x33U +#define CSR_SEQUENCEREG0B3S2_ADDR 0x34U +#define CSR_SEQUENCEREG0B4S0_ADDR 0x35U +#define CSR_SEQUENCEREG0B4S1_ADDR 0x36U +#define CSR_SEQUENCEREG0B4S2_ADDR 0x37U +#define CSR_SEQUENCEREG0B5S0_ADDR 0x38U +#define CSR_SEQUENCEREG0B5S1_ADDR 0x39U +#define CSR_SEQUENCEREG0B5S2_ADDR 0x3AU +#define CSR_SEQUENCEREG0B6S0_ADDR 0x3BU +#define CSR_SEQUENCEREG0B6S1_ADDR 0x3CU +#define CSR_SEQUENCEREG0B6S2_ADDR 0x3DU +#define CSR_SEQUENCEREG0B7S0_ADDR 0x3EU +#define CSR_SEQUENCEREG0B7S1_ADDR 0x3FU +#define CSR_SEQUENCEREG0B7S2_ADDR 0x40U +#define CSR_SEQUENCEREG0B8S0_ADDR 0x41U +#define CSR_SEQUENCEREG0B8S1_ADDR 0x42U +#define CSR_SEQUENCEREG0B8S2_ADDR 0x43U +#define CSR_SEQUENCEREG0B9S0_ADDR 0x44U +#define CSR_SEQUENCEREG0B9S1_ADDR 0x45U +#define CSR_SEQUENCEREG0B9S2_ADDR 0x46U +#define CSR_SEQUENCEREG0B10S0_ADDR 0x47U +#define CSR_SEQUENCEREG0B10S1_ADDR 0x48U +#define CSR_SEQUENCEREG0B10S2_ADDR 0x49U +#define CSR_SEQUENCEREG0B11S0_ADDR 0x4AU +#define CSR_SEQUENCEREG0B11S1_ADDR 0x4BU +#define CSR_SEQUENCEREG0B11S2_ADDR 0x4CU +#define CSR_SEQUENCEREG0B12S0_ADDR 0x4DU +#define CSR_SEQUENCEREG0B12S1_ADDR 0x4EU +#define CSR_SEQUENCEREG0B12S2_ADDR 0x4FU +#define CSR_SEQUENCEREG0B13S0_ADDR 0x50U +#define CSR_SEQUENCEREG0B13S1_ADDR 0x51U +#define CSR_SEQUENCEREG0B13S2_ADDR 0x52U +#define CSR_SEQUENCEREG0B14S0_ADDR 0x53U +#define CSR_SEQUENCEREG0B14S1_ADDR 0x54U +#define CSR_SEQUENCEREG0B14S2_ADDR 0x55U +#define CSR_SEQUENCEREG0B15S0_ADDR 0x56U +#define CSR_SEQUENCEREG0B15S1_ADDR 0x57U +#define CSR_SEQUENCEREG0B15S2_ADDR 0x58U +#define CSR_SEQUENCEREG0B16S0_ADDR 0x59U +#define CSR_SEQUENCEREG0B16S1_ADDR 0x5AU +#define CSR_SEQUENCEREG0B16S2_ADDR 0x5BU +#define CSR_SEQUENCEREG0B17S0_ADDR 0x5CU +#define CSR_SEQUENCEREG0B17S1_ADDR 0x5DU +#define CSR_SEQUENCEREG0B17S2_ADDR 0x5EU +#define CSR_SEQUENCEREG0B18S0_ADDR 0x5FU +#define CSR_SEQUENCEREG0B18S1_ADDR 0x60U +#define CSR_SEQUENCEREG0B18S2_ADDR 0x61U +#define CSR_SEQUENCEREG0B19S0_ADDR 0x62U +#define CSR_SEQUENCEREG0B19S1_ADDR 0x63U +#define CSR_SEQUENCEREG0B19S2_ADDR 0x64U +#define CSR_SEQUENCEREG0B20S0_ADDR 0x65U +#define CSR_SEQUENCEREG0B20S1_ADDR 0x66U +#define CSR_SEQUENCEREG0B20S2_ADDR 0x67U +#define CSR_SEQUENCEREG0B21S0_ADDR 0x68U +#define CSR_SEQUENCEREG0B21S1_ADDR 0x69U +#define CSR_SEQUENCEREG0B21S2_ADDR 0x6AU +#define CSR_SEQUENCEREG0B22S0_ADDR 0x6BU +#define CSR_SEQUENCEREG0B22S1_ADDR 0x6CU +#define CSR_SEQUENCEREG0B22S2_ADDR 0x6DU +#define CSR_SEQUENCEREG0B23S0_ADDR 0x6EU +#define CSR_SEQUENCEREG0B23S1_ADDR 0x6FU +#define CSR_SEQUENCEREG0B23S2_ADDR 0x70U +#define CSR_SEQUENCEREG0B24S0_ADDR 0x71U +#define CSR_SEQUENCEREG0B24S1_ADDR 0x72U +#define CSR_SEQUENCEREG0B24S2_ADDR 0x73U +#define CSR_SEQUENCEREG0B25S0_ADDR 0x74U +#define CSR_SEQUENCEREG0B25S1_ADDR 0x75U +#define CSR_SEQUENCEREG0B25S2_ADDR 0x76U +#define CSR_SEQUENCEREG0B26S0_ADDR 0x77U +#define CSR_SEQUENCEREG0B26S1_ADDR 0x78U +#define CSR_SEQUENCEREG0B26S2_ADDR 0x79U +#define CSR_SEQUENCEREG0B27S0_ADDR 0x7AU +#define CSR_SEQUENCEREG0B27S1_ADDR 0x7BU +#define CSR_SEQUENCEREG0B27S2_ADDR 0x7CU +#define CSR_SEQUENCEREG0B28S0_ADDR 0x7DU +#define CSR_SEQUENCEREG0B28S1_ADDR 0x7EU +#define CSR_SEQUENCEREG0B28S2_ADDR 0x7FU +#define CSR_SEQUENCEREG0B29S0_ADDR 0x80U +#define CSR_SEQUENCEREG0B29S1_ADDR 0x81U +#define CSR_SEQUENCEREG0B29S2_ADDR 0x82U +#define CSR_SEQUENCEREG0B30S0_ADDR 0x83U +#define CSR_SEQUENCEREG0B30S1_ADDR 0x84U +#define CSR_SEQUENCEREG0B30S2_ADDR 0x85U +#define CSR_SEQUENCEREG0B31S0_ADDR 0x86U +#define CSR_SEQUENCEREG0B31S1_ADDR 0x87U +#define CSR_SEQUENCEREG0B31S2_ADDR 0x88U +#define CSR_SEQUENCEREG0B32S0_ADDR 0x89U +#define CSR_SEQUENCEREG0B32S1_ADDR 0x8AU +#define CSR_SEQUENCEREG0B32S2_ADDR 0x8BU +#define CSR_SEQUENCEREG0B33S0_ADDR 0x8CU +#define CSR_SEQUENCEREG0B33S1_ADDR 0x8DU +#define CSR_SEQUENCEREG0B33S2_ADDR 0x8EU +#define CSR_SEQUENCEREG0B34S0_ADDR 0x8FU +#define CSR_SEQUENCEREG0B34S1_ADDR 0x90U +#define CSR_SEQUENCEREG0B34S2_ADDR 0x91U +#define CSR_SEQUENCEREG0B35S0_ADDR 0x92U +#define CSR_SEQUENCEREG0B35S1_ADDR 0x93U +#define CSR_SEQUENCEREG0B35S2_ADDR 0x94U +#define CSR_SEQUENCEREG0B36S0_ADDR 0x95U +#define CSR_SEQUENCEREG0B36S1_ADDR 0x96U +#define CSR_SEQUENCEREG0B36S2_ADDR 0x97U +#define CSR_SEQUENCEREG0B37S0_ADDR 0x98U +#define CSR_SEQUENCEREG0B37S1_ADDR 0x99U +#define CSR_SEQUENCEREG0B37S2_ADDR 0x9AU +#define CSR_SEQUENCEREG0B38S0_ADDR 0x9BU +#define CSR_SEQUENCEREG0B38S1_ADDR 0x9CU +#define CSR_SEQUENCEREG0B38S2_ADDR 0x9DU +#define CSR_SEQUENCEREG0B39S0_ADDR 0x9EU +#define CSR_SEQUENCEREG0B39S1_ADDR 0x9FU +#define CSR_SEQUENCEREG0B39S2_ADDR 0xA0U +#define CSR_SEQUENCEREG0B40S0_ADDR 0xA1U +#define CSR_SEQUENCEREG0B40S1_ADDR 0xA2U +#define CSR_SEQUENCEREG0B40S2_ADDR 0xA3U +#define CSR_SEQUENCEREG0B41S0_ADDR 0xA4U +#define CSR_SEQUENCEREG0B41S1_ADDR 0xA5U +#define CSR_SEQUENCEREG0B41S2_ADDR 0xA6U +#define CSR_SEQUENCEREG0B42S0_ADDR 0xA7U +#define CSR_SEQUENCEREG0B42S1_ADDR 0xA8U +#define CSR_SEQUENCEREG0B42S2_ADDR 0xA9U +#define CSR_SEQUENCEREG0B43S0_ADDR 0xAAU +#define CSR_SEQUENCEREG0B43S1_ADDR 0xABU +#define CSR_SEQUENCEREG0B43S2_ADDR 0xACU +#define CSR_SEQUENCEREG0B44S0_ADDR 0xADU +#define CSR_SEQUENCEREG0B44S1_ADDR 0xAEU +#define CSR_SEQUENCEREG0B44S2_ADDR 0xAFU +#define CSR_SEQUENCEREG0B45S0_ADDR 0xB0U +#define CSR_SEQUENCEREG0B45S1_ADDR 0xB1U +#define CSR_SEQUENCEREG0B45S2_ADDR 0xB2U +#define CSR_SEQUENCEREG0B46S0_ADDR 0xB3U +#define CSR_SEQUENCEREG0B46S1_ADDR 0xB4U +#define CSR_SEQUENCEREG0B46S2_ADDR 0xB5U +#define CSR_SEQUENCEREG0B47S0_ADDR 0xB6U +#define CSR_SEQUENCEREG0B47S1_ADDR 0xB7U +#define CSR_SEQUENCEREG0B47S2_ADDR 0xB8U +#define CSR_SEQUENCEREG0B48S0_ADDR 0xB9U +#define CSR_SEQUENCEREG0B48S1_ADDR 0xBAU +#define CSR_SEQUENCEREG0B48S2_ADDR 0xBBU +#define CSR_SEQUENCEREG0B49S0_ADDR 0xBCU +#define CSR_SEQUENCEREG0B49S1_ADDR 0xBDU +#define CSR_SEQUENCEREG0B49S2_ADDR 0xBEU +#define CSR_SEQUENCEREG0B50S0_ADDR 0xBFU +#define CSR_SEQUENCEREG0B50S1_ADDR 0xC0U +#define CSR_SEQUENCEREG0B50S2_ADDR 0xC1U +#define CSR_SEQUENCEREG0B51S0_ADDR 0xC2U +#define CSR_SEQUENCEREG0B51S1_ADDR 0xC3U +#define CSR_SEQUENCEREG0B51S2_ADDR 0xC4U +#define CSR_SEQUENCEREG0B52S0_ADDR 0xC5U +#define CSR_SEQUENCEREG0B52S1_ADDR 0xC6U +#define CSR_SEQUENCEREG0B52S2_ADDR 0xC7U +#define CSR_SEQUENCEREG0B53S0_ADDR 0xC8U +#define CSR_SEQUENCEREG0B53S1_ADDR 0xC9U +#define CSR_SEQUENCEREG0B53S2_ADDR 0xCAU +#define CSR_SEQUENCEREG0B54S0_ADDR 0xCBU +#define CSR_SEQUENCEREG0B54S1_ADDR 0xCCU +#define CSR_SEQUENCEREG0B54S2_ADDR 0xCDU +#define CSR_SEQUENCEREG0B55S0_ADDR 0xCEU +#define CSR_SEQUENCEREG0B55S1_ADDR 0xCFU +#define CSR_SEQUENCEREG0B55S2_ADDR 0xD0U +#define CSR_SEQUENCEREG0B56S0_ADDR 0xD1U +#define CSR_SEQUENCEREG0B56S1_ADDR 0xD2U +#define CSR_SEQUENCEREG0B56S2_ADDR 0xD3U +#define CSR_SEQUENCEREG0B57S0_ADDR 0xD4U +#define CSR_SEQUENCEREG0B57S1_ADDR 0xD5U +#define CSR_SEQUENCEREG0B57S2_ADDR 0xD6U +#define CSR_SEQUENCEREG0B58S0_ADDR 0xD7U +#define CSR_SEQUENCEREG0B58S1_ADDR 0xD8U +#define CSR_SEQUENCEREG0B58S2_ADDR 0xD9U +#define CSR_SEQUENCEREG0B59S0_ADDR 0xDAU +#define CSR_SEQUENCEREG0B59S1_ADDR 0xDBU +#define CSR_SEQUENCEREG0B59S2_ADDR 0xDCU +#define CSR_SEQUENCEREG0B60S0_ADDR 0xDDU +#define CSR_SEQUENCEREG0B60S1_ADDR 0xDEU +#define CSR_SEQUENCEREG0B60S2_ADDR 0xDFU +#define CSR_SEQUENCEREG0B61S0_ADDR 0xE0U +#define CSR_SEQUENCEREG0B61S1_ADDR 0xE1U +#define CSR_SEQUENCEREG0B61S2_ADDR 0xE2U +#define CSR_SEQUENCEREG0B62S0_ADDR 0xE3U +#define CSR_SEQUENCEREG0B62S1_ADDR 0xE4U +#define CSR_SEQUENCEREG0B62S2_ADDR 0xE5U +#define CSR_SEQUENCEREG0B63S0_ADDR 0xE6U +#define CSR_SEQUENCEREG0B63S1_ADDR 0xE7U +#define CSR_SEQUENCEREG0B63S2_ADDR 0xE8U +#define CSR_SEQUENCEREG0B64S0_ADDR 0xE9U +#define CSR_SEQUENCEREG0B64S1_ADDR 0xEAU +#define CSR_SEQUENCEREG0B64S2_ADDR 0xEBU +#define CSR_SEQUENCEREG0B65S0_ADDR 0xECU +#define CSR_SEQUENCEREG0B65S1_ADDR 0xEDU +#define CSR_SEQUENCEREG0B65S2_ADDR 0xEEU +#define CSR_SEQUENCEREG0B66S0_ADDR 0xEFU +#define CSR_SEQUENCEREG0B66S1_ADDR 0xF0U +#define CSR_SEQUENCEREG0B66S2_ADDR 0xF1U +#define CSR_SEQUENCEREG0B67S0_ADDR 0xF2U +#define CSR_SEQUENCEREG0B67S1_ADDR 0xF3U +#define CSR_SEQUENCEREG0B67S2_ADDR 0xF4U +#define CSR_SEQUENCEREG0B68S0_ADDR 0xF5U +#define CSR_SEQUENCEREG0B68S1_ADDR 0xF6U +#define CSR_SEQUENCEREG0B68S2_ADDR 0xF7U +#define CSR_SEQUENCEREG0B69S0_ADDR 0xF8U +#define CSR_SEQUENCEREG0B69S1_ADDR 0xF9U +#define CSR_SEQUENCEREG0B69S2_ADDR 0xFAU +#define CSR_SEQUENCEREG0B70S0_ADDR 0xFBU +#define CSR_SEQUENCEREG0B70S1_ADDR 0xFCU +#define CSR_SEQUENCEREG0B70S2_ADDR 0xFDU +#define CSR_SEQUENCEREG0B71S0_ADDR 0xFEU +#define CSR_SEQUENCEREG0B71S1_ADDR 0xFFU +#define CSR_SEQUENCEREG0B71S2_ADDR 0x100U +#define CSR_SEQUENCEREG0B72S0_ADDR 0x101U +#define CSR_SEQUENCEREG0B72S1_ADDR 0x102U +#define CSR_SEQUENCEREG0B72S2_ADDR 0x103U +#define CSR_SEQUENCEREG0B73S0_ADDR 0x104U +#define CSR_SEQUENCEREG0B73S1_ADDR 0x105U +#define CSR_SEQUENCEREG0B73S2_ADDR 0x106U +#define CSR_SEQUENCEREG0B74S0_ADDR 0x107U +#define CSR_SEQUENCEREG0B74S1_ADDR 0x108U +#define CSR_SEQUENCEREG0B74S2_ADDR 0x109U +#define CSR_SEQUENCEREG0B75S0_ADDR 0x10AU +#define CSR_SEQUENCEREG0B75S1_ADDR 0x10BU +#define CSR_SEQUENCEREG0B75S2_ADDR 0x10CU +#define CSR_SEQUENCEREG0B76S0_ADDR 0x10DU +#define CSR_SEQUENCEREG0B76S1_ADDR 0x10EU +#define CSR_SEQUENCEREG0B76S2_ADDR 0x10FU +#define CSR_SEQUENCEREG0B77S0_ADDR 0x110U +#define CSR_SEQUENCEREG0B77S1_ADDR 0x111U +#define CSR_SEQUENCEREG0B77S2_ADDR 0x112U +#define CSR_SEQUENCEREG0B78S0_ADDR 0x113U +#define CSR_SEQUENCEREG0B78S1_ADDR 0x114U +#define CSR_SEQUENCEREG0B78S2_ADDR 0x115U +#define CSR_SEQUENCEREG0B79S0_ADDR 0x116U +#define CSR_SEQUENCEREG0B79S1_ADDR 0x117U +#define CSR_SEQUENCEREG0B79S2_ADDR 0x118U +#define CSR_SEQUENCEREG0B80S0_ADDR 0x119U +#define CSR_SEQUENCEREG0B80S1_ADDR 0x11AU +#define CSR_SEQUENCEREG0B80S2_ADDR 0x11BU +#define CSR_SEQUENCEREG0B81S0_ADDR 0x11CU +#define CSR_SEQUENCEREG0B81S1_ADDR 0x11DU +#define CSR_SEQUENCEREG0B81S2_ADDR 0x11EU +#define CSR_SEQUENCEREG0B82S0_ADDR 0x11FU +#define CSR_SEQUENCEREG0B82S1_ADDR 0x120U +#define CSR_SEQUENCEREG0B82S2_ADDR 0x121U +#define CSR_SEQUENCEREG0B83S0_ADDR 0x122U +#define CSR_SEQUENCEREG0B83S1_ADDR 0x123U +#define CSR_SEQUENCEREG0B83S2_ADDR 0x124U +#define CSR_SEQUENCEREG0B84S0_ADDR 0x125U +#define CSR_SEQUENCEREG0B84S1_ADDR 0x126U +#define CSR_SEQUENCEREG0B84S2_ADDR 0x127U +#define CSR_SEQUENCEREG0B85S0_ADDR 0x128U +#define CSR_SEQUENCEREG0B85S1_ADDR 0x129U +#define CSR_SEQUENCEREG0B85S2_ADDR 0x12AU +#define CSR_SEQUENCEREG0B86S0_ADDR 0x12BU +#define CSR_SEQUENCEREG0B86S1_ADDR 0x12CU +#define CSR_SEQUENCEREG0B86S2_ADDR 0x12DU +#define CSR_SEQUENCEREG0B87S0_ADDR 0x12EU +#define CSR_SEQUENCEREG0B87S1_ADDR 0x12FU +#define CSR_SEQUENCEREG0B87S2_ADDR 0x130U +#define CSR_SEQUENCEREG0B88S0_ADDR 0x131U +#define CSR_SEQUENCEREG0B88S1_ADDR 0x132U +#define CSR_SEQUENCEREG0B88S2_ADDR 0x133U +#define CSR_SEQUENCEREG0B89S0_ADDR 0x134U +#define CSR_SEQUENCEREG0B89S1_ADDR 0x135U +#define CSR_SEQUENCEREG0B89S2_ADDR 0x136U +#define CSR_SEQUENCEREG0B90S0_ADDR 0x137U +#define CSR_SEQUENCEREG0B90S1_ADDR 0x138U +#define CSR_SEQUENCEREG0B90S2_ADDR 0x139U +#define CSR_SEQUENCEREG0B91S0_ADDR 0x13AU +#define CSR_SEQUENCEREG0B91S1_ADDR 0x13BU +#define CSR_SEQUENCEREG0B91S2_ADDR 0x13CU +#define CSR_SEQUENCEREG0B92S0_ADDR 0x13DU +#define CSR_SEQUENCEREG0B92S1_ADDR 0x13EU +#define CSR_SEQUENCEREG0B92S2_ADDR 0x13FU +#define CSR_SEQUENCEREG0B93S0_ADDR 0x140U +#define CSR_SEQUENCEREG0B93S1_ADDR 0x141U +#define CSR_SEQUENCEREG0B93S2_ADDR 0x142U +#define CSR_SEQUENCEREG0B94S0_ADDR 0x143U +#define CSR_SEQUENCEREG0B94S1_ADDR 0x144U +#define CSR_SEQUENCEREG0B94S2_ADDR 0x145U +#define CSR_SEQUENCEREG0B95S0_ADDR 0x146U +#define CSR_SEQUENCEREG0B95S1_ADDR 0x147U +#define CSR_SEQUENCEREG0B95S2_ADDR 0x148U +#define CSR_SEQUENCEREG0B96S0_ADDR 0x149U +#define CSR_SEQUENCEREG0B96S1_ADDR 0x14AU +#define CSR_SEQUENCEREG0B96S2_ADDR 0x14BU +#define CSR_SEQUENCEREG0B97S0_ADDR 0x14CU +#define CSR_SEQUENCEREG0B97S1_ADDR 0x14DU +#define CSR_SEQUENCEREG0B97S2_ADDR 0x14EU +#define CSR_SEQUENCEREG0B98S0_ADDR 0x14FU +#define CSR_SEQUENCEREG0B98S1_ADDR 0x150U +#define CSR_SEQUENCEREG0B98S2_ADDR 0x151U +#define CSR_SEQUENCEREG0B99S0_ADDR 0x152U +#define CSR_SEQUENCEREG0B99S1_ADDR 0x153U +#define CSR_SEQUENCEREG0B99S2_ADDR 0x154U +#define CSR_SEQUENCEREG0B100S0_ADDR 0x155U +#define CSR_SEQUENCEREG0B100S1_ADDR 0x156U +#define CSR_SEQUENCEREG0B100S2_ADDR 0x157U +#define CSR_SEQUENCEREG0B101S0_ADDR 0x158U +#define CSR_SEQUENCEREG0B101S1_ADDR 0x159U +#define CSR_SEQUENCEREG0B101S2_ADDR 0x15AU +#define CSR_SEQUENCEREG0B102S0_ADDR 0x15BU +#define CSR_SEQUENCEREG0B102S1_ADDR 0x15CU +#define CSR_SEQUENCEREG0B102S2_ADDR 0x15DU +#define CSR_SEQUENCEREG0B103S0_ADDR 0x15EU +#define CSR_SEQUENCEREG0B103S1_ADDR 0x15FU +#define CSR_SEQUENCEREG0B103S2_ADDR 0x160U +#define CSR_SEQUENCEREG0B104S0_ADDR 0x161U +#define CSR_SEQUENCEREG0B104S1_ADDR 0x162U +#define CSR_SEQUENCEREG0B104S2_ADDR 0x163U +#define CSR_SEQUENCEREG0B105S0_ADDR 0x164U +#define CSR_SEQUENCEREG0B105S1_ADDR 0x165U +#define CSR_SEQUENCEREG0B105S2_ADDR 0x166U +#define CSR_SEQUENCEREG0B106S0_ADDR 0x167U +#define CSR_SEQUENCEREG0B106S1_ADDR 0x168U +#define CSR_SEQUENCEREG0B106S2_ADDR 0x169U +#define CSR_SEQUENCEREG0B107S0_ADDR 0x16AU +#define CSR_SEQUENCEREG0B107S1_ADDR 0x16BU +#define CSR_SEQUENCEREG0B107S2_ADDR 0x16CU +#define CSR_SEQUENCEREG0B108S0_ADDR 0x16DU +#define CSR_SEQUENCEREG0B108S1_ADDR 0x16EU +#define CSR_SEQUENCEREG0B108S2_ADDR 0x16FU +#define CSR_SEQUENCEREG0B109S0_ADDR 0x170U +#define CSR_SEQUENCEREG0B109S1_ADDR 0x171U +#define CSR_SEQUENCEREG0B109S2_ADDR 0x172U +#define CSR_SEQUENCEREG0B110S0_ADDR 0x173U +#define CSR_SEQUENCEREG0B110S1_ADDR 0x174U +#define CSR_SEQUENCEREG0B110S2_ADDR 0x175U +#define CSR_SEQUENCEREG0B111S0_ADDR 0x176U +#define CSR_SEQUENCEREG0B111S1_ADDR 0x177U +#define CSR_SEQUENCEREG0B111S2_ADDR 0x178U +#define CSR_SEQUENCEREG0B112S0_ADDR 0x179U +#define CSR_SEQUENCEREG0B112S1_ADDR 0x17AU +#define CSR_SEQUENCEREG0B112S2_ADDR 0x17BU +#define CSR_SEQUENCEREG0B113S0_ADDR 0x17CU +#define CSR_SEQUENCEREG0B113S1_ADDR 0x17DU +#define CSR_SEQUENCEREG0B113S2_ADDR 0x17EU +#define CSR_SEQUENCEREG0B114S0_ADDR 0x17FU +#define CSR_SEQUENCEREG0B114S1_ADDR 0x180U +#define CSR_SEQUENCEREG0B114S2_ADDR 0x181U +#define CSR_SEQUENCEREG0B115S0_ADDR 0x182U +#define CSR_SEQUENCEREG0B115S1_ADDR 0x183U +#define CSR_SEQUENCEREG0B115S2_ADDR 0x184U +#define CSR_SEQUENCEREG0B116S0_ADDR 0x185U +#define CSR_SEQUENCEREG0B116S1_ADDR 0x186U +#define CSR_SEQUENCEREG0B116S2_ADDR 0x187U +#define CSR_SEQUENCEREG0B117S0_ADDR 0x188U +#define CSR_SEQUENCEREG0B117S1_ADDR 0x189U +#define CSR_SEQUENCEREG0B117S2_ADDR 0x18AU +#define CSR_SEQUENCEREG0B118S0_ADDR 0x18BU +#define CSR_SEQUENCEREG0B118S1_ADDR 0x18CU +#define CSR_SEQUENCEREG0B118S2_ADDR 0x18DU +#define CSR_SEQUENCEREG0B119S0_ADDR 0x18EU +#define CSR_SEQUENCEREG0B119S1_ADDR 0x18FU +#define CSR_SEQUENCEREG0B119S2_ADDR 0x190U +#define CSR_SEQUENCEREG0B120S0_ADDR 0x191U +#define CSR_SEQUENCEREG0B120S1_ADDR 0x192U +#define CSR_SEQUENCEREG0B120S2_ADDR 0x193U +#define CSR_SEQUENCEREG0B121S0_ADDR 0x194U +#define CSR_SEQUENCEREG0B121S1_ADDR 0x195U +#define CSR_SEQUENCEREG0B121S2_ADDR 0x196U +#define CSR_SEQ0BGPR1_ADDR 0x201U +#define CSR_SEQ0BGPR2_ADDR 0x202U +#define CSR_SEQ0BGPR3_ADDR 0x203U +#define CSR_SEQ0BGPR4_ADDR 0x204U +#define CSR_SEQ0BGPR5_ADDR 0x205U +#define CSR_SEQ0BGPR6_ADDR 0x206U +#define CSR_SEQ0BGPR7_ADDR 0x207U +#define CSR_SEQ0BGPR8_ADDR 0x208U +#define CSR_SEQ0BFIXEDADDRBITS_ADDR 0x2FFU + +/* DRTUB0 register offsets */ +#define CSR_DCTSHADOWREGS_ADDR 0x4U +#define CSR_DCTWRITEONLYSHADOW_ADDR 0x30U +#define CSR_UCTWRITEONLY_ADDR 0x32U +#define CSR_UCTWRITEPROT_ADDR 0x33U +#define CSR_UCTDATWRITEONLY_ADDR 0x34U +#define CSR_UCTDATWRITEPROT_ADDR 0x35U +#define CSR_UCTLERR_ADDR 0x36U +#define CSR_UCCLKHCLKENABLES_ADDR 0x80U +#define CSR_CURPSTATE0B_ADDR 0x81U +#define CSR_CLRWAKEUPSTICKY_ADDR 0x95U +#define CSR_WAKEUPMASK_ADDR 0x96U +#define CSR_CUSTPUBREV_ADDR 0xEDU +#define CSR_PUBREV_ADDR 0xEEU + +/* APBONLY0 register offsets */ +#define CSR_MICROCONTMUXSEL_ADDR 0x0U +#define CSR_UCTSHADOWREGS_ADDR 0x4U +#define CSR_DCTWRITEONLY_ADDR 0x30U +#define CSR_DCTWRITEPROT_ADDR 0x31U +#define CSR_UCTWRITEONLYSHADOW_ADDR 0x32U +#define CSR_UCTDATWRITEONLYSHADOW_ADDR 0x34U +#define CSR_NEVERGATECSRCLOCK_ADDR 0x35U +#define CSR_DFICFGRDDATAVALIDTICKS_ADDR 0x37U +#define CSR_MICRORESET_ADDR 0x99U +#define CSR_SEQUENCEROVERRIDE_ADDR 0xE7U +#define CSR_DFIINITCOMPLETESHADOW_ADDR 0xFAU + +/* ANIBx register bit fields */ +/* CSR_MTESTMUXSEL */ +#define CSR_MTESTMUXSEL_LSB 0 +#define CSR_MTESTMUXSEL_MASK GENMASK_32(5, 0) +/* CSR_AFORCEDRVCONT */ +#define CSR_AFORCEDRVCONT_LSB 0 +#define CSR_AFORCEDRVCONT_MASK GENMASK_32(3, 0) +/* CSR_AFORCETRICONT */ +#define CSR_AFORCETRICONT_LSB 0 +#define CSR_AFORCETRICONT_MASK GENMASK_32(3, 0) +/* CSR_ATXIMPEDANCE */ +#define CSR_ATXIMPEDANCE_LSB 0 +#define CSR_ATXIMPEDANCE_MASK GENMASK_32(9, 0) +#define CSR_ADRVSTRENP_LSB 0 +#define CSR_ADRVSTRENP_MASK GENMASK_32(4, 0) +#define CSR_ADRVSTRENN_LSB 5 +#define CSR_ADRVSTRENN_MASK GENMASK_32(9, 5) +/* CSR_ATESTPRBSERR */ +#define CSR_ATESTPRBSERR_LSB 0 +#define CSR_ATESTPRBSERR_MASK GENMASK_32(3, 0) +/* CSR_ATXSLEWRATE */ +#define CSR_ATXSLEWRATE_LSB 0 +#define CSR_ATXSLEWRATE_MASK GENMASK_32(10, 0) +#define CSR_ATXPREP_LSB 0 +#define CSR_ATXPREP_MASK GENMASK_32(3, 0) +#define CSR_ATXPREN_LSB 4 +#define CSR_ATXPREN_MASK GENMASK_32(7, 4) +#define CSR_ATXPREDRVMODE_LSB 8 +#define CSR_ATXPREDRVMODE_MASK GENMASK_32(10, 8) +/* CSR_ATESTPRBSERRCNT */ +#define CSR_ATESTPRBSERRCNT_LSB 0 +#define CSR_ATESTPRBSERRCNT_MASK GENMASK_32(15, 0) +/* CSR_ATXDLY */ +#define CSR_ATXDLY_LSB 0 +#define CSR_ATXDLY_MASK GENMASK_32(6, 0) + +/* DBYTEx register bit fields */ +/* CSR_DBYTEMISCMODE */ +#define CSR_DBYTEMISCMODE_LSB 2 +#define CSR_DBYTEMISCMODE_MASK BIT(2) +#define CSR_DBYTEDISABLE_LSB 2 +#define CSR_DBYTEDISABLE_MASK BIT(2) +/* CSR_TSMBYTE0 */ +#define CSR_TSMBYTE0_LSB 0 +#define CSR_TSMBYTE0_MASK GENMASK_32(15, 0) +#define CSR_PERPHTRAINEN_LSB 0 +#define CSR_PERPHTRAINEN_MASK BIT(0) +#define CSR_EYEINC_LSB 1 +#define CSR_EYEINC_MASK BIT(1) +#define CSR_EDGEINC_LSB 2 +#define CSR_EDGEINC_MASK BIT(2) +#define CSR_EDGEEYEMXSEL_LSB 3 +#define CSR_EDGEEYEMXSEL_MASK BIT(3) +#define CSR_TSMBYTE0RSVD_LSB 4 +#define CSR_TSMBYTE0RSVD_MASK GENMASK_32(5, 4) +#define CSR_DIMMBROADINC_LSB 6 +#define CSR_DIMMBROADINC_MASK BIT(6) +#define CSR_DIMMINC_LSB 7 +#define CSR_DIMMINC_MASK GENMASK_32(8, 7) +#define CSR_COARSEINC_LSB 9 +#define CSR_COARSEINC_MASK BIT(9) +#define CSR_DELAYINC_LSB 10 +#define CSR_DELAYINC_MASK BIT(10) +#define CSR_RXINC_LSB 11 +#define CSR_RXINC_MASK BIT(11) +#define CSR_RXPERTRAIN_LSB 12 +#define CSR_RXPERTRAIN_MASK BIT(12) +#define CSR_TXPERTRAIN_LSB 13 +#define CSR_TXPERTRAIN_MASK BIT(13) +#define CSR_DMTRAIN_LSB 14 +#define CSR_DMTRAIN_MASK BIT(14) +#define CSR_WRLEVTRAIN_LSB 15 +#define CSR_WRLEVTRAIN_MASK BIT(15) +/* CSR_TRAININGPARAM */ +#define CSR_TRAININGPARAM_LSB 0 +#define CSR_TRAININGPARAM_MASK GENMASK_32(15, 0) +#define CSR_ENDYNRATEREDUCTION_LSB 0 +#define CSR_ENDYNRATEREDUCTION_MASK BIT(0) +#define CSR_TRAININGPARAM01RSVD_LSB 1 +#define CSR_TRAININGPARAM01RSVD_MASK BIT(1) +#define CSR_TRAINENRXCLK_LSB 2 +#define CSR_TRAINENRXCLK_MASK BIT(2) +#define CSR_TRAINENRXEN_LSB 3 +#define CSR_TRAINENRXEN_MASK BIT(3) +#define CSR_TRAINENTXDQS_LSB 4 +#define CSR_TRAINENTXDQS_MASK BIT(4) +#define CSR_TRAINENTXDQ_LSB 5 +#define CSR_TRAINENTXDQ_MASK BIT(5) +#define CSR_TRAINENVREFDAC1_LSB 6 +#define CSR_TRAINENVREFDAC1_MASK BIT(6) +#define CSR_TRAINENVREFDAC0_LSB 7 +#define CSR_TRAINENVREFDAC0_MASK BIT(7) +#define CSR_TRAINENRXPBD_LSB 8 +#define CSR_TRAINENRXPBD_MASK BIT(8) +#define CSR_ROLLINTOCOARSE_LSB 9 +#define CSR_ROLLINTOCOARSE_MASK BIT(9) +#define CSR_TRAINUSINGNATIVEDDLCNTL_LSB 10 +#define CSR_TRAINUSINGNATIVEDDLCNTL_MASK BIT(10) +#define CSR_TRAININGPARAM11RSVD_LSB 11 +#define CSR_TRAININGPARAM11RSVD_MASK BIT(11) +#define CSR_TRAININGPARAM12RSVD_LSB 12 +#define CSR_TRAININGPARAM12RSVD_MASK BIT(12) +#define CSR_INCDECRATE_LSB 13 +#define CSR_INCDECRATE_MASK GENMASK_32(15, 13) +/* CSR_USEDQSENREPLICA */ +#define CSR_USEDQSENREPLICA_LSB 0 +#define CSR_USEDQSENREPLICA_MASK BIT(0) +/* CSR_RXTRAINPATTERNENABLE */ +#define CSR_RXTRAINPATTERNENABLE_LSB 0 +#define CSR_RXTRAINPATTERNENABLE_MASK BIT(0) +/* CSR_TSMBYTE1 */ +#define CSR_TSMBYTE1_LSB 0 +#define CSR_TSMBYTE1_MASK GENMASK_32(15, 0) +#define CSR_DTSMBDSTP_LSB 0 +#define CSR_DTSMBDSTP_MASK GENMASK_32(7, 0) +#define CSR_DTSMGDSTP_LSB 8 +#define CSR_DTSMGDSTP_MASK GENMASK_32(15, 8) +/* CSR_TSMBYTE2 */ +#define CSR_TSMBYTE2_LSB 0 +#define CSR_TSMBYTE2_MASK GENMASK_32(15, 0) +#define CSR_DTSMGDBAR_LSB 0 +#define CSR_DTSMGDBAR_MASK GENMASK_32(15, 0) +/* CSR_TSMBYTE3 */ +#define CSR_TSMBYTE3_LSB 0 +#define CSR_TSMBYTE3_MASK GENMASK_32(8, 0) +#define CSR_DTSMINCDECMODE_LSB 0 +#define CSR_DTSMINCDECMODE_MASK BIT(0) +#define CSR_DTSMINCDECCTRL_LSB 1 +#define CSR_DTSMINCDECCTRL_MASK BIT(1) +#define CSR_ENBLRXSAMPFLOPS_LSB 2 +#define CSR_ENBLRXSAMPFLOPS_MASK BIT(2) +#define CSR_SELRXSAMPFLOPS_LSB 3 +#define CSR_SELRXSAMPFLOPS_MASK BIT(3) +#define CSR_SELRXBYBASS_LSB 4 +#define CSR_SELRXBYBASS_MASK BIT(4) +#define CSR_DTSMIGNUPDATEACK_LSB 5 +#define CSR_DTSMIGNUPDATEACK_MASK BIT(5) +#define CSR_ENABLERXDQASYNC_LSB 6 +#define CSR_ENABLERXDQASYNC_MASK BIT(6) +#define CSR_DTSMSTATICCMPR_LSB 7 +#define CSR_DTSMSTATICCMPR_MASK BIT(7) +#define CSR_DTSMSTATICCMPRVAL_LSB 8 +#define CSR_DTSMSTATICCMPRVAL_MASK BIT(8) +/* CSR_TSMBYTE4 */ +#define CSR_TSMBYTE4_LSB 0 +#define CSR_TSMBYTE4_MASK GENMASK_32(3, 0) +#define CSR_DTSMINCDECPW_LSB 0 +#define CSR_DTSMINCDECPW_MASK GENMASK_32(3, 0) +/* CSR_TESTMODECONFIG */ +#define CSR_TESTMODECONFIG_LSB 0 +#define CSR_TESTMODECONFIG_MASK GENMASK_32(9, 0) +#define CSR_LOOPBACKEN_LSB 0 +#define CSR_LOOPBACKEN_MASK BIT(0) +#define CSR_RSVDTESTDLLEN_LSB 1 +#define CSR_RSVDTESTDLLEN_MASK BIT(1) +#define CSR_RSVDTWOTCKTXDQSPRE_LSB 2 +#define CSR_RSVDTWOTCKTXDQSPRE_MASK BIT(2) +#define CSR_TESTMODERSVD_LSB 3 +#define CSR_TESTMODERSVD_MASK GENMASK_32(7, 3) +#define CSR_LOOPBACKDISDQSTRI_LSB 8 +#define CSR_LOOPBACKDISDQSTRI_MASK BIT(8) +#define CSR_RSVDDISTXDQEQPREAMBLE_LSB 9 +#define CSR_RSVDDISTXDQEQPREAMBLE_MASK BIT(9) +/* CSR_TSMBYTE5 */ +#define CSR_TSMBYTE5_LSB 0 +#define CSR_TSMBYTE5_MASK GENMASK_32(15, 0) +#define CSR_DTSMBDBAR_LSB 0 +#define CSR_DTSMBDBAR_MASK GENMASK_32(15, 0) +/* MTESTMUXSEL already defined in ANIBx section */ +/* CSR_DTSMTRAINMODECTRL */ +#define CSR_DTSMTRAINMODECTRL_LSB 0 +#define CSR_DTSMTRAINMODECTRL_MASK GENMASK_32(3, 0) +#define CSR_DTSMSOELANEMODE_LSB 0 +#define CSR_DTSMSOELANEMODE_MASK GENMASK_32(1, 0) +#define CSR_DTSMBYTEERRANDMODE_LSB 2 +#define CSR_DTSMBYTEERRANDMODE_MASK BIT(2) +#define CSR_DTSMNIBERRMODE_LSB 3 +#define CSR_DTSMNIBERRMODE_MASK BIT(3) +/* CSR_DFIMRL */ +#define CSR_DFIMRL_LSB 0 +#define CSR_DFIMRL_MASK GENMASK_32(4, 0) +/* CSR_ASYNCDBYTEMODE */ +#define CSR_ASYNCDBYTEMODE_LSB 0 +#define CSR_ASYNCDBYTEMODE_MASK GENMASK_32(8, 0) +/* CSR_ASYNCDBYTETXEN */ +#define CSR_ASYNCDBYTETXEN_LSB 0 +#define CSR_ASYNCDBYTETXEN_MASK GENMASK_32(11, 0) +/* CSR_ASYNCDBYTETXDATA */ +#define CSR_ASYNCDBYTETXDATA_LSB 0 +#define CSR_ASYNCDBYTETXDATA_MASK GENMASK_32(11, 0) +/* CSR_ASYNCDBYTERXDATA */ +#define CSR_ASYNCDBYTERXDATA_LSB 0 +#define CSR_ASYNCDBYTERXDATA_MASK GENMASK_32(11, 0) +/* CSR_VREFDAC1 */ +#define CSR_VREFDAC1_LSB 0 +#define CSR_VREFDAC1_MASK GENMASK_32(6, 0) +/* CSR_TRAININGCNTR */ +#define CSR_TRAININGCNTR_LSB 0 +#define CSR_TRAININGCNTR_MASK GENMASK_32(15, 0) +#define CSR_TRAININGCNTRFINE_LSB 0 +#define CSR_TRAININGCNTRFINE_MASK GENMASK_32(9, 0) +#define CSR_TRAININGCNTRCOARSE_LSB 10 +#define CSR_TRAININGCNTRCOARSE_MASK GENMASK_32(15, 10) +/* CSR_VREFDAC0 */ +#define CSR_VREFDAC0_LSB 0 +#define CSR_VREFDAC0_MASK GENMASK_32(6, 0) +/* CSR_TXIMPEDANCECTRL0 */ +#define CSR_TXIMPEDANCECTRL0_LSB 0 +#define CSR_TXIMPEDANCECTRL0_MASK GENMASK_32(11, 0) +#define CSR_DRVSTRENDQP_LSB 0 +#define CSR_DRVSTRENDQP_MASK GENMASK_32(5, 0) +#define CSR_DRVSTRENDQN_LSB 6 +#define CSR_DRVSTRENDQN_MASK GENMASK_32(11, 6) +/* CSR_DQDQSRCVCNTRL */ +#define CSR_DQDQSRCVCNTRL_LSB 0 +#define CSR_DQDQSRCVCNTRL_MASK GENMASK_32(15, 0) +#define CSR_SELANALOGVREF_LSB 0 +#define CSR_SELANALOGVREF_MASK BIT(0) +#define CSR_EXTVREFRANGE_LSB 1 +#define CSR_EXTVREFRANGE_MASK BIT(1) +#define CSR_DFECTRL_LSB 2 +#define CSR_DFECTRL_MASK GENMASK_32(3, 2) +#define CSR_MAJORMODEDBYTE_LSB 4 +#define CSR_MAJORMODEDBYTE_MASK GENMASK_32(6, 4) +#define CSR_GAINCURRADJ_LSB 7 +#define CSR_GAINCURRADJ_MASK GENMASK_32(11, 7) +#define CSR_RESERVED_LSB 12 +#define CSR_RESERVED_MASK GENMASK_32(15, 12) +/* CSR_TXEQUALIZATIONMODE */ +#define CSR_TXEQUALIZATIONMODE_LSB 0 +#define CSR_TXEQUALIZATIONMODE_MASK GENMASK_32(1, 0) +#define CSR_TXEQMODE_LSB 0 +#define CSR_TXEQMODE_MASK GENMASK_32(1, 0) +/* CSR_TXIMPEDANCECTRL1 */ +#define CSR_TXIMPEDANCECTRL1_LSB 0 +#define CSR_TXIMPEDANCECTRL1_MASK GENMASK_32(11, 0) +#define CSR_DRVSTRENFSDQP_LSB 0 +#define CSR_DRVSTRENFSDQP_MASK GENMASK_32(5, 0) +#define CSR_DRVSTRENFSDQN_LSB 6 +#define CSR_DRVSTRENFSDQN_MASK GENMASK_32(11, 6) +/* CSR_DQDQSRCVCNTRL1 */ +#define CSR_DQDQSRCVCNTRL1_LSB 0 +#define CSR_DQDQSRCVCNTRL1_MASK GENMASK_32(11, 0) +#define CSR_POWERDOWNRCVR_LSB 0 +#define CSR_POWERDOWNRCVR_MASK GENMASK_32(8, 0) +#define CSR_POWERDOWNRCVRDQS_LSB 9 +#define CSR_POWERDOWNRCVRDQS_MASK BIT(9) +#define CSR_RXPADSTANDBYEN_LSB 10 +#define CSR_RXPADSTANDBYEN_MASK BIT(10) +#define CSR_ENLPREQPDR_LSB 11 +#define CSR_ENLPREQPDR_MASK BIT(11) +/* CSR_TXIMPEDANCECTRL2 */ +#define CSR_TXIMPEDANCECTRL2_LSB 0 +#define CSR_TXIMPEDANCECTRL2_MASK GENMASK_32(11, 0) +#define CSR_DRVSTRENEQHIDQP_LSB 0 +#define CSR_DRVSTRENEQHIDQP_MASK GENMASK_32(5, 0) +#define CSR_DRVSTRENEQLODQN_LSB 6 +#define CSR_DRVSTRENEQLODQN_MASK GENMASK_32(11, 6) +/* CSR_DQDQSRCVCNTRL2 */ +#define CSR_DQDQSRCVCNTRL2_LSB 0 +#define CSR_DQDQSRCVCNTRL2_MASK BIT(0) +#define CSR_ENRXAGRESSIVEPDR_LSB 0 +#define CSR_ENRXAGRESSIVEPDR_MASK BIT(0) +/* CSR_TXODTDRVSTREN */ +#define CSR_TXODTDRVSTREN_LSB 0 +#define CSR_TXODTDRVSTREN_MASK GENMASK_32(11, 0) +#define CSR_ODTSTRENP_LSB 0 +#define CSR_ODTSTRENP_MASK GENMASK_32(5, 0) +#define CSR_ODTSTRENN_LSB 6 +#define CSR_ODTSTRENN_MASK GENMASK_32(11, 6) +/* CSR_RXFIFOCHECKSTATUS */ +#define CSR_RXFIFOCHECKSTATUS_LSB 0 +#define CSR_RXFIFOCHECKSTATUS_MASK GENMASK_32(1, 0) +#define CSR_RXFIFOLOCERR_LSB 0 +#define CSR_RXFIFOLOCERR_MASK BIT(0) +#define CSR_RXFIFOLOCUERR_LSB 1 +#define CSR_RXFIFOLOCUERR_MASK BIT(1) +/* CSR_RXFIFOCHECKERRVALUES */ +#define CSR_RXFIFOCHECKERRVALUES_LSB 0 +#define CSR_RXFIFOCHECKERRVALUES_MASK GENMASK_32(15, 0) +#define CSR_RXFIFORDLOCERRVALUE_LSB 0 +#define CSR_RXFIFORDLOCERRVALUE_MASK GENMASK_32(3, 0) +#define CSR_RXFIFOWRLOCERRVALUE_LSB 4 +#define CSR_RXFIFOWRLOCERRVALUE_MASK GENMASK_32(7, 4) +#define CSR_RXFIFORDLOCUERRVALUE_LSB 8 +#define CSR_RXFIFORDLOCUERRVALUE_MASK GENMASK_32(11, 8) +#define CSR_RXFIFOWRLOCUERRVALUE_LSB 12 +#define CSR_RXFIFOWRLOCUERRVALUE_MASK GENMASK_32(15, 12) +/* CSR_RXFIFOINFO */ +#define CSR_RXFIFOINFO_LSB 0 +#define CSR_RXFIFOINFO_MASK GENMASK_32(15, 0) +#define CSR_RXFIFORDLOC_LSB 0 +#define CSR_RXFIFORDLOC_MASK GENMASK_32(3, 0) +#define CSR_RXFIFOWRLOC_LSB 4 +#define CSR_RXFIFOWRLOC_MASK GENMASK_32(7, 4) +#define CSR_RXFIFORDLOCU_LSB 8 +#define CSR_RXFIFORDLOCU_MASK GENMASK_32(11, 8) +#define CSR_RXFIFOWRLOCU_LSB 12 +#define CSR_RXFIFOWRLOCU_MASK GENMASK_32(15, 12) +/* CSR_RXFIFOVISIBILITY */ +#define CSR_RXFIFOVISIBILITY_LSB 0 +#define CSR_RXFIFOVISIBILITY_MASK GENMASK_32(4, 0) +#define CSR_RXFIFORDPTR_LSB 0 +#define CSR_RXFIFORDPTR_MASK GENMASK_32(2, 0) +#define CSR_RXFIFORDPTROVR_LSB 3 +#define CSR_RXFIFORDPTROVR_MASK BIT(3) +#define CSR_RXFIFORDEN_LSB 4 +#define CSR_RXFIFORDEN_MASK BIT(4) +/* CSR_RXFIFOCONTENTSDQ3210 */ +#define CSR_RXFIFOCONTENTSDQ3210_LSB 0 +#define CSR_RXFIFOCONTENTSDQ3210_MASK GENMASK_32(15, 0) +/* CSR_RXFIFOCONTENTSDQ7654 */ +#define CSR_RXFIFOCONTENTSDQ7654_LSB 0 +#define CSR_RXFIFOCONTENTSDQ7654_MASK GENMASK_32(15, 0) +/* CSR_RXFIFOCONTENTSDBI */ +#define CSR_RXFIFOCONTENTSDBI_LSB 0 +#define CSR_RXFIFOCONTENTSDBI_MASK GENMASK_32(3, 0) +/* CSR_TXSLEWRATE */ +#define CSR_TXSLEWRATE_LSB 0 +#define CSR_TXSLEWRATE_MASK GENMASK_32(10, 0) +#define CSR_TXPREP_LSB 0 +#define CSR_TXPREP_MASK GENMASK_32(3, 0) +#define CSR_TXPREN_LSB 4 +#define CSR_TXPREN_MASK GENMASK_32(7, 4) +#define CSR_TXPREDRVMODE_LSB 8 +#define CSR_TXPREDRVMODE_MASK GENMASK_32(10, 8) +/* CSR_TRAININGINCDECDTSMEN */ +#define CSR_TRAININGINCDECDTSMEN_LSB 0 +#define CSR_TRAININGINCDECDTSMEN_MASK GENMASK_32(8, 0) +/* CSR_RXPBDLYTG0 */ +#define CSR_RXPBDLYTG0_LSB 0 +#define CSR_RXPBDLYTG0_MASK GENMASK_32(6, 0) +/* CSR_RXPBDLYTG1 */ +#define CSR_RXPBDLYTG1_LSB 0 +#define CSR_RXPBDLYTG1_MASK GENMASK_32(6, 0) +/* CSR_RXPBDLYTG2 */ +#define CSR_RXPBDLYTG2_LSB 0 +#define CSR_RXPBDLYTG2_MASK GENMASK_32(6, 0) +/* CSR_RXPBDLYTG3 */ +#define CSR_RXPBDLYTG3_LSB 0 +#define CSR_RXPBDLYTG3_MASK GENMASK_32(6, 0) +/* CSR_RXENDLYTG0 */ +#define CSR_RXENDLYTG0_LSB 0 +#define CSR_RXENDLYTG0_MASK GENMASK_32(10, 0) +/* CSR_RXENDLYTG1 */ +#define CSR_RXENDLYTG1_LSB 0 +#define CSR_RXENDLYTG1_MASK GENMASK_32(10, 0) +/* CSR_RXENDLYTG2 */ +#define CSR_RXENDLYTG2_LSB 0 +#define CSR_RXENDLYTG2_MASK GENMASK_32(10, 0) +/* CSR_RXENDLYTG3 */ +#define CSR_RXENDLYTG3_LSB 0 +#define CSR_RXENDLYTG3_MASK GENMASK_32(10, 0) +/* CSR_RXCLKDLYTG0 */ +#define CSR_RXCLKDLYTG0_LSB 0 +#define CSR_RXCLKDLYTG0_MASK GENMASK_32(5, 0) +/* CSR_RXCLKDLYTG1 */ +#define CSR_RXCLKDLYTG1_LSB 0 +#define CSR_RXCLKDLYTG1_MASK GENMASK_32(5, 0) +/* CSR_RXCLKDLYTG2 */ +#define CSR_RXCLKDLYTG2_LSB 0 +#define CSR_RXCLKDLYTG2_MASK GENMASK_32(5, 0) +/* CSR_RXCLKDLYTG3 */ +#define CSR_RXCLKDLYTG3_LSB 0 +#define CSR_RXCLKDLYTG3_MASK GENMASK_32(5, 0) +/* CSR_RXCLKCDLYTG0 */ +#define CSR_RXCLKCDLYTG0_LSB 0 +#define CSR_RXCLKCDLYTG0_MASK GENMASK_32(5, 0) +/* CSR_RXCLKCDLYTG1 */ +#define CSR_RXCLKCDLYTG1_LSB 0 +#define CSR_RXCLKCDLYTG1_MASK GENMASK_32(5, 0) +/* CSR_RXCLKCDLYTG2 */ +#define CSR_RXCLKCDLYTG2_LSB 0 +#define CSR_RXCLKCDLYTG2_MASK GENMASK_32(5, 0) +/* CSR_RXCLKCDLYTG3 */ +#define CSR_RXCLKCDLYTG3_LSB 0 +#define CSR_RXCLKCDLYTG3_MASK GENMASK_32(5, 0) +/* CSR_DQ0LNSEL */ +#define CSR_DQ0LNSEL_LSB 0 +#define CSR_DQ0LNSEL_MASK GENMASK_32(2, 0) +/* CSR_DQ1LNSEL */ +#define CSR_DQ1LNSEL_LSB 0 +#define CSR_DQ1LNSEL_MASK GENMASK_32(2, 0) +/* CSR_DQ2LNSEL */ +#define CSR_DQ2LNSEL_LSB 0 +#define CSR_DQ2LNSEL_MASK GENMASK_32(2, 0) +/* CSR_DQ3LNSEL */ +#define CSR_DQ3LNSEL_LSB 0 +#define CSR_DQ3LNSEL_MASK GENMASK_32(2, 0) +/* CSR_DQ4LNSEL */ +#define CSR_DQ4LNSEL_LSB 0 +#define CSR_DQ4LNSEL_MASK GENMASK_32(2, 0) +/* CSR_DQ5LNSEL */ +#define CSR_DQ5LNSEL_LSB 0 +#define CSR_DQ5LNSEL_MASK GENMASK_32(2, 0) +/* CSR_DQ6LNSEL */ +#define CSR_DQ6LNSEL_LSB 0 +#define CSR_DQ6LNSEL_MASK GENMASK_32(2, 0) +/* CSR_DQ7LNSEL */ +#define CSR_DQ7LNSEL_LSB 0 +#define CSR_DQ7LNSEL_MASK GENMASK_32(2, 0) +/* CSR_PPTCTLSTATIC */ +#define CSR_PPTCTLSTATIC_LSB 0 +#define CSR_PPTCTLSTATIC_MASK GENMASK_32(11, 0) +#define CSR_PPTENDQS2DQTG0_LSB 0 +#define CSR_PPTENDQS2DQTG0_MASK BIT(0) +#define CSR_PPTENDQS2DQTG1_LSB 1 +#define CSR_PPTENDQS2DQTG1_MASK BIT(1) +#define CSR_DOCBYTESELTG0_LSB 2 +#define CSR_DOCBYTESELTG0_MASK BIT(2) +#define CSR_DOCBYTESELTG1_LSB 3 +#define CSR_DOCBYTESELTG1_MASK BIT(3) +#define CSR_PPTINFOSEL_LSB 4 +#define CSR_PPTINFOSEL_MASK GENMASK_32(7, 4) +#define CSR_PPTENRXENDLYTG0_LSB 8 +#define CSR_PPTENRXENDLYTG0_MASK BIT(8) +#define CSR_PPTENRXENDLYTG1_LSB 9 +#define CSR_PPTENRXENDLYTG1_MASK BIT(9) +#define CSR_PPTENRXENBACKOFF_LSB 10 +#define CSR_PPTENRXENBACKOFF_MASK GENMASK_32(11, 10) +/* CSR_PPTCTLDYN */ +#define CSR_PPTCTLDYN_LSB 0 +#define CSR_PPTCTLDYN_MASK GENMASK_32(1, 0) +#define CSR_PPTDQS2DQACTIVE_LSB 0 +#define CSR_PPTDQS2DQACTIVE_MASK BIT(0) +#define CSR_PPTENRXENUSEDQSSAMPVAL_LSB 1 +#define CSR_PPTENRXENUSEDQSSAMPVAL_MASK BIT(1) +/* CSR_PPTINFO */ +#define CSR_PPTINFO_LSB 0 +#define CSR_PPTINFO_MASK GENMASK_32(15, 0) +/* CSR_PPTRXENEVNT */ +#define CSR_PPTRXENEVNT_LSB 0 +#define CSR_PPTRXENEVNT_MASK GENMASK_32(1, 0) +#define CSR_PPTRXENINIT_LSB 0 +#define CSR_PPTRXENINIT_MASK BIT(0) +#define CSR_PPTRXENMHUI_LSB 1 +#define CSR_PPTRXENMHUI_MASK BIT(1) +/* CSR_PPTDQSCNTINVTRNTG0 */ +#define CSR_PPTDQSCNTINVTRNTG0_LSB 0 +#define CSR_PPTDQSCNTINVTRNTG0_MASK GENMASK_32(15, 0) +/* CSR_PPTDQSCNTINVTRNTG1 */ +#define CSR_PPTDQSCNTINVTRNTG1_LSB 0 +#define CSR_PPTDQSCNTINVTRNTG1_MASK GENMASK_32(15, 0) +/* CSR_DTSMBLANKINGCTRL */ +#define CSR_DTSMBLANKINGCTRL_LSB 0 +#define CSR_DTSMBLANKINGCTRL_MASK GENMASK_32(9, 0) +#define CSR_DTSMBLANK_LSB 0 +#define CSR_DTSMBLANK_MASK GENMASK_32(9, 0) +/* CSR_TSM0 */ +#define CSR_TSM0_LSB 0 +#define CSR_TSM0_MASK GENMASK_32(13, 0) +#define CSR_DTSMENB_LSB 0 +#define CSR_DTSMENB_MASK BIT(0) +#define CSR_DTSMDIR_LSB 1 +#define CSR_DTSMDIR_MASK BIT(1) +#define CSR_DTSMIGNFRST_LSB 2 +#define CSR_DTSMIGNFRST_MASK BIT(2) +#define CSR_DTSMODDPHASE_LSB 3 +#define CSR_DTSMODDPHASE_MASK BIT(3) +#define CSR_DTSMFLTPRE_LSB 4 +#define CSR_DTSMFLTPRE_MASK BIT(4) +#define CSR_DTSMFLTCUR_LSB 5 +#define CSR_DTSMFLTCUR_MASK BIT(5) +#define CSR_DTSMFLTNXT_LSB 6 +#define CSR_DTSMFLTNXT_MASK BIT(6) +#define CSR_DTSMFLTVAL_LSB 7 +#define CSR_DTSMFLTVAL_MASK GENMASK_32(9, 7) +#define CSR_DTSMMSKBIT_LSB 10 +#define CSR_DTSMMSKBIT_MASK GENMASK_32(13, 10) +/* CSR_TSM1 */ +#define CSR_TSM1_LSB 0 +#define CSR_TSM1_MASK GENMASK_32(15, 0) +#define CSR_DTSMERRCNT_LSB 0 +#define CSR_DTSMERRCNT_MASK GENMASK_32(15, 0) +/* CSR_TSM2 */ +#define CSR_TSM2_LSB 0 +#define CSR_TSM2_MASK BIT(0) +#define CSR_DTSMDISERRCHK_LSB 0 +#define CSR_DTSMDISERRCHK_MASK BIT(0) +/* CSR_TSM3 */ +#define CSR_TSM3_LSB 0 +#define CSR_TSM3_MASK GENMASK_32(9, 0) +#define CSR_DTSMCLRERRCNTMSK_LSB 0 +#define CSR_DTSMCLRERRCNTMSK_MASK GENMASK_32(8, 0) +#define CSR_DTSMCLRERRCNT_LSB 9 +#define CSR_DTSMCLRERRCNT_MASK BIT(9) +/* CSR_TXCHKDATASELECTS */ +#define CSR_TXCHKDATASELECTS_LSB 0 +#define CSR_TXCHKDATASELECTS_MASK GENMASK_32(1, 0) +#define CSR_SELCHKTOTX_LSB 0 +#define CSR_SELCHKTOTX_MASK BIT(0) +#define CSR_SELTXTOCHK_LSB 1 +#define CSR_SELTXTOCHK_MASK BIT(1) +/* CSR_DTSMUPTHLDXINGIND */ +#define CSR_DTSMUPTHLDXINGIND_LSB 0 +#define CSR_DTSMUPTHLDXINGIND_MASK GENMASK_32(8, 0) +/* CSR_DTSMLOTHLDXINGIND */ +#define CSR_DTSMLOTHLDXINGIND_LSB 0 +#define CSR_DTSMLOTHLDXINGIND_MASK GENMASK_32(8, 0) +/* CSR_DBYTEALLDTSMCTRL0 */ +#define CSR_DBYTEALLDTSMCTRL0_LSB 0 +#define CSR_DBYTEALLDTSMCTRL0_MASK GENMASK_32(8, 0) +#define CSR_DTSMINHIBDTSM_LSB 0 +#define CSR_DTSMINHIBDTSM_MASK GENMASK_32(8, 0) +/* CSR_DBYTEALLDTSMCTRL1 */ +#define CSR_DBYTEALLDTSMCTRL1_LSB 0 +#define CSR_DBYTEALLDTSMCTRL1_MASK GENMASK_32(8, 0) +#define CSR_DTSMGATEINC_LSB 0 +#define CSR_DTSMGATEINC_MASK GENMASK_32(8, 0) +/* CSR_DBYTEALLDTSMCTRL2 */ +#define CSR_DBYTEALLDTSMCTRL2_LSB 0 +#define CSR_DBYTEALLDTSMCTRL2_MASK GENMASK_32(8, 0) +#define CSR_DTSMGATEDEC_LSB 0 +#define CSR_DTSMGATEDEC_MASK GENMASK_32(8, 0) +/* CSR_TXDQDLYTG0 */ +#define CSR_TXDQDLYTG0_LSB 0 +#define CSR_TXDQDLYTG0_MASK GENMASK_32(8, 0) +/* CSR_TXDQDLYTG1 */ +#define CSR_TXDQDLYTG1_LSB 0 +#define CSR_TXDQDLYTG1_MASK GENMASK_32(8, 0) +/* CSR_TXDQDLYTG2 */ +#define CSR_TXDQDLYTG2_LSB 0 +#define CSR_TXDQDLYTG2_MASK GENMASK_32(8, 0) +/* CSR_TXDQDLYTG3 */ +#define CSR_TXDQDLYTG3_LSB 0 +#define CSR_TXDQDLYTG3_MASK GENMASK_32(8, 0) +/* CSR_TXDQSDLYTG0 */ +#define CSR_TXDQSDLYTG0_LSB 0 +#define CSR_TXDQSDLYTG0_MASK GENMASK_32(9, 0) +/* CSR_TXDQSDLYTG1 */ +#define CSR_TXDQSDLYTG1_LSB 0 +#define CSR_TXDQSDLYTG1_MASK GENMASK_32(9, 0) +/* CSR_TXDQSDLYTG2 */ +#define CSR_TXDQSDLYTG2_LSB 0 +#define CSR_TXDQSDLYTG2_MASK GENMASK_32(9, 0) +/* CSR_TXDQSDLYTG3 */ +#define CSR_TXDQSDLYTG3_LSB 0 +#define CSR_TXDQSDLYTG3_MASK GENMASK_32(9, 0) +/* CSR_DXLCDLSTATUS_ADDR */ +#define CSR_DXLCDLSTATUS_LSB 0 +#define CSR_DXLCDLSTATUS_MASK GENMASK_32(13, 0) +#define CSR_DXLCDLFINESNAPVAL_LSB 0 +#define CSR_DXLCDLFINESNAPVAL_MASK GENMASK_32(9, 0) +#define CSR_DXLCDLPHDSNAPVAL_LSB 10 +#define CSR_DXLCDLPHDSNAPVAL_MASK BIT(10) +#define CSR_DXLCDLSTICKYLOCK_LSB 11 +#define CSR_DXLCDLSTICKYLOCK_MASK BIT(11) +#define CSR_DXLCDLSTICKYUNLOCK_LSB 12 +#define CSR_DXLCDLSTICKYUNLOCK_MASK BIT(12) +#define CSR_DXLCDLLIVELOCK_LSB 13 +#define CSR_DXLCDLLIVELOCK_MASK BIT(13) + +/* MASTER0 register offsets */ +/* CSR_RXFIFOINIT */ +#define CSR_RXFIFOINIT_LSB 0 +#define CSR_RXFIFOINIT_MASK GENMASK_32(1, 0) +#define CSR_RXFIFOINITPTR_LSB 0 +#define CSR_RXFIFOINITPTR_MASK BIT(0) +#define CSR_INHIBITRXFIFORD_LSB 1 +#define CSR_INHIBITRXFIFORD_MASK BIT(1) +/* CSR_FORCECLKDISABLE */ +#define CSR_FORCECLKDISABLE_LSB 0 +#define CSR_FORCECLKDISABLE_MASK GENMASK_32(3, 0) +/* CSR_CLOCKINGCTRL */ +#define CSR_CLOCKINGCTRL_LSB 0 +#define CSR_CLOCKINGCTRL_MASK GENMASK_32(1, 0) +#define CSR_PCLKENASYNCCTRL_LSB 0 +#define CSR_PCLKENASYNCCTRL_MASK BIT(0) +#define CSR_DLLTRACKENCTRL_LSB 1 +#define CSR_DLLTRACKENCTRL_MASK BIT(1) +/* CSR_FORCEINTERNALUPDATE */ +#define CSR_FORCEINTERNALUPDATE_LSB 0 +#define CSR_FORCEINTERNALUPDATE_MASK BIT(0) +/* CSR_PHYCONFIG */ +#define CSR_PHYCONFIG_LSB 0 +#define CSR_PHYCONFIG_MASK GENMASK_32(9, 0) +#define CSR_PHYCONFIGANIBS_LSB 0 +#define CSR_PHYCONFIGANIBS_MASK GENMASK_32(3, 0) +#define CSR_PHYCONFIGDBYTES_LSB 4 +#define CSR_PHYCONFIGDBYTES_MASK GENMASK_32(7, 4) +#define CSR_PHYCONFIGDFI_LSB 8 +#define CSR_PHYCONFIGDFI_MASK GENMASK_32(9, 8) +/* CSR_PGCR */ +#define CSR_PGCR_LSB 0 +#define CSR_PGCR_MASK BIT(0) +#define CSR_RXCLKRISEFALLMODE_LSB 0 +#define CSR_RXCLKRISEFALLMODE_MASK BIT(0) +/* CSR_TESTBUMPCNTRL1 */ +#define CSR_TESTBUMPCNTRL1_LSB 0 +#define CSR_TESTBUMPCNTRL1_MASK GENMASK_32(15, 0) +#define CSR_TESTMAJORMODE_LSB 0 +#define CSR_TESTMAJORMODE_MASK GENMASK_32(2, 0) +#define CSR_TESTBIASBYPASSEN_LSB 3 +#define CSR_TESTBIASBYPASSEN_MASK BIT(3) +#define CSR_TESTANALOGOUTCTRL_LSB 4 +#define CSR_TESTANALOGOUTCTRL_MASK GENMASK_32(7, 4) +#define CSR_TESTGAINCURRADJ_LSB 8 +#define CSR_TESTGAINCURRADJ_MASK GENMASK_32(12, 8) +#define CSR_TESTSELEXTERNALVREF_LSB 13 +#define CSR_TESTSELEXTERNALVREF_MASK BIT(13) +#define CSR_TESTEXTVREFRANGE_LSB 14 +#define CSR_TESTEXTVREFRANGE_MASK BIT(14) +#define CSR_TESTPOWERGATEEN_LSB 15 +#define CSR_TESTPOWERGATEEN_MASK BIT(15) +/* CSR_CALUCLKINFO */ +#define CSR_CALUCLKINFO_LSB 0 +#define CSR_CALUCLKINFO_MASK GENMASK_32(10, 0) +#define CSR_CALUCLKTICKSPER1US_LSB 0 +#define CSR_CALUCLKTICKSPER1US_MASK GENMASK_32(10, 0) +/* CSR_TESTBUMPCNTRL */ +#define CSR_TESTBUMPCNTRL_LSB 0 +#define CSR_TESTBUMPCNTRL_MASK GENMASK_32(9, 0) +#define CSR_TESTBUMPEN_LSB 0 +#define CSR_TESTBUMPEN_MASK GENMASK_32(1, 0) +#define CSR_TESTBUMPTOGGLE_LSB 2 +#define CSR_TESTBUMPTOGGLE_MASK BIT(2) +#define CSR_TESTBUMPDATASEL_LSB 3 +#define CSR_TESTBUMPDATASEL_MASK GENMASK_32(8, 3) +#define CSR_FORCEMTESTONALERT_LSB 9 +#define CSR_FORCEMTESTONALERT_MASK BIT(9) +/* CSR_SEQ0BDLY0 */ +#define CSR_SEQ0BDLY0_LSB 0 +#define CSR_SEQ0BDLY0_MASK GENMASK_32(15, 0) +/* CSR_SEQ0BDLY1 */ +#define CSR_SEQ0BDLY1_LSB 0 +#define CSR_SEQ0BDLY1_MASK GENMASK_32(15, 0) +/* CSR_SEQ0BDLY2 */ +#define CSR_SEQ0BDLY2_LSB 0 +#define CSR_SEQ0BDLY2_MASK GENMASK_32(15, 0) +/* CSR_SEQ0BDLY3 */ +#define CSR_SEQ0BDLY3_LSB 0 +#define CSR_SEQ0BDLY3_MASK GENMASK_32(15, 0) +/* CSR_PHYALERTSTATUS */ +#define CSR_PHYALERTSTATUS_LSB 0 +#define CSR_PHYALERTSTATUS_MASK BIT(0) +#define CSR_PHYALERT_LSB 0 +#define CSR_PHYALERT_MASK BIT(0) +/* CSR_PPTTRAINSETUP */ +#define CSR_PPTTRAINSETUP_LSB 0 +#define CSR_PPTTRAINSETUP_MASK GENMASK_32(6, 0) +#define CSR_PHYMSTRTRAININTERVAL_LSB 0 +#define CSR_PHYMSTRTRAININTERVAL_MASK GENMASK_32(3, 0) +#define CSR_PHYMSTRMAXREQTOACK_LSB 4 +#define CSR_PHYMSTRMAXREQTOACK_MASK GENMASK_32(6, 4) +/* CSR_PPTTRAINSETUP2 */ +#define CSR_PPTTRAINSETUP2_LSB 0 +#define CSR_PPTTRAINSETUP2_MASK GENMASK_32(2, 0) +#define CSR_PHYMSTRFREQOVERRIDE_LSB 0 +#define CSR_PHYMSTRFREQOVERRIDE_MASK GENMASK_32(2, 0) +/* CSR_ATESTMODE */ +#define CSR_ATESTMODE_LSB 0 +#define CSR_ATESTMODE_MASK GENMASK_32(4, 0) +#define CSR_ATESTPRBSEN_LSB 0 +#define CSR_ATESTPRBSEN_MASK BIT(0) +#define CSR_ATESTCLKEN_LSB 1 +#define CSR_ATESTCLKEN_MASK BIT(1) +#define CSR_ATESTMODESEL_LSB 2 +#define CSR_ATESTMODESEL_MASK GENMASK_32(4, 2) +/* CSR_TXCALBINP */ +#define CSR_TXCALBINP_LSB 0 +#define CSR_TXCALBINP_MASK GENMASK_32(4, 0) +/* CSR_TXCALBINN */ +#define CSR_TXCALBINN_LSB 0 +#define CSR_TXCALBINN_MASK GENMASK_32(4, 0) +/* CSR_TXCALPOVR */ +#define CSR_TXCALPOVR_LSB 0 +#define CSR_TXCALPOVR_MASK GENMASK_32(5, 0) +#define CSR_TXCALBINPOVRVAL_LSB 0 +#define CSR_TXCALBINPOVRVAL_MASK GENMASK_32(4, 0) +#define CSR_TXCALBINPOVREN_LSB 5 +#define CSR_TXCALBINPOVREN_MASK BIT(5) +/* CSR_TXCALNOVR */ +#define CSR_TXCALNOVR_LSB 0 +#define CSR_TXCALNOVR_MASK GENMASK_32(5, 0) +#define CSR_TXCALBINNOVRVAL_LSB 0 +#define CSR_TXCALBINNOVRVAL_MASK GENMASK_32(4, 0) +#define CSR_TXCALBINNOVREN_LSB 5 +#define CSR_TXCALBINNOVREN_MASK BIT(5) +/* CSR_DFIMODE */ +#define CSR_DFIMODE_LSB 0 +#define CSR_DFIMODE_MASK GENMASK_32(2, 0) +#define CSR_DFI0ENABLE_LSB 0 +#define CSR_DFI0ENABLE_MASK BIT(0) +#define CSR_DFI1ENABLE_LSB 1 +#define CSR_DFI1ENABLE_MASK BIT(1) +#define CSR_DFI1OVERRIDE_LSB 2 +#define CSR_DFI1OVERRIDE_MASK BIT(2) +/* CSR_TRISTATEMODECA */ +#define CSR_TRISTATEMODECA_LSB 0 +#define CSR_TRISTATEMODECA_MASK GENMASK_32(3, 0) +#define CSR_DISDYNADRTRI_LSB 0 +#define CSR_DISDYNADRTRI_MASK BIT(0) +#define CSR_DDR2TMODE_LSB 1 +#define CSR_DDR2TMODE_MASK BIT(1) +#define CSR_CKDISVAL_LSB 2 +#define CSR_CKDISVAL_MASK GENMASK_32(3, 2) +/* MTESTMUXSEL already defined in ANIBx section */ +/* CSR_MTESTPGMINFO */ +#define CSR_MTESTPGMINFO_LSB 0 +#define CSR_MTESTPGMINFO_MASK BIT(0) +/* CSR_DYNPWRDNUP */ +#define CSR_DYNPWRDNUP_LSB 0 +#define CSR_DYNPWRDNUP_MASK BIT(0) +#define CSR_DYNPOWERDOWN_LSB 0 +#define CSR_DYNPOWERDOWN_MASK BIT(0) +/* CSR_PMIENABLE */ +#define CSR_PMIENABLE_LSB 0 +#define CSR_PMIENABLE_MASK BIT(0) +/* CSR_PHYTID */ +#define CSR_PHYTID_LSB 0 +#define CSR_PHYTID_MASK GENMASK_32(15, 0) +/* CSR_HWTMRL */ +#define CSR_HWTMRL_LSB 0 +#define CSR_HWTMRL_MASK GENMASK_32(4, 0) +/* CSR_DFIPHYUPD */ +#define CSR_DFIPHYUPD_LSB 0 +#define CSR_DFIPHYUPD_MASK GENMASK_32(15, 0) +#define CSR_DFIPHYUPDCNT_LSB 0 +#define CSR_DFIPHYUPDCNT_MASK GENMASK_32(3, 0) +#define CSR_DFIPHYUPDRESP_LSB 4 +#define CSR_DFIPHYUPDRESP_MASK GENMASK_32(6, 4) +#define CSR_DFIPHYUPDMODE_LSB 7 +#define CSR_DFIPHYUPDMODE_MASK BIT(7) +#define CSR_DFIPHYUPDTHRESHOLD_LSB 8 +#define CSR_DFIPHYUPDTHRESHOLD_MASK GENMASK_32(11, 8) +#define CSR_DFIPHYUPDINTTHRESHOLD_LSB 12 +#define CSR_DFIPHYUPDINTTHRESHOLD_MASK GENMASK_32(15, 12) +/* CSR_PDAMRSWRITEMODE */ +#define CSR_PDAMRSWRITEMODE_LSB 0 +#define CSR_PDAMRSWRITEMODE_MASK BIT(0) +/* CSR_DFIGEARDOWNCTL */ +#define CSR_DFIGEARDOWNCTL_LSB 0 +#define CSR_DFIGEARDOWNCTL_MASK GENMASK_32(1, 0) +/* CSR_DQSPREAMBLECONTROL */ +#define CSR_DQSPREAMBLECONTROL_LSB 0 +#define CSR_DQSPREAMBLECONTROL_MASK GENMASK_32(8, 0) +#define CSR_TWOTCKRXDQSPRE_LSB 0 +#define CSR_TWOTCKRXDQSPRE_MASK BIT(0) +#define CSR_TWOTCKTXDQSPRE_LSB 1 +#define CSR_TWOTCKTXDQSPRE_MASK BIT(1) +#define CSR_POSITIONDFEINIT_LSB 2 +#define CSR_POSITIONDFEINIT_MASK GENMASK_32(4, 2) +#define CSR_LP4TGLTWOTCKTXDQSPRE_LSB 5 +#define CSR_LP4TGLTWOTCKTXDQSPRE_MASK BIT(5) +#define CSR_LP4POSTAMBLEEXT_LSB 6 +#define CSR_LP4POSTAMBLEEXT_MASK BIT(6) +#define CSR_LP4STTCPREBRIDGERXEN_LSB 7 +#define CSR_LP4STTCPREBRIDGERXEN_MASK BIT(7) +#define CSR_WDQSEXTENSION_LSB 8 +#define CSR_WDQSEXTENSION_MASK BIT(8) +/* CSR_MASTERX4CONFIG */ +#define CSR_MASTERX4CONFIG_LSB 0 +#define CSR_MASTERX4CONFIG_MASK GENMASK_32(3, 0) +#define CSR_X4TG_LSB 0 +#define CSR_X4TG_MASK GENMASK_32(3, 0) +/* CSR_WRLEVBITS */ +#define CSR_WRLEVBITS_LSB 0 +#define CSR_WRLEVBITS_MASK GENMASK_32(7, 0) +#define CSR_WRLEVFORDQSL_LSB 0 +#define CSR_WRLEVFORDQSL_MASK GENMASK_32(3, 0) +#define CSR_WRLEVFORDQSU_LSB 4 +#define CSR_WRLEVFORDQSU_MASK GENMASK_32(7, 4) +/* CSR_ENABLECSMULTICAST */ +#define CSR_ENABLECSMULTICAST_LSB 0 +#define CSR_ENABLECSMULTICAST_MASK BIT(0) +/* CSR_HWTLPCSMULTICAST */ +#define CSR_HWTLPCSMULTICAST_LSB 0 +#define CSR_HWTLPCSMULTICAST_MASK BIT(0) +/* CSR_ACX4ANIBDIS */ +#define CSR_ACX4ANIBDIS_LSB 0 +#define CSR_ACX4ANIBDIS_MASK GENMASK_32(11, 0) +/* CSR_DMIPINPRESENT */ +#define CSR_DMIPINPRESENT_LSB 0 +#define CSR_DMIPINPRESENT_MASK BIT(0) +#define CSR_RDDBIENABLED_LSB 0 +#define CSR_RDDBIENABLED_MASK BIT(0) +/* CSR_ARDPTRINITVAL */ +#define CSR_ARDPTRINITVAL_LSB 0 +#define CSR_ARDPTRINITVAL_MASK GENMASK_32(3, 0) +/* CSR_DB0LCDLCALPHDETOUT */ +#define CSR_DB0LCDLCALPHDETOUT_LSB 0 +#define CSR_DB0LCDLCALPHDETOUT_MASK GENMASK_32(15, 0) +/* CSR_DB1LCDLCALPHDETOUT */ +#define CSR_DB1LCDLCALPHDETOUT_LSB 0 +#define CSR_DB1LCDLCALPHDETOUT_MASK GENMASK_32(15, 0) +/* CSR_DB2LCDLCALPHDETOUT */ +#define CSR_DB2LCDLCALPHDETOUT_LSB 0 +#define CSR_DB2LCDLCALPHDETOUT_MASK GENMASK_32(15, 0) +/* CSR_DB3LCDLCALPHDETOUT */ +#define CSR_DB3LCDLCALPHDETOUT_LSB 0 +#define CSR_DB3LCDLCALPHDETOUT_MASK GENMASK_32(15, 0) +/* CSR_DB4LCDLCALPHDETOUT */ +#define CSR_DB4LCDLCALPHDETOUT_LSB 0 +#define CSR_DB4LCDLCALPHDETOUT_MASK GENMASK_32(15, 0) +/* CSR_DB5LCDLCALPHDETOUT */ +#define CSR_DB5LCDLCALPHDETOUT_LSB 0 +#define CSR_DB5LCDLCALPHDETOUT_MASK GENMASK_32(15, 0) +/* CSR_DB6LCDLCALPHDETOUT */ +#define CSR_DB6LCDLCALPHDETOUT_LSB 0 +#define CSR_DB6LCDLCALPHDETOUT_MASK GENMASK_32(15, 0) +/* CSR_DB7LCDLCALPHDETOUT */ +#define CSR_DB7LCDLCALPHDETOUT_LSB 0 +#define CSR_DB7LCDLCALPHDETOUT_MASK GENMASK_32(15, 0) +/* CSR_DB8LCDLCALPHDETOUT */ +#define CSR_DB8LCDLCALPHDETOUT_LSB 0 +#define CSR_DB8LCDLCALPHDETOUT_MASK GENMASK_32(15, 0) +/* CSR_DB9LCDLCALPHDETOUT */ +#define CSR_DB9LCDLCALPHDETOUT_LSB 0 +#define CSR_DB9LCDLCALPHDETOUT_MASK GENMASK_32(15, 0) +/* CSR_DBYTEDLLMODECNTRL */ +#define CSR_DBYTEDLLMODECNTRL_LSB 1 +#define CSR_DBYTEDLLMODECNTRL_MASK BIT(1) +#define CSR_DLLRXPREAMBLEMODE_LSB 1 +#define CSR_DLLRXPREAMBLEMODE_MASK BIT(1) +/* CSR_DBYTERXENTRAIN */ +#define CSR_DBYTERXENTRAIN_LSB 0 +#define CSR_DBYTERXENTRAIN_MASK BIT(0) +#define CSR_RXENTRAIN_LSB 0 +#define CSR_RXENTRAIN_MASK BIT(0) +/* CSR_ANLCDLCALPHDETOUT */ +#define CSR_ANLCDLCALPHDETOUT_LSB 0 +#define CSR_ANLCDLCALPHDETOUT_MASK GENMASK_32(11, 0) +/* CSR_CALOFFSETS */ +#define CSR_CALOFFSETS_LSB 0 +#define CSR_CALOFFSETS_MASK GENMASK_32(13, 0) +#define CSR_CALCMPR5OFFSET_LSB 0 +#define CSR_CALCMPR5OFFSET_MASK GENMASK_32(5, 0) +#define CSR_CALDRVPDTHOFFSET_LSB 6 +#define CSR_CALDRVPDTHOFFSET_MASK GENMASK_32(9, 6) +#define CSR_CALDRVPUTHOFFSET_LSB 10 +#define CSR_CALDRVPUTHOFFSET_MASK GENMASK_32(13, 10) +/* CSR_SARINITVALS */ +#define CSR_SARINITVALS_LSB 0 +#define CSR_SARINITVALS_MASK GENMASK_32(8, 0) +#define CSR_SARINITOFFSET05_LSB 0 +#define CSR_SARINITOFFSET05_MASK GENMASK_32(2, 0) +#define CSR_SARINITNINT_LSB 3 +#define CSR_SARINITNINT_MASK GENMASK_32(5, 3) +#define CSR_SARINITPEXT_LSB 6 +#define CSR_SARINITPEXT_MASK GENMASK_32(8, 6) +/* CSR_CALPEXTOVR */ +#define CSR_CALPEXTOVR_LSB 0 +#define CSR_CALPEXTOVR_MASK GENMASK_32(4, 0) +/* CSR_CALCMPR5OVR */ +#define CSR_CALCMPR5OVR_LSB 0 +#define CSR_CALCMPR5OVR_MASK GENMASK_32(7, 0) +/* CSR_CALNINTOVR */ +#define CSR_CALNINTOVR_LSB 0 +#define CSR_CALNINTOVR_MASK GENMASK_32(4, 0) +/* CSR_CALDRVSTR0 */ +#define CSR_CALDRVSTR0_LSB 0 +#define CSR_CALDRVSTR0_MASK GENMASK_32(7, 0) +#define CSR_CALDRVSTRPD50_LSB 0 +#define CSR_CALDRVSTRPD50_MASK GENMASK_32(3, 0) +#define CSR_CALDRVSTRPU50_LSB 4 +#define CSR_CALDRVSTRPU50_MASK GENMASK_32(7, 4) +/* CSR_PROCODTCTL */ +#define CSR_PROCODTCTL_LSB 0 +#define CSR_PROCODTCTL_MASK GENMASK_32(1, 0) +#define CSR_PROCODTALWAYSOFF_LSB 0 +#define CSR_PROCODTALWAYSOFF_MASK BIT(0) +#define CSR_PROCODTALWAYSON_LSB 1 +#define CSR_PROCODTALWAYSON_MASK BIT(1) +/* CSR_PROCODTTIMECTL */ +#define CSR_PROCODTTIMECTL_LSB 0 +#define CSR_PROCODTTIMECTL_MASK GENMASK_32(5, 0) +#define CSR_PODTTAILWIDTH_LSB 0 +#define CSR_PODTTAILWIDTH_MASK GENMASK_32(1, 0) +#define CSR_PODTSTARTDELAY_LSB 2 +#define CSR_PODTSTARTDELAY_MASK GENMASK_32(3, 2) +#define CSR_PODTTAILWIDTHEXT_LSB 4 +#define CSR_PODTTAILWIDTHEXT_MASK GENMASK_32(5, 4) +/* CSR_MEMALERTCONTROL */ +#define CSR_MEMALERTCONTROL_LSB 0 +#define CSR_MEMALERTCONTROL_MASK GENMASK_32(15, 0) +#define CSR_MALERTVREFLEVEL_LSB 0 +#define CSR_MALERTVREFLEVEL_MASK GENMASK_32(6, 0) +#define CSR_MALERTVREFEXTEN_LSB 7 +#define CSR_MALERTVREFEXTEN_MASK BIT(7) +#define CSR_MALERTPUSTREN_LSB 8 +#define CSR_MALERTPUSTREN_MASK GENMASK_32(11, 8) +#define CSR_MALERTPUEN_LSB 12 +#define CSR_MALERTPUEN_MASK BIT(12) +#define CSR_MALERTRXEN_LSB 13 +#define CSR_MALERTRXEN_MASK BIT(13) +#define CSR_MALERTDISABLEVAL_LSB 14 +#define CSR_MALERTDISABLEVAL_MASK BIT(14) +#define CSR_MALERTFORCEERROR_LSB 15 +#define CSR_MALERTFORCEERROR_MASK BIT(15) +/* CSR_MEMALERTCONTROL2 */ +#define CSR_MEMALERTCONTROL2_LSB 0 +#define CSR_MEMALERTCONTROL2_MASK BIT(0) +#define CSR_MALERTSYNCBYPASS_LSB 0 +#define CSR_MALERTSYNCBYPASS_MASK BIT(0) +/* CSR_MEMRESETL */ +#define CSR_MEMRESETL_LSB 0 +#define CSR_MEMRESETL_MASK GENMASK_32(1, 0) +#define CSR_MEMRESETLVALUE_LSB 0 +#define CSR_MEMRESETLVALUE_MASK BIT(0) +#define CSR_PROTECTMEMRESET_LSB 1 +#define CSR_PROTECTMEMRESET_MASK BIT(1) +/* CSR_PUBMODE */ +#define CSR_PUBMODE_LSB 0 +#define CSR_PUBMODE_MASK BIT(0) +#define CSR_HWTMEMSRC_LSB 0 +#define CSR_HWTMEMSRC_MASK BIT(0) +/* CSR_MISCPHYSTATUS */ +#define CSR_MISCPHYSTATUS_LSB 0 +#define CSR_MISCPHYSTATUS_MASK GENMASK_32(1, 0) +#define CSR_DCTSANE_LSB 0 +#define CSR_DCTSANE_MASK BIT(0) +#define CSR_PORMEMRESET_LSB 1 +#define CSR_PORMEMRESET_MASK BIT(1) +/* CSR_CORELOOPBACKSEL */ +#define CSR_CORELOOPBACKSEL_LSB 0 +#define CSR_CORELOOPBACKSEL_MASK BIT(0) +/* CSR_DLLTRAINPARAM */ +#define CSR_DLLTRAINPARAM_LSB 0 +#define CSR_DLLTRAINPARAM_MASK GENMASK_32(1, 0) +#define CSR_EXTENDPHDTIME_LSB 0 +#define CSR_EXTENDPHDTIME_MASK GENMASK_32(1, 0) +/* CSR_HWTLPCSENA */ +#define CSR_HWTLPCSENA_LSB 0 +#define CSR_HWTLPCSENA_MASK GENMASK_32(1, 0) +/* CSR_HWTLPCSENB */ +#define CSR_HWTLPCSENB_LSB 0 +#define CSR_HWTLPCSENB_MASK GENMASK_32(1, 0) +/* CSR_HWTLPCSENBYPASS */ +#define CSR_HWTLPCSENBYPASS_LSB 0 +#define CSR_HWTLPCSENBYPASS_MASK BIT(0) +/* CSR_DFICAMODE */ +#define CSR_DFICAMODE_LSB 0 +#define CSR_DFICAMODE_MASK GENMASK_32(3, 0) +#define CSR_DFILP3CAMODE_LSB 0 +#define CSR_DFILP3CAMODE_MASK BIT(0) +#define CSR_DFID4CAMODE_LSB 1 +#define CSR_DFID4CAMODE_MASK BIT(1) +#define CSR_DFILP4CAMODE_LSB 2 +#define CSR_DFILP4CAMODE_MASK BIT(2) +#define CSR_DFID4ALTCAMODE_LSB 3 +#define CSR_DFID4ALTCAMODE_MASK BIT(3) +/* CSR_HWTCACTL */ +#define CSR_HWTCACTL_LSB 0 +#define CSR_HWTCACTL_MASK BIT(0) +#define CSR_HWTDISDYNADRTRI_LSB 0 +#define CSR_HWTDISDYNADRTRI_MASK BIT(0) +/* CSR_HWTCAMODE */ +#define CSR_HWTCAMODE_LSB 0 +#define CSR_HWTCAMODE_MASK GENMASK_32(5, 0) +#define CSR_HWTLP3CAMODE_LSB 0 +#define CSR_HWTLP3CAMODE_MASK BIT(0) +#define CSR_HWTD4CAMODE_LSB 1 +#define CSR_HWTD4CAMODE_MASK BIT(1) +#define CSR_HWTLP4CAMODE_LSB 2 +#define CSR_HWTLP4CAMODE_MASK BIT(2) +#define CSR_HWTD4ALTCAMODE_LSB 3 +#define CSR_HWTD4ALTCAMODE_MASK BIT(3) +#define CSR_HWTCSINVERT_LSB 4 +#define CSR_HWTCSINVERT_MASK BIT(4) +#define CSR_HWTDBIINVERT_LSB 5 +#define CSR_HWTDBIINVERT_MASK BIT(5) +/* CSR_DLLCONTROL */ +#define CSR_DLLCONTROL_LSB 0 +#define CSR_DLLCONTROL_MASK GENMASK_32(2, 0) +#define CSR_DLLRESETRELOCK_LSB 0 +#define CSR_DLLRESETRELOCK_MASK BIT(0) +#define CSR_DLLRESETSLAVE_LSB 1 +#define CSR_DLLRESETSLAVE_MASK BIT(1) +#define CSR_DLLRESETRSVD_LSB 2 +#define CSR_DLLRESETRSVD_MASK BIT(2) +/* CSR_PULSEDLLUPDATEPHASE */ +#define CSR_PULSEDLLUPDATEPHASE_LSB 0 +#define CSR_PULSEDLLUPDATEPHASE_MASK GENMASK_32(7, 0) +#define CSR_PULSEDBYTEDLLUPDATEPHASE_LSB 0 +#define CSR_PULSEDBYTEDLLUPDATEPHASE_MASK BIT(0) +#define CSR_PULSEACKDLLUPDATEPHASE_LSB 1 +#define CSR_PULSEACKDLLUPDATEPHASE_MASK BIT(1) +#define CSR_PULSEACADLLUPDATEPHASE_LSB 2 +#define CSR_PULSEACADLLUPDATEPHASE_MASK BIT(2) +#define CSR_UPDATEPHASEDESTRESERVED_LSB 3 +#define CSR_UPDATEPHASEDESTRESERVED_MASK GENMASK_32(5, 3) +#define CSR_TRAINUPDATEPHASEONLONGBUBBLE_LSB 6 +#define CSR_TRAINUPDATEPHASEONLONGBUBBLE_MASK BIT(6) +#define CSR_ALWAYSUPDATELCDLPHASE_LSB 7 +#define CSR_ALWAYSUPDATELCDLPHASE_MASK BIT(7) +/* CSR_HWTCONTROLOVR0 */ +#define CSR_HWTCONTROLOVR0_LSB 0 +#define CSR_HWTCONTROLOVR0_MASK GENMASK_32(12, 0) +#define CSR_HWTCS0OVR0_LSB 0 +#define CSR_HWTCS0OVR0_MASK BIT(0) +#define CSR_HWTCS1OVR0_LSB 1 +#define CSR_HWTCS1OVR0_MASK BIT(1) +#define CSR_HWTCS2OVR0_LSB 2 +#define CSR_HWTCS2OVR0_MASK BIT(2) +#define CSR_HWTCS3OVR0_LSB 3 +#define CSR_HWTCS3OVR0_MASK BIT(3) +#define CSR_HWTCKE0OVR0_LSB 4 +#define CSR_HWTCKE0OVR0_MASK BIT(4) +#define CSR_HWTCKE1OVR0_LSB 5 +#define CSR_HWTCKE1OVR0_MASK BIT(5) +#define CSR_HWTCKE2OVR0_LSB 6 +#define CSR_HWTCKE2OVR0_MASK BIT(6) +#define CSR_HWTCKE3OVR0_LSB 7 +#define CSR_HWTCKE3OVR0_MASK BIT(7) +#define CSR_HWTODT0OVR0_LSB 8 +#define CSR_HWTODT0OVR0_MASK BIT(8) +#define CSR_HWTODT1OVR0_LSB 9 +#define CSR_HWTODT1OVR0_MASK BIT(9) +#define CSR_HWTODT2OVR0_LSB 10 +#define CSR_HWTODT2OVR0_MASK BIT(10) +#define CSR_HWTODT3OVR0_LSB 11 +#define CSR_HWTODT3OVR0_MASK BIT(11) +#define CSR_HWTPARITYOVR0_LSB 12 +#define CSR_HWTPARITYOVR0_MASK BIT(12) +/* CSR_HWTCONTROLOVR1 */ +#define CSR_HWTCONTROLOVR1_LSB 0 +#define CSR_HWTCONTROLOVR1_MASK GENMASK_32(12, 0) +#define CSR_HWTCS0OVR1_LSB 0 +#define CSR_HWTCS0OVR1_MASK BIT(0) +#define CSR_HWTCS1OVR1_LSB 1 +#define CSR_HWTCS1OVR1_MASK BIT(1) +#define CSR_HWTCS2OVR1_LSB 2 +#define CSR_HWTCS2OVR1_MASK BIT(2) +#define CSR_HWTCS3OVR1_LSB 3 +#define CSR_HWTCS3OVR1_MASK BIT(3) +#define CSR_HWTCKE0OVR1_LSB 4 +#define CSR_HWTCKE0OVR1_MASK BIT(4) +#define CSR_HWTCKE1OVR1_LSB 5 +#define CSR_HWTCKE1OVR1_MASK BIT(5) +#define CSR_HWTCKE2OVR1_LSB 6 +#define CSR_HWTCKE2OVR1_MASK BIT(6) +#define CSR_HWTCKE3OVR1_LSB 7 +#define CSR_HWTCKE3OVR1_MASK BIT(7) +#define CSR_HWTODT0OVR1_LSB 8 +#define CSR_HWTODT0OVR1_MASK BIT(8) +#define CSR_HWTODT1OVR1_LSB 9 +#define CSR_HWTODT1OVR1_MASK BIT(9) +#define CSR_HWTODT2OVR1_LSB 10 +#define CSR_HWTODT2OVR1_MASK BIT(10) +#define CSR_HWTODT3OVR1_LSB 11 +#define CSR_HWTODT3OVR1_MASK BIT(11) +#define CSR_HWTPARITYOVR1_LSB 12 +#define CSR_HWTPARITYOVR1_MASK BIT(12) +/* CSR_DLLGAINCTL */ +#define CSR_DLLGAINCTL_LSB 0 +#define CSR_DLLGAINCTL_MASK GENMASK_32(11, 0) +#define CSR_DLLGAINIV_LSB 0 +#define CSR_DLLGAINIV_MASK GENMASK_32(3, 0) +#define CSR_DLLGAINTV_LSB 4 +#define CSR_DLLGAINTV_MASK GENMASK_32(7, 4) +#define CSR_DLLSEEDSEL_LSB 8 +#define CSR_DLLSEEDSEL_MASK GENMASK_32(11, 8) +/* CSR_DLLLOCKPARAM */ +#define CSR_DLLLOCKPARAM_LSB 0 +#define CSR_DLLLOCKPARAM_MASK GENMASK_32(12, 0) +#define CSR_DISDLLSEEDSEL_LSB 0 +#define CSR_DISDLLSEEDSEL_MASK BIT(0) +#define CSR_DISDLLGAINIVSEED_LSB 1 +#define CSR_DISDLLGAINIVSEED_MASK BIT(1) +#define CSR_DLLLOCKPARAMSPARE_LSB 2 +#define CSR_DLLLOCKPARAMSPARE_MASK GENMASK_32(3, 2) +#define CSR_LCDLSEED0_LSB 4 +#define CSR_LCDLSEED0_MASK GENMASK_32(12, 4) +/* CSR_HWTCONTROLVAL0 */ +#define CSR_HWTCONTROLVAL0_LSB 0 +#define CSR_HWTCONTROLVAL0_MASK GENMASK_32(12, 0) +#define CSR_HWTCS0VAL0_LSB 0 +#define CSR_HWTCS0VAL0_MASK BIT(0) +#define CSR_HWTCS1VAL0_LSB 1 +#define CSR_HWTCS1VAL0_MASK BIT(1) +#define CSR_HWTCS2VAL0_LSB 2 +#define CSR_HWTCS2VAL0_MASK BIT(2) +#define CSR_HWTCS3VAL0_LSB 3 +#define CSR_HWTCS3VAL0_MASK BIT(3) +#define CSR_HWTCKE0VAL0_LSB 4 +#define CSR_HWTCKE0VAL0_MASK BIT(4) +#define CSR_HWTCKE1VAL0_LSB 5 +#define CSR_HWTCKE1VAL0_MASK BIT(5) +#define CSR_HWTCKE2VAL0_LSB 6 +#define CSR_HWTCKE2VAL0_MASK BIT(6) +#define CSR_HWTCKE3VAL0_LSB 7 +#define CSR_HWTCKE3VAL0_MASK BIT(7) +#define CSR_HWTODT0VAL0_LSB 8 +#define CSR_HWTODT0VAL0_MASK BIT(8) +#define CSR_HWTODT1VAL0_LSB 9 +#define CSR_HWTODT1VAL0_MASK BIT(9) +#define CSR_HWTODT2VAL0_LSB 10 +#define CSR_HWTODT2VAL0_MASK BIT(10) +#define CSR_HWTODT3VAL0_LSB 11 +#define CSR_HWTODT3VAL0_MASK BIT(11) +#define CSR_HWTPARITYVAL0_LSB 12 +#define CSR_HWTPARITYVAL0_MASK BIT(12) +/* CSR_HWTCONTROLVAL1 */ +#define CSR_HWTCONTROLVAL1_LSB 0 +#define CSR_HWTCONTROLVAL1_MASK GENMASK_32(12, 0) +#define CSR_HWTCS0VAL1_LSB 0 +#define CSR_HWTCS0VAL1_MASK BIT(0) +#define CSR_HWTCS1VAL1_LSB 1 +#define CSR_HWTCS1VAL1_MASK BIT(1) +#define CSR_HWTCS2VAL1_LSB 2 +#define CSR_HWTCS2VAL1_MASK BIT(2) +#define CSR_HWTCS3VAL1_LSB 3 +#define CSR_HWTCS3VAL1_MASK BIT(3) +#define CSR_HWTCKE0VAL1_LSB 4 +#define CSR_HWTCKE0VAL1_MASK BIT(4) +#define CSR_HWTCKE1VAL1_LSB 5 +#define CSR_HWTCKE1VAL1_MASK BIT(5) +#define CSR_HWTCKE2VAL1_LSB 6 +#define CSR_HWTCKE2VAL1_MASK BIT(6) +#define CSR_HWTCKE3VAL1_LSB 7 +#define CSR_HWTCKE3VAL1_MASK BIT(7) +#define CSR_HWTODT0VAL1_LSB 8 +#define CSR_HWTODT0VAL1_MASK BIT(8) +#define CSR_HWTODT1VAL1_LSB 9 +#define CSR_HWTODT1VAL1_MASK BIT(9) +#define CSR_HWTODT2VAL1_LSB 10 +#define CSR_HWTODT2VAL1_MASK BIT(10) +#define CSR_HWTODT3VAL1_LSB 11 +#define CSR_HWTODT3VAL1_MASK BIT(11) +#define CSR_HWTPARITYVAL1_LSB 12 +#define CSR_HWTPARITYVAL1_MASK BIT(12) +/* CSR_ACSMGLBLSTART */ +#define CSR_ACSMGLBLSTART_LSB 0 +#define CSR_ACSMGLBLSTART_MASK BIT(0) +/* CSR_ACSMGLBLSGLSTPCTRL */ +#define CSR_ACSMGLBLSGLSTPCTRL_LSB 0 +#define CSR_ACSMGLBLSGLSTPCTRL_MASK GENMASK_32(1, 0) +#define CSR_ACSMSGLSTPMODE_LSB 0 +#define CSR_ACSMSGLSTPMODE_MASK BIT(0) +#define CSR_ACSMSGLSTP_LSB 1 +#define CSR_ACSMSGLSTP_MASK BIT(1) +/* CSR_LCDLCALPHASE */ +#define CSR_LCDLCALPHASE_LSB 0 +#define CSR_LCDLCALPHASE_MASK GENMASK_32(8, 0) +/* CSR_LCDLCALCTRL */ +#define CSR_LCDLCALCTRL_LSB 0 +#define CSR_LCDLCALCTRL_MASK GENMASK_32(6, 0) +#define CSR_LCDLCALMODE_LSB 0 +#define CSR_LCDLCALMODE_MASK BIT(0) +#define CSR_LCDLCALSLOWCLKSEL_LSB 1 +#define CSR_LCDLCALSLOWCLKSEL_MASK BIT(1) +#define CSR_LCDLCALEN_LSB 2 +#define CSR_LCDLCALEN_MASK BIT(2) +#define CSR_LCDLCALPHASEUPDATE_LSB 3 +#define CSR_LCDLCALPHASEUPDATE_MASK BIT(3) +#define CSR_LCDLCALCLKEN_LSB 4 +#define CSR_LCDLCALCLKEN_MASK BIT(4) +#define CSR_LCDLCALSAMPEN_LSB 5 +#define CSR_LCDLCALSAMPEN_MASK BIT(5) +#define CSR_LCDLCALSLOWCLKEN_LSB 6 +#define CSR_LCDLCALSLOWCLKEN_MASK BIT(6) +/* CSR_CALRATE */ +#define CSR_CALRATE_LSB 0 +#define CSR_CALRATE_MASK GENMASK_32(6, 0) +#define CSR_CALINTERVAL_LSB 0 +#define CSR_CALINTERVAL_MASK GENMASK_32(3, 0) +#define CSR_CALRUN_LSB 4 +#define CSR_CALRUN_MASK BIT(4) +#define CSR_CALONCE_LSB 5 +#define CSR_CALONCE_MASK BIT(5) +#define CSR_DISABLEBACKGROUNDZQUPDATES_LSB 6 +#define CSR_DISABLEBACKGROUNDZQUPDATES_MASK BIT(6) +/* CSR_CALZAP */ +#define CSR_CALZAP_LSB 0 +#define CSR_CALZAP_MASK BIT(0) +/* CSR_PSTATE */ +#define CSR_PSTATE_LSB 0 +#define CSR_PSTATE_MASK GENMASK_32(3, 0) +/* CSR_CALPREDRIVEROVERRIDE */ +#define CSR_CALPREDRIVEROVERRIDE_LSB 0 +#define CSR_CALPREDRIVEROVERRIDE_MASK GENMASK_32(7, 0) +#define CSR_TXPREOVN_LSB 0 +#define CSR_TXPREOVN_MASK GENMASK_32(3, 0) +#define CSR_TXPREOVP_LSB 4 +#define CSR_TXPREOVP_MASK GENMASK_32(7, 4) +/* CSR_PLLOUTGATECONTROL */ +#define CSR_PLLOUTGATECONTROL_LSB 0 +#define CSR_PLLOUTGATECONTROL_MASK GENMASK_32(1, 0) +#define CSR_PCLKGATEEN_LSB 0 +#define CSR_PCLKGATEEN_MASK BIT(0) +#define CSR_RESERVED2X1_LSB 1 +#define CSR_RESERVED2X1_MASK BIT(1) +/* CSR_UCMEMRESETCONTROL */ +#define CSR_UCMEMRESETCONTROL_LSB 0 +#define CSR_UCMEMRESETCONTROL_MASK BIT(0) +#define CSR_UCDCTSANE_LSB 0 +#define CSR_UCDCTSANE_MASK BIT(0) +/* CSR_PORCONTROL */ +#define CSR_PORCONTROL_LSB 0 +#define CSR_PORCONTROL_MASK BIT(0) +#define CSR_PLLDLLLOCKDONE_LSB 0 +#define CSR_PLLDLLLOCKDONE_MASK BIT(0) +/* CSR_CALBUSY */ +#define CSR_CALBUSY_LSB 0 +#define CSR_CALBUSY_MASK BIT(0) +/* CSR_CALMISC2 */ +#define CSR_CALMISC2_LSB 0 +#define CSR_CALMISC2_MASK GENMASK_32(15, 0) +#define CSR_CALNUMVOTES_LSB 0 +#define CSR_CALNUMVOTES_MASK GENMASK_32(2, 0) +#define CSR_RESERVED10X3_LSB 3 +#define CSR_RESERVED10X3_MASK GENMASK_32(10, 3) +#define CSR_RESERVED11_LSB 11 +#define CSR_RESERVED11_MASK BIT(11) +#define CSR_CALCMPTRRESTRIM_LSB 12 +#define CSR_CALCMPTRRESTRIM_MASK BIT(12) +#define CSR_CALCANCELROUNDERRDIS_LSB 13 +#define CSR_CALCANCELROUNDERRDIS_MASK BIT(13) +#define CSR_CALSLOWCMPANA_LSB 14 +#define CSR_CALSLOWCMPANA_MASK BIT(14) +#define CSR_RESERVED15_LSB 15 +#define CSR_RESERVED15_MASK BIT(15) +/* CSR_CALMISC */ +#define CSR_CALMISC_LSB 0 +#define CSR_CALMISC_MASK GENMASK_32(2, 0) +#define CSR_CALCMPR5DIS_LSB 0 +#define CSR_CALCMPR5DIS_MASK BIT(0) +#define CSR_CALNINTDIS_LSB 1 +#define CSR_CALNINTDIS_MASK BIT(1) +#define CSR_CALPEXTDIS_LSB 2 +#define CSR_CALPEXTDIS_MASK BIT(2) +/* CSR_CALVREFS */ +#define CSR_CALVREFS_LSB 0 +#define CSR_CALVREFS_MASK GENMASK_32(1, 0) +/* CSR_CALCMPR5 */ +#define CSR_CALCMPR5_LSB 0 +#define CSR_CALCMPR5_MASK GENMASK_32(7, 0) +/* CSR_CALNINT */ +#define CSR_CALNINT_LSB 0 +#define CSR_CALNINT_MASK GENMASK_32(4, 0) +#define CSR_CALNINTTHB_LSB 0 +#define CSR_CALNINTTHB_MASK GENMASK_32(4, 0) +/* CSR_CALPEXT */ +#define CSR_CALPEXT_LSB 0 +#define CSR_CALPEXT_MASK GENMASK_32(4, 0) +#define CSR_CALPEXTTHB_LSB 0 +#define CSR_CALPEXTTHB_MASK GENMASK_32(4, 0) +/* CSR_CALCMPINVERT */ +#define CSR_CALCMPINVERT_LSB 0 +#define CSR_CALCMPINVERT_MASK GENMASK_32(4, 0) +#define CSR_CMPINVERTCALDAC50_LSB 0 +#define CSR_CMPINVERTCALDAC50_MASK BIT(0) +#define CSR_CMPINVERTCALDRVPD50_LSB 1 +#define CSR_CMPINVERTCALDRVPD50_MASK BIT(1) +#define CSR_CMPINVERTCALDRVPU50_LSB 2 +#define CSR_CMPINVERTCALDRVPU50_MASK BIT(2) +#define CSR_CMPINVERTCALODTPD_LSB 3 +#define CSR_CMPINVERTCALODTPD_MASK BIT(3) +#define CSR_CMPINVERTCALODTPU_LSB 4 +#define CSR_CMPINVERTCALODTPU_MASK BIT(4) +/* CSR_CALCMPANACNTRL */ +#define CSR_CALCMPANACNTRL_LSB 0 +#define CSR_CALCMPANACNTRL_MASK GENMASK_32(9, 0) +#define CSR_CMPRGAINCURRADJ_LSB 0 +#define CSR_CMPRGAINCURRADJ_MASK GENMASK_32(7, 0) +#define CSR_CMPRGAINRESADJ_LSB 8 +#define CSR_CMPRGAINRESADJ_MASK BIT(8) +#define CSR_CMPRBIASBYPASSEN_LSB 9 +#define CSR_CMPRBIASBYPASSEN_MASK BIT(9) +/* CSR_DFIRDDATACSDESTMAP */ +#define CSR_DFIRDDATACSDESTMAP_LSB 0 +#define CSR_DFIRDDATACSDESTMAP_MASK GENMASK_32(7, 0) +#define CSR_DFIRDDESTM0_LSB 0 +#define CSR_DFIRDDESTM0_MASK GENMASK_32(1, 0) +#define CSR_DFIRDDESTM1_LSB 2 +#define CSR_DFIRDDESTM1_MASK GENMASK_32(3, 2) +#define CSR_DFIRDDESTM2_LSB 4 +#define CSR_DFIRDDESTM2_MASK GENMASK_32(5, 4) +#define CSR_DFIRDDESTM3_LSB 6 +#define CSR_DFIRDDESTM3_MASK GENMASK_32(7, 6) +/* CSR_VREFINGLOBAL */ +#define CSR_VREFINGLOBAL_LSB 0 +#define CSR_VREFINGLOBAL_MASK GENMASK_32(14, 0) +#define CSR_GLOBALVREFINSEL_LSB 0 +#define CSR_GLOBALVREFINSEL_MASK GENMASK_32(2, 0) +#define CSR_GLOBALVREFINDAC_LSB 3 +#define CSR_GLOBALVREFINDAC_MASK GENMASK_32(9, 3) +#define CSR_GLOBALVREFINTRIM_LSB 10 +#define CSR_GLOBALVREFINTRIM_MASK GENMASK_32(13, 10) +#define CSR_GLOBALVREFINMODE_LSB 14 +#define CSR_GLOBALVREFINMODE_MASK BIT(14) +/* CSR_DFIWRDATACSDESTMAP */ +#define CSR_DFIWRDATACSDESTMAP_LSB 0 +#define CSR_DFIWRDATACSDESTMAP_MASK GENMASK_32(7, 0) +#define CSR_DFIWRDESTM0_LSB 0 +#define CSR_DFIWRDESTM0_MASK GENMASK_32(1, 0) +#define CSR_DFIWRDESTM1_LSB 2 +#define CSR_DFIWRDESTM1_MASK GENMASK_32(3, 2) +#define CSR_DFIWRDESTM2_LSB 4 +#define CSR_DFIWRDESTM2_MASK GENMASK_32(5, 4) +#define CSR_DFIWRDESTM3_LSB 6 +#define CSR_DFIWRDESTM3_MASK GENMASK_32(7, 6) +/* CSR_MASUPDGOODCTR */ +#define CSR_MASUPDGOODCTR_LSB 0 +#define CSR_MASUPDGOODCTR_MASK GENMASK_32(15, 0) +/* CSR_PHYUPD0GOODCTR */ +#define CSR_PHYUPD0GOODCTR_LSB 0 +#define CSR_PHYUPD0GOODCTR_MASK GENMASK_32(15, 0) +/* CSR_PHYUPD1GOODCTR */ +#define CSR_PHYUPD1GOODCTR_LSB 0 +#define CSR_PHYUPD1GOODCTR_MASK GENMASK_32(15, 0) +/* CSR_CTLUPD0GOODCTR */ +#define CSR_CTLUPD0GOODCTR_LSB 0 +#define CSR_CTLUPD0GOODCTR_MASK GENMASK_32(15, 0) +/* CSR_CTLUPD1GOODCTR */ +#define CSR_CTLUPD1GOODCTR_LSB 0 +#define CSR_CTLUPD1GOODCTR_MASK GENMASK_32(15, 0) +/* CSR_MASUPDFAILCTR */ +#define CSR_MASUPDFAILCTR_LSB 0 +#define CSR_MASUPDFAILCTR_MASK GENMASK_32(15, 0) +/* CSR_PHYUPD0FAILCTR */ +#define CSR_PHYUPD0FAILCTR_LSB 0 +#define CSR_PHYUPD0FAILCTR_MASK GENMASK_32(15, 0) +/* CSR_PHYUPD1FAILCTR */ +#define CSR_PHYUPD1FAILCTR_LSB 0 +#define CSR_PHYUPD1FAILCTR_MASK GENMASK_32(15, 0) +/* CSR_PHYPERFCTRENABLE */ +#define CSR_PHYPERFCTRENABLE_LSB 0 +#define CSR_PHYPERFCTRENABLE_MASK GENMASK_32(7, 0) +#define CSR_MASUPDGOODCTL_LSB 0 +#define CSR_MASUPDGOODCTL_MASK BIT(0) +#define CSR_PHYUPD0GOODCTL_LSB 1 +#define CSR_PHYUPD0GOODCTL_MASK BIT(1) +#define CSR_PHYUPD1GOODCTL_LSB 2 +#define CSR_PHYUPD1GOODCTL_MASK BIT(2) +#define CSR_CTLUPD0GOODCTL_LSB 3 +#define CSR_CTLUPD0GOODCTL_MASK BIT(3) +#define CSR_CTLUPD1GOODCTL_LSB 4 +#define CSR_CTLUPD1GOODCTL_MASK BIT(4) +#define CSR_MASUPDFAILCTL_LSB 5 +#define CSR_MASUPDFAILCTL_MASK BIT(5) +#define CSR_PHYUPD0FAILCTL_LSB 6 +#define CSR_PHYUPD0FAILCTL_MASK BIT(6) +#define CSR_PHYUPD1FAILCTL_LSB 7 +#define CSR_PHYUPD1FAILCTL_MASK BIT(7) +/* CSR_DFIWRRDDATACSCONFIG */ +#define CSR_DFIWRRDDATACSCONFIG_LSB 0 +#define CSR_DFIWRRDDATACSCONFIG_MASK GENMASK_32(1, 0) +#define CSR_DFIWRDATACSPOLARITY_LSB 0 +#define CSR_DFIWRDATACSPOLARITY_MASK BIT(0) +#define CSR_DFIRDDATACSPOLARITY_LSB 1 +#define CSR_DFIRDDATACSPOLARITY_MASK BIT(1) +/* CSR_PLLPWRDN */ +#define CSR_PLLPWRDN_LSB 0 +#define CSR_PLLPWRDN_MASK BIT(0) +/* CSR_PLLRESET */ +#define CSR_PLLRESET_LSB 0 +#define CSR_PLLRESET_MASK BIT(0) +/* CSR_PLLCTRL2 */ +#define CSR_PLLCTRL2_LSB 0 +#define CSR_PLLCTRL2_MASK GENMASK_32(4, 0) +#define CSR_PLLFREQSEL_LSB 0 +#define CSR_PLLFREQSEL_MASK GENMASK_32(4, 0) +/* CSR_PLLCTRL0 */ +#define CSR_PLLCTRL0_LSB 0 +#define CSR_PLLCTRL0_MASK GENMASK_32(15, 0) +#define CSR_PLLSTANDBY_LSB 0 +#define CSR_PLLSTANDBY_MASK BIT(0) +#define CSR_PLLBYPSEL_LSB 1 +#define CSR_PLLBYPSEL_MASK BIT(1) +#define CSR_PLLX2MODE_LSB 2 +#define CSR_PLLX2MODE_MASK BIT(2) +#define CSR_PLLOUTBYPEN_LSB 3 +#define CSR_PLLOUTBYPEN_MASK BIT(3) +#define CSR_PLLPRESET_LSB 4 +#define CSR_PLLPRESET_MASK BIT(4) +#define CSR_PLLBYPASSMODE_LSB 5 +#define CSR_PLLBYPASSMODE_MASK BIT(5) +#define CSR_PLLSELDFIFREQRATIO_LSB 6 +#define CSR_PLLSELDFIFREQRATIO_MASK BIT(6) +#define CSR_PLLSYNCBUSFLUSH_LSB 7 +#define CSR_PLLSYNCBUSFLUSH_MASK BIT(7) +#define CSR_PLLSYNCBUSBYP_LSB 8 +#define CSR_PLLSYNCBUSBYP_MASK BIT(8) +#define CSR_PLLRESERVED10X9_LSB 9 +#define CSR_PLLRESERVED10X9_MASK GENMASK_32(10, 9) +#define CSR_PLLGEARSHIFT_LSB 11 +#define CSR_PLLGEARSHIFT_MASK BIT(11) +#define CSR_PLLLOCKCNTSEL_LSB 12 +#define CSR_PLLLOCKCNTSEL_MASK BIT(12) +#define CSR_PLLLOCKPHSEL_LSB 13 +#define CSR_PLLLOCKPHSEL_MASK GENMASK_32(14, 13) +#define CSR_PLLSPARECTRL0_LSB 15 +#define CSR_PLLSPARECTRL0_MASK BIT(15) +/* CSR_PLLCTRL1 */ +#define CSR_PLLCTRL1_LSB 0 +#define CSR_PLLCTRL1_MASK GENMASK_32(8, 0) +#define CSR_PLLCPINTCTRL_LSB 0 +#define CSR_PLLCPINTCTRL_MASK GENMASK_32(4, 0) +#define CSR_PLLCPPROPCTRL_LSB 5 +#define CSR_PLLCPPROPCTRL_MASK GENMASK_32(8, 5) +/* CSR_PLLTST */ +#define CSR_PLLTST_LSB 0 +#define CSR_PLLTST_MASK GENMASK_32(8, 0) +#define CSR_PLLANATSTEN_LSB 0 +#define CSR_PLLANATSTEN_MASK BIT(0) +#define CSR_PLLANATSTSEL_LSB 1 +#define CSR_PLLANATSTSEL_MASK GENMASK_32(4, 1) +#define CSR_PLLDIGTSTSEL_LSB 5 +#define CSR_PLLDIGTSTSEL_MASK GENMASK_32(8, 5) +/* CSR_PLLLOCKSTATUS */ +#define CSR_PLLLOCKSTATUS_LSB 0 +#define CSR_PLLLOCKSTATUS_MASK BIT(0) +/* CSR_PLLTESTMODE */ +#define CSR_PLLTESTMODE_LSB 0 +#define CSR_PLLTESTMODE_MASK GENMASK_32(15, 0) +/* CSR_PLLCTRL3 */ +#define CSR_PLLCTRL3_LSB 0 +#define CSR_PLLCTRL3_MASK GENMASK_32(15, 0) +#define CSR_PLLSPARE_LSB 0 +#define CSR_PLLSPARE_MASK GENMASK_32(3, 0) +#define CSR_PLLMAXRANGE_LSB 4 +#define CSR_PLLMAXRANGE_MASK GENMASK_32(8, 4) +#define CSR_PLLDACVALIN_LSB 9 +#define CSR_PLLDACVALIN_MASK GENMASK_32(13, 9) +#define CSR_PLLFORCECAL_LSB 14 +#define CSR_PLLFORCECAL_MASK BIT(14) +#define CSR_PLLENCAL_LSB 15 +#define CSR_PLLENCAL_MASK BIT(15) +/* CSR_PLLCTRL4 */ +#define CSR_PLLCTRL4_LSB 0 +#define CSR_PLLCTRL4_MASK GENMASK_32(8, 0) +#define CSR_PLLCPINTGSCTRL_LSB 0 +#define CSR_PLLCPINTGSCTRL_MASK GENMASK_32(4, 0) +#define CSR_PLLCPPROPGSCTRL_LSB 5 +#define CSR_PLLCPPROPGSCTRL_MASK GENMASK_32(8, 5) +/* CSR_PLLENDOFCAL */ +#define CSR_PLLENDOFCAL_LSB 0 +#define CSR_PLLENDOFCAL_MASK BIT(0) +/* CSR_PLLSTANDBYEFF */ +#define CSR_PLLSTANDBYEFF_LSB 0 +#define CSR_PLLSTANDBYEFF_MASK BIT(0) +/* CSR_PLLDACVALOUT */ +#define CSR_PLLDACVALOUT_LSB 0 +#define CSR_PLLDACVALOUT_MASK GENMASK_32(4, 0) +/* CSR_DLYTESTSEQ */ +#define CSR_DLYTESTSEQ_LSB 0 +#define CSR_DLYTESTSEQ_MASK GENMASK_32(5, 0) +#define CSR_DLYTESTEN_LSB 0 +#define CSR_DLYTESTEN_MASK BIT(0) +#define CSR_DLYTESTCNTINIT_LSB 1 +#define CSR_DLYTESTCNTINIT_MASK BIT(1) +#define CSR_DLYTESTENOVERRIDE1_LSB 2 +#define CSR_DLYTESTENOVERRIDE1_MASK BIT(2) +#define CSR_DLYTESTENOVERRIDE2_LSB 3 +#define CSR_DLYTESTENOVERRIDE2_MASK BIT(3) +#define CSR_SYNCDLYMULTIPLIER_LSB 4 +#define CSR_SYNCDLYMULTIPLIER_MASK GENMASK_32(5, 4) +/* CSR_DLYTESTRINGSELDB */ +#define CSR_DLYTESTRINGSELDB_LSB 0 +#define CSR_DLYTESTRINGSELDB_MASK GENMASK_32(4, 0) +#define CSR_DLYTESTCUTDB_LSB 0 +#define CSR_DLYTESTCUTDB_MASK GENMASK_32(4, 0) +/* CSR_DLYTESTRINGSELAC */ +#define CSR_DLYTESTRINGSELAC_LSB 0 +#define CSR_DLYTESTRINGSELAC_MASK GENMASK_32(4, 0) +#define CSR_DLYTESTCUTAC_LSB 0 +#define CSR_DLYTESTCUTAC_MASK GENMASK_32(4, 0) +/* CSR_DLYTESTCNTDFICLKIV */ +#define CSR_DLYTESTCNTDFICLKIV_LSB 0 +#define CSR_DLYTESTCNTDFICLKIV_MASK GENMASK_32(15, 0) +/* CSR_DLYTESTCNTDFICLK */ +#define CSR_DLYTESTCNTDFICLK_LSB 0 +#define CSR_DLYTESTCNTDFICLK_MASK GENMASK_32(15, 0) +/* CSR_DLYTESTCNTRINGOSCDB0 */ +#define CSR_DLYTESTCNTRINGOSCDB0_LSB 0 +#define CSR_DLYTESTCNTRINGOSCDB0_MASK GENMASK_32(15, 0) +/* CSR_DLYTESTCNTRINGOSCDB1 */ +#define CSR_DLYTESTCNTRINGOSCDB1_LSB 0 +#define CSR_DLYTESTCNTRINGOSCDB1_MASK GENMASK_32(15, 0) +/* CSR_DLYTESTCNTRINGOSCDB2 */ +#define CSR_DLYTESTCNTRINGOSCDB2_LSB 0 +#define CSR_DLYTESTCNTRINGOSCDB2_MASK GENMASK_32(15, 0) +/* CSR_DLYTESTCNTRINGOSCDB3 */ +#define CSR_DLYTESTCNTRINGOSCDB3_LSB 0 +#define CSR_DLYTESTCNTRINGOSCDB3_MASK GENMASK_32(15, 0) +/* CSR_DLYTESTCNTRINGOSCDB4 */ +#define CSR_DLYTESTCNTRINGOSCDB4_LSB 0 +#define CSR_DLYTESTCNTRINGOSCDB4_MASK GENMASK_32(15, 0) +/* CSR_DLYTESTCNTRINGOSCDB5 */ +#define CSR_DLYTESTCNTRINGOSCDB5_LSB 0 +#define CSR_DLYTESTCNTRINGOSCDB5_MASK GENMASK_32(15, 0) +/* CSR_DLYTESTCNTRINGOSCDB6 */ +#define CSR_DLYTESTCNTRINGOSCDB6_LSB 0 +#define CSR_DLYTESTCNTRINGOSCDB6_MASK GENMASK_32(15, 0) +/* CSR_DLYTESTCNTRINGOSCDB7 */ +#define CSR_DLYTESTCNTRINGOSCDB7_LSB 0 +#define CSR_DLYTESTCNTRINGOSCDB7_MASK GENMASK_32(15, 0) +/* CSR_DLYTESTCNTRINGOSCDB8 */ +#define CSR_DLYTESTCNTRINGOSCDB8_LSB 0 +#define CSR_DLYTESTCNTRINGOSCDB8_MASK GENMASK_32(15, 0) +/* CSR_DLYTESTCNTRINGOSCDB9 */ +#define CSR_DLYTESTCNTRINGOSCDB9_LSB 0 +#define CSR_DLYTESTCNTRINGOSCDB9_MASK GENMASK_32(15, 0) +/* CSR_DLYTESTCNTRINGOSCAC */ +#define CSR_DLYTESTCNTRINGOSCAC_LSB 0 +#define CSR_DLYTESTCNTRINGOSCAC_MASK GENMASK_32(15, 0) +/* CSR_MSTLCDLDBGCNTL */ +#define CSR_MSTLCDLDBGCNTL_LSB 0 +#define CSR_MSTLCDLDBGCNTL_MASK GENMASK_32(11, 0) +#define CSR_MSTLCDLFINEOVRVAL_LSB 0 +#define CSR_MSTLCDLFINEOVRVAL_MASK GENMASK_32(8, 0) +#define CSR_MSTLCDLFINEOVR_LSB 9 +#define CSR_MSTLCDLFINEOVR_MASK BIT(9) +#define CSR_MSTLCDLFINESNAP_LSB 10 +#define CSR_MSTLCDLFINESNAP_MASK BIT(10) +#define CSR_MSTLCDLTSTENABLE_LSB 11 +#define CSR_MSTLCDLTSTENABLE_MASK BIT(11) +/* CSR_MSTLCDL0DBGRES */ +#define CSR_MSTLCDL0DBGRES_LSB 0 +#define CSR_MSTLCDL0DBGRES_MASK GENMASK_32(12, 0) +#define CSR_MSTLCDL0FINESNAPVAL_LSB 0 +#define CSR_MSTLCDL0FINESNAPVAL_MASK GENMASK_32(8, 0) +#define CSR_MSTLCDL0PHDSNAPVAL_LSB 9 +#define CSR_MSTLCDL0PHDSNAPVAL_MASK BIT(9) +#define CSR_MSTLCDL0STICKYLOCK_LSB 10 +#define CSR_MSTLCDL0STICKYLOCK_MASK BIT(10) +#define CSR_MSTLCDL0STICKYUNLOCK_LSB 11 +#define CSR_MSTLCDL0STICKYUNLOCK_MASK BIT(11) +#define CSR_MSTLCDL0LIVELOCK_LSB 12 +#define CSR_MSTLCDL0LIVELOCK_MASK BIT(12) +/* CSR_MSTLCDL1DBGRES */ +#define CSR_MSTLCDL1DBGRES_LSB 0 +#define CSR_MSTLCDL1DBGRES_MASK GENMASK_32(12, 0) +#define CSR_MSTLCDL1FINESNAPVAL_LSB 0 +#define CSR_MSTLCDL1FINESNAPVAL_MASK GENMASK_32(8, 0) +#define CSR_MSTLCDL1PHDSNAPVAL_LSB 9 +#define CSR_MSTLCDL1PHDSNAPVAL_MASK BIT(9) +#define CSR_MSTLCDL1STICKYLOCK_LSB 10 +#define CSR_MSTLCDL1STICKYLOCK_MASK BIT(10) +#define CSR_MSTLCDL1STICKYUNLOCK_LSB 11 +#define CSR_MSTLCDL1STICKYUNLOCK_MASK BIT(11) +#define CSR_MSTLCDL1LIVELOCK_LSB 12 +#define CSR_MSTLCDL1LIVELOCK_MASK BIT(12) +/* CSR_LCDLDBGCNTL */ +#define CSR_LCDLDBGCNTL_LSB 0 +#define CSR_LCDLDBGCNTL_MASK GENMASK_32(15, 0) +#define CSR_LCDLFINEOVRVAL_LSB 0 +#define CSR_LCDLFINEOVRVAL_MASK GENMASK_32(8, 0) +#define CSR_LCDLFINEOVR_LSB 9 +#define CSR_LCDLFINEOVR_MASK BIT(9) +#define CSR_LCDLFINESNAP_LSB 10 +#define CSR_LCDLFINESNAP_MASK BIT(10) +#define CSR_LCDLTSTENABLE_LSB 11 +#define CSR_LCDLTSTENABLE_MASK BIT(11) +#define CSR_LCDLSTATUSSEL_LSB 12 +#define CSR_LCDLSTATUSSEL_MASK GENMASK_32(15, 12) +/* CSR_ACLCDLSTATUS */ +#define CSR_ACLCDLSTATUS_LSB 0 +#define CSR_ACLCDLSTATUS_MASK GENMASK_32(13, 0) +#define CSR_ACLCDLFINESNAPVAL_LSB 0 +#define CSR_ACLCDLFINESNAPVAL_MASK GENMASK_32(9, 0) +#define CSR_ACLCDLPHDSNAPVAL_LSB 10 +#define CSR_ACLCDLPHDSNAPVAL_MASK BIT(10) +#define CSR_ACLCDLSTICKYLOCK_LSB 11 +#define CSR_ACLCDLSTICKYLOCK_MASK BIT(11) +#define CSR_ACLCDLSTICKYUNLOCK_LSB 12 +#define CSR_ACLCDLSTICKYUNLOCK_MASK BIT(12) +#define CSR_ACLCDLLIVELOCK_LSB 13 +#define CSR_ACLCDLLIVELOCK_MASK BIT(13) +/* CSR_CUSTPHYREV */ +#define CSR_CUSTPHYREV_LSB 0 +#define CSR_CUSTPHYREV_MASK GENMASK_32(5, 0) +/* CSR_PHYREV */ +#define CSR_PHYREV_LSB 0 +#define CSR_PHYREV_MASK GENMASK_32(15, 0) +#define CSR_PHYMNR_LSB 0 +#define CSR_PHYMNR_MASK GENMASK_32(3, 0) +#define CSR_PHYMDR_LSB 4 +#define CSR_PHYMDR_MASK GENMASK_32(7, 4) +#define CSR_PHYMJR_LSB 8 +#define CSR_PHYMJR_MASK GENMASK_32(15, 8) +/* CSR_LP3EXITSEQ0BSTARTVECTOR */ +#define CSR_LP3EXITSEQ0BSTARTVECTOR_LSB 0 +#define CSR_LP3EXITSEQ0BSTARTVECTOR_MASK GENMASK_32(7, 0) +#define CSR_LP3EXITSEQ0BSTARTVECPLLENABLED_LSB 0 +#define CSR_LP3EXITSEQ0BSTARTVECPLLENABLED_MASK GENMASK_32(3, 0) +#define CSR_LP3EXITSEQ0BSTARTVECPLLBYPASSED_LSB 4 +#define CSR_LP3EXITSEQ0BSTARTVECPLLBYPASSED_MASK GENMASK_32(7, 4) +/* CSR_DFIFREQXLAT0 */ +#define CSR_DFIFREQXLAT0_LSB 0 +#define CSR_DFIFREQXLAT0_MASK GENMASK_32(15, 0) +#define CSR_DFIFREQXLATVAL0_LSB 0 +#define CSR_DFIFREQXLATVAL0_MASK GENMASK_32(3, 0) +#define CSR_DFIFREQXLATVAL1_LSB 4 +#define CSR_DFIFREQXLATVAL1_MASK GENMASK_32(7, 4) +#define CSR_DFIFREQXLATVAL2_LSB 8 +#define CSR_DFIFREQXLATVAL2_MASK GENMASK_32(11, 8) +#define CSR_DFIFREQXLATVAL3_LSB 12 +#define CSR_DFIFREQXLATVAL3_MASK GENMASK_32(15, 12) +/* CSR_DFIFREQXLAT1 */ +#define CSR_DFIFREQXLAT1_LSB 0 +#define CSR_DFIFREQXLAT1_MASK GENMASK_32(15, 0) +#define CSR_DFIFREQXLATVAL4_LSB 0 +#define CSR_DFIFREQXLATVAL4_MASK GENMASK_32(3, 0) +#define CSR_DFIFREQXLATVAL5_LSB 4 +#define CSR_DFIFREQXLATVAL5_MASK GENMASK_32(7, 4) +#define CSR_DFIFREQXLATVAL6_LSB 8 +#define CSR_DFIFREQXLATVAL6_MASK GENMASK_32(11, 8) +#define CSR_DFIFREQXLATVAL7_LSB 12 +#define CSR_DFIFREQXLATVAL7_MASK GENMASK_32(15, 12) +/* CSR_DFIFREQXLAT2 */ +#define CSR_DFIFREQXLAT2_LSB 0 +#define CSR_DFIFREQXLAT2_MASK GENMASK_32(15, 0) +#define CSR_DFIFREQXLATVAL8_LSB 0 +#define CSR_DFIFREQXLATVAL8_MASK GENMASK_32(3, 0) +#define CSR_DFIFREQXLATVAL9_LSB 4 +#define CSR_DFIFREQXLATVAL9_MASK GENMASK_32(7, 4) +#define CSR_DFIFREQXLATVAL10_LSB 8 +#define CSR_DFIFREQXLATVAL10_MASK GENMASK_32(11, 8) +#define CSR_DFIFREQXLATVAL11_LSB 12 +#define CSR_DFIFREQXLATVAL11_MASK GENMASK_32(15, 12) +/* CSR_DFIFREQXLAT3 */ +#define CSR_DFIFREQXLAT3_LSB 0 +#define CSR_DFIFREQXLAT3_MASK GENMASK_32(15, 0) +#define CSR_DFIFREQXLATVAL12_LSB 0 +#define CSR_DFIFREQXLATVAL12_MASK GENMASK_32(3, 0) +#define CSR_DFIFREQXLATVAL13_LSB 4 +#define CSR_DFIFREQXLATVAL13_MASK GENMASK_32(7, 4) +#define CSR_DFIFREQXLATVAL14_LSB 8 +#define CSR_DFIFREQXLATVAL14_MASK GENMASK_32(11, 8) +#define CSR_DFIFREQXLATVAL15_LSB 12 +#define CSR_DFIFREQXLATVAL15_MASK GENMASK_32(15, 12) +/* CSR_DFIFREQXLAT4 */ +#define CSR_DFIFREQXLAT4_LSB 0 +#define CSR_DFIFREQXLAT4_MASK GENMASK_32(15, 0) +#define CSR_DFIFREQXLATVAL16_LSB 0 +#define CSR_DFIFREQXLATVAL16_MASK GENMASK_32(3, 0) +#define CSR_DFIFREQXLATVAL17_LSB 4 +#define CSR_DFIFREQXLATVAL17_MASK GENMASK_32(7, 4) +#define CSR_DFIFREQXLATVAL18_LSB 8 +#define CSR_DFIFREQXLATVAL18_MASK GENMASK_32(11, 8) +#define CSR_DFIFREQXLATVAL19_LSB 12 +#define CSR_DFIFREQXLATVAL19_MASK GENMASK_32(15, 12) +/* CSR_DFIFREQXLAT5 */ +#define CSR_DFIFREQXLAT5_LSB 0 +#define CSR_DFIFREQXLAT5_MASK GENMASK_32(15, 0) +#define CSR_DFIFREQXLATVAL20_LSB 0 +#define CSR_DFIFREQXLATVAL20_MASK GENMASK_32(3, 0) +#define CSR_DFIFREQXLATVAL21_LSB 4 +#define CSR_DFIFREQXLATVAL21_MASK GENMASK_32(7, 4) +#define CSR_DFIFREQXLATVAL22_LSB 8 +#define CSR_DFIFREQXLATVAL22_MASK GENMASK_32(11, 8) +#define CSR_DFIFREQXLATVAL23_LSB 12 +#define CSR_DFIFREQXLATVAL23_MASK GENMASK_32(15, 12) +/* CSR_DFIFREQXLAT6 */ +#define CSR_DFIFREQXLAT6_LSB 0 +#define CSR_DFIFREQXLAT6_MASK GENMASK_32(15, 0) +#define CSR_DFIFREQXLATVAL24_LSB 0 +#define CSR_DFIFREQXLATVAL24_MASK GENMASK_32(3, 0) +#define CSR_DFIFREQXLATVAL25_LSB 4 +#define CSR_DFIFREQXLATVAL25_MASK GENMASK_32(7, 4) +#define CSR_DFIFREQXLATVAL26_LSB 8 +#define CSR_DFIFREQXLATVAL26_MASK GENMASK_32(11, 8) +#define CSR_DFIFREQXLATVAL27_LSB 12 +#define CSR_DFIFREQXLATVAL27_MASK GENMASK_32(15, 12) +/* CSR_DFIFREQXLAT7 */ +#define CSR_DFIFREQXLAT7_LSB 0 +#define CSR_DFIFREQXLAT7_MASK GENMASK_32(15, 0) +#define CSR_DFIFREQXLATVAL28_LSB 0 +#define CSR_DFIFREQXLATVAL28_MASK GENMASK_32(3, 0) +#define CSR_DFIFREQXLATVAL29_LSB 4 +#define CSR_DFIFREQXLATVAL29_MASK GENMASK_32(7, 4) +#define CSR_DFIFREQXLATVAL30_LSB 8 +#define CSR_DFIFREQXLATVAL30_MASK GENMASK_32(11, 8) +#define CSR_DFIFREQXLATVAL31_LSB 12 +#define CSR_DFIFREQXLATVAL31_MASK GENMASK_32(15, 12) +/* CSR_TXRDPTRINIT */ +#define CSR_TXRDPTRINIT_LSB 0 +#define CSR_TXRDPTRINIT_MASK BIT(0) +/* CSR_DFIINITCOMPLETE */ +#define CSR_DFIINITCOMPLETE_LSB 0 +#define CSR_DFIINITCOMPLETE_MASK BIT(0) +/* CSR_DFIFREQRATIO */ +#define CSR_DFIFREQRATIO_LSB 0 +#define CSR_DFIFREQRATIO_MASK GENMASK_32(1, 0) +/* CSR_RXFIFOCHECKS */ +#define CSR_RXFIFOCHECKS_LSB 0 +#define CSR_RXFIFOCHECKS_MASK BIT(0) +#define CSR_DOFREQUENTRXFIFOCHECKS_LSB 0 +#define CSR_DOFREQUENTRXFIFOCHECKS_MASK BIT(0) +/* CSR_MTESTDTOCTRL */ +#define CSR_MTESTDTOCTRL_LSB 0 +#define CSR_MTESTDTOCTRL_MASK BIT(0) +#define CSR_MTESTDTOEN_LSB 0 +#define CSR_MTESTDTOEN_MASK BIT(0) +/* CSR_MAPCAA0TODFI */ +#define CSR_MAPCAA0TODFI_LSB 0 +#define CSR_MAPCAA0TODFI_MASK GENMASK_32(3, 0) +/* CSR_MAPCAA1TODFI */ +#define CSR_MAPCAA1TODFI_LSB 0 +#define CSR_MAPCAA1TODFI_MASK GENMASK_32(3, 0) +/* CSR_MAPCAA2TODFI */ +#define CSR_MAPCAA2TODFI_LSB 0 +#define CSR_MAPCAA2TODFI_MASK GENMASK_32(3, 0) +/* CSR_MAPCAA3TODFI */ +#define CSR_MAPCAA3TODFI_LSB 0 +#define CSR_MAPCAA3TODFI_MASK GENMASK_32(3, 0) +/* CSR_MAPCAA4TODFI */ +#define CSR_MAPCAA4TODFI_LSB 0 +#define CSR_MAPCAA4TODFI_MASK GENMASK_32(3, 0) +/* CSR_MAPCAA5TODFI */ +#define CSR_MAPCAA5TODFI_LSB 0 +#define CSR_MAPCAA5TODFI_MASK GENMASK_32(3, 0) +/* CSR_MAPCAA6TODFI */ +#define CSR_MAPCAA6TODFI_LSB 0 +#define CSR_MAPCAA6TODFI_MASK GENMASK_32(3, 0) +/* CSR_MAPCAA7TODFI */ +#define CSR_MAPCAA7TODFI_LSB 0 +#define CSR_MAPCAA7TODFI_MASK GENMASK_32(3, 0) +/* CSR_MAPCAA8TODFI */ +#define CSR_MAPCAA8TODFI_LSB 0 +#define CSR_MAPCAA8TODFI_MASK GENMASK_32(3, 0) +/* CSR_MAPCAA9TODFI */ +#define CSR_MAPCAA9TODFI_LSB 0 +#define CSR_MAPCAA9TODFI_MASK GENMASK_32(3, 0) +/* CSR_MAPCAB0TODFI */ +#define CSR_MAPCAB0TODFI_LSB 0 +#define CSR_MAPCAB0TODFI_MASK GENMASK_32(3, 0) +/* CSR_MAPCAB1TODFI */ +#define CSR_MAPCAB1TODFI_LSB 0 +#define CSR_MAPCAB1TODFI_MASK GENMASK_32(3, 0) +/* CSR_MAPCAB2TODFI */ +#define CSR_MAPCAB2TODFI_LSB 0 +#define CSR_MAPCAB2TODFI_MASK GENMASK_32(3, 0) +/* CSR_MAPCAB3TODFI */ +#define CSR_MAPCAB3TODFI_LSB 0 +#define CSR_MAPCAB3TODFI_MASK GENMASK_32(3, 0) +/* CSR_MAPCAB4TODFI */ +#define CSR_MAPCAB4TODFI_LSB 0 +#define CSR_MAPCAB4TODFI_MASK GENMASK_32(3, 0) +/* CSR_MAPCAB5TODFI */ +#define CSR_MAPCAB5TODFI_LSB 0 +#define CSR_MAPCAB5TODFI_MASK GENMASK_32(3, 0) +/* CSR_MAPCAB6TODFI */ +#define CSR_MAPCAB6TODFI_LSB 0 +#define CSR_MAPCAB6TODFI_MASK GENMASK_32(3, 0) +/* CSR_MAPCAB7TODFI */ +#define CSR_MAPCAB7TODFI_LSB 0 +#define CSR_MAPCAB7TODFI_MASK GENMASK_32(3, 0) +/* CSR_MAPCAB8TODFI */ +#define CSR_MAPCAB8TODFI_LSB 0 +#define CSR_MAPCAB8TODFI_MASK GENMASK_32(3, 0) +/* CSR_MAPCAB9TODFI */ +#define CSR_MAPCAB9TODFI_LSB 0 +#define CSR_MAPCAB9TODFI_MASK GENMASK_32(3, 0) +/* CSR_PHYINTERRUPTENABLE */ +#define CSR_PHYINTERRUPTENABLE_LSB 0 +#define CSR_PHYINTERRUPTENABLE_MASK GENMASK_32(15, 0) +#define CSR_PHYTRNGCMPLTEN_LSB 0 +#define CSR_PHYTRNGCMPLTEN_MASK BIT(0) +#define CSR_PHYINITCMPLTEN_LSB 1 +#define CSR_PHYINITCMPLTEN_MASK BIT(1) +#define CSR_PHYTRNGFAILEN_LSB 2 +#define CSR_PHYTRNGFAILEN_MASK BIT(2) +#define CSR_PHYFWRESERVEDEN_LSB 3 +#define CSR_PHYFWRESERVEDEN_MASK GENMASK_32(7, 3) +#define CSR_PHYVTDRIFTALARMEN_LSB 8 +#define CSR_PHYVTDRIFTALARMEN_MASK GENMASK_32(9, 8) +#define CSR_PHYRXFIFOCHECKEN_LSB 10 +#define CSR_PHYRXFIFOCHECKEN_MASK BIT(10) +#define CSR_PHYHWRESERVEDEN_LSB 11 +#define CSR_PHYHWRESERVEDEN_MASK GENMASK_32(15, 11) +/* CSR_PHYINTERRUPTFWCONTROL */ +#define CSR_PHYINTERRUPTFWCONTROL_LSB 0 +#define CSR_PHYINTERRUPTFWCONTROL_MASK GENMASK_32(7, 0) +#define CSR_PHYTRNGCMPLTFW_LSB 0 +#define CSR_PHYTRNGCMPLTFW_MASK BIT(0) +#define CSR_PHYINITCMPLTFW_LSB 1 +#define CSR_PHYINITCMPLTFW_MASK BIT(1) +#define CSR_PHYTRNGFAILFW_LSB 2 +#define CSR_PHYTRNGFAILFW_MASK BIT(2) +#define CSR_PHYFWRESERVEDFW_LSB 3 +#define CSR_PHYFWRESERVEDFW_MASK GENMASK_32(7, 3) +/* CSR_PHYINTERRUPTMASK */ +#define CSR_PHYINTERRUPTMASK_LSB 0 +#define CSR_PHYINTERRUPTMASK_MASK GENMASK_32(15, 0) +#define CSR_PHYTRNGCMPLTMSK_LSB 0 +#define CSR_PHYTRNGCMPLTMSK_MASK BIT(0) +#define CSR_PHYINITCMPLTMSK_LSB 1 +#define CSR_PHYINITCMPLTMSK_MASK BIT(1) +#define CSR_PHYTRNGFAILMSK_LSB 2 +#define CSR_PHYTRNGFAILMSK_MASK BIT(2) +#define CSR_PHYFWRESERVEDMSK_LSB 3 +#define CSR_PHYFWRESERVEDMSK_MASK GENMASK_32(7, 3) +#define CSR_PHYVTDRIFTALARMMSK_LSB 8 +#define CSR_PHYVTDRIFTALARMMSK_MASK GENMASK_32(9, 8) +#define CSR_PHYRXFIFOCHECKMSK_LSB 10 +#define CSR_PHYRXFIFOCHECKMSK_MASK BIT(10) +#define CSR_PHYHWRESERVEDMSK_LSB 11 +#define CSR_PHYHWRESERVEDMSK_MASK GENMASK_32(15, 11) +/* CSR_PHYINTERRUPTCLEAR */ +#define CSR_PHYINTERRUPTCLEAR_LSB 0 +#define CSR_PHYINTERRUPTCLEAR_MASK GENMASK_32(15, 0) +#define CSR_PHYTRNGCMPLTCLR_LSB 0 +#define CSR_PHYTRNGCMPLTCLR_MASK BIT(0) +#define CSR_PHYINITCMPLTCLR_LSB 1 +#define CSR_PHYINITCMPLTCLR_MASK BIT(1) +#define CSR_PHYTRNGFAILCLR_LSB 2 +#define CSR_PHYTRNGFAILCLR_MASK BIT(2) +#define CSR_PHYFWRESERVEDCLR_LSB 3 +#define CSR_PHYFWRESERVEDCLR_MASK GENMASK_32(7, 3) +#define CSR_PHYVTDRIFTALARMCLR_LSB 8 +#define CSR_PHYVTDRIFTALARMCLR_MASK GENMASK_32(9, 8) +#define CSR_PHYRXFIFOCHECKCLR_LSB 10 +#define CSR_PHYRXFIFOCHECKCLR_MASK BIT(10) +#define CSR_PHYHWRESERVEDCLR_LSB 11 +#define CSR_PHYHWRESERVEDCLR_MASK GENMASK_32(15, 11) +/* CSR_PHYINTERRUPTSTATUS */ +#define CSR_PHYINTERRUPTSTATUS_LSB 0 +#define CSR_PHYINTERRUPTSTATUS_MASK GENMASK_32(15, 0) +#define CSR_PHYTRNGCMPLT_LSB 0 +#define CSR_PHYTRNGCMPLT_MASK BIT(0) +#define CSR_PHYINITCMPLT_LSB 1 +#define CSR_PHYINITCMPLT_MASK BIT(1) +#define CSR_PHYTRNGFAIL_LSB 2 +#define CSR_PHYTRNGFAIL_MASK BIT(2) +#define CSR_PHYFWRESERVED_LSB 3 +#define CSR_PHYFWRESERVED_MASK GENMASK_32(7, 3) +#define CSR_VTDRIFTALARM_LSB 8 +#define CSR_VTDRIFTALARM_MASK GENMASK_32(9, 8) +#define CSR_PHYRXFIFOCHECK_LSB 10 +#define CSR_PHYRXFIFOCHECK_MASK BIT(10) +#define CSR_PHYHWRESERVED_LSB 11 +#define CSR_PHYHWRESERVED_MASK GENMASK_32(15, 11) +/* CSR_HWTSWIZZLEHWTADDRESS0 */ +#define CSR_HWTSWIZZLEHWTADDRESS0_LSB 0 +#define CSR_HWTSWIZZLEHWTADDRESS0_MASK GENMASK_32(4, 0) +/* CSR_HWTSWIZZLEHWTADDRESS1 */ +#define CSR_HWTSWIZZLEHWTADDRESS1_LSB 0 +#define CSR_HWTSWIZZLEHWTADDRESS1_MASK GENMASK_32(4, 0) +/* CSR_HWTSWIZZLEHWTADDRESS2 */ +#define CSR_HWTSWIZZLEHWTADDRESS2_LSB 0 +#define CSR_HWTSWIZZLEHWTADDRESS2_MASK GENMASK_32(4, 0) +/* CSR_HWTSWIZZLEHWTADDRESS3 */ +#define CSR_HWTSWIZZLEHWTADDRESS3_LSB 0 +#define CSR_HWTSWIZZLEHWTADDRESS3_MASK GENMASK_32(4, 0) +/* CSR_HWTSWIZZLEHWTADDRESS4 */ +#define CSR_HWTSWIZZLEHWTADDRESS4_LSB 0 +#define CSR_HWTSWIZZLEHWTADDRESS4_MASK GENMASK_32(4, 0) +/* CSR_HWTSWIZZLEHWTADDRESS5 */ +#define CSR_HWTSWIZZLEHWTADDRESS5_LSB 0 +#define CSR_HWTSWIZZLEHWTADDRESS5_MASK GENMASK_32(4, 0) +/* CSR_HWTSWIZZLEHWTADDRESS6 */ +#define CSR_HWTSWIZZLEHWTADDRESS6_LSB 0 +#define CSR_HWTSWIZZLEHWTADDRESS6_MASK GENMASK_32(4, 0) +/* CSR_HWTSWIZZLEHWTADDRESS7 */ +#define CSR_HWTSWIZZLEHWTADDRESS7_LSB 0 +#define CSR_HWTSWIZZLEHWTADDRESS7_MASK GENMASK_32(4, 0) +/* CSR_HWTSWIZZLEHWTADDRESS8 */ +#define CSR_HWTSWIZZLEHWTADDRESS8_LSB 0 +#define CSR_HWTSWIZZLEHWTADDRESS8_MASK GENMASK_32(4, 0) +/* CSR_HWTSWIZZLEHWTADDRESS9 */ +#define CSR_HWTSWIZZLEHWTADDRESS9_LSB 0 +#define CSR_HWTSWIZZLEHWTADDRESS9_MASK GENMASK_32(4, 0) +/* CSR_HWTSWIZZLEHWTADDRESS10 */ +#define CSR_HWTSWIZZLEHWTADDRESS10_LSB 0 +#define CSR_HWTSWIZZLEHWTADDRESS10_MASK GENMASK_32(4, 0) +/* CSR_HWTSWIZZLEHWTADDRESS11 */ +#define CSR_HWTSWIZZLEHWTADDRESS11_LSB 0 +#define CSR_HWTSWIZZLEHWTADDRESS11_MASK GENMASK_32(4, 0) +/* CSR_HWTSWIZZLEHWTADDRESS12 */ +#define CSR_HWTSWIZZLEHWTADDRESS12_LSB 0 +#define CSR_HWTSWIZZLEHWTADDRESS12_MASK GENMASK_32(4, 0) +/* CSR_HWTSWIZZLEHWTADDRESS13 */ +#define CSR_HWTSWIZZLEHWTADDRESS13_LSB 0 +#define CSR_HWTSWIZZLEHWTADDRESS13_MASK GENMASK_32(4, 0) +/* CSR_HWTSWIZZLEHWTADDRESS14 */ +#define CSR_HWTSWIZZLEHWTADDRESS14_LSB 0 +#define CSR_HWTSWIZZLEHWTADDRESS14_MASK GENMASK_32(4, 0) +/* CSR_HWTSWIZZLEHWTADDRESS15 */ +#define CSR_HWTSWIZZLEHWTADDRESS15_LSB 0 +#define CSR_HWTSWIZZLEHWTADDRESS15_MASK GENMASK_32(4, 0) +/* CSR_HWTSWIZZLEHWTADDRESS17 */ +#define CSR_HWTSWIZZLEHWTADDRESS17_LSB 0 +#define CSR_HWTSWIZZLEHWTADDRESS17_MASK GENMASK_32(4, 0) +/* CSR_HWTSWIZZLEHWTACTN */ +#define CSR_HWTSWIZZLEHWTACTN_LSB 0 +#define CSR_HWTSWIZZLEHWTACTN_MASK GENMASK_32(4, 0) +/* CSR_HWTSWIZZLEHWTBANK0 */ +#define CSR_HWTSWIZZLEHWTBANK0_LSB 0 +#define CSR_HWTSWIZZLEHWTBANK0_MASK GENMASK_32(4, 0) +/* CSR_HWTSWIZZLEHWTBANK1 */ +#define CSR_HWTSWIZZLEHWTBANK1_LSB 0 +#define CSR_HWTSWIZZLEHWTBANK1_MASK GENMASK_32(4, 0) +/* CSR_HWTSWIZZLEHWTBANK2 */ +#define CSR_HWTSWIZZLEHWTBANK2_LSB 0 +#define CSR_HWTSWIZZLEHWTBANK2_MASK GENMASK_32(4, 0) +/* CSR_HWTSWIZZLEHWTBG0 */ +#define CSR_HWTSWIZZLEHWTBG0_LSB 0 +#define CSR_HWTSWIZZLEHWTBG0_MASK GENMASK_32(4, 0) +/* CSR_HWTSWIZZLEHWTBG1 */ +#define CSR_HWTSWIZZLEHWTBG1_LSB 0 +#define CSR_HWTSWIZZLEHWTBG1_MASK GENMASK_32(4, 0) +/* CSR_HWTSWIZZLEHWTCASN */ +#define CSR_HWTSWIZZLEHWTCASN_LSB 0 +#define CSR_HWTSWIZZLEHWTCASN_MASK GENMASK_32(4, 0) +/* CSR_HWTSWIZZLEHWTRASN */ +#define CSR_HWTSWIZZLEHWTRASN_LSB 0 +#define CSR_HWTSWIZZLEHWTRASN_MASK GENMASK_32(4, 0) +/* CSR_HWTSWIZZLEHWTWEN */ +#define CSR_HWTSWIZZLEHWTWEN_LSB 0 +#define CSR_HWTSWIZZLEHWTWEN_MASK GENMASK_32(4, 0) +/* CSR_HWTSWIZZLEHWTPARITYIN */ +#define CSR_HWTSWIZZLEHWTPARITYIN_LSB 0 +#define CSR_HWTSWIZZLEHWTPARITYIN_MASK GENMASK_32(4, 0) +/* CSR_DFIHANDSHAKEDELAYS0 */ +#define CSR_DFIHANDSHAKEDELAYS0_LSB 0 +#define CSR_DFIHANDSHAKEDELAYS0_MASK GENMASK_32(15, 0) +#define CSR_PHYUPDACKDELAY0_LSB 0 +#define CSR_PHYUPDACKDELAY0_MASK GENMASK_32(3, 0) +#define CSR_PHYUPDREQDELAY0_LSB 4 +#define CSR_PHYUPDREQDELAY0_MASK GENMASK_32(7, 4) +#define CSR_CTRLUPDACKDELAY0_LSB 8 +#define CSR_CTRLUPDACKDELAY0_MASK GENMASK_32(11, 8) +#define CSR_CTRLUPDREQDELAY0_LSB 12 +#define CSR_CTRLUPDREQDELAY0_MASK GENMASK_32(15, 12) +/* CSR_DFIHANDSHAKEDELAYS1 */ +#define CSR_DFIHANDSHAKEDELAYS1_LSB 0 +#define CSR_DFIHANDSHAKEDELAYS1_MASK GENMASK_32(15, 0) +#define CSR_PHYUPDACKDELAY1_LSB 0 +#define CSR_PHYUPDACKDELAY1_MASK GENMASK_32(3, 0) +#define CSR_PHYUPDREQDELAY1_LSB 4 +#define CSR_PHYUPDREQDELAY1_MASK GENMASK_32(7, 4) +#define CSR_CTRLUPDACKDELAY1_LSB 8 +#define CSR_CTRLUPDACKDELAY1_MASK GENMASK_32(11, 8) +#define CSR_CTRLUPDREQDELAY1_LSB 12 +#define CSR_CTRLUPDREQDELAY1_MASK GENMASK_32(15, 12) +/* CSR_REMOTEIMPCAL */ +#define CSR_REMOTEIMPCAL_LSB 0 +#define CSR_REMOTEIMPCAL_MASK GENMASK_32(1, 0) +#define CSR_CALIBSLAVE_LSB 0 +#define CSR_CALIBSLAVE_MASK BIT(0) +#define CSR_SLAVECODEUPDATED_LSB 1 +#define CSR_SLAVECODEUPDATED_MASK BIT(1) +/* CSR_ACLOOPBACKCTL */ +#define CSR_ACLOOPBACKCTL_LSB 0 +#define CSR_ACLOOPBACKCTL_MASK GENMASK_32(1, 0) +#define CSR_TERMINATION_LSB 0 +#define CSR_TERMINATION_MASK BIT(0) +#define CSR_NOISECANCEL_LSB 1 +#define CSR_NOISECANCEL_MASK BIT(1) + +/* ACSM0 register offsets */ +/* CSR_ACSMSEQ0X0 */ +#define CSR_ACSMSEQ0X0_LSB 0 +#define CSR_ACSMSEQ0X0_MASK GENMASK_32(15, 0) +#define CSR_ACSMMCLKDLY0_LSB 0 +#define CSR_ACSMMCLKDLY0_MASK GENMASK_32(7, 0) +#define CSR_ACSMDDRWE0_LSB 8 +#define CSR_ACSMDDRWE0_MASK BIT(8) +#define CSR_ACSMDDRCAS0_LSB 9 +#define CSR_ACSMDDRCAS0_MASK BIT(9) +#define CSR_ACSMDDRRAS0_LSB 10 +#define CSR_ACSMDDRRAS0_MASK BIT(10) +#define CSR_ACSMDDRCKESET0_LSB 11 +#define CSR_ACSMDDRCKESET0_MASK BIT(11) +#define CSR_ACSMDDRCKECLR0_LSB 12 +#define CSR_ACSMDDRCKECLR0_MASK BIT(12) +#define CSR_ACSMSEQGATECMD0_LSB 13 +#define CSR_ACSMSEQGATECMD0_MASK BIT(13) +#define CSR_ACSMSEQTERM0_LSB 14 +#define CSR_ACSMSEQTERM0_MASK BIT(14) +#define CSR_ACSMLP3CA30_LSB 15 +#define CSR_ACSMLP3CA30_MASK BIT(15) +/* CSR_ACSMSEQ0X1 */ +#define CSR_ACSMSEQ0X1_LSB 0 +#define CSR_ACSMSEQ0X1_MASK GENMASK_32(15, 0) +#define CSR_ACSMMCLKDLY1_LSB 0 +#define CSR_ACSMMCLKDLY1_MASK GENMASK_32(7, 0) +#define CSR_ACSMDDRWE1_LSB 8 +#define CSR_ACSMDDRWE1_MASK BIT(8) +#define CSR_ACSMDDRCAS1_LSB 9 +#define CSR_ACSMDDRCAS1_MASK BIT(9) +#define CSR_ACSMDDRRAS1_LSB 10 +#define CSR_ACSMDDRRAS1_MASK BIT(10) +#define CSR_ACSMDDRCKESET1_LSB 11 +#define CSR_ACSMDDRCKESET1_MASK BIT(11) +#define CSR_ACSMDDRCKECLR1_LSB 12 +#define CSR_ACSMDDRCKECLR1_MASK BIT(12) +#define CSR_ACSMSEQGATECMD1_LSB 13 +#define CSR_ACSMSEQGATECMD1_MASK BIT(13) +#define CSR_ACSMSEQTERM1_LSB 14 +#define CSR_ACSMSEQTERM1_MASK BIT(14) +#define CSR_ACSMLP3CA31_LSB 15 +#define CSR_ACSMLP3CA31_MASK BIT(15) +/* CSR_ACSMSEQ0X2 */ +#define CSR_ACSMSEQ0X2_LSB 0 +#define CSR_ACSMSEQ0X2_MASK GENMASK_32(15, 0) +#define CSR_ACSMMCLKDLY2_LSB 0 +#define CSR_ACSMMCLKDLY2_MASK GENMASK_32(7, 0) +#define CSR_ACSMDDRWE2_LSB 8 +#define CSR_ACSMDDRWE2_MASK BIT(8) +#define CSR_ACSMDDRCAS2_LSB 9 +#define CSR_ACSMDDRCAS2_MASK BIT(9) +#define CSR_ACSMDDRRAS2_LSB 10 +#define CSR_ACSMDDRRAS2_MASK BIT(10) +#define CSR_ACSMDDRCKESET2_LSB 11 +#define CSR_ACSMDDRCKESET2_MASK BIT(11) +#define CSR_ACSMDDRCKECLR2_LSB 12 +#define CSR_ACSMDDRCKECLR2_MASK BIT(12) +#define CSR_ACSMSEQGATECMD2_LSB 13 +#define CSR_ACSMSEQGATECMD2_MASK BIT(13) +#define CSR_ACSMSEQTERM2_LSB 14 +#define CSR_ACSMSEQTERM2_MASK BIT(14) +#define CSR_ACSMLP3CA32_LSB 15 +#define CSR_ACSMLP3CA32_MASK BIT(15) +/* CSR_ACSMSEQ0X3 */ +#define CSR_ACSMSEQ0X3_LSB 0 +#define CSR_ACSMSEQ0X3_MASK GENMASK_32(15, 0) +#define CSR_ACSMMCLKDLY3_LSB 0 +#define CSR_ACSMMCLKDLY3_MASK GENMASK_32(7, 0) +#define CSR_ACSMDDRWE3_LSB 8 +#define CSR_ACSMDDRWE3_MASK BIT(8) +#define CSR_ACSMDDRCAS3_LSB 9 +#define CSR_ACSMDDRCAS3_MASK BIT(9) +#define CSR_ACSMDDRRAS3_LSB 10 +#define CSR_ACSMDDRRAS3_MASK BIT(10) +#define CSR_ACSMDDRCKESET3_LSB 11 +#define CSR_ACSMDDRCKESET3_MASK BIT(11) +#define CSR_ACSMDDRCKECLR3_LSB 12 +#define CSR_ACSMDDRCKECLR3_MASK BIT(12) +#define CSR_ACSMSEQGATECMD3_LSB 13 +#define CSR_ACSMSEQGATECMD3_MASK BIT(13) +#define CSR_ACSMSEQTERM3_LSB 14 +#define CSR_ACSMSEQTERM3_MASK BIT(14) +#define CSR_ACSMLP3CA33_LSB 15 +#define CSR_ACSMLP3CA33_MASK BIT(15) +/* CSR_ACSMSEQ0X4 */ +#define CSR_ACSMSEQ0X4_LSB 0 +#define CSR_ACSMSEQ0X4_MASK GENMASK_32(15, 0) +#define CSR_ACSMMCLKDLY4_LSB 0 +#define CSR_ACSMMCLKDLY4_MASK GENMASK_32(7, 0) +#define CSR_ACSMDDRWE4_LSB 8 +#define CSR_ACSMDDRWE4_MASK BIT(8) +#define CSR_ACSMDDRCAS4_LSB 9 +#define CSR_ACSMDDRCAS4_MASK BIT(9) +#define CSR_ACSMDDRRAS4_LSB 10 +#define CSR_ACSMDDRRAS4_MASK BIT(10) +#define CSR_ACSMDDRCKESET4_LSB 11 +#define CSR_ACSMDDRCKESET4_MASK BIT(11) +#define CSR_ACSMDDRCKECLR4_LSB 12 +#define CSR_ACSMDDRCKECLR4_MASK BIT(12) +#define CSR_ACSMSEQGATECMD4_LSB 13 +#define CSR_ACSMSEQGATECMD4_MASK BIT(13) +#define CSR_ACSMSEQTERM4_LSB 14 +#define CSR_ACSMSEQTERM4_MASK BIT(14) +#define CSR_ACSMLP3CA34_LSB 15 +#define CSR_ACSMLP3CA34_MASK BIT(15) +/* CSR_ACSMSEQ0X5 */ +#define CSR_ACSMSEQ0X5_LSB 0 +#define CSR_ACSMSEQ0X5_MASK GENMASK_32(15, 0) +#define CSR_ACSMMCLKDLY5_LSB 0 +#define CSR_ACSMMCLKDLY5_MASK GENMASK_32(7, 0) +#define CSR_ACSMDDRWE5_LSB 8 +#define CSR_ACSMDDRWE5_MASK BIT(8) +#define CSR_ACSMDDRCAS5_LSB 9 +#define CSR_ACSMDDRCAS5_MASK BIT(9) +#define CSR_ACSMDDRRAS5_LSB 10 +#define CSR_ACSMDDRRAS5_MASK BIT(10) +#define CSR_ACSMDDRCKESET5_LSB 11 +#define CSR_ACSMDDRCKESET5_MASK BIT(11) +#define CSR_ACSMDDRCKECLR5_LSB 12 +#define CSR_ACSMDDRCKECLR5_MASK BIT(12) +#define CSR_ACSMSEQGATECMD5_LSB 13 +#define CSR_ACSMSEQGATECMD5_MASK BIT(13) +#define CSR_ACSMSEQTERM5_LSB 14 +#define CSR_ACSMSEQTERM5_MASK BIT(14) +#define CSR_ACSMLP3CA35_LSB 15 +#define CSR_ACSMLP3CA35_MASK BIT(15) +/* CSR_ACSMSEQ0X6 */ +#define CSR_ACSMSEQ0X6_LSB 0 +#define CSR_ACSMSEQ0X6_MASK GENMASK_32(15, 0) +#define CSR_ACSMMCLKDLY6_LSB 0 +#define CSR_ACSMMCLKDLY6_MASK GENMASK_32(7, 0) +#define CSR_ACSMDDRWE6_LSB 8 +#define CSR_ACSMDDRWE6_MASK BIT(8) +#define CSR_ACSMDDRCAS6_LSB 9 +#define CSR_ACSMDDRCAS6_MASK BIT(9) +#define CSR_ACSMDDRRAS6_LSB 10 +#define CSR_ACSMDDRRAS6_MASK BIT(10) +#define CSR_ACSMDDRCKESET6_LSB 11 +#define CSR_ACSMDDRCKESET6_MASK BIT(11) +#define CSR_ACSMDDRCKECLR6_LSB 12 +#define CSR_ACSMDDRCKECLR6_MASK BIT(12) +#define CSR_ACSMSEQGATECMD6_LSB 13 +#define CSR_ACSMSEQGATECMD6_MASK BIT(13) +#define CSR_ACSMSEQTERM6_LSB 14 +#define CSR_ACSMSEQTERM6_MASK BIT(14) +#define CSR_ACSMLP3CA36_LSB 15 +#define CSR_ACSMLP3CA36_MASK BIT(15) +/* CSR_ACSMSEQ0X7 */ +#define CSR_ACSMSEQ0X7_LSB 0 +#define CSR_ACSMSEQ0X7_MASK GENMASK_32(15, 0) +#define CSR_ACSMMCLKDLY7_LSB 0 +#define CSR_ACSMMCLKDLY7_MASK GENMASK_32(7, 0) +#define CSR_ACSMDDRWE7_LSB 8 +#define CSR_ACSMDDRWE7_MASK BIT(8) +#define CSR_ACSMDDRCAS7_LSB 9 +#define CSR_ACSMDDRCAS7_MASK BIT(9) +#define CSR_ACSMDDRRAS7_LSB 10 +#define CSR_ACSMDDRRAS7_MASK BIT(10) +#define CSR_ACSMDDRCKESET7_LSB 11 +#define CSR_ACSMDDRCKESET7_MASK BIT(11) +#define CSR_ACSMDDRCKECLR7_LSB 12 +#define CSR_ACSMDDRCKECLR7_MASK BIT(12) +#define CSR_ACSMSEQGATECMD7_LSB 13 +#define CSR_ACSMSEQGATECMD7_MASK BIT(13) +#define CSR_ACSMSEQTERM7_LSB 14 +#define CSR_ACSMSEQTERM7_MASK BIT(14) +#define CSR_ACSMLP3CA37_LSB 15 +#define CSR_ACSMLP3CA37_MASK BIT(15) +/* CSR_ACSMSEQ0X8 */ +#define CSR_ACSMSEQ0X8_LSB 0 +#define CSR_ACSMSEQ0X8_MASK GENMASK_32(15, 0) +#define CSR_ACSMMCLKDLY8_LSB 0 +#define CSR_ACSMMCLKDLY8_MASK GENMASK_32(7, 0) +#define CSR_ACSMDDRWE8_LSB 8 +#define CSR_ACSMDDRWE8_MASK BIT(8) +#define CSR_ACSMDDRCAS8_LSB 9 +#define CSR_ACSMDDRCAS8_MASK BIT(9) +#define CSR_ACSMDDRRAS8_LSB 10 +#define CSR_ACSMDDRRAS8_MASK BIT(10) +#define CSR_ACSMDDRCKESET8_LSB 11 +#define CSR_ACSMDDRCKESET8_MASK BIT(11) +#define CSR_ACSMDDRCKECLR8_LSB 12 +#define CSR_ACSMDDRCKECLR8_MASK BIT(12) +#define CSR_ACSMSEQGATECMD8_LSB 13 +#define CSR_ACSMSEQGATECMD8_MASK BIT(13) +#define CSR_ACSMSEQTERM8_LSB 14 +#define CSR_ACSMSEQTERM8_MASK BIT(14) +#define CSR_ACSMLP3CA38_LSB 15 +#define CSR_ACSMLP3CA38_MASK BIT(15) +/* CSR_ACSMSEQ0X9 */ +#define CSR_ACSMSEQ0X9_LSB 0 +#define CSR_ACSMSEQ0X9_MASK GENMASK_32(15, 0) +#define CSR_ACSMMCLKDLY9_LSB 0 +#define CSR_ACSMMCLKDLY9_MASK GENMASK_32(7, 0) +#define CSR_ACSMDDRWE9_LSB 8 +#define CSR_ACSMDDRWE9_MASK BIT(8) +#define CSR_ACSMDDRCAS9_LSB 9 +#define CSR_ACSMDDRCAS9_MASK BIT(9) +#define CSR_ACSMDDRRAS9_LSB 10 +#define CSR_ACSMDDRRAS9_MASK BIT(10) +#define CSR_ACSMDDRCKESET9_LSB 11 +#define CSR_ACSMDDRCKESET9_MASK BIT(11) +#define CSR_ACSMDDRCKECLR9_LSB 12 +#define CSR_ACSMDDRCKECLR9_MASK BIT(12) +#define CSR_ACSMSEQGATECMD9_LSB 13 +#define CSR_ACSMSEQGATECMD9_MASK BIT(13) +#define CSR_ACSMSEQTERM9_LSB 14 +#define CSR_ACSMSEQTERM9_MASK BIT(14) +#define CSR_ACSMLP3CA39_LSB 15 +#define CSR_ACSMLP3CA39_MASK BIT(15) +/* CSR_ACSMSEQ0X10 */ +#define CSR_ACSMSEQ0X10_LSB 0 +#define CSR_ACSMSEQ0X10_MASK GENMASK_32(15, 0) +#define CSR_ACSMMCLKDLY10_LSB 0 +#define CSR_ACSMMCLKDLY10_MASK GENMASK_32(7, 0) +#define CSR_ACSMDDRWE10_LSB 8 +#define CSR_ACSMDDRWE10_MASK BIT(8) +#define CSR_ACSMDDRCAS10_LSB 9 +#define CSR_ACSMDDRCAS10_MASK BIT(9) +#define CSR_ACSMDDRRAS10_LSB 10 +#define CSR_ACSMDDRRAS10_MASK BIT(10) +#define CSR_ACSMDDRCKESET10_LSB 11 +#define CSR_ACSMDDRCKESET10_MASK BIT(11) +#define CSR_ACSMDDRCKECLR10_LSB 12 +#define CSR_ACSMDDRCKECLR10_MASK BIT(12) +#define CSR_ACSMSEQGATECMD10_LSB 13 +#define CSR_ACSMSEQGATECMD10_MASK BIT(13) +#define CSR_ACSMSEQTERM10_LSB 14 +#define CSR_ACSMSEQTERM10_MASK BIT(14) +#define CSR_ACSMLP3CA310_LSB 15 +#define CSR_ACSMLP3CA310_MASK BIT(15) +/* CSR_ACSMSEQ0X11 */ +#define CSR_ACSMSEQ0X11_LSB 0 +#define CSR_ACSMSEQ0X11_MASK GENMASK_32(15, 0) +#define CSR_ACSMMCLKDLY11_LSB 0 +#define CSR_ACSMMCLKDLY11_MASK GENMASK_32(7, 0) +#define CSR_ACSMDDRWE11_LSB 8 +#define CSR_ACSMDDRWE11_MASK BIT(8) +#define CSR_ACSMDDRCAS11_LSB 9 +#define CSR_ACSMDDRCAS11_MASK BIT(9) +#define CSR_ACSMDDRRAS11_LSB 10 +#define CSR_ACSMDDRRAS11_MASK BIT(10) +#define CSR_ACSMDDRCKESET11_LSB 11 +#define CSR_ACSMDDRCKESET11_MASK BIT(11) +#define CSR_ACSMDDRCKECLR11_LSB 12 +#define CSR_ACSMDDRCKECLR11_MASK BIT(12) +#define CSR_ACSMSEQGATECMD11_LSB 13 +#define CSR_ACSMSEQGATECMD11_MASK BIT(13) +#define CSR_ACSMSEQTERM11_LSB 14 +#define CSR_ACSMSEQTERM11_MASK BIT(14) +#define CSR_ACSMLP3CA311_LSB 15 +#define CSR_ACSMLP3CA311_MASK BIT(15) +/* CSR_ACSMSEQ0X12 */ +#define CSR_ACSMSEQ0X12_LSB 0 +#define CSR_ACSMSEQ0X12_MASK GENMASK_32(15, 0) +#define CSR_ACSMMCLKDLY12_LSB 0 +#define CSR_ACSMMCLKDLY12_MASK GENMASK_32(7, 0) +#define CSR_ACSMDDRWE12_LSB 8 +#define CSR_ACSMDDRWE12_MASK BIT(8) +#define CSR_ACSMDDRCAS12_LSB 9 +#define CSR_ACSMDDRCAS12_MASK BIT(9) +#define CSR_ACSMDDRRAS12_LSB 10 +#define CSR_ACSMDDRRAS12_MASK BIT(10) +#define CSR_ACSMDDRCKESET12_LSB 11 +#define CSR_ACSMDDRCKESET12_MASK BIT(11) +#define CSR_ACSMDDRCKECLR12_LSB 12 +#define CSR_ACSMDDRCKECLR12_MASK BIT(12) +#define CSR_ACSMSEQGATECMD12_LSB 13 +#define CSR_ACSMSEQGATECMD12_MASK BIT(13) +#define CSR_ACSMSEQTERM12_LSB 14 +#define CSR_ACSMSEQTERM12_MASK BIT(14) +#define CSR_ACSMLP3CA312_LSB 15 +#define CSR_ACSMLP3CA312_MASK BIT(15) +/* CSR_ACSMSEQ0X13 */ +#define CSR_ACSMSEQ0X13_LSB 0 +#define CSR_ACSMSEQ0X13_MASK GENMASK_32(15, 0) +#define CSR_ACSMMCLKDLY13_LSB 0 +#define CSR_ACSMMCLKDLY13_MASK GENMASK_32(7, 0) +#define CSR_ACSMDDRWE13_LSB 8 +#define CSR_ACSMDDRWE13_MASK BIT(8) +#define CSR_ACSMDDRCAS13_LSB 9 +#define CSR_ACSMDDRCAS13_MASK BIT(9) +#define CSR_ACSMDDRRAS13_LSB 10 +#define CSR_ACSMDDRRAS13_MASK BIT(10) +#define CSR_ACSMDDRCKESET13_LSB 11 +#define CSR_ACSMDDRCKESET13_MASK BIT(11) +#define CSR_ACSMDDRCKECLR13_LSB 12 +#define CSR_ACSMDDRCKECLR13_MASK BIT(12) +#define CSR_ACSMSEQGATECMD13_LSB 13 +#define CSR_ACSMSEQGATECMD13_MASK BIT(13) +#define CSR_ACSMSEQTERM13_LSB 14 +#define CSR_ACSMSEQTERM13_MASK BIT(14) +#define CSR_ACSMLP3CA313_LSB 15 +#define CSR_ACSMLP3CA313_MASK BIT(15) +/* CSR_ACSMSEQ0X14 */ +#define CSR_ACSMSEQ0X14_LSB 0 +#define CSR_ACSMSEQ0X14_MASK GENMASK_32(15, 0) +#define CSR_ACSMMCLKDLY14_LSB 0 +#define CSR_ACSMMCLKDLY14_MASK GENMASK_32(7, 0) +#define CSR_ACSMDDRWE14_LSB 8 +#define CSR_ACSMDDRWE14_MASK BIT(8) +#define CSR_ACSMDDRCAS14_LSB 9 +#define CSR_ACSMDDRCAS14_MASK BIT(9) +#define CSR_ACSMDDRRAS14_LSB 10 +#define CSR_ACSMDDRRAS14_MASK BIT(10) +#define CSR_ACSMDDRCKESET14_LSB 11 +#define CSR_ACSMDDRCKESET14_MASK BIT(11) +#define CSR_ACSMDDRCKECLR14_LSB 12 +#define CSR_ACSMDDRCKECLR14_MASK BIT(12) +#define CSR_ACSMSEQGATECMD14_LSB 13 +#define CSR_ACSMSEQGATECMD14_MASK BIT(13) +#define CSR_ACSMSEQTERM14_LSB 14 +#define CSR_ACSMSEQTERM14_MASK BIT(14) +#define CSR_ACSMLP3CA314_LSB 15 +#define CSR_ACSMLP3CA314_MASK BIT(15) +/* CSR_ACSMSEQ0X15 */ +#define CSR_ACSMSEQ0X15_LSB 0 +#define CSR_ACSMSEQ0X15_MASK GENMASK_32(15, 0) +#define CSR_ACSMMCLKDLY15_LSB 0 +#define CSR_ACSMMCLKDLY15_MASK GENMASK_32(7, 0) +#define CSR_ACSMDDRWE15_LSB 8 +#define CSR_ACSMDDRWE15_MASK BIT(8) +#define CSR_ACSMDDRCAS15_LSB 9 +#define CSR_ACSMDDRCAS15_MASK BIT(9) +#define CSR_ACSMDDRRAS15_LSB 10 +#define CSR_ACSMDDRRAS15_MASK BIT(10) +#define CSR_ACSMDDRCKESET15_LSB 11 +#define CSR_ACSMDDRCKESET15_MASK BIT(11) +#define CSR_ACSMDDRCKECLR15_LSB 12 +#define CSR_ACSMDDRCKECLR15_MASK BIT(12) +#define CSR_ACSMSEQGATECMD15_LSB 13 +#define CSR_ACSMSEQGATECMD15_MASK BIT(13) +#define CSR_ACSMSEQTERM15_LSB 14 +#define CSR_ACSMSEQTERM15_MASK BIT(14) +#define CSR_ACSMLP3CA315_LSB 15 +#define CSR_ACSMLP3CA315_MASK BIT(15) +/* CSR_ACSMSEQ0X16 */ +#define CSR_ACSMSEQ0X16_LSB 0 +#define CSR_ACSMSEQ0X16_MASK GENMASK_32(15, 0) +#define CSR_ACSMMCLKDLY16_LSB 0 +#define CSR_ACSMMCLKDLY16_MASK GENMASK_32(7, 0) +#define CSR_ACSMDDRWE16_LSB 8 +#define CSR_ACSMDDRWE16_MASK BIT(8) +#define CSR_ACSMDDRCAS16_LSB 9 +#define CSR_ACSMDDRCAS16_MASK BIT(9) +#define CSR_ACSMDDRRAS16_LSB 10 +#define CSR_ACSMDDRRAS16_MASK BIT(10) +#define CSR_ACSMDDRCKESET16_LSB 11 +#define CSR_ACSMDDRCKESET16_MASK BIT(11) +#define CSR_ACSMDDRCKECLR16_LSB 12 +#define CSR_ACSMDDRCKECLR16_MASK BIT(12) +#define CSR_ACSMSEQGATECMD16_LSB 13 +#define CSR_ACSMSEQGATECMD16_MASK BIT(13) +#define CSR_ACSMSEQTERM16_LSB 14 +#define CSR_ACSMSEQTERM16_MASK BIT(14) +#define CSR_ACSMLP3CA316_LSB 15 +#define CSR_ACSMLP3CA316_MASK BIT(15) +/* CSR_ACSMSEQ0X17 */ +#define CSR_ACSMSEQ0X17_LSB 0 +#define CSR_ACSMSEQ0X17_MASK GENMASK_32(15, 0) +#define CSR_ACSMMCLKDLY17_LSB 0 +#define CSR_ACSMMCLKDLY17_MASK GENMASK_32(7, 0) +#define CSR_ACSMDDRWE17_LSB 8 +#define CSR_ACSMDDRWE17_MASK BIT(8) +#define CSR_ACSMDDRCAS17_LSB 9 +#define CSR_ACSMDDRCAS17_MASK BIT(9) +#define CSR_ACSMDDRRAS17_LSB 10 +#define CSR_ACSMDDRRAS17_MASK BIT(10) +#define CSR_ACSMDDRCKESET17_LSB 11 +#define CSR_ACSMDDRCKESET17_MASK BIT(11) +#define CSR_ACSMDDRCKECLR17_LSB 12 +#define CSR_ACSMDDRCKECLR17_MASK BIT(12) +#define CSR_ACSMSEQGATECMD17_LSB 13 +#define CSR_ACSMSEQGATECMD17_MASK BIT(13) +#define CSR_ACSMSEQTERM17_LSB 14 +#define CSR_ACSMSEQTERM17_MASK BIT(14) +#define CSR_ACSMLP3CA317_LSB 15 +#define CSR_ACSMLP3CA317_MASK BIT(15) +/* CSR_ACSMSEQ0X18 */ +#define CSR_ACSMSEQ0X18_LSB 0 +#define CSR_ACSMSEQ0X18_MASK GENMASK_32(15, 0) +#define CSR_ACSMMCLKDLY18_LSB 0 +#define CSR_ACSMMCLKDLY18_MASK GENMASK_32(7, 0) +#define CSR_ACSMDDRWE18_LSB 8 +#define CSR_ACSMDDRWE18_MASK BIT(8) +#define CSR_ACSMDDRCAS18_LSB 9 +#define CSR_ACSMDDRCAS18_MASK BIT(9) +#define CSR_ACSMDDRRAS18_LSB 10 +#define CSR_ACSMDDRRAS18_MASK BIT(10) +#define CSR_ACSMDDRCKESET18_LSB 11 +#define CSR_ACSMDDRCKESET18_MASK BIT(11) +#define CSR_ACSMDDRCKECLR18_LSB 12 +#define CSR_ACSMDDRCKECLR18_MASK BIT(12) +#define CSR_ACSMSEQGATECMD18_LSB 13 +#define CSR_ACSMSEQGATECMD18_MASK BIT(13) +#define CSR_ACSMSEQTERM18_LSB 14 +#define CSR_ACSMSEQTERM18_MASK BIT(14) +#define CSR_ACSMLP3CA318_LSB 15 +#define CSR_ACSMLP3CA318_MASK BIT(15) +/* CSR_ACSMSEQ0X19 */ +#define CSR_ACSMSEQ0X19_LSB 0 +#define CSR_ACSMSEQ0X19_MASK GENMASK_32(15, 0) +#define CSR_ACSMMCLKDLY19_LSB 0 +#define CSR_ACSMMCLKDLY19_MASK GENMASK_32(7, 0) +#define CSR_ACSMDDRWE19_LSB 8 +#define CSR_ACSMDDRWE19_MASK BIT(8) +#define CSR_ACSMDDRCAS19_LSB 9 +#define CSR_ACSMDDRCAS19_MASK BIT(9) +#define CSR_ACSMDDRRAS19_LSB 10 +#define CSR_ACSMDDRRAS19_MASK BIT(10) +#define CSR_ACSMDDRCKESET19_LSB 11 +#define CSR_ACSMDDRCKESET19_MASK BIT(11) +#define CSR_ACSMDDRCKECLR19_LSB 12 +#define CSR_ACSMDDRCKECLR19_MASK BIT(12) +#define CSR_ACSMSEQGATECMD19_LSB 13 +#define CSR_ACSMSEQGATECMD19_MASK BIT(13) +#define CSR_ACSMSEQTERM19_LSB 14 +#define CSR_ACSMSEQTERM19_MASK BIT(14) +#define CSR_ACSMLP3CA319_LSB 15 +#define CSR_ACSMLP3CA319_MASK BIT(15) +/* CSR_ACSMSEQ0X20 */ +#define CSR_ACSMSEQ0X20_LSB 0 +#define CSR_ACSMSEQ0X20_MASK GENMASK_32(15, 0) +#define CSR_ACSMMCLKDLY20_LSB 0 +#define CSR_ACSMMCLKDLY20_MASK GENMASK_32(7, 0) +#define CSR_ACSMDDRWE20_LSB 8 +#define CSR_ACSMDDRWE20_MASK BIT(8) +#define CSR_ACSMDDRCAS20_LSB 9 +#define CSR_ACSMDDRCAS20_MASK BIT(9) +#define CSR_ACSMDDRRAS20_LSB 10 +#define CSR_ACSMDDRRAS20_MASK BIT(10) +#define CSR_ACSMDDRCKESET20_LSB 11 +#define CSR_ACSMDDRCKESET20_MASK BIT(11) +#define CSR_ACSMDDRCKECLR20_LSB 12 +#define CSR_ACSMDDRCKECLR20_MASK BIT(12) +#define CSR_ACSMSEQGATECMD20_LSB 13 +#define CSR_ACSMSEQGATECMD20_MASK BIT(13) +#define CSR_ACSMSEQTERM20_LSB 14 +#define CSR_ACSMSEQTERM20_MASK BIT(14) +#define CSR_ACSMLP3CA320_LSB 15 +#define CSR_ACSMLP3CA320_MASK BIT(15) +/* CSR_ACSMSEQ0X21 */ +#define CSR_ACSMSEQ0X21_LSB 0 +#define CSR_ACSMSEQ0X21_MASK GENMASK_32(15, 0) +#define CSR_ACSMMCLKDLY21_LSB 0 +#define CSR_ACSMMCLKDLY21_MASK GENMASK_32(7, 0) +#define CSR_ACSMDDRWE21_LSB 8 +#define CSR_ACSMDDRWE21_MASK BIT(8) +#define CSR_ACSMDDRCAS21_LSB 9 +#define CSR_ACSMDDRCAS21_MASK BIT(9) +#define CSR_ACSMDDRRAS21_LSB 10 +#define CSR_ACSMDDRRAS21_MASK BIT(10) +#define CSR_ACSMDDRCKESET21_LSB 11 +#define CSR_ACSMDDRCKESET21_MASK BIT(11) +#define CSR_ACSMDDRCKECLR21_LSB 12 +#define CSR_ACSMDDRCKECLR21_MASK BIT(12) +#define CSR_ACSMSEQGATECMD21_LSB 13 +#define CSR_ACSMSEQGATECMD21_MASK BIT(13) +#define CSR_ACSMSEQTERM21_LSB 14 +#define CSR_ACSMSEQTERM21_MASK BIT(14) +#define CSR_ACSMLP3CA321_LSB 15 +#define CSR_ACSMLP3CA321_MASK BIT(15) +/* CSR_ACSMSEQ0X22 */ +#define CSR_ACSMSEQ0X22_LSB 0 +#define CSR_ACSMSEQ0X22_MASK GENMASK_32(15, 0) +#define CSR_ACSMMCLKDLY22_LSB 0 +#define CSR_ACSMMCLKDLY22_MASK GENMASK_32(7, 0) +#define CSR_ACSMDDRWE22_LSB 8 +#define CSR_ACSMDDRWE22_MASK BIT(8) +#define CSR_ACSMDDRCAS22_LSB 9 +#define CSR_ACSMDDRCAS22_MASK BIT(9) +#define CSR_ACSMDDRRAS22_LSB 10 +#define CSR_ACSMDDRRAS22_MASK BIT(10) +#define CSR_ACSMDDRCKESET22_LSB 11 +#define CSR_ACSMDDRCKESET22_MASK BIT(11) +#define CSR_ACSMDDRCKECLR22_LSB 12 +#define CSR_ACSMDDRCKECLR22_MASK BIT(12) +#define CSR_ACSMSEQGATECMD22_LSB 13 +#define CSR_ACSMSEQGATECMD22_MASK BIT(13) +#define CSR_ACSMSEQTERM22_LSB 14 +#define CSR_ACSMSEQTERM22_MASK BIT(14) +#define CSR_ACSMLP3CA322_LSB 15 +#define CSR_ACSMLP3CA322_MASK BIT(15) +/* CSR_ACSMSEQ0X23 */ +#define CSR_ACSMSEQ0X23_LSB 0 +#define CSR_ACSMSEQ0X23_MASK GENMASK_32(15, 0) +#define CSR_ACSMMCLKDLY23_LSB 0 +#define CSR_ACSMMCLKDLY23_MASK GENMASK_32(7, 0) +#define CSR_ACSMDDRWE23_LSB 8 +#define CSR_ACSMDDRWE23_MASK BIT(8) +#define CSR_ACSMDDRCAS23_LSB 9 +#define CSR_ACSMDDRCAS23_MASK BIT(9) +#define CSR_ACSMDDRRAS23_LSB 10 +#define CSR_ACSMDDRRAS23_MASK BIT(10) +#define CSR_ACSMDDRCKESET23_LSB 11 +#define CSR_ACSMDDRCKESET23_MASK BIT(11) +#define CSR_ACSMDDRCKECLR23_LSB 12 +#define CSR_ACSMDDRCKECLR23_MASK BIT(12) +#define CSR_ACSMSEQGATECMD23_LSB 13 +#define CSR_ACSMSEQGATECMD23_MASK BIT(13) +#define CSR_ACSMSEQTERM23_LSB 14 +#define CSR_ACSMSEQTERM23_MASK BIT(14) +#define CSR_ACSMLP3CA323_LSB 15 +#define CSR_ACSMLP3CA323_MASK BIT(15) +/* CSR_ACSMSEQ0X24 */ +#define CSR_ACSMSEQ0X24_LSB 0 +#define CSR_ACSMSEQ0X24_MASK GENMASK_32(15, 0) +#define CSR_ACSMMCLKDLY24_LSB 0 +#define CSR_ACSMMCLKDLY24_MASK GENMASK_32(7, 0) +#define CSR_ACSMDDRWE24_LSB 8 +#define CSR_ACSMDDRWE24_MASK BIT(8) +#define CSR_ACSMDDRCAS24_LSB 9 +#define CSR_ACSMDDRCAS24_MASK BIT(9) +#define CSR_ACSMDDRRAS24_LSB 10 +#define CSR_ACSMDDRRAS24_MASK BIT(10) +#define CSR_ACSMDDRCKESET24_LSB 11 +#define CSR_ACSMDDRCKESET24_MASK BIT(11) +#define CSR_ACSMDDRCKECLR24_LSB 12 +#define CSR_ACSMDDRCKECLR24_MASK BIT(12) +#define CSR_ACSMSEQGATECMD24_LSB 13 +#define CSR_ACSMSEQGATECMD24_MASK BIT(13) +#define CSR_ACSMSEQTERM24_LSB 14 +#define CSR_ACSMSEQTERM24_MASK BIT(14) +#define CSR_ACSMLP3CA324_LSB 15 +#define CSR_ACSMLP3CA324_MASK BIT(15) +/* CSR_ACSMSEQ0X25 */ +#define CSR_ACSMSEQ0X25_LSB 0 +#define CSR_ACSMSEQ0X25_MASK GENMASK_32(15, 0) +#define CSR_ACSMMCLKDLY25_LSB 0 +#define CSR_ACSMMCLKDLY25_MASK GENMASK_32(7, 0) +#define CSR_ACSMDDRWE25_LSB 8 +#define CSR_ACSMDDRWE25_MASK BIT(8) +#define CSR_ACSMDDRCAS25_LSB 9 +#define CSR_ACSMDDRCAS25_MASK BIT(9) +#define CSR_ACSMDDRRAS25_LSB 10 +#define CSR_ACSMDDRRAS25_MASK BIT(10) +#define CSR_ACSMDDRCKESET25_LSB 11 +#define CSR_ACSMDDRCKESET25_MASK BIT(11) +#define CSR_ACSMDDRCKECLR25_LSB 12 +#define CSR_ACSMDDRCKECLR25_MASK BIT(12) +#define CSR_ACSMSEQGATECMD25_LSB 13 +#define CSR_ACSMSEQGATECMD25_MASK BIT(13) +#define CSR_ACSMSEQTERM25_LSB 14 +#define CSR_ACSMSEQTERM25_MASK BIT(14) +#define CSR_ACSMLP3CA325_LSB 15 +#define CSR_ACSMLP3CA325_MASK BIT(15) +/* CSR_ACSMSEQ0X26 */ +#define CSR_ACSMSEQ0X26_LSB 0 +#define CSR_ACSMSEQ0X26_MASK GENMASK_32(15, 0) +#define CSR_ACSMMCLKDLY26_LSB 0 +#define CSR_ACSMMCLKDLY26_MASK GENMASK_32(7, 0) +#define CSR_ACSMDDRWE26_LSB 8 +#define CSR_ACSMDDRWE26_MASK BIT(8) +#define CSR_ACSMDDRCAS26_LSB 9 +#define CSR_ACSMDDRCAS26_MASK BIT(9) +#define CSR_ACSMDDRRAS26_LSB 10 +#define CSR_ACSMDDRRAS26_MASK BIT(10) +#define CSR_ACSMDDRCKESET26_LSB 11 +#define CSR_ACSMDDRCKESET26_MASK BIT(11) +#define CSR_ACSMDDRCKECLR26_LSB 12 +#define CSR_ACSMDDRCKECLR26_MASK BIT(12) +#define CSR_ACSMSEQGATECMD26_LSB 13 +#define CSR_ACSMSEQGATECMD26_MASK BIT(13) +#define CSR_ACSMSEQTERM26_LSB 14 +#define CSR_ACSMSEQTERM26_MASK BIT(14) +#define CSR_ACSMLP3CA326_LSB 15 +#define CSR_ACSMLP3CA326_MASK BIT(15) +/* CSR_ACSMSEQ0X27 */ +#define CSR_ACSMSEQ0X27_LSB 0 +#define CSR_ACSMSEQ0X27_MASK GENMASK_32(15, 0) +#define CSR_ACSMMCLKDLY27_LSB 0 +#define CSR_ACSMMCLKDLY27_MASK GENMASK_32(7, 0) +#define CSR_ACSMDDRWE27_LSB 8 +#define CSR_ACSMDDRWE27_MASK BIT(8) +#define CSR_ACSMDDRCAS27_LSB 9 +#define CSR_ACSMDDRCAS27_MASK BIT(9) +#define CSR_ACSMDDRRAS27_LSB 10 +#define CSR_ACSMDDRRAS27_MASK BIT(10) +#define CSR_ACSMDDRCKESET27_LSB 11 +#define CSR_ACSMDDRCKESET27_MASK BIT(11) +#define CSR_ACSMDDRCKECLR27_LSB 12 +#define CSR_ACSMDDRCKECLR27_MASK BIT(12) +#define CSR_ACSMSEQGATECMD27_LSB 13 +#define CSR_ACSMSEQGATECMD27_MASK BIT(13) +#define CSR_ACSMSEQTERM27_LSB 14 +#define CSR_ACSMSEQTERM27_MASK BIT(14) +#define CSR_ACSMLP3CA327_LSB 15 +#define CSR_ACSMLP3CA327_MASK BIT(15) +/* CSR_ACSMSEQ0X28 */ +#define CSR_ACSMSEQ0X28_LSB 0 +#define CSR_ACSMSEQ0X28_MASK GENMASK_32(15, 0) +#define CSR_ACSMMCLKDLY28_LSB 0 +#define CSR_ACSMMCLKDLY28_MASK GENMASK_32(7, 0) +#define CSR_ACSMDDRWE28_LSB 8 +#define CSR_ACSMDDRWE28_MASK BIT(8) +#define CSR_ACSMDDRCAS28_LSB 9 +#define CSR_ACSMDDRCAS28_MASK BIT(9) +#define CSR_ACSMDDRRAS28_LSB 10 +#define CSR_ACSMDDRRAS28_MASK BIT(10) +#define CSR_ACSMDDRCKESET28_LSB 11 +#define CSR_ACSMDDRCKESET28_MASK BIT(11) +#define CSR_ACSMDDRCKECLR28_LSB 12 +#define CSR_ACSMDDRCKECLR28_MASK BIT(12) +#define CSR_ACSMSEQGATECMD28_LSB 13 +#define CSR_ACSMSEQGATECMD28_MASK BIT(13) +#define CSR_ACSMSEQTERM28_LSB 14 +#define CSR_ACSMSEQTERM28_MASK BIT(14) +#define CSR_ACSMLP3CA328_LSB 15 +#define CSR_ACSMLP3CA328_MASK BIT(15) +/* CSR_ACSMSEQ0X29 */ +#define CSR_ACSMSEQ0X29_LSB 0 +#define CSR_ACSMSEQ0X29_MASK GENMASK_32(15, 0) +#define CSR_ACSMMCLKDLY29_LSB 0 +#define CSR_ACSMMCLKDLY29_MASK GENMASK_32(7, 0) +#define CSR_ACSMDDRWE29_LSB 8 +#define CSR_ACSMDDRWE29_MASK BIT(8) +#define CSR_ACSMDDRCAS29_LSB 9 +#define CSR_ACSMDDRCAS29_MASK BIT(9) +#define CSR_ACSMDDRRAS29_LSB 10 +#define CSR_ACSMDDRRAS29_MASK BIT(10) +#define CSR_ACSMDDRCKESET29_LSB 11 +#define CSR_ACSMDDRCKESET29_MASK BIT(11) +#define CSR_ACSMDDRCKECLR29_LSB 12 +#define CSR_ACSMDDRCKECLR29_MASK BIT(12) +#define CSR_ACSMSEQGATECMD29_LSB 13 +#define CSR_ACSMSEQGATECMD29_MASK BIT(13) +#define CSR_ACSMSEQTERM29_LSB 14 +#define CSR_ACSMSEQTERM29_MASK BIT(14) +#define CSR_ACSMLP3CA329_LSB 15 +#define CSR_ACSMLP3CA329_MASK BIT(15) +/* CSR_ACSMSEQ0X30 */ +#define CSR_ACSMSEQ0X30_LSB 0 +#define CSR_ACSMSEQ0X30_MASK GENMASK_32(15, 0) +#define CSR_ACSMMCLKDLY30_LSB 0 +#define CSR_ACSMMCLKDLY30_MASK GENMASK_32(7, 0) +#define CSR_ACSMDDRWE30_LSB 8 +#define CSR_ACSMDDRWE30_MASK BIT(8) +#define CSR_ACSMDDRCAS30_LSB 9 +#define CSR_ACSMDDRCAS30_MASK BIT(9) +#define CSR_ACSMDDRRAS30_LSB 10 +#define CSR_ACSMDDRRAS30_MASK BIT(10) +#define CSR_ACSMDDRCKESET30_LSB 11 +#define CSR_ACSMDDRCKESET30_MASK BIT(11) +#define CSR_ACSMDDRCKECLR30_LSB 12 +#define CSR_ACSMDDRCKECLR30_MASK BIT(12) +#define CSR_ACSMSEQGATECMD30_LSB 13 +#define CSR_ACSMSEQGATECMD30_MASK BIT(13) +#define CSR_ACSMSEQTERM30_LSB 14 +#define CSR_ACSMSEQTERM30_MASK BIT(14) +#define CSR_ACSMLP3CA330_LSB 15 +#define CSR_ACSMLP3CA330_MASK BIT(15) +/* CSR_ACSMSEQ0X31 */ +#define CSR_ACSMSEQ0X31_LSB 0 +#define CSR_ACSMSEQ0X31_MASK GENMASK_32(15, 0) +#define CSR_ACSMMCLKDLY31_LSB 0 +#define CSR_ACSMMCLKDLY31_MASK GENMASK_32(7, 0) +#define CSR_ACSMDDRWE31_LSB 8 +#define CSR_ACSMDDRWE31_MASK BIT(8) +#define CSR_ACSMDDRCAS31_LSB 9 +#define CSR_ACSMDDRCAS31_MASK BIT(9) +#define CSR_ACSMDDRRAS31_LSB 10 +#define CSR_ACSMDDRRAS31_MASK BIT(10) +#define CSR_ACSMDDRCKESET31_LSB 11 +#define CSR_ACSMDDRCKESET31_MASK BIT(11) +#define CSR_ACSMDDRCKECLR31_LSB 12 +#define CSR_ACSMDDRCKECLR31_MASK BIT(12) +#define CSR_ACSMSEQGATECMD31_LSB 13 +#define CSR_ACSMSEQGATECMD31_MASK BIT(13) +#define CSR_ACSMSEQTERM31_LSB 14 +#define CSR_ACSMSEQTERM31_MASK BIT(14) +#define CSR_ACSMLP3CA331_LSB 15 +#define CSR_ACSMLP3CA331_MASK BIT(15) +/* CSR_ACSMSEQ1X0 */ +#define CSR_ACSMSEQ1X0_LSB 0 +#define CSR_ACSMSEQ1X0_MASK GENMASK_32(15, 0) +#define CSR_ACSMDDRCS0_LSB 0 +#define CSR_ACSMDDRCS0_MASK GENMASK_32(7, 0) +#define CSR_ACSMSAVEGEN0_LSB 8 +#define CSR_ACSMSAVEGEN0_MASK BIT(8) +#define CSR_ACSMLOADCHK0_LSB 9 +#define CSR_ACSMLOADCHK0_MASK BIT(9) +#define CSR_ACSMNORXENB0_LSB 10 +#define CSR_ACSMNORXENB0_MASK BIT(10) +#define CSR_ACSMNORXVAL0_LSB 11 +#define CSR_ACSMNORXVAL0_MASK BIT(11) +#define CSR_ACSMDDRBNK0_LSB 12 +#define CSR_ACSMDDRBNK0_MASK GENMASK_32(15, 12) +/* CSR_ACSMSEQ1X1 */ +#define CSR_ACSMSEQ1X1_LSB 0 +#define CSR_ACSMSEQ1X1_MASK GENMASK_32(15, 0) +#define CSR_ACSMDDRCS1_LSB 0 +#define CSR_ACSMDDRCS1_MASK GENMASK_32(7, 0) +#define CSR_ACSMSAVEGEN1_LSB 8 +#define CSR_ACSMSAVEGEN1_MASK BIT(8) +#define CSR_ACSMLOADCHK1_LSB 9 +#define CSR_ACSMLOADCHK1_MASK BIT(9) +#define CSR_ACSMNORXENB1_LSB 10 +#define CSR_ACSMNORXENB1_MASK BIT(10) +#define CSR_ACSMNORXVAL1_LSB 11 +#define CSR_ACSMNORXVAL1_MASK BIT(11) +#define CSR_ACSMDDRBNK1_LSB 12 +#define CSR_ACSMDDRBNK1_MASK GENMASK_32(15, 12) +/* CSR_ACSMSEQ1X2 */ +#define CSR_ACSMSEQ1X2_LSB 0 +#define CSR_ACSMSEQ1X2_MASK GENMASK_32(15, 0) +#define CSR_ACSMDDRCS2_LSB 0 +#define CSR_ACSMDDRCS2_MASK GENMASK_32(7, 0) +#define CSR_ACSMSAVEGEN2_LSB 8 +#define CSR_ACSMSAVEGEN2_MASK BIT(8) +#define CSR_ACSMLOADCHK2_LSB 9 +#define CSR_ACSMLOADCHK2_MASK BIT(9) +#define CSR_ACSMNORXENB2_LSB 10 +#define CSR_ACSMNORXENB2_MASK BIT(10) +#define CSR_ACSMNORXVAL2_LSB 11 +#define CSR_ACSMNORXVAL2_MASK BIT(11) +#define CSR_ACSMDDRBNK2_LSB 12 +#define CSR_ACSMDDRBNK2_MASK GENMASK_32(15, 12) +/* CSR_ACSMSEQ1X3 */ +#define CSR_ACSMSEQ1X3_LSB 0 +#define CSR_ACSMSEQ1X3_MASK GENMASK_32(15, 0) +#define CSR_ACSMDDRCS3_LSB 0 +#define CSR_ACSMDDRCS3_MASK GENMASK_32(7, 0) +#define CSR_ACSMSAVEGEN3_LSB 8 +#define CSR_ACSMSAVEGEN3_MASK BIT(8) +#define CSR_ACSMLOADCHK3_LSB 9 +#define CSR_ACSMLOADCHK3_MASK BIT(9) +#define CSR_ACSMNORXENB3_LSB 10 +#define CSR_ACSMNORXENB3_MASK BIT(10) +#define CSR_ACSMNORXVAL3_LSB 11 +#define CSR_ACSMNORXVAL3_MASK BIT(11) +#define CSR_ACSMDDRBNK3_LSB 12 +#define CSR_ACSMDDRBNK3_MASK GENMASK_32(15, 12) +/* CSR_ACSMSEQ1X4 */ +#define CSR_ACSMSEQ1X4_LSB 0 +#define CSR_ACSMSEQ1X4_MASK GENMASK_32(15, 0) +#define CSR_ACSMDDRCS4_LSB 0 +#define CSR_ACSMDDRCS4_MASK GENMASK_32(7, 0) +#define CSR_ACSMSAVEGEN4_LSB 8 +#define CSR_ACSMSAVEGEN4_MASK BIT(8) +#define CSR_ACSMLOADCHK4_LSB 9 +#define CSR_ACSMLOADCHK4_MASK BIT(9) +#define CSR_ACSMNORXENB4_LSB 10 +#define CSR_ACSMNORXENB4_MASK BIT(10) +#define CSR_ACSMNORXVAL4_LSB 11 +#define CSR_ACSMNORXVAL4_MASK BIT(11) +#define CSR_ACSMDDRBNK4_LSB 12 +#define CSR_ACSMDDRBNK4_MASK GENMASK_32(15, 12) +/* CSR_ACSMSEQ1X5 */ +#define CSR_ACSMSEQ1X5_LSB 0 +#define CSR_ACSMSEQ1X5_MASK GENMASK_32(15, 0) +#define CSR_ACSMDDRCS5_LSB 0 +#define CSR_ACSMDDRCS5_MASK GENMASK_32(7, 0) +#define CSR_ACSMSAVEGEN5_LSB 8 +#define CSR_ACSMSAVEGEN5_MASK BIT(8) +#define CSR_ACSMLOADCHK5_LSB 9 +#define CSR_ACSMLOADCHK5_MASK BIT(9) +#define CSR_ACSMNORXENB5_LSB 10 +#define CSR_ACSMNORXENB5_MASK BIT(10) +#define CSR_ACSMNORXVAL5_LSB 11 +#define CSR_ACSMNORXVAL5_MASK BIT(11) +#define CSR_ACSMDDRBNK5_LSB 12 +#define CSR_ACSMDDRBNK5_MASK GENMASK_32(15, 12) +/* CSR_ACSMSEQ1X6 */ +#define CSR_ACSMSEQ1X6_LSB 0 +#define CSR_ACSMSEQ1X6_MASK GENMASK_32(15, 0) +#define CSR_ACSMDDRCS6_LSB 0 +#define CSR_ACSMDDRCS6_MASK GENMASK_32(7, 0) +#define CSR_ACSMSAVEGEN6_LSB 8 +#define CSR_ACSMSAVEGEN6_MASK BIT(8) +#define CSR_ACSMLOADCHK6_LSB 9 +#define CSR_ACSMLOADCHK6_MASK BIT(9) +#define CSR_ACSMNORXENB6_LSB 10 +#define CSR_ACSMNORXENB6_MASK BIT(10) +#define CSR_ACSMNORXVAL6_LSB 11 +#define CSR_ACSMNORXVAL6_MASK BIT(11) +#define CSR_ACSMDDRBNK6_LSB 12 +#define CSR_ACSMDDRBNK6_MASK GENMASK_32(15, 12) +/* CSR_ACSMSEQ1X7 */ +#define CSR_ACSMSEQ1X7_LSB 0 +#define CSR_ACSMSEQ1X7_MASK GENMASK_32(15, 0) +#define CSR_ACSMDDRCS7_LSB 0 +#define CSR_ACSMDDRCS7_MASK GENMASK_32(7, 0) +#define CSR_ACSMSAVEGEN7_LSB 8 +#define CSR_ACSMSAVEGEN7_MASK BIT(8) +#define CSR_ACSMLOADCHK7_LSB 9 +#define CSR_ACSMLOADCHK7_MASK BIT(9) +#define CSR_ACSMNORXENB7_LSB 10 +#define CSR_ACSMNORXENB7_MASK BIT(10) +#define CSR_ACSMNORXVAL7_LSB 11 +#define CSR_ACSMNORXVAL7_MASK BIT(11) +#define CSR_ACSMDDRBNK7_LSB 12 +#define CSR_ACSMDDRBNK7_MASK GENMASK_32(15, 12) +/* CSR_ACSMSEQ1X8 */ +#define CSR_ACSMSEQ1X8_LSB 0 +#define CSR_ACSMSEQ1X8_MASK GENMASK_32(15, 0) +#define CSR_ACSMDDRCS8_LSB 0 +#define CSR_ACSMDDRCS8_MASK GENMASK_32(7, 0) +#define CSR_ACSMSAVEGEN8_LSB 8 +#define CSR_ACSMSAVEGEN8_MASK BIT(8) +#define CSR_ACSMLOADCHK8_LSB 9 +#define CSR_ACSMLOADCHK8_MASK BIT(9) +#define CSR_ACSMNORXENB8_LSB 10 +#define CSR_ACSMNORXENB8_MASK BIT(10) +#define CSR_ACSMNORXVAL8_LSB 11 +#define CSR_ACSMNORXVAL8_MASK BIT(11) +#define CSR_ACSMDDRBNK8_LSB 12 +#define CSR_ACSMDDRBNK8_MASK GENMASK_32(15, 12) +/* CSR_ACSMSEQ1X9 */ +#define CSR_ACSMSEQ1X9_LSB 0 +#define CSR_ACSMSEQ1X9_MASK GENMASK_32(15, 0) +#define CSR_ACSMDDRCS9_LSB 0 +#define CSR_ACSMDDRCS9_MASK GENMASK_32(7, 0) +#define CSR_ACSMSAVEGEN9_LSB 8 +#define CSR_ACSMSAVEGEN9_MASK BIT(8) +#define CSR_ACSMLOADCHK9_LSB 9 +#define CSR_ACSMLOADCHK9_MASK BIT(9) +#define CSR_ACSMNORXENB9_LSB 10 +#define CSR_ACSMNORXENB9_MASK BIT(10) +#define CSR_ACSMNORXVAL9_LSB 11 +#define CSR_ACSMNORXVAL9_MASK BIT(11) +#define CSR_ACSMDDRBNK9_LSB 12 +#define CSR_ACSMDDRBNK9_MASK GENMASK_32(15, 12) +/* CSR_ACSMSEQ1X10 */ +#define CSR_ACSMSEQ1X10_LSB 0 +#define CSR_ACSMSEQ1X10_MASK GENMASK_32(15, 0) +#define CSR_ACSMDDRCS10_LSB 0 +#define CSR_ACSMDDRCS10_MASK GENMASK_32(7, 0) +#define CSR_ACSMSAVEGEN10_LSB 8 +#define CSR_ACSMSAVEGEN10_MASK BIT(8) +#define CSR_ACSMLOADCHK10_LSB 9 +#define CSR_ACSMLOADCHK10_MASK BIT(9) +#define CSR_ACSMNORXENB10_LSB 10 +#define CSR_ACSMNORXENB10_MASK BIT(10) +#define CSR_ACSMNORXVAL10_LSB 11 +#define CSR_ACSMNORXVAL10_MASK BIT(11) +#define CSR_ACSMDDRBNK10_LSB 12 +#define CSR_ACSMDDRBNK10_MASK GENMASK_32(15, 12) +/* CSR_ACSMSEQ1X11 */ +#define CSR_ACSMSEQ1X11_LSB 0 +#define CSR_ACSMSEQ1X11_MASK GENMASK_32(15, 0) +#define CSR_ACSMDDRCS11_LSB 0 +#define CSR_ACSMDDRCS11_MASK GENMASK_32(7, 0) +#define CSR_ACSMSAVEGEN11_LSB 8 +#define CSR_ACSMSAVEGEN11_MASK BIT(8) +#define CSR_ACSMLOADCHK11_LSB 9 +#define CSR_ACSMLOADCHK11_MASK BIT(9) +#define CSR_ACSMNORXENB11_LSB 10 +#define CSR_ACSMNORXENB11_MASK BIT(10) +#define CSR_ACSMNORXVAL11_LSB 11 +#define CSR_ACSMNORXVAL11_MASK BIT(11) +#define CSR_ACSMDDRBNK11_LSB 12 +#define CSR_ACSMDDRBNK11_MASK GENMASK_32(15, 12) +/* CSR_ACSMSEQ1X12 */ +#define CSR_ACSMSEQ1X12_LSB 0 +#define CSR_ACSMSEQ1X12_MASK GENMASK_32(15, 0) +#define CSR_ACSMDDRCS12_LSB 0 +#define CSR_ACSMDDRCS12_MASK GENMASK_32(7, 0) +#define CSR_ACSMSAVEGEN12_LSB 8 +#define CSR_ACSMSAVEGEN12_MASK BIT(8) +#define CSR_ACSMLOADCHK12_LSB 9 +#define CSR_ACSMLOADCHK12_MASK BIT(9) +#define CSR_ACSMNORXENB12_LSB 10 +#define CSR_ACSMNORXENB12_MASK BIT(10) +#define CSR_ACSMNORXVAL12_LSB 11 +#define CSR_ACSMNORXVAL12_MASK BIT(11) +#define CSR_ACSMDDRBNK12_LSB 12 +#define CSR_ACSMDDRBNK12_MASK GENMASK_32(15, 12) +/* CSR_ACSMSEQ1X13 */ +#define CSR_ACSMSEQ1X13_LSB 0 +#define CSR_ACSMSEQ1X13_MASK GENMASK_32(15, 0) +#define CSR_ACSMDDRCS13_LSB 0 +#define CSR_ACSMDDRCS13_MASK GENMASK_32(7, 0) +#define CSR_ACSMSAVEGEN13_LSB 8 +#define CSR_ACSMSAVEGEN13_MASK BIT(8) +#define CSR_ACSMLOADCHK13_LSB 9 +#define CSR_ACSMLOADCHK13_MASK BIT(9) +#define CSR_ACSMNORXENB13_LSB 10 +#define CSR_ACSMNORXENB13_MASK BIT(10) +#define CSR_ACSMNORXVAL13_LSB 11 +#define CSR_ACSMNORXVAL13_MASK BIT(11) +#define CSR_ACSMDDRBNK13_LSB 12 +#define CSR_ACSMDDRBNK13_MASK GENMASK_32(15, 12) +/* CSR_ACSMSEQ1X14 */ +#define CSR_ACSMSEQ1X14_LSB 0 +#define CSR_ACSMSEQ1X14_MASK GENMASK_32(15, 0) +#define CSR_ACSMDDRCS14_LSB 0 +#define CSR_ACSMDDRCS14_MASK GENMASK_32(7, 0) +#define CSR_ACSMSAVEGEN14_LSB 8 +#define CSR_ACSMSAVEGEN14_MASK BIT(8) +#define CSR_ACSMLOADCHK14_LSB 9 +#define CSR_ACSMLOADCHK14_MASK BIT(9) +#define CSR_ACSMNORXENB14_LSB 10 +#define CSR_ACSMNORXENB14_MASK BIT(10) +#define CSR_ACSMNORXVAL14_LSB 11 +#define CSR_ACSMNORXVAL14_MASK BIT(11) +#define CSR_ACSMDDRBNK14_LSB 12 +#define CSR_ACSMDDRBNK14_MASK GENMASK_32(15, 12) +/* CSR_ACSMSEQ1X15 */ +#define CSR_ACSMSEQ1X15_LSB 0 +#define CSR_ACSMSEQ1X15_MASK GENMASK_32(15, 0) +#define CSR_ACSMDDRCS15_LSB 0 +#define CSR_ACSMDDRCS15_MASK GENMASK_32(7, 0) +#define CSR_ACSMSAVEGEN15_LSB 8 +#define CSR_ACSMSAVEGEN15_MASK BIT(8) +#define CSR_ACSMLOADCHK15_LSB 9 +#define CSR_ACSMLOADCHK15_MASK BIT(9) +#define CSR_ACSMNORXENB15_LSB 10 +#define CSR_ACSMNORXENB15_MASK BIT(10) +#define CSR_ACSMNORXVAL15_LSB 11 +#define CSR_ACSMNORXVAL15_MASK BIT(11) +#define CSR_ACSMDDRBNK15_LSB 12 +#define CSR_ACSMDDRBNK15_MASK GENMASK_32(15, 12) +/* CSR_ACSMSEQ1X16 */ +#define CSR_ACSMSEQ1X16_LSB 0 +#define CSR_ACSMSEQ1X16_MASK GENMASK_32(15, 0) +#define CSR_ACSMDDRCS16_LSB 0 +#define CSR_ACSMDDRCS16_MASK GENMASK_32(7, 0) +#define CSR_ACSMSAVEGEN16_LSB 8 +#define CSR_ACSMSAVEGEN16_MASK BIT(8) +#define CSR_ACSMLOADCHK16_LSB 9 +#define CSR_ACSMLOADCHK16_MASK BIT(9) +#define CSR_ACSMNORXENB16_LSB 10 +#define CSR_ACSMNORXENB16_MASK BIT(10) +#define CSR_ACSMNORXVAL16_LSB 11 +#define CSR_ACSMNORXVAL16_MASK BIT(11) +#define CSR_ACSMDDRBNK16_LSB 12 +#define CSR_ACSMDDRBNK16_MASK GENMASK_32(15, 12) +/* CSR_ACSMSEQ1X17 */ +#define CSR_ACSMSEQ1X17_LSB 0 +#define CSR_ACSMSEQ1X17_MASK GENMASK_32(15, 0) +#define CSR_ACSMDDRCS17_LSB 0 +#define CSR_ACSMDDRCS17_MASK GENMASK_32(7, 0) +#define CSR_ACSMSAVEGEN17_LSB 8 +#define CSR_ACSMSAVEGEN17_MASK BIT(8) +#define CSR_ACSMLOADCHK17_LSB 9 +#define CSR_ACSMLOADCHK17_MASK BIT(9) +#define CSR_ACSMNORXENB17_LSB 10 +#define CSR_ACSMNORXENB17_MASK BIT(10) +#define CSR_ACSMNORXVAL17_LSB 11 +#define CSR_ACSMNORXVAL17_MASK BIT(11) +#define CSR_ACSMDDRBNK17_LSB 12 +#define CSR_ACSMDDRBNK17_MASK GENMASK_32(15, 12) +/* CSR_ACSMSEQ1X18 */ +#define CSR_ACSMSEQ1X18_LSB 0 +#define CSR_ACSMSEQ1X18_MASK GENMASK_32(15, 0) +#define CSR_ACSMDDRCS18_LSB 0 +#define CSR_ACSMDDRCS18_MASK GENMASK_32(7, 0) +#define CSR_ACSMSAVEGEN18_LSB 8 +#define CSR_ACSMSAVEGEN18_MASK BIT(8) +#define CSR_ACSMLOADCHK18_LSB 9 +#define CSR_ACSMLOADCHK18_MASK BIT(9) +#define CSR_ACSMNORXENB18_LSB 10 +#define CSR_ACSMNORXENB18_MASK BIT(10) +#define CSR_ACSMNORXVAL18_LSB 11 +#define CSR_ACSMNORXVAL18_MASK BIT(11) +#define CSR_ACSMDDRBNK18_LSB 12 +#define CSR_ACSMDDRBNK18_MASK GENMASK_32(15, 12) +/* CSR_ACSMSEQ1X19 */ +#define CSR_ACSMSEQ1X19_LSB 0 +#define CSR_ACSMSEQ1X19_MASK GENMASK_32(15, 0) +#define CSR_ACSMDDRCS19_LSB 0 +#define CSR_ACSMDDRCS19_MASK GENMASK_32(7, 0) +#define CSR_ACSMSAVEGEN19_LSB 8 +#define CSR_ACSMSAVEGEN19_MASK BIT(8) +#define CSR_ACSMLOADCHK19_LSB 9 +#define CSR_ACSMLOADCHK19_MASK BIT(9) +#define CSR_ACSMNORXENB19_LSB 10 +#define CSR_ACSMNORXENB19_MASK BIT(10) +#define CSR_ACSMNORXVAL19_LSB 11 +#define CSR_ACSMNORXVAL19_MASK BIT(11) +#define CSR_ACSMDDRBNK19_LSB 12 +#define CSR_ACSMDDRBNK19_MASK GENMASK_32(15, 12) +/* CSR_ACSMSEQ1X20 */ +#define CSR_ACSMSEQ1X20_LSB 0 +#define CSR_ACSMSEQ1X20_MASK GENMASK_32(15, 0) +#define CSR_ACSMDDRCS20_LSB 0 +#define CSR_ACSMDDRCS20_MASK GENMASK_32(7, 0) +#define CSR_ACSMSAVEGEN20_LSB 8 +#define CSR_ACSMSAVEGEN20_MASK BIT(8) +#define CSR_ACSMLOADCHK20_LSB 9 +#define CSR_ACSMLOADCHK20_MASK BIT(9) +#define CSR_ACSMNORXENB20_LSB 10 +#define CSR_ACSMNORXENB20_MASK BIT(10) +#define CSR_ACSMNORXVAL20_LSB 11 +#define CSR_ACSMNORXVAL20_MASK BIT(11) +#define CSR_ACSMDDRBNK20_LSB 12 +#define CSR_ACSMDDRBNK20_MASK GENMASK_32(15, 12) +/* CSR_ACSMSEQ1X21 */ +#define CSR_ACSMSEQ1X21_LSB 0 +#define CSR_ACSMSEQ1X21_MASK GENMASK_32(15, 0) +#define CSR_ACSMDDRCS21_LSB 0 +#define CSR_ACSMDDRCS21_MASK GENMASK_32(7, 0) +#define CSR_ACSMSAVEGEN21_LSB 8 +#define CSR_ACSMSAVEGEN21_MASK BIT(8) +#define CSR_ACSMLOADCHK21_LSB 9 +#define CSR_ACSMLOADCHK21_MASK BIT(9) +#define CSR_ACSMNORXENB21_LSB 10 +#define CSR_ACSMNORXENB21_MASK BIT(10) +#define CSR_ACSMNORXVAL21_LSB 11 +#define CSR_ACSMNORXVAL21_MASK BIT(11) +#define CSR_ACSMDDRBNK21_LSB 12 +#define CSR_ACSMDDRBNK21_MASK GENMASK_32(15, 12) +/* CSR_ACSMSEQ1X22 */ +#define CSR_ACSMSEQ1X22_LSB 0 +#define CSR_ACSMSEQ1X22_MASK GENMASK_32(15, 0) +#define CSR_ACSMDDRCS22_LSB 0 +#define CSR_ACSMDDRCS22_MASK GENMASK_32(7, 0) +#define CSR_ACSMSAVEGEN22_LSB 8 +#define CSR_ACSMSAVEGEN22_MASK BIT(8) +#define CSR_ACSMLOADCHK22_LSB 9 +#define CSR_ACSMLOADCHK22_MASK BIT(9) +#define CSR_ACSMNORXENB22_LSB 10 +#define CSR_ACSMNORXENB22_MASK BIT(10) +#define CSR_ACSMNORXVAL22_LSB 11 +#define CSR_ACSMNORXVAL22_MASK BIT(11) +#define CSR_ACSMDDRBNK22_LSB 12 +#define CSR_ACSMDDRBNK22_MASK GENMASK_32(15, 12) +/* CSR_ACSMSEQ1X23 */ +#define CSR_ACSMSEQ1X23_LSB 0 +#define CSR_ACSMSEQ1X23_MASK GENMASK_32(15, 0) +#define CSR_ACSMDDRCS23_LSB 0 +#define CSR_ACSMDDRCS23_MASK GENMASK_32(7, 0) +#define CSR_ACSMSAVEGEN23_LSB 8 +#define CSR_ACSMSAVEGEN23_MASK BIT(8) +#define CSR_ACSMLOADCHK23_LSB 9 +#define CSR_ACSMLOADCHK23_MASK BIT(9) +#define CSR_ACSMNORXENB23_LSB 10 +#define CSR_ACSMNORXENB23_MASK BIT(10) +#define CSR_ACSMNORXVAL23_LSB 11 +#define CSR_ACSMNORXVAL23_MASK BIT(11) +#define CSR_ACSMDDRBNK23_LSB 12 +#define CSR_ACSMDDRBNK23_MASK GENMASK_32(15, 12) +/* CSR_ACSMSEQ1X24 */ +#define CSR_ACSMSEQ1X24_LSB 0 +#define CSR_ACSMSEQ1X24_MASK GENMASK_32(15, 0) +#define CSR_ACSMDDRCS24_LSB 0 +#define CSR_ACSMDDRCS24_MASK GENMASK_32(7, 0) +#define CSR_ACSMSAVEGEN24_LSB 8 +#define CSR_ACSMSAVEGEN24_MASK BIT(8) +#define CSR_ACSMLOADCHK24_LSB 9 +#define CSR_ACSMLOADCHK24_MASK BIT(9) +#define CSR_ACSMNORXENB24_LSB 10 +#define CSR_ACSMNORXENB24_MASK BIT(10) +#define CSR_ACSMNORXVAL24_LSB 11 +#define CSR_ACSMNORXVAL24_MASK BIT(11) +#define CSR_ACSMDDRBNK24_LSB 12 +#define CSR_ACSMDDRBNK24_MASK GENMASK_32(15, 12) +/* CSR_ACSMSEQ1X25 */ +#define CSR_ACSMSEQ1X25_LSB 0 +#define CSR_ACSMSEQ1X25_MASK GENMASK_32(15, 0) +#define CSR_ACSMDDRCS25_LSB 0 +#define CSR_ACSMDDRCS25_MASK GENMASK_32(7, 0) +#define CSR_ACSMSAVEGEN25_LSB 8 +#define CSR_ACSMSAVEGEN25_MASK BIT(8) +#define CSR_ACSMLOADCHK25_LSB 9 +#define CSR_ACSMLOADCHK25_MASK BIT(9) +#define CSR_ACSMNORXENB25_LSB 10 +#define CSR_ACSMNORXENB25_MASK BIT(10) +#define CSR_ACSMNORXVAL25_LSB 11 +#define CSR_ACSMNORXVAL25_MASK BIT(11) +#define CSR_ACSMDDRBNK25_LSB 12 +#define CSR_ACSMDDRBNK25_MASK GENMASK_32(15, 12) +/* CSR_ACSMSEQ1X26 */ +#define CSR_ACSMSEQ1X26_LSB 0 +#define CSR_ACSMSEQ1X26_MASK GENMASK_32(15, 0) +#define CSR_ACSMDDRCS26_LSB 0 +#define CSR_ACSMDDRCS26_MASK GENMASK_32(7, 0) +#define CSR_ACSMSAVEGEN26_LSB 8 +#define CSR_ACSMSAVEGEN26_MASK BIT(8) +#define CSR_ACSMLOADCHK26_LSB 9 +#define CSR_ACSMLOADCHK26_MASK BIT(9) +#define CSR_ACSMNORXENB26_LSB 10 +#define CSR_ACSMNORXENB26_MASK BIT(10) +#define CSR_ACSMNORXVAL26_LSB 11 +#define CSR_ACSMNORXVAL26_MASK BIT(11) +#define CSR_ACSMDDRBNK26_LSB 12 +#define CSR_ACSMDDRBNK26_MASK GENMASK_32(15, 12) +/* CSR_ACSMSEQ1X27 */ +#define CSR_ACSMSEQ1X27_LSB 0 +#define CSR_ACSMSEQ1X27_MASK GENMASK_32(15, 0) +#define CSR_ACSMDDRCS27_LSB 0 +#define CSR_ACSMDDRCS27_MASK GENMASK_32(7, 0) +#define CSR_ACSMSAVEGEN27_LSB 8 +#define CSR_ACSMSAVEGEN27_MASK BIT(8) +#define CSR_ACSMLOADCHK27_LSB 9 +#define CSR_ACSMLOADCHK27_MASK BIT(9) +#define CSR_ACSMNORXENB27_LSB 10 +#define CSR_ACSMNORXENB27_MASK BIT(10) +#define CSR_ACSMNORXVAL27_LSB 11 +#define CSR_ACSMNORXVAL27_MASK BIT(11) +#define CSR_ACSMDDRBNK27_LSB 12 +#define CSR_ACSMDDRBNK27_MASK GENMASK_32(15, 12) +/* CSR_ACSMSEQ1X28 */ +#define CSR_ACSMSEQ1X28_LSB 0 +#define CSR_ACSMSEQ1X28_MASK GENMASK_32(15, 0) +#define CSR_ACSMDDRCS28_LSB 0 +#define CSR_ACSMDDRCS28_MASK GENMASK_32(7, 0) +#define CSR_ACSMSAVEGEN28_LSB 8 +#define CSR_ACSMSAVEGEN28_MASK BIT(8) +#define CSR_ACSMLOADCHK28_LSB 9 +#define CSR_ACSMLOADCHK28_MASK BIT(9) +#define CSR_ACSMNORXENB28_LSB 10 +#define CSR_ACSMNORXENB28_MASK BIT(10) +#define CSR_ACSMNORXVAL28_LSB 11 +#define CSR_ACSMNORXVAL28_MASK BIT(11) +#define CSR_ACSMDDRBNK28_LSB 12 +#define CSR_ACSMDDRBNK28_MASK GENMASK_32(15, 12) +/* CSR_ACSMSEQ1X29 */ +#define CSR_ACSMSEQ1X29_LSB 0 +#define CSR_ACSMSEQ1X29_MASK GENMASK_32(15, 0) +#define CSR_ACSMDDRCS29_LSB 0 +#define CSR_ACSMDDRCS29_MASK GENMASK_32(7, 0) +#define CSR_ACSMSAVEGEN29_LSB 8 +#define CSR_ACSMSAVEGEN29_MASK BIT(8) +#define CSR_ACSMLOADCHK29_LSB 9 +#define CSR_ACSMLOADCHK29_MASK BIT(9) +#define CSR_ACSMNORXENB29_LSB 10 +#define CSR_ACSMNORXENB29_MASK BIT(10) +#define CSR_ACSMNORXVAL29_LSB 11 +#define CSR_ACSMNORXVAL29_MASK BIT(11) +#define CSR_ACSMDDRBNK29_LSB 12 +#define CSR_ACSMDDRBNK29_MASK GENMASK_32(15, 12) +/* CSR_ACSMSEQ1X30 */ +#define CSR_ACSMSEQ1X30_LSB 0 +#define CSR_ACSMSEQ1X30_MASK GENMASK_32(15, 0) +#define CSR_ACSMDDRCS30_LSB 0 +#define CSR_ACSMDDRCS30_MASK GENMASK_32(7, 0) +#define CSR_ACSMSAVEGEN30_LSB 8 +#define CSR_ACSMSAVEGEN30_MASK BIT(8) +#define CSR_ACSMLOADCHK30_LSB 9 +#define CSR_ACSMLOADCHK30_MASK BIT(9) +#define CSR_ACSMNORXENB30_LSB 10 +#define CSR_ACSMNORXENB30_MASK BIT(10) +#define CSR_ACSMNORXVAL30_LSB 11 +#define CSR_ACSMNORXVAL30_MASK BIT(11) +#define CSR_ACSMDDRBNK30_LSB 12 +#define CSR_ACSMDDRBNK30_MASK GENMASK_32(15, 12) +/* CSR_ACSMSEQ1X31 */ +#define CSR_ACSMSEQ1X31_LSB 0 +#define CSR_ACSMSEQ1X31_MASK GENMASK_32(15, 0) +#define CSR_ACSMDDRCS31_LSB 0 +#define CSR_ACSMDDRCS31_MASK GENMASK_32(7, 0) +#define CSR_ACSMSAVEGEN31_LSB 8 +#define CSR_ACSMSAVEGEN31_MASK BIT(8) +#define CSR_ACSMLOADCHK31_LSB 9 +#define CSR_ACSMLOADCHK31_MASK BIT(9) +#define CSR_ACSMNORXENB31_LSB 10 +#define CSR_ACSMNORXENB31_MASK BIT(10) +#define CSR_ACSMNORXVAL31_LSB 11 +#define CSR_ACSMNORXVAL31_MASK BIT(11) +#define CSR_ACSMDDRBNK31_LSB 12 +#define CSR_ACSMDDRBNK31_MASK GENMASK_32(15, 12) +/* CSR_ACSMSEQ2X0 */ +#define CSR_ACSMSEQ2X0_LSB 0 +#define CSR_ACSMSEQ2X0_MASK GENMASK_32(15, 0) +#define CSR_ACSMDDRADRX15X0X0_LSB 0 +#define CSR_ACSMDDRADRX15X0X0_MASK GENMASK_32(15, 0) +/* CSR_ACSMSEQ2X1 */ +#define CSR_ACSMSEQ2X1_LSB 0 +#define CSR_ACSMSEQ2X1_MASK GENMASK_32(15, 0) +#define CSR_ACSMDDRADRX15X0X1_LSB 0 +#define CSR_ACSMDDRADRX15X0X1_MASK GENMASK_32(15, 0) +/* CSR_ACSMSEQ2X2 */ +#define CSR_ACSMSEQ2X2_LSB 0 +#define CSR_ACSMSEQ2X2_MASK GENMASK_32(15, 0) +#define CSR_ACSMDDRADRX15X0X2_LSB 0 +#define CSR_ACSMDDRADRX15X0X2_MASK GENMASK_32(15, 0) +/* CSR_ACSMSEQ2X3 */ +#define CSR_ACSMSEQ2X3_LSB 0 +#define CSR_ACSMSEQ2X3_MASK GENMASK_32(15, 0) +#define CSR_ACSMDDRADRX15X0X3_LSB 0 +#define CSR_ACSMDDRADRX15X0X3_MASK GENMASK_32(15, 0) +/* CSR_ACSMSEQ2X4 */ +#define CSR_ACSMSEQ2X4_LSB 0 +#define CSR_ACSMSEQ2X4_MASK GENMASK_32(15, 0) +#define CSR_ACSMDDRADRX15X0X4_LSB 0 +#define CSR_ACSMDDRADRX15X0X4_MASK GENMASK_32(15, 0) +/* CSR_ACSMSEQ2X5 */ +#define CSR_ACSMSEQ2X5_LSB 0 +#define CSR_ACSMSEQ2X5_MASK GENMASK_32(15, 0) +#define CSR_ACSMDDRADRX15X0X5_LSB 0 +#define CSR_ACSMDDRADRX15X0X5_MASK GENMASK_32(15, 0) +/* CSR_ACSMSEQ2X6 */ +#define CSR_ACSMSEQ2X6_LSB 0 +#define CSR_ACSMSEQ2X6_MASK GENMASK_32(15, 0) +#define CSR_ACSMDDRADRX15X0X6_LSB 0 +#define CSR_ACSMDDRADRX15X0X6_MASK GENMASK_32(15, 0) +/* CSR_ACSMSEQ2X7 */ +#define CSR_ACSMSEQ2X7_LSB 0 +#define CSR_ACSMSEQ2X7_MASK GENMASK_32(15, 0) +#define CSR_ACSMDDRADRX15X0X7_LSB 0 +#define CSR_ACSMDDRADRX15X0X7_MASK GENMASK_32(15, 0) +/* CSR_ACSMSEQ2X8 */ +#define CSR_ACSMSEQ2X8_LSB 0 +#define CSR_ACSMSEQ2X8_MASK GENMASK_32(15, 0) +#define CSR_ACSMDDRADRX15X0X8_LSB 0 +#define CSR_ACSMDDRADRX15X0X8_MASK GENMASK_32(15, 0) +/* CSR_ACSMSEQ2X9 */ +#define CSR_ACSMSEQ2X9_LSB 0 +#define CSR_ACSMSEQ2X9_MASK GENMASK_32(15, 0) +#define CSR_ACSMDDRADRX15X0X9_LSB 0 +#define CSR_ACSMDDRADRX15X0X9_MASK GENMASK_32(15, 0) +/* CSR_ACSMSEQ2X10 */ +#define CSR_ACSMSEQ2X10_LSB 0 +#define CSR_ACSMSEQ2X10_MASK GENMASK_32(15, 0) +#define CSR_ACSMDDRADRX15X0X10_LSB 0 +#define CSR_ACSMDDRADRX15X0X10_MASK GENMASK_32(15, 0) +/* CSR_ACSMSEQ2X11 */ +#define CSR_ACSMSEQ2X11_LSB 0 +#define CSR_ACSMSEQ2X11_MASK GENMASK_32(15, 0) +#define CSR_ACSMDDRADRX15X0X11_LSB 0 +#define CSR_ACSMDDRADRX15X0X11_MASK GENMASK_32(15, 0) +/* CSR_ACSMSEQ2X12 */ +#define CSR_ACSMSEQ2X12_LSB 0 +#define CSR_ACSMSEQ2X12_MASK GENMASK_32(15, 0) +#define CSR_ACSMDDRADRX15X0X12_LSB 0 +#define CSR_ACSMDDRADRX15X0X12_MASK GENMASK_32(15, 0) +/* CSR_ACSMSEQ2X13 */ +#define CSR_ACSMSEQ2X13_LSB 0 +#define CSR_ACSMSEQ2X13_MASK GENMASK_32(15, 0) +#define CSR_ACSMDDRADRX15X0X13_LSB 0 +#define CSR_ACSMDDRADRX15X0X13_MASK GENMASK_32(15, 0) +/* CSR_ACSMSEQ2X14 */ +#define CSR_ACSMSEQ2X14_LSB 0 +#define CSR_ACSMSEQ2X14_MASK GENMASK_32(15, 0) +#define CSR_ACSMDDRADRX15X0X14_LSB 0 +#define CSR_ACSMDDRADRX15X0X14_MASK GENMASK_32(15, 0) +/* CSR_ACSMSEQ2X15 */ +#define CSR_ACSMSEQ2X15_LSB 0 +#define CSR_ACSMSEQ2X15_MASK GENMASK_32(15, 0) +#define CSR_ACSMDDRADRX15X0X15_LSB 0 +#define CSR_ACSMDDRADRX15X0X15_MASK GENMASK_32(15, 0) +/* CSR_ACSMSEQ2X16 */ +#define CSR_ACSMSEQ2X16_LSB 0 +#define CSR_ACSMSEQ2X16_MASK GENMASK_32(15, 0) +#define CSR_ACSMDDRADRX15X0X16_LSB 0 +#define CSR_ACSMDDRADRX15X0X16_MASK GENMASK_32(15, 0) +/* CSR_ACSMSEQ2X17 */ +#define CSR_ACSMSEQ2X17_LSB 0 +#define CSR_ACSMSEQ2X17_MASK GENMASK_32(15, 0) +#define CSR_ACSMDDRADRX15X0X17_LSB 0 +#define CSR_ACSMDDRADRX15X0X17_MASK GENMASK_32(15, 0) +/* CSR_ACSMSEQ2X18 */ +#define CSR_ACSMSEQ2X18_LSB 0 +#define CSR_ACSMSEQ2X18_MASK GENMASK_32(15, 0) +#define CSR_ACSMDDRADRX15X0X18_LSB 0 +#define CSR_ACSMDDRADRX15X0X18_MASK GENMASK_32(15, 0) +/* CSR_ACSMSEQ2X19 */ +#define CSR_ACSMSEQ2X19_LSB 0 +#define CSR_ACSMSEQ2X19_MASK GENMASK_32(15, 0) +#define CSR_ACSMDDRADRX15X0X19_LSB 0 +#define CSR_ACSMDDRADRX15X0X19_MASK GENMASK_32(15, 0) +/* CSR_ACSMSEQ2X20 */ +#define CSR_ACSMSEQ2X20_LSB 0 +#define CSR_ACSMSEQ2X20_MASK GENMASK_32(15, 0) +#define CSR_ACSMDDRADRX15X0X20_LSB 0 +#define CSR_ACSMDDRADRX15X0X20_MASK GENMASK_32(15, 0) +/* CSR_ACSMSEQ2X21 */ +#define CSR_ACSMSEQ2X21_LSB 0 +#define CSR_ACSMSEQ2X21_MASK GENMASK_32(15, 0) +#define CSR_ACSMDDRADRX15X0X21_LSB 0 +#define CSR_ACSMDDRADRX15X0X21_MASK GENMASK_32(15, 0) +/* CSR_ACSMSEQ2X22 */ +#define CSR_ACSMSEQ2X22_LSB 0 +#define CSR_ACSMSEQ2X22_MASK GENMASK_32(15, 0) +#define CSR_ACSMDDRADRX15X0X22_LSB 0 +#define CSR_ACSMDDRADRX15X0X22_MASK GENMASK_32(15, 0) +/* CSR_ACSMSEQ2X23 */ +#define CSR_ACSMSEQ2X23_LSB 0 +#define CSR_ACSMSEQ2X23_MASK GENMASK_32(15, 0) +#define CSR_ACSMDDRADRX15X0X23_LSB 0 +#define CSR_ACSMDDRADRX15X0X23_MASK GENMASK_32(15, 0) +/* CSR_ACSMSEQ2X24 */ +#define CSR_ACSMSEQ2X24_LSB 0 +#define CSR_ACSMSEQ2X24_MASK GENMASK_32(15, 0) +#define CSR_ACSMDDRADRX15X0X24_LSB 0 +#define CSR_ACSMDDRADRX15X0X24_MASK GENMASK_32(15, 0) +/* CSR_ACSMSEQ2X25 */ +#define CSR_ACSMSEQ2X25_LSB 0 +#define CSR_ACSMSEQ2X25_MASK GENMASK_32(15, 0) +#define CSR_ACSMDDRADRX15X0X25_LSB 0 +#define CSR_ACSMDDRADRX15X0X25_MASK GENMASK_32(15, 0) +/* CSR_ACSMSEQ2X26 */ +#define CSR_ACSMSEQ2X26_LSB 0 +#define CSR_ACSMSEQ2X26_MASK GENMASK_32(15, 0) +#define CSR_ACSMDDRADRX15X0X26_LSB 0 +#define CSR_ACSMDDRADRX15X0X26_MASK GENMASK_32(15, 0) +/* CSR_ACSMSEQ2X27 */ +#define CSR_ACSMSEQ2X27_LSB 0 +#define CSR_ACSMSEQ2X27_MASK GENMASK_32(15, 0) +#define CSR_ACSMDDRADRX15X0X27_LSB 0 +#define CSR_ACSMDDRADRX15X0X27_MASK GENMASK_32(15, 0) +/* CSR_ACSMSEQ2X28 */ +#define CSR_ACSMSEQ2X28_LSB 0 +#define CSR_ACSMSEQ2X28_MASK GENMASK_32(15, 0) +#define CSR_ACSMDDRADRX15X0X28_LSB 0 +#define CSR_ACSMDDRADRX15X0X28_MASK GENMASK_32(15, 0) +/* CSR_ACSMSEQ2X29 */ +#define CSR_ACSMSEQ2X29_LSB 0 +#define CSR_ACSMSEQ2X29_MASK GENMASK_32(15, 0) +#define CSR_ACSMDDRADRX15X0X29_LSB 0 +#define CSR_ACSMDDRADRX15X0X29_MASK GENMASK_32(15, 0) +/* CSR_ACSMSEQ2X30 */ +#define CSR_ACSMSEQ2X30_LSB 0 +#define CSR_ACSMSEQ2X30_MASK GENMASK_32(15, 0) +#define CSR_ACSMDDRADRX15X0X30_LSB 0 +#define CSR_ACSMDDRADRX15X0X30_MASK GENMASK_32(15, 0) +/* CSR_ACSMSEQ2X31 */ +#define CSR_ACSMSEQ2X31_LSB 0 +#define CSR_ACSMSEQ2X31_MASK GENMASK_32(15, 0) +#define CSR_ACSMDDRADRX15X0X31_LSB 0 +#define CSR_ACSMDDRADRX15X0X31_MASK GENMASK_32(15, 0) +/* CSR_ACSMSEQ3X0 */ +#define CSR_ACSMSEQ3X0_LSB 0 +#define CSR_ACSMSEQ3X0_MASK GENMASK_32(15, 0) +#define CSR_ACSMCMDREPCNT0_LSB 0 +#define CSR_ACSMCMDREPCNT0_MASK GENMASK_32(7, 0) +#define CSR_ACSMADRADV0_LSB 8 +#define CSR_ACSMADRADV0_MASK GENMASK_32(9, 8) +#define CSR_ACSMBNKADV0_LSB 10 +#define CSR_ACSMBNKADV0_MASK GENMASK_32(11, 10) +#define CSR_ACSMADRSELLOAD0_LSB 12 +#define CSR_ACSMADRSELLOAD0_MASK GENMASK_32(13, 12) +#define CSR_ACSMBNKSELLOAD0_LSB 14 +#define CSR_ACSMBNKSELLOAD0_MASK BIT(14) +#define CSR_ACSMLONGBUBBLE0_LSB 15 +#define CSR_ACSMLONGBUBBLE0_MASK BIT(15) +/* CSR_ACSMSEQ3X1 */ +#define CSR_ACSMSEQ3X1_LSB 0 +#define CSR_ACSMSEQ3X1_MASK GENMASK_32(15, 0) +#define CSR_ACSMCMDREPCNT1_LSB 0 +#define CSR_ACSMCMDREPCNT1_MASK GENMASK_32(7, 0) +#define CSR_ACSMADRADV1_LSB 8 +#define CSR_ACSMADRADV1_MASK GENMASK_32(9, 8) +#define CSR_ACSMBNKADV1_LSB 10 +#define CSR_ACSMBNKADV1_MASK GENMASK_32(11, 10) +#define CSR_ACSMADRSELLOAD1_LSB 12 +#define CSR_ACSMADRSELLOAD1_MASK GENMASK_32(13, 12) +#define CSR_ACSMBNKSELLOAD1_LSB 14 +#define CSR_ACSMBNKSELLOAD1_MASK BIT(14) +#define CSR_ACSMLONGBUBBLE1_LSB 15 +#define CSR_ACSMLONGBUBBLE1_MASK BIT(15) +/* CSR_ACSMSEQ3X2 */ +#define CSR_ACSMSEQ3X2_LSB 0 +#define CSR_ACSMSEQ3X2_MASK GENMASK_32(15, 0) +#define CSR_ACSMCMDREPCNT2_LSB 0 +#define CSR_ACSMCMDREPCNT2_MASK GENMASK_32(7, 0) +#define CSR_ACSMADRADV2_LSB 8 +#define CSR_ACSMADRADV2_MASK GENMASK_32(9, 8) +#define CSR_ACSMBNKADV2_LSB 10 +#define CSR_ACSMBNKADV2_MASK GENMASK_32(11, 10) +#define CSR_ACSMADRSELLOAD2_LSB 12 +#define CSR_ACSMADRSELLOAD2_MASK GENMASK_32(13, 12) +#define CSR_ACSMBNKSELLOAD2_LSB 14 +#define CSR_ACSMBNKSELLOAD2_MASK BIT(14) +#define CSR_ACSMLONGBUBBLE2_LSB 15 +#define CSR_ACSMLONGBUBBLE2_MASK BIT(15) +/* CSR_ACSMSEQ3X3 */ +#define CSR_ACSMSEQ3X3_LSB 0 +#define CSR_ACSMSEQ3X3_MASK GENMASK_32(15, 0) +#define CSR_ACSMCMDREPCNT3_LSB 0 +#define CSR_ACSMCMDREPCNT3_MASK GENMASK_32(7, 0) +#define CSR_ACSMADRADV3_LSB 8 +#define CSR_ACSMADRADV3_MASK GENMASK_32(9, 8) +#define CSR_ACSMBNKADV3_LSB 10 +#define CSR_ACSMBNKADV3_MASK GENMASK_32(11, 10) +#define CSR_ACSMADRSELLOAD3_LSB 12 +#define CSR_ACSMADRSELLOAD3_MASK GENMASK_32(13, 12) +#define CSR_ACSMBNKSELLOAD3_LSB 14 +#define CSR_ACSMBNKSELLOAD3_MASK BIT(14) +#define CSR_ACSMLONGBUBBLE3_LSB 15 +#define CSR_ACSMLONGBUBBLE3_MASK BIT(15) +/* CSR_ACSMSEQ3X4 */ +#define CSR_ACSMSEQ3X4_LSB 0 +#define CSR_ACSMSEQ3X4_MASK GENMASK_32(15, 0) +#define CSR_ACSMCMDREPCNT4_LSB 0 +#define CSR_ACSMCMDREPCNT4_MASK GENMASK_32(7, 0) +#define CSR_ACSMADRADV4_LSB 8 +#define CSR_ACSMADRADV4_MASK GENMASK_32(9, 8) +#define CSR_ACSMBNKADV4_LSB 10 +#define CSR_ACSMBNKADV4_MASK GENMASK_32(11, 10) +#define CSR_ACSMADRSELLOAD4_LSB 12 +#define CSR_ACSMADRSELLOAD4_MASK GENMASK_32(13, 12) +#define CSR_ACSMBNKSELLOAD4_LSB 14 +#define CSR_ACSMBNKSELLOAD4_MASK BIT(14) +#define CSR_ACSMLONGBUBBLE4_LSB 15 +#define CSR_ACSMLONGBUBBLE4_MASK BIT(15) +/* CSR_ACSMSEQ3X5 */ +#define CSR_ACSMSEQ3X5_LSB 0 +#define CSR_ACSMSEQ3X5_MASK GENMASK_32(15, 0) +#define CSR_ACSMCMDREPCNT5_LSB 0 +#define CSR_ACSMCMDREPCNT5_MASK GENMASK_32(7, 0) +#define CSR_ACSMADRADV5_LSB 8 +#define CSR_ACSMADRADV5_MASK GENMASK_32(9, 8) +#define CSR_ACSMBNKADV5_LSB 10 +#define CSR_ACSMBNKADV5_MASK GENMASK_32(11, 10) +#define CSR_ACSMADRSELLOAD5_LSB 12 +#define CSR_ACSMADRSELLOAD5_MASK GENMASK_32(13, 12) +#define CSR_ACSMBNKSELLOAD5_LSB 14 +#define CSR_ACSMBNKSELLOAD5_MASK BIT(14) +#define CSR_ACSMLONGBUBBLE5_LSB 15 +#define CSR_ACSMLONGBUBBLE5_MASK BIT(15) +/* CSR_ACSMSEQ3X6 */ +#define CSR_ACSMSEQ3X6_LSB 0 +#define CSR_ACSMSEQ3X6_MASK GENMASK_32(15, 0) +#define CSR_ACSMCMDREPCNT6_LSB 0 +#define CSR_ACSMCMDREPCNT6_MASK GENMASK_32(7, 0) +#define CSR_ACSMADRADV6_LSB 8 +#define CSR_ACSMADRADV6_MASK GENMASK_32(9, 8) +#define CSR_ACSMBNKADV6_LSB 10 +#define CSR_ACSMBNKADV6_MASK GENMASK_32(11, 10) +#define CSR_ACSMADRSELLOAD6_LSB 12 +#define CSR_ACSMADRSELLOAD6_MASK GENMASK_32(13, 12) +#define CSR_ACSMBNKSELLOAD6_LSB 14 +#define CSR_ACSMBNKSELLOAD6_MASK BIT(14) +#define CSR_ACSMLONGBUBBLE6_LSB 15 +#define CSR_ACSMLONGBUBBLE6_MASK BIT(15) +/* CSR_ACSMSEQ3X7 */ +#define CSR_ACSMSEQ3X7_LSB 0 +#define CSR_ACSMSEQ3X7_MASK GENMASK_32(15, 0) +#define CSR_ACSMCMDREPCNT7_LSB 0 +#define CSR_ACSMCMDREPCNT7_MASK GENMASK_32(7, 0) +#define CSR_ACSMADRADV7_LSB 8 +#define CSR_ACSMADRADV7_MASK GENMASK_32(9, 8) +#define CSR_ACSMBNKADV7_LSB 10 +#define CSR_ACSMBNKADV7_MASK GENMASK_32(11, 10) +#define CSR_ACSMADRSELLOAD7_LSB 12 +#define CSR_ACSMADRSELLOAD7_MASK GENMASK_32(13, 12) +#define CSR_ACSMBNKSELLOAD7_LSB 14 +#define CSR_ACSMBNKSELLOAD7_MASK BIT(14) +#define CSR_ACSMLONGBUBBLE7_LSB 15 +#define CSR_ACSMLONGBUBBLE7_MASK BIT(15) +/* CSR_ACSMSEQ3X8 */ +#define CSR_ACSMSEQ3X8_LSB 0 +#define CSR_ACSMSEQ3X8_MASK GENMASK_32(15, 0) +#define CSR_ACSMCMDREPCNT8_LSB 0 +#define CSR_ACSMCMDREPCNT8_MASK GENMASK_32(7, 0) +#define CSR_ACSMADRADV8_LSB 8 +#define CSR_ACSMADRADV8_MASK GENMASK_32(9, 8) +#define CSR_ACSMBNKADV8_LSB 10 +#define CSR_ACSMBNKADV8_MASK GENMASK_32(11, 10) +#define CSR_ACSMADRSELLOAD8_LSB 12 +#define CSR_ACSMADRSELLOAD8_MASK GENMASK_32(13, 12) +#define CSR_ACSMBNKSELLOAD8_LSB 14 +#define CSR_ACSMBNKSELLOAD8_MASK BIT(14) +#define CSR_ACSMLONGBUBBLE8_LSB 15 +#define CSR_ACSMLONGBUBBLE8_MASK BIT(15) +/* CSR_ACSMSEQ3X9 */ +#define CSR_ACSMSEQ3X9_LSB 0 +#define CSR_ACSMSEQ3X9_MASK GENMASK_32(15, 0) +#define CSR_ACSMCMDREPCNT9_LSB 0 +#define CSR_ACSMCMDREPCNT9_MASK GENMASK_32(7, 0) +#define CSR_ACSMADRADV9_LSB 8 +#define CSR_ACSMADRADV9_MASK GENMASK_32(9, 8) +#define CSR_ACSMBNKADV9_LSB 10 +#define CSR_ACSMBNKADV9_MASK GENMASK_32(11, 10) +#define CSR_ACSMADRSELLOAD9_LSB 12 +#define CSR_ACSMADRSELLOAD9_MASK GENMASK_32(13, 12) +#define CSR_ACSMBNKSELLOAD9_LSB 14 +#define CSR_ACSMBNKSELLOAD9_MASK BIT(14) +#define CSR_ACSMLONGBUBBLE9_LSB 15 +#define CSR_ACSMLONGBUBBLE9_MASK BIT(15) +/* CSR_ACSMSEQ3X10 */ +#define CSR_ACSMSEQ3X10_LSB 0 +#define CSR_ACSMSEQ3X10_MASK GENMASK_32(15, 0) +#define CSR_ACSMCMDREPCNT10_LSB 0 +#define CSR_ACSMCMDREPCNT10_MASK GENMASK_32(7, 0) +#define CSR_ACSMADRADV10_LSB 8 +#define CSR_ACSMADRADV10_MASK GENMASK_32(9, 8) +#define CSR_ACSMBNKADV10_LSB 10 +#define CSR_ACSMBNKADV10_MASK GENMASK_32(11, 10) +#define CSR_ACSMADRSELLOAD10_LSB 12 +#define CSR_ACSMADRSELLOAD10_MASK GENMASK_32(13, 12) +#define CSR_ACSMBNKSELLOAD10_LSB 14 +#define CSR_ACSMBNKSELLOAD10_MASK BIT(14) +#define CSR_ACSMLONGBUBBLE10_LSB 15 +#define CSR_ACSMLONGBUBBLE10_MASK BIT(15) +/* CSR_ACSMSEQ3X11 */ +#define CSR_ACSMSEQ3X11_LSB 0 +#define CSR_ACSMSEQ3X11_MASK GENMASK_32(15, 0) +#define CSR_ACSMCMDREPCNT11_LSB 0 +#define CSR_ACSMCMDREPCNT11_MASK GENMASK_32(7, 0) +#define CSR_ACSMADRADV11_LSB 8 +#define CSR_ACSMADRADV11_MASK GENMASK_32(9, 8) +#define CSR_ACSMBNKADV11_LSB 10 +#define CSR_ACSMBNKADV11_MASK GENMASK_32(11, 10) +#define CSR_ACSMADRSELLOAD11_LSB 12 +#define CSR_ACSMADRSELLOAD11_MASK GENMASK_32(13, 12) +#define CSR_ACSMBNKSELLOAD11_LSB 14 +#define CSR_ACSMBNKSELLOAD11_MASK BIT(14) +#define CSR_ACSMLONGBUBBLE11_LSB 15 +#define CSR_ACSMLONGBUBBLE11_MASK BIT(15) +/* CSR_ACSMSEQ3X12 */ +#define CSR_ACSMSEQ3X12_LSB 0 +#define CSR_ACSMSEQ3X12_MASK GENMASK_32(15, 0) +#define CSR_ACSMCMDREPCNT12_LSB 0 +#define CSR_ACSMCMDREPCNT12_MASK GENMASK_32(7, 0) +#define CSR_ACSMADRADV12_LSB 8 +#define CSR_ACSMADRADV12_MASK GENMASK_32(9, 8) +#define CSR_ACSMBNKADV12_LSB 10 +#define CSR_ACSMBNKADV12_MASK GENMASK_32(11, 10) +#define CSR_ACSMADRSELLOAD12_LSB 12 +#define CSR_ACSMADRSELLOAD12_MASK GENMASK_32(13, 12) +#define CSR_ACSMBNKSELLOAD12_LSB 14 +#define CSR_ACSMBNKSELLOAD12_MASK BIT(14) +#define CSR_ACSMLONGBUBBLE12_LSB 15 +#define CSR_ACSMLONGBUBBLE12_MASK BIT(15) +/* CSR_ACSMSEQ3X13 */ +#define CSR_ACSMSEQ3X13_LSB 0 +#define CSR_ACSMSEQ3X13_MASK GENMASK_32(15, 0) +#define CSR_ACSMCMDREPCNT13_LSB 0 +#define CSR_ACSMCMDREPCNT13_MASK GENMASK_32(7, 0) +#define CSR_ACSMADRADV13_LSB 8 +#define CSR_ACSMADRADV13_MASK GENMASK_32(9, 8) +#define CSR_ACSMBNKADV13_LSB 10 +#define CSR_ACSMBNKADV13_MASK GENMASK_32(11, 10) +#define CSR_ACSMADRSELLOAD13_LSB 12 +#define CSR_ACSMADRSELLOAD13_MASK GENMASK_32(13, 12) +#define CSR_ACSMBNKSELLOAD13_LSB 14 +#define CSR_ACSMBNKSELLOAD13_MASK BIT(14) +#define CSR_ACSMLONGBUBBLE13_LSB 15 +#define CSR_ACSMLONGBUBBLE13_MASK BIT(15) +/* CSR_ACSMSEQ3X14 */ +#define CSR_ACSMSEQ3X14_LSB 0 +#define CSR_ACSMSEQ3X14_MASK GENMASK_32(15, 0) +#define CSR_ACSMCMDREPCNT14_LSB 0 +#define CSR_ACSMCMDREPCNT14_MASK GENMASK_32(7, 0) +#define CSR_ACSMADRADV14_LSB 8 +#define CSR_ACSMADRADV14_MASK GENMASK_32(9, 8) +#define CSR_ACSMBNKADV14_LSB 10 +#define CSR_ACSMBNKADV14_MASK GENMASK_32(11, 10) +#define CSR_ACSMADRSELLOAD14_LSB 12 +#define CSR_ACSMADRSELLOAD14_MASK GENMASK_32(13, 12) +#define CSR_ACSMBNKSELLOAD14_LSB 14 +#define CSR_ACSMBNKSELLOAD14_MASK BIT(14) +#define CSR_ACSMLONGBUBBLE14_LSB 15 +#define CSR_ACSMLONGBUBBLE14_MASK BIT(15) +/* CSR_ACSMSEQ3X15 */ +#define CSR_ACSMSEQ3X15_LSB 0 +#define CSR_ACSMSEQ3X15_MASK GENMASK_32(15, 0) +#define CSR_ACSMCMDREPCNT15_LSB 0 +#define CSR_ACSMCMDREPCNT15_MASK GENMASK_32(7, 0) +#define CSR_ACSMADRADV15_LSB 8 +#define CSR_ACSMADRADV15_MASK GENMASK_32(9, 8) +#define CSR_ACSMBNKADV15_LSB 10 +#define CSR_ACSMBNKADV15_MASK GENMASK_32(11, 10) +#define CSR_ACSMADRSELLOAD15_LSB 12 +#define CSR_ACSMADRSELLOAD15_MASK GENMASK_32(13, 12) +#define CSR_ACSMBNKSELLOAD15_LSB 14 +#define CSR_ACSMBNKSELLOAD15_MASK BIT(14) +#define CSR_ACSMLONGBUBBLE15_LSB 15 +#define CSR_ACSMLONGBUBBLE15_MASK BIT(15) +/* CSR_ACSMSEQ3X16 */ +#define CSR_ACSMSEQ3X16_LSB 0 +#define CSR_ACSMSEQ3X16_MASK GENMASK_32(15, 0) +#define CSR_ACSMCMDREPCNT16_LSB 0 +#define CSR_ACSMCMDREPCNT16_MASK GENMASK_32(7, 0) +#define CSR_ACSMADRADV16_LSB 8 +#define CSR_ACSMADRADV16_MASK GENMASK_32(9, 8) +#define CSR_ACSMBNKADV16_LSB 10 +#define CSR_ACSMBNKADV16_MASK GENMASK_32(11, 10) +#define CSR_ACSMADRSELLOAD16_LSB 12 +#define CSR_ACSMADRSELLOAD16_MASK GENMASK_32(13, 12) +#define CSR_ACSMBNKSELLOAD16_LSB 14 +#define CSR_ACSMBNKSELLOAD16_MASK BIT(14) +#define CSR_ACSMLONGBUBBLE16_LSB 15 +#define CSR_ACSMLONGBUBBLE16_MASK BIT(15) +/* CSR_ACSMSEQ3X17 */ +#define CSR_ACSMSEQ3X17_LSB 0 +#define CSR_ACSMSEQ3X17_MASK GENMASK_32(15, 0) +#define CSR_ACSMCMDREPCNT17_LSB 0 +#define CSR_ACSMCMDREPCNT17_MASK GENMASK_32(7, 0) +#define CSR_ACSMADRADV17_LSB 8 +#define CSR_ACSMADRADV17_MASK GENMASK_32(9, 8) +#define CSR_ACSMBNKADV17_LSB 10 +#define CSR_ACSMBNKADV17_MASK GENMASK_32(11, 10) +#define CSR_ACSMADRSELLOAD17_LSB 12 +#define CSR_ACSMADRSELLOAD17_MASK GENMASK_32(13, 12) +#define CSR_ACSMBNKSELLOAD17_LSB 14 +#define CSR_ACSMBNKSELLOAD17_MASK BIT(14) +#define CSR_ACSMLONGBUBBLE17_LSB 15 +#define CSR_ACSMLONGBUBBLE17_MASK BIT(15) +/* CSR_ACSMSEQ3X18 */ +#define CSR_ACSMSEQ3X18_LSB 0 +#define CSR_ACSMSEQ3X18_MASK GENMASK_32(15, 0) +#define CSR_ACSMCMDREPCNT18_LSB 0 +#define CSR_ACSMCMDREPCNT18_MASK GENMASK_32(7, 0) +#define CSR_ACSMADRADV18_LSB 8 +#define CSR_ACSMADRADV18_MASK GENMASK_32(9, 8) +#define CSR_ACSMBNKADV18_LSB 10 +#define CSR_ACSMBNKADV18_MASK GENMASK_32(11, 10) +#define CSR_ACSMADRSELLOAD18_LSB 12 +#define CSR_ACSMADRSELLOAD18_MASK GENMASK_32(13, 12) +#define CSR_ACSMBNKSELLOAD18_LSB 14 +#define CSR_ACSMBNKSELLOAD18_MASK BIT(14) +#define CSR_ACSMLONGBUBBLE18_LSB 15 +#define CSR_ACSMLONGBUBBLE18_MASK BIT(15) +/* CSR_ACSMSEQ3X19 */ +#define CSR_ACSMSEQ3X19_LSB 0 +#define CSR_ACSMSEQ3X19_MASK GENMASK_32(15, 0) +#define CSR_ACSMCMDREPCNT19_LSB 0 +#define CSR_ACSMCMDREPCNT19_MASK GENMASK_32(7, 0) +#define CSR_ACSMADRADV19_LSB 8 +#define CSR_ACSMADRADV19_MASK GENMASK_32(9, 8) +#define CSR_ACSMBNKADV19_LSB 10 +#define CSR_ACSMBNKADV19_MASK GENMASK_32(11, 10) +#define CSR_ACSMADRSELLOAD19_LSB 12 +#define CSR_ACSMADRSELLOAD19_MASK GENMASK_32(13, 12) +#define CSR_ACSMBNKSELLOAD19_LSB 14 +#define CSR_ACSMBNKSELLOAD19_MASK BIT(14) +#define CSR_ACSMLONGBUBBLE19_LSB 15 +#define CSR_ACSMLONGBUBBLE19_MASK BIT(15) +/* CSR_ACSMSEQ3X20 */ +#define CSR_ACSMSEQ3X20_LSB 0 +#define CSR_ACSMSEQ3X20_MASK GENMASK_32(15, 0) +#define CSR_ACSMCMDREPCNT20_LSB 0 +#define CSR_ACSMCMDREPCNT20_MASK GENMASK_32(7, 0) +#define CSR_ACSMADRADV20_LSB 8 +#define CSR_ACSMADRADV20_MASK GENMASK_32(9, 8) +#define CSR_ACSMBNKADV20_LSB 10 +#define CSR_ACSMBNKADV20_MASK GENMASK_32(11, 10) +#define CSR_ACSMADRSELLOAD20_LSB 12 +#define CSR_ACSMADRSELLOAD20_MASK GENMASK_32(13, 12) +#define CSR_ACSMBNKSELLOAD20_LSB 14 +#define CSR_ACSMBNKSELLOAD20_MASK BIT(14) +#define CSR_ACSMLONGBUBBLE20_LSB 15 +#define CSR_ACSMLONGBUBBLE20_MASK BIT(15) +/* CSR_ACSMSEQ3X21 */ +#define CSR_ACSMSEQ3X21_LSB 0 +#define CSR_ACSMSEQ3X21_MASK GENMASK_32(15, 0) +#define CSR_ACSMCMDREPCNT21_LSB 0 +#define CSR_ACSMCMDREPCNT21_MASK GENMASK_32(7, 0) +#define CSR_ACSMADRADV21_LSB 8 +#define CSR_ACSMADRADV21_MASK GENMASK_32(9, 8) +#define CSR_ACSMBNKADV21_LSB 10 +#define CSR_ACSMBNKADV21_MASK GENMASK_32(11, 10) +#define CSR_ACSMADRSELLOAD21_LSB 12 +#define CSR_ACSMADRSELLOAD21_MASK GENMASK_32(13, 12) +#define CSR_ACSMBNKSELLOAD21_LSB 14 +#define CSR_ACSMBNKSELLOAD21_MASK BIT(14) +#define CSR_ACSMLONGBUBBLE21_LSB 15 +#define CSR_ACSMLONGBUBBLE21_MASK BIT(15) +/* CSR_ACSMSEQ3X22 */ +#define CSR_ACSMSEQ3X22_LSB 0 +#define CSR_ACSMSEQ3X22_MASK GENMASK_32(15, 0) +#define CSR_ACSMCMDREPCNT22_LSB 0 +#define CSR_ACSMCMDREPCNT22_MASK GENMASK_32(7, 0) +#define CSR_ACSMADRADV22_LSB 8 +#define CSR_ACSMADRADV22_MASK GENMASK_32(9, 8) +#define CSR_ACSMBNKADV22_LSB 10 +#define CSR_ACSMBNKADV22_MASK GENMASK_32(11, 10) +#define CSR_ACSMADRSELLOAD22_LSB 12 +#define CSR_ACSMADRSELLOAD22_MASK GENMASK_32(13, 12) +#define CSR_ACSMBNKSELLOAD22_LSB 14 +#define CSR_ACSMBNKSELLOAD22_MASK BIT(14) +#define CSR_ACSMLONGBUBBLE22_LSB 15 +#define CSR_ACSMLONGBUBBLE22_MASK BIT(15) +/* CSR_ACSMSEQ3X23 */ +#define CSR_ACSMSEQ3X23_LSB 0 +#define CSR_ACSMSEQ3X23_MASK GENMASK_32(15, 0) +#define CSR_ACSMCMDREPCNT23_LSB 0 +#define CSR_ACSMCMDREPCNT23_MASK GENMASK_32(7, 0) +#define CSR_ACSMADRADV23_LSB 8 +#define CSR_ACSMADRADV23_MASK GENMASK_32(9, 8) +#define CSR_ACSMBNKADV23_LSB 10 +#define CSR_ACSMBNKADV23_MASK GENMASK_32(11, 10) +#define CSR_ACSMADRSELLOAD23_LSB 12 +#define CSR_ACSMADRSELLOAD23_MASK GENMASK_32(13, 12) +#define CSR_ACSMBNKSELLOAD23_LSB 14 +#define CSR_ACSMBNKSELLOAD23_MASK BIT(14) +#define CSR_ACSMLONGBUBBLE23_LSB 15 +#define CSR_ACSMLONGBUBBLE23_MASK BIT(15) +/* CSR_ACSMSEQ3X24 */ +#define CSR_ACSMSEQ3X24_LSB 0 +#define CSR_ACSMSEQ3X24_MASK GENMASK_32(15, 0) +#define CSR_ACSMCMDREPCNT24_LSB 0 +#define CSR_ACSMCMDREPCNT24_MASK GENMASK_32(7, 0) +#define CSR_ACSMADRADV24_LSB 8 +#define CSR_ACSMADRADV24_MASK GENMASK_32(9, 8) +#define CSR_ACSMBNKADV24_LSB 10 +#define CSR_ACSMBNKADV24_MASK GENMASK_32(11, 10) +#define CSR_ACSMADRSELLOAD24_LSB 12 +#define CSR_ACSMADRSELLOAD24_MASK GENMASK_32(13, 12) +#define CSR_ACSMBNKSELLOAD24_LSB 14 +#define CSR_ACSMBNKSELLOAD24_MASK BIT(14) +#define CSR_ACSMLONGBUBBLE24_LSB 15 +#define CSR_ACSMLONGBUBBLE24_MASK BIT(15) +/* CSR_ACSMSEQ3X25 */ +#define CSR_ACSMSEQ3X25_LSB 0 +#define CSR_ACSMSEQ3X25_MASK GENMASK_32(15, 0) +#define CSR_ACSMCMDREPCNT25_LSB 0 +#define CSR_ACSMCMDREPCNT25_MASK GENMASK_32(7, 0) +#define CSR_ACSMADRADV25_LSB 8 +#define CSR_ACSMADRADV25_MASK GENMASK_32(9, 8) +#define CSR_ACSMBNKADV25_LSB 10 +#define CSR_ACSMBNKADV25_MASK GENMASK_32(11, 10) +#define CSR_ACSMADRSELLOAD25_LSB 12 +#define CSR_ACSMADRSELLOAD25_MASK GENMASK_32(13, 12) +#define CSR_ACSMBNKSELLOAD25_LSB 14 +#define CSR_ACSMBNKSELLOAD25_MASK BIT(14) +#define CSR_ACSMLONGBUBBLE25_LSB 15 +#define CSR_ACSMLONGBUBBLE25_MASK BIT(15) +/* CSR_ACSMSEQ3X26 */ +#define CSR_ACSMSEQ3X26_LSB 0 +#define CSR_ACSMSEQ3X26_MASK GENMASK_32(15, 0) +#define CSR_ACSMCMDREPCNT26_LSB 0 +#define CSR_ACSMCMDREPCNT26_MASK GENMASK_32(7, 0) +#define CSR_ACSMADRADV26_LSB 8 +#define CSR_ACSMADRADV26_MASK GENMASK_32(9, 8) +#define CSR_ACSMBNKADV26_LSB 10 +#define CSR_ACSMBNKADV26_MASK GENMASK_32(11, 10) +#define CSR_ACSMADRSELLOAD26_LSB 12 +#define CSR_ACSMADRSELLOAD26_MASK GENMASK_32(13, 12) +#define CSR_ACSMBNKSELLOAD26_LSB 14 +#define CSR_ACSMBNKSELLOAD26_MASK BIT(14) +#define CSR_ACSMLONGBUBBLE26_LSB 15 +#define CSR_ACSMLONGBUBBLE26_MASK BIT(15) +/* CSR_ACSMSEQ3X27 */ +#define CSR_ACSMSEQ3X27_LSB 0 +#define CSR_ACSMSEQ3X27_MASK GENMASK_32(15, 0) +#define CSR_ACSMCMDREPCNT27_LSB 0 +#define CSR_ACSMCMDREPCNT27_MASK GENMASK_32(7, 0) +#define CSR_ACSMADRADV27_LSB 8 +#define CSR_ACSMADRADV27_MASK GENMASK_32(9, 8) +#define CSR_ACSMBNKADV27_LSB 10 +#define CSR_ACSMBNKADV27_MASK GENMASK_32(11, 10) +#define CSR_ACSMADRSELLOAD27_LSB 12 +#define CSR_ACSMADRSELLOAD27_MASK GENMASK_32(13, 12) +#define CSR_ACSMBNKSELLOAD27_LSB 14 +#define CSR_ACSMBNKSELLOAD27_MASK BIT(14) +#define CSR_ACSMLONGBUBBLE27_LSB 15 +#define CSR_ACSMLONGBUBBLE27_MASK BIT(15) +/* CSR_ACSMSEQ3X28 */ +#define CSR_ACSMSEQ3X28_LSB 0 +#define CSR_ACSMSEQ3X28_MASK GENMASK_32(15, 0) +#define CSR_ACSMCMDREPCNT28_LSB 0 +#define CSR_ACSMCMDREPCNT28_MASK GENMASK_32(7, 0) +#define CSR_ACSMADRADV28_LSB 8 +#define CSR_ACSMADRADV28_MASK GENMASK_32(9, 8) +#define CSR_ACSMBNKADV28_LSB 10 +#define CSR_ACSMBNKADV28_MASK GENMASK_32(11, 10) +#define CSR_ACSMADRSELLOAD28_LSB 12 +#define CSR_ACSMADRSELLOAD28_MASK GENMASK_32(13, 12) +#define CSR_ACSMBNKSELLOAD28_LSB 14 +#define CSR_ACSMBNKSELLOAD28_MASK BIT(14) +#define CSR_ACSMLONGBUBBLE28_LSB 15 +#define CSR_ACSMLONGBUBBLE28_MASK BIT(15) +/* CSR_ACSMSEQ3X29 */ +#define CSR_ACSMSEQ3X29_LSB 0 +#define CSR_ACSMSEQ3X29_MASK GENMASK_32(15, 0) +#define CSR_ACSMCMDREPCNT29_LSB 0 +#define CSR_ACSMCMDREPCNT29_MASK GENMASK_32(7, 0) +#define CSR_ACSMADRADV29_LSB 8 +#define CSR_ACSMADRADV29_MASK GENMASK_32(9, 8) +#define CSR_ACSMBNKADV29_LSB 10 +#define CSR_ACSMBNKADV29_MASK GENMASK_32(11, 10) +#define CSR_ACSMADRSELLOAD29_LSB 12 +#define CSR_ACSMADRSELLOAD29_MASK GENMASK_32(13, 12) +#define CSR_ACSMBNKSELLOAD29_LSB 14 +#define CSR_ACSMBNKSELLOAD29_MASK BIT(14) +#define CSR_ACSMLONGBUBBLE29_LSB 15 +#define CSR_ACSMLONGBUBBLE29_MASK BIT(15) +/* CSR_ACSMSEQ3X30 */ +#define CSR_ACSMSEQ3X30_LSB 0 +#define CSR_ACSMSEQ3X30_MASK GENMASK_32(15, 0) +#define CSR_ACSMCMDREPCNT30_LSB 0 +#define CSR_ACSMCMDREPCNT30_MASK GENMASK_32(7, 0) +#define CSR_ACSMADRADV30_LSB 8 +#define CSR_ACSMADRADV30_MASK GENMASK_32(9, 8) +#define CSR_ACSMBNKADV30_LSB 10 +#define CSR_ACSMBNKADV30_MASK GENMASK_32(11, 10) +#define CSR_ACSMADRSELLOAD30_LSB 12 +#define CSR_ACSMADRSELLOAD30_MASK GENMASK_32(13, 12) +#define CSR_ACSMBNKSELLOAD30_LSB 14 +#define CSR_ACSMBNKSELLOAD30_MASK BIT(14) +#define CSR_ACSMLONGBUBBLE30_LSB 15 +#define CSR_ACSMLONGBUBBLE30_MASK BIT(15) +/* CSR_ACSMSEQ3X31 */ +#define CSR_ACSMSEQ3X31_LSB 0 +#define CSR_ACSMSEQ3X31_MASK GENMASK_32(15, 0) +#define CSR_ACSMCMDREPCNT31_LSB 0 +#define CSR_ACSMCMDREPCNT31_MASK GENMASK_32(7, 0) +#define CSR_ACSMADRADV31_LSB 8 +#define CSR_ACSMADRADV31_MASK GENMASK_32(9, 8) +#define CSR_ACSMBNKADV31_LSB 10 +#define CSR_ACSMBNKADV31_MASK GENMASK_32(11, 10) +#define CSR_ACSMADRSELLOAD31_LSB 12 +#define CSR_ACSMADRSELLOAD31_MASK GENMASK_32(13, 12) +#define CSR_ACSMBNKSELLOAD31_LSB 14 +#define CSR_ACSMBNKSELLOAD31_MASK BIT(14) +#define CSR_ACSMLONGBUBBLE31_LSB 15 +#define CSR_ACSMLONGBUBBLE31_MASK BIT(15) +/* CSR_ACSMPLAYBACK0X0 */ +#define CSR_ACSMPLAYBACK0X0_LSB 0 +#define CSR_ACSMPLAYBACK0X0_MASK GENMASK_32(11, 0) +/* CSR_ACSMPLAYBACK1X0 */ +#define CSR_ACSMPLAYBACK1X0_LSB 0 +#define CSR_ACSMPLAYBACK1X0_MASK GENMASK_32(11, 0) +/* CSR_ACSMPLAYBACK0X1 */ +#define CSR_ACSMPLAYBACK0X1_LSB 0 +#define CSR_ACSMPLAYBACK0X1_MASK GENMASK_32(11, 0) +/* CSR_ACSMPLAYBACK1X1 */ +#define CSR_ACSMPLAYBACK1X1_LSB 0 +#define CSR_ACSMPLAYBACK1X1_MASK GENMASK_32(11, 0) +/* CSR_ACSMPLAYBACK0X2 */ +#define CSR_ACSMPLAYBACK0X2_LSB 0 +#define CSR_ACSMPLAYBACK0X2_MASK GENMASK_32(11, 0) +/* CSR_ACSMPLAYBACK1X2 */ +#define CSR_ACSMPLAYBACK1X2_LSB 0 +#define CSR_ACSMPLAYBACK1X2_MASK GENMASK_32(11, 0) +/* CSR_ACSMPLAYBACK0X3 */ +#define CSR_ACSMPLAYBACK0X3_LSB 0 +#define CSR_ACSMPLAYBACK0X3_MASK GENMASK_32(11, 0) +/* CSR_ACSMPLAYBACK1X3 */ +#define CSR_ACSMPLAYBACK1X3_LSB 0 +#define CSR_ACSMPLAYBACK1X3_MASK GENMASK_32(11, 0) +/* CSR_ACSMPLAYBACK0X4 */ +#define CSR_ACSMPLAYBACK0X4_LSB 0 +#define CSR_ACSMPLAYBACK0X4_MASK GENMASK_32(11, 0) +/* CSR_ACSMPLAYBACK1X4 */ +#define CSR_ACSMPLAYBACK1X4_LSB 0 +#define CSR_ACSMPLAYBACK1X4_MASK GENMASK_32(11, 0) +/* CSR_ACSMPLAYBACK0X5 */ +#define CSR_ACSMPLAYBACK0X5_LSB 0 +#define CSR_ACSMPLAYBACK0X5_MASK GENMASK_32(11, 0) +/* CSR_ACSMPLAYBACK1X5 */ +#define CSR_ACSMPLAYBACK1X5_LSB 0 +#define CSR_ACSMPLAYBACK1X5_MASK GENMASK_32(11, 0) +/* CSR_ACSMPLAYBACK0X6 */ +#define CSR_ACSMPLAYBACK0X6_LSB 0 +#define CSR_ACSMPLAYBACK0X6_MASK GENMASK_32(11, 0) +/* CSR_ACSMPLAYBACK1X6 */ +#define CSR_ACSMPLAYBACK1X6_LSB 0 +#define CSR_ACSMPLAYBACK1X6_MASK GENMASK_32(11, 0) +/* CSR_ACSMPLAYBACK0X7 */ +#define CSR_ACSMPLAYBACK0X7_LSB 0 +#define CSR_ACSMPLAYBACK0X7_MASK GENMASK_32(11, 0) +/* CSR_ACSMPLAYBACK1X7 */ +#define CSR_ACSMPLAYBACK1X7_LSB 0 +#define CSR_ACSMPLAYBACK1X7_MASK GENMASK_32(11, 0) +/* CSR_ACSMPSTATEOVREN */ +#define CSR_ACSMPSTATEOVREN_LSB 0 +#define CSR_ACSMPSTATEOVREN_MASK BIT(0) +/* CSR_ACSMPSTATEOVRVAL */ +#define CSR_ACSMPSTATEOVRVAL_LSB 0 +#define CSR_ACSMPSTATEOVRVAL_MASK GENMASK_32(3, 0) +/* CSR_ACSMCTRL23 */ +#define CSR_ACSMCTRL23_LSB 0 +#define CSR_ACSMCTRL23_MASK GENMASK_32(12, 0) +#define CSR_ACSMCSMASK_LSB 0 +#define CSR_ACSMCSMASK_MASK GENMASK_32(7, 0) +#define CSR_ACSMCSMODE_LSB 8 +#define CSR_ACSMCSMODE_MASK BIT(8) +#define CSR_ACSMPARMASK_LSB 9 +#define CSR_ACSMPARMASK_MASK GENMASK_32(12, 9) +/* CSR_ACSMCKEVAL */ +#define CSR_ACSMCKEVAL_LSB 0 +#define CSR_ACSMCKEVAL_MASK GENMASK_32(3, 0) +/* CSR_LOWSPEEDCLOCKDIVIDER */ +#define CSR_LOWSPEEDCLOCKDIVIDER_LSB 0 +#define CSR_LOWSPEEDCLOCKDIVIDER_MASK GENMASK_32(5, 0) +/* CSR_ACSMCSMAPCTRL0 */ +#define CSR_ACSMCSMAPCTRL0_LSB 0 +#define CSR_ACSMCSMAPCTRL0_MASK GENMASK_32(14, 0) +#define CSR_ACSMCSMAP0_LSB 0 +#define CSR_ACSMCSMAP0_MASK GENMASK_32(7, 0) +#define CSR_ACSMDESTMAP0_LSB 8 +#define CSR_ACSMDESTMAP0_MASK GENMASK_32(11, 8) +#define CSR_ACSMODTMAP0_LSB 12 +#define CSR_ACSMODTMAP0_MASK GENMASK_32(14, 12) +/* CSR_ACSMCSMAPCTRL1 */ +#define CSR_ACSMCSMAPCTRL1_LSB 0 +#define CSR_ACSMCSMAPCTRL1_MASK GENMASK_32(14, 0) +#define CSR_ACSMCSMAP1_LSB 0 +#define CSR_ACSMCSMAP1_MASK GENMASK_32(7, 0) +#define CSR_ACSMDESTMAP1_LSB 8 +#define CSR_ACSMDESTMAP1_MASK GENMASK_32(11, 8) +#define CSR_ACSMODTMAP1_LSB 12 +#define CSR_ACSMODTMAP1_MASK GENMASK_32(14, 12) +/* CSR_ACSMCSMAPCTRL2 */ +#define CSR_ACSMCSMAPCTRL2_LSB 0 +#define CSR_ACSMCSMAPCTRL2_MASK GENMASK_32(14, 0) +#define CSR_ACSMCSMAP2_LSB 0 +#define CSR_ACSMCSMAP2_MASK GENMASK_32(7, 0) +#define CSR_ACSMDESTMAP2_LSB 8 +#define CSR_ACSMDESTMAP2_MASK GENMASK_32(11, 8) +#define CSR_ACSMODTMAP2_LSB 12 +#define CSR_ACSMODTMAP2_MASK GENMASK_32(14, 12) +/* CSR_ACSMCSMAPCTRL3 */ +#define CSR_ACSMCSMAPCTRL3_LSB 0 +#define CSR_ACSMCSMAPCTRL3_MASK GENMASK_32(14, 0) +#define CSR_ACSMCSMAP3_LSB 0 +#define CSR_ACSMCSMAP3_MASK GENMASK_32(7, 0) +#define CSR_ACSMDESTMAP3_LSB 8 +#define CSR_ACSMDESTMAP3_MASK GENMASK_32(11, 8) +#define CSR_ACSMODTMAP3_LSB 12 +#define CSR_ACSMODTMAP3_MASK GENMASK_32(14, 12) +/* CSR_ACSMCSMAPCTRL4 */ +#define CSR_ACSMCSMAPCTRL4_LSB 0 +#define CSR_ACSMCSMAPCTRL4_MASK GENMASK_32(14, 0) +#define CSR_ACSMCSMAP4_LSB 0 +#define CSR_ACSMCSMAP4_MASK GENMASK_32(7, 0) +#define CSR_ACSMDESTMAP4_LSB 8 +#define CSR_ACSMDESTMAP4_MASK GENMASK_32(11, 8) +#define CSR_ACSMODTMAP4_LSB 12 +#define CSR_ACSMODTMAP4_MASK GENMASK_32(14, 12) +/* CSR_ACSMCSMAPCTRL5 */ +#define CSR_ACSMCSMAPCTRL5_LSB 0 +#define CSR_ACSMCSMAPCTRL5_MASK GENMASK_32(14, 0) +#define CSR_ACSMCSMAP5_LSB 0 +#define CSR_ACSMCSMAP5_MASK GENMASK_32(7, 0) +#define CSR_ACSMDESTMAP5_LSB 8 +#define CSR_ACSMDESTMAP5_MASK GENMASK_32(11, 8) +#define CSR_ACSMODTMAP5_LSB 12 +#define CSR_ACSMODTMAP5_MASK GENMASK_32(14, 12) +/* CSR_ACSMCSMAPCTRL6 */ +#define CSR_ACSMCSMAPCTRL6_LSB 0 +#define CSR_ACSMCSMAPCTRL6_MASK GENMASK_32(14, 0) +#define CSR_ACSMCSMAP6_LSB 0 +#define CSR_ACSMCSMAP6_MASK GENMASK_32(7, 0) +#define CSR_ACSMDESTMAP6_LSB 8 +#define CSR_ACSMDESTMAP6_MASK GENMASK_32(11, 8) +#define CSR_ACSMODTMAP6_LSB 12 +#define CSR_ACSMODTMAP6_MASK GENMASK_32(14, 12) +/* CSR_ACSMCSMAPCTRL7 */ +#define CSR_ACSMCSMAPCTRL7_LSB 0 +#define CSR_ACSMCSMAPCTRL7_MASK GENMASK_32(14, 0) +#define CSR_ACSMCSMAP7_LSB 0 +#define CSR_ACSMCSMAP7_MASK GENMASK_32(7, 0) +#define CSR_ACSMDESTMAP7_LSB 8 +#define CSR_ACSMDESTMAP7_MASK GENMASK_32(11, 8) +#define CSR_ACSMODTMAP7_LSB 12 +#define CSR_ACSMODTMAP7_MASK GENMASK_32(14, 12) +/* CSR_ACSMCSMAPCTRL8 */ +#define CSR_ACSMCSMAPCTRL8_LSB 0 +#define CSR_ACSMCSMAPCTRL8_MASK GENMASK_32(14, 0) +#define CSR_ACSMCSMAP8_LSB 0 +#define CSR_ACSMCSMAP8_MASK GENMASK_32(7, 0) +#define CSR_ACSMDESTMAP8_LSB 8 +#define CSR_ACSMDESTMAP8_MASK GENMASK_32(11, 8) +#define CSR_ACSMODTMAP8_LSB 12 +#define CSR_ACSMODTMAP8_MASK GENMASK_32(14, 12) +/* CSR_ACSMCSMAPCTRL9 */ +#define CSR_ACSMCSMAPCTRL9_LSB 0 +#define CSR_ACSMCSMAPCTRL9_MASK GENMASK_32(14, 0) +#define CSR_ACSMCSMAP9_LSB 0 +#define CSR_ACSMCSMAP9_MASK GENMASK_32(7, 0) +#define CSR_ACSMDESTMAP9_LSB 8 +#define CSR_ACSMDESTMAP9_MASK GENMASK_32(11, 8) +#define CSR_ACSMODTMAP9_LSB 12 +#define CSR_ACSMODTMAP9_MASK GENMASK_32(14, 12) +/* CSR_ACSMCSMAPCTRL10 */ +#define CSR_ACSMCSMAPCTRL10_LSB 0 +#define CSR_ACSMCSMAPCTRL10_MASK GENMASK_32(14, 0) +#define CSR_ACSMCSMAP10_LSB 0 +#define CSR_ACSMCSMAP10_MASK GENMASK_32(7, 0) +#define CSR_ACSMDESTMAP10_LSB 8 +#define CSR_ACSMDESTMAP10_MASK GENMASK_32(11, 8) +#define CSR_ACSMODTMAP10_LSB 12 +#define CSR_ACSMODTMAP10_MASK GENMASK_32(14, 12) +/* CSR_ACSMCSMAPCTRL11 */ +#define CSR_ACSMCSMAPCTRL11_LSB 0 +#define CSR_ACSMCSMAPCTRL11_MASK GENMASK_32(14, 0) +#define CSR_ACSMCSMAP11_LSB 0 +#define CSR_ACSMCSMAP11_MASK GENMASK_32(7, 0) +#define CSR_ACSMDESTMAP11_LSB 8 +#define CSR_ACSMDESTMAP11_MASK GENMASK_32(11, 8) +#define CSR_ACSMODTMAP11_LSB 12 +#define CSR_ACSMODTMAP11_MASK GENMASK_32(14, 12) +/* CSR_ACSMCSMAPCTRL12 */ +#define CSR_ACSMCSMAPCTRL12_LSB 0 +#define CSR_ACSMCSMAPCTRL12_MASK GENMASK_32(14, 0) +#define CSR_ACSMCSMAP12_LSB 0 +#define CSR_ACSMCSMAP12_MASK GENMASK_32(7, 0) +#define CSR_ACSMDESTMAP12_LSB 8 +#define CSR_ACSMDESTMAP12_MASK GENMASK_32(11, 8) +#define CSR_ACSMODTMAP12_LSB 12 +#define CSR_ACSMODTMAP12_MASK GENMASK_32(14, 12) +/* CSR_ACSMCSMAPCTRL13 */ +#define CSR_ACSMCSMAPCTRL13_LSB 0 +#define CSR_ACSMCSMAPCTRL13_MASK GENMASK_32(14, 0) +#define CSR_ACSMCSMAP13_LSB 0 +#define CSR_ACSMCSMAP13_MASK GENMASK_32(7, 0) +#define CSR_ACSMDESTMAP13_LSB 8 +#define CSR_ACSMDESTMAP13_MASK GENMASK_32(11, 8) +#define CSR_ACSMODTMAP13_LSB 12 +#define CSR_ACSMODTMAP13_MASK GENMASK_32(14, 12) +/* CSR_ACSMCSMAPCTRL14 */ +#define CSR_ACSMCSMAPCTRL14_LSB 0 +#define CSR_ACSMCSMAPCTRL14_MASK GENMASK_32(14, 0) +#define CSR_ACSMCSMAP14_LSB 0 +#define CSR_ACSMCSMAP14_MASK GENMASK_32(7, 0) +#define CSR_ACSMDESTMAP14_LSB 8 +#define CSR_ACSMDESTMAP14_MASK GENMASK_32(11, 8) +#define CSR_ACSMODTMAP14_LSB 12 +#define CSR_ACSMODTMAP14_MASK GENMASK_32(14, 12) +/* CSR_ACSMCSMAPCTRL15 */ +#define CSR_ACSMCSMAPCTRL15_LSB 0 +#define CSR_ACSMCSMAPCTRL15_MASK GENMASK_32(14, 0) +#define CSR_ACSMCSMAP15_LSB 0 +#define CSR_ACSMCSMAP15_MASK GENMASK_32(7, 0) +#define CSR_ACSMDESTMAP15_LSB 8 +#define CSR_ACSMDESTMAP15_MASK GENMASK_32(11, 8) +#define CSR_ACSMODTMAP15_LSB 12 +#define CSR_ACSMODTMAP15_MASK GENMASK_32(14, 12) +/* CSR_ACSMODTCTRL0 */ +#define CSR_ACSMODTCTRL0_LSB 0 +#define CSR_ACSMODTCTRL0_MASK GENMASK_32(7, 0) +#define CSR_ACSMODTWRPATCS0_LSB 0 +#define CSR_ACSMODTWRPATCS0_MASK GENMASK_32(3, 0) +#define CSR_ACSMODTRDPATCS0_LSB 4 +#define CSR_ACSMODTRDPATCS0_MASK GENMASK_32(7, 4) +/* CSR_ACSMODTCTRL1 */ +#define CSR_ACSMODTCTRL1_LSB 0 +#define CSR_ACSMODTCTRL1_MASK GENMASK_32(7, 0) +#define CSR_ACSMODTWRPATCS1_LSB 0 +#define CSR_ACSMODTWRPATCS1_MASK GENMASK_32(3, 0) +#define CSR_ACSMODTRDPATCS1_LSB 4 +#define CSR_ACSMODTRDPATCS1_MASK GENMASK_32(7, 4) +/* CSR_ACSMODTCTRL2 */ +#define CSR_ACSMODTCTRL2_LSB 0 +#define CSR_ACSMODTCTRL2_MASK GENMASK_32(7, 0) +#define CSR_ACSMODTWRPATCS2_LSB 0 +#define CSR_ACSMODTWRPATCS2_MASK GENMASK_32(3, 0) +#define CSR_ACSMODTRDPATCS2_LSB 4 +#define CSR_ACSMODTRDPATCS2_MASK GENMASK_32(7, 4) +/* CSR_ACSMODTCTRL3 */ +#define CSR_ACSMODTCTRL3_LSB 0 +#define CSR_ACSMODTCTRL3_MASK GENMASK_32(7, 0) +#define CSR_ACSMODTWRPATCS3_LSB 0 +#define CSR_ACSMODTWRPATCS3_MASK GENMASK_32(3, 0) +#define CSR_ACSMODTRDPATCS3_LSB 4 +#define CSR_ACSMODTRDPATCS3_MASK GENMASK_32(7, 4) +/* CSR_ACSMODTCTRL4 */ +#define CSR_ACSMODTCTRL4_LSB 0 +#define CSR_ACSMODTCTRL4_MASK GENMASK_32(7, 0) +#define CSR_ACSMODTWRPATCS4_LSB 0 +#define CSR_ACSMODTWRPATCS4_MASK GENMASK_32(3, 0) +#define CSR_ACSMODTRDPATCS4_LSB 4 +#define CSR_ACSMODTRDPATCS4_MASK GENMASK_32(7, 4) +/* CSR_ACSMODTCTRL5 */ +#define CSR_ACSMODTCTRL5_LSB 0 +#define CSR_ACSMODTCTRL5_MASK GENMASK_32(7, 0) +#define CSR_ACSMODTWRPATCS5_LSB 0 +#define CSR_ACSMODTWRPATCS5_MASK GENMASK_32(3, 0) +#define CSR_ACSMODTRDPATCS5_LSB 4 +#define CSR_ACSMODTRDPATCS5_MASK GENMASK_32(7, 4) +/* CSR_ACSMODTCTRL6 */ +#define CSR_ACSMODTCTRL6_LSB 0 +#define CSR_ACSMODTCTRL6_MASK GENMASK_32(7, 0) +#define CSR_ACSMODTWRPATCS6_LSB 0 +#define CSR_ACSMODTWRPATCS6_MASK GENMASK_32(3, 0) +#define CSR_ACSMODTRDPATCS6_LSB 4 +#define CSR_ACSMODTRDPATCS6_MASK GENMASK_32(7, 4) +/* CSR_ACSMODTCTRL7 */ +#define CSR_ACSMODTCTRL7_LSB 0 +#define CSR_ACSMODTCTRL7_MASK GENMASK_32(7, 0) +#define CSR_ACSMODTWRPATCS7_LSB 0 +#define CSR_ACSMODTWRPATCS7_MASK GENMASK_32(3, 0) +#define CSR_ACSMODTRDPATCS7_LSB 4 +#define CSR_ACSMODTRDPATCS7_MASK GENMASK_32(7, 4) +/* CSR_ACSMODTCTRL8 */ +#define CSR_ACSMODTCTRL8_LSB 0 +#define CSR_ACSMODTCTRL8_MASK GENMASK_32(15, 0) +#define CSR_ACSMODTWRDURCTRL_LSB 0 +#define CSR_ACSMODTWRDURCTRL_MASK GENMASK_32(3, 0) +#define CSR_ACSMODTRDDURCTRL_LSB 4 +#define CSR_ACSMODTRDDURCTRL_MASK GENMASK_32(7, 4) +#define CSR_ACSMODTWRSTRTCTRL_LSB 8 +#define CSR_ACSMODTWRSTRTCTRL_MASK GENMASK_32(11, 8) +#define CSR_ACSMODTRDSTRTCTRL_LSB 12 +#define CSR_ACSMODTRDSTRTCTRL_MASK GENMASK_32(15, 12) +/* CSR_ACSMCTRL16 */ +#define CSR_ACSMCTRL16_LSB 0 +#define CSR_ACSMCTRL16_MASK GENMASK_32(15, 0) +#define CSR_ACSMDDRADRUP_LSB 0 +#define CSR_ACSMDDRADRUP_MASK GENMASK_32(3, 0) +#define CSR_ACSMHIGHADDR_LSB 4 +#define CSR_ACSMHIGHADDR_MASK BIT(4) +#define CSR_ACSMADR13PLUGHOLE_LSB 5 +#define CSR_ACSMADR13PLUGHOLE_MASK BIT(5) +#define CSR_ACSMCTRL16RSVD_LSB 6 +#define CSR_ACSMCTRL16RSVD_MASK BIT(6) +#define CSR_ACSMWRTLVLODTCTRL_LSB 7 +#define CSR_ACSMWRTLVLODTCTRL_MASK BIT(7) +#define CSR_ACSMWRTLVLODT_LSB 8 +#define CSR_ACSMWRTLVLODT_MASK GENMASK_32(11, 8) +#define CSR_ACSM2TGRPINHIBIT_LSB 12 +#define CSR_ACSM2TGRPINHIBIT_MASK GENMASK_32(15, 12) +/* CSR_LOWSPEEDCLOCKSTOPVAL */ +#define CSR_LOWSPEEDCLOCKSTOPVAL_LSB 0 +#define CSR_LOWSPEEDCLOCKSTOPVAL_MASK BIT(0) +/* CSR_ACSMCTRL18 */ +#define CSR_ACSMCTRL18_LSB 0 +#define CSR_ACSMCTRL18_MASK GENMASK_32(1, 0) +#define CSR_ACSMLOCALDONE_LSB 0 +#define CSR_ACSMLOCALDONE_MASK BIT(0) +#define CSR_ACSMSTOPONERRASRTD_LSB 1 +#define CSR_ACSMSTOPONERRASRTD_MASK BIT(1) +/* CSR_ACSMCTRL19 */ +#define CSR_ACSMCTRL19_LSB 0 +#define CSR_ACSMCTRL19_MASK GENMASK_32(2, 0) +#define CSR_ACSMVISSEL_LSB 0 +#define CSR_ACSMVISSEL_MASK GENMASK_32(2, 0) +/* CSR_ACSMCTRL20 */ +#define CSR_ACSMCTRL20_LSB 0 +#define CSR_ACSMCTRL20_MASK GENMASK_32(15, 0) +#define CSR_ACSMVISVAL_LSB 0 +#define CSR_ACSMVISVAL_MASK GENMASK_32(15, 0) +/* CSR_ACSMCTRL21 */ +#define CSR_ACSMCTRL21_LSB 0 +#define CSR_ACSMCTRL21_MASK GENMASK_32(11, 0) +#define CSR_ACSMMAPDIMMCS0_LSB 0 +#define CSR_ACSMMAPDIMMCS0_MASK GENMASK_32(2, 0) +#define CSR_ACSMMAPDIMMCS1_LSB 3 +#define CSR_ACSMMAPDIMMCS1_MASK GENMASK_32(5, 3) +#define CSR_ACSMMAPDIMMCS2_LSB 6 +#define CSR_ACSMMAPDIMMCS2_MASK GENMASK_32(8, 6) +#define CSR_ACSMMAPDIMMCS3_LSB 9 +#define CSR_ACSMMAPDIMMCS3_MASK GENMASK_32(11, 9) +/* CSR_ACSMCTRL22 */ +#define CSR_ACSMCTRL22_LSB 0 +#define CSR_ACSMCTRL22_MASK GENMASK_32(11, 0) +#define CSR_ACSMMAPDIMMCS4_LSB 0 +#define CSR_ACSMMAPDIMMCS4_MASK GENMASK_32(2, 0) +#define CSR_ACSMMAPDIMMCS5_LSB 3 +#define CSR_ACSMMAPDIMMCS5_MASK GENMASK_32(5, 3) +#define CSR_ACSMMAPDIMMCS6_LSB 6 +#define CSR_ACSMMAPDIMMCS6_MASK GENMASK_32(8, 6) +#define CSR_ACSMMAPDIMMCS7_LSB 9 +#define CSR_ACSMMAPDIMMCS7_MASK GENMASK_32(11, 9) +/* CSR_ACSMCTRL0 */ +#define CSR_ACSMCTRL0_LSB 0 +#define CSR_ACSMCTRL0_MASK GENMASK_32(15, 0) +#define CSR_ACSMRSVDCTRL00_LSB 0 +#define CSR_ACSMRSVDCTRL00_MASK BIT(0) +#define CSR_ACSMDYNBLMODE_LSB 1 +#define CSR_ACSMDYNBLMODE_MASK BIT(1) +#define CSR_ACSMBURSTLEN_LSB 2 +#define CSR_ACSMBURSTLEN_MASK BIT(2) +#define CSR_ACSMINFLOOP_LSB 3 +#define CSR_ACSMINFLOOP_MASK BIT(3) +#define CSR_ACSMRXVALMODE_LSB 4 +#define CSR_ACSMRXVALMODE_MASK BIT(4) +#define CSR_ACSMSTPONERRMODE_LSB 5 +#define CSR_ACSMSTPONERRMODE_MASK BIT(5) +#define CSR_ACSM2TMODE_LSB 6 +#define CSR_ACSM2TMODE_MASK BIT(6) +#define CSR_ACSMTRAINSOEMODE_LSB 7 +#define CSR_ACSMTRAINSOEMODE_MASK BIT(7) +#define CSR_ACSMGATEDDRCMD_LSB 8 +#define CSR_ACSMGATEDDRCMD_MASK BIT(8) +#define CSR_ACSMGEARDOWNMODE_LSB 9 +#define CSR_ACSMGEARDOWNMODE_MASK BIT(9) +#define CSR_ACSMGEARDOWNPHASE_LSB 10 +#define CSR_ACSMGEARDOWNPHASE_MASK BIT(10) +#define CSR_ACSMGEARDOWNSYNC_LSB 11 +#define CSR_ACSMGEARDOWNSYNC_MASK BIT(11) +#define CSR_ACSMCAPRBSMODE_LSB 12 +#define CSR_ACSMCAPRBSMODE_MASK BIT(12) +#define CSR_ACSMGATERXFIFOWRITE_LSB 13 +#define CSR_ACSMGATERXFIFOWRITE_MASK BIT(13) +#define CSR_ACSMPARMODE_LSB 14 +#define CSR_ACSMPARMODE_MASK BIT(14) +#define CSR_ACSMTDSMODE_LSB 15 +#define CSR_ACSMTDSMODE_MASK BIT(15) +/* CSR_ACSMCTRL1 */ +#define CSR_ACSMCTRL1_LSB 0 +#define CSR_ACSMCTRL1_MASK GENMASK_32(15, 0) +#define CSR_ACSMREPCNT_LSB 0 +#define CSR_ACSMREPCNT_MASK GENMASK_32(15, 0) +/* CSR_ACSMCTRL2 */ +#define CSR_ACSMCTRL2_LSB 0 +#define CSR_ACSMCTRL2_MASK GENMASK_32(4, 0) +#define CSR_ACSMSTARTPTR_LSB 0 +#define CSR_ACSMSTARTPTR_MASK GENMASK_32(4, 0) +/* CSR_ACSMCTRL3 */ +#define CSR_ACSMCTRL3_LSB 0 +#define CSR_ACSMCTRL3_MASK GENMASK_32(4, 0) +#define CSR_ACSMLOOPPTR_LSB 0 +#define CSR_ACSMLOOPPTR_MASK GENMASK_32(4, 0) +/* CSR_ACSMCTRL4 */ +#define CSR_ACSMCTRL4_LSB 0 +#define CSR_ACSMCTRL4_MASK GENMASK_32(4, 0) +#define CSR_ACSMENDPTR_LSB 0 +#define CSR_ACSMENDPTR_MASK GENMASK_32(4, 0) +/* CSR_ACSMCTRL5 */ +#define CSR_ACSMCTRL5_LSB 0 +#define CSR_ACSMCTRL5_MASK GENMASK_32(13, 0) +#define CSR_ACSMMXRDLAT_LSB 0 +#define CSR_ACSMMXRDLAT_MASK GENMASK_32(7, 0) +#define CSR_ACSMRCASLAT_LSB 8 +#define CSR_ACSMRCASLAT_MASK GENMASK_32(13, 8) +/* CSR_ACSMCTRL6 */ +#define CSR_ACSMCTRL6_LSB 0 +#define CSR_ACSMCTRL6_MASK GENMASK_32(10, 0) +#define CSR_ACSMWCASLAT_LSB 0 +#define CSR_ACSMWCASLAT_MASK GENMASK_32(5, 0) +#define CSR_ACSMWRRSVD_LSB 6 +#define CSR_ACSMWRRSVD_MASK GENMASK_32(7, 6) +#define CSR_ACSMWRDATLAT_LSB 8 +#define CSR_ACSMWRDATLAT_MASK GENMASK_32(10, 8) +/* CSR_ACSMCTRL7 */ +#define CSR_ACSMCTRL7_LSB 0 +#define CSR_ACSMCTRL7_MASK GENMASK_32(15, 0) +#define CSR_ACSMRASPCFG_LSB 0 +#define CSR_ACSMRASPCFG_MASK GENMASK_32(15, 0) +/* CSR_ACSMCTRL8 */ +#define CSR_ACSMCTRL8_LSB 0 +#define CSR_ACSMCTRL8_MASK GENMASK_32(15, 0) +#define CSR_ACSMRASPSEED_LSB 0 +#define CSR_ACSMRASPSEED_MASK GENMASK_32(15, 0) +/* CSR_ACSMCTRL9 */ +#define CSR_ACSMCTRL9_LSB 0 +#define CSR_ACSMCTRL9_MASK GENMASK_32(15, 0) +#define CSR_ACSMCASPCFG_LSB 0 +#define CSR_ACSMCASPCFG_MASK GENMASK_32(15, 0) +/* CSR_ACSMCTRL10 */ +#define CSR_ACSMCTRL10_LSB 0 +#define CSR_ACSMCTRL10_MASK GENMASK_32(15, 0) +#define CSR_ACSMCASPSEED_LSB 0 +#define CSR_ACSMCASPSEED_MASK GENMASK_32(15, 0) +/* CSR_ACSMCTRL11 */ +#define CSR_ACSMCTRL11_LSB 0 +#define CSR_ACSMCTRL11_MASK GENMASK_32(15, 0) +#define CSR_ACSMRASADRINC_LSB 0 +#define CSR_ACSMRASADRINC_MASK GENMASK_32(7, 0) +#define CSR_ACSMCASADRINC_LSB 8 +#define CSR_ACSMCASADRINC_MASK GENMASK_32(15, 8) +/* CSR_ACSMCTRL12 */ +#define CSR_ACSMCTRL12_LSB 0 +#define CSR_ACSMCTRL12_MASK GENMASK_32(11, 0) +#define CSR_ACSMBNKPCFG_LSB 0 +#define CSR_ACSMBNKPCFG_MASK GENMASK_32(3, 0) +#define CSR_ACSMBNKPSEED_LSB 4 +#define CSR_ACSMBNKPSEED_MASK GENMASK_32(7, 4) +#define CSR_ACSMBNKADRINC_LSB 8 +#define CSR_ACSMBNKADRINC_MASK GENMASK_32(11, 8) +/* CSR_ACSMCTRL13 */ +#define CSR_ACSMCTRL13_LSB 0 +#define CSR_ACSMCTRL13_MASK GENMASK_32(3, 0) +#define CSR_ACSMCKEENB_LSB 0 +#define CSR_ACSMCKEENB_MASK GENMASK_32(3, 0) +/* CSR_ACSMCTRL14 */ +#define CSR_ACSMCTRL14_LSB 0 +#define CSR_ACSMCTRL14_MASK GENMASK_32(3, 0) +#define CSR_ACSMRASPCFGUP_LSB 0 +#define CSR_ACSMRASPCFGUP_MASK GENMASK_32(3, 0) +/* CSR_ACSMCTRL15 */ +#define CSR_ACSMCTRL15_LSB 0 +#define CSR_ACSMCTRL15_MASK GENMASK_32(3, 0) +#define CSR_ACSMRASPSEEDUP_LSB 0 +#define CSR_ACSMRASPSEEDUP_MASK GENMASK_32(3, 0) + +/* PPGC0 register offsets */ +/* CSR_PPGCCTRL1 */ +#define CSR_PPGCCTRL1_LSB 1 +#define CSR_PPGCCTRL1_MASK GENMASK_32(4, 1) +#define CSR_HWTTXDBIEN_LSB 1 +#define CSR_HWTTXDBIEN_MASK BIT(1) +#define CSR_HWTRXDBIEN_LSB 2 +#define CSR_HWTRXDBIEN_MASK BIT(2) +#define CSR_HWTTXDMDBIVAL_LSB 3 +#define CSR_HWTTXDMDBIVAL_MASK BIT(3) +#define CSR_HWTTXDMDBISEL_LSB 4 +#define CSR_HWTTXDMDBISEL_MASK BIT(4) +/* CSR_PPGCLANE2CRCINMAP0 */ +#define CSR_PPGCLANE2CRCINMAP0_LSB 0 +#define CSR_PPGCLANE2CRCINMAP0_MASK GENMASK_32(11, 0) +#define CSR_PPGCCRCLANEMAP0_LSB 0 +#define CSR_PPGCCRCLANEMAP0_MASK GENMASK_32(2, 0) +#define CSR_PPGCCRCLANEMAP1_LSB 3 +#define CSR_PPGCCRCLANEMAP1_MASK GENMASK_32(5, 3) +#define CSR_PPGCCRCLANEMAP2_LSB 6 +#define CSR_PPGCCRCLANEMAP2_MASK GENMASK_32(8, 6) +#define CSR_PPGCCRCLANEMAP3_LSB 9 +#define CSR_PPGCCRCLANEMAP3_MASK GENMASK_32(11, 9) +/* CSR_PPGCLANE2CRCINMAP1 */ +#define CSR_PPGCLANE2CRCINMAP1_LSB 0 +#define CSR_PPGCLANE2CRCINMAP1_MASK GENMASK_32(11, 0) +#define CSR_PPGCCRCLANEMAP4_LSB 0 +#define CSR_PPGCCRCLANEMAP4_MASK GENMASK_32(2, 0) +#define CSR_PPGCCRCLANEMAP5_LSB 3 +#define CSR_PPGCCRCLANEMAP5_MASK GENMASK_32(5, 3) +#define CSR_PPGCCRCLANEMAP6_LSB 6 +#define CSR_PPGCCRCLANEMAP6_MASK GENMASK_32(8, 6) +#define CSR_PPGCCRCLANEMAP7_LSB 9 +#define CSR_PPGCCRCLANEMAP7_MASK GENMASK_32(11, 9) +/* CSR_PRBSTAPDLY0 */ +#define CSR_PRBSTAPDLY0_LSB 0 +#define CSR_PRBSTAPDLY0_MASK GENMASK_32(15, 0) +/* CSR_PRBSTAPDLY1 */ +#define CSR_PRBSTAPDLY1_LSB 0 +#define CSR_PRBSTAPDLY1_MASK GENMASK_32(15, 0) +/* CSR_PRBSTAPDLY2 */ +#define CSR_PRBSTAPDLY2_LSB 0 +#define CSR_PRBSTAPDLY2_MASK GENMASK_32(15, 0) +/* CSR_PRBSTAPDLY3 */ +#define CSR_PRBSTAPDLY3_LSB 0 +#define CSR_PRBSTAPDLY3_MASK GENMASK_32(15, 0) +/* CSR_GENPRBSBYTE0 */ +#define CSR_GENPRBSBYTE0_LSB 0 +#define CSR_GENPRBSBYTE0_MASK GENMASK_32(15, 0) +/* CSR_GENPRBSBYTE1² */ +#define CSR_GENPRBSBYTE1_LSB 0 +#define CSR_GENPRBSBYTE1_MASK GENMASK_32(15, 0) +/* CSR_GENPRBSBYTE2 */ +#define CSR_GENPRBSBYTE2_LSB 0 +#define CSR_GENPRBSBYTE2_MASK GENMASK_32(15, 0) +/* CSR_GENPRBSBYTE3 */ +#define CSR_GENPRBSBYTE3_LSB 0 +#define CSR_GENPRBSBYTE3_MASK GENMASK_32(15, 0) +/* CSR_GENPRBSBYTE4 */ +#define CSR_GENPRBSBYTE4_LSB 0 +#define CSR_GENPRBSBYTE4_MASK GENMASK_32(15, 0) +/* CSR_GENPRBSBYTE5 */ +#define CSR_GENPRBSBYTE5_LSB 0 +#define CSR_GENPRBSBYTE5_MASK GENMASK_32(15, 0) +/* CSR_GENPRBSBYTE6 */ +#define CSR_GENPRBSBYTE6_LSB 0 +#define CSR_GENPRBSBYTE6_MASK GENMASK_32(15, 0) +/* CSR_GENPRBSBYTE7 */ +#define CSR_GENPRBSBYTE7_LSB 0 +#define CSR_GENPRBSBYTE7_MASK GENMASK_32(15, 0) +/* CSR_GENPRBSBYTE8 */ +#define CSR_GENPRBSBYTE8_LSB 0 +#define CSR_GENPRBSBYTE8_MASK GENMASK_32(15, 0) +/* CSR_GENPRBSBYTE9 */ +#define CSR_GENPRBSBYTE9_LSB 0 +#define CSR_GENPRBSBYTE9_MASK GENMASK_32(15, 0) +/* CSR_GENPRBSBYTE10 */ +#define CSR_GENPRBSBYTE10_LSB 0 +#define CSR_GENPRBSBYTE10_MASK GENMASK_32(15, 0) +/* CSR_GENPRBSBYTE11 */ +#define CSR_GENPRBSBYTE11_LSB 0 +#define CSR_GENPRBSBYTE11_MASK GENMASK_32(15, 0) +/* CSR_GENPRBSBYTE12 */ +#define CSR_GENPRBSBYTE12_LSB 0 +#define CSR_GENPRBSBYTE12_MASK GENMASK_32(15, 0) +/* CSR_GENPRBSBYTE13 */ +#define CSR_GENPRBSBYTE13_LSB 0 +#define CSR_GENPRBSBYTE13_MASK GENMASK_32(15, 0) +/* CSR_GENPRBSBYTE14 */ +#define CSR_GENPRBSBYTE14_LSB 0 +#define CSR_GENPRBSBYTE14_MASK GENMASK_32(15, 0) +/* CSR_GENPRBSBYTE15 */ +#define CSR_GENPRBSBYTE15_LSB 0 +#define CSR_GENPRBSBYTE15_MASK GENMASK_32(15, 0) +/* CSR_PRBSGENCTL */ +#define CSR_PRBSGENCTL_LSB 0 +#define CSR_PRBSGENCTL_MASK GENMASK_32(6, 0) +#define CSR_PPGCMODE_LSB 0 +#define CSR_PPGCMODE_MASK BIT(0) +#define CSR_PPGCDMMODE_LSB 1 +#define CSR_PPGCDMMODE_MASK BIT(1) +#define CSR_PPGCLDFFMODE_LSB 2 +#define CSR_PPGCLDFFMODE_MASK BIT(2) +#define CSR_PPGCSEL23BPRBS_LSB 3 +#define CSR_PPGCSEL23BPRBS_MASK BIT(3) +#define CSR_PPGCPATADV_LSB 4 +#define CSR_PPGCPATADV_MASK GENMASK_32(5, 4) +#define CSR_PPGCENBPATSTRESSMODE_LSB 6 +#define CSR_PPGCENBPATSTRESSMODE_MASK BIT(6) +/* CSR_PRBSGENSTATELO */ +#define CSR_PRBSGENSTATELO_LSB 0 +#define CSR_PRBSGENSTATELO_MASK GENMASK_32(15, 0) +/* CSR_PRBSGENSTATEHI */ +#define CSR_PRBSGENSTATEHI_LSB 0 +#define CSR_PRBSGENSTATEHI_MASK GENMASK_32(6, 0) +/* CSR_PRBSCHKSTATELO */ +#define CSR_PRBSCHKSTATELO_LSB 0 +#define CSR_PRBSCHKSTATELO_MASK GENMASK_32(15, 0) +/* CSR_PRBSCHKSTATEHI */ +#define CSR_PRBSCHKSTATEHI_LSB 0 +#define CSR_PRBSCHKSTATEHI_MASK GENMASK_32(6, 0) +/* CSR_PRBSGENCTL1 */ +#define CSR_PRBSGENCTL1_LSB 0 +#define CSR_PRBSGENCTL1_MASK GENMASK_32(8, 0) +#define CSR_PPGCMODELANE_LSB 0 +#define CSR_PPGCMODELANE_MASK GENMASK_32(8, 0) +/* CSR_PRBSGENCTL2 */ +#define CSR_PRBSGENCTL2_LSB 0 +#define CSR_PRBSGENCTL2_MASK GENMASK_32(15, 0) +#define CSR_PPGCMSKPERIODLIM_LSB 0 +#define CSR_PPGCMSKPERIODLIM_MASK GENMASK_32(15, 0) + +/* INITENG0 register offsets */ +/* CSR_PRESEQUENCEREG0B0S0 */ +#define CSR_PRESEQUENCEREG0B0S0_LSB 0 +#define CSR_PRESEQUENCEREG0B0S0_MASK GENMASK_32(15, 0) +/* CSR_PRESEQUENCEREG0B0S1 */ +#define CSR_PRESEQUENCEREG0B0S1_LSB 0 +#define CSR_PRESEQUENCEREG0B0S1_MASK GENMASK_32(15, 0) +/* CSR_PRESEQUENCEREG0B0S2 */ +#define CSR_PRESEQUENCEREG0B0S2_LSB 0 +#define CSR_PRESEQUENCEREG0B0S2_MASK GENMASK_32(8, 0) +/* CSR_PRESEQUENCEREG0B1S0 */ +#define CSR_PRESEQUENCEREG0B1S0_LSB 0 +#define CSR_PRESEQUENCEREG0B1S0_MASK GENMASK_32(15, 0) +/* CSR_PRESEQUENCEREG0B1S1 */ +#define CSR_PRESEQUENCEREG0B1S1_LSB 0 +#define CSR_PRESEQUENCEREG0B1S1_MASK GENMASK_32(15, 0) +/* CSR_PRESEQUENCEREG0B1S2 */ +#define CSR_PRESEQUENCEREG0B1S2_LSB 0 +#define CSR_PRESEQUENCEREG0B1S2_MASK GENMASK_32(8, 0) +/* CSR_POSTSEQUENCEREG0B0S0 */ +#define CSR_POSTSEQUENCEREG0B0S0_LSB 0 +#define CSR_POSTSEQUENCEREG0B0S0_MASK GENMASK_32(15, 0) +/* CSR_POSTSEQUENCEREG0B0S1 */ +#define CSR_POSTSEQUENCEREG0B0S1_LSB 0 +#define CSR_POSTSEQUENCEREG0B0S1_MASK GENMASK_32(15, 0) +/* CSR_POSTSEQUENCEREG0B0S2 */ +#define CSR_POSTSEQUENCEREG0B0S2_LSB 0 +#define CSR_POSTSEQUENCEREG0B0S2_MASK GENMASK_32(8, 0) +/* CSR_POSTSEQUENCEREG0B1S0 */ +#define CSR_POSTSEQUENCEREG0B1S0_LSB 0 +#define CSR_POSTSEQUENCEREG0B1S0_MASK GENMASK_32(15, 0) +/* CSR_POSTSEQUENCEREG0B1S1 */ +#define CSR_POSTSEQUENCEREG0B1S1_LSB 0 +#define CSR_POSTSEQUENCEREG0B1S1_MASK GENMASK_32(15, 0) +/* CSR_POSTSEQUENCEREG0B1S2 */ +#define CSR_POSTSEQUENCEREG0B1S2_LSB 0 +#define CSR_POSTSEQUENCEREG0B1S2_MASK GENMASK_32(8, 0) +/* CSR_SEQ0BDISABLEFLAG0 */ +#define CSR_SEQ0BDISABLEFLAG0_LSB 0 +#define CSR_SEQ0BDISABLEFLAG0_MASK GENMASK_32(15, 0) +/* CSR_SEQ0BDISABLEFLAG1 */ +#define CSR_SEQ0BDISABLEFLAG1_LSB 0 +#define CSR_SEQ0BDISABLEFLAG1_MASK GENMASK_32(15, 0) +/* CSR_SEQ0BDISABLEFLAG2 */ +#define CSR_SEQ0BDISABLEFLAG2_LSB 0 +#define CSR_SEQ0BDISABLEFLAG2_MASK GENMASK_32(15, 0) +/* CSR_SEQ0BDISABLEFLAG3 */ +#define CSR_SEQ0BDISABLEFLAG3_LSB 0 +#define CSR_SEQ0BDISABLEFLAG3_MASK GENMASK_32(15, 0) +/* CSR_SEQ0BDISABLEFLAG4 */ +#define CSR_SEQ0BDISABLEFLAG4_LSB 0 +#define CSR_SEQ0BDISABLEFLAG4_MASK GENMASK_32(15, 0) +/* CSR_SEQ0BDISABLEFLAG5 */ +#define CSR_SEQ0BDISABLEFLAG5_LSB 0 +#define CSR_SEQ0BDISABLEFLAG5_MASK GENMASK_32(15, 0) +/* CSR_SEQ0BDISABLEFLAG6 */ +#define CSR_SEQ0BDISABLEFLAG6_LSB 0 +#define CSR_SEQ0BDISABLEFLAG6_MASK GENMASK_32(15, 0) +/* CSR_SEQ0BDISABLEFLAG7 */ +#define CSR_SEQ0BDISABLEFLAG7_LSB 0 +#define CSR_SEQ0BDISABLEFLAG7_MASK GENMASK_32(15, 0) +/* CSR_STARTVECTOR0B0 */ +#define CSR_STARTVECTOR0B0_LSB 0 +#define CSR_STARTVECTOR0B0_MASK GENMASK_32(6, 0) +#define CSR_SEQ0BSTARTVEC0_LSB 0 +#define CSR_SEQ0BSTARTVEC0_MASK GENMASK_32(6, 0) +/* CSR_STARTVECTOR0B1 */ +#define CSR_STARTVECTOR0B1_LSB 0 +#define CSR_STARTVECTOR0B1_MASK GENMASK_32(6, 0) +#define CSR_SEQ0BSTARTVEC1_LSB 0 +#define CSR_SEQ0BSTARTVEC1_MASK GENMASK_32(6, 0) +/* CSR_STARTVECTOR0B2 */ +#define CSR_STARTVECTOR0B2_LSB 0 +#define CSR_STARTVECTOR0B2_MASK GENMASK_32(6, 0) +#define CSR_SEQ0BSTARTVEC2_LSB 0 +#define CSR_SEQ0BSTARTVEC2_MASK GENMASK_32(6, 0) +/* CSR_STARTVECTOR0B3 */ +#define CSR_STARTVECTOR0B3_LSB 0 +#define CSR_STARTVECTOR0B3_MASK GENMASK_32(6, 0) +#define CSR_SEQ0BSTARTVEC3_LSB 0 +#define CSR_SEQ0BSTARTVEC3_MASK GENMASK_32(6, 0) +/* CSR_STARTVECTOR0B4 */ +#define CSR_STARTVECTOR0B4_LSB 0 +#define CSR_STARTVECTOR0B4_MASK GENMASK_32(6, 0) +#define CSR_SEQ0BSTARTVEC4_LSB 0 +#define CSR_SEQ0BSTARTVEC4_MASK GENMASK_32(6, 0) +/* CSR_STARTVECTOR0B5 */ +#define CSR_STARTVECTOR0B5_LSB 0 +#define CSR_STARTVECTOR0B5_MASK GENMASK_32(6, 0) +#define CSR_SEQ0BSTARTVEC5_LSB 0 +#define CSR_SEQ0BSTARTVEC5_MASK GENMASK_32(6, 0) +/* CSR_STARTVECTOR0B6 */ +#define CSR_STARTVECTOR0B6_LSB 0 +#define CSR_STARTVECTOR0B6_MASK GENMASK_32(6, 0) +#define CSR_SEQ0BSTARTVEC6_LSB 0 +#define CSR_SEQ0BSTARTVEC6_MASK GENMASK_32(6, 0) +/* CSR_STARTVECTOR0B7 */ +#define CSR_STARTVECTOR0B7_LSB 0 +#define CSR_STARTVECTOR0B7_MASK GENMASK_32(6, 0) +#define CSR_SEQ0BSTARTVEC7_LSB 0 +#define CSR_SEQ0BSTARTVEC7_MASK GENMASK_32(6, 0) +/* CSR_STARTVECTOR0B8 */ +#define CSR_STARTVECTOR0B8_LSB 0 +#define CSR_STARTVECTOR0B8_MASK GENMASK_32(6, 0) +#define CSR_SEQ0BSTARTVEC8_LSB 0 +#define CSR_SEQ0BSTARTVEC8_MASK GENMASK_32(6, 0) +/* CSR_STARTVECTOR0B9 */ +#define CSR_STARTVECTOR0B9_LSB 0 +#define CSR_STARTVECTOR0B9_MASK GENMASK_32(6, 0) +#define CSR_SEQ0BSTARTVEC9_LSB 0 +#define CSR_SEQ0BSTARTVEC9_MASK GENMASK_32(6, 0) +/* CSR_STARTVECTOR0B10 */ +#define CSR_STARTVECTOR0B10_LSB 0 +#define CSR_STARTVECTOR0B10_MASK GENMASK_32(6, 0) +#define CSR_SEQ0BSTARTVEC10_LSB 0 +#define CSR_SEQ0BSTARTVEC10_MASK GENMASK_32(6, 0) +/* CSR_STARTVECTOR0B11 */ +#define CSR_STARTVECTOR0B11_LSB 0 +#define CSR_STARTVECTOR0B11_MASK GENMASK_32(6, 0) +#define CSR_SEQ0BSTARTVEC11_LSB 0 +#define CSR_SEQ0BSTARTVEC11_MASK GENMASK_32(6, 0) +/* CSR_STARTVECTOR0B12 */ +#define CSR_STARTVECTOR0B12_LSB 0 +#define CSR_STARTVECTOR0B12_MASK GENMASK_32(6, 0) +#define CSR_SEQ0BSTARTVEC12_LSB 0 +#define CSR_SEQ0BSTARTVEC12_MASK GENMASK_32(6, 0) +/* CSR_STARTVECTOR0B13 */ +#define CSR_STARTVECTOR0B13_LSB 0 +#define CSR_STARTVECTOR0B13_MASK GENMASK_32(6, 0) +#define CSR_SEQ0BSTARTVEC13_LSB 0 +#define CSR_SEQ0BSTARTVEC13_MASK GENMASK_32(6, 0) +/* CSR_STARTVECTOR0B14 */ +#define CSR_STARTVECTOR0B14_LSB 0 +#define CSR_STARTVECTOR0B14_MASK GENMASK_32(6, 0) +#define CSR_SEQ0BSTARTVEC14_LSB 0 +#define CSR_SEQ0BSTARTVEC14_MASK GENMASK_32(6, 0) +/* CSR_STARTVECTOR0B15 */ +#define CSR_STARTVECTOR0B15_LSB 0 +#define CSR_STARTVECTOR0B15_MASK GENMASK_32(6, 0) +#define CSR_SEQ0BSTARTVEC15_LSB 0 +#define CSR_SEQ0BSTARTVEC15_MASK GENMASK_32(6, 0) +/* CSR_SEQ0BWAITCONDSEL */ +#define CSR_SEQ0BWAITCONDSEL_LSB 0 +#define CSR_SEQ0BWAITCONDSEL_MASK GENMASK_32(2, 0) +/* CSR_PHYINLP3 */ +#define CSR_PHYINLP3_LSB 0 +#define CSR_PHYINLP3_MASK BIT(0) +/* CSR_SEQUENCEREG0B0S0 */ +#define CSR_SEQUENCEREG0B0S0_LSB 0 +#define CSR_SEQUENCEREG0B0S0_MASK GENMASK_32(15, 0) +/* CSR_SEQUENCEREG0B0S1 */ +#define CSR_SEQUENCEREG0B0S1_LSB 0 +#define CSR_SEQUENCEREG0B0S1_MASK GENMASK_32(15, 0) +/* CSR_SEQUENCEREG0B0S2 */ +#define CSR_SEQUENCEREG0B0S2_LSB 0 +#define CSR_SEQUENCEREG0B0S2_MASK GENMASK_32(8, 0) +/* CSR_SEQUENCEREG0B1S0 */ +#define CSR_SEQUENCEREG0B1S0_LSB 0 +#define CSR_SEQUENCEREG0B1S0_MASK GENMASK_32(15, 0) +/* CSR_SEQUENCEREG0B1S1 */ +#define CSR_SEQUENCEREG0B1S1_LSB 0 +#define CSR_SEQUENCEREG0B1S1_MASK GENMASK_32(15, 0) +/* CSR_SEQUENCEREG0B1S2 */ +#define CSR_SEQUENCEREG0B1S2_LSB 0 +#define CSR_SEQUENCEREG0B1S2_MASK GENMASK_32(8, 0) +/* CSR_SEQUENCEREG0B2S0 */ +#define CSR_SEQUENCEREG0B2S0_LSB 0 +#define CSR_SEQUENCEREG0B2S0_MASK GENMASK_32(15, 0) +/* CSR_SEQUENCEREG0B2S1 */ +#define CSR_SEQUENCEREG0B2S1_LSB 0 +#define CSR_SEQUENCEREG0B2S1_MASK GENMASK_32(15, 0) +/* CSR_SEQUENCEREG0B2S2 */ +#define CSR_SEQUENCEREG0B2S2_LSB 0 +#define CSR_SEQUENCEREG0B2S2_MASK GENMASK_32(8, 0) +/* CSR_SEQUENCEREG0B3S0 */ +#define CSR_SEQUENCEREG0B3S0_LSB 0 +#define CSR_SEQUENCEREG0B3S0_MASK GENMASK_32(15, 0) +/* CSR_SEQUENCEREG0B3S1 */ +#define CSR_SEQUENCEREG0B3S1_LSB 0 +#define CSR_SEQUENCEREG0B3S1_MASK GENMASK_32(15, 0) +/* CSR_SEQUENCEREG0B3S2 */ +#define CSR_SEQUENCEREG0B3S2_LSB 0 +#define CSR_SEQUENCEREG0B3S2_MASK GENMASK_32(8, 0) +/* CSR_SEQUENCEREG0B4S0 */ +#define CSR_SEQUENCEREG0B4S0_LSB 0 +#define CSR_SEQUENCEREG0B4S0_MASK GENMASK_32(15, 0) +/* CSR_SEQUENCEREG0B4S1 */ +#define CSR_SEQUENCEREG0B4S1_LSB 0 +#define CSR_SEQUENCEREG0B4S1_MASK GENMASK_32(15, 0) +/* CSR_SEQUENCEREG0B4S2 */ +#define CSR_SEQUENCEREG0B4S2_LSB 0 +#define CSR_SEQUENCEREG0B4S2_MASK GENMASK_32(8, 0) +/* CSR_SEQUENCEREG0B5S0 */ +#define CSR_SEQUENCEREG0B5S0_LSB 0 +#define CSR_SEQUENCEREG0B5S0_MASK GENMASK_32(15, 0) +/* CSR_SEQUENCEREG0B5S1 */ +#define CSR_SEQUENCEREG0B5S1_LSB 0 +#define CSR_SEQUENCEREG0B5S1_MASK GENMASK_32(15, 0) +/* CSR_SEQUENCEREG0B5S2 */ +#define CSR_SEQUENCEREG0B5S2_LSB 0 +#define CSR_SEQUENCEREG0B5S2_MASK GENMASK_32(8, 0) +/* CSR_SEQUENCEREG0B6S0 */ +#define CSR_SEQUENCEREG0B6S0_LSB 0 +#define CSR_SEQUENCEREG0B6S0_MASK GENMASK_32(15, 0) +/* CSR_SEQUENCEREG0B6S1 */ +#define CSR_SEQUENCEREG0B6S1_LSB 0 +#define CSR_SEQUENCEREG0B6S1_MASK GENMASK_32(15, 0) +/* CSR_SEQUENCEREG0B6S2 */ +#define CSR_SEQUENCEREG0B6S2_LSB 0 +#define CSR_SEQUENCEREG0B6S2_MASK GENMASK_32(8, 0) +/* CSR_SEQUENCEREG0B7S0 */ +#define CSR_SEQUENCEREG0B7S0_LSB 0 +#define CSR_SEQUENCEREG0B7S0_MASK GENMASK_32(15, 0) +/* CSR_SEQUENCEREG0B7S1 */ +#define CSR_SEQUENCEREG0B7S1_LSB 0 +#define CSR_SEQUENCEREG0B7S1_MASK GENMASK_32(15, 0) +/* CSR_SEQUENCEREG0B7S2 */ +#define CSR_SEQUENCEREG0B7S2_LSB 0 +#define CSR_SEQUENCEREG0B7S2_MASK GENMASK_32(8, 0) +/* CSR_SEQUENCEREG0B8S0 */ +#define CSR_SEQUENCEREG0B8S0_LSB 0 +#define CSR_SEQUENCEREG0B8S0_MASK GENMASK_32(15, 0) +/* CSR_SEQUENCEREG0B8S1 */ +#define CSR_SEQUENCEREG0B8S1_LSB 0 +#define CSR_SEQUENCEREG0B8S1_MASK GENMASK_32(15, 0) +/* CSR_SEQUENCEREG0B8S2 */ +#define CSR_SEQUENCEREG0B8S2_LSB 0 +#define CSR_SEQUENCEREG0B8S2_MASK GENMASK_32(8, 0) +/* CSR_SEQUENCEREG0B9S0 */ +#define CSR_SEQUENCEREG0B9S0_LSB 0 +#define CSR_SEQUENCEREG0B9S0_MASK GENMASK_32(15, 0) +/* CSR_SEQUENCEREG0B9S1 */ +#define CSR_SEQUENCEREG0B9S1_LSB 0 +#define CSR_SEQUENCEREG0B9S1_MASK GENMASK_32(15, 0) +/* CSR_SEQUENCEREG0B9S2 */ +#define CSR_SEQUENCEREG0B9S2_LSB 0 +#define CSR_SEQUENCEREG0B9S2_MASK GENMASK_32(8, 0) +/* CSR_SEQUENCEREG0B10S0 */ +#define CSR_SEQUENCEREG0B10S0_LSB 0 +#define CSR_SEQUENCEREG0B10S0_MASK GENMASK_32(15, 0) +/* CSR_SEQUENCEREG0B10S1 */ +#define CSR_SEQUENCEREG0B10S1_LSB 0 +#define CSR_SEQUENCEREG0B10S1_MASK GENMASK_32(15, 0) +/* CSR_SEQUENCEREG0B10S2 */ +#define CSR_SEQUENCEREG0B10S2_LSB 0 +#define CSR_SEQUENCEREG0B10S2_MASK GENMASK_32(8, 0) +/* CSR_SEQUENCEREG0B11S0 */ +#define CSR_SEQUENCEREG0B11S0_LSB 0 +#define CSR_SEQUENCEREG0B11S0_MASK GENMASK_32(15, 0) +/* CSR_SEQUENCEREG0B11S1 */ +#define CSR_SEQUENCEREG0B11S1_LSB 0 +#define CSR_SEQUENCEREG0B11S1_MASK GENMASK_32(15, 0) +/* CSR_SEQUENCEREG0B11S2 */ +#define CSR_SEQUENCEREG0B11S2_LSB 0 +#define CSR_SEQUENCEREG0B11S2_MASK GENMASK_32(8, 0) +/* CSR_SEQUENCEREG0B12S0 */ +#define CSR_SEQUENCEREG0B12S0_LSB 0 +#define CSR_SEQUENCEREG0B12S0_MASK GENMASK_32(15, 0) +/* CSR_SEQUENCEREG0B12S1 */ +#define CSR_SEQUENCEREG0B12S1_LSB 0 +#define CSR_SEQUENCEREG0B12S1_MASK GENMASK_32(15, 0) +/* CSR_SEQUENCEREG0B12S2 */ +#define CSR_SEQUENCEREG0B12S2_LSB 0 +#define CSR_SEQUENCEREG0B12S2_MASK GENMASK_32(8, 0) +/* CSR_SEQUENCEREG0B13S0 */ +#define CSR_SEQUENCEREG0B13S0_LSB 0 +#define CSR_SEQUENCEREG0B13S0_MASK GENMASK_32(15, 0) +/* CSR_SEQUENCEREG0B13S1 */ +#define CSR_SEQUENCEREG0B13S1_LSB 0 +#define CSR_SEQUENCEREG0B13S1_MASK GENMASK_32(15, 0) +/* CSR_SEQUENCEREG0B13S2 */ +#define CSR_SEQUENCEREG0B13S2_LSB 0 +#define CSR_SEQUENCEREG0B13S2_MASK GENMASK_32(8, 0) +/* CSR_SEQUENCEREG0B14S0 */ +#define CSR_SEQUENCEREG0B14S0_LSB 0 +#define CSR_SEQUENCEREG0B14S0_MASK GENMASK_32(15, 0) +/* CSR_SEQUENCEREG0B14S1 */ +#define CSR_SEQUENCEREG0B14S1_LSB 0 +#define CSR_SEQUENCEREG0B14S1_MASK GENMASK_32(15, 0) +/* CSR_SEQUENCEREG0B14S2 */ +#define CSR_SEQUENCEREG0B14S2_LSB 0 +#define CSR_SEQUENCEREG0B14S2_MASK GENMASK_32(8, 0) +/* CSR_SEQUENCEREG0B15S0 */ +#define CSR_SEQUENCEREG0B15S0_LSB 0 +#define CSR_SEQUENCEREG0B15S0_MASK GENMASK_32(15, 0) +/* CSR_SEQUENCEREG0B15S1 */ +#define CSR_SEQUENCEREG0B15S1_LSB 0 +#define CSR_SEQUENCEREG0B15S1_MASK GENMASK_32(15, 0) +/* CSR_SEQUENCEREG0B15S2 */ +#define CSR_SEQUENCEREG0B15S2_LSB 0 +#define CSR_SEQUENCEREG0B15S2_MASK GENMASK_32(8, 0) +/* CSR_SEQUENCEREG0B16S0 */ +#define CSR_SEQUENCEREG0B16S0_LSB 0 +#define CSR_SEQUENCEREG0B16S0_MASK GENMASK_32(15, 0) +/* CSR_SEQUENCEREG0B16S1 */ +#define CSR_SEQUENCEREG0B16S1_LSB 0 +#define CSR_SEQUENCEREG0B16S1_MASK GENMASK_32(15, 0) +/* CSR_SEQUENCEREG0B16S2 */ +#define CSR_SEQUENCEREG0B16S2_LSB 0 +#define CSR_SEQUENCEREG0B16S2_MASK GENMASK_32(8, 0) +/* CSR_SEQUENCEREG0B17S0 */ +#define CSR_SEQUENCEREG0B17S0_LSB 0 +#define CSR_SEQUENCEREG0B17S0_MASK GENMASK_32(15, 0) +/* CSR_SEQUENCEREG0B17S1 */ +#define CSR_SEQUENCEREG0B17S1_LSB 0 +#define CSR_SEQUENCEREG0B17S1_MASK GENMASK_32(15, 0) +/* CSR_SEQUENCEREG0B17S2 */ +#define CSR_SEQUENCEREG0B17S2_LSB 0 +#define CSR_SEQUENCEREG0B17S2_MASK GENMASK_32(8, 0) +/* CSR_SEQUENCEREG0B18S0 */ +#define CSR_SEQUENCEREG0B18S0_LSB 0 +#define CSR_SEQUENCEREG0B18S0_MASK GENMASK_32(15, 0) +/* CSR_SEQUENCEREG0B18S1 */ +#define CSR_SEQUENCEREG0B18S1_LSB 0 +#define CSR_SEQUENCEREG0B18S1_MASK GENMASK_32(15, 0) +/* CSR_SEQUENCEREG0B18S2 */ +#define CSR_SEQUENCEREG0B18S2_LSB 0 +#define CSR_SEQUENCEREG0B18S2_MASK GENMASK_32(8, 0) +/* CSR_SEQUENCEREG0B19S0 */ +#define CSR_SEQUENCEREG0B19S0_LSB 0 +#define CSR_SEQUENCEREG0B19S0_MASK GENMASK_32(15, 0) +/* CSR_SEQUENCEREG0B19S1 */ +#define CSR_SEQUENCEREG0B19S1_LSB 0 +#define CSR_SEQUENCEREG0B19S1_MASK GENMASK_32(15, 0) +/* CSR_SEQUENCEREG0B19S2 */ +#define CSR_SEQUENCEREG0B19S2_LSB 0 +#define CSR_SEQUENCEREG0B19S2_MASK GENMASK_32(8, 0) +/* CSR_SEQUENCEREG0B20S0 */ +#define CSR_SEQUENCEREG0B20S0_LSB 0 +#define CSR_SEQUENCEREG0B20S0_MASK GENMASK_32(15, 0) +/* CSR_SEQUENCEREG0B20S1 */ +#define CSR_SEQUENCEREG0B20S1_LSB 0 +#define CSR_SEQUENCEREG0B20S1_MASK GENMASK_32(15, 0) +/* CSR_SEQUENCEREG0B20S2 */ +#define CSR_SEQUENCEREG0B20S2_LSB 0 +#define CSR_SEQUENCEREG0B20S2_MASK GENMASK_32(8, 0) +/* CSR_SEQUENCEREG0B21S0 */ +#define CSR_SEQUENCEREG0B21S0_LSB 0 +#define CSR_SEQUENCEREG0B21S0_MASK GENMASK_32(15, 0) +/* CSR_SEQUENCEREG0B21S1 */ +#define CSR_SEQUENCEREG0B21S1_LSB 0 +#define CSR_SEQUENCEREG0B21S1_MASK GENMASK_32(15, 0) +/* CSR_SEQUENCEREG0B21S2 */ +#define CSR_SEQUENCEREG0B21S2_LSB 0 +#define CSR_SEQUENCEREG0B21S2_MASK GENMASK_32(8, 0) +/* CSR_SEQUENCEREG0B22S0 */ +#define CSR_SEQUENCEREG0B22S0_LSB 0 +#define CSR_SEQUENCEREG0B22S0_MASK GENMASK_32(15, 0) +/* CSR_SEQUENCEREG0B22S1 */ +#define CSR_SEQUENCEREG0B22S1_LSB 0 +#define CSR_SEQUENCEREG0B22S1_MASK GENMASK_32(15, 0) +/* CSR_SEQUENCEREG0B22S2 */ +#define CSR_SEQUENCEREG0B22S2_LSB 0 +#define CSR_SEQUENCEREG0B22S2_MASK GENMASK_32(8, 0) +/* CSR_SEQUENCEREG0B23S0 */ +#define CSR_SEQUENCEREG0B23S0_LSB 0 +#define CSR_SEQUENCEREG0B23S0_MASK GENMASK_32(15, 0) +/* CSR_SEQUENCEREG0B23S1 */ +#define CSR_SEQUENCEREG0B23S1_LSB 0 +#define CSR_SEQUENCEREG0B23S1_MASK GENMASK_32(15, 0) +/* CSR_SEQUENCEREG0B23S2 */ +#define CSR_SEQUENCEREG0B23S2_LSB 0 +#define CSR_SEQUENCEREG0B23S2_MASK GENMASK_32(8, 0) +/* CSR_SEQUENCEREG0B24S0 */ +#define CSR_SEQUENCEREG0B24S0_LSB 0 +#define CSR_SEQUENCEREG0B24S0_MASK GENMASK_32(15, 0) +/* CSR_SEQUENCEREG0B24S1 */ +#define CSR_SEQUENCEREG0B24S1_LSB 0 +#define CSR_SEQUENCEREG0B24S1_MASK GENMASK_32(15, 0) +/* CSR_SEQUENCEREG0B24S2 */ +#define CSR_SEQUENCEREG0B24S2_LSB 0 +#define CSR_SEQUENCEREG0B24S2_MASK GENMASK_32(8, 0) +/* CSR_SEQUENCEREG0B25S0 */ +#define CSR_SEQUENCEREG0B25S0_LSB 0 +#define CSR_SEQUENCEREG0B25S0_MASK GENMASK_32(15, 0) +/* CSR_SEQUENCEREG0B25S1 */ +#define CSR_SEQUENCEREG0B25S1_LSB 0 +#define CSR_SEQUENCEREG0B25S1_MASK GENMASK_32(15, 0) +/* CSR_SEQUENCEREG0B25S2 */ +#define CSR_SEQUENCEREG0B25S2_LSB 0 +#define CSR_SEQUENCEREG0B25S2_MASK GENMASK_32(8, 0) +/* CSR_SEQUENCEREG0B26S0 */ +#define CSR_SEQUENCEREG0B26S0_LSB 0 +#define CSR_SEQUENCEREG0B26S0_MASK GENMASK_32(15, 0) +/* CSR_SEQUENCEREG0B26S1 */ +#define CSR_SEQUENCEREG0B26S1_LSB 0 +#define CSR_SEQUENCEREG0B26S1_MASK GENMASK_32(15, 0) +/* CSR_SEQUENCEREG0B26S2 */ +#define CSR_SEQUENCEREG0B26S2_LSB 0 +#define CSR_SEQUENCEREG0B26S2_MASK GENMASK_32(8, 0) +/* CSR_SEQUENCEREG0B27S0 */ +#define CSR_SEQUENCEREG0B27S0_LSB 0 +#define CSR_SEQUENCEREG0B27S0_MASK GENMASK_32(15, 0) +/* CSR_SEQUENCEREG0B27S1 */ +#define CSR_SEQUENCEREG0B27S1_LSB 0 +#define CSR_SEQUENCEREG0B27S1_MASK GENMASK_32(15, 0) +/* CSR_SEQUENCEREG0B27S2 */ +#define CSR_SEQUENCEREG0B27S2_LSB 0 +#define CSR_SEQUENCEREG0B27S2_MASK GENMASK_32(8, 0) +/* CSR_SEQUENCEREG0B28S0 */ +#define CSR_SEQUENCEREG0B28S0_LSB 0 +#define CSR_SEQUENCEREG0B28S0_MASK GENMASK_32(15, 0) +/* CSR_SEQUENCEREG0B28S1 */ +#define CSR_SEQUENCEREG0B28S1_LSB 0 +#define CSR_SEQUENCEREG0B28S1_MASK GENMASK_32(15, 0) +/* CSR_SEQUENCEREG0B28S2 */ +#define CSR_SEQUENCEREG0B28S2_LSB 0 +#define CSR_SEQUENCEREG0B28S2_MASK GENMASK_32(8, 0) +/* CSR_SEQUENCEREG0B29S0 */ +#define CSR_SEQUENCEREG0B29S0_LSB 0 +#define CSR_SEQUENCEREG0B29S0_MASK GENMASK_32(15, 0) +/* CSR_SEQUENCEREG0B29S1 */ +#define CSR_SEQUENCEREG0B29S1_LSB 0 +#define CSR_SEQUENCEREG0B29S1_MASK GENMASK_32(15, 0) +/* CSR_SEQUENCEREG0B29S2 */ +#define CSR_SEQUENCEREG0B29S2_LSB 0 +#define CSR_SEQUENCEREG0B29S2_MASK GENMASK_32(8, 0) +/* CSR_SEQUENCEREG0B30S0 */ +#define CSR_SEQUENCEREG0B30S0_LSB 0 +#define CSR_SEQUENCEREG0B30S0_MASK GENMASK_32(15, 0) +/* CSR_SEQUENCEREG0B30S1 */ +#define CSR_SEQUENCEREG0B30S1_LSB 0 +#define CSR_SEQUENCEREG0B30S1_MASK GENMASK_32(15, 0) +/* CSR_SEQUENCEREG0B30S2 */ +#define CSR_SEQUENCEREG0B30S2_LSB 0 +#define CSR_SEQUENCEREG0B30S2_MASK GENMASK_32(8, 0) +/* CSR_SEQUENCEREG0B31S0 */ +#define CSR_SEQUENCEREG0B31S0_LSB 0 +#define CSR_SEQUENCEREG0B31S0_MASK GENMASK_32(15, 0) +/* CSR_SEQUENCEREG0B31S1 */ +#define CSR_SEQUENCEREG0B31S1_LSB 0 +#define CSR_SEQUENCEREG0B31S1_MASK GENMASK_32(15, 0) +/* CSR_SEQUENCEREG0B31S2 */ +#define CSR_SEQUENCEREG0B31S2_LSB 0 +#define CSR_SEQUENCEREG0B31S2_MASK GENMASK_32(8, 0) +/* CSR_SEQUENCEREG0B32S0 */ +#define CSR_SEQUENCEREG0B32S0_LSB 0 +#define CSR_SEQUENCEREG0B32S0_MASK GENMASK_32(15, 0) +/* CSR_SEQUENCEREG0B32S1 */ +#define CSR_SEQUENCEREG0B32S1_LSB 0 +#define CSR_SEQUENCEREG0B32S1_MASK GENMASK_32(15, 0) +/* CSR_SEQUENCEREG0B32S2 */ +#define CSR_SEQUENCEREG0B32S2_LSB 0 +#define CSR_SEQUENCEREG0B32S2_MASK GENMASK_32(8, 0) +/* CSR_SEQUENCEREG0B33S0 */ +#define CSR_SEQUENCEREG0B33S0_LSB 0 +#define CSR_SEQUENCEREG0B33S0_MASK GENMASK_32(15, 0) +/* CSR_SEQUENCEREG0B33S1 */ +#define CSR_SEQUENCEREG0B33S1_LSB 0 +#define CSR_SEQUENCEREG0B33S1_MASK GENMASK_32(15, 0) +/* CSR_SEQUENCEREG0B33S2 */ +#define CSR_SEQUENCEREG0B33S2_LSB 0 +#define CSR_SEQUENCEREG0B33S2_MASK GENMASK_32(8, 0) +/* CSR_SEQUENCEREG0B34S0 */ +#define CSR_SEQUENCEREG0B34S0_LSB 0 +#define CSR_SEQUENCEREG0B34S0_MASK GENMASK_32(15, 0) +/* CSR_SEQUENCEREG0B34S1 */ +#define CSR_SEQUENCEREG0B34S1_LSB 0 +#define CSR_SEQUENCEREG0B34S1_MASK GENMASK_32(15, 0) +/* CSR_SEQUENCEREG0B34S2 */ +#define CSR_SEQUENCEREG0B34S2_LSB 0 +#define CSR_SEQUENCEREG0B34S2_MASK GENMASK_32(8, 0) +/* CSR_SEQUENCEREG0B35S0 */ +#define CSR_SEQUENCEREG0B35S0_LSB 0 +#define CSR_SEQUENCEREG0B35S0_MASK GENMASK_32(15, 0) +/* CSR_SEQUENCEREG0B35S1 */ +#define CSR_SEQUENCEREG0B35S1_LSB 0 +#define CSR_SEQUENCEREG0B35S1_MASK GENMASK_32(15, 0) +/* CSR_SEQUENCEREG0B35S2 */ +#define CSR_SEQUENCEREG0B35S2_LSB 0 +#define CSR_SEQUENCEREG0B35S2_MASK GENMASK_32(8, 0) +/* CSR_SEQUENCEREG0B36S0 */ +#define CSR_SEQUENCEREG0B36S0_LSB 0 +#define CSR_SEQUENCEREG0B36S0_MASK GENMASK_32(15, 0) +/* CSR_SEQUENCEREG0B36S1 */ +#define CSR_SEQUENCEREG0B36S1_LSB 0 +#define CSR_SEQUENCEREG0B36S1_MASK GENMASK_32(15, 0) +/* CSR_SEQUENCEREG0B36S2 */ +#define CSR_SEQUENCEREG0B36S2_LSB 0 +#define CSR_SEQUENCEREG0B36S2_MASK GENMASK_32(8, 0) +/* CSR_SEQUENCEREG0B37S0 */ +#define CSR_SEQUENCEREG0B37S0_LSB 0 +#define CSR_SEQUENCEREG0B37S0_MASK GENMASK_32(15, 0) +/* CSR_SEQUENCEREG0B37S1 */ +#define CSR_SEQUENCEREG0B37S1_LSB 0 +#define CSR_SEQUENCEREG0B37S1_MASK GENMASK_32(15, 0) +/* CSR_SEQUENCEREG0B37S2 */ +#define CSR_SEQUENCEREG0B37S2_LSB 0 +#define CSR_SEQUENCEREG0B37S2_MASK GENMASK_32(8, 0) +/* CSR_SEQUENCEREG0B38S0 */ +#define CSR_SEQUENCEREG0B38S0_LSB 0 +#define CSR_SEQUENCEREG0B38S0_MASK GENMASK_32(15, 0) +/* CSR_SEQUENCEREG0B38S1 */ +#define CSR_SEQUENCEREG0B38S1_LSB 0 +#define CSR_SEQUENCEREG0B38S1_MASK GENMASK_32(15, 0) +/* CSR_SEQUENCEREG0B38S2 */ +#define CSR_SEQUENCEREG0B38S2_LSB 0 +#define CSR_SEQUENCEREG0B38S2_MASK GENMASK_32(8, 0) +/* CSR_SEQUENCEREG0B39S0 */ +#define CSR_SEQUENCEREG0B39S0_LSB 0 +#define CSR_SEQUENCEREG0B39S0_MASK GENMASK_32(15, 0) +/* CSR_SEQUENCEREG0B39S1 */ +#define CSR_SEQUENCEREG0B39S1_LSB 0 +#define CSR_SEQUENCEREG0B39S1_MASK GENMASK_32(15, 0) +/* CSR_SEQUENCEREG0B39S2 */ +#define CSR_SEQUENCEREG0B39S2_LSB 0 +#define CSR_SEQUENCEREG0B39S2_MASK GENMASK_32(8, 0) +/* CSR_SEQUENCEREG0B40S0 */ +#define CSR_SEQUENCEREG0B40S0_LSB 0 +#define CSR_SEQUENCEREG0B40S0_MASK GENMASK_32(15, 0) +/* CSR_SEQUENCEREG0B40S1 */ +#define CSR_SEQUENCEREG0B40S1_LSB 0 +#define CSR_SEQUENCEREG0B40S1_MASK GENMASK_32(15, 0) +/* CSR_SEQUENCEREG0B40S2 */ +#define CSR_SEQUENCEREG0B40S2_LSB 0 +#define CSR_SEQUENCEREG0B40S2_MASK GENMASK_32(8, 0) +/* CSR_SEQUENCEREG0B41S0 */ +#define CSR_SEQUENCEREG0B41S0_LSB 0 +#define CSR_SEQUENCEREG0B41S0_MASK GENMASK_32(15, 0) +/* CSR_SEQUENCEREG0B41S1 */ +#define CSR_SEQUENCEREG0B41S1_LSB 0 +#define CSR_SEQUENCEREG0B41S1_MASK GENMASK_32(15, 0) +/* CSR_SEQUENCEREG0B41S2 */ +#define CSR_SEQUENCEREG0B41S2_LSB 0 +#define CSR_SEQUENCEREG0B41S2_MASK GENMASK_32(8, 0) +/* CSR_SEQUENCEREG0B42S0 */ +#define CSR_SEQUENCEREG0B42S0_LSB 0 +#define CSR_SEQUENCEREG0B42S0_MASK GENMASK_32(15, 0) +/* CSR_SEQUENCEREG0B42S1 */ +#define CSR_SEQUENCEREG0B42S1_LSB 0 +#define CSR_SEQUENCEREG0B42S1_MASK GENMASK_32(15, 0) +/* CSR_SEQUENCEREG0B42S2 */ +#define CSR_SEQUENCEREG0B42S2_LSB 0 +#define CSR_SEQUENCEREG0B42S2_MASK GENMASK_32(8, 0) +/* CSR_SEQUENCEREG0B43S0 */ +#define CSR_SEQUENCEREG0B43S0_LSB 0 +#define CSR_SEQUENCEREG0B43S0_MASK GENMASK_32(15, 0) +/* CSR_SEQUENCEREG0B43S1 */ +#define CSR_SEQUENCEREG0B43S1_LSB 0 +#define CSR_SEQUENCEREG0B43S1_MASK GENMASK_32(15, 0) +/* CSR_SEQUENCEREG0B43S2 */ +#define CSR_SEQUENCEREG0B43S2_LSB 0 +#define CSR_SEQUENCEREG0B43S2_MASK GENMASK_32(8, 0) +/* CSR_SEQUENCEREG0B44S0 */ +#define CSR_SEQUENCEREG0B44S0_LSB 0 +#define CSR_SEQUENCEREG0B44S0_MASK GENMASK_32(15, 0) +/* CSR_SEQUENCEREG0B44S1 */ +#define CSR_SEQUENCEREG0B44S1_LSB 0 +#define CSR_SEQUENCEREG0B44S1_MASK GENMASK_32(15, 0) +/* CSR_SEQUENCEREG0B44S2 */ +#define CSR_SEQUENCEREG0B44S2_LSB 0 +#define CSR_SEQUENCEREG0B44S2_MASK GENMASK_32(8, 0) +/* CSR_SEQUENCEREG0B45S0 */ +#define CSR_SEQUENCEREG0B45S0_LSB 0 +#define CSR_SEQUENCEREG0B45S0_MASK GENMASK_32(15, 0) +/* CSR_SEQUENCEREG0B45S1 */ +#define CSR_SEQUENCEREG0B45S1_LSB 0 +#define CSR_SEQUENCEREG0B45S1_MASK GENMASK_32(15, 0) +/* CSR_SEQUENCEREG0B45S2 */ +#define CSR_SEQUENCEREG0B45S2_LSB 0 +#define CSR_SEQUENCEREG0B45S2_MASK GENMASK_32(8, 0) +/* CSR_SEQUENCEREG0B46S0 */ +#define CSR_SEQUENCEREG0B46S0_LSB 0 +#define CSR_SEQUENCEREG0B46S0_MASK GENMASK_32(15, 0) +/* CSR_SEQUENCEREG0B46S1 */ +#define CSR_SEQUENCEREG0B46S1_LSB 0 +#define CSR_SEQUENCEREG0B46S1_MASK GENMASK_32(15, 0) +/* CSR_SEQUENCEREG0B46S2 */ +#define CSR_SEQUENCEREG0B46S2_LSB 0 +#define CSR_SEQUENCEREG0B46S2_MASK GENMASK_32(8, 0) +/* CSR_SEQUENCEREG0B47S0 */ +#define CSR_SEQUENCEREG0B47S0_LSB 0 +#define CSR_SEQUENCEREG0B47S0_MASK GENMASK_32(15, 0) +/* CSR_SEQUENCEREG0B47S1 */ +#define CSR_SEQUENCEREG0B47S1_LSB 0 +#define CSR_SEQUENCEREG0B47S1_MASK GENMASK_32(15, 0) +/* CSR_SEQUENCEREG0B47S2 */ +#define CSR_SEQUENCEREG0B47S2_LSB 0 +#define CSR_SEQUENCEREG0B47S2_MASK GENMASK_32(8, 0) +/* CSR_SEQUENCEREG0B48S0 */ +#define CSR_SEQUENCEREG0B48S0_LSB 0 +#define CSR_SEQUENCEREG0B48S0_MASK GENMASK_32(15, 0) +/* CSR_SEQUENCEREG0B48S1 */ +#define CSR_SEQUENCEREG0B48S1_LSB 0 +#define CSR_SEQUENCEREG0B48S1_MASK GENMASK_32(15, 0) +/* CSR_SEQUENCEREG0B48S2 */ +#define CSR_SEQUENCEREG0B48S2_LSB 0 +#define CSR_SEQUENCEREG0B48S2_MASK GENMASK_32(8, 0) +/* CSR_SEQUENCEREG0B49S0 */ +#define CSR_SEQUENCEREG0B49S0_LSB 0 +#define CSR_SEQUENCEREG0B49S0_MASK GENMASK_32(15, 0) +/* CSR_SEQUENCEREG0B49S1 */ +#define CSR_SEQUENCEREG0B49S1_LSB 0 +#define CSR_SEQUENCEREG0B49S1_MASK GENMASK_32(15, 0) +/* CSR_SEQUENCEREG0B49S2 */ +#define CSR_SEQUENCEREG0B49S2_LSB 0 +#define CSR_SEQUENCEREG0B49S2_MASK GENMASK_32(8, 0) +/* CSR_SEQUENCEREG0B50S0 */ +#define CSR_SEQUENCEREG0B50S0_LSB 0 +#define CSR_SEQUENCEREG0B50S0_MASK GENMASK_32(15, 0) +/* CSR_SEQUENCEREG0B50S1 */ +#define CSR_SEQUENCEREG0B50S1_LSB 0 +#define CSR_SEQUENCEREG0B50S1_MASK GENMASK_32(15, 0) +/* CSR_SEQUENCEREG0B50S2 */ +#define CSR_SEQUENCEREG0B50S2_LSB 0 +#define CSR_SEQUENCEREG0B50S2_MASK GENMASK_32(8, 0) +/* CSR_SEQUENCEREG0B51S0 */ +#define CSR_SEQUENCEREG0B51S0_LSB 0 +#define CSR_SEQUENCEREG0B51S0_MASK GENMASK_32(15, 0) +/* CSR_SEQUENCEREG0B51S1 */ +#define CSR_SEQUENCEREG0B51S1_LSB 0 +#define CSR_SEQUENCEREG0B51S1_MASK GENMASK_32(15, 0) +/* CSR_SEQUENCEREG0B51S2 */ +#define CSR_SEQUENCEREG0B51S2_LSB 0 +#define CSR_SEQUENCEREG0B51S2_MASK GENMASK_32(8, 0) +/* CSR_SEQUENCEREG0B52S0 */ +#define CSR_SEQUENCEREG0B52S0_LSB 0 +#define CSR_SEQUENCEREG0B52S0_MASK GENMASK_32(15, 0) +/* CSR_SEQUENCEREG0B52S1 */ +#define CSR_SEQUENCEREG0B52S1_LSB 0 +#define CSR_SEQUENCEREG0B52S1_MASK GENMASK_32(15, 0) +/* CSR_SEQUENCEREG0B52S2 */ +#define CSR_SEQUENCEREG0B52S2_LSB 0 +#define CSR_SEQUENCEREG0B52S2_MASK GENMASK_32(8, 0) +/* CSR_SEQUENCEREG0B53S0 */ +#define CSR_SEQUENCEREG0B53S0_LSB 0 +#define CSR_SEQUENCEREG0B53S0_MASK GENMASK_32(15, 0) +/* CSR_SEQUENCEREG0B53S1 */ +#define CSR_SEQUENCEREG0B53S1_LSB 0 +#define CSR_SEQUENCEREG0B53S1_MASK GENMASK_32(15, 0) +/* CSR_SEQUENCEREG0B53S2 */ +#define CSR_SEQUENCEREG0B53S2_LSB 0 +#define CSR_SEQUENCEREG0B53S2_MASK GENMASK_32(8, 0) +/* CSR_SEQUENCEREG0B54S0 */ +#define CSR_SEQUENCEREG0B54S0_LSB 0 +#define CSR_SEQUENCEREG0B54S0_MASK GENMASK_32(15, 0) +/* CSR_SEQUENCEREG0B54S1 */ +#define CSR_SEQUENCEREG0B54S1_LSB 0 +#define CSR_SEQUENCEREG0B54S1_MASK GENMASK_32(15, 0) +/* CSR_SEQUENCEREG0B54S2 */ +#define CSR_SEQUENCEREG0B54S2_LSB 0 +#define CSR_SEQUENCEREG0B54S2_MASK GENMASK_32(8, 0) +/* CSR_SEQUENCEREG0B55S0 */ +#define CSR_SEQUENCEREG0B55S0_LSB 0 +#define CSR_SEQUENCEREG0B55S0_MASK GENMASK_32(15, 0) +/* CSR_SEQUENCEREG0B55S1 */ +#define CSR_SEQUENCEREG0B55S1_LSB 0 +#define CSR_SEQUENCEREG0B55S1_MASK GENMASK_32(15, 0) +/* CSR_SEQUENCEREG0B55S2 */ +#define CSR_SEQUENCEREG0B55S2_LSB 0 +#define CSR_SEQUENCEREG0B55S2_MASK GENMASK_32(8, 0) +/* CSR_SEQUENCEREG0B56S0 */ +#define CSR_SEQUENCEREG0B56S0_LSB 0 +#define CSR_SEQUENCEREG0B56S0_MASK GENMASK_32(15, 0) +/* CSR_SEQUENCEREG0B56S1 */ +#define CSR_SEQUENCEREG0B56S1_LSB 0 +#define CSR_SEQUENCEREG0B56S1_MASK GENMASK_32(15, 0) +/* CSR_SEQUENCEREG0B56S2 */ +#define CSR_SEQUENCEREG0B56S2_LSB 0 +#define CSR_SEQUENCEREG0B56S2_MASK GENMASK_32(8, 0) +/* CSR_SEQUENCEREG0B57S0 */ +#define CSR_SEQUENCEREG0B57S0_LSB 0 +#define CSR_SEQUENCEREG0B57S0_MASK GENMASK_32(15, 0) +/* CSR_SEQUENCEREG0B57S1 */ +#define CSR_SEQUENCEREG0B57S1_LSB 0 +#define CSR_SEQUENCEREG0B57S1_MASK GENMASK_32(15, 0) +/* CSR_SEQUENCEREG0B57S2 */ +#define CSR_SEQUENCEREG0B57S2_LSB 0 +#define CSR_SEQUENCEREG0B57S2_MASK GENMASK_32(8, 0) +/* CSR_SEQUENCEREG0B58S0 */ +#define CSR_SEQUENCEREG0B58S0_LSB 0 +#define CSR_SEQUENCEREG0B58S0_MASK GENMASK_32(15, 0) +/* CSR_SEQUENCEREG0B58S1 */ +#define CSR_SEQUENCEREG0B58S1_LSB 0 +#define CSR_SEQUENCEREG0B58S1_MASK GENMASK_32(15, 0) +/* CSR_SEQUENCEREG0B58S2 */ +#define CSR_SEQUENCEREG0B58S2_LSB 0 +#define CSR_SEQUENCEREG0B58S2_MASK GENMASK_32(8, 0) +/* CSR_SEQUENCEREG0B59S0 */ +#define CSR_SEQUENCEREG0B59S0_LSB 0 +#define CSR_SEQUENCEREG0B59S0_MASK GENMASK_32(15, 0) +/* CSR_SEQUENCEREG0B59S1 */ +#define CSR_SEQUENCEREG0B59S1_LSB 0 +#define CSR_SEQUENCEREG0B59S1_MASK GENMASK_32(15, 0) +/* CSR_SEQUENCEREG0B59S2 */ +#define CSR_SEQUENCEREG0B59S2_LSB 0 +#define CSR_SEQUENCEREG0B59S2_MASK GENMASK_32(8, 0) +/* CSR_SEQUENCEREG0B60S0 */ +#define CSR_SEQUENCEREG0B60S0_LSB 0 +#define CSR_SEQUENCEREG0B60S0_MASK GENMASK_32(15, 0) +/* CSR_SEQUENCEREG0B60S1 */ +#define CSR_SEQUENCEREG0B60S1_LSB 0 +#define CSR_SEQUENCEREG0B60S1_MASK GENMASK_32(15, 0) +/* CSR_SEQUENCEREG0B60S2 */ +#define CSR_SEQUENCEREG0B60S2_LSB 0 +#define CSR_SEQUENCEREG0B60S2_MASK GENMASK_32(8, 0) +/* CSR_SEQUENCEREG0B61S0 */ +#define CSR_SEQUENCEREG0B61S0_LSB 0 +#define CSR_SEQUENCEREG0B61S0_MASK GENMASK_32(15, 0) +/* CSR_SEQUENCEREG0B61S1 */ +#define CSR_SEQUENCEREG0B61S1_LSB 0 +#define CSR_SEQUENCEREG0B61S1_MASK GENMASK_32(15, 0) +/* CSR_SEQUENCEREG0B61S2 */ +#define CSR_SEQUENCEREG0B61S2_LSB 0 +#define CSR_SEQUENCEREG0B61S2_MASK GENMASK_32(8, 0) +/* CSR_SEQUENCEREG0B62S0 */ +#define CSR_SEQUENCEREG0B62S0_LSB 0 +#define CSR_SEQUENCEREG0B62S0_MASK GENMASK_32(15, 0) +/* CSR_SEQUENCEREG0B62S1 */ +#define CSR_SEQUENCEREG0B62S1_LSB 0 +#define CSR_SEQUENCEREG0B62S1_MASK GENMASK_32(15, 0) +/* CSR_SEQUENCEREG0B62S2 */ +#define CSR_SEQUENCEREG0B62S2_LSB 0 +#define CSR_SEQUENCEREG0B62S2_MASK GENMASK_32(8, 0) +/* CSR_SEQUENCEREG0B63S0 */ +#define CSR_SEQUENCEREG0B63S0_LSB 0 +#define CSR_SEQUENCEREG0B63S0_MASK GENMASK_32(15, 0) +/* CSR_SEQUENCEREG0B63S1 */ +#define CSR_SEQUENCEREG0B63S1_LSB 0 +#define CSR_SEQUENCEREG0B63S1_MASK GENMASK_32(15, 0) +/* CSR_SEQUENCEREG0B63S2 */ +#define CSR_SEQUENCEREG0B63S2_LSB 0 +#define CSR_SEQUENCEREG0B63S2_MASK GENMASK_32(8, 0) +/* CSR_SEQUENCEREG0B64S0 */ +#define CSR_SEQUENCEREG0B64S0_LSB 0 +#define CSR_SEQUENCEREG0B64S0_MASK GENMASK_32(15, 0) +/* CSR_SEQUENCEREG0B64S1 */ +#define CSR_SEQUENCEREG0B64S1_LSB 0 +#define CSR_SEQUENCEREG0B64S1_MASK GENMASK_32(15, 0) +/* CSR_SEQUENCEREG0B64S2 */ +#define CSR_SEQUENCEREG0B64S2_LSB 0 +#define CSR_SEQUENCEREG0B64S2_MASK GENMASK_32(8, 0) +/* CSR_SEQUENCEREG0B65S0 */ +#define CSR_SEQUENCEREG0B65S0_LSB 0 +#define CSR_SEQUENCEREG0B65S0_MASK GENMASK_32(15, 0) +/* CSR_SEQUENCEREG0B65S1 */ +#define CSR_SEQUENCEREG0B65S1_LSB 0 +#define CSR_SEQUENCEREG0B65S1_MASK GENMASK_32(15, 0) +/* CSR_SEQUENCEREG0B65S2 */ +#define CSR_SEQUENCEREG0B65S2_LSB 0 +#define CSR_SEQUENCEREG0B65S2_MASK GENMASK_32(8, 0) +/* CSR_SEQUENCEREG0B66S0 */ +#define CSR_SEQUENCEREG0B66S0_LSB 0 +#define CSR_SEQUENCEREG0B66S0_MASK GENMASK_32(15, 0) +/* CSR_SEQUENCEREG0B66S1 */ +#define CSR_SEQUENCEREG0B66S1_LSB 0 +#define CSR_SEQUENCEREG0B66S1_MASK GENMASK_32(15, 0) +/* CSR_SEQUENCEREG0B66S2 */ +#define CSR_SEQUENCEREG0B66S2_LSB 0 +#define CSR_SEQUENCEREG0B66S2_MASK GENMASK_32(8, 0) +/* CSR_SEQUENCEREG0B67S0 */ +#define CSR_SEQUENCEREG0B67S0_LSB 0 +#define CSR_SEQUENCEREG0B67S0_MASK GENMASK_32(15, 0) +/* CSR_SEQUENCEREG0B67S1 */ +#define CSR_SEQUENCEREG0B67S1_LSB 0 +#define CSR_SEQUENCEREG0B67S1_MASK GENMASK_32(15, 0) +/* CSR_SEQUENCEREG0B67S2 */ +#define CSR_SEQUENCEREG0B67S2_LSB 0 +#define CSR_SEQUENCEREG0B67S2_MASK GENMASK_32(8, 0) +/* CSR_SEQUENCEREG0B68S0 */ +#define CSR_SEQUENCEREG0B68S0_LSB 0 +#define CSR_SEQUENCEREG0B68S0_MASK GENMASK_32(15, 0) +/* CSR_SEQUENCEREG0B68S1 */ +#define CSR_SEQUENCEREG0B68S1_LSB 0 +#define CSR_SEQUENCEREG0B68S1_MASK GENMASK_32(15, 0) +/* CSR_SEQUENCEREG0B68S2 */ +#define CSR_SEQUENCEREG0B68S2_LSB 0 +#define CSR_SEQUENCEREG0B68S2_MASK GENMASK_32(8, 0) +/* CSR_SEQUENCEREG0B69S0 */ +#define CSR_SEQUENCEREG0B69S0_LSB 0 +#define CSR_SEQUENCEREG0B69S0_MASK GENMASK_32(15, 0) +/* CSR_SEQUENCEREG0B69S1 */ +#define CSR_SEQUENCEREG0B69S1_LSB 0 +#define CSR_SEQUENCEREG0B69S1_MASK GENMASK_32(15, 0) +/* CSR_SEQUENCEREG0B69S2 */ +#define CSR_SEQUENCEREG0B69S2_LSB 0 +#define CSR_SEQUENCEREG0B69S2_MASK GENMASK_32(8, 0) +/* CSR_SEQUENCEREG0B70S0 */ +#define CSR_SEQUENCEREG0B70S0_LSB 0 +#define CSR_SEQUENCEREG0B70S0_MASK GENMASK_32(15, 0) +/* CSR_SEQUENCEREG0B70S1 */ +#define CSR_SEQUENCEREG0B70S1_LSB 0 +#define CSR_SEQUENCEREG0B70S1_MASK GENMASK_32(15, 0) +/* CSR_SEQUENCEREG0B70S2 */ +#define CSR_SEQUENCEREG0B70S2_LSB 0 +#define CSR_SEQUENCEREG0B70S2_MASK GENMASK_32(8, 0) +/* CSR_SEQUENCEREG0B71S0 */ +#define CSR_SEQUENCEREG0B71S0_LSB 0 +#define CSR_SEQUENCEREG0B71S0_MASK GENMASK_32(15, 0) +/* CSR_SEQUENCEREG0B71S1 */ +#define CSR_SEQUENCEREG0B71S1_LSB 0 +#define CSR_SEQUENCEREG0B71S1_MASK GENMASK_32(15, 0) +/* CSR_SEQUENCEREG0B71S2 */ +#define CSR_SEQUENCEREG0B71S2_LSB 0 +#define CSR_SEQUENCEREG0B71S2_MASK GENMASK_32(8, 0) +/* CSR_SEQUENCEREG0B72S0 */ +#define CSR_SEQUENCEREG0B72S0_LSB 0 +#define CSR_SEQUENCEREG0B72S0_MASK GENMASK_32(15, 0) +/* CSR_SEQUENCEREG0B72S1 */ +#define CSR_SEQUENCEREG0B72S1_LSB 0 +#define CSR_SEQUENCEREG0B72S1_MASK GENMASK_32(15, 0) +/* CSR_SEQUENCEREG0B72S2 */ +#define CSR_SEQUENCEREG0B72S2_LSB 0 +#define CSR_SEQUENCEREG0B72S2_MASK GENMASK_32(8, 0) +/* CSR_SEQUENCEREG0B73S0 */ +#define CSR_SEQUENCEREG0B73S0_LSB 0 +#define CSR_SEQUENCEREG0B73S0_MASK GENMASK_32(15, 0) +/* CSR_SEQUENCEREG0B73S1 */ +#define CSR_SEQUENCEREG0B73S1_LSB 0 +#define CSR_SEQUENCEREG0B73S1_MASK GENMASK_32(15, 0) +/* CSR_SEQUENCEREG0B73S2 */ +#define CSR_SEQUENCEREG0B73S2_LSB 0 +#define CSR_SEQUENCEREG0B73S2_MASK GENMASK_32(8, 0) +/* CSR_SEQUENCEREG0B74S0 */ +#define CSR_SEQUENCEREG0B74S0_LSB 0 +#define CSR_SEQUENCEREG0B74S0_MASK GENMASK_32(15, 0) +/* CSR_SEQUENCEREG0B74S1 */ +#define CSR_SEQUENCEREG0B74S1_LSB 0 +#define CSR_SEQUENCEREG0B74S1_MASK GENMASK_32(15, 0) +/* CSR_SEQUENCEREG0B74S2 */ +#define CSR_SEQUENCEREG0B74S2_LSB 0 +#define CSR_SEQUENCEREG0B74S2_MASK GENMASK_32(8, 0) +/* CSR_SEQUENCEREG0B75S0 */ +#define CSR_SEQUENCEREG0B75S0_LSB 0 +#define CSR_SEQUENCEREG0B75S0_MASK GENMASK_32(15, 0) +/* CSR_SEQUENCEREG0B75S1 */ +#define CSR_SEQUENCEREG0B75S1_LSB 0 +#define CSR_SEQUENCEREG0B75S1_MASK GENMASK_32(15, 0) +/* CSR_SEQUENCEREG0B75S2 */ +#define CSR_SEQUENCEREG0B75S2_LSB 0 +#define CSR_SEQUENCEREG0B75S2_MASK GENMASK_32(8, 0) +/* CSR_SEQUENCEREG0B76S0 */ +#define CSR_SEQUENCEREG0B76S0_LSB 0 +#define CSR_SEQUENCEREG0B76S0_MASK GENMASK_32(15, 0) +/* CSR_SEQUENCEREG0B76S1 */ +#define CSR_SEQUENCEREG0B76S1_LSB 0 +#define CSR_SEQUENCEREG0B76S1_MASK GENMASK_32(15, 0) +/* CSR_SEQUENCEREG0B76S2 */ +#define CSR_SEQUENCEREG0B76S2_LSB 0 +#define CSR_SEQUENCEREG0B76S2_MASK GENMASK_32(8, 0) +/* CSR_SEQUENCEREG0B77S0 */ +#define CSR_SEQUENCEREG0B77S0_LSB 0 +#define CSR_SEQUENCEREG0B77S0_MASK GENMASK_32(15, 0) +/* CSR_SEQUENCEREG0B77S1 */ +#define CSR_SEQUENCEREG0B77S1_LSB 0 +#define CSR_SEQUENCEREG0B77S1_MASK GENMASK_32(15, 0) +/* CSR_SEQUENCEREG0B77S2 */ +#define CSR_SEQUENCEREG0B77S2_LSB 0 +#define CSR_SEQUENCEREG0B77S2_MASK GENMASK_32(8, 0) +/* CSR_SEQUENCEREG0B78S0 */ +#define CSR_SEQUENCEREG0B78S0_LSB 0 +#define CSR_SEQUENCEREG0B78S0_MASK GENMASK_32(15, 0) +/* CSR_SEQUENCEREG0B78S1 */ +#define CSR_SEQUENCEREG0B78S1_LSB 0 +#define CSR_SEQUENCEREG0B78S1_MASK GENMASK_32(15, 0) +/* CSR_SEQUENCEREG0B78S2 */ +#define CSR_SEQUENCEREG0B78S2_LSB 0 +#define CSR_SEQUENCEREG0B78S2_MASK GENMASK_32(8, 0) +/* CSR_SEQUENCEREG0B79S0 */ +#define CSR_SEQUENCEREG0B79S0_LSB 0 +#define CSR_SEQUENCEREG0B79S0_MASK GENMASK_32(15, 0) +/* CSR_SEQUENCEREG0B79S1 */ +#define CSR_SEQUENCEREG0B79S1_LSB 0 +#define CSR_SEQUENCEREG0B79S1_MASK GENMASK_32(15, 0) +/* CSR_SEQUENCEREG0B79S2 */ +#define CSR_SEQUENCEREG0B79S2_LSB 0 +#define CSR_SEQUENCEREG0B79S2_MASK GENMASK_32(8, 0) +/* CSR_SEQUENCEREG0B80S0 */ +#define CSR_SEQUENCEREG0B80S0_LSB 0 +#define CSR_SEQUENCEREG0B80S0_MASK GENMASK_32(15, 0) +/* CSR_SEQUENCEREG0B80S1 */ +#define CSR_SEQUENCEREG0B80S1_LSB 0 +#define CSR_SEQUENCEREG0B80S1_MASK GENMASK_32(15, 0) +/* CSR_SEQUENCEREG0B80S2 */ +#define CSR_SEQUENCEREG0B80S2_LSB 0 +#define CSR_SEQUENCEREG0B80S2_MASK GENMASK_32(8, 0) +/* CSR_SEQUENCEREG0B81S0 */ +#define CSR_SEQUENCEREG0B81S0_LSB 0 +#define CSR_SEQUENCEREG0B81S0_MASK GENMASK_32(15, 0) +/* CSR_SEQUENCEREG0B81S1 */ +#define CSR_SEQUENCEREG0B81S1_LSB 0 +#define CSR_SEQUENCEREG0B81S1_MASK GENMASK_32(15, 0) +/* CSR_SEQUENCEREG0B81S2 */ +#define CSR_SEQUENCEREG0B81S2_LSB 0 +#define CSR_SEQUENCEREG0B81S2_MASK GENMASK_32(8, 0) +/* CSR_SEQUENCEREG0B82S0 */ +#define CSR_SEQUENCEREG0B82S0_LSB 0 +#define CSR_SEQUENCEREG0B82S0_MASK GENMASK_32(15, 0) +/* CSR_SEQUENCEREG0B82S1 */ +#define CSR_SEQUENCEREG0B82S1_LSB 0 +#define CSR_SEQUENCEREG0B82S1_MASK GENMASK_32(15, 0) +/* CSR_SEQUENCEREG0B82S2 */ +#define CSR_SEQUENCEREG0B82S2_LSB 0 +#define CSR_SEQUENCEREG0B82S2_MASK GENMASK_32(8, 0) +/* CSR_SEQUENCEREG0B83S0 */ +#define CSR_SEQUENCEREG0B83S0_LSB 0 +#define CSR_SEQUENCEREG0B83S0_MASK GENMASK_32(15, 0) +/* CSR_SEQUENCEREG0B83S1 */ +#define CSR_SEQUENCEREG0B83S1_LSB 0 +#define CSR_SEQUENCEREG0B83S1_MASK GENMASK_32(15, 0) +/* CSR_SEQUENCEREG0B83S2 */ +#define CSR_SEQUENCEREG0B83S2_LSB 0 +#define CSR_SEQUENCEREG0B83S2_MASK GENMASK_32(8, 0) +/* CSR_SEQUENCEREG0B84S0 */ +#define CSR_SEQUENCEREG0B84S0_LSB 0 +#define CSR_SEQUENCEREG0B84S0_MASK GENMASK_32(15, 0) +/* CSR_SEQUENCEREG0B84S1 */ +#define CSR_SEQUENCEREG0B84S1_LSB 0 +#define CSR_SEQUENCEREG0B84S1_MASK GENMASK_32(15, 0) +/* CSR_SEQUENCEREG0B84S2 */ +#define CSR_SEQUENCEREG0B84S2_LSB 0 +#define CSR_SEQUENCEREG0B84S2_MASK GENMASK_32(8, 0) +/* CSR_SEQUENCEREG0B85S0 */ +#define CSR_SEQUENCEREG0B85S0_LSB 0 +#define CSR_SEQUENCEREG0B85S0_MASK GENMASK_32(15, 0) +/* CSR_SEQUENCEREG0B85S1 */ +#define CSR_SEQUENCEREG0B85S1_LSB 0 +#define CSR_SEQUENCEREG0B85S1_MASK GENMASK_32(15, 0) +/* CSR_SEQUENCEREG0B85S2 */ +#define CSR_SEQUENCEREG0B85S2_LSB 0 +#define CSR_SEQUENCEREG0B85S2_MASK GENMASK_32(8, 0) +/* CSR_SEQUENCEREG0B86S0 */ +#define CSR_SEQUENCEREG0B86S0_LSB 0 +#define CSR_SEQUENCEREG0B86S0_MASK GENMASK_32(15, 0) +/* CSR_SEQUENCEREG0B86S1 */ +#define CSR_SEQUENCEREG0B86S1_LSB 0 +#define CSR_SEQUENCEREG0B86S1_MASK GENMASK_32(15, 0) +/* CSR_SEQUENCEREG0B86S2 */ +#define CSR_SEQUENCEREG0B86S2_LSB 0 +#define CSR_SEQUENCEREG0B86S2_MASK GENMASK_32(8, 0) +/* CSR_SEQUENCEREG0B87S0 */ +#define CSR_SEQUENCEREG0B87S0_LSB 0 +#define CSR_SEQUENCEREG0B87S0_MASK GENMASK_32(15, 0) +/* CSR_SEQUENCEREG0B87S1 */ +#define CSR_SEQUENCEREG0B87S1_LSB 0 +#define CSR_SEQUENCEREG0B87S1_MASK GENMASK_32(15, 0) +/* CSR_SEQUENCEREG0B87S2 */ +#define CSR_SEQUENCEREG0B87S2_LSB 0 +#define CSR_SEQUENCEREG0B87S2_MASK GENMASK_32(8, 0) +/* CSR_SEQUENCEREG0B88S0 */ +#define CSR_SEQUENCEREG0B88S0_LSB 0 +#define CSR_SEQUENCEREG0B88S0_MASK GENMASK_32(15, 0) +/* CSR_SEQUENCEREG0B88S1 */ +#define CSR_SEQUENCEREG0B88S1_LSB 0 +#define CSR_SEQUENCEREG0B88S1_MASK GENMASK_32(15, 0) +/* CSR_SEQUENCEREG0B88S2 */ +#define CSR_SEQUENCEREG0B88S2_LSB 0 +#define CSR_SEQUENCEREG0B88S2_MASK GENMASK_32(8, 0) +/* CSR_SEQUENCEREG0B89S0 */ +#define CSR_SEQUENCEREG0B89S0_LSB 0 +#define CSR_SEQUENCEREG0B89S0_MASK GENMASK_32(15, 0) +/* CSR_SEQUENCEREG0B89S1 */ +#define CSR_SEQUENCEREG0B89S1_LSB 0 +#define CSR_SEQUENCEREG0B89S1_MASK GENMASK_32(15, 0) +/* CSR_SEQUENCEREG0B89S2 */ +#define CSR_SEQUENCEREG0B89S2_LSB 0 +#define CSR_SEQUENCEREG0B89S2_MASK GENMASK_32(8, 0) +/* CSR_SEQUENCEREG0B90S0 */ +#define CSR_SEQUENCEREG0B90S0_LSB 0 +#define CSR_SEQUENCEREG0B90S0_MASK GENMASK_32(15, 0) +/* CSR_SEQUENCEREG0B90S1 */ +#define CSR_SEQUENCEREG0B90S1_LSB 0 +#define CSR_SEQUENCEREG0B90S1_MASK GENMASK_32(15, 0) +/* CSR_SEQUENCEREG0B90S2 */ +#define CSR_SEQUENCEREG0B90S2_LSB 0 +#define CSR_SEQUENCEREG0B90S2_MASK GENMASK_32(8, 0) +/* CSR_SEQUENCEREG0B91S0 */ +#define CSR_SEQUENCEREG0B91S0_LSB 0 +#define CSR_SEQUENCEREG0B91S0_MASK GENMASK_32(15, 0) +/* CSR_SEQUENCEREG0B91S1 */ +#define CSR_SEQUENCEREG0B91S1_LSB 0 +#define CSR_SEQUENCEREG0B91S1_MASK GENMASK_32(15, 0) +/* CSR_SEQUENCEREG0B91S2 */ +#define CSR_SEQUENCEREG0B91S2_LSB 0 +#define CSR_SEQUENCEREG0B91S2_MASK GENMASK_32(8, 0) +/* CSR_SEQUENCEREG0B92S0 */ +#define CSR_SEQUENCEREG0B92S0_LSB 0 +#define CSR_SEQUENCEREG0B92S0_MASK GENMASK_32(15, 0) +/* CSR_SEQUENCEREG0B92S1 */ +#define CSR_SEQUENCEREG0B92S1_LSB 0 +#define CSR_SEQUENCEREG0B92S1_MASK GENMASK_32(15, 0) +/* CSR_SEQUENCEREG0B92S2 */ +#define CSR_SEQUENCEREG0B92S2_LSB 0 +#define CSR_SEQUENCEREG0B92S2_MASK GENMASK_32(8, 0) +/* CSR_SEQUENCEREG0B93S0 */ +#define CSR_SEQUENCEREG0B93S0_LSB 0 +#define CSR_SEQUENCEREG0B93S0_MASK GENMASK_32(15, 0) +/* CSR_SEQUENCEREG0B93S1 */ +#define CSR_SEQUENCEREG0B93S1_LSB 0 +#define CSR_SEQUENCEREG0B93S1_MASK GENMASK_32(15, 0) +/* CSR_SEQUENCEREG0B93S2 */ +#define CSR_SEQUENCEREG0B93S2_LSB 0 +#define CSR_SEQUENCEREG0B93S2_MASK GENMASK_32(8, 0) +/* CSR_SEQUENCEREG0B94S0 */ +#define CSR_SEQUENCEREG0B94S0_LSB 0 +#define CSR_SEQUENCEREG0B94S0_MASK GENMASK_32(15, 0) +/* CSR_SEQUENCEREG0B94S1 */ +#define CSR_SEQUENCEREG0B94S1_LSB 0 +#define CSR_SEQUENCEREG0B94S1_MASK GENMASK_32(15, 0) +/* CSR_SEQUENCEREG0B94S2 */ +#define CSR_SEQUENCEREG0B94S2_LSB 0 +#define CSR_SEQUENCEREG0B94S2_MASK GENMASK_32(8, 0) +/* CSR_SEQUENCEREG0B95S0 */ +#define CSR_SEQUENCEREG0B95S0_LSB 0 +#define CSR_SEQUENCEREG0B95S0_MASK GENMASK_32(15, 0) +/* CSR_SEQUENCEREG0B95S1 */ +#define CSR_SEQUENCEREG0B95S1_LSB 0 +#define CSR_SEQUENCEREG0B95S1_MASK GENMASK_32(15, 0) +/* CSR_SEQUENCEREG0B95S2 */ +#define CSR_SEQUENCEREG0B95S2_LSB 0 +#define CSR_SEQUENCEREG0B95S2_MASK GENMASK_32(8, 0) +/* CSR_SEQUENCEREG0B96S0 */ +#define CSR_SEQUENCEREG0B96S0_LSB 0 +#define CSR_SEQUENCEREG0B96S0_MASK GENMASK_32(15, 0) +/* CSR_SEQUENCEREG0B96S1 */ +#define CSR_SEQUENCEREG0B96S1_LSB 0 +#define CSR_SEQUENCEREG0B96S1_MASK GENMASK_32(15, 0) +/* CSR_SEQUENCEREG0B96S2 */ +#define CSR_SEQUENCEREG0B96S2_LSB 0 +#define CSR_SEQUENCEREG0B96S2_MASK GENMASK_32(8, 0) +/* CSR_SEQUENCEREG0B97S0 */ +#define CSR_SEQUENCEREG0B97S0_LSB 0 +#define CSR_SEQUENCEREG0B97S0_MASK GENMASK_32(15, 0) +/* CSR_SEQUENCEREG0B97S1 */ +#define CSR_SEQUENCEREG0B97S1_LSB 0 +#define CSR_SEQUENCEREG0B97S1_MASK GENMASK_32(15, 0) +/* CSR_SEQUENCEREG0B97S2 */ +#define CSR_SEQUENCEREG0B97S2_LSB 0 +#define CSR_SEQUENCEREG0B97S2_MASK GENMASK_32(8, 0) +/* CSR_SEQUENCEREG0B98S0 */ +#define CSR_SEQUENCEREG0B98S0_LSB 0 +#define CSR_SEQUENCEREG0B98S0_MASK GENMASK_32(15, 0) +/* CSR_SEQUENCEREG0B98S1 */ +#define CSR_SEQUENCEREG0B98S1_LSB 0 +#define CSR_SEQUENCEREG0B98S1_MASK GENMASK_32(15, 0) +/* CSR_SEQUENCEREG0B98S2 */ +#define CSR_SEQUENCEREG0B98S2_LSB 0 +#define CSR_SEQUENCEREG0B98S2_MASK GENMASK_32(8, 0) +/* CSR_SEQUENCEREG0B99S0 */ +#define CSR_SEQUENCEREG0B99S0_LSB 0 +#define CSR_SEQUENCEREG0B99S0_MASK GENMASK_32(15, 0) +/* CSR_SEQUENCEREG0B99S1 */ +#define CSR_SEQUENCEREG0B99S1_LSB 0 +#define CSR_SEQUENCEREG0B99S1_MASK GENMASK_32(15, 0) +/* CSR_SEQUENCEREG0B99S2 */ +#define CSR_SEQUENCEREG0B99S2_LSB 0 +#define CSR_SEQUENCEREG0B99S2_MASK GENMASK_32(8, 0) +/* CSR_SEQUENCEREG0B100S0 */ +#define CSR_SEQUENCEREG0B100S0_LSB 0 +#define CSR_SEQUENCEREG0B100S0_MASK GENMASK_32(15, 0) +/* CSR_SEQUENCEREG0B100S1 */ +#define CSR_SEQUENCEREG0B100S1_LSB 0 +#define CSR_SEQUENCEREG0B100S1_MASK GENMASK_32(15, 0) +/* CSR_SEQUENCEREG0B100S2 */ +#define CSR_SEQUENCEREG0B100S2_LSB 0 +#define CSR_SEQUENCEREG0B100S2_MASK GENMASK_32(8, 0) +/* CSR_SEQUENCEREG0B101S0 */ +#define CSR_SEQUENCEREG0B101S0_LSB 0 +#define CSR_SEQUENCEREG0B101S0_MASK GENMASK_32(15, 0) +/* CSR_SEQUENCEREG0B101S1 */ +#define CSR_SEQUENCEREG0B101S1_LSB 0 +#define CSR_SEQUENCEREG0B101S1_MASK GENMASK_32(15, 0) +/* CSR_SEQUENCEREG0B101S2 */ +#define CSR_SEQUENCEREG0B101S2_LSB 0 +#define CSR_SEQUENCEREG0B101S2_MASK GENMASK_32(8, 0) +/* CSR_SEQUENCEREG0B102S0 */ +#define CSR_SEQUENCEREG0B102S0_LSB 0 +#define CSR_SEQUENCEREG0B102S0_MASK GENMASK_32(15, 0) +/* CSR_SEQUENCEREG0B102S1 */ +#define CSR_SEQUENCEREG0B102S1_LSB 0 +#define CSR_SEQUENCEREG0B102S1_MASK GENMASK_32(15, 0) +/* CSR_SEQUENCEREG0B102S2 */ +#define CSR_SEQUENCEREG0B102S2_LSB 0 +#define CSR_SEQUENCEREG0B102S2_MASK GENMASK_32(8, 0) +/* CSR_SEQUENCEREG0B103S0 */ +#define CSR_SEQUENCEREG0B103S0_LSB 0 +#define CSR_SEQUENCEREG0B103S0_MASK GENMASK_32(15, 0) +/* CSR_SEQUENCEREG0B103S1 */ +#define CSR_SEQUENCEREG0B103S1_LSB 0 +#define CSR_SEQUENCEREG0B103S1_MASK GENMASK_32(15, 0) +/* CSR_SEQUENCEREG0B103S2 */ +#define CSR_SEQUENCEREG0B103S2_LSB 0 +#define CSR_SEQUENCEREG0B103S2_MASK GENMASK_32(8, 0) +/* CSR_SEQUENCEREG0B104S0 */ +#define CSR_SEQUENCEREG0B104S0_LSB 0 +#define CSR_SEQUENCEREG0B104S0_MASK GENMASK_32(15, 0) +/* CSR_SEQUENCEREG0B104S1 */ +#define CSR_SEQUENCEREG0B104S1_LSB 0 +#define CSR_SEQUENCEREG0B104S1_MASK GENMASK_32(15, 0) +/* CSR_SEQUENCEREG0B104S2 */ +#define CSR_SEQUENCEREG0B104S2_LSB 0 +#define CSR_SEQUENCEREG0B104S2_MASK GENMASK_32(8, 0) +/* CSR_SEQUENCEREG0B105S0 */ +#define CSR_SEQUENCEREG0B105S0_LSB 0 +#define CSR_SEQUENCEREG0B105S0_MASK GENMASK_32(15, 0) +/* CSR_SEQUENCEREG0B105S1 */ +#define CSR_SEQUENCEREG0B105S1_LSB 0 +#define CSR_SEQUENCEREG0B105S1_MASK GENMASK_32(15, 0) +/* CSR_SEQUENCEREG0B105S2 */ +#define CSR_SEQUENCEREG0B105S2_LSB 0 +#define CSR_SEQUENCEREG0B105S2_MASK GENMASK_32(8, 0) +/* CSR_SEQUENCEREG0B106S0 */ +#define CSR_SEQUENCEREG0B106S0_LSB 0 +#define CSR_SEQUENCEREG0B106S0_MASK GENMASK_32(15, 0) +/* CSR_SEQUENCEREG0B106S1 */ +#define CSR_SEQUENCEREG0B106S1_LSB 0 +#define CSR_SEQUENCEREG0B106S1_MASK GENMASK_32(15, 0) +/* CSR_SEQUENCEREG0B106S2 */ +#define CSR_SEQUENCEREG0B106S2_LSB 0 +#define CSR_SEQUENCEREG0B106S2_MASK GENMASK_32(8, 0) +/* CSR_SEQUENCEREG0B107S0 */ +#define CSR_SEQUENCEREG0B107S0_LSB 0 +#define CSR_SEQUENCEREG0B107S0_MASK GENMASK_32(15, 0) +/* CSR_SEQUENCEREG0B107S1 */ +#define CSR_SEQUENCEREG0B107S1_LSB 0 +#define CSR_SEQUENCEREG0B107S1_MASK GENMASK_32(15, 0) +/* CSR_SEQUENCEREG0B107S2 */ +#define CSR_SEQUENCEREG0B107S2_LSB 0 +#define CSR_SEQUENCEREG0B107S2_MASK GENMASK_32(8, 0) +/* CSR_SEQUENCEREG0B108S0 */ +#define CSR_SEQUENCEREG0B108S0_LSB 0 +#define CSR_SEQUENCEREG0B108S0_MASK GENMASK_32(15, 0) +/* CSR_SEQUENCEREG0B108S1 */ +#define CSR_SEQUENCEREG0B108S1_LSB 0 +#define CSR_SEQUENCEREG0B108S1_MASK GENMASK_32(15, 0) +/* CSR_SEQUENCEREG0B108S2 */ +#define CSR_SEQUENCEREG0B108S2_LSB 0 +#define CSR_SEQUENCEREG0B108S2_MASK GENMASK_32(8, 0) +/* CSR_SEQUENCEREG0B109S0 */ +#define CSR_SEQUENCEREG0B109S0_LSB 0 +#define CSR_SEQUENCEREG0B109S0_MASK GENMASK_32(15, 0) +/* CSR_SEQUENCEREG0B109S1 */ +#define CSR_SEQUENCEREG0B109S1_LSB 0 +#define CSR_SEQUENCEREG0B109S1_MASK GENMASK_32(15, 0) +/* CSR_SEQUENCEREG0B109S2 */ +#define CSR_SEQUENCEREG0B109S2_LSB 0 +#define CSR_SEQUENCEREG0B109S2_MASK GENMASK_32(8, 0) +/* CSR_SEQUENCEREG0B110S0 */ +#define CSR_SEQUENCEREG0B110S0_LSB 0 +#define CSR_SEQUENCEREG0B110S0_MASK GENMASK_32(15, 0) +/* CSR_SEQUENCEREG0B110S1 */ +#define CSR_SEQUENCEREG0B110S1_LSB 0 +#define CSR_SEQUENCEREG0B110S1_MASK GENMASK_32(15, 0) +/* CSR_SEQUENCEREG0B110S2 */ +#define CSR_SEQUENCEREG0B110S2_LSB 0 +#define CSR_SEQUENCEREG0B110S2_MASK GENMASK_32(8, 0) +/* CSR_SEQUENCEREG0B111S0 */ +#define CSR_SEQUENCEREG0B111S0_LSB 0 +#define CSR_SEQUENCEREG0B111S0_MASK GENMASK_32(15, 0) +/* CSR_SEQUENCEREG0B111S1 */ +#define CSR_SEQUENCEREG0B111S1_LSB 0 +#define CSR_SEQUENCEREG0B111S1_MASK GENMASK_32(15, 0) +/* CSR_SEQUENCEREG0B111S2 */ +#define CSR_SEQUENCEREG0B111S2_LSB 0 +#define CSR_SEQUENCEREG0B111S2_MASK GENMASK_32(8, 0) +/* CSR_SEQUENCEREG0B112S0 */ +#define CSR_SEQUENCEREG0B112S0_LSB 0 +#define CSR_SEQUENCEREG0B112S0_MASK GENMASK_32(15, 0) +/* CSR_SEQUENCEREG0B112S1 */ +#define CSR_SEQUENCEREG0B112S1_LSB 0 +#define CSR_SEQUENCEREG0B112S1_MASK GENMASK_32(15, 0) +/* CSR_SEQUENCEREG0B112S2 */ +#define CSR_SEQUENCEREG0B112S2_LSB 0 +#define CSR_SEQUENCEREG0B112S2_MASK GENMASK_32(8, 0) +/* CSR_SEQUENCEREG0B113S0 */ +#define CSR_SEQUENCEREG0B113S0_LSB 0 +#define CSR_SEQUENCEREG0B113S0_MASK GENMASK_32(15, 0) +/* CSR_SEQUENCEREG0B113S1 */ +#define CSR_SEQUENCEREG0B113S1_LSB 0 +#define CSR_SEQUENCEREG0B113S1_MASK GENMASK_32(15, 0) +/* CSR_SEQUENCEREG0B113S2 */ +#define CSR_SEQUENCEREG0B113S2_LSB 0 +#define CSR_SEQUENCEREG0B113S2_MASK GENMASK_32(8, 0) +/* CSR_SEQUENCEREG0B114S0 */ +#define CSR_SEQUENCEREG0B114S0_LSB 0 +#define CSR_SEQUENCEREG0B114S0_MASK GENMASK_32(15, 0) +/* CSR_SEQUENCEREG0B114S1 */ +#define CSR_SEQUENCEREG0B114S1_LSB 0 +#define CSR_SEQUENCEREG0B114S1_MASK GENMASK_32(15, 0) +/* CSR_SEQUENCEREG0B114S2 */ +#define CSR_SEQUENCEREG0B114S2_LSB 0 +#define CSR_SEQUENCEREG0B114S2_MASK GENMASK_32(8, 0) +/* CSR_SEQUENCEREG0B115S0 */ +#define CSR_SEQUENCEREG0B115S0_LSB 0 +#define CSR_SEQUENCEREG0B115S0_MASK GENMASK_32(15, 0) +/* CSR_SEQUENCEREG0B115S1 */ +#define CSR_SEQUENCEREG0B115S1_LSB 0 +#define CSR_SEQUENCEREG0B115S1_MASK GENMASK_32(15, 0) +/* CSR_SEQUENCEREG0B115S2 */ +#define CSR_SEQUENCEREG0B115S2_LSB 0 +#define CSR_SEQUENCEREG0B115S2_MASK GENMASK_32(8, 0) +/* CSR_SEQUENCEREG0B116S0 */ +#define CSR_SEQUENCEREG0B116S0_LSB 0 +#define CSR_SEQUENCEREG0B116S0_MASK GENMASK_32(15, 0) +/* CSR_SEQUENCEREG0B116S1 */ +#define CSR_SEQUENCEREG0B116S1_LSB 0 +#define CSR_SEQUENCEREG0B116S1_MASK GENMASK_32(15, 0) +/* CSR_SEQUENCEREG0B116S2 */ +#define CSR_SEQUENCEREG0B116S2_LSB 0 +#define CSR_SEQUENCEREG0B116S2_MASK GENMASK_32(8, 0) +/* CSR_SEQUENCEREG0B117S0 */ +#define CSR_SEQUENCEREG0B117S0_LSB 0 +#define CSR_SEQUENCEREG0B117S0_MASK GENMASK_32(15, 0) +/* CSR_SEQUENCEREG0B117S1 */ +#define CSR_SEQUENCEREG0B117S1_LSB 0 +#define CSR_SEQUENCEREG0B117S1_MASK GENMASK_32(15, 0) +/* CSR_SEQUENCEREG0B117S2 */ +#define CSR_SEQUENCEREG0B117S2_LSB 0 +#define CSR_SEQUENCEREG0B117S2_MASK GENMASK_32(8, 0) +/* CSR_SEQUENCEREG0B118S0 */ +#define CSR_SEQUENCEREG0B118S0_LSB 0 +#define CSR_SEQUENCEREG0B118S0_MASK GENMASK_32(15, 0) +/* CSR_SEQUENCEREG0B118S1 */ +#define CSR_SEQUENCEREG0B118S1_LSB 0 +#define CSR_SEQUENCEREG0B118S1_MASK GENMASK_32(15, 0) +/* CSR_SEQUENCEREG0B118S2 */ +#define CSR_SEQUENCEREG0B118S2_LSB 0 +#define CSR_SEQUENCEREG0B118S2_MASK GENMASK_32(8, 0) +/* CSR_SEQUENCEREG0B119S0 */ +#define CSR_SEQUENCEREG0B119S0_LSB 0 +#define CSR_SEQUENCEREG0B119S0_MASK GENMASK_32(15, 0) +/* CSR_SEQUENCEREG0B119S1 */ +#define CSR_SEQUENCEREG0B119S1_LSB 0 +#define CSR_SEQUENCEREG0B119S1_MASK GENMASK_32(15, 0) +/* CSR_SEQUENCEREG0B119S2 */ +#define CSR_SEQUENCEREG0B119S2_LSB 0 +#define CSR_SEQUENCEREG0B119S2_MASK GENMASK_32(8, 0) +/* CSR_SEQUENCEREG0B120S0 */ +#define CSR_SEQUENCEREG0B120S0_LSB 0 +#define CSR_SEQUENCEREG0B120S0_MASK GENMASK_32(15, 0) +/* CSR_SEQUENCEREG0B120S1 */ +#define CSR_SEQUENCEREG0B120S1_LSB 0 +#define CSR_SEQUENCEREG0B120S1_MASK GENMASK_32(15, 0) +/* CSR_SEQUENCEREG0B120S2 */ +#define CSR_SEQUENCEREG0B120S2_LSB 0 +#define CSR_SEQUENCEREG0B120S2_MASK GENMASK_32(8, 0) +/* CSR_SEQUENCEREG0B121S0 */ +#define CSR_SEQUENCEREG0B121S0_LSB 0 +#define CSR_SEQUENCEREG0B121S0_MASK GENMASK_32(15, 0) +/* CSR_SEQUENCEREG0B121S1 */ +#define CSR_SEQUENCEREG0B121S1_LSB 0 +#define CSR_SEQUENCEREG0B121S1_MASK GENMASK_32(15, 0) +/* CSR_SEQUENCEREG0B121S2 */ +#define CSR_SEQUENCEREG0B121S2_LSB 0 +#define CSR_SEQUENCEREG0B121S2_MASK GENMASK_32(8, 0) +/* CSR_SEQ0BGPR1 */ +#define CSR_SEQ0BGPR1_LSB 0 +#define CSR_SEQ0BGPR1_MASK GENMASK_32(15, 0) +/* CSR_SEQ0BGPR2 */ +#define CSR_SEQ0BGPR2_LSB 0 +#define CSR_SEQ0BGPR2_MASK GENMASK_32(15, 0) +/* CSR_SEQ0BGPR3 */ +#define CSR_SEQ0BGPR3_LSB 0 +#define CSR_SEQ0BGPR3_MASK GENMASK_32(15, 0) +/* CSR_SEQ0BGPR4 */ +#define CSR_SEQ0BGPR4_LSB 0 +#define CSR_SEQ0BGPR4_MASK GENMASK_32(15, 0) +/* CSR_SEQ0BGPR5 */ +#define CSR_SEQ0BGPR5_LSB 0 +#define CSR_SEQ0BGPR5_MASK GENMASK_32(15, 0) +/* CSR_SEQ0BGPR6 */ +#define CSR_SEQ0BGPR6_LSB 0 +#define CSR_SEQ0BGPR6_MASK GENMASK_32(15, 0) +/* CSR_SEQ0BGPR7 */ +#define CSR_SEQ0BGPR7_LSB 0 +#define CSR_SEQ0BGPR7_MASK GENMASK_32(15, 0) +/* CSR_SEQ0BGPR8 */ +#define CSR_SEQ0BGPR8_LSB 0 +#define CSR_SEQ0BGPR8_MASK GENMASK_32(15, 0) +/* CSR_SEQ0BFIXEDADDRBITS */ +#define CSR_SEQ0BFIXEDADDRBITS_LSB 0 +#define CSR_SEQ0BFIXEDADDRBITS_MASK GENMASK_32(6, 0) +#define CSR_SEQ0BCHIPLETBITS_LSB 0 +#define CSR_SEQ0BCHIPLETBITS_MASK GENMASK_32(3, 0) +#define CSR_SEQ0BPSTATEBITS_LSB 4 +#define CSR_SEQ0BPSTATEBITS_MASK GENMASK_32(6, 4) + +/* DRTUB0 register offsets */ +/* CSR_DCTSHADOWREGS */ +#define CSR_DCTSHADOWREGS_LSB 0 +#define CSR_DCTSHADOWREGS_MASK BIT(0) +#define CSR_DCTWRITEPROTSHADOW_LSB 0 +#define CSR_DCTWRITEPROTSHADOW_MASK BIT(0) +/* CSR_DCTWRITEONLYSHADOW */ +#define CSR_DCTWRITEONLYSHADOW_LSB 0 +#define CSR_DCTWRITEONLYSHADOW_MASK GENMASK_32(15, 0) +/* CSR_UCTWRITEONLY */ +#define CSR_UCTWRITEONLY_LSB 0 +#define CSR_UCTWRITEONLY_MASK GENMASK_32(15, 0) +/* CSR_UCTWRITEPROT */ +#define CSR_UCTWRITEPROT_LSB 0 +#define CSR_UCTWRITEPROT_MASK BIT(0) +/* CSR_UCTDATWRITEONLY */ +#define CSR_UCTDATWRITEONLY_LSB 0 +#define CSR_UCTDATWRITEONLY_MASK GENMASK_32(15, 0) +/* CSR_UCTDATWRITEPROT */ +#define CSR_UCTDATWRITEPROT_LSB 0 +#define CSR_UCTDATWRITEPROT_MASK BIT(0) +/* CSR_UCTLERR */ +#define CSR_UCTLERR_LSB 0 +#define CSR_UCTLERR_MASK BIT(0) +/* CSR_UCCLKHCLKENABLES */ +#define CSR_UCCLKHCLKENABLES_LSB 0 +#define CSR_UCCLKHCLKENABLES_MASK GENMASK_32(1, 0) +#define CSR_UCCLKEN_LSB 0 +#define CSR_UCCLKEN_MASK BIT(0) +#define CSR_HCLKEN_LSB 1 +#define CSR_HCLKEN_MASK BIT(1) +/* CSR_CURPSTATE0B */ +#define CSR_CURPSTATE0B_LSB 0 +#define CSR_CURPSTATE0B_MASK GENMASK_32(3, 0) +/* CSR_CLRWAKEUPSTICKY */ +#define CSR_CLRWAKEUPSTICKY_LSB 0 +#define CSR_CLRWAKEUPSTICKY_MASK GENMASK_32(3, 0) +/* CSR_WAKEUPMASK */ +#define CSR_WAKEUPMASK_LSB 0 +#define CSR_WAKEUPMASK_MASK GENMASK_32(3, 0) +/* CSR_CUSTPUBREV */ +#define CSR_CUSTPUBREV_LSB 0 +#define CSR_CUSTPUBREV_MASK GENMASK_32(5, 0) +/* CSR_PUBREV */ +#define CSR_PUBREV_LSB 0 +#define CSR_PUBREV_MASK GENMASK_32(15, 0) +#define CSR_RESERVEDPUBREV_LSB 0 +#define CSR_RESERVEDPUBREV_MASK GENMASK_32(3, 0) +#define CSR_PUBMNR_LSB 4 +#define CSR_PUBMNR_MASK GENMASK_32(7, 4) +#define CSR_PUBMDR_LSB 8 +#define CSR_PUBMDR_MASK GENMASK_32(11, 8) +#define CSR_PUBMJR_LSB 12 +#define CSR_PUBMJR_MASK GENMASK_32(15, 12) + +/* APBONLY0 register offsets */ +/* CSR_MICROCONTMUXSEL */ +#define CSR_MICROCONTMUXSEL_LSB 0 +#define CSR_MICROCONTMUXSEL_MASK BIT(0) +/* CSR_UCTSHADOWREGS */ +#define CSR_UCTSHADOWREGS_LSB 0 +#define CSR_UCTSHADOWREGS_MASK GENMASK_32(1, 0) +#define CSR_UCTWRITEPROTSHADOW_LSB 0 +#define CSR_UCTWRITEPROTSHADOW_MASK BIT(0) +#define CSR_UCTDATWRITEPROTSHADOW_LSB 1 +#define CSR_UCTDATWRITEPROTSHADOW_MASK BIT(1) +/* CSR_DCTWRITEONLY */ +#define CSR_DCTWRITEONLY_LSB 0 +#define CSR_DCTWRITEONLY_MASK GENMASK_32(15, 0) +/* CSR_DCTWRITEPROT */ +#define CSR_DCTWRITEPROT_LSB 0 +#define CSR_DCTWRITEPROT_MASK BIT(0) +/* CSR_UCTWRITEONLYSHADOW */ +#define CSR_UCTWRITEONLYSHADOW_LSB 0 +#define CSR_UCTWRITEONLYSHADOW_MASK GENMASK_32(15, 0) +/* CSR_UCTDATWRITEONLYSHADOW */ +#define CSR_UCTDATWRITEONLYSHADOW_LSB 0 +#define CSR_UCTDATWRITEONLYSHADOW_MASK GENMASK_32(15, 0) +/* CSR_NEVERGATECSRCLOCK */ +#define CSR_NEVERGATECSRCLOCK_LSB 0 +#define CSR_NEVERGATECSRCLOCK_MASK BIT(0) +/* CSR_DFICFGRDDATAVALIDTICKS */ +#define CSR_DFICFGRDDATAVALIDTICKS_LSB 0 +#define CSR_DFICFGRDDATAVALIDTICKS_MASK GENMASK_32(5, 0) +/* CSR_MICRORESET */ +#define CSR_MICRORESET_LSB 0 +#define CSR_MICRORESET_MASK GENMASK_32(3, 0) +#define CSR_STALLTOMICRO_LSB 0 +#define CSR_STALLTOMICRO_MASK BIT(0) +#define CSR_TESTWAKEUP_LSB 1 +#define CSR_TESTWAKEUP_MASK BIT(1) +#define CSR_RSVDMICRO_LSB 2 +#define CSR_RSVDMICRO_MASK BIT(2) +#define CSR_RESETTOMICRO_LSB 3 +#define CSR_RESETTOMICRO_MASK BIT(3) +/* CSR_SEQUENCEROVERRIDE */ +#define CSR_SEQUENCEROVERRIDE_LSB 0 +#define CSR_SEQUENCEROVERRIDE_MASK GENMASK_32(10, 0) +#define CSR_FORCESEQ0BDFIFREQ_LSB 0 +#define CSR_FORCESEQ0BDFIFREQ_MASK GENMASK_32(4, 0) +#define CSR_FORCESEQ0BSTART_LSB 5 +#define CSR_FORCESEQ0BSTART_MASK BIT(5) +#define CSR_FORCESEQ0BSTOP_LSB 6 +#define CSR_FORCESEQ0BSTOP_MASK BIT(6) +#define CSR_BLOCKSEQ0BREQUESTS_LSB 7 +#define CSR_BLOCKSEQ0BREQUESTS_MASK BIT(7) +#define CSR_BLOCKSEQ0BACK_LSB 8 +#define CSR_BLOCKSEQ0BACK_MASK BIT(8) +#define CSR_DISABLETERMINATEFLAG_LSB 9 +#define CSR_DISABLETERMINATEFLAG_MASK BIT(9) +#define CSR_SELECTDFIFREQTOGPRMUX_LSB 10 +#define CSR_SELECTDFIFREQTOGPRMUX_MASK BIT(10) +/* CSR_DFIINITCOMPLETESHADOW */ +#define CSR_DFIINITCOMPLETESHADOW_LSB 0 +#define CSR_DFIINITCOMPLETESHADOW_MASK BIT(0) + +/* Fields brought to you by the letter B */ +#define B_MIN 0U +#define B_MAX 1U +#define B0 0x0U +#define B1 0x100U +#define BBRD 0xF00U +#define BB_MIN 0U +#define BB_MAX 15U +#define BB0 0x0U +#define BB1 0x1000U +#define BB2 0x2000U +#define BB3 0x3000U +#define BB4 0x4000U +#define BB5 0x5000U +#define BB6 0x6000U +#define BB7 0x7000U +#define BB8 0x8000U +#define BB9 0x9000U +#define BB10 0xA000U +#define BB11 0xB000U +#define BB12 0xC000U +#define BB13 0xD000U +#define BB14 0xE000U +#define BB15 0xF000U +#define BBBRD 0xF000U +/* Fields brought to you by the letter C */ +#define C_MIN 0U +#define C_MAX 15U +#define C0 0x0U +#define C1 0x1000U +#define C2 0x2000U +#define C3 0x3000U +#define C4 0x4000U +#define C5 0x5000U +#define C6 0x6000U +#define C7 0x7000U +#define C8 0x8000U +#define C9 0x9000U +#define C10 0xA000U +#define C11 0xB000U +#define C12 0xC000U +#define C13 0xD000U +#define C14 0xE000U +#define C15 0xF000U +#define CBRD 0xF000U +/* Fields brought to you by the letter D */ +#define D_MIN 0U +#define D_MAX 3U +#define D0 0x0U +#define D1 0x100U +#define D2 0x200U +#define D3 0x300U +#define DBRD 0xF00U +/* Fields brought to you by the letter I */ +#define I_MIN 0U +#define I_MAX 8U +#define I0 0x0U +#define I1 0x100U +#define I2 0x200U +#define I3 0x300U +#define I4 0x400U +#define I5 0x500U +#define I6 0x600U +#define I7 0x700U +#define I8 0x800U +#define IBRD 0xF00U +/* Fields brought to you by the letter J */ +#define J_MIN 0U +#define J_MAX 0U +#define J0 0x0U +#define JBRD 0xF00U +/* Fields brought to you by the letter L */ +#define L_MIN 0U +#define L_MAX 13U +#define L0 0x0U +#define L1 0x100U +#define L2 0x200U +#define L3 0x300U +#define L4 0x400U +#define L5 0x500U +#define L6 0x600U +#define L7 0x700U +#define L8 0x800U +#define L9 0x900U +#define L10 0xA00U +#define L11 0xB00U +#define L12 0xC00U +#define L13 0xD00U +#define LBRD 0xF00U +/* Fields brought to you by the letter M */ +#define M_MIN 0U +#define M_MAX 8U +#define M0 0x0U +#define M1 0x100U +#define M2 0x200U +#define M3 0x300U +#define M4 0x400U +#define M5 0x500U +#define M6 0x600U +#define M7 0x700U +#define M8 0x800U +#define MBRD 0xF00U +/* Fields brought to you by the letter N */ +#define N_MIN 0U +#define N_MAX 15U +#define N0 0x0U +#define N1 0x100U +#define N2 0x200U +#define N3 0x300U +#define N4 0x400U +#define N5 0x500U +#define N6 0x600U +#define N7 0x700U +#define N8 0x800U +#define N9 0x900U +#define N10 0xA00U +#define N11 0xB00U +#define N12 0xC00U +#define N13 0xD00U +#define N14 0xE00U +#define N15 0xF00U +#define NBRD 0xF00U +/* Fields brought to you by the letter P */ +#define P_MIN 0U +#define P_MAX 3U +#define P0 0x0U +#define P1 0x100000U +#define P2 0x200000U +#define P3 0x300000U +#define PBRD 0x700000U +#define PP_MIN 0U +#define PP_MAX 3U +#define PP0 0x0U +#define PP1 0x100000U +#define PP2 0x200000U +#define PP3 0x300000U +#define PPBRD 0x700000U +/* Fields brought to you by the letter Q */ +#define Q_MIN 0U +#define Q_MAX 3U +#define Q0 0x0U +#define Q1 0x100000U +#define Q2 0x200000U +#define Q3 0x300000U +#define QBRD 0x700000U +/* Fields brought to you by the letter R */ +#define R_MIN 0U +#define R_MAX 8U +#define R0 0x0U +#define R1 0x100U +#define R2 0x200U +#define R3 0x300U +#define R4 0x400U +#define R5 0x500U +#define R6 0x600U +#define R7 0x700U +#define R8 0x800U +#define RBRD 0xF00U +/* Fields brought to you by the letter T */ +#define T_MIN 0U +#define T_MAX 15U +#define T0 0x0U +#define T1 0x10000U +#define T2 0x20000U +#define T3 0x30000U +#define T4 0x40000U +#define T5 0x50000U +#define T6 0x60000U +#define T7 0x70000U +#define T8 0x80000U +#define T9 0x90000U +#define T10 0xA0000U +#define T11 0xB0000U +#define T12 0xC0000U +#define T13 0xD0000U +#define T14 0xE0000U +#define T15 0xF0000U +#define TBRD 0xF0000U +/* Fields brought to you by the letter U */ +#define U_MIN 0U +#define U_MAX 1U +#define U0 0x0U +#define U1 0x100U +#define UBRD 0xF00U +/* Fields brought to you by the letter Y */ +#define Y_MIN 0U +#define Y_MAX 0U +#define Y0 0x0U +#define YBRD 0xF000000U + +#define TACSM 0x40000U +#define TACSMBRD 0x4F000U +#define TALL 0xF0000U +#define TALLBRD 0xFF000U +#define TANIB 0x0U +#define TANIBBRD 0xF000U +#define TAPBONLY 0xD0000U +#define TAPBONLYBRD 0xDF000U +#define TDBYTE 0x10000U +#define TDBYTEBRD 0x1F000U +#define TDRTUB 0xC0000U +#define TDRTUBBRD 0xCF000U +#define TINITENG 0x90000U +#define TINITENGBRD 0x9F000U +#define TMASTER 0x20000U +#define TMASTERBRD 0x2F000U +#define TPPGC 0x70000U +#define TPPGCBRD 0x7F000U +#define TUCTL_MEM 0x50000U +#define TUCTL_MEMBRD 0x5F000U + +#define DBYTE_NUM 9U +#define ANIB_NUM 12U + +#endif /* DDRPHY_PHYINIT_CSR_ALL_DEFINES_H */ diff --git a/drivers/st/ddr/phy/phyinit/include/ddrphy_phyinit.h b/drivers/st/ddr/phy/phyinit/include/ddrphy_phyinit.h new file mode 100644 index 00000000..acd7072a --- /dev/null +++ b/drivers/st/ddr/phy/phyinit/include/ddrphy_phyinit.h @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2021-2024, STMicroelectronics - All Rights Reserved + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef DDRPHY_PHYINIT_H +#define DDRPHY_PHYINIT_H + +#include <stdbool.h> + +#include <ddrphy_phyinit_usercustom.h> + +enum message_block_field { + MB_FIELD_PSTATE, + MB_FIELD_PLLBYPASSEN, + MB_FIELD_DRAMFREQ, + MB_FIELD_DFIFREQRATIO, + MB_FIELD_BPZNRESVAL, + MB_FIELD_PHYODTIMPEDANCE, + MB_FIELD_PHYDRVIMPEDANCE, + MB_FIELD_DRAMTYPE, + MB_FIELD_DISABLEDDBYTE, + MB_FIELD_ENABLEDDQS, + MB_FIELD_PHYCFG, + MB_FIELD_X16PRESENT, + MB_FIELD_ENABLEDDQSCHA, + MB_FIELD_CSPRESENTCHA, + MB_FIELD_ENABLEDDQSCHB, + MB_FIELD_CSPRESENTCHB, +}; + +/* Function definitions */ +int ddrphy_phyinit_softsetmb(struct pmu_smb_ddr_1d *mb_ddr_1d, enum message_block_field field, + uint32_t value); +void ddrphy_phyinit_initstruct(struct stm32mp_ddr_config *config, struct pmu_smb_ddr_1d *mb_ddr_1d); +#endif /* DDRPHY_PHYINIT_H */ diff --git a/drivers/st/ddr/phy/phyinit/include/ddrphy_phyinit_struct.h b/drivers/st/ddr/phy/phyinit/include/ddrphy_phyinit_struct.h new file mode 100644 index 00000000..ae34c0c8 --- /dev/null +++ b/drivers/st/ddr/phy/phyinit/include/ddrphy_phyinit_struct.h @@ -0,0 +1,786 @@ +/* + * Copyright (C) 2021-2024, STMicroelectronics - All Rights Reserved + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef DDRPHY_PHYINIT_STRUCT_H +#define DDRPHY_PHYINIT_STRUCT_H + +/* This file defines the internal data structures used in PhyInit to store user configuration */ + +/* DIMM Type definitions */ +#define DDR_DIMMTYPE_NODIMM 4U /* No DIMM (Soldered-on) */ + +/* + * Structure for basic user inputs + * + * The following basic data structure must be set and completed correctly so + * that the PhyInit software package can accurate program PHY registers. + */ +struct user_input_basic { + uint32_t dramtype; /* + * DRAM module type. + * + * Value | Description + * ----- | ------ + * 0x0 | DDR4 + * 0x1 | DDR3 + * 0x2 | LPDDR4 + */ + + uint32_t dimmtype; /* + * DIMM type. + * + * Value | Description + * ----- | ------ + * 0x4 | No DIMM (Soldered-on) (DDR_DIMMTYPE_NODIMM) + */ + + uint32_t lp4xmode; /* + * LPDDR4X mode support. + * Only used for LPDDR4, but not valid here. + * + * Value | Description + * ----- | ------ + * 0x0 | LPDDR4 mode, when dramtype is LPDDR4 + */ + + uint32_t numdbyte; /* Number of dbytes physically instantiated */ + + uint32_t numactivedbytedfi0; /* Number of active dbytes to be controlled by dfi0 */ + + uint32_t numactivedbytedfi1; /* + * Number of active dbytes to be controlled by dfi1. + * Only used for LPDDR4. + */ + + uint32_t numanib; /* Number of ANIBs physically instantiated */ + + uint32_t numrank_dfi0; /* Number of ranks in DFI0 channel */ + + uint32_t numrank_dfi1; /* Number of ranks in DFI1 channel (if DFI1 exists) */ + + uint32_t dramdatawidth; /* + * Width of the DRAM device. + * + * Enter 4,8,16 or 32 depending on protocol and dram type + * according below table. + * + * Protocol | Valid Options | Default + * -------- | ------------- | --- + * DDR3 | 4,8,16 | 8 + * DDR4 | 4,8,16 | 8 + * LPDDR4 | 8,16 | 16 + * + * For mixed x8 and x16 width devices, set variable to x8. + */ + + uint32_t numpstates; /* Number of p-states used. Must be set to 1 */ + + uint32_t frequency; /* + * Memclk frequency for each PState. + * Memclk frequency in MHz round up to next highest integer. + * Enter 334 for 333.333, etc. + */ + + uint32_t pllbypass; /* + * Indicates if PLL should be in Bypass mode. + * If DDR datarate < 333, PLL must be in Bypass Mode. + * + * Value | Description + * ----- | ------ + * 0x1 | Enabled + * 0x0 | Disabled + */ + + uint32_t dfifreqratio; /* + * Selected Dfi Frequency ratio. + * Used to program the dfifreqratio register. This register + * controls how dfi_freq_ratio input pin should be driven + * inaccordance with DFI Spec. + * + * Binary Value | Description + * ----- | ------ + * 2'b01 | 1:2 DFI Frequency Ratio (default) + */ + + uint32_t dfi1exists; /* Indicates if the PHY configuration has Dfi1 channel */ + + uint32_t train2d; /* Obsolete. Not used. */ + + uint32_t hardmacrover; /* + * Hard Macro Family version in use. + * + * Value | Description + * ----- | ------ + * 3 | hardmacro family D + */ + + uint32_t readdbienable; /* Obsolete. Not Used. */ + + uint32_t dfimode; /* Obsolete. Not Used. */ +}; + +/* + * Structure for advanced user inputs + */ +struct user_input_advanced { + uint32_t lp4rxpreamblemode; /* + * Selects between DRAM read static vs toggle preamble. + * Determine desired DRAM Read Preamble Mode based on SI + * Analysis and DRAM Part in use. + * The PHY training firmware will program DRAM mr1-OP[3] + * after training based on setting. + * + * Value | Description + * ----- | ------ + * 0x1 | toggling preamble + * 0x0 | static preamble + */ + + uint32_t lp4postambleext; /* + * Extend write postamble in LPDDR4. + * Only used for LPDDR4. + * This variable is used to calculate LPDDR4 mr3-OP[1] set + * in the messageBlock. + * The training firmware will set DRAM MR according to MR + * value in the messageBlock at the end of training. + * Set value according to your SI analysis and DRAM + * requirement. + * + * Value | Description + * ----- | ------ + * 0x0 | half Memclk postamble + * 0x1 | 1.5 Memclk postabmle (default) + */ + + uint32_t d4rxpreamblelength; /* + * Length of read preamble in DDR4 mode. + * Only used for DDR4. + * This variable is used to calculate DDR4 mr4-OP[11] set + * in the messageBlock. + * The training firmware will set DRAM MR according to MR + * value in the messageBlock at the end of training. + * Set value according to your SI analysis and DRAM + * requirement. + * + * Value | Description + * ----- | ------ + * 0x0 | 1 Tck + * 0x1 | 2 Tck (default) + */ + + uint32_t d4txpreamblelength; /* + * Length of write preamble in DDR4 mode. + * Only used for DDR4. + * This variable is used to calculate DDR4 mr4-OP[12] set + * in the messageBlock. + * The training firmware will set DRAM MR according to MR + * value in the messageBlock at the end of training. + * Set value according to your SI analysis and DRAM + * requirement. + * + * Value | Description + * ----- | ------ + * 0x0 | 1 Tck (default) + * 0x1 | 2 Tck + */ + + uint32_t extcalresval; /* + * External Impedance calibration pull-down resistor value + * select. + * Indicates value of impedance calibration pull-down + * resistor connected to BP_ZN pin of the PHY. + * Value | Description + * ----- | ------ + * 0x0 | 240 ohm (default) + */ + + uint32_t is2ttiming; /* + * Set to 1 to use 2T timing for address/command, otherwise + * 1T timing will be used. + * Determine 1T or 2T Timing operation mode based on SI + * Analysis and DRAM Timing. + * - In 1T mode, CK, CS, CA all have the same nominal + * timing, ie. ATxDly[6:0] will have same value for all + * ANIBs. + * - In 2T mode, CK, CS,have the same nominal timing + * (e.g. AtxDly[6:0]=0x00), while CA is delayed by 1UI + * (e.g. ATxDly[6:0]=0x40) + * Used to program phycfg setting in messageBlock. + * + * Value | Description + * ----- | ------ + * 0x0 | 1T Timing (default) + * 0x1 | 2T Timing + */ + + uint32_t odtimpedance; /* + * ODT impedance in ohm. + * Used for programming TxOdtDrvStren registers. + * Enter 0 for open/high-impedance. + * Default value: 60 + */ + + uint32_t tximpedance; /* + * Tx Drive Impedance for DQ/DQS in ohm. + * Used for programming TxImpedanceCtrl1 registers. + * Enter 0 for open/high-impedance. + * Default value: 60 + */ + + uint32_t atximpedance; /* + * Tx Drive Impedance for AC in ohm. + * Used for programming ATxImpedance register. + * Enter 0 for open/high-impedance + * Default value: 20 (HMA,HMB,HMC,HMD), 40 (HME) + */ + + uint32_t memalerten; /* + * Enables BP_ALERT programming of PHY registers. + * Only used for DDR3 and DDR4. + * Used for programming MemAlertControl and MemAlertControl2 + * registers. + * Program if you require using BP_ALERT pin (to receive or + * terminate signal) of the PHY otherwise leave at default + * value to save power. + * + * Value | Description + * ----- | ------ + * 0x0 | Disable BP_ALERT (default) + */ + + uint32_t memalertpuimp; /* + * Specify MemAlert Pull-up Termination Impedance. + * Programs the pull-up termination on BP_ALERT. + * Not valid here (fixed 0 value). + */ + + uint32_t memalertvreflevel; /* + * Specify the Vref level for BP_ALERT(MemAlert) Receiver. + * Not valid here (fixed 0 value). + */ + + uint32_t memalertsyncbypass; /* + * When set, this bit bypasses the DfiClk synchronizer on + * dfi_alert_n. + * Not valid here (fixed 0 value). + */ + + uint32_t disdynadrtri; /* + * Disable Dynamic Per-MEMCLK Address Tristate feature. + * Program this variable if you require to disable this + * feature. + * - In DDR3/2T and DDR4/2T/2N modes, the dynamic tristate + * feature should be disabled if the controller cannot + * follow the 2T PHY tristate protocol. + * - In LPDDR4 mode, the dynamic tristate feature should + * be disabled. + * + * Value | Description + * ----- | ------ + * 0x1 | Disable Dynamic Tristate + */ + + uint32_t phymstrtraininterval; /* + * Specifies the how frequent dfi_phymstr_req is issued by + * PHY. + * Only required in LPDDR4. + * Based on SI analysis determine how frequent DRAM drift + * compensation and re-training is required. + * Determine if Memory controller supports DFI PHY Master + * Interface. + * Program based on desired setting for + * PPTTrainSetup.PhyMstrTrainInterval register. + * Default value: 0xa + * + * Example: + * Value | Description + * ----- | ------ + * 0xa | PPT Train Interval = 268435456 MEMCLKs (default) + */ + + uint32_t phymstrmaxreqtoack; /* + * Max time from dfi_phymstr_req asserted to dfi_phymstr_ack + * asserted. + * Only required in LPDDR4. + * Based on your Memory controller's(MC) specification + * determine how long the PHY should wait for the assertion + * of dfi_phymstr_ack once dfi_phymstr_req has been issued + * by the PHY. If the MC does not ack the PHY's request, PHY + * may issue dfi_error. + * This value will be used to program + * PPTTrainSetup.PhyMstrMaxReqToAck register. + * Default value: 0x5 + * + * Example: + * Value | Description + * ----- | ------ + * 0x5 | PPT Max. Req to Ack. = 8192 MEMCLKs (default) + */ + + uint32_t wdqsext; /* + * Enable Write DQS Extension feature of PHY. + * + * Value | Description + * ----- | ------ + * 0x0 | Disable Write DQS Extension feature. (default) + * 0x1 | Enable Write DQS Extension feature. + */ + + uint32_t calinterval; /* + * Specifies the interval between successive calibrations, + * in mS. + * Program variable based on desired setting for + * CalRate.CalInterval register. + * - Fixed 0x9 value (20mS interval) + */ + + uint32_t calonce; /* + * This setting changes the behaviour of CalRun register. + * If you desire to manually trigger impedance calibration + * in mission mode set this variable to 1, and toggle CalRun + * in mission mode. + * + * Value | Description + * ----- | ------ + * 0x0 | Calibration will proceed at the rate determined + * | by CalInterval. This field should only be changed + * | while the calibrator is idle. ie before csr + * | CalRun is set. + */ + + uint32_t lp4rl; /* + * LPDDR4 Dram Read Latency. + * Applicable only if dramtype == LPDDR4. + * This variable is used to calculate LPDDR4 mr2-OP[2:0] + * set in the messageBlock. + * The training firmware will set DRAM MR according to MR + * value in the messageBlock at the end of training. + * Please refer to JEDEC JESD209-4A (LPDDR4) Spec for + * definition of MR. + * Determine values based on your DRAM part's supported + * speed and latency bin. + * Default: calculated based on user_input_basic.frequency + * and "JEDEC JESD209-4A (LPDDR4)" Table 28 "Read and Write + * Latencies". + * Lowest latency selected when more than one latency can be + * used. For example given configuration for LPDDR4, x16, + * NoDbi and DDR533, RL=10 is selected rather than 14. + */ + + uint32_t lp4wl; /* + * LPDDR4 Dram Write Latency. + * Applicable only if dramtype == LPDDR4. + * This variable is used to calculate LPDDR4 mr2-OP[5:3] + * set in the messageBlock. + * The training firmware will set DRAM MR according to MR + * value in the messageBlock at the end of training. + * Please refer to JEDEC JESD209-4A (LPDDR4) Spec for + * definition of MR. + * Determine values based on your DRAM part's supported + * speed and latency bin. + * Default: calculated based on user_input_basic.frequency + * and "JEDEC JESD209-4A (LPDDR4)" Table 28 "Read and Write + * Latencies". + * Lowest latency selected when more than one latency can be + * used. + */ + + uint32_t lp4wls; /* + * LPDDR4 Dram WL Set. + * Applicable only if dramtype == LPDDR4. + * This variable is used to calculate LPDDR4 mr2-OP[6] set + * in the messageBlock. + * The training firmware will set DRAM MR according to MR + * value in the messageBlock at the end of training. + * Please refer to JEDEC JESD209-4A (LPDDR4) Spec for + * definition of MR. + * Determine value based on Memory controllers requirement + * of DRAM State after PHY training. + * + * Value | Description + * --- | --- + * 0x0 | WL Set "A" (default) + */ + + uint32_t lp4dbird; /* + * LPDDR4 Dram DBI-Read Enable. + * Applicable only if dramtype == LPDDR4. + * Determine if you require to using DBI for the given + * PState. + * If Read DBI is not used PHY receivers are turned off to + * save power. + * This variable is used to calculate LPDDR4 mr3-OP[6] set + * in the messageBlock. + * The training firmware will set DRAM MR according to MR + * value in the messageBlock at the end of training. + * PHY register DMIPinPresent is programmed based on this + * parameter. + * Please refer to JEDEC JESD209-4A (LPDDR4) Spec for + * definition of MR. + * + * Value | Description + * --- | --- + * 0x0 | Disabled (default) + * 0x1 | Enabled + */ + + uint32_t lp4dbiwr; /* + * LPDDR4 Dram DBI-Write Enable. + * Applicable only if dramtype == LPDDR4. + * This variable is used to calculate LPDDR4 mr3-OP[7] set + * in the messageBlock. + * The training firmware will set DRAM MR according to MR + * value in the messageBlock at the end of training. + * Please refer to JEDEC JESD209-4A (LPDDR4) Spec for + * definition of MR. + * + * Value | Description + * --- | --- + * 0x0 | Disabled (default) + * 0x1 | Enabled + */ + + uint32_t lp4nwr; /* + * LPDDR4 Write-Recovery for Auto- Pre-charge commands. + * Applicable only if dramtype == LPDDR4. + * This variable is used to calculate LPDDR4 mr1-OP[6:4] set + * in the messageBlock. + * The training firmware will set DRAM MR according to MR + * value in the messageBlock at the end of training. + * Please refer to JEDEC JESD209-4A (LPDDR4) Spec for + * definition of MR. + * Determine values based on your DRAM part's supported + * speed and latency bin. + * Default: calculated based on user_input_basic.frequency + * and "JEDEC JESD209-4A (LPDDR4)" Table 28 "Read and Write + * Latencies". + * Lowest latency selected when more than one latency can be + * used. + * + * Binary Value | Description + * --- | --- + * 000 | nWR = 6 (default) + * 001 | nWR = 10 + * 010 | nWR = 16 + * 011 | nWR = 20 + * 100 | nWR = 24 + * 101 | nWR = 30 + * 110 | nWR = 34 + * 111 | nWR = 40 + */ + + uint32_t lp4lowpowerdrv; /* + * Configure output Driver in Low power mode. + * Feature only supported for Hard Macro Family E (HME). + * Use NMOS Pull-up for Low-Power IO. + * Not valid here + */ + + uint32_t drambyteswap; /* + * DRAM Oscillator count source mapping for skip_training. + * The PHY supports swapping of DRAM oscillator count values + * between paired DBytes for the purpose of tDQSDQ DRAM + * Drift Compensation(DDC). + * Each DByte has a register bit to control the source of + * the oscillator count value used to perform tDQSDQ Drift + * compensation. + * On silicon the training firmware will determine the DByte + * swap and program PptCtlStatic register to select + * oscillator count source. When skip_train is used, + * training firmware is skipped thus manual programming may + * be required depending on configuration. + * The default hardware configuration is for odd Dbyte + * instance n to use oscillator count values from its paired + * Dbyte instance n-1. So Dbyte1 will use the oscillator + * count values from Dbyte0, Dbyte3 will use Dbyte2 and so + * on. This is required for DRAM Data width =16. + * Each bit of this field corresponds to a DBYTE: + * - bit-0 = setting for DBYTE0 + * - bit-1 = setting for DBYTE1 + * - bit-2 = setting for DBYTE2 + * - . . . + * - bit-n = setting for DBYTEn + * By setting the associated bit for each DByte to 1, PHY + * will use non-default source for count value. + * - for even Dbytes, non-default source is to use the odd + * pair count value. + * - for odd Dbytes, no-default source to use data + * received directly from the DRAM. + * Byte swapping must be the same across different ranks. + * Default value: 0x0 + * If Byte mode devices are indicated via the x8mode + * messageBlock parameter, this variable is ignored as PHY + * only supports a limited configuration set based on Byte + * mode configuration. + * + * Example: + * DramByteSwap = 0x03 - Dbyte0: use count values from + * Dbyte1, Dbyte1 uses count values received directly + * received from DRAM. + * Rest of Dbytes have default source for DRAM oscilator + * count. + */ + + uint32_t rxenbackoff; /* + * Determines the Placement of PHY Read Gate signal. + * Only used in LPDDR4 when lp4rxpreamblemode==0 (static + * preamble) for skip_train==true. + * For other dramtypes or LPDDR4-toggling-preamble no + * options are available and PhyInit will set position as + * required. See source code in + * ddrphy_phyinit_c_initphyconfig() to see how the + * RxEnBackOff register is set. + * For skip_train==false, FW will set the position based on + * Preamble. + * We recommend keeping this setting at default value. + * SI analysis is required to determine if default value + * needs to be changed. + * + * Value | Description + * ----- | --- + * 0x1 | Position read gate 1UI from the first valid edge + * | of DQS_t (LPDDR4 Static preamble only) (default) + */ + + uint32_t trainsequencectrl; /* + * Firmware Training Sequence Control. + * This input is used to program sequencectrl in + * messageBlock. + * It controls the training stages executed by firmware. + * For production silicon we recommend to use default value + * programmed by PhyInit. + */ + + uint32_t snpsumctlopt; /* + * Enable Fast Frequency Change (FFC) Optimizations + * specific to UMCTL2 (DDRCTRL). + * Not valid for dimmtype=NODIMM. + * Consult DDRCTRL documentation in Reference Manual to + * ensure when optimizations can be enabled. + * + * Value | Description + * ----- | --- + * 0 | Disable FFC MRW optimization (default) + */ + + uint32_t snpsumctlf0rc5x; /* + * F0RX5x RCD Control Word when using Fast Frequency + * Change(FFC) optimizations specific to UMCTL2 + * Not valid for dimmtype=NODIMM. + * Only valid for when SnpsUmctlOpt=1. + * When UMCTL2 optimizations are enabled PHY will perform + * RCD MRW during fast frequency change request. + * The correct RCD control word value for each PState must + * be programmed in this field. + * Consult the RCD spec and UMCTL documentation to + * determine the correct value based on DRAM configuration + * and operating speed. + */ + + uint32_t txslewrisedq; /* + * Pull-up slew rate control for DBYTE Tx. + * Value specified here will be written to register + * TxSlewRate.TxPreP by PhyInit. + * See register description for more information. + */ + + uint32_t txslewfalldq; /* + * Pull-down slew rate control for DBYTE Tx. + * Value specified here will be written to + * TxSlewRate.TxPreN by PhyInit. + * See register description for more information. + */ + + uint32_t txslewriseac; /* + * Pull-up slew rate control for ANIB Tx. + * Value specified here will be written to + * ATxSlewRate.ATxPreP. + * See register description for more information. + */ + + uint32_t txslewfallac; /* + * Pull-down slew rate control for ANIB Tx. + * Value specified here will be written to + * ATxSlewRate.ATxPreN. + * See register description for more information. + */ + + uint32_t disableretraining; /* + * Disable PHY DRAM Drift compensation re-training. + * Only applied to LPDDR4. No retraining is required in + * DDR4/3. + * Disable PHY re-training during DFI frequency change + * requests in LPDDR4. + * The purpose of retraining is to compensate for drift in + * the DRAM. + * Determine based on SI analysis and DRAM datasheet if + * retraining can be disabled. + * + * Value | Description + * ----- | --- + * 0x1 | Disable retraining + * 0x0 | Enable retraining + */ + + uint32_t disablephyupdate; /* + * Disable DFI PHY Update feature. + * Only effects LPDDR4. + * Disable DFI PHY Update feature. When set PHY will not + * assert dfi0/1_phyupd_req. + * + * Value | Description + * ----- | --- + * 0x1 | Disable DFI PHY Update + * 0x0 | Enable DFI PHY Update + */ + + uint32_t enablehighclkskewfix; /* + * Enable alternative PIE program. + * If enabled the PIE reinitializes the FIFO pointers a + * second time due for designs with large skew between + * chiplet DfiClk branches. If enabled PIE latencies in all + * protocols are increased by 60 DfiClks. + * + * Value | Description + * ----- | --- + * 0x0 | Disable (default) + */ + + uint32_t disableunusedaddrlns; /* + * Turn off or tristate Address Lanes when possible. + * + * When enabled, PHY will tristate unused address lanes to + * save power when possible by using Acx4AnibDis and + * AForceTriCont registers. + * This feature is only implemented for the default PHY + * Address bump mapping and Ranks must be populated in + * order. ie Rank1 cannot be used if Rank0 is unpopulated. + * For alternative bump mapping follow the following + * guideline to achieve maximum power savings: + * - For each unused BP_A bump program AForceTriCont[4:0] + * bits based on register description. + * - if all lanes of an Anib are unused _AND_ ANIB is not + * the first or last instance set bit associated with + * the instance in Acs4AnibDis registers. see register + * description for details. + * + * Value | Description + * ----- | --- + * 0x1 | Enable + */ + + uint32_t phyinitsequencenum; /* + * Switches between supported phyinit training sequences. + * + * Value | Description + * ----- | --- + * 0x0 | Minimizes number of Imem/Dmem loads (default) + */ + + uint32_t enabledficspolarityfix;/* + * Enable alternative PIE program. + * Set to 1 if PUB_VERSION <2.43a, otherwise set to 0. If + * enabled the PIE programs Dfi{Rd,Wr}DataCsDestMap CSR's + * to default values 0x00E4 before running PPT. + * Before exiting PPT, PIE will restore + * Dfi{Rd,Wr}DataCsDestMap CSR's to 0x00E1. + * + * Value | Description + * ----- | --- + * 0x0 | Disable (default) + */ + + uint32_t phyvref; /* + * Must be programmed with the Vref level to be used by the + * PHY during reads. + * The units of this field are a percentage of VDDQ + * according to the following equation: + * Receiver Vref = VDDQ*phyvref[6:0]/128 + * For example to set Vref at 0.75*VDDQ, set this field to + * 0x60. + * For digital simulation, any legal value can be used. For + * silicon, the users must calculate the analytical Vref by + * using the impedances, terminations, and series resistance + * present in the system. + */ + + uint32_t sequencectrl; /* + * Controls the training steps to be run. Each bit + * corresponds to a training step. + * If the bit is set to 1, the training step will run. + * If the bit is set to 0, the training step will be + * skipped. + * Training step to bit mapping: + * sequencectrl[0] = Run DevInit - Device/phy + * initialization. Should always be set. + * sequencectrl[1] = Run WrLvl - Write leveling + * sequencectrl[2] = Run RxEn - Read gate training + * sequencectrl[3] = Run RdDQS1D - 1d read dqs training + * sequencectrl[4] = Run WrDQ1D - 1d write dq training + * sequencectrl[5] = RFU, must be zero + * sequencectrl[6] = RFU, must be zero + * sequencectrl[7] = RFU, must be zero + * sequencectrl[8] = Run RdDeskew - Per lane read dq deskew + * training + * sequencectrl[9] = Run MxRdLat - Max read latency training + * sequencectrl[10] = RFU, must be zero + * sequencectrl[11] = RFU, must be zero + * sequencectrl[12] = RFU, must be zero + * sequencectrl[13] = RFU, must be zero + * sequencectrl[15-14] = RFU, must be zero + */ +}; + +/* + * Structure for mode register user inputs + * + * The following data structure must be set and completed correctly so that the PhyInit software + * package can accurate fill message block structure. + * Only some mrx are used per DDR type, on related width: + * - DDR3: mr0..2 are used (16-bits values) + * - DDR4: mr0..6 are used (16-bits values) + * - LPDDR4: mr1..4 and mr11..22 are used (8-bits values) + */ +struct user_input_mode_register { + uint32_t mr0; + uint32_t mr1; + uint32_t mr2; + uint32_t mr3; + uint32_t mr4; + uint32_t mr5; + uint32_t mr6; + uint32_t mr11; + uint32_t mr12; + uint32_t mr13; + uint32_t mr14; + uint32_t mr22; +}; + +/* + * Structure for swizzle user inputs + * + * The following data structure must be set and completed correctly sothat the PhyInit software + * package can accurate set swizzle (IO muxing) config. + * Only some swizzles are used per DDR type: + * - DDR3/DDR4: swizzle 0..32 are used + * - 26 for hwtswizzle + * - 7 for acswizzle + * - LPDDR4: swizzle 0..43 are used + * - 8 per byte for dqlnsel (total 32) + * - 6 for mapcaatodfi + * - 6 for mapcabtodfi + */ +#define NB_HWT_SWIZZLE 26U +#define NB_AC_SWIZZLE 7U +#define NB_DQLNSEL_SWIZZLE_PER_BYTE 8U +#define NB_MAPCAATODFI_SWIZZLE 6U +#define NB_MAPCABTODFI_SWIZZLE 6U +#define NB_SWIZZLE 44 +struct user_input_swizzle { + uint32_t swizzle[NB_SWIZZLE]; +}; + +#endif /* DDRPHY_PHYINIT_STRUCT_H */ diff --git a/drivers/st/ddr/phy/phyinit/include/ddrphy_phyinit_usercustom.h b/drivers/st/ddr/phy/phyinit/include/ddrphy_phyinit_usercustom.h new file mode 100644 index 00000000..b248f592 --- /dev/null +++ b/drivers/st/ddr/phy/phyinit/include/ddrphy_phyinit_usercustom.h @@ -0,0 +1,118 @@ +/* + * Copyright (C) 2021-2024, STMicroelectronics - All Rights Reserved + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef DDRPHY_PHYINIT_USERCUSTOM_H +#define DDRPHY_PHYINIT_USERCUSTOM_H + +#include <stdbool.h> +#include <stdint.h> + +#include <ddrphy_csr_all_cdefines.h> + +#include <drivers/st/stm32mp2_ddr.h> + +/* Message Block Structure Definitions */ +#if STM32MP_DDR3_TYPE +#include <mnpmusrammsgblock_ddr3.h> +#elif STM32MP_DDR4_TYPE +#include <mnpmusrammsgblock_ddr4.h> +#else /* STM32MP_LPDDR4_TYPE */ +#include <mnpmusrammsgblock_lpddr4.h> +#endif /* STM32MP_DDR3_TYPE */ + +/* + * ------------------------------------------------------------- + * Defines for Firmware Images + * - indicate IMEM/DMEM address, size (bytes) and offsets. + * ------------------------------------------------------------- + * + * IMEM_SIZE max size of instruction memory. + * DMEM_SIZE max size of data memory. + * + * IMEM_ST_ADDR start of IMEM address in memory. + * DMEM_ST_ADDR start of DMEM address in memory. + * DMEM_BIN_OFFSET start offset in DMEM memory (message block). + */ +#if STM32MP_DDR3_TYPE +#define IMEM_SIZE 0x4C28U +#define DMEM_SIZE 0x6C8U +#elif STM32MP_DDR4_TYPE +#define IMEM_SIZE 0x6D24U +#define DMEM_SIZE 0x6CCU +#else /* STM32MP_LPDDR4_TYPE */ +#define IMEM_SIZE 0x7E50U +#define DMEM_SIZE 0x67CU +#endif /* STM32MP_DDR3_TYPE */ +#define IMEM_ST_ADDR 0x50000U +#define DMEM_ST_ADDR 0x54000U +#define DMEM_BIN_OFFSET 0x200U + +/* + * ------------------ + * Type definitions + * ------------------ + */ + +/* A structure used to SRAM memory address space */ +enum return_offset_lastaddr { + RETURN_OFFSET, + RETURN_LASTADDR +}; + +/* Enumeration of instructions for PhyInit Register Interface */ +enum reginstr { + STARTTRACK, /* Start register tracking */ + STOPTRACK, /* Stop register tracking */ + SAVEREGS, /* Save(read) tracked register values */ + RESTOREREGS, /* Restore (write) saved register values */ +}; + +/* Data structure to store register address/value pairs */ +struct reg_addr_val { + uint32_t address; /* Register address */ + uint16_t value; /* Register value */ +}; + +/* Target CSR for the impedance value for ddrphy_phyinit_mapdrvstren() */ +enum drvtype { + DRVSTRENFSDQP, + DRVSTRENFSDQN, + ODTSTRENP, + ODTSTRENN, + ADRVSTRENP, + ADRVSTRENN +}; + +/* + * ------------------------------------------------------------- + * Fixed Function prototypes + * ------------------------------------------------------------- + */ +int ddrphy_phyinit_sequence(struct stm32mp_ddr_config *config, bool skip_training, bool reten); +int ddrphy_phyinit_restore_sequence(void); +int ddrphy_phyinit_c_initphyconfig(struct stm32mp_ddr_config *config, + struct pmu_smb_ddr_1d *mb_ddr_1d, uint32_t *ardptrinitval); +void ddrphy_phyinit_d_loadimem(void); +void ddrphy_phyinit_progcsrskiptrain(struct stm32mp_ddr_config *config, + struct pmu_smb_ddr_1d *mb_ddr_1d, uint32_t ardptrinitval); +int ddrphy_phyinit_f_loaddmem(struct stm32mp_ddr_config *config, struct pmu_smb_ddr_1d *mb_ddr_1d); +int ddrphy_phyinit_g_execfw(void); +void ddrphy_phyinit_i_loadpieimage(struct stm32mp_ddr_config *config, bool skip_training); +void ddrphy_phyinit_loadpieprodcode(void); +int ddrphy_phyinit_mapdrvstren(uint32_t drvstren_ohm, enum drvtype targetcsr); +int ddrphy_phyinit_calcmb(struct stm32mp_ddr_config *config, struct pmu_smb_ddr_1d *mb_ddr_1d); +void ddrphy_phyinit_writeoutmem(uint32_t *mem, uint32_t mem_offset, uint32_t mem_size); +void ddrphy_phyinit_writeoutmsgblk(uint16_t *mem, uint32_t mem_offset, uint32_t mem_size); +int ddrphy_phyinit_isdbytedisabled(struct stm32mp_ddr_config *config, + struct pmu_smb_ddr_1d *mb_ddr_1d, uint32_t dbytenumber); +int ddrphy_phyinit_trackreg(uint32_t adr); +int ddrphy_phyinit_reginterface(enum reginstr myreginstr, uint32_t adr, uint16_t dat); + +void ddrphy_phyinit_usercustom_custompretrain(struct stm32mp_ddr_config *config); +int ddrphy_phyinit_usercustom_g_waitfwdone(void); +int ddrphy_phyinit_usercustom_saveretregs(struct stm32mp_ddr_config *config); + +#endif /* DDRPHY_PHYINIT_USERCUSTOM_H */ diff --git a/drivers/st/ddr/phy/phyinit/include/ddrphy_wrapper.h b/drivers/st/ddr/phy/phyinit/include/ddrphy_wrapper.h new file mode 100644 index 00000000..ed4be1c0 --- /dev/null +++ b/drivers/st/ddr/phy/phyinit/include/ddrphy_wrapper.h @@ -0,0 +1,20 @@ +/* + * Copyright (C) 2021-2024, STMicroelectronics - All Rights Reserved + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef DDRPHY_WRAPPER_H +#define DDRPHY_WRAPPER_H + +static inline long long fmodll(long long x, long long y) +{ + return x - ((x / y) * y); +} + +static inline int fmodi(int x, int y) +{ + return (int)fmodll((long long)x, (long long)y); +} + +#endif /* DDRPHY_WRAPPER_H */ diff --git a/drivers/st/ddr/phy/phyinit/src/ddrphy_phyinit_c_initphyconfig.c b/drivers/st/ddr/phy/phyinit/src/ddrphy_phyinit_c_initphyconfig.c new file mode 100644 index 00000000..e5c82580 --- /dev/null +++ b/drivers/st/ddr/phy/phyinit/src/ddrphy_phyinit_c_initphyconfig.c @@ -0,0 +1,1141 @@ +/* + * Copyright (C) 2021-2024, STMicroelectronics - All Rights Reserved + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <stdlib.h> + +#include <common/debug.h> + +#include <ddrphy_phyinit.h> +#include <ddrphy_wrapper.h> + +#include <lib/mmio.h> +#include <lib/utils_def.h> + +#include <platform_def.h> + +/* + * Program txslewrate: + * - txslewrate::txpredrvmode is dependent on dramtype. + * - txslewrate::txprep and txslewrate::txpren are technology-specific. + */ +static void txslewrate_program(struct stm32mp_ddr_config *config) +{ + uint32_t txpredrvmode; + uint32_t byte; + uint32_t txpren; /* Default to 0xf (max). Optimal setting is technology specific */ + uint32_t txprep; /* Default to 0xf (max). Optimal setting is technology specific */ + uint16_t txslewrate; + +#if STM32MP_DDR3_TYPE + txpredrvmode = 0x3U; +#elif STM32MP_DDR4_TYPE + txpredrvmode = 0x2U; +#else /* STM32MP_LPDDR4_TYPE */ + txpredrvmode = 0x1U; +#endif /* STM32MP_DDR3_TYPE */ + + txprep = config->uia.txslewrisedq; + txpren = config->uia.txslewfalldq; + + txslewrate = (uint16_t)((txpredrvmode << CSR_TXPREDRVMODE_LSB) | + (txpren << CSR_TXPREN_LSB) | + (txprep << CSR_TXPREP_LSB)); + + for (byte = 0U; byte < config->uib.numdbyte; byte++) { + uint32_t c_addr; + uint32_t lane; + + c_addr = byte << 12; + for (lane = 0U; lane <= B_MAX; lane++) { + uint32_t b_addr; + + b_addr = lane << 8; + mmio_write_16((uintptr_t)(DDRPHYC_BASE + (4U * (TDBYTE | c_addr | b_addr | + CSR_TXSLEWRATE_ADDR))), + txslewrate); + } + } +} + +/* + * Program atxslewrate: + * - atxslewrate::atxpredrvmode is dependent on dramtype and whether + * the ACX4 instance is used for AC or CK. + * - atxslewrate::atxprep and atxslewrate::atxpren are technology-specific. + */ +static void atxslewrate_program(struct stm32mp_ddr_config *config) +{ + uint32_t anib; + uint32_t atxpren; /* Default to 0xf (max). Optimal setting is technology specific */ + uint32_t atxprep; /* Default to 0xf (max). Optimal setting is technology specific */ + uint32_t ck_anib_inst[2] = {0}; + + atxprep = config->uia.txslewriseac; + atxpren = config->uia.txslewfallac; + + /* + * # of ANIBs CK ANIB Instance + * ACX8 ANIB 1 + */ + if (config->uib.numanib == 8U) { + ck_anib_inst[0] = 1U; + ck_anib_inst[1] = 1U; + } + + for (anib = 0U; anib < config->uib.numanib; anib++) { + uint32_t atxpredrvmode; + uint32_t c_addr; + uint16_t atxslewrate; + + c_addr = anib << 12; + + if ((anib == ck_anib_inst[0]) || (anib == ck_anib_inst[1])) { + /* CK ANIB instance */ +#if STM32MP_DDR3_TYPE || STM32MP_DDR4_TYPE + atxpredrvmode = 0x0U; +#else /* STM32MP_LPDDR4_TYPE */ + atxpredrvmode = 0x1U; +#endif /* STM32MP_DDR3_TYPE || STM32MP_DDR4_TYPE */ + } else { + /* non-CK ANIB instance */ +#if STM32MP_DDR3_TYPE || STM32MP_DDR4_TYPE + atxpredrvmode = 0x3U; +#else /* STM32MP_LPDDR4_TYPE */ + atxpredrvmode = 0x1U; +#endif /* STM32MP_DDR3_TYPE || STM32MP_DDR4_TYPE */ + } + + atxslewrate = (uint16_t)((atxpredrvmode << CSR_ATXPREDRVMODE_LSB) | + (atxpren << CSR_ATXPREN_LSB) | + (atxprep << CSR_ATXPREP_LSB)); + + mmio_write_16((uintptr_t)(DDRPHYC_BASE + (4U * (TANIB | c_addr | + CSR_ATXSLEWRATE_ADDR))), + atxslewrate); + } +} + +#if STM32MP_DDR3_TYPE || STM32MP_DDR4_TYPE +/* + * Program dfirddatacsdestmap and dfiwrdatacsdestmap: + * - Dependencies: mb_ddr_1d->msgmisc[6] Determine Partial Rank Support. + */ +static void dfidatacsdestmap_program(struct pmu_smb_ddr_1d *mb_ddr_1d) +{ + if ((mb_ddr_1d->msgmisc & 0x40U) != 0U) { + uint16_t dfirddatacsdestmap = 0xA0U; + uint16_t dfiwrdatacsdestmap = 0xA0U; + + mmio_write_16((uintptr_t)(DDRPHYC_BASE + (4U * (TMASTER | + CSR_DFIRDDATACSDESTMAP_ADDR))), + dfirddatacsdestmap); + mmio_write_16((uintptr_t)(DDRPHYC_BASE + (4U * (TMASTER | + CSR_DFIWRDATACSDESTMAP_ADDR))), + dfiwrdatacsdestmap); + } +} +#endif /* STM32MP_DDR3_TYPE || STM32MP_DDR4_TYPE */ + +/* + * Program pllctrl2: + * - Calculate PLL controls from frequency. + */ +static void pllctrl2_program(struct stm32mp_ddr_config *config) +{ + uint16_t pllctrl2; + uint32_t halffreq = config->uib.frequency / 2U; + + if (halffreq < 235U) { + pllctrl2 = 0x7U; + } else if (halffreq < 313U) { + pllctrl2 = 0x6U; + } else if (halffreq < 469U) { + pllctrl2 = 0xBU; + } else if (halffreq < 625U) { + pllctrl2 = 0xAU; + } else if (halffreq < 938U) { + pllctrl2 = 0x19U; + } else if (halffreq < 1067U) { + pllctrl2 = 0x18U; + } else { + pllctrl2 = 0x19U; + } + + mmio_write_16((uintptr_t)(DDRPHYC_BASE + (4U * (TMASTER | CSR_PLLCTRL2_ADDR))), pllctrl2); +} + +/* + * Program ardptrinitval: + * - The values programmed here assume ideal properties of DfiClk and Pclk including: + * - DfiClk skew + * - DfiClk jitter + * - DfiClk PVT variations + * - Pclk skew + * - Pclk jitter + * + * ardptrinitval Programmed differently based on PLL Bypass mode and frequency: + * - PLL Bypassed mode: + * - For MemClk frequency > 933MHz, the valid range of ardptrinitval[3:0] is: 2-5 + * - For MemClk frequency < 933MHz, the valid range of ardptrinitval[3:0] is: 1-5 + * - PLL Enabled mode: + * - For MemClk frequency > 933MHz, the valid range of ardptrinitval[3:0] is: 1-5 + * - For MemClk frequency < 933MHz, the valid range of ardptrinitval[3:0] is: 0-5 + */ +static void ardptrinitval_program(struct stm32mp_ddr_config *config, uint32_t *ardptrinitval) +{ + uint16_t regdata; + + if (config->uib.frequency >= 933U) { + regdata = 0x2U; + } else { + regdata = 0x1U; + } + + /* Add one UI for synchronizer on SyncBus when PLL is bypassed */ + if (config->uib.pllbypass == 1U) { + regdata++; + } + + mmio_write_16((uintptr_t)(DDRPHYC_BASE + (4U * (TMASTER | CSR_ARDPTRINITVAL_ADDR))), + regdata); + + *ardptrinitval = (uint32_t)regdata; +} + +#if STM32MP_LPDDR4_TYPE +/* + * Program ProcOdtCtl: + * - Sets procodtalwayson/procodtalwaysoff for LPDDR4 using the PIE register seq0bgpr4. + */ +static void procodtctl_program(void) +{ + mmio_write_16((uintptr_t)(DDRPHYC_BASE + (4U * (TINITENG | C0 | CSR_SEQ0BGPR4_ADDR))), 0U); +} +#endif /* STM32MP_LPDDR4_TYPE */ + +/* + * Program dbytedllmodecntrl: + * - dllrxpreamblemode + * Program dqspreamblecontrol: + * - Fields: + * - twotckrxdqspre + * - twotcktxdqspre + * - positiondfeinit + * - lp4tgltwotcktxdqspre + * - lp4postambleext + * - lp4sttcprebridgerxen + * - Dependencies: + * - user_input_advanced.lp4rxpreamblemode (LPDDR4) + * - user_input_advanced.lp4postambleext (LPDDR4) + * - user_input_advanced.wdqsext (LPDDR4) + * - user_input_advanced.d4rxpreamblelength (DDR4) + * - user_input_advanced.d4txpreamblelength (DDR4) + */ +static void dbytedllmodecntrl_program(struct stm32mp_ddr_config *config, uint32_t *twotckrxdqspre) +{ + uint32_t disdllgainivseed = 1U; + uint32_t disdllseedsel = 0U; + uint32_t dllgainiv = 0x1U; + uint32_t dllgaintv = 0x6U; + uint32_t dllrxpreamblemode = 0U; + uint32_t lcdlseed0 = 0x21U; + uint32_t lp4postambleext = 0U; + uint32_t lp4sttcprebridgerxen = 0U; + uint32_t lp4tgltwotcktxdqspre = 0U; + uint32_t positiondfeinit; + uint32_t twotcktxdqspre = 0U; + uint32_t wdqsextension = 0U; + uint16_t dbytedllmodecntrl; + uint16_t dllgainctl; + uint16_t dlllockparam; + uint16_t dqspreamblecontrol; + +#if STM32MP_DDR3_TYPE + /* Same as default */ + *twotckrxdqspre = 0x0U; + lp4sttcprebridgerxen = 0x0U; + dllrxpreamblemode = 0x0U; + twotcktxdqspre = 0x0U; + lp4tgltwotcktxdqspre = 0x0U; + positiondfeinit = 0x0U; + lp4postambleext = 0x0U; +#elif STM32MP_DDR4_TYPE + *twotckrxdqspre = config->uia.d4rxpreamblelength; + lp4sttcprebridgerxen = 0x0U; + dllrxpreamblemode = 0x1U; + twotcktxdqspre = config->uia.d4txpreamblelength; + lp4tgltwotcktxdqspre = 0x0U; + positiondfeinit = 0x2U; + lp4postambleext = 0x0U; +#else /* STM32MP_LPDDR4_TYPE */ + /* Set to 1 if static Rx preamble */ + *twotckrxdqspre = (config->uia.lp4rxpreamblemode == 0U) ? 1U : 0U; + /* Set to 1 if static Rx preamble */ + lp4sttcprebridgerxen = (config->uia.lp4rxpreamblemode == 0U) ? 1U : 0U; + dllrxpreamblemode = 0x1U; + /* Must be 2*Tck Tx preamble according to JEDEC (mr1.OP[2] = 1) */ + twotcktxdqspre = 0x1U; + /* Must be toggling Tx preamble */ + lp4tgltwotcktxdqspre = 0x1U; + positiondfeinit = 0x0U; + lp4postambleext = config->uia.lp4postambleext; + wdqsextension = config->uia.wdqsext; +#endif /* STM32MP_DDR3_TYPE */ + + dqspreamblecontrol = (uint16_t)((wdqsextension << CSR_WDQSEXTENSION_LSB) | + (lp4sttcprebridgerxen << CSR_LP4STTCPREBRIDGERXEN_LSB) | + (lp4postambleext << CSR_LP4POSTAMBLEEXT_LSB) | + (lp4tgltwotcktxdqspre << CSR_LP4TGLTWOTCKTXDQSPRE_LSB) | + (positiondfeinit << CSR_POSITIONDFEINIT_LSB) | + (twotcktxdqspre << CSR_TWOTCKTXDQSPRE_LSB) | + (*twotckrxdqspre << CSR_TWOTCKRXDQSPRE_LSB)); + + mmio_write_16((uintptr_t)(DDRPHYC_BASE + (4U * (TMASTER | CSR_DQSPREAMBLECONTROL_ADDR))), + dqspreamblecontrol); + + dbytedllmodecntrl = (uint16_t)(dllrxpreamblemode << CSR_DLLRXPREAMBLEMODE_LSB); + mmio_write_16((uintptr_t)(DDRPHYC_BASE + (4U * (TMASTER | CSR_DBYTEDLLMODECNTRL_ADDR))), + dbytedllmodecntrl); + + dllgainctl = (uint16_t)(dllgainiv | (dllgaintv << CSR_DLLGAINTV_LSB)); + dlllockparam = (uint16_t)(disdllseedsel | (disdllgainivseed << CSR_DISDLLGAINIVSEED_LSB) | + (lcdlseed0 << CSR_LCDLSEED0_LSB)); + + mmio_write_16((uintptr_t)(DDRPHYC_BASE + (4U * (TMASTER | CSR_DLLLOCKPARAM_ADDR))), + dlllockparam); + + mmio_write_16((uintptr_t)(DDRPHYC_BASE + (4U * (TMASTER | CSR_DLLGAINCTL_ADDR))), + dllgainctl); +} + +/* + * Program procodttimectl: + * - Fields: + * - POdtStartDelay[3:2] + * - POdtTailWidth[1:0] + * - Dependencies: + * - user_input_basic.frequency + * - user_input_advanced.wdqsext + */ +static void procodttimectl_program(struct stm32mp_ddr_config *config, uint32_t twotckrxdqspre) +{ + uint16_t procodttimectl; + + if (config->uia.wdqsext != 0U) { + /* POdtStartDelay = 0x0 and POdtTailWidth = 0x3 */ + procodttimectl = 0x3U; + } else if (config->uib.frequency <= 933U) { + /* Memclk Freq <= 933MHz: POdtStartDelay = 0x2 and POdtTailWidth = 0x2 */ + procodttimectl = 0xAU; + } else if (config->uib.frequency <= 1200U) { + /* 933MHz < Memclk Freq <= 1200MHz */ + if (twotckrxdqspre == 1U) { + /* POdtStartDelay = 0x0 and POdtTailWidth = 0x2 */ + procodttimectl = 0x2U; + } else { + /* POdtStartDelay = 0x1 and POdtTailWidth = 0x2 */ + procodttimectl = 0x6U; + } + } else { + /* Memclk Freq > 1200MHz */ + if (twotckrxdqspre == 1U) { + /* POdtStartDelay = 0x0 and POdtTailWidth = 0x3 */ + procodttimectl = 0x3U; + } else { + /* POdtStartDelay = 0x1 and POdtTailWidth = 0x3 */ + procodttimectl = 0x7U; + } + } + + mmio_write_16((uintptr_t)(DDRPHYC_BASE + (4U * (TMASTER | CSR_PROCODTTIMECTL_ADDR))), + procodttimectl); +} + +/* + * Program txodtdrvstren: + * - Fields: + * - ODTStrenP_px[5:0] + * - ODTStrenN_px[11:6] + * - Dependencies: + * - user_input_basic.numdbyte + * - user_input_advanced.odtimpedance + * \return 0 on success. + */ +static int txodtdrvstren_program(struct stm32mp_ddr_config *config) +{ + uint32_t byte; + int odtstrenn_state; + int odtstrenp_state; + uint16_t txodtdrvstren; + + odtstrenp_state = ddrphy_phyinit_mapdrvstren(config->uia.odtimpedance, ODTSTRENP); + if (odtstrenp_state < 0) { + return odtstrenp_state; + } + + odtstrenn_state = ddrphy_phyinit_mapdrvstren(config->uia.odtimpedance, ODTSTRENN); + if (odtstrenn_state < 0) { + return odtstrenn_state; + } + + txodtdrvstren = (uint16_t)((odtstrenn_state << CSR_ODTSTRENN_LSB) | odtstrenp_state); + + for (byte = 0U; byte < config->uib.numdbyte; byte++) { + uint32_t c_addr; + uint32_t lane; + + c_addr = byte << 12; + for (lane = 0U; lane <= B_MAX; lane++) { + uint32_t b_addr; + + b_addr = lane << 8; + mmio_write_16((uintptr_t)(DDRPHYC_BASE + (4U * (TDBYTE | c_addr | b_addr | + CSR_TXODTDRVSTREN_ADDR))), + txodtdrvstren); + } + } + + return 0; +} + +/* + * Program tximpedancectrl1: + * - Fields: + * - DrvStrenFSDqP[5:0] + * - DrvStrenFSDqN[11:6] + * - Dependencies: + * - user_input_basic.numdbyte + * - user_input_advanced.tximpedance + * \return 0 on success. + */ +static int tximpedancectrl1_program(struct stm32mp_ddr_config *config) +{ + uint32_t byte; + int drvstrenfsdqn_state; + int drvstrenfsdqp_state; + uint16_t tximpedancectrl1; + + drvstrenfsdqp_state = ddrphy_phyinit_mapdrvstren(config->uia.tximpedance, + DRVSTRENFSDQP); + if (drvstrenfsdqp_state < 0) { + return drvstrenfsdqp_state; + } + + drvstrenfsdqn_state = ddrphy_phyinit_mapdrvstren(config->uia.tximpedance, + DRVSTRENFSDQN); + if (drvstrenfsdqn_state < 0) { + return drvstrenfsdqn_state; + } + + tximpedancectrl1 = (uint16_t)((drvstrenfsdqn_state << CSR_DRVSTRENFSDQN_LSB) | + (drvstrenfsdqp_state << CSR_DRVSTRENFSDQP_LSB)); + + for (byte = 0U; byte < config->uib.numdbyte; byte++) { + uint32_t c_addr; + uint32_t lane; + + c_addr = byte << 12; + for (lane = 0U; lane <= B_MAX; lane++) { + uint32_t b_addr; + + b_addr = lane << 8; + mmio_write_16((uintptr_t)(DDRPHYC_BASE + (4U * + (TDBYTE | c_addr | b_addr | + CSR_TXIMPEDANCECTRL1_ADDR))), + tximpedancectrl1); + } + } + + return 0; +} + +/* + * Program atximpedance: + * - Fields: + * - ADrvStrenP[4:0] + * - ADrvStrenN[9:5] + * - Dependencies: + * - user_input_basic.numanib + * - user_input_advanced.atximpedance + * \return 0 on success. + */ +static int atximpedance_program(struct stm32mp_ddr_config *config) +{ + int adrvstrenn_state; + int adrvstrenp_state; + uint32_t anib; + uint16_t atximpedance; + + adrvstrenp_state = ddrphy_phyinit_mapdrvstren(config->uia.atximpedance, + ADRVSTRENP); + if (adrvstrenp_state < 0) { + return adrvstrenp_state; + } + + adrvstrenn_state = ddrphy_phyinit_mapdrvstren(config->uia.atximpedance, + ADRVSTRENN); + if (adrvstrenn_state < 0) { + return adrvstrenn_state; + } + + atximpedance = (uint16_t)((adrvstrenn_state << CSR_ADRVSTRENN_LSB) | + (adrvstrenp_state << CSR_ADRVSTRENP_LSB)); + + for (anib = 0U; anib < config->uib.numanib; anib++) { + uint32_t c_addr; + + c_addr = anib << 12; + mmio_write_16((uintptr_t)(DDRPHYC_BASE + (4U * (TANIB | c_addr | + CSR_ATXIMPEDANCE_ADDR))), + atximpedance); + } + + return 0; +} + +/* + * Program dfimode: + * - Dependencies: + * - user_input_basic.dfi1exists + */ +static void dfimode_program(struct stm32mp_ddr_config *config) +{ + uint16_t dfimode = 0x5U; + +#if STM32MP_DDR3_TYPE || STM32MP_DDR4_TYPE + if (config->uib.dfi1exists == 0U) { + dfimode = 0x1U; /* DFI1 does not physically exists */ + } +#endif /* STM32MP_DDR3_TYPE || STM32MP_DDR4_TYPE */ + + mmio_write_16((uintptr_t)(DDRPHYC_BASE + (4U * (TMASTER | CSR_DFIMODE_ADDR))), dfimode); +} + +/* + * Program dficamode: + * - Fields: + * - DfiLp3CAMode + * - DfiD4CAMode + * - DfiLp4CAMode + * - DfiD4AltCAMode + */ +static void dficamode_program(void) +{ + uint16_t dficamode; + +#if STM32MP_DDR3_TYPE + dficamode = 0U; +#elif STM32MP_DDR4_TYPE + dficamode = 2U; +#else /* STM32MP_LPDDR4_TYPE */ + dficamode = 4U; +#endif /* STM32MP_DDR3_TYPE */ + + mmio_write_16((uintptr_t)(DDRPHYC_BASE + (4U * (TMASTER | CSR_DFICAMODE_ADDR))), dficamode); +} + +/* + * Program caldrvstr0: + * - Fields: + * - caldrvstrpd50[3:0] + * - caldrvstrpu50[7:4] + * - Dependencies: + * - user_input_advanced.extcalresval + */ +static void caldrvstr0_program(struct stm32mp_ddr_config *config) +{ + uint16_t caldrvstr0; + uint16_t caldrvstrp50 = (uint16_t)config->uia.extcalresval; + + caldrvstr0 = (caldrvstrp50 << CSR_CALDRVSTRPU50_LSB) | caldrvstrp50; + + mmio_write_16((uintptr_t)(DDRPHYC_BASE + (4U * (TMASTER | CSR_CALDRVSTR0_ADDR))), + caldrvstr0); +} + +/* + * Program CalUclkInfo: + * - Impedance calibration CLK Counter. + * - Fields: + * - caluclkticksper1us + * - Dependencies: + * - user_input_basic.frequency + */ +static void caluclkinfo_program(struct stm32mp_ddr_config *config) +{ + uint32_t caluclkticksper1us_x10; + uint16_t caluclkticksper1us; + + /* Number of DfiClk cycles per 1us */ + caluclkticksper1us_x10 = (10U * config->uib.frequency) / 2U; + caluclkticksper1us = (uint16_t)(caluclkticksper1us_x10 / 10U); + + if ((config->uib.frequency % 2U) != 0U) { + caluclkticksper1us++; + } + + if (caluclkticksper1us < 24U) { + /* Minimum value of caluclkticksper1us = 24 */ + caluclkticksper1us = 24U; + } + + mmio_write_16((uintptr_t)(DDRPHYC_BASE + (4U * (TMASTER | CSR_CALUCLKINFO_ADDR))), + caluclkticksper1us); +} + +/* + * Program Calibration CSRs based on user input + * - Fields: + * - calinterval + * - calonce + * - Dependencies: + * - user_input_advanced.calinterval + * - user_input_advanced.calonce + */ +static void calibration_program(struct stm32mp_ddr_config *config) +{ + uint32_t calinterval; + uint32_t calonce; + uint16_t calrate; + + calinterval = config->uia.calinterval; + calonce = config->uia.calonce; + + calrate = (uint16_t)((calonce << CSR_CALONCE_LSB) | (calinterval << CSR_CALINTERVAL_LSB)); + + mmio_write_16((uintptr_t)(DDRPHYC_BASE + (4U * (TMASTER | CSR_CALRATE_ADDR))), calrate); +} + +/* + * Program vrefinglobal: + * - dqdqsrcvcntrl and csrvrefinglobal to select Global VREF + * from Master to be used in each DQ. + * - Fields: + * - globalvrefinsel: Select Range of GlobalVref DAC. Default: set to 1. + * - globalvrefindac: Vref level is set based on mb_ddr_1d->phyvref value. + * The following formula is used to convert the phyvref into the register setting. + * \f{eqnarray*}{ + * PhyVrefPrcnt &=& \frac{mb_ddr_1d->phyvref}{128} \\ + * if globalvrefinsel = 1 : + * globalvrefindac &=& 1+\frac{PhyVrefPrcnt}{0.005} \\ + * if globalvrefinsel = 0 : + * globalvrefindac &=& \frac{(PhyVrefPrcnt-0.345)}{0.005} \\ + * RxVref &=& (globalvrefindac == 0) ? Hi-Z : (PhyVrefPrcnt \times VDDQ) + * \f} + * + * Program dqdqsrcvcntrl: + * - dqdqsrcvcntrl and csrvrefinglobal to select Global VREF + * from Master to be used in each DQ + * - Fields: + * - selanalogvref + * - majormodedbyte + * - ExtVrefRange + * - DfeCtrl + * - GainCurrAdj + * - Dependencies: + * - user_input_basic.numdbyte + */ +static void vrefinglobal_program(struct stm32mp_ddr_config *config, + struct pmu_smb_ddr_1d *mb_ddr_1d) +{ + uint32_t majormodedbyte; + int32_t vref_percentvddq = (int32_t)mb_ddr_1d->phyvref * 1000 * 100 / 128; + uint8_t globalvrefindac = 0x0U; + uint8_t globalvrefinsel = 0x4U; + uint32_t byte; + uint32_t dfectrl_defval = 0U; + uint32_t extvrefrange_defval = 0U; + uint32_t gaincurradj_defval = 0xBU; + uint32_t selanalogvref = 1U; /* Use Global VREF from Master */ + uint16_t dqdqsrcvcntrl; + uint16_t vrefinglobal; + +#if STM32MP_DDR3_TYPE + majormodedbyte = 0U; +#elif STM32MP_DDR4_TYPE + majormodedbyte = 3U; +#else /* STM32MP_LPDDR4_TYPE */ + majormodedbyte = 2U; +#endif /* STM32MP_DDR3_TYPE */ + + /* Check range1 first. Only use range0 if customer input maxes out range1. */ + globalvrefindac = (uint8_t)((vref_percentvddq / 500) + 1); + if (globalvrefindac > 127U) { + /* Min value is 1 */ + globalvrefindac = (uint8_t)(MAX((vref_percentvddq - 34500), 500) / 500); + globalvrefinsel = 0x0U; + } + globalvrefindac = MIN(globalvrefindac, (uint8_t)127); + + vrefinglobal = (uint16_t)((globalvrefindac << CSR_GLOBALVREFINDAC_LSB) | globalvrefinsel); + mmio_write_16((uintptr_t)(DDRPHYC_BASE + (4U * (TMASTER | CSR_VREFINGLOBAL_ADDR))), + vrefinglobal); + + dqdqsrcvcntrl = (uint16_t)((gaincurradj_defval << CSR_GAINCURRADJ_LSB) | + (majormodedbyte << CSR_MAJORMODEDBYTE_LSB) | + (dfectrl_defval << CSR_DFECTRL_LSB) | + (extvrefrange_defval << CSR_EXTVREFRANGE_LSB) | + (selanalogvref << CSR_SELANALOGVREF_LSB)); + + for (byte = 0U; byte < config->uib.numdbyte; byte++) { + uint32_t c_addr; + uint32_t lane; + + c_addr = byte << 12; + for (lane = 0U; lane <= B_MAX; lane++) { + uint32_t b_addr; + + b_addr = lane << 8; + mmio_write_16((uintptr_t)(DDRPHYC_BASE + (4U * (TDBYTE | c_addr | b_addr | + CSR_DQDQSRCVCNTRL_ADDR))), + dqdqsrcvcntrl); + } + } +} + +/* + * Program dfifreqratio : + * - Dependencies: + * - user_input_basic.dfifreqratio + */ +static void dfifreqratio_program(struct stm32mp_ddr_config *config) +{ + uint16_t dfifreqratio; + + dfifreqratio = (uint16_t)config->uib.dfifreqratio; + + mmio_write_16((uintptr_t)(DDRPHYC_BASE + (4U * (TMASTER | CSR_DFIFREQRATIO_ADDR))), + dfifreqratio); +} + +/* + * Program tristatemodeca based on dramtype and 2T Timing + * - Fields: + * - CkDisVal + * - disdynadrtri + * - ddr2tmode + * - Dependencies: + * - user_input_advanced.is2ttiming + * - user_input_advanced.disdynadrtri + */ +static void tristatemodeca_program(struct stm32mp_ddr_config *config) +{ + uint32_t ckdisval_def; + uint32_t ddr2tmode; + uint32_t disdynadrtri; + uint16_t tristatemodeca; + + /* CkDisVal depends on dramtype */ +#if STM32MP_DDR3_TYPE || STM32MP_DDR4_TYPE + ckdisval_def = 1U; /* {CLK_t,CLK_c} = 2'b00; */ +#else /* STM32MP_LPDDR4_TYPE */ + ckdisval_def = 0U; /* {CLK_t,CLK_c} = 2'b01; */ +#endif /* STM32MP_DDR3_TYPE || STM32MP_DDR4_TYPE */ + +#if STM32MP_DDR3_TYPE || STM32MP_DDR4_TYPE + disdynadrtri = config->uia.disdynadrtri; +#else /* STM32MP_LPDDR4_TYPE */ + disdynadrtri = 1U; +#endif /* STM32MP_DDR3_TYPE || STM32MP_DDR4_TYPE */ + + ddr2tmode = config->uia.is2ttiming; + + tristatemodeca = (uint16_t)((ckdisval_def << CSR_CKDISVAL_LSB) | + (ddr2tmode << CSR_DDR2TMODE_LSB) | + (disdynadrtri << CSR_DISDYNADRTRI_LSB)); + + mmio_write_16((uintptr_t)(DDRPHYC_BASE + (4U * (TMASTER | CSR_TRISTATEMODECA_ADDR))), + tristatemodeca); +} + +/* + * Program DfiXlat based on Pll Bypass Input + * - Dependencies: + * - user_input_basic.frequency + * - user_input_basic.pllbypass + */ +static void dfixlat_program(struct stm32mp_ddr_config *config) +{ + uint16_t loopvector; + uint16_t pllbypass_dat = 0U; + + pllbypass_dat |= (uint16_t)config->uib.pllbypass; + + for (loopvector = 0U; loopvector < 8U; loopvector++) { + uint16_t dfifreqxlat_dat; + uintptr_t reg = (uintptr_t)(DDRPHYC_BASE + (4U * (C0 | TMASTER | + (CSR_DFIFREQXLAT0_ADDR + + loopvector)))); + +#if STM32MP_DDR3_TYPE || STM32MP_DDR4_TYPE + if (loopvector == 0U) { + /* + * Relock DfiFreq = 00,01,02,03) Use StartVec 5 (pll_enabled) or + * StartVec 6 (pll_bypassed). + */ + dfifreqxlat_dat = pllbypass_dat + 0x5555U; + + mmio_write_16(reg, dfifreqxlat_dat); + } else if (loopvector == 7U) { + /* LP3-entry DfiFreq = 1F */ + mmio_write_16(reg, 0xF000U); + } else { + /* + * Everything else = skip retrain (could also map to 0000 since retrain + * code is excluded, but this is cleaner). + */ + mmio_write_16(reg, 0x5555U); + } +#else /* STM32MP_LPDDR4_TYPE */ + if (loopvector == 0U) { + uint16_t skipddc_dat = 0U; /* + * Set to vector offset based on frequency + * to disable dram drift compensation. + */ + + if (config->uib.frequency < 333U) { + skipddc_dat |= 0x5U; + } + + /* + * Retrain & Relock DfiFreq = 00,01,02,03) Use StartVec 0 (pll_enabled) or + * StartVec 1 (pll_bypassed). + */ + dfifreqxlat_dat = pllbypass_dat + skipddc_dat; + mmio_write_16(reg, dfifreqxlat_dat); + } else if (loopvector == 2U) { + /* + * Retrain only DfiFreq = 08,09,0A,0B) Use StartVec 4 (1, and maybe 2,3, + * used by verif). + */ + mmio_write_16(reg, 0x4444U); + } else if (loopvector == 3U) { + /* Phymstr type state change, StartVec 8 */ + mmio_write_16(reg, 0x8888U); + } else if (loopvector == 4U) { + /* + * Relock only DfiFreq = 10,11,12,13 Use StartVec 5 (pll_enabled) or + * StartVec 6 (pll_bypassed). + */ + dfifreqxlat_dat = pllbypass_dat + 0x5555U; + mmio_write_16(reg, dfifreqxlat_dat); + } else if (loopvector == 7U) { + /* LP3-entry DfiFreq = 1F */ + mmio_write_16(reg, 0xF000U); + } else { + /* Everything else */ + mmio_write_16(reg, 0x0000U); + } +#endif /* STM32MP_DDR3_TYPE || STM32MP_DDR4_TYPE */ + } +} + +/* + * Program dqdqsrcvcntrl1 (Receiver Powerdown) and DbyteMiscMode + * - see function ddrphy_phyinit_isdbytedisabled() to determine + * which DBytes are turned off completely based on PHY configuration. + * - Fields: + * - DByteDisable + * - PowerDownRcvr + * - PowerDownRcvrDqs + * - RxPadStandbyEn + * - Dependencies: + * - user_input_basic.numdbyte + * - user_input_basic.dramdatawidth (DDR3/DDR4) + * - mb_ddr_1d->mr5 (DDR4) + * - user_input_advanced.lp4dbird (LPDDR4) + */ +static void dqdqsrcvcntrl1_program(struct stm32mp_ddr_config *config, + struct pmu_smb_ddr_1d *mb_ddr_1d) +{ + uint32_t d; + uint16_t mr5 __maybe_unused; + uint16_t regdata; + uint16_t regdata1; + uint16_t regdata2; /* Turn off Rx of DBI lane */ + + regdata = 0x1U << CSR_DBYTEDISABLE_LSB; + + regdata1 = (0x1FFU << CSR_POWERDOWNRCVR_LSB) | + (0x1U << CSR_POWERDOWNRCVRDQS_LSB) | + (0x1U << CSR_RXPADSTANDBYEN_LSB); + + regdata2 = (0x100U << CSR_POWERDOWNRCVR_LSB) | CSR_RXPADSTANDBYEN_MASK; + +#if STM32MP_DDR4_TYPE + /* OR all mr4 masked values, to help check in next loop */ + mr5 = (mb_ddr_1d->mr5 >> 12) & 0x1U; +#endif /* STM32MP_DDR4_TYPE */ + + for (d = 0U; d < config->uib.numdbyte; d++) { + uint32_t c_addr; + + c_addr = d * C1; + if (ddrphy_phyinit_isdbytedisabled(config, mb_ddr_1d, d) != 0) { + mmio_write_16((uintptr_t)(DDRPHYC_BASE + (4U * (c_addr | TDBYTE | + CSR_DBYTEMISCMODE_ADDR))), + regdata); + mmio_write_16((uintptr_t)(DDRPHYC_BASE + (4U * (c_addr | TDBYTE | + CSR_DQDQSRCVCNTRL1_ADDR))), + regdata1); + } else { + /* Disable RDBI lane if not used. */ +#if STM32MP_DDR3_TYPE + if (config->uib.dramdatawidth != 4U) { +#elif STM32MP_DDR4_TYPE + if ((config->uib.dramdatawidth != 4U) && (mr5 == 0U)) { +#else /* STM32MP_LPDDR4_TYPE */ + if (config->uia.lp4dbird == 0U) { +#endif /* STM32MP_DDR3_TYPE */ + mmio_write_16((uintptr_t) + (DDRPHYC_BASE + (4U * (c_addr | TDBYTE | + CSR_DQDQSRCVCNTRL1_ADDR))), + regdata2); + } + } + } +} + +/* + * Program masterx4config + * - Fields: + * - x4tg + * - masterx4config + * - Dependencies: + * - user_input_basic.dramdatawidth + * + * \note PHY does not support mixed dram device data width + */ +static void masterx4config_program(struct stm32mp_ddr_config *config) +{ + uint32_t x4tg = 0U; + uint16_t masterx4config; + + if (config->uib.dramdatawidth == 4U) { + x4tg = 0xFU; + } + + masterx4config = (uint16_t)(x4tg << CSR_X4TG_LSB); + + mmio_write_16((uintptr_t)(DDRPHYC_BASE + (4U * (TMASTER | CSR_MASTERX4CONFIG_ADDR))), + masterx4config); +} + +#if !STM32MP_DDR3_TYPE +/* + * Program dmipinpresent based on dramtype and Read-DBI enable + * - Fields: + * - RdDbiEnabled + * - Dependencies: + * - mb_ddr_1d->mr5 (DDR4) + * - user_input_advanced.lp4dbird (LPDDR4) + */ +static void dmipinpresent_program(struct stm32mp_ddr_config *config, + struct pmu_smb_ddr_1d *mb_ddr_1d) +{ + uint16_t dmipinpresent; + +#if STM32MP_DDR4_TYPE + /* For DDR4, Read DBI is enabled in mr5-A12 */ + dmipinpresent = (mb_ddr_1d->mr5 >> 12) & 0x1U; +#else /* STM32MP_LPDDR4_TYPE */ + dmipinpresent = (uint16_t)config->uia.lp4dbird; +#endif /* STM32MP_DDR4_TYPE */ + + mmio_write_16((uintptr_t)(DDRPHYC_BASE + (4U * (TMASTER | CSR_DMIPINPRESENT_ADDR))), + dmipinpresent); +} +#endif /* !STM32MP_DDR3_TYPE */ + +/* + * Program aforcetricont and acx4anibdis + * - Fields: + * - aforcetricont + * - acx4anibdis + * - Dependencies: + * - user_input_basic.numrank_dfi0 + * - user_input_basic.numrank_dfi1 + * - user_input_basic.numanib + * - user_input_advanced.disableunusedaddrlns + */ +static void aforcetricont_acx4anibdis_program(struct stm32mp_ddr_config *config) +{ + uint32_t anib; + uint16_t acx4anibdis = 0x0U; + + for (anib = 0U; (anib < config->uib.numanib) && (config->uia.disableunusedaddrlns != 0U); + anib++) { + uint32_t c_addr; +#if STM32MP_DDR3_TYPE || STM32MP_DDR4_TYPE + uint32_t numrank = config->uib.numrank_dfi0 + config->uib.numrank_dfi1; +#else /* STM32MP_LPDDR4_TYPE */ + uint32_t numrank0 = config->uib.numrank_dfi0; + uint32_t numrank1 = config->uib.numrank_dfi1; +#endif /* STM32MP_DDR3_TYPE || STM32MP_DDR4_TYPE */ + uint16_t aforcetricont = 0x0U; + + c_addr = anib << 12; + +#if STM32MP_DDR3_TYPE || STM32MP_DDR4_TYPE + if ((anib == 0U) && (numrank == 1U)) { + aforcetricont = 0x2U; + } else if ((anib == 1U) && (numrank == 1U)) { + aforcetricont = 0xCU; + } else if (anib == 6U) { + aforcetricont = 0x1U; + } +#else /* STM32MP_LPDDR4_TYPE */ + if ((anib == 0U) && (numrank0 == 0U)) { + aforcetricont = 0xFU; + } else if ((anib == 0U) && (numrank0 == 1U)) { + aforcetricont = 0x2U; + } else if ((anib == 1U) && (numrank0 == 0U)) { + aforcetricont = 0xFU; + } else if ((anib == 1U) && (numrank0 == 1U)) { + aforcetricont = 0x8U; + } else if ((anib == 2U) && (numrank0 == 0U)) { + aforcetricont = 0xFU; + } else if ((anib == 3U) && (numrank1 == 0U)) { + aforcetricont = 0xFU; + } else if ((anib == 3U) && (numrank1 == 1U)) { + aforcetricont = 0x2U; + } else if ((anib == 4U) && (numrank1 == 0U)) { + aforcetricont = 0xFU; + } else if ((anib == 4U) && (numrank1 == 1U)) { + aforcetricont = 0x8U; + } else if ((anib == 5U) && (numrank1 == 0U)) { + aforcetricont = 0xFU; + } else if (anib == 6U) { + aforcetricont = 0xFU; + } else if (anib == 7U) { + aforcetricont = 0xFU; + } + + /* + * If all the lanes can be disabled, and Anib is not the first or last disable + * entire chiplet + */ + if ((aforcetricont == 0xFU) && (anib != 0U) && + (anib != (config->uib.numanib - 1U))) { + acx4anibdis = acx4anibdis | (0x1U << anib); + } +#endif /* STM32MP_DDR3_TYPE || STM32MP_DDR4_TYPE */ + + mmio_write_16((uintptr_t)(DDRPHYC_BASE + (4U * (TANIB | c_addr | + CSR_AFORCETRICONT_ADDR))), + aforcetricont); + } + + mmio_write_16((uintptr_t)(DDRPHYC_BASE + (4U * (TMASTER | CSR_ACX4ANIBDIS_ADDR))), + acx4anibdis); +} + +/* + * Implements Step C of initialization sequence + * + * This function programs majority of PHY configuration registers based + * on data input into PhyInit data structures. + * + * This function programs PHY configuration registers based on information + * provided in the PhyInit data structures (config->uib, config->uia). + * The user can overwrite the programming of this function by modifying + * ddrphy_phyinit_usercustom_custompretrain(). Please see + * ddrphy_phyinit_struct.h for PhyInit data structure definition. + * + * \return 0 on success. + */ +int ddrphy_phyinit_c_initphyconfig(struct stm32mp_ddr_config *config, + struct pmu_smb_ddr_1d *mb_ddr_1d, uint32_t *ardptrinitval) +{ + uint32_t twotckrxdqspre; + int ret; + + /* + * Step (C) Initialize PHY Configuration + * Load the required PHY configuration registers for the appropriate mode and memory + * configuration. + */ + + txslewrate_program(config); + + atxslewrate_program(config); + +#if STM32MP_DDR3_TYPE || STM32MP_DDR4_TYPE + dfidatacsdestmap_program(mb_ddr_1d); +#endif /* STM32MP_DDR3_TYPE || STM32MP_DDR4_TYPE */ + + pllctrl2_program(config); + + ardptrinitval_program(config, ardptrinitval); + +#if STM32MP_LPDDR4_TYPE + procodtctl_program(); +#endif /* STM32MP_LPDDR4_TYPE */ + + dbytedllmodecntrl_program(config, &twotckrxdqspre); + + procodttimectl_program(config, twotckrxdqspre); + + ret = txodtdrvstren_program(config); + if (ret != 0) { + return ret; + } + + ret = tximpedancectrl1_program(config); + if (ret != 0) { + return ret; + } + + ret = atximpedance_program(config); + if (ret != 0) { + return ret; + } + + dfimode_program(config); + + dficamode_program(); + + caldrvstr0_program(config); + + caluclkinfo_program(config); + + calibration_program(config); + + vrefinglobal_program(config, mb_ddr_1d); + + dfifreqratio_program(config); + + tristatemodeca_program(config); + + dfixlat_program(config); + + dqdqsrcvcntrl1_program(config, mb_ddr_1d); + + masterx4config_program(config); + +#if !STM32MP_DDR3_TYPE + dmipinpresent_program(config, mb_ddr_1d); + +#if STM32MP_LPDDR4_TYPE + /* + * Program DFIPHYUPD + * - Fields: + * - DFIPHYUPDMODE + * - DFIPHYUPDCNT + * - Dependencies: + * - user_input_advanced.disablephyupdate + */ + if (config->uia.disablephyupdate != 0U) { + mmio_write_16((uintptr_t)(DDRPHYC_BASE + (4U * (TMASTER | CSR_DFIPHYUPD_ADDR))), + 0x0U); + } +#endif /* STM32MP_LPDDR4_TYPE */ +#endif /* !STM32MP_DDR3_TYPE */ + + aforcetricont_acx4anibdis_program(config); + + return 0; +} diff --git a/drivers/st/ddr/phy/phyinit/src/ddrphy_phyinit_calcmb.c b/drivers/st/ddr/phy/phyinit/src/ddrphy_phyinit_calcmb.c new file mode 100644 index 00000000..c5fa5f1b --- /dev/null +++ b/drivers/st/ddr/phy/phyinit/src/ddrphy_phyinit_calcmb.c @@ -0,0 +1,210 @@ +/* + * Copyright (C) 2021-2024, STMicroelectronics - All Rights Reserved + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <stdlib.h> + +#include <common/debug.h> + +#include <ddrphy_phyinit.h> + +/* + * Reads PhyInit inputs structures and sets relevant message block + * parameters. + * + * This function sets Message Block parameters based on user_input_basic and + * user_input_advanced. user changes in these files takes precedence + * over this function call. + * + * MessageBlock fields set : + * - dramtype + * - pstate + * - dramfreq + * - pllbypassen + * - dfifreqratio + * - phyodtimpedance + * - phydrvimpedance + * - bpznresval + * - enableddqscha (LPDDR4) + * - cspresentcha (LPDDR4) + * - enableddqsChb (LPDDR4) + * - cspresentchb (LPDDR4) + * - enableddqs (DDR3/DDR4) + * - phycfg (DDR3/DDR4) + * - x16present (DDR4) + * + * \return 0 on success. + */ +int ddrphy_phyinit_calcmb(struct stm32mp_ddr_config *config, struct pmu_smb_ddr_1d *mb_ddr_1d) +{ + uint32_t nad0 = config->uib.numactivedbytedfi0; + uint32_t nad1 = 0; + uint16_t mr4 __maybe_unused; + uint16_t disableddbyte __maybe_unused; + uint32_t dbyte __maybe_unused; + int ret; + +#if STM32MP_LPDDR4_TYPE + nad1 = config->uib.numactivedbytedfi1; +#endif /* STM32MP_LPDDR4_TYPE */ + + /* A few checks to make sure valid programming */ + if ((nad0 == 0U) || (config->uib.numdbyte == 0U)) { + ERROR("%s %d\n", __func__, __LINE__); + VERBOSE("%s numactivedbytedfi0, numactivedbytedfi0, NumByte out of range.\n", + __func__); + return -1; + } + + if ((nad0 + nad1) > config->uib.numdbyte) { + ERROR("%s %d\n", __func__, __LINE__); + VERBOSE("%s numactivedbytedfi0+numactivedbytedfi1 is larger than numdbyteDfi0\n", + __func__); + return -1; + } + + if ((config->uib.dfi1exists == 0U) && (nad1 != 0U)) { + ERROR("%s %d\n", __func__, __LINE__); + VERBOSE("%s dfi1exists==0 but numdbyteDfi0 != 0\n", __func__); + return -1; + } + +#if STM32MP_DDR4_TYPE + /* OR all mr4 masked values, to help check in next loop */ + mr4 = mb_ddr_1d->mr4 & 0x1C0U; + + /* 1D message block defaults */ + if (mr4 != 0x0U) { + ERROR("mr4 != 0x0\n"); + VERBOSE("%s Setting DRAM CAL mode is not supported by the PHY.\n", __func__); + VERBOSE("Memory controller may set CAL mode after PHY has entered mission\n"); + VERBOSE("mode. Please check value programmed in mb_ddr_1d[*].mr4\n"); + VERBOSE("and unset A8:6\n"); + return -1; + } +#endif /* STM32MP_DDR4_TYPE */ + +#if STM32MP_DDR3_TYPE + if (config->uib.dimmtype == DDR_DIMMTYPE_NODIMM) { + ret = ddrphy_phyinit_softsetmb(mb_ddr_1d, MB_FIELD_DRAMTYPE, 0x1U); + if (ret != 0) { + return ret; + } + } +#elif STM32MP_DDR4_TYPE + if (config->uib.dimmtype == DDR_DIMMTYPE_NODIMM) { + ret = ddrphy_phyinit_softsetmb(mb_ddr_1d, MB_FIELD_DRAMTYPE, 0x2U); + if (ret != 0) { + return ret; + } + } +#else /* STM32MP_LPDDR4_TYPE */ + /* Nothing to do */ +#endif /* STM32MP_DDR3_TYPE */ + + ret = ddrphy_phyinit_softsetmb(mb_ddr_1d, MB_FIELD_PSTATE, 0U); + if (ret != 0) { + return ret; + } + + ret = ddrphy_phyinit_softsetmb(mb_ddr_1d, MB_FIELD_DRAMFREQ, config->uib.frequency * 2U); + if (ret != 0) { + return ret; + } + + ret = ddrphy_phyinit_softsetmb(mb_ddr_1d, MB_FIELD_PLLBYPASSEN, config->uib.pllbypass); + if (ret != 0) { + return ret; + } + + if (config->uib.dfifreqratio == 1U) { + ret = ddrphy_phyinit_softsetmb(mb_ddr_1d, MB_FIELD_DFIFREQRATIO, 0x2U); + if (ret != 0) { + return ret; + } + } + + ret = ddrphy_phyinit_softsetmb(mb_ddr_1d, MB_FIELD_PHYODTIMPEDANCE, 0U); + if (ret != 0) { + return ret; + } + + ret = ddrphy_phyinit_softsetmb(mb_ddr_1d, MB_FIELD_PHYDRVIMPEDANCE, 0U); + if (ret != 0) { + return ret; + } + + ret = ddrphy_phyinit_softsetmb(mb_ddr_1d, MB_FIELD_BPZNRESVAL, 0U); + if (ret != 0) { + return ret; + } + +#if STM32MP_DDR3_TYPE || STM32MP_DDR4_TYPE + ret = ddrphy_phyinit_softsetmb(mb_ddr_1d, MB_FIELD_ENABLEDDQS, nad0 * 8U); + if (ret != 0) { + return ret; + } + + disableddbyte = 0x0U; + + for (dbyte = 0U; (dbyte < config->uib.numdbyte) && (dbyte < 8U); dbyte++) { + if (ddrphy_phyinit_isdbytedisabled(config, mb_ddr_1d, dbyte) != 0) { + disableddbyte |= 0x1U << dbyte; + } + } + + ret = ddrphy_phyinit_softsetmb(mb_ddr_1d, MB_FIELD_DISABLEDDBYTE, disableddbyte); + if (ret != 0) { + return ret; + } + +#if STM32MP_DDR3_TYPE + ret = ddrphy_phyinit_softsetmb(mb_ddr_1d, MB_FIELD_PHYCFG, config->uia.is2ttiming); + if (ret != 0) { + return ret; + } +#else /* STM32MP_DDR4_TYPE */ + ret = ddrphy_phyinit_softsetmb(mb_ddr_1d, MB_FIELD_PHYCFG, + ((mb_ddr_1d->mr3 & 0x8U) != 0U) ? + 0U : config->uia.is2ttiming); + if (ret != 0) { + return ret; + } + + ret = ddrphy_phyinit_softsetmb(mb_ddr_1d, MB_FIELD_X16PRESENT, + (config->uib.dramdatawidth == 0x10U) ? + mb_ddr_1d->cspresent : 0x0U); + if (ret != 0) { + return ret; + } +#endif /* STM32MP_DDR3_TYPE */ +#else /* STM32MP_LPDDR4_TYPE */ + ret = ddrphy_phyinit_softsetmb(mb_ddr_1d, MB_FIELD_ENABLEDDQSCHA, nad0 * 8U); + if (ret != 0) { + return ret; + } + + ret = ddrphy_phyinit_softsetmb(mb_ddr_1d, MB_FIELD_CSPRESENTCHA, + (config->uib.numrank_dfi0 == 2U) ? + 0x3U : config->uib.numrank_dfi0); + if (ret != 0) { + return ret; + } + + ret = ddrphy_phyinit_softsetmb(mb_ddr_1d, MB_FIELD_ENABLEDDQSCHB, nad1 * 8U); + if (ret != 0) { + return ret; + } + + ret = ddrphy_phyinit_softsetmb(mb_ddr_1d, MB_FIELD_CSPRESENTCHB, + (config->uib.numrank_dfi1 == 2U) ? + 0x3U : config->uib.numrank_dfi1); + if (ret != 0) { + return ret; + } +#endif /* STM32MP_DDR3_TYPE || STM32MP_DDR4_TYPE */ + + return 0; +} diff --git a/drivers/st/ddr/phy/phyinit/src/ddrphy_phyinit_d_loadimem.c b/drivers/st/ddr/phy/phyinit/src/ddrphy_phyinit_d_loadimem.c new file mode 100644 index 00000000..8bec30ba --- /dev/null +++ b/drivers/st/ddr/phy/phyinit/src/ddrphy_phyinit_d_loadimem.c @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2021-2024, STMicroelectronics - All Rights Reserved + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <stdio.h> +#include <stdlib.h> + +#include <common/debug.h> + +#include <ddrphy_phyinit.h> + +#include <lib/mmio.h> + +#include <platform_def.h> + +/* + * This function loads the training firmware IMEM image into the PHY. + * + * This function reads the DDR firmware source memory area to generate a + * set of apb writes to load IMEM image into the PHY. The exact steps in this + * function are as follows: + * + * -# Ensure DRAM is in reset. + * -# Load the microcontroller memory with the provided training firmware + * -# Initialize the firmware mailbox structures to be able to communicate with + * the firmware. + * + * \return void + */ +void ddrphy_phyinit_d_loadimem(void) +{ + uint16_t memresetl; + uint32_t *ptr32; + + /* Set memresetl to avoid glitch on BP_MemReset_L during training */ + memresetl = CSR_PROTECTMEMRESET_MASK; + mmio_write_16((uintptr_t)(DDRPHYC_BASE + (4U * (TMASTER | CSR_MEMRESETL_ADDR))), memresetl); + + ptr32 = (uint32_t *)(STM32MP_DDR_FW_BASE + STM32MP_DDR_FW_IMEM_OFFSET); + ddrphy_phyinit_writeoutmem(ptr32, IMEM_ST_ADDR, IMEM_SIZE); +} diff --git a/drivers/st/ddr/phy/phyinit/src/ddrphy_phyinit_f_loaddmem.c b/drivers/st/ddr/phy/phyinit/src/ddrphy_phyinit_f_loaddmem.c new file mode 100644 index 00000000..3c6c87f4 --- /dev/null +++ b/drivers/st/ddr/phy/phyinit/src/ddrphy_phyinit_f_loaddmem.c @@ -0,0 +1,70 @@ +/* + * Copyright (C) 2021-2024, STMicroelectronics - All Rights Reserved + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <stdio.h> +#include <stdlib.h> + +#include <common/debug.h> + +#include <ddrphy_phyinit.h> + +#include <platform_def.h> + +/* + * This function loads the training firmware DMEM image and write the + * Message Block parameters for the training firmware into the PHY. + * + * This function performs the following tasks: + * + * -# Load the firmware DMEM segment to initialize the data structures from the + * DDR firmware source memory area. + * -# Write the Firmware Message Block with the required contents detailing the training parameters. + * + * \return 0 on success. + */ +int ddrphy_phyinit_f_loaddmem(struct stm32mp_ddr_config *config, struct pmu_smb_ddr_1d *mb_ddr_1d) +{ + uint32_t sizeofmsgblk; + uint16_t *ptr16; + uint32_t *ptr32; + + /* Some basic checks on MessageBlock */ +#if STM32MP_DDR3_TYPE || STM32MP_DDR4_TYPE + if ((mb_ddr_1d->enableddqs > (8U * (uint8_t)config->uib.numactivedbytedfi0)) || + (mb_ddr_1d->enableddqs <= 0U)) { + ERROR("%s %d\n", __func__, __LINE__); + VERBOSE("%s enableddqs is Zero or greater than NumActiveDbytes for Dfi0\n", + __func__); + return -1; + } +#else /* STM32MP_LPDDR4_TYPE */ + if (((mb_ddr_1d->enableddqscha % 16U) != 0U) || ((mb_ddr_1d->enableddqschb % 16U) != 0U)) { + ERROR("%s %d\n", __func__, __LINE__); + VERBOSE("%s Lp3/Lp4 - Number of Dq's Enabled per Channel much be multipe of 16\n", + __func__); + return -1; + } + + if ((mb_ddr_1d->enableddqscha > (uint8_t)(8U * config->uib.numactivedbytedfi0)) || + (mb_ddr_1d->enableddqschb > (uint8_t)(8U * config->uib.numactivedbytedfi1)) || + ((mb_ddr_1d->enableddqscha == 0U) && (mb_ddr_1d->enableddqschb == 0U))) { + ERROR("%s %d\n", __func__, __LINE__); + VERBOSE("%s EnabledDqsChA/B are not set correctly./1\n", __func__); + return -1; + } +#endif /* STM32MP_DDR3_TYPE || STM32MP_DDR4_TYPE */ + + sizeofmsgblk = sizeof(struct pmu_smb_ddr_1d); + + ptr16 = (uint16_t *)mb_ddr_1d; + ddrphy_phyinit_writeoutmsgblk(ptr16, DMEM_ST_ADDR, sizeofmsgblk); + + ptr32 = (uint32_t *)(STM32MP_DDR_FW_BASE + STM32MP_DDR_FW_DMEM_OFFSET); + ddrphy_phyinit_writeoutmem(ptr32, DMEM_ST_ADDR + DMEM_BIN_OFFSET, + DMEM_SIZE - STM32MP_DDR_FW_DMEM_OFFSET); + + return 0; +} diff --git a/drivers/st/ddr/phy/phyinit/src/ddrphy_phyinit_g_execfw.c b/drivers/st/ddr/phy/phyinit/src/ddrphy_phyinit_g_execfw.c new file mode 100644 index 00000000..0c11594b --- /dev/null +++ b/drivers/st/ddr/phy/phyinit/src/ddrphy_phyinit_g_execfw.c @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2021-2024, STMicroelectronics - All Rights Reserved + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <common/debug.h> + +#include <ddrphy_phyinit.h> + +#include <lib/mmio.h> + +#include <platform_def.h> + +/* + * Execute the Training Firmware + * + * The training firmware is executed with the procedure: + * + * -# Reset the firmware microcontroller by writing the MicroReset register to + * set the StallToMicro and ResetToMicro fields to 1 (all other fields should be + * zero). Then rewrite the registers so that only the StallToMicro remains set + * (all other fields should be zero). + * -# Begin execution of the training firmware by setting the MicroReset + * register to 0. + * -# Wait for the training firmware to complete by following the procedure implemented in + * ddrphy_phyinit_usercustom_g_waitfwdone() function. + * -# Halt the microcontroller. + * + * \return 0 on success. + */ +int ddrphy_phyinit_g_execfw(void) +{ + int ret; + + /* + * 1. Reset the firmware microcontroller by writing the MicroReset CSR to set the + * StallToMicro and ResetToMicro fields to 1 (all other fields should be zero). + * Then rewrite the CSR so that only the StallToMicro remains set (all other fields should + * be zero). + */ + mmio_write_16((uintptr_t)(DDRPHYC_BASE + (4U * (TAPBONLY | CSR_MICROCONTMUXSEL_ADDR))), + CSR_STALLTOMICRO_MASK); + mmio_write_16((uintptr_t)(DDRPHYC_BASE + (4U * (TAPBONLY | CSR_MICRORESET_ADDR))), + CSR_RESETTOMICRO_MASK | CSR_STALLTOMICRO_MASK); + mmio_write_16((uintptr_t)(DDRPHYC_BASE + (4U * (TAPBONLY | CSR_MICRORESET_ADDR))), + CSR_STALLTOMICRO_MASK); + + /* 2. Begin execution of the training firmware by setting the MicroReset CSR to 0 */ + mmio_write_16((uintptr_t)(DDRPHYC_BASE + (4U * (TAPBONLY | CSR_MICRORESET_ADDR))), 0x0U); + + /* + * 3. Wait for the training firmware to complete by following the procedure + * implemented in ddrphy_phyinit_usercustom_g_waitfwdone() function. + */ + ret = ddrphy_phyinit_usercustom_g_waitfwdone(); + if (ret != 0) { + return ret; + } + + /* 4. Halt the microcontroller */ + mmio_write_16((uintptr_t)(DDRPHYC_BASE + (4U * (TAPBONLY | CSR_MICRORESET_ADDR))), + CSR_STALLTOMICRO_MASK); + + return 0; +} diff --git a/drivers/st/ddr/phy/phyinit/src/ddrphy_phyinit_i_loadpieimage.c b/drivers/st/ddr/phy/phyinit/src/ddrphy_phyinit_i_loadpieimage.c new file mode 100644 index 00000000..6ca0ddcd --- /dev/null +++ b/drivers/st/ddr/phy/phyinit/src/ddrphy_phyinit_i_loadpieimage.c @@ -0,0 +1,394 @@ +/* + * Copyright (C) 2021-2024, STMicroelectronics - All Rights Reserved + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <arch_helpers.h> + +#include <common/debug.h> + +#include <ddrphy_phyinit.h> + +#include <lib/mmio.h> + +#include <platform_def.h> + +#if STM32MP_LPDDR4_TYPE +/* + * Program DfiWrRdDataCsConfig + * - Fields: + * - dfiwrdatacspolarity + * - dfirddatacspolarity + */ +static void dfiwrrddatacsconfig_program(void) +{ + uint16_t dfiwrdatacspolarity; + uint16_t dfirddatacspolarity; + + /* + * DfiWrRdDataCsConfig : dfiwrdatacspolarity=0x1 and dfirddatacspolarity=0x1. + * Set DataCsPolarity bits to enable active high + */ + dfiwrdatacspolarity = 0x1U; + dfirddatacspolarity = 0x1U; + + mmio_write_16((uintptr_t)(DDRPHYC_BASE + (4U * (TMASTER | C0 | + CSR_DFIWRRDDATACSCONFIG_ADDR))), + (dfiwrdatacspolarity << CSR_DFIWRDATACSPOLARITY_LSB) | + (dfirddatacspolarity << CSR_DFIRDDATACSPOLARITY_LSB)); +} +#endif /* STM32MP_LPDDR4_TYPE */ + +/* + * Registers: Seq0BDLY0, Seq0BDLY1, Seq0BDLY2, Seq0BDLY3 + * - Program PIE instruction delays + * - Dependencies: + * - user_input_basic.frequency + */ +static void seq0bdly_program(struct stm32mp_ddr_config *config) +{ + uint16_t lowfreqopt __unused; + uint16_t dfifrq_x10; + uint16_t pscount_ref; + uint16_t pscount[4]; /* Need delays for 0.5us, 1us, 10us, and 25us */ + + /* + * Calculate the counts to obtain the correct delay for each frequency + * Need to divide by 4 since the delay value are specified in units of + * 4 clocks. + */ + dfifrq_x10 = (10U * (uint16_t)config->uib.frequency) / 2U; + pscount_ref = dfifrq_x10 / 4U; + pscount[0] = pscount_ref / (2U * 10U); + +#if STM32MP_DDR3_TYPE || STM32MP_DDR4_TYPE + if (config->uib.frequency < 400U) { + lowfreqopt = 3U; + } else if (config->uib.frequency < 533U) { + lowfreqopt = 11U; + } else { + lowfreqopt = 0U; + } + + pscount[1] = (pscount_ref / 10U) - lowfreqopt; +#else /* STM32MP_LPDDR4_TYPE */ + pscount[1] = pscount_ref / 10U; +#endif /* STM32MP_DDR3_TYPE || STM32MP_DDR4_TYPE */ + pscount[2] = pscount_ref; + + if (dfifrq_x10 > 2665U) { + pscount[3] = 44U; + } else if ((dfifrq_x10 <= 2665U) && (dfifrq_x10 > 2000U)) { + pscount[3] = 33U; + } else { + pscount[3] = 16U; + } + + mmio_write_16((uintptr_t)(DDRPHYC_BASE + (4U * (C0 | TMASTER | CSR_SEQ0BDLY0_ADDR))), + pscount[0]); + + mmio_write_16((uintptr_t)(DDRPHYC_BASE + (4U * (C0 | TMASTER | CSR_SEQ0BDLY1_ADDR))), + pscount[1]); + + mmio_write_16((uintptr_t)(DDRPHYC_BASE + (4U * (C0 | TMASTER | CSR_SEQ0BDLY2_ADDR))), + pscount[2]); + + mmio_write_16((uintptr_t)(DDRPHYC_BASE + (4U * (C0 | TMASTER | CSR_SEQ0BDLY3_ADDR))), + pscount[3]); +} + +/* + * Registers: Seq0BDisableFlag0..7 + * - Program PIE Instruction Disable Flags + * - Dependencies: + * - user_input_advanced.DisableRetraining (LPDDR4) + * - skip_training (LPDDR4) + * - user_input_basic.frequency (LPDDR4) + */ +static void seq0bdisableflag_program(struct stm32mp_ddr_config *config, bool skip_training) +{ + mmio_write_16((uintptr_t)(DDRPHYC_BASE + (4U * (TINITENG | CSR_SEQ0BDISABLEFLAG0_ADDR))), + 0x0000U); + mmio_write_16((uintptr_t)(DDRPHYC_BASE + (4U * (TINITENG | CSR_SEQ0BDISABLEFLAG1_ADDR))), + 0x0173U); + mmio_write_16((uintptr_t)(DDRPHYC_BASE + (4U * (TINITENG | CSR_SEQ0BDISABLEFLAG2_ADDR))), + 0x0060U); + mmio_write_16((uintptr_t)(DDRPHYC_BASE + (4U * (TINITENG | CSR_SEQ0BDISABLEFLAG3_ADDR))), + 0x6110U); + mmio_write_16((uintptr_t)(DDRPHYC_BASE + (4U * (TINITENG | CSR_SEQ0BDISABLEFLAG4_ADDR))), + 0x2152U); + mmio_write_16((uintptr_t)(DDRPHYC_BASE + (4U * (TINITENG | CSR_SEQ0BDISABLEFLAG5_ADDR))), + 0xDFBDU); + +#if STM32MP_DDR3_TYPE || STM32MP_DDR4_TYPE + mmio_write_16((uintptr_t)(DDRPHYC_BASE + (4U * (TINITENG | CSR_SEQ0BDISABLEFLAG6_ADDR))), + 0xFFFFU); +#else /* STM32MP_LPDDR4_TYPE */ + if (skip_training || (config->uia.disableretraining != 0U) || + (config->uib.frequency < 333U)) { + /* Disabling DRAM drift compensation */ + mmio_write_16((uintptr_t)(DDRPHYC_BASE + (4U * (TINITENG | + CSR_SEQ0BDISABLEFLAG6_ADDR))), + 0xFFFFU); + } else { + mmio_write_16((uintptr_t)(DDRPHYC_BASE + (4U * (TINITENG | + CSR_SEQ0BDISABLEFLAG6_ADDR))), + 0x2060U); + } +#endif /* STM32MP_DDR3_TYPE || STM32MP_DDR4_TYPE */ + + /* + * - Register: Seq0BGPR7 + * - Program active CSx for MRS7 during D4 RDIMM frequency change + */ + mmio_write_16((uintptr_t)(DDRPHYC_BASE + (4U * (TINITENG | CSR_SEQ0BDISABLEFLAG7_ADDR))), + 0x6152U); +} + +#if STM32MP_LPDDR4_TYPE +/* + * Registers: ppttrainsetup and ppttrainsetup2 + * - Related to DFI PHY Master Interface (PMI). + * - Enable DFI PMI if training firmware was run + * - Fields: + * - PhyMstrTrainInterval + * - PhyMstrMaxReqToAck + * - PhyMstrFreqOverride + * - Dependencies: + * - user_input_basic.frequency + * - user_input_advanced.PhyMstrTrainInterval + * - user_input_advanced.PhyMstrMaxReqToAck + */ +static void ppttrainsetup_program(struct stm32mp_ddr_config *config) +{ + uint16_t ppttrainsetup; + + /* Enabling Phy Master Interface for DRAM drift compensation */ + if (config->uib.frequency >= 333U) { + ppttrainsetup = (uint16_t)((config->uia.phymstrtraininterval << + CSR_PHYMSTRTRAININTERVAL_LSB) | + (config->uia.phymstrmaxreqtoack << + CSR_PHYMSTRMAXREQTOACK_LSB)); + + mmio_write_16((uintptr_t)(DDRPHYC_BASE + (4U * (TMASTER | CSR_PPTTRAINSETUP_ADDR))), + ppttrainsetup); + mmio_write_16((uintptr_t)(DDRPHYC_BASE + (4U * (TMASTER | + CSR_PPTTRAINSETUP2_ADDR))), + 0x0003U); + } +} + +/* + * Registers AcsmPlayback*x* + * - Program Address/Command Sequence Engine (ACSM) registers with + * required instructions for retraining algorithm. + */ +static void acsmplayback_program(void) +{ + uint32_t vec; + + for (vec = 0U; vec < 3U; vec++) { + mmio_write_16((uintptr_t)(DDRPHYC_BASE + (4U * (TACSM | (CSR_ACSMPLAYBACK0X0_ADDR + + (vec * 2U))))), + 0xE0U); + + mmio_write_16((uintptr_t)(DDRPHYC_BASE + (4U * (TACSM | (CSR_ACSMPLAYBACK1X0_ADDR + + (vec * 2U))))), + 0x12U); + } +} + +/* + * Program Training Hardware Registers for mission mode retraining + * and DRAM drift compensation algorithm. + */ +static void traininghwreg_program(struct stm32mp_ddr_config *config) +{ + uint32_t byte; + + /* Programing Training Hardware Registers for mission mode retraining */ + + /* + * - Register: AcsmCtrl13 + * - Fields: AcsmCkeEnb + */ + mmio_write_16((uintptr_t)(DDRPHYC_BASE + (4U * (C0 | TACSM | CSR_ACSMCTRL13_ADDR))), + 0xFU << CSR_ACSMCKEENB_LSB); + + /* + * - Register: AcsmCtrl1 + * - Fields: AcsmRepCnt + * Need 19 iterations @ 0.25ui increments to cover 4.5UI + */ + mmio_write_16((uintptr_t)(DDRPHYC_BASE + (4U * (C0 | TACSM | CSR_ACSMCTRL1_ADDR))), + 0xEU << CSR_ACSMREPCNT_LSB); + + /* + * - Register: TsmByte1, TsmByte2 + * - Dependencies: config->uib.numdbyte + */ + + for (byte = 0U; byte < config->uib.numdbyte; byte++) { + uint32_t c_addr; + uint32_t i_addr; + uint16_t regdata; + uint32_t vec; + + c_addr = byte * C1; + mmio_write_16((uintptr_t)(DDRPHYC_BASE + (4U * (c_addr | TDBYTE | + CSR_TSMBYTE1_ADDR))), + 0x1U); /* [15:8] gstep; [7:0]bstep; */ + mmio_write_16((uintptr_t)(DDRPHYC_BASE + (4U * (c_addr | TDBYTE | + CSR_TSMBYTE2_ADDR))), + 0x1U); /* [15:0] good_bar; */ + + regdata = (CSR_DTSMSTATICCMPR_MASK | CSR_DTSMSTATICCMPRVAL_MASK); + + /* + * - Register: TsmByte3, TsmByte5 + * - Fields: + * - DtsmStaticCmpr + * - DtsmStaticCmprVal + */ + mmio_write_16((uintptr_t)(DDRPHYC_BASE + (4U * (c_addr | TDBYTE | + CSR_TSMBYTE3_ADDR))), + regdata); + mmio_write_16((uintptr_t)(DDRPHYC_BASE + (4U * (c_addr | TDBYTE | + CSR_TSMBYTE5_ADDR))), + 0x1U); /* [15:0] bad_bar; */ + + /* + * - Register: TrainingParam + * - Fields: + * - EnDynRateReduction + * - RollIntoCoarse + * - IncDecRate + * - TrainEnRxEn + * - Dependencies: + * - user_input_advanced.DisableRetraining + */ + regdata = (CSR_ENDYNRATEREDUCTION_MASK | CSR_ROLLINTOCOARSE_MASK | + (0x3U << CSR_INCDECRATE_LSB)); + regdata = config->uia.disableretraining ? + regdata : (regdata | CSR_TRAINENRXEN_MASK); + mmio_write_16((uintptr_t)(DDRPHYC_BASE + (4U * (c_addr | TDBYTE | + CSR_TRAININGPARAM_ADDR))), + regdata); + + /* + * - Register: Tsm0 + * - Fields: + * - DtsmEnb + */ + regdata = (0x1U << CSR_DTSMENB_LSB); + mmio_write_16((uintptr_t)(DDRPHYC_BASE + (4U * (c_addr | I0 | TDBYTE | + CSR_TSM0_ADDR))), + regdata); + + /* + * - Register: Tsm2 + * - Fields: + * - DtsmDisErrChk + */ + regdata = (0x1U << CSR_DTSMDISERRCHK_LSB); + for (vec = 1U; vec <= I_MAX; vec++) { + i_addr = vec * I1; + mmio_write_16((uintptr_t)(DDRPHYC_BASE + (4U * (c_addr | i_addr | TDBYTE | + CSR_TSM2_ADDR))), + regdata); + } + } +} +#endif /* STM32MP_LPDDR4_TYPE */ + +/* + * - Register: calrate + * - Fields: + * - calOnce + * - calinterval + * - Dependencies + * - user_input_advanced.calinterval + * - user_input_advanced.calonce + */ +static void calrate_program(struct stm32mp_ddr_config *config) +{ + uint32_t calinterval; + uint32_t calonce; + uint16_t calrate; + + calinterval = config->uia.calinterval; + calonce = config->uia.calonce; + + calrate = (uint16_t)((0x1U << CSR_CALRUN_LSB) | (calonce << CSR_CALONCE_LSB) | + (calinterval << CSR_CALINTERVAL_LSB)); + + mmio_write_16((uintptr_t)(DDRPHYC_BASE + (4U * (TMASTER | CSR_CALRATE_ADDR))), calrate); +} + +/* + * Loads registers after training + * + * This function programs the PHY Initialization Engine (PIE) instructions and + * the associated registers. + * Training hardware registers are also programmed to for mission mode + * retraining. (LPDDR4) + * + * \return void + */ +void ddrphy_phyinit_i_loadpieimage(struct stm32mp_ddr_config *config, bool skip_training) +{ + /* + * Enable access to the internal CSRs by setting the MicroContMuxSel CSR to 0. + * This allows the memory controller unrestricted access to the configuration CSRs. + */ + mmio_write_16((uintptr_t)(DDRPHYC_BASE + (4U * (TAPBONLY | CSR_MICROCONTMUXSEL_ADDR))), + 0x0U); + + ddrphy_phyinit_loadpieprodcode(); + +#if STM32MP_LPDDR4_TYPE + /* + * No user specified EnableDfiCsPolarityFix, running with new PUB with DFI CS polarity fix + * so program the data polarity CSR. + */ + dfiwrrddatacsconfig_program(); +#endif /* STM32MP_LPDDR4_TYPE */ + + seq0bdly_program(config); + + seq0bdisableflag_program(config, skip_training); + +#if STM32MP_LPDDR4_TYPE + if (!skip_training) { + ppttrainsetup_program(config); + } + + acsmplayback_program(); + + traininghwreg_program(config); +#endif /* STM32MP_LPDDR4_TYPE */ + + /* + * - Register: CalZap + * - Prepare the calibration controller for mission mode. + * Turn on calibration and hold idle until dfi_init_start is asserted sequence is + * triggered. + */ + mmio_write_16((uintptr_t)(DDRPHYC_BASE + (4U * (TMASTER | CSR_CALZAP_ADDR))), 0x1U); + + calrate_program(config); + + /* + * At the end of this function, PHY Clk gating register UcclkHclkEnables is + * set for mission mode. Additionally APB access is Isolated by setting + * MicroContMuxSel. + */ + /* Disabling Ucclk (PMU) and Hclk (training hardware) */ + mmio_write_16((uintptr_t)(DDRPHYC_BASE + (4U * (TDRTUB | CSR_UCCLKHCLKENABLES_ADDR))), + 0x0U); + + /* Isolate the APB access from the internal CSRs by setting the MicroContMuxSel CSR to 1 */ + mmio_write_16((uintptr_t)(DDRPHYC_BASE + (4U * (TAPBONLY | CSR_MICROCONTMUXSEL_ADDR))), + 0x1U); +} diff --git a/drivers/st/ddr/phy/phyinit/src/ddrphy_phyinit_initstruct.c b/drivers/st/ddr/phy/phyinit/src/ddrphy_phyinit_initstruct.c new file mode 100644 index 00000000..50a88be4 --- /dev/null +++ b/drivers/st/ddr/phy/phyinit/src/ddrphy_phyinit_initstruct.c @@ -0,0 +1,262 @@ +/* + * Copyright (C) 2021-2024, STMicroelectronics - All Rights Reserved + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <string.h> + +#include <common/debug.h> + +#include <ddrphy_phyinit.h> + +/* + * This is used to initialize the PhyInit structures before user defaults and overrides are applied. + * + * @return Void + */ +void ddrphy_phyinit_initstruct(struct stm32mp_ddr_config *config, struct pmu_smb_ddr_1d *mb_ddr_1d) +{ + /* + * ############################################################## + * Basic Message Block Variables + * ############################################################## + */ + + uint8_t msgmisc = 0x00U; /* For fast simulation */ + uint8_t reserved00 = 0x0U; /* + * Set reserved00[7] = 1 (If using T28 attenuated receivers) + * Set reserved00[6:0] = 0 (Reserved; must be set to 0) + */ + + uint8_t hdtctrl = 0xFFU; +#if STM32MP_DDR3_TYPE || STM32MP_DDR4_TYPE + uint8_t cspresent = 0x01U; /* + * Indicates presence of DRAM at each chip select for PHY. + * + * If the bit is set to 1, the CS is connected to DRAM. + * If the bit is set to 0, the CS is not connected to DRAM. + * + * Set cspresent[0] = 1 (if CS0 is populated with DRAM) + * Set cspresent[1] = 1 (if CS1 is populated with DRAM) + * Set cspresent[2] = 1 (if CS2 is populated with DRAM) + * Set cspresent[3] = 1 (if CS3 is populated with DRAM) + * Set cspresent[7:4] = 0 (Reserved; must be set to 0) + */ + uint8_t dfimrlmargin = 0x01U; /* 1 is typically good in DDR3 */ +#if STM32MP_DDR3_TYPE + uint8_t addrmirror = 0x00U; /* + * Set addrmirror if CS is mirrored. + * (typically odd CS are mirroed in DIMMs) + */ +#else /* STM32MP_DDR4_TYPE */ + uint8_t addrmirror = 0xAAU; +#endif /* STM32MP_DDR3_TYPE */ + uint8_t wrodtpat_rank0 = 0x01U; /* + * When Writing Rank0 : Bits[3:0] should be set to the + * desired setting of ODT[3:0] to the DRAM + */ + uint8_t wrodtpat_rank1 = 0x02U; /* + * When Writing Rank1 : Bits[3:0] should be set to the + * desired setting of ODT[3:0] to the DRAM + */ +#if STM32MP_DDR3_TYPE + uint8_t wrodtpat_rank2 = 0x04U; /* + * When Writing Rank2 : Bits[3:0] should be set to the + * desired setting of ODT[3:0] to the DRAM + */ + uint8_t wrodtpat_rank3 = 0x08U; /* + * When Writing Rank3 : Bits[3:0] should be set to the + * desired setting of ODT[3:0] to the DRAM + */ +#else /* STM32MP_DDR4_TYPE */ + uint8_t wrodtpat_rank2 = 0x00U; + uint8_t wrodtpat_rank3 = 0x00U; +#endif /* STM32MP_DDR3_TYPE */ + uint8_t rdodtpat_rank0 = 0x20U; /* + * When Reading Rank0 : Bits[7:4] should be set to the + * desired setting of ODT[3:0] to the DRAM + */ + uint8_t rdodtpat_rank1 = 0x10U; /* + * When Reading Rank1 : Bits[7:4] should be set to the + * desired setting of ODT[3:0] to the DRAM + */ +#if STM32MP_DDR3_TYPE + uint8_t rdodtpat_rank2 = 0x80U; /* + * When Reading Rank2 : Bits[7:4] should be set to the + * desired setting of ODT[3:0] to the DRAM + */ + uint8_t rdodtpat_rank3 = 0x40U; /* + * When Reading Rank3 : Bits[7:4] should be set to the + * desired setting of ODT[3:0] to the DRAM + */ +#else /* STM32MP_DDR4_TYPE */ + uint8_t rdodtpat_rank2 = 0x00U; + uint8_t rdodtpat_rank3 = 0x00U; + + uint8_t d4misc = 0x1U; /* + * Protect memory reset: + * 0x1 = dfi_reset_n cannot control BP_MEMRESERT_L to + * devices after training. + * 0x0 = dfi_resert_n can control BP_MEMRESERT_L to + * devices after training. + */ +#endif /* STM32MP_DDR3_TYPE */ +#else /* STM32MP_LPDDR4_TYPE */ + uint8_t caterminatingrankcha = 0x00U; /* Usually Rank0 is terminating rank */ + uint8_t caterminatingrankchb = 0x00U; /* Usually Rank0 is terminating rank */ + uint8_t dfimrlmargin = 0x02U; /* This needs to be large enough for max tDQSCK variation */ +#endif /* STM32MP_DDR3_TYPE || STM32MP_DDR4_TYPE */ + +#if STM32MP_DDR3_TYPE + uint8_t share2dvrefresult = 0x0U; /* + * Bitmap that controls which vref generator the + * phy will use per pstate + * If share2dvrefresult[x] = 1, pstate x will + * use the per-lane VrefDAC0/1 CSRs which can be + * trained by 2d training. If 2D has not run + * yet, VrefDAC0/1 will default to pstate 0's + * 1D phyVref messageBlock setting. + * If share2dvrefresult[x] = 0, pstate x will + * use the per-phy VrefInGlobal CSR, which are + * set to pstate x's 1D phyVref messageBlock + * setting. + */ +#elif STM32MP_DDR4_TYPE + uint8_t share2dvrefresult = 0x1U; +#else /* STM32MP_LPDDR4_TYPE */ + uint8_t share2dvrefresult = 0x1U; + uint8_t usebroadcastmr = 0x00U; +#endif /* STM32MP_DDR3_TYPE */ + + /* 1D message block defaults */ + memset((void *)mb_ddr_1d, 0, sizeof(struct pmu_smb_ddr_1d)); + + mb_ddr_1d->pstate = 0U; + mb_ddr_1d->sequencectrl = (uint16_t)config->uia.sequencectrl; + mb_ddr_1d->phyconfigoverride = 0x0U; + mb_ddr_1d->hdtctrl = hdtctrl; + mb_ddr_1d->msgmisc = msgmisc; + mb_ddr_1d->reserved00 = reserved00; + mb_ddr_1d->dfimrlmargin = dfimrlmargin; + mb_ddr_1d->phyvref = (uint8_t)config->uia.phyvref; + +#if STM32MP_DDR3_TYPE || STM32MP_DDR4_TYPE + mb_ddr_1d->cspresent = cspresent; + mb_ddr_1d->cspresentd0 = cspresent; + /* mb_ddr_1d->cspresentd1 = 0x0U; Unused */ + mb_ddr_1d->addrmirror = addrmirror; + + mb_ddr_1d->acsmodtctrl0 = wrodtpat_rank0 | rdodtpat_rank0; + mb_ddr_1d->acsmodtctrl1 = wrodtpat_rank1 | rdodtpat_rank1; + mb_ddr_1d->acsmodtctrl2 = wrodtpat_rank2 | rdodtpat_rank2; + mb_ddr_1d->acsmodtctrl3 = wrodtpat_rank3 | rdodtpat_rank3; + + /* mb_ddr_1d->acsmodtctrl4 = 0x0U; Unused */ + /* mb_ddr_1d->acsmodtctrl5 = 0x0U; Unused */ + /* mb_ddr_1d->acsmodtctrl6 = 0x0U; Unused */ + /* mb_ddr_1d->acsmodtctrl7 = 0x0U; Unused */ + mb_ddr_1d->enableddqs = (uint8_t)((config->uib.numactivedbytedfi0 + + config->uib.numactivedbytedfi1) * 8U); +#if STM32MP_DDR3_TYPE + mb_ddr_1d->phycfg = (uint8_t)config->uia.is2ttiming; +#else /* STM32MP_DDR4_TYPE */ + mb_ddr_1d->phycfg = ((config->uim.mr3 & 0x8U) == 0x8U) ? + 0U : (uint8_t)config->uia.is2ttiming; + mb_ddr_1d->x16present = (config->uib.dramdatawidth == 0x10) ? + mb_ddr_1d->cspresent : 0x0U; + mb_ddr_1d->d4misc = d4misc; + mb_ddr_1d->cssetupgddec = 0x1U; /* If Geardown is chosen, dynamically modify CS timing */ + + /* + * Outputs - just initialize these to zero + * mb_ddr_1d->rtt_nom_wr_park<0..7> + */ +#endif /* STM32MP_DDR3_TYPE */ + + mb_ddr_1d->mr0 = (uint16_t)config->uim.mr0; + mb_ddr_1d->mr1 = (uint16_t)config->uim.mr1; + mb_ddr_1d->mr2 = (uint16_t)config->uim.mr2; +#if STM32MP_DDR4_TYPE + mb_ddr_1d->mr3 = (uint16_t)config->uim.mr3; + mb_ddr_1d->mr4 = (uint16_t)config->uim.mr4; + mb_ddr_1d->mr5 = (uint16_t)config->uim.mr5; + mb_ddr_1d->mr6 = (uint16_t)config->uim.mr6; + + mb_ddr_1d->alt_cas_l = 0x0U; + mb_ddr_1d->alt_wcas_l = 0x0U; + + /* + * Outputs - just initialize these to zero + * mb_ddr_1d->vrefdqr<0..3>nib<0..19> + */ +#endif /* STM32MP_DDR4_TYPE */ +#else /* STM32MP_LPDDR4_TYPE */ + mb_ddr_1d->enableddqscha = (uint8_t)(config->uib.numactivedbytedfi0 * 8U); + mb_ddr_1d->cspresentcha = (config->uib.numrank_dfi0 == 2U) ? + 0x3U : (uint8_t)config->uib.numrank_dfi0; + mb_ddr_1d->enableddqschb = (uint8_t)(config->uib.numactivedbytedfi1 * 8U); + mb_ddr_1d->cspresentchb = (config->uib.numrank_dfi1 == 2U) ? + 0x3U : (uint8_t)config->uib.numrank_dfi1; + mb_ddr_1d->usebroadcastmr = usebroadcastmr; + + mb_ddr_1d->lp4misc = 0x00U; + mb_ddr_1d->caterminatingrankcha = caterminatingrankcha; + mb_ddr_1d->caterminatingrankchb = caterminatingrankchb; + mb_ddr_1d->lp4quickboot = 0x00U; + mb_ddr_1d->catrainopt = 0x00U; + mb_ddr_1d->x8mode = 0x00U; + + mb_ddr_1d->mr1_a0 = (uint8_t)config->uim.mr1; + mb_ddr_1d->mr2_a0 = (uint8_t)config->uim.mr2; + mb_ddr_1d->mr3_a0 = (uint8_t)config->uim.mr3; + mb_ddr_1d->mr4_a0 = (uint8_t)config->uim.mr4; + mb_ddr_1d->mr11_a0 = (uint8_t)config->uim.mr11; + mb_ddr_1d->mr12_a0 = (uint8_t)config->uim.mr12; + mb_ddr_1d->mr13_a0 = (uint8_t)config->uim.mr13; + mb_ddr_1d->mr14_a0 = (uint8_t)config->uim.mr14; + mb_ddr_1d->mr16_a0 = 0x00U; + mb_ddr_1d->mr17_a0 = 0x00U; + mb_ddr_1d->mr22_a0 = (uint8_t)config->uim.mr22; + mb_ddr_1d->mr24_a0 = 0x00U; + mb_ddr_1d->mr1_a1 = (uint8_t)config->uim.mr1; + mb_ddr_1d->mr2_a1 = (uint8_t)config->uim.mr2; + mb_ddr_1d->mr3_a1 = (uint8_t)config->uim.mr3; + mb_ddr_1d->mr4_a1 = (uint8_t)config->uim.mr4; + mb_ddr_1d->mr11_a1 = (uint8_t)config->uim.mr11; + mb_ddr_1d->mr12_a1 = (uint8_t)config->uim.mr12; + mb_ddr_1d->mr13_a1 = (uint8_t)config->uim.mr13; + mb_ddr_1d->mr14_a1 = (uint8_t)config->uim.mr14; + mb_ddr_1d->mr16_a1 = 0x00U; + mb_ddr_1d->mr17_a1 = 0x00U; + mb_ddr_1d->mr22_a1 = (uint8_t)config->uim.mr22; + mb_ddr_1d->mr24_a1 = 0x00U; + + mb_ddr_1d->mr1_b0 = (uint8_t)config->uim.mr1; + mb_ddr_1d->mr2_b0 = (uint8_t)config->uim.mr2; + mb_ddr_1d->mr3_b0 = (uint8_t)config->uim.mr3; + mb_ddr_1d->mr4_b0 = (uint8_t)config->uim.mr4; + mb_ddr_1d->mr11_b0 = (uint8_t)config->uim.mr11; + mb_ddr_1d->mr12_b0 = (uint8_t)config->uim.mr12; + mb_ddr_1d->mr13_b0 = (uint8_t)config->uim.mr13; + mb_ddr_1d->mr14_b0 = (uint8_t)config->uim.mr14; + mb_ddr_1d->mr16_b0 = 0x00U; + mb_ddr_1d->mr17_b0 = 0x00U; + mb_ddr_1d->mr22_b0 = (uint8_t)config->uim.mr22; + mb_ddr_1d->mr24_b0 = 0x00U; + mb_ddr_1d->mr1_b1 = (uint8_t)config->uim.mr1; + mb_ddr_1d->mr2_b1 = (uint8_t)config->uim.mr2; + mb_ddr_1d->mr3_b1 = (uint8_t)config->uim.mr3; + mb_ddr_1d->mr4_b1 = (uint8_t)config->uim.mr4; + mb_ddr_1d->mr11_b1 = (uint8_t)config->uim.mr11; + mb_ddr_1d->mr12_b1 = (uint8_t)config->uim.mr12; + mb_ddr_1d->mr13_b1 = (uint8_t)config->uim.mr13; + mb_ddr_1d->mr14_b1 = (uint8_t)config->uim.mr14; + mb_ddr_1d->mr16_b1 = 0x00U; + mb_ddr_1d->mr17_b1 = 0x00U; + mb_ddr_1d->mr22_b1 = (uint8_t)config->uim.mr22; + mb_ddr_1d->mr24_b1 = 0x00U; +#endif /* STM32MP_DDR3_TYPE || STM32MP_DDR4_TYPE */ + + mb_ddr_1d->share2dvrefresult = share2dvrefresult; +} diff --git a/drivers/st/ddr/phy/phyinit/src/ddrphy_phyinit_isdbytedisabled.c b/drivers/st/ddr/phy/phyinit/src/ddrphy_phyinit_isdbytedisabled.c new file mode 100644 index 00000000..4daf2bb0 --- /dev/null +++ b/drivers/st/ddr/phy/phyinit/src/ddrphy_phyinit_isdbytedisabled.c @@ -0,0 +1,88 @@ +/* + * Copyright (C) 2021-2024, STMicroelectronics - All Rights Reserved + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <stdlib.h> + +#include <common/debug.h> + +#include <ddrphy_phyinit.h> +#include <ddrphy_wrapper.h> + +/* + * Helper function to determine if a given DByte is Disabled given PhyInit inputs. + * @return 1 if disabled, 0 if enabled. + */ +int ddrphy_phyinit_isdbytedisabled(struct stm32mp_ddr_config *config, + struct pmu_smb_ddr_1d *mb_ddr_1d, uint32_t dbytenumber) +{ + int disabledbyte; + uint32_t nad0 __maybe_unused; + uint32_t nad1 __maybe_unused; + + disabledbyte = 0; /* Default assume Dbyte is Enabled */ + +#if STM32MP_DDR3_TYPE || STM32MP_DDR4_TYPE + disabledbyte = (dbytenumber > (config->uib.numactivedbytedfi0 - 1U)) ? 1 : 0; +#else /* STM32MP_LPDDR4_TYPE */ + nad0 = config->uib.numactivedbytedfi0; + nad1 = config->uib.numactivedbytedfi1; + + if ((nad0 + nad1) > config->uib.numdbyte) { + ERROR("%s %d\n", __func__, __LINE__); + VERBOSE("%s invalid PHY configuration:\n", __func__); + VERBOSE("numactivedbytedfi0(%u)+numactivedbytedfi1(%u)>numdbytes(%u).\n", + nad0, nad1, config->uib.numdbyte); + } + + if (config->uib.dfi1exists != 0U) { + if (config->uib.numactivedbytedfi1 == 0U) { + /* Only dfi0 (ChA) is enabled, dfi1 (ChB) disabled */ + disabledbyte = (dbytenumber > (config->uib.numactivedbytedfi0 - 1U)) ? + 1 : 0; + } else { + /* DFI1 enabled */ + disabledbyte = (((config->uib.numactivedbytedfi0 - 1U) < dbytenumber) && + (dbytenumber < (config->uib.numdbyte / 2U))) ? + 1 : (dbytenumber > + ((config->uib.numdbyte / 2U) + + config->uib.numactivedbytedfi1 - 1U)) ? 1 : 0; + } + } else { + disabledbyte = (dbytenumber > (config->uib.numactivedbytedfi0 - 1U)) ? 1 : 0; + } +#endif /* STM32MP_DDR3_TYPE || STM32MP_DDR4_TYPE */ + + /* Qualify results against MessageBlock */ +#if STM32MP_DDR3_TYPE || STM32MP_DDR4_TYPE + if ((mb_ddr_1d->enableddqs < 1U) || + (mb_ddr_1d->enableddqs > (uint8_t)(8U * config->uib.numactivedbytedfi0))) { + ERROR("%s %d\n", __func__, __LINE__); + VERBOSE("%s enableddqs(%u)\n", __func__, mb_ddr_1d->enableddqs); + VERBOSE("Value must be 0 < enableddqs < config->uib.numactivedbytedfi0 * 8.\n"); + } + + if (dbytenumber < 8) { + disabledbyte |= (int)mb_ddr_1d->disableddbyte & (0x1 << dbytenumber); + } +#else /* STM32MP_LPDDR4_TYPE */ + if ((mb_ddr_1d->enableddqscha < 1U) || + (mb_ddr_1d->enableddqscha > (uint8_t)(8U * config->uib.numactivedbytedfi0))) { + ERROR("%s %d\n", __func__, __LINE__); + VERBOSE("%s enableddqscha(%u)\n", __func__, mb_ddr_1d->enableddqscha); + VERBOSE("Value must be 0 < enableddqscha < config->uib.numactivedbytedfi0*8\n"); + } + + if ((config->uib.dfi1exists != 0U) && (config->uib.numactivedbytedfi1 > 0U) && + ((mb_ddr_1d->enableddqschb < 1U) || + (mb_ddr_1d->enableddqschb > (uint8_t)(8U * config->uib.numactivedbytedfi1)))) { + ERROR("%s %d\n", __func__, __LINE__); + VERBOSE("%s enableddqschb(%u)\n", __func__, mb_ddr_1d->enableddqschb); + VERBOSE("Value must be 0 < enableddqschb < config->uib.numactivedbytedfi1*8\n"); + } +#endif /* STM32MP_DDR3_TYPE || STM32MP_DDR4_TYPE */ + + return disabledbyte; +} diff --git a/drivers/st/ddr/phy/phyinit/src/ddrphy_phyinit_loadpieprodcode.c b/drivers/st/ddr/phy/phyinit/src/ddrphy_phyinit_loadpieprodcode.c new file mode 100644 index 00000000..2843b10e --- /dev/null +++ b/drivers/st/ddr/phy/phyinit/src/ddrphy_phyinit_loadpieprodcode.c @@ -0,0 +1,189 @@ +/* + * Copyright (C) 2021-2024, STMicroelectronics - All Rights Reserved + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <stdint.h> + +#include <common/debug.h> + +#include <ddrphy_phyinit.h> + +#include <lib/mmio.h> + +#include <platform_def.h> + +#if STM32MP_DDR3_TYPE || STM32MP_DDR4_TYPE +#define PRODCODE_SIZE 177 + +static const uint32_t prodcode_addr[PRODCODE_SIZE] = { + 0x90000U, 0x90001U, 0x90002U, 0x90003U, 0x90004U, 0x90005U, 0x90029U, 0x9002AU, 0x9002BU, + 0x9002CU, 0x9002DU, 0x9002EU, 0x9002FU, 0x90030U, 0x90031U, 0x90032U, 0x90033U, 0x90034U, + 0x90035U, 0x90036U, 0x90037U, 0x90038U, 0x90039U, 0x9003AU, 0x9003BU, 0x9003CU, 0x9003DU, + 0x9003EU, 0x9003FU, 0x90040U, 0x90041U, 0x90042U, 0x90043U, 0x90044U, 0x90045U, 0x90046U, + 0x90047U, 0x90048U, 0x90049U, 0x9004AU, 0x9004BU, 0x9004CU, 0x9004DU, 0x9004EU, 0x9004FU, + 0x90050U, 0x90051U, 0x90052U, 0x90053U, 0x90054U, 0x90055U, 0x90056U, 0x90057U, 0x90058U, + 0x90059U, 0x9005AU, 0x9005BU, 0x9005CU, 0x9005DU, 0x9005EU, 0x9005FU, 0x90060U, 0x90061U, + 0x90062U, 0x90063U, 0x90064U, 0x90065U, 0x90066U, 0x90067U, 0x90068U, 0x90069U, 0x9006AU, + 0x9006BU, 0x9006CU, 0x9006DU, 0x9006EU, 0x9006FU, 0x90070U, 0x90071U, 0x90072U, 0x90073U, + 0x90074U, 0x90075U, 0x90076U, 0x90077U, 0x90078U, 0x90079U, 0x9007AU, 0x9007BU, 0x9007CU, + 0x9007DU, 0x9007EU, 0x9007FU, 0x90080U, 0x90081U, 0x90082U, 0x90083U, 0x90084U, 0x90085U, + 0x90086U, 0x90087U, 0x90088U, 0x90089U, 0x9008AU, 0x9008BU, 0x9008CU, 0x9008DU, 0x9008EU, + 0x9008FU, 0x90090U, 0x90091U, 0x90092U, 0x90093U, 0x90094U, 0x90095U, 0x90096U, 0x90097U, + 0x90098U, 0x90099U, 0x9009AU, 0x9009BU, 0x9009CU, 0x9009DU, 0x9009EU, 0x9009FU, 0x900A0U, + 0x900A1U, 0x900A2U, 0x900A3U, 0x900A4U, 0x900A5U, 0x900A6U, 0x900A7U, 0x900A8U, 0x900A9U, + 0x900AAU, 0x900ABU, 0x900ACU, 0x900ADU, 0x900AEU, 0x900AFU, 0x900B0U, 0x900B1U, 0x900B2U, + 0x900B3U, 0x900B4U, 0x900B5U, 0x900B6U, 0x900B7U, 0x900B8U, 0x900B9U, 0x900BAU, 0x900BBU, + 0x900BCU, 0x900BDU, 0x900BEU, 0x900BFU, 0x900C0U, 0x900C1U, 0x900C2U, 0x900C3U, 0x900C4U, + 0x900C5U, 0x900C6U, 0x900C7U, 0x900C8U, 0x900C9U, 0x900CAU, 0x90006U, 0x90007U, 0x90008U, + 0x90009U, 0x9000AU, 0x9000BU, 0xD00E7U, 0x90017U, 0x90026U, +}; + +static const uint16_t prodcode_data[PRODCODE_SIZE] = { + 0x0010U, 0x0400U, 0x010EU, 0x0000U, 0x0000U, 0x0008U, 0x000BU, 0x0480U, 0x0109U, 0x0008U, + 0x0448U, 0x0139U, 0x0008U, 0x0478U, 0x0109U, 0x0002U, 0x0010U, 0x0139U, 0x000BU, 0x07C0U, + 0x0139U, 0x0044U, 0x0633U, 0x0159U, 0x014FU, 0x0630U, 0x0159U, 0x0047U, 0x0633U, 0x0149U, + 0x004FU, 0x0633U, 0x0179U, 0x0008U, 0x00E0U, 0x0109U, 0x0000U, 0x07C8U, 0x0109U, 0x0000U, + 0x0001U, 0x0008U, 0x0030U, 0x065AU, 0x0009U, 0x0000U, 0x045AU, 0x0009U, 0x0000U, 0x0448U, + 0x0109U, 0x0040U, 0x0633U, 0x0179U, 0x0001U, 0x0618U, 0x0109U, 0x40C0U, 0x0633U, 0x0149U, + 0x0008U, 0x0004U, 0x0048U, 0x4040U, 0x0633U, 0x0149U, 0x0000U, 0x0004U, 0x0048U, 0x0040U, + 0x0633U, 0x0149U, 0x0000U, 0x0658U, 0x0109U, 0x0010U, 0x0004U, 0x0018U, 0x0000U, 0x0004U, + 0x0078U, 0x0549U, 0x0633U, 0x0159U, 0x0D49U, 0x0633U, 0x0159U, 0x094AU, 0x0633U, 0x0159U, + 0x0441U, 0x0633U, 0x0149U, 0x0042U, 0x0633U, 0x0149U, 0x0001U, 0x0633U, 0x0149U, 0x0000U, + 0x00E0U, 0x0109U, 0x000AU, 0x0010U, 0x0109U, 0x0009U, 0x03C0U, 0x0149U, 0x0009U, 0x03C0U, + 0x0159U, 0x0018U, 0x0010U, 0x0109U, 0x0000U, 0x03C0U, 0x0109U, 0x0018U, 0x0004U, 0x0048U, + 0x0018U, 0x0004U, 0x0058U, 0x000BU, 0x0010U, 0x0109U, 0x0001U, 0x0010U, 0x0109U, 0x0005U, + 0x07C0U, 0x0109U, 0x0000U, 0x8140U, 0x010CU, 0x0010U, 0x8138U, 0x0104U, 0x0008U, 0x0448U, + 0x0109U, 0x000FU, 0x07C0U, 0x0109U, 0x0047U, 0x0630U, 0x0109U, 0x0008U, 0x0618U, 0x0109U, + 0x0008U, 0x00E0U, 0x0109U, 0x0000U, 0x07C8U, 0x0109U, 0x0008U, 0x8140U, 0x010CU, 0x0000U, + 0x0478U, 0x0109U, 0x0000U, 0x0001U, 0x0008U, 0x0008U, 0x0004U, 0x0000U, 0x0008U, 0x07C8U, + 0x0109U, 0x0000U, 0x0400U, 0x0106U, 0x0400U, 0x0000U, 0x002CU, + }; + +#else /* STM32MP_LPDDR4_TYPE */ +#define PRODCODE_SIZE 481 + +static const uint32_t prodcode_addr[PRODCODE_SIZE] = { + 0x90000U, 0x90001U, 0x90002U, 0x90003U, 0x90004U, 0x90005U, 0x90029U, 0x9002AU, 0x9002BU, + 0x9002CU, 0x9002DU, 0x9002EU, 0x9002FU, 0x90030U, 0x90031U, 0x90032U, 0x90033U, 0x90034U, + 0x90035U, 0x90036U, 0x90037U, 0x90038U, 0x90039U, 0x9003AU, 0x9003BU, 0x9003CU, 0x9003DU, + 0x9003EU, 0x9003FU, 0x90040U, 0x90041U, 0x90042U, 0x90043U, 0x90044U, 0x90045U, 0x90046U, + 0x90047U, 0x90048U, 0x90049U, 0x9004AU, 0x9004BU, 0x9004CU, 0x9004DU, 0x9004EU, 0x9004FU, + 0x90050U, 0x90051U, 0x90052U, 0x90053U, 0x90054U, 0x90055U, 0x90056U, 0x90057U, 0x90058U, + 0x90059U, 0x9005AU, 0x9005BU, 0x9005CU, 0x9005DU, 0x9005EU, 0x9005FU, 0x90060U, 0x90061U, + 0x90062U, 0x90063U, 0x90064U, 0x90065U, 0x90066U, 0x90067U, 0x90068U, 0x90069U, 0x9006AU, + 0x9006BU, 0x9006CU, 0x9006DU, 0x9006EU, 0x9006FU, 0x90070U, 0x90071U, 0x90072U, 0x90073U, + 0x90074U, 0x90075U, 0x90076U, 0x90077U, 0x90078U, 0x90079U, 0x9007AU, 0x9007BU, 0x9007CU, + 0x9007DU, 0x9007EU, 0x9007FU, 0x90080U, 0x90081U, 0x90082U, 0x90083U, 0x90084U, 0x90085U, + 0x90086U, 0x90087U, 0x90088U, 0x90089U, 0x9008AU, 0x9008BU, 0x9008CU, 0x9008DU, 0x9008EU, + 0x9008FU, 0x90090U, 0x90091U, 0x90092U, 0x90093U, 0x90094U, 0x90095U, 0x90096U, 0x90097U, + 0x90098U, 0x90099U, 0x9009AU, 0x9009BU, 0x9009CU, 0x9009DU, 0x9009EU, 0x9009FU, 0x900A0U, + 0x900A1U, 0x900A2U, 0x900A3U, 0x900A4U, 0x900A5U, 0x900A6U, 0x900A7U, 0x900A8U, 0x900A9U, + 0x40000U, 0x40020U, 0x40040U, 0x40060U, 0x40001U, 0x40021U, 0x40041U, 0x40061U, 0x40002U, + 0x40022U, 0x40042U, 0x40062U, 0x40003U, 0x40023U, 0x40043U, 0x40063U, 0x40004U, 0x40024U, + 0x40044U, 0x40064U, 0x40005U, 0x40025U, 0x40045U, 0x40065U, 0x40006U, 0x40026U, 0x40046U, + 0x40066U, 0x40007U, 0x40027U, 0x40047U, 0x40067U, 0x40008U, 0x40028U, 0x40048U, 0x40068U, + 0x40009U, 0x40029U, 0x40049U, 0x40069U, 0x4000AU, 0x4002AU, 0x4004AU, 0x4006AU, 0x4000BU, + 0x4002BU, 0x4004BU, 0x4006BU, 0x4000CU, 0x4002CU, 0x4004CU, 0x4006CU, 0x4000DU, 0x4002DU, + 0x4004DU, 0x4006DU, 0x4000EU, 0x4002EU, 0x4004EU, 0x4006EU, 0x4000FU, 0x4002FU, 0x4004FU, + 0x4006FU, 0x40010U, 0x40030U, 0x40050U, 0x40070U, 0x40011U, 0x40031U, 0x40051U, 0x40071U, + 0x40012U, 0x40032U, 0x40052U, 0x40072U, 0x40013U, 0x40033U, 0x40053U, 0x40073U, 0x40014U, + 0x40034U, 0x40054U, 0x40074U, 0x40015U, 0x40035U, 0x40055U, 0x40075U, 0x40016U, 0x40036U, + 0x40056U, 0x40076U, 0x40017U, 0x40037U, 0x40057U, 0x40077U, 0x40018U, 0x40038U, 0x40058U, + 0x40078U, 0x40019U, 0x40039U, 0x40059U, 0x40079U, 0x4001AU, 0x4003AU, 0x4005AU, 0x4007AU, + 0x900AAU, 0x900ABU, 0x900ACU, 0x900ADU, 0x900AEU, 0x900AFU, 0x900B0U, 0x900B1U, 0x900B2U, + 0x900B3U, 0x900B4U, 0x900B5U, 0x900B6U, 0x900B7U, 0x900B8U, 0x900B9U, 0x900BAU, 0x900BBU, + 0x900BCU, 0x900BDU, 0x900BEU, 0x900BFU, 0x900C0U, 0x900C1U, 0x900C2U, 0x900C3U, 0x900C4U, + 0x900C5U, 0x900C6U, 0x900C7U, 0x900C8U, 0x900C9U, 0x900CAU, 0x900CBU, 0x900CCU, 0x900CDU, + 0x900CEU, 0x900CFU, 0x900D0U, 0x900D1U, 0x900D2U, 0x900D3U, 0x900D4U, 0x900D5U, 0x900D6U, + 0x900D7U, 0x900D8U, 0x900D9U, 0x900DAU, 0x900DBU, 0x900DCU, 0x900DDU, 0x900DEU, 0x900DFU, + 0x900E0U, 0x900E1U, 0x900E2U, 0x900E3U, 0x900E4U, 0x900E5U, 0x900E6U, 0x900E7U, 0x900E8U, + 0x900E9U, 0x900EAU, 0x900EBU, 0x900ECU, 0x900EDU, 0x900EEU, 0x900EFU, 0x900F0U, 0x900F1U, + 0x900F2U, 0x900F3U, 0x900F4U, 0x900F5U, 0x900F6U, 0x900F7U, 0x900F8U, 0x900F9U, 0x900FAU, + 0x900FBU, 0x900FCU, 0x900FDU, 0x900FEU, 0x900FFU, 0x90100U, 0x90101U, 0x90102U, 0x90103U, + 0x90104U, 0x90105U, 0x90106U, 0x90107U, 0x90108U, 0x90109U, 0x9010AU, 0x9010BU, 0x9010CU, + 0x9010DU, 0x9010EU, 0x9010FU, 0x90110U, 0x90111U, 0x90112U, 0x90113U, 0x90114U, 0x90115U, + 0x90116U, 0x90117U, 0x90118U, 0x90119U, 0x9011AU, 0x9011BU, 0x9011CU, 0x9011DU, 0x9011EU, + 0x9011FU, 0x90120U, 0x90121U, 0x90122U, 0x90123U, 0x90124U, 0x90125U, 0x90126U, 0x90127U, + 0x90128U, 0x90129U, 0x9012AU, 0x9012BU, 0x9012CU, 0x9012DU, 0x9012EU, 0x9012FU, 0x90130U, + 0x90131U, 0x90132U, 0x90133U, 0x90134U, 0x90135U, 0x90136U, 0x90137U, 0x90138U, 0x90139U, + 0x9013AU, 0x9013BU, 0x9013CU, 0x9013DU, 0x9013EU, 0x9013FU, 0x90140U, 0x90141U, 0x90142U, + 0x90143U, 0x90144U, 0x90145U, 0x90146U, 0x90147U, 0x90148U, 0x90149U, 0x9014AU, 0x9014BU, + 0x9014CU, 0x9014DU, 0x9014EU, 0x9014FU, 0x90150U, 0x90151U, 0x90152U, 0x90153U, 0x90154U, + 0x90155U, 0x90156U, 0x90157U, 0x90158U, 0x90159U, 0x9015AU, 0x9015BU, 0x9015CU, 0x9015DU, + 0x9015EU, 0x9015FU, 0x90160U, 0x90161U, 0x90162U, 0x90163U, 0x90164U, 0x90165U, 0x90166U, + 0x90167U, 0x90168U, 0x90169U, 0x9016AU, 0x9016BU, 0x9016CU, 0x9016DU, 0x9016EU, 0x9016FU, + 0x90170U, 0x90171U, 0x90172U, 0x90173U, 0x90174U, 0x90175U, 0x90176U, 0x90177U, 0x90178U, + 0x90179U, 0x9017AU, 0x9017BU, 0x9017CU, 0x9017DU, 0x9017EU, 0x9017FU, 0x90180U, 0x90181U, + 0x90182U, 0x90183U, 0x90184U, 0x90006U, 0x90007U, 0x90008U, 0x90009U, 0x9000AU, 0x9000BU, + 0xD00E7U, 0x90017U, 0x9001FU, 0x90026U, 0x400D0U, 0x400D1U, 0x400D2U, 0x400D3U, 0x400D4U, + 0x400D5U, 0x400D6U, 0x400D7U, 0x2003AU, + }; + +static const uint16_t prodcode_data[PRODCODE_SIZE] = { + 0x0010U, 0x0400U, 0x010EU, 0x0000U, 0x0000U, 0x0008U, 0x000BU, 0x0480U, 0x0109U, 0x0008U, + 0x0448U, 0x0139U, 0x0008U, 0x0478U, 0x0109U, 0x0000U, 0x00E8U, 0x0109U, 0x0002U, 0x0010U, + 0x0139U, 0x000BU, 0x07C0U, 0x0139U, 0x0044U, 0x0633U, 0x0159U, 0x014FU, 0x0630U, 0x0159U, + 0x0047U, 0x0633U, 0x0149U, 0x004FU, 0x0633U, 0x0179U, 0x0008U, 0x00E0U, 0x0109U, 0x0000U, + 0x07C8U, 0x0109U, 0x0000U, 0x0001U, 0x0008U, 0x0030U, 0x065AU, 0x0009U, 0x0000U, 0x045AU, + 0x0009U, 0x0000U, 0x0448U, 0x0109U, 0x0040U, 0x0633U, 0x0179U, 0x0001U, 0x0618U, 0x0109U, + 0x40C0U, 0x0633U, 0x0149U, 0x0008U, 0x0004U, 0x0048U, 0x4040U, 0x0633U, 0x0149U, 0x0000U, + 0x0004U, 0x0048U, 0x0040U, 0x0633U, 0x0149U, 0x0000U, 0x0658U, 0x0109U, 0x0010U, 0x0004U, + 0x0018U, 0x0000U, 0x0004U, 0x0078U, 0x0549U, 0x0633U, 0x0159U, 0x0D49U, 0x0633U, 0x0159U, + 0x094AU, 0x0633U, 0x0159U, 0x0441U, 0x0633U, 0x0149U, 0x0042U, 0x0633U, 0x0149U, 0x0001U, + 0x0633U, 0x0149U, 0x0000U, 0x00E0U, 0x0109U, 0x000AU, 0x0010U, 0x0109U, 0x0009U, 0x03C0U, + 0x0149U, 0x0009U, 0x03C0U, 0x0159U, 0x0018U, 0x0010U, 0x0109U, 0x0000U, 0x03C0U, 0x0109U, + 0x0018U, 0x0004U, 0x0048U, 0x0018U, 0x0004U, 0x0058U, 0x000BU, 0x0010U, 0x0109U, 0x0001U, + 0x0010U, 0x0109U, 0x0005U, 0x07C0U, 0x0109U, 0x0811U, 0x0880U, 0x0000U, 0x0000U, 0x4008U, + 0x0083U, 0x004FU, 0x0000U, 0x4040U, 0x0083U, 0x0051U, 0x0000U, 0x0811U, 0x0880U, 0x0000U, + 0x0000U, 0x0720U, 0x000FU, 0x1740U, 0x0000U, 0x0016U, 0x0083U, 0x004BU, 0x0000U, 0x0716U, + 0x000FU, 0x2001U, 0x0000U, 0x0716U, 0x000FU, 0x2800U, 0x0000U, 0x0716U, 0x000FU, 0x0F00U, + 0x0000U, 0x0720U, 0x000FU, 0x1400U, 0x0000U, 0x0E08U, 0x0C15U, 0x0000U, 0x0000U, 0x0625U, + 0x0015U, 0x0000U, 0x0000U, 0x4028U, 0x0080U, 0x0000U, 0x0000U, 0x0E08U, 0x0C1AU, 0x0000U, + 0x0000U, 0x0625U, 0x001AU, 0x0000U, 0x0000U, 0x4040U, 0x0080U, 0x0000U, 0x0000U, 0x2604U, + 0x0015U, 0x0000U, 0x0000U, 0x0708U, 0x0005U, 0x0000U, 0x2002U, 0x0008U, 0x0080U, 0x0000U, + 0x0000U, 0x2604U, 0x001AU, 0x0000U, 0x0000U, 0x0708U, 0x000AU, 0x0000U, 0x2002U, 0x4040U, + 0x0080U, 0x0000U, 0x0000U, 0x060AU, 0x0015U, 0x1200U, 0x0000U, 0x061AU, 0x0015U, 0x1300U, + 0x0000U, 0x060AU, 0x001AU, 0x1200U, 0x0000U, 0x0642U, 0x001AU, 0x1300U, 0x0000U, 0x4808U, + 0x0880U, 0x0000U, 0x0000U, 0x0000U, 0x0790U, 0x011AU, 0x0008U, 0x07AAU, 0x002AU, 0x0010U, + 0x07B2U, 0x002AU, 0x0000U, 0x07C8U, 0x0109U, 0x0010U, 0x0010U, 0x0109U, 0x0010U, 0x02A8U, + 0x0129U, 0x0008U, 0x0370U, 0x0129U, 0x000AU, 0x03C8U, 0x01A9U, 0x000CU, 0x0408U, 0x0199U, + 0x0014U, 0x0790U, 0x011AU, 0x0008U, 0x0004U, 0x0018U, 0x000EU, 0x0408U, 0x0199U, 0x0008U, + 0x8568U, 0x0108U, 0x0018U, 0x0790U, 0x016AU, 0x0008U, 0x01D8U, 0x0169U, 0x0010U, 0x8558U, + 0x0168U, 0x1FF8U, 0x85A8U, 0x01E8U, 0x0050U, 0x0798U, 0x016AU, 0x0060U, 0x07A0U, 0x016AU, + 0x0008U, 0x8310U, 0x0168U, 0x0008U, 0xA310U, 0x0168U, 0x000AU, 0x0408U, 0x0169U, 0x006EU, + 0x0000U, 0x0068U, 0x0000U, 0x0408U, 0x0169U, 0x0000U, 0x8310U, 0x0168U, 0x0000U, 0xA310U, + 0x0168U, 0x1FF8U, 0x85A8U, 0x01E8U, 0x0068U, 0x0798U, 0x016AU, 0x0078U, 0x07A0U, 0x016AU, + 0x0068U, 0x0790U, 0x016AU, 0x0008U, 0x8B10U, 0x0168U, 0x0008U, 0xAB10U, 0x0168U, 0x000AU, + 0x0408U, 0x0169U, 0x0058U, 0x0000U, 0x0068U, 0x0000U, 0x0408U, 0x0169U, 0x0000U, 0x8B10U, + 0x0168U, 0x0001U, 0xAB10U, 0x0168U, 0x0000U, 0x01D8U, 0x0169U, 0x0080U, 0x0790U, 0x016AU, + 0x0018U, 0x07AAU, 0x006AU, 0x000AU, 0x0000U, 0x01E9U, 0x0008U, 0x8080U, 0x0108U, 0x000FU, + 0x0408U, 0x0169U, 0x000CU, 0x0000U, 0x0068U, 0x0009U, 0x0000U, 0x01A9U, 0x0000U, 0x0408U, + 0x0169U, 0x0000U, 0x8080U, 0x0108U, 0x0008U, 0x07AAU, 0x006AU, 0x0000U, 0x8568U, 0x0108U, + 0x00B7U, 0x0790U, 0x016AU, 0x001FU, 0x0000U, 0x0068U, 0x0008U, 0x8558U, 0x0168U, 0x000FU, + 0x0408U, 0x0169U, 0x000DU, 0x0000U, 0x0068U, 0x0000U, 0x0408U, 0x0169U, 0x0000U, 0x8558U, + 0x0168U, 0x0008U, 0x03C8U, 0x01A9U, 0x0003U, 0x0370U, 0x0129U, 0x0020U, 0x02AAU, 0x0009U, + 0x0008U, 0x00E8U, 0x0109U, 0x0000U, 0x8140U, 0x010CU, 0x0010U, 0x8138U, 0x0104U, 0x0008U, + 0x0448U, 0x0109U, 0x000FU, 0x07C0U, 0x0109U, 0x0000U, 0x00E8U, 0x0109U, 0x0047U, 0x0630U, + 0x0109U, 0x0008U, 0x0618U, 0x0109U, 0x0008U, 0x00E0U, 0x0109U, 0x0000U, 0x07C8U, 0x0109U, + 0x0008U, 0x8140U, 0x010CU, 0x0000U, 0x0478U, 0x0109U, 0x0000U, 0x0001U, 0x0008U, 0x0008U, + 0x0004U, 0x0000U, 0x0008U, 0x07C8U, 0x0109U, 0x0000U, 0x0400U, 0x0106U, 0x0400U, 0x0000U, + 0x002BU, 0x0069U, 0x0000U, 0x0101U, 0x0105U, 0x0107U, 0x010FU, 0x0202U, 0x020AU, 0x020BU, + 0x0002U, + }; +#endif /* STM32MP_DDR3_TYPE || STM32MP_DDR4_TYPE */ + +/* + * Loads PIE instruction sequence PHY registers + * @returns void + */ +void ddrphy_phyinit_loadpieprodcode(void) +{ + int i; + + for (i = 0; i < PRODCODE_SIZE; i++) { + mmio_write_16((uintptr_t)(DDRPHYC_BASE + (4U * prodcode_addr[i])), + prodcode_data[i]); + } +} diff --git a/drivers/st/ddr/phy/phyinit/src/ddrphy_phyinit_mapdrvstren.c b/drivers/st/ddr/phy/phyinit/src/ddrphy_phyinit_mapdrvstren.c new file mode 100644 index 00000000..f1eeb820 --- /dev/null +++ b/drivers/st/ddr/phy/phyinit/src/ddrphy_phyinit_mapdrvstren.c @@ -0,0 +1,282 @@ +/* + * Copyright (C) 2021-2024, STMicroelectronics - All Rights Reserved + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <stdlib.h> +#include <string.h> + +#include <common/debug.h> + +#include <ddrphy_phyinit.h> + +/* + * Maps impedance values to register settings + * + * Reads the pull-up/pull-down driver impedance from drvstren_ohm input + * and encodes that value for the CSR field specified in targetcsr input, + * based on DDR protocol. + * + * @param[in] drvstren_ohm drive strenght / ODT impedance in Ohms + * + * @param[in] targetcsr Target CSR for the impedance value. on of following + * enum drvtype: + * - DRVSTRENFSDQP + * - DRVSTRENFSDQN + * - ODTSTRENP + * - ODTSTRENN + * - ADRVSTRENP + * - ADRVSTRENN + * + * \return >=0 value on success, else negative. + */ +int ddrphy_phyinit_mapdrvstren(uint32_t drvstren_ohm, enum drvtype targetcsr) +{ + int stren_setting = -1; + + if ((targetcsr == DRVSTRENFSDQP) || (targetcsr == DRVSTRENFSDQN)) { + if (drvstren_ohm == 0U) { + stren_setting = 0x00; /* High-impedance */ + } else if (drvstren_ohm < 29U) { + stren_setting = 0x3f; + } else if (drvstren_ohm < 31U) { + stren_setting = 0x3e; + } else if (drvstren_ohm < 33U) { + stren_setting = 0x3b; + } else if (drvstren_ohm < 35U) { + stren_setting = 0x3a; + } else if (drvstren_ohm < 38U) { + stren_setting = 0x39; + } else if (drvstren_ohm < 41U) { + stren_setting = 0x38; + } else if (drvstren_ohm < 45U) { + stren_setting = 0x1b; + } else if (drvstren_ohm < 50U) { + stren_setting = 0x1a; + } else if (drvstren_ohm < 56U) { + stren_setting = 0x19; + } else if (drvstren_ohm < 64U) { + stren_setting = 0x18; + } else if (drvstren_ohm < 74U) { + stren_setting = 0x0b; + } else if (drvstren_ohm < 88U) { + stren_setting = 0x0a; + } else if (drvstren_ohm < 108U) { + stren_setting = 0x09; + } else if (drvstren_ohm < 140U) { + stren_setting = 0x08; + } else if (drvstren_ohm < 200U) { + stren_setting = 0x03; + } else if (drvstren_ohm < 360U) { + stren_setting = 0x02; + } else if (drvstren_ohm < 481U) { + stren_setting = 0x01; + } else { + stren_setting = 0x00; /* High-impedance */ + } + } else if (targetcsr == ODTSTRENP) { +#if STM32MP_DDR3_TYPE + /* + * DDR3 - P and N has the same impedance and non-zero. + * user input is half the individual pull-up and pull-down impedances values + * because of parallel between them. + */ + if (drvstren_ohm == 0U) { + stren_setting = 0x00; /* High-impedance */ + } else if (drvstren_ohm < 15U) { + stren_setting = 0x3f; + } else if (drvstren_ohm < 16U) { + stren_setting = 0x3e; + } else if (drvstren_ohm < 17U) { + stren_setting = 0x3b; + } else if (drvstren_ohm < 18U) { + stren_setting = 0x3a; + } else if (drvstren_ohm < 20U) { + stren_setting = 0x39; + } else if (drvstren_ohm < 21U) { + stren_setting = 0x38; + } else if (drvstren_ohm < 23U) { + stren_setting = 0x1b; + } else if (drvstren_ohm < 26U) { + stren_setting = 0x1a; + } else if (drvstren_ohm < 29U) { + stren_setting = 0x19; + } else if (drvstren_ohm < 33U) { + stren_setting = 0x18; + } else if (drvstren_ohm < 38U) { + stren_setting = 0x0b; + } else if (drvstren_ohm < 45U) { + stren_setting = 0x0a; + } else if (drvstren_ohm < 55U) { + stren_setting = 0x09; + } else if (drvstren_ohm < 71U) { + stren_setting = 0x08; + } else if (drvstren_ohm < 101U) { + stren_setting = 0x03; + } else if (drvstren_ohm < 181U) { + stren_setting = 0x02; + } else if (drvstren_ohm < 241U) { + stren_setting = 0x01; + } else { + stren_setting = 0x00; /* High-impedance */ + } +#elif STM32MP_DDR4_TYPE + /* DDR4 - P is non-zero */ + if (drvstren_ohm == 0U) { + stren_setting = 0x00; /* High-impedance */ + } else if (drvstren_ohm < 29U) { + stren_setting = 0x3f; + } else if (drvstren_ohm < 31U) { + stren_setting = 0x3e; + } else if (drvstren_ohm < 33U) { + stren_setting = 0x3b; + } else if (drvstren_ohm < 35U) { + stren_setting = 0x3a; + } else if (drvstren_ohm < 38U) { + stren_setting = 0x39; + } else if (drvstren_ohm < 41U) { + stren_setting = 0x38; + } else if (drvstren_ohm < 45U) { + stren_setting = 0x1b; + } else if (drvstren_ohm < 50U) { + stren_setting = 0x1a; + } else if (drvstren_ohm < 56U) { + stren_setting = 0x19; + } else if (drvstren_ohm < 64U) { + stren_setting = 0x18; + } else if (drvstren_ohm < 74U) { + stren_setting = 0x0b; + } else if (drvstren_ohm < 88U) { + stren_setting = 0x0a; + } else if (drvstren_ohm < 108U) { + stren_setting = 0x09; + } else if (drvstren_ohm < 140U) { + stren_setting = 0x08; + } else if (drvstren_ohm < 200U) { + stren_setting = 0x03; + } else if (drvstren_ohm < 360U) { + stren_setting = 0x02; + } else if (drvstren_ohm < 481U) { + stren_setting = 0x01; + } else { + stren_setting = 0x00; /* High-impedance */ + } +#else /* STM32MP_LPDDR4_TYPE */ + /* LPDDR4 - P is high-Z */ + stren_setting = 0x00; /* High-impedance */ +#endif /* STM32MP_DDR3_TYPE */ + } else if (targetcsr == ODTSTRENN) { +#if STM32MP_DDR3_TYPE + /* + * DDR3 - P and N has the same impedance and non-zero. + * Times 2 of user input because of parallel pull-up and pull-down termination. + */ + if (drvstren_ohm == 0U) { + stren_setting = 0x00; /* High-impedance */ + } else if (drvstren_ohm < 15U) { + stren_setting = 0x3f; + } else if (drvstren_ohm < 16U) { + stren_setting = 0x3e; + } else if (drvstren_ohm < 17U) { + stren_setting = 0x3b; + } else if (drvstren_ohm < 18U) { + stren_setting = 0x3a; + } else if (drvstren_ohm < 20U) { + stren_setting = 0x39; + } else if (drvstren_ohm < 21U) { + stren_setting = 0x38; + } else if (drvstren_ohm < 23U) { + stren_setting = 0x1b; + } else if (drvstren_ohm < 26U) { + stren_setting = 0x1a; + } else if (drvstren_ohm < 29U) { + stren_setting = 0x19; + } else if (drvstren_ohm < 33U) { + stren_setting = 0x18; + } else if (drvstren_ohm < 38U) { + stren_setting = 0x0b; + } else if (drvstren_ohm < 45U) { + stren_setting = 0x0a; + } else if (drvstren_ohm < 55U) { + stren_setting = 0x09; + } else if (drvstren_ohm < 71U) { + stren_setting = 0x08; + } else if (drvstren_ohm < 101U) { + stren_setting = 0x03; + } else if (drvstren_ohm < 181U) { + stren_setting = 0x02; + } else if (drvstren_ohm < 241U) { + stren_setting = 0x01; + } else { + stren_setting = 0x00; /* High-impedance */ + } +#elif STM32MP_DDR4_TYPE + /* DDR4 - N is high-Z */ + stren_setting = 0x00; /* High-impedance */ +#else /* STM32MP_LPDDR4_TYPE */ + /* LPDDR4 - N is non-zero */ + if (drvstren_ohm == 0U) { + stren_setting = 0x00; /* High-impedance */ + } else if (drvstren_ohm < 29U) { + stren_setting = 0x3f; + } else if (drvstren_ohm < 31U) { + stren_setting = 0x3e; + } else if (drvstren_ohm < 33U) { + stren_setting = 0x3b; + } else if (drvstren_ohm < 35U) { + stren_setting = 0x3a; + } else if (drvstren_ohm < 38U) { + stren_setting = 0x39; + } else if (drvstren_ohm < 41U) { + stren_setting = 0x38; + } else if (drvstren_ohm < 45U) { + stren_setting = 0x1b; + } else if (drvstren_ohm < 50U) { + stren_setting = 0x1a; + } else if (drvstren_ohm < 56U) { + stren_setting = 0x19; + } else if (drvstren_ohm < 64U) { + stren_setting = 0x18; + } else if (drvstren_ohm < 74U) { + stren_setting = 0x0b; + } else if (drvstren_ohm < 88U) { + stren_setting = 0x0a; + } else if (drvstren_ohm < 108U) { + stren_setting = 0x09; + } else if (drvstren_ohm < 140U) { + stren_setting = 0x08; + } else if (drvstren_ohm < 200U) { + stren_setting = 0x03; + } else if (drvstren_ohm < 360U) { + stren_setting = 0x02; + } else if (drvstren_ohm < 481U) { + stren_setting = 0x01; + } else { + stren_setting = 0x00; /* High-impedance */ + } +#endif /* STM32MP_DDR3_TYPE */ + } else { + /* if ((targetcsr == ADRVSTRENP) || (targetcsr == ADRVSTRENN)) */ + if (drvstren_ohm == 120U) { + stren_setting = 0x00; + } else if (drvstren_ohm == 60U) { + stren_setting = 0x01; + } else if (drvstren_ohm == 40U) { + stren_setting = 0x03; + } else if (drvstren_ohm == 30U) { + stren_setting = 0x07; + } else if (drvstren_ohm == 24U) { + stren_setting = 0x0F; + } else if (drvstren_ohm == 20U) { + stren_setting = 0x1F; + } else { + ERROR("%s %d\n", __func__, __LINE__); + VERBOSE("%s userinputadvanced.atximpedance %u Ohms value is not valid.\n", + __func__, drvstren_ohm); + VERBOSE("Valid values are: 20, 24, 30, 40, 60 and 120 Ohms.\n"); + } + } + + return stren_setting; +} diff --git a/drivers/st/ddr/phy/phyinit/src/ddrphy_phyinit_progcsrskiptrain.c b/drivers/st/ddr/phy/phyinit/src/ddrphy_phyinit_progcsrskiptrain.c new file mode 100644 index 00000000..c9a71f4d --- /dev/null +++ b/drivers/st/ddr/phy/phyinit/src/ddrphy_phyinit_progcsrskiptrain.c @@ -0,0 +1,893 @@ +/* + * Copyright (C) 2021-2024, STMicroelectronics - All Rights Reserved + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <arch_helpers.h> + +#include <common/debug.h> + +#include <ddrphy_phyinit.h> +#include <ddrphy_wrapper.h> + +#include <lib/mmio.h> + +#include <platform_def.h> + +struct phyinit_timings { + int tstaoff; + int tpdm; + int tcasl_add; +}; + +static struct phyinit_timings timings; + +/* + * Program dfimrl according to this formula: + * + * dfimrl = ceiling( (ARdPtrinitval*UI + phy_tx_insertion_dly + + * phy_rx_insertion_dly + PHY_Rx_Fifo_dly + tDQSCK + tstaoff) / + * dficlk_period) + * + * All terms in above equation specified in ps + * tDQSCK - determine from memory model + * tstaoff - determine from memory model + * phy_tx_insertion_dly = 200ps + * phy_rx_insertion_dly = 200ps + * phy_rx_fifo_dly = 200ps + 4UI + */ +static void dfimrl_program(struct stm32mp_ddr_config *config, struct pmu_smb_ddr_1d *mb_ddr_1d, + int ardptrinitval) +{ + uint32_t byte; + int dfimrl_in_dficlk; + int phy_rx_fifo_dly; + int phy_rx_insertion_dly = 200; + int phy_tx_insertion_dly = 200; + long long dficlk_period_x1000; + long long dfimrl_in_fs; + long long uifs; + uint16_t dfimrl; + + uifs = (1000 * 1000000) / ((int)config->uib.frequency * 2); + dficlk_period_x1000 = 4 * uifs; + + phy_rx_fifo_dly = (int)(((200 * 1000) + (4 * uifs)) / 1000); + + dfimrl_in_fs = (ardptrinitval * uifs) + + ((phy_tx_insertion_dly + phy_rx_insertion_dly + phy_rx_fifo_dly + + timings.tstaoff + timings.tcasl_add + timings.tpdm) * 1000); + + dfimrl_in_dficlk = (int)(dfimrl_in_fs / dficlk_period_x1000); + if ((dfimrl_in_fs % dficlk_period_x1000) != 0) { + dfimrl_in_dficlk++; + } + dfimrl = (uint16_t)(dfimrl_in_dficlk + mb_ddr_1d->dfimrlmargin); + + /* + * mmio_write_16((uintptr_t)(DDRPHYC_BASE + (4U * (TDBYTE | CBRD | CSR_DFIMRL_ADDR))), + * dfimrl); + */ + for (byte = 0U; byte < config->uib.numdbyte; byte++) { + uint32_t c_addr; + + c_addr = byte << 12; + mmio_write_16((uintptr_t)(DDRPHYC_BASE + (4U * (TDBYTE | c_addr | + CSR_DFIMRL_ADDR))), + dfimrl); + } + + mmio_write_16((uintptr_t)(DDRPHYC_BASE + (4U * (TMASTER | CSR_HWTMRL_ADDR))), dfimrl); +} + +/* + * Program txdqsdlytg0/1[9:0]: + * + * txdqsdlytg*[9:6] = floor( (4*UI + tstaoff) / UI) + * txdqsdlytg*[5:0] = ceiling( (tstaoff%UI / UI) * 32) + * + * tstaoff and UI expressed in ps + * + * For HMD and LPDDR4X and MEMCLK <= 533 mhz: + * txdqsdlytg*[9:6] = 0x5 + * + * For other dimm types, leave TDqsDlyTg*[9:0] at default 0x100 + * + * ppp_0001_cccc_uuuu_1101_0000 + * + * if DDR3 or DDR4 + * num_timingroup = numrank_dfi0; + * else + * num_timingroup = numrank_dfi0 + numrank_dfi1 * dfi1exists; + */ +static void txdqsdlytg_program(struct stm32mp_ddr_config *config, struct pmu_smb_ddr_1d *mb_ddr_1d, + uint16_t *txdqsdly) +{ + uint32_t byte; + int txdqsdlytg_5to0; /* Fine delay - 1/32UI per increment */ + int txdqsdlytg_9to6; /* Coarse delay - 1UI per increment */ + int txdqsdlytg_fine_default = 0; + int txdqsdlytg_coarse_default = 4; + long long tmp_value; + long long uifs; + + uifs = (1000 * 1000000) / ((int)config->uib.frequency * 2); + + txdqsdlytg_9to6 = (int)(((int)((txdqsdlytg_coarse_default * uifs) / 1000) + + timings.tstaoff + timings.tcasl_add + - timings.tpdm) / (int)(uifs / 1000)); + + tmp_value = fmodll(((txdqsdlytg_fine_default * uifs / 32) + + ((timings.tstaoff + timings.tcasl_add) * 1000) - + (timings.tpdm * 1000)), + uifs); + txdqsdlytg_5to0 = (int)(tmp_value / uifs * 32); + if ((tmp_value % uifs) != 0) { + txdqsdlytg_5to0++; + } + + /* Bit-5 of LCDL is no longer used, so bumping bit-5 of fine_dly up to coarse_dly */ + if (txdqsdlytg_5to0 >= 32) { + txdqsdlytg_9to6 = txdqsdlytg_9to6 + 1; + txdqsdlytg_5to0 = txdqsdlytg_5to0 - 32; + } + + *txdqsdly = (uint16_t)((txdqsdlytg_9to6 << 6) | txdqsdlytg_5to0); + + for (byte = 0U; byte < config->uib.numdbyte; byte++) { + uint32_t c_addr; + uint32_t nibble; + + c_addr = byte << 12; + for (nibble = 0U; nibble < 2U; nibble++) { + uint32_t u_addr; + + if (ddrphy_phyinit_isdbytedisabled(config, mb_ddr_1d, byte) != 0) { + continue; + } + + u_addr = nibble << 8; + +#if STM32MP_DDR3_TYPE || STM32MP_DDR4_TYPE + if ((mb_ddr_1d->cspresent & 0x1U) != 0U) { +#else /* STM32MP_LPDDR4_TYPE */ + if (((mb_ddr_1d->cspresentcha & 0x1U) | + (mb_ddr_1d->cspresentchb & 0x1U)) != 0U) { +#endif /* STM32MP_DDR3_TYPE || STM32MP_DDR4_TYPE */ + mmio_write_16((uintptr_t) + (DDRPHYC_BASE + (4U * (TDBYTE | c_addr | u_addr | + CSR_TXDQSDLYTG0_ADDR))), + *txdqsdly); + } + +#if STM32MP_LPDDR4_TYPE + if ((((mb_ddr_1d->cspresentcha & 0x2U) >> 1) | + ((mb_ddr_1d->cspresentchb & 0x2U) >> 1)) != 0U) { + mmio_write_16((uintptr_t) + (DDRPHYC_BASE + (4U * (TDBYTE | c_addr | u_addr | + CSR_TXDQSDLYTG1_ADDR))), + *txdqsdly); + } +#endif /* STM32MP_LPDDR4_TYPE */ + } + } +} + +/* + * ############################################################## + * + * Program txdqdlyTg0/1[8:0]: + * + * txdqdlyTg*[8:6] = floor( (txdqsdlytg*[5:0]*UI/32 + tDQS2DQ + 0.5UI) / UI) + * txdqdlyTg*[5:0] = ceil( ((txdqsdlytg*[5:0]*UI/32 + tDQS2DQ + 0.5UI)%UI / UI) * 32) + * + * ############################################################## + */ +static void txdqdlytg_program(struct stm32mp_ddr_config *config, struct pmu_smb_ddr_1d *mb_ddr_1d, + uint16_t txdqsdly) +{ + uint32_t byte; + int txdqdly_5to0; /* Fine delay - 1/32UI per increment */ + int txdqdly_8to6; /* Coarse delay - 1UI per increment */ + int txdqsdlytg_5to0; /* Fine delay - 1/32UI per increment */ + long long tmp_value; + long long uifs; + uint16_t txdqdly; + + uifs = (1000 * 1000000) / ((int)config->uib.frequency * 2); + + txdqsdlytg_5to0 = (int)txdqsdly & 0x3F; + + txdqdly_8to6 = (int)(((txdqsdlytg_5to0 * uifs / 32) + (uifs / 2)) / uifs); + tmp_value = fmodll(((txdqsdlytg_5to0 * uifs / 32) + (uifs / 2)), uifs); + txdqdly_5to0 = (int)(((tmp_value * 32) / uifs)); + if ((tmp_value % uifs) != 0) { + txdqdly_5to0++; + } + + /* Bit-5 of LCDL is no longer used, so bumping bit-5 of fine_dly up to coarse_dly */ + if (txdqdly_5to0 >= 32) { + txdqdly_8to6 = txdqdly_8to6 + 1; + txdqdly_5to0 = txdqdly_5to0 - 32; + } + + txdqdly = (uint16_t)((txdqdly_8to6 << 6) | txdqdly_5to0); + + for (byte = 0U; byte < config->uib.numdbyte; byte++) { + uint32_t c_addr; + uint32_t lane; + + c_addr = byte << 12; + for (lane = 0U; lane < 9U; lane++) { + uint32_t r_addr; + + if (ddrphy_phyinit_isdbytedisabled(config, mb_ddr_1d, byte) != 0) { + continue; + } + + r_addr = lane << 8; + +#if STM32MP_DDR3_TYPE || STM32MP_DDR4_TYPE + if ((mb_ddr_1d->cspresent & 0x1U) != 0U) { +#else /* STM32MP_LPDDR4_TYPE */ + if (((mb_ddr_1d->cspresentcha & 0x1U) | + (mb_ddr_1d->cspresentchb & 0x1U)) != 0U) { +#endif /* STM32MP_DDR3_TYPE || STM32MP_DDR4_TYPE */ + mmio_write_16((uintptr_t) + (DDRPHYC_BASE + (4U * (TDBYTE | c_addr | r_addr | + CSR_TXDQDLYTG0_ADDR))), + txdqdly); + } + +#if STM32MP_LPDDR4_TYPE + if ((((mb_ddr_1d->cspresentcha & 0x2U) >> 1) | + ((mb_ddr_1d->cspresentchb & 0x2U) >> 1)) != 0U) { + mmio_write_16((uintptr_t) + (DDRPHYC_BASE + (4U * (TDBYTE | c_addr | r_addr | + CSR_TXDQDLYTG1_ADDR))), + txdqdly); + } +#endif /* STM32MP_LPDDR4_TYPE */ + } + } +} + +/* + * Program rxendly0/1[10:0]: + * + * rxendly[10:6] = floor( (4*UI + tDQSCK + tstaoff) / UI) + * rxendly[5:0] = ceil( ((tDQSCK + tstaoff) % UI) * 32) + * + * tDQSCK, tstaoff and UI expressed in ps + */ +static void rxendly_program(struct stm32mp_ddr_config *config, struct pmu_smb_ddr_1d *mb_ddr_1d) +{ + int rxendly_coarse_default = 4; + int rxendly_fine_default = 0; + + int backoff_x1000 __maybe_unused; + int zerobackoff_x1000 __maybe_unused; + uint32_t byte; + int rxendly_10to6; /* Coarse delay - 1UI per increment */ + int rxendly_5to0; /* Fine delay - 1/32UI per increment */ + int totfinestep; + long long finestepfs; /* Fine steps in fs */ + long long rxendly_offset_x1000000 = 0; /* 0 Offset is 1UI before the first DQS. */ + long long totfs; + long long uifs; + uint16_t rxendly; + + uifs = (1000 * 1000000) / ((int)config->uib.frequency * 2); + +#if STM32MP_LPDDR4_TYPE + /* Compensate for pptenrxenbackoff */ + zerobackoff_x1000 = (1000 * 24) / 32; + if (config->uia.lp4rxpreamblemode == 1U) { + backoff_x1000 = 1000 - ((1000 * 2) / 32); + } else { + backoff_x1000 = (1000 * (int)config->uia.rxenbackoff) - ((1000 * 2) / 32); + } + + if (config->uia.disableretraining == 0U) { + rxendly_offset_x1000000 = config->uib.frequency < 333U ? + backoff_x1000 * uifs : zerobackoff_x1000 * uifs; + } else { + rxendly_offset_x1000000 = zerobackoff_x1000 * uifs; + } +#endif /* STM32MP_LPDDR4_TYPE */ + + finestepfs = uifs / 32; + totfs = ((32 * rxendly_coarse_default * finestepfs) + + (rxendly_fine_default * finestepfs) + + ((timings.tstaoff + timings.tcasl_add + + timings.tpdm) * 1000) + (rxendly_offset_x1000000 / 1000)); + totfinestep = totfs / finestepfs; + + rxendly_10to6 = totfinestep / 32; + rxendly_5to0 = fmodi(totfinestep, 32); + + /* Bit-5 of LCDL is no longer used, so bumping bit-5 of fine_dly up to coarse_dly */ + if (rxendly_5to0 >= 32) { + rxendly_10to6 = rxendly_10to6 + 1; + rxendly_5to0 = rxendly_5to0 - 32; + } + + rxendly = (uint16_t)((rxendly_10to6 << 6) | rxendly_5to0); + + for (byte = 0U; byte < config->uib.numdbyte; byte++) { + uint32_t c_addr; + uint32_t nibble; + + c_addr = byte << 12; + for (nibble = 0U; nibble < 2U; nibble++) { + uint32_t u_addr; + + if (ddrphy_phyinit_isdbytedisabled(config, mb_ddr_1d, byte) != 0) { + continue; + } + + u_addr = nibble << 8; + +#if STM32MP_DDR3_TYPE || STM32MP_DDR4_TYPE + if ((mb_ddr_1d->cspresent & 0x1U) != 0) { +#else /* STM32MP_LPDDR4_TYPE */ + if (((mb_ddr_1d->cspresentcha & 0x1U) | + (mb_ddr_1d->cspresentchb & 0x1U)) != 0U) { +#endif /* STM32MP_DDR3_TYPE || STM32MP_DDR4_TYPE */ + mmio_write_16((uintptr_t) + (DDRPHYC_BASE + (4U * (TDBYTE | c_addr | u_addr | + CSR_RXENDLYTG0_ADDR))), + rxendly); + } + +#if STM32MP_LPDDR4_TYPE + if ((((mb_ddr_1d->cspresentcha & 0x2U) >> 1) | + ((mb_ddr_1d->cspresentchb & 0x2U) >> 1)) != 0U) { + mmio_write_16((uintptr_t) + (DDRPHYC_BASE + (4U * (TDBYTE | c_addr | u_addr | + CSR_RXENDLYTG1_ADDR))), + rxendly); + } +#endif /* STM32MP_LPDDR4_TYPE */ + } + } +} + +#if STM32MP_LPDDR4_TYPE +/* + * Programming Seq0BGPR1/2/3 for LPDDR4 + */ +static void seq0bgpr_program(struct stm32mp_ddr_config *config) +{ + uint32_t extradly = 3U; + uint32_t rl = 0U; /* Computed read latency */ + uint32_t wl = 0U; /* Computed write latency */ + uint16_t mr_dbi_rd; /* Extracted field from MR */ + uint16_t mr_rl; + uint16_t mr_wl; + uint16_t mr_wls; + uint16_t regdata; + + mr_rl = (uint16_t)config->uia.lp4rl; /* RL[2:0] */ + mr_wl = (uint16_t)config->uia.lp4wl; /* WL[5:3] */ + mr_wls = (uint16_t)config->uia.lp4wls; /* WLS */ + mr_dbi_rd = (uint16_t)config->uia.lp4dbird; /* DBI-RD */ + + switch ((mr_dbi_rd << 3) | mr_rl) { + /* DBI-RD Disabled */ + case 0U: + rl = 6U; + break; + case 1U: + rl = 10U; + break; + case 2U: + rl = 14U; + break; + case 3U: + rl = 20U; + break; + case 4U: + rl = 24U; + break; + case 5U: + rl = 28U; + break; + case 6U: + rl = 32U; + break; + case 7U: + rl = 36U; + break; + /* DBI-RD Enabled */ + case 8U: + rl = 6U; + break; + case 9U: + rl = 12U; + break; + case 10U: + rl = 16U; + break; + case 11U: + rl = 22U; + break; + case 12U: + rl = 28U; + break; + case 13U: + rl = 32U; + break; + case 14U: + rl = 36U; + break; + case 15U: + rl = 40U; + break; + default: + rl = 6U; + break; + } + + switch ((mr_wls << 3) | mr_wl) { + /* DBI-RD Disabled */ + case 0U: + wl = 4U; + break; + case 1U: + wl = 6U; + break; + case 2U: + wl = 8U; + break; + case 3U: + wl = 10U; + break; + case 4U: + wl = 12U; + break; + case 5U: + wl = 14U; + break; + case 6U: + wl = 16U; + break; + case 7U: + wl = 18U; + break; + /* DBI-RD Enabled */ + case 8U: + wl = 4U; + break; + case 9U: + wl = 8U; + break; + case 10U: + wl = 12U; + break; + case 11U: + wl = 18U; + break; + case 12U: + wl = 22U; + break; + case 13U: + wl = 26U; + break; + case 14U: + wl = 30U; + break; + case 15U: + wl = 34U; + break; + default: + wl = 4U; + break; + } + + /* Program Seq0b_GPRx */ + regdata = (uint16_t)((rl - 5U + extradly) << CSR_ACSMRCASLAT_LSB); + mmio_write_16((uintptr_t)(DDRPHYC_BASE + (4U * (P0 | C0 | TINITENG | R2 | + CSR_SEQ0BGPR1_ADDR))), + regdata); + + regdata = (uint16_t)((wl - 5U + extradly) << CSR_ACSMWCASLAT_LSB); + mmio_write_16((uintptr_t)(DDRPHYC_BASE + (4U * (P0 | C0 | TINITENG | R2 | + CSR_SEQ0BGPR2_ADDR))), + regdata); + + regdata = (uint16_t)((rl - 5U + extradly + 4U + 8U) << CSR_ACSMRCASLAT_LSB); + mmio_write_16((uintptr_t)(DDRPHYC_BASE + (4U * (P0 | C0 | TINITENG | R2 | + CSR_SEQ0BGPR3_ADDR))), + regdata); +} + +/* + * Program hwtlpcsena and hwtlpcsenb based on number of ranks per channel + * Applicable only for LPDDR4. These CSRs have no effect for DDR3/4. + * + * CSRs to program: + * hwtlpcsena + * hwtlpcsenb + * + * User input dependencies: + * config->uib.numrank_dfi0 + * config->uib.numrank_dfi1 + * config->uib.dfi1exists + * config->uib.numactivedbytedfi1 + */ +static void hwtlpcsen_program(struct stm32mp_ddr_config *config) +{ + uint16_t hwtlpcsena; + uint16_t hwtlpcsenb; + + /* Channel A - 1'b01 if signal-rank, 2'b11 if dual-rank */ + hwtlpcsena = (uint16_t)config->uib.numrank_dfi0 | 0x1U; + mmio_write_16((uintptr_t)(DDRPHYC_BASE + (4U * (TMASTER | CSR_HWTLPCSENA_ADDR))), + hwtlpcsena); + + /* + * Channel B - 1'b01 if signal-rank, 2'b11 if dual-rank + * If DFI1 exists but disabled, numrank_dfi0 is used to program CsEnB + */ + if ((config->uib.dfi1exists != 0U) && (config->uib.numactivedbytedfi1 == 0U)) { + hwtlpcsenb = (uint16_t)config->uib.numrank_dfi0 | 0x1U; + mmio_write_16((uintptr_t)(DDRPHYC_BASE + (4U * (TMASTER | CSR_HWTLPCSENB_ADDR))), + hwtlpcsenb); + } else if ((config->uib.dfi1exists != 0U) && (config->uib.numactivedbytedfi1 > 0U)) { + hwtlpcsenb = (uint16_t)config->uib.numrank_dfi1 | 0x1U; + mmio_write_16((uintptr_t)(DDRPHYC_BASE + (4U * (TMASTER | CSR_HWTLPCSENB_ADDR))), + hwtlpcsenb); + } else { + /* Disable Channel B */ + hwtlpcsenb = 0x0U; + mmio_write_16((uintptr_t)(DDRPHYC_BASE + (4U * (TMASTER | CSR_HWTLPCSENB_ADDR))), + hwtlpcsenb); + } +} + +/* + * Program pptdqscntinvtrntg0 and pptdqscntinvtrntg1 + * Calculated based on tDQS2DQ and Frequencey + * Applicable to LPDDR4 only + * + * 65536*(tdqs2dq_value_rank<rank>_chan<chan>*2)/(2*2048*UI(ps)_int) + * + * CSRs to program: + * pptdqscntinvtrntg0 + * pptdqscntinvtrntg1 + * + * User input dependencies: + * config->uib.numrank_dfi0 + * config->uib.numrank_dfi1 + * config->uib.dfi1exists + * config->uib.numdbyte + */ +static void pptdqscntinvtrntg_program(struct stm32mp_ddr_config *config) +{ + uint32_t numrank_total = config->uib.numrank_dfi0; + uint32_t rank; + + /* Calculate total number of timing groups (ranks) */ + if (config->uib.dfi1exists != 0U) { + numrank_total += config->uib.numrank_dfi1; + } + + /* Set per timing group */ + for (rank = 0U; rank < numrank_total; rank++) { + uint32_t byte; + + for (byte = 0U; byte < config->uib.numdbyte; byte++) { + uint32_t c_addr; + + c_addr = byte << 12; + if (rank == 0U) { + mmio_write_16((uintptr_t) + (DDRPHYC_BASE + (4U * (TDBYTE | c_addr | + CSR_PPTDQSCNTINVTRNTG0_ADDR))), + 0U); + } else if (rank == 1U) { + mmio_write_16((uintptr_t) + (DDRPHYC_BASE + (4U * (TDBYTE | c_addr | + CSR_PPTDQSCNTINVTRNTG1_ADDR))), + 0U); + } + } + } +} + +/* + * CSRs to program: + * PptCtlStatic:: DOCByteSelTg0/1 + * :: pptenrxenbackoff + * + * User input dependencies:: + * config->uib.numdbyte + * config->uib.numrank_dfi0 + * config->uib.numrank_dfi1 + * config->uia.lp4rxpreamblemode + * config->uia.rxenbackoff + * config->uia.drambyteswap + */ +static void pptctlstatic_program(struct stm32mp_ddr_config *config) +{ + uint32_t byte; + uint32_t pptenrxenbackoff; + + /* + * The customer will setup some fields in this csr so the fw needs to do a + * read-modify-write here. + */ + + if (config->uia.lp4rxpreamblemode == 1U) { + /* Rx-preamble mode for PS0 */ + /* Programming PptCtlStatic detected toggling preamble */ + pptenrxenbackoff = 0x1U; /* Toggling RD_PRE */ + } else { + pptenrxenbackoff = config->uia.rxenbackoff; /* Static RD_PRE */ + } + + for (byte = 0U; byte < config->uib.numdbyte; byte++) { + uint32_t c_addr; + uint16_t regdata; + uint8_t pptentg1; + uint32_t docbytetg0; + uint32_t docbytetg1; + + /* Each Dbyte could have a different configuration */ + c_addr = byte * C1; + if ((byte % 2) == 0) { + docbytetg0 = 0x1U & (config->uia.drambyteswap >> byte); + docbytetg1 = 0x1U & (config->uia.drambyteswap >> byte); + } else { + docbytetg0 = 0x1U & (~(config->uia.drambyteswap >> byte)); + docbytetg1 = 0x1U & (~(config->uia.drambyteswap >> byte)); + } + + pptentg1 = ((config->uib.numrank_dfi0 == 2U) || (config->uib.numrank_dfi1 == 2U)) ? + 0x1U : 0x0U; + regdata = (uint16_t)((0x1U << CSR_PPTENDQS2DQTG0_LSB) | + (pptentg1 << CSR_PPTENDQS2DQTG1_LSB) | + (0x1U << CSR_PPTENRXENDLYTG0_LSB) | + (pptentg1 << CSR_PPTENRXENDLYTG1_LSB) | + (pptenrxenbackoff << CSR_PPTENRXENBACKOFF_LSB) | + (docbytetg0 << CSR_DOCBYTESELTG0_LSB) | + (docbytetg1 << CSR_DOCBYTESELTG1_LSB)); + + mmio_write_16((uintptr_t)(DDRPHYC_BASE + (4U * (c_addr | TDBYTE | + CSR_PPTCTLSTATIC_ADDR))), + regdata); + } +} +#endif /* STM32MP_LPDDR4_TYPE */ + +/* + * Program hwtcamode based on dram type + * + * CSRs to program: + * hwtcamode::hwtlp3camode + * ::hwtd4camode + * ::hwtlp4camode + * ::hwtd4altcamode + * ::hwtcsinvert + * ::hwtdbiinvert + */ +static void hwtcamode_program(void) +{ + uint32_t hwtlp3camode = 0U; + uint32_t hwtd4camode = 0U; + uint32_t hwtlp4camode = 0U; + uint32_t hwtd4altcamode = 0U; + uint32_t hwtcsinvert = 0U; + uint32_t hwtdbiinvert = 0U; + uint16_t hwtcamode; + +#if STM32MP_DDR4_TYPE + hwtd4camode = 1U; +#elif STM32MP_LPDDR4_TYPE + hwtlp4camode = 1U; + hwtcsinvert = 1U; + hwtdbiinvert = 1U; +#else /* STM32MP_DDR3_TYPE */ + /* Nothing to declare */ +#endif /* STM32MP_DDR4_TYPE */ + + hwtcamode = (uint16_t)((hwtdbiinvert << CSR_HWTDBIINVERT_LSB) | + (hwtcsinvert << CSR_HWTCSINVERT_LSB) | + (hwtd4altcamode << CSR_HWTD4ALTCAMODE_LSB) | + (hwtlp4camode << CSR_HWTLP4CAMODE_LSB) | + (hwtd4camode << CSR_HWTD4CAMODE_LSB) | + (hwtlp3camode << CSR_HWTLP3CAMODE_LSB)); + + mmio_write_16((uintptr_t)(DDRPHYC_BASE + (4U * (TMASTER | CSR_HWTCAMODE_ADDR))), hwtcamode); +} + +/* + * Program DllGainCtl and DllLockParam based on frequency + */ +static void dllgainctl_dlllockparam_program(struct stm32mp_ddr_config *config) +{ + uint32_t dllgainiv; + uint32_t dllgaintv; + uint32_t lcdlseed; + uint32_t memck_freq; + uint32_t stepsize_x10 = 47U; /* + * Nominal stepsize, in units of tenths of a ps, + * if nominal=4.7ps use 47 + */ + uint16_t wddllgainctl; + uint16_t wddlllockparam; + + memck_freq = config->uib.frequency; + + /* + * lcdlseed = ((1000000/memck_freq)/2)/lcdl_stepsize ... + * where default lcdl_stepsize=4.7 in simulation. + */ + if (memck_freq >= 1200U) { + dllgainiv = 0x04U; + dllgaintv = 0x05U; + } else if (memck_freq >= 800U) { + dllgainiv = 0x03U; + dllgaintv = 0x05U; + } else if (memck_freq >= 532U) { + dllgainiv = 0x02U; + dllgaintv = 0x04U; + } else if (memck_freq >= 332U) { + dllgainiv = 0x01U; + dllgaintv = 0x03U; + } else { + dllgainiv = 0x00U; + dllgaintv = 0x02U; + } + + /* + * lcdlseed= (1000000/(2*memck_freq)) * (100/(120*(stepsize_nominal))); + * *100/105 is to bias the seed low. + */ + lcdlseed = (1000000U * 10U * 100U) / (2U * memck_freq * stepsize_x10 * 105U); + + if (lcdlseed > (511U - 32U)) { + lcdlseed = 511U - 32U; + } + + if (lcdlseed < 32U) { + lcdlseed = 32U; + } + + wddllgainctl = (uint16_t)((CSR_DLLGAINTV_MASK & (dllgaintv << CSR_DLLGAINTV_LSB)) | + (CSR_DLLGAINIV_MASK & (dllgainiv << CSR_DLLGAINIV_LSB))); + + mmio_write_16((uintptr_t)(DDRPHYC_BASE + (4U * (TMASTER | CSR_DLLGAINCTL_ADDR))), + wddllgainctl); + + wddlllockparam = (uint16_t)((CSR_LCDLSEED0_MASK & (lcdlseed << CSR_LCDLSEED0_LSB)) | + (CSR_DISDLLGAINIVSEED_MASK & 0xFFFFU)); + + mmio_write_16((uintptr_t)(DDRPHYC_BASE + (4U * (TMASTER | CSR_DLLLOCKPARAM_ADDR))), + wddlllockparam); +} + +/* + * Program AcsmCtrl23 for Fw and Ppt. + * + * CSRs to program: + * AcsmCtrl23::AcsmCsMask + * AcsmCsMode + */ +static void acsmctrl23_program(void) +{ + uint16_t regdata; + + regdata = (0x0FU << CSR_ACSMCSMASK_LSB) | (0x1U << CSR_ACSMCSMODE_LSB); + mmio_write_16((uintptr_t)(DDRPHYC_BASE + (4U * (C0 | TACSM | CSR_ACSMCTRL23_ADDR))), + regdata); +} + +/* + * Set PllForceCal to 1 and PllDacValIn to some arbitrary value + */ +static void pllforcecal_plldacvalin_program(void) +{ + uint32_t dacval_in = 0x10U; + uint32_t force_cal = 0x1U; + uint32_t pllencal = 0x1U; + uint32_t maxrange = 0x1FU; + uint16_t pllctrl3_gpr; + uint16_t pllctrl3_startup; + + pllctrl3_startup = (uint16_t)((dacval_in << CSR_PLLDACVALIN_LSB) | + (maxrange << CSR_PLLMAXRANGE_LSB)); + pllctrl3_gpr = pllctrl3_startup | (uint16_t)((force_cal << CSR_PLLFORCECAL_LSB) | + (pllencal << CSR_PLLENCAL_LSB)); + + mmio_write_16((uintptr_t)(DDRPHYC_BASE + (4U * (TMASTER | CSR_PLLCTRL3_ADDR))), + pllctrl3_startup); + mmio_write_16((uintptr_t)(DDRPHYC_BASE + (4U * (TINITENG | CSR_SEQ0BGPR6_ADDR))), + pllctrl3_gpr); +} + +/* + * This function programs registers that are normally set by training + * firmware. + * + * This function is used in place of running 1D or 1D training steps. PhyInit + * calls this function when skip_train = true. In that case, PhyInit does not + * execute training firmware and this function is called instead to program + * PHY registers according to DRAM timing parameters specified in userInput + * data structure. See documentation of ddrphy_phyinit_struct.h file + * details of timing parameters available in skip training. + * + * \warning ddrphy_phyinit_progcsrskiptrain() only supports zero board + * delay model. If system board delays are set or randomized, full 1D or 1D + * initialization flow must be executed. + * + * This function replaces these steps in the PHY Initialization sequence: + * - (E) Set the PHY input clocks to the desired frequency + * - (F) Write the Message Block parameters for the training firmware + * - (G) Execute the Training Firmware + * - (H) Read the Message Block results + * + * \returns \c void + */ +void ddrphy_phyinit_progcsrskiptrain(struct stm32mp_ddr_config *config, + struct pmu_smb_ddr_1d *mb_ddr_1d, uint32_t ardptrinitval) +{ + uint16_t txdqsdly; + + /* + * Program ATxDlY + * For DDR4, DDR3 and LPDDR4, leave AtxDly[6:0] at default (0x0) + */ + + dfimrl_program(config, mb_ddr_1d, ardptrinitval); + + txdqsdlytg_program(config, mb_ddr_1d, &txdqsdly); + + txdqdlytg_program(config, mb_ddr_1d, txdqsdly); + + rxendly_program(config, mb_ddr_1d); + +#if STM32MP_LPDDR4_TYPE + seq0bgpr_program(config); + + hwtlpcsen_program(config); + + pptdqscntinvtrntg_program(config); + + pptctlstatic_program(config); +#endif /* STM32MP_LPDDR4_TYPE */ + + hwtcamode_program(); + + dllgainctl_dlllockparam_program(config); + + acsmctrl23_program(); + + pllforcecal_plldacvalin_program(); + + /* + * ############################################################## + * + * Setting PhyInLP3 to 0 to cause PIE to execute LP2 sequence instead of INIT on first + * dfi_init_start. + * This prevents any DRAM commands before DRAM is initialized, which is the case for + * skip_train. + * + * Moved to here from dddrphy_phyinit_I_loadPIEImage() + * These should not be needed on S3-exit + * + * Note this executes for SkipTrain only, *not* DevInit+SkipTrain + * DevInit+SkipTrain already initializes DRAM and thus don't need to avoid DRAM commands + * + * ############################################################## + */ + + /* + * Special skipTraining configuration to Prevent DRAM Commands on the first dfi + * status interface handshake. In order to see this behavior, the first dfi_freq + * should be in the range of 0x0f < dfi_freq_sel[4:0] < 0x14. + */ + mmio_write_16((uintptr_t)(DDRPHYC_BASE + (4U * (TINITENG | CSR_PHYINLP3_ADDR))), 0x0U); +} diff --git a/drivers/st/ddr/phy/phyinit/src/ddrphy_phyinit_reginterface.c b/drivers/st/ddr/phy/phyinit/src/ddrphy_phyinit_reginterface.c new file mode 100644 index 00000000..21400f72 --- /dev/null +++ b/drivers/st/ddr/phy/phyinit/src/ddrphy_phyinit_reginterface.c @@ -0,0 +1,170 @@ +/* + * Copyright (C) 2021-2024, STMicroelectronics - All Rights Reserved + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +/* + * This file provides a group of functions that are used to track PHY register + * writes by intercepting io_write16 function calls. Once the registers are + * tracked, their value can be saved at a given time spot, and restored later + * as required. This implementation is useful to capture any PHY register + * programing in any function during PHY initialization. + */ + +#include <stdint.h> + +#include <common/debug.h> + +#include <ddrphy_phyinit.h> + +#include <lib/mmio.h> + +#include <platform_def.h> + +/* + * MAX_NUM_RET_REGS default Max number of retention registers. + * + * This define is only used by the PhyInit Register interface to define the max + * amount of registered that can be saved. The user may increase this variable + * as desired if a larger number of registers need to be restored. + */ +#if STM32MP_DDR3_TYPE || STM32MP_DDR4_TYPE +#define MAX_NUM_RET_REGS 129 +#else /* STM32MP_LPDDR4_TYPE */ +#define MAX_NUM_RET_REGS 283 +#endif /* STM32MP_DDR3_TYPE || STM32MP_DDR4_TYPE */ + +/* + * Array of Address/value pairs used to store register values for the purpose + * of retention restore. + */ +#define RETREG_AREA (MAX_NUM_RET_REGS + 1) * sizeof(struct reg_addr_val) +#define RETREG_BASE RETRAM_BASE + RETRAM_SIZE - RETREG_AREA + +static int *retregsize = (int *)(RETREG_BASE); +static struct reg_addr_val *retreglist = (struct reg_addr_val *)(RETREG_BASE + sizeof(int)); + +static int numregsaved; /* Current Number of registers saved. */ +static int tracken = 1; /* Enabled tracking of registers */ + +/* + * Tags a register if tracking is enabled in the register + * interface. + * + * During PhyInit registers writes, keeps track of address + * for the purpose of restoring the PHY register state during PHY + * retention exit process. Tracking can be turned on/off via the + * ddrphy_phyinit_reginterface STARTTRACK, STOPTRACK instructions. By + * default tracking is always turned on. + * + * \return 0 on success. + */ +int ddrphy_phyinit_trackreg(uint32_t adr) +{ + int regindx = 0; + + /* Return if tracking is disabled */ + if (tracken == 0) { + return 0; + } + + /* Search register address within the array */ + for (regindx = 0; regindx < numregsaved; regindx++) { + if (retreglist[regindx].address == adr) { + /* Register found */ + return 0; + } + } + + /* Register not found, so add it */ + if (numregsaved > MAX_NUM_RET_REGS) { + ERROR("numregsaved > MAX_NUM_RET_REGS\n"); + VERBOSE("[ddrphy_phyinit_reginterface:%s]\n", __func__); + VERBOSE("Max Number of Restore Registers reached: %d.\n", numregsaved); + VERBOSE("Please recompile PhyInit with larger MAX_NUM_RET_REG value.\n"); + return -1; + } + + retreglist[regindx].address = adr; + numregsaved++; + + return 0; +} + +/* + * Register interface function used to track, save and restore retention registers. + * + * ### Usage + * Register tracking is enabled by calling: + * + * \code + * ddrphy_phyinit_reginterface(STARTTRACK,0,0); + * \endcode + * + * from this point on any call to mmio_write_16() in + * return will be capture by the register interface via a call to + * ddrphy_phyinit_trackreg(). Tracking is disabled by calling: + * + * \code + * ddrphy_phyinit_reginterface(STOPTRACK,0,0); + * \endcode + * + * On calling this function, register write via mmio_write_16 are no longer tracked until a + * STARTTRACK call is made. Once all the register write are complete, SAVEREGS + * command can be issue to save register values into the internal data array of + * the register interface. Upon retention exit RESTOREREGS are command can be + * used to issue register write commands to the PHY based on values stored in + * the array. + * \code + * ddrphy_phyinit_reginterface(SAVEREGS,0,0); + * ddrphy_phyinit_reginterface(RESTOREREGS,0,0); + * \endcode + * \return 0 on success. + */ +int ddrphy_phyinit_reginterface(enum reginstr myreginstr, uint32_t adr, uint16_t dat) +{ + if (myreginstr == SAVEREGS) { + int regindx; + + /* + * Go through all the tracked registers, issue a register read and place + * the result in the data structure for future recovery. + */ + for (regindx = 0; regindx < numregsaved; regindx++) { + uint16_t data; + + data = mmio_read_16((uintptr_t)(DDRPHYC_BASE + + (4U * retreglist[regindx].address))); + retreglist[regindx].value = data; + } + + *retregsize = numregsaved; + + return 0; + } else if (myreginstr == RESTOREREGS) { + int regindx; + + /* + * Write PHY registers based on Address, Data value pairs stores in + * retreglist. + */ + for (regindx = 0; regindx < *retregsize; regindx++) { + mmio_write_16((uintptr_t) + (DDRPHYC_BASE + (4U * retreglist[regindx].address)), + retreglist[regindx].value); + } + + return 0; + } else if (myreginstr == STARTTRACK) { + /* Enable tracking */ + tracken = 1; + return 0; + } else if (myreginstr == STOPTRACK) { + /* Disable tracking */ + tracken = 0; + return 0; + } else { + return -1; + } +} diff --git a/drivers/st/ddr/phy/phyinit/src/ddrphy_phyinit_restore_sequence.c b/drivers/st/ddr/phy/phyinit/src/ddrphy_phyinit_restore_sequence.c new file mode 100644 index 00000000..cddb9552 --- /dev/null +++ b/drivers/st/ddr/phy/phyinit/src/ddrphy_phyinit_restore_sequence.c @@ -0,0 +1,78 @@ +/* + * Copyright (C) 2021-2024, STMicroelectronics - All Rights Reserved + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <common/debug.h> + +#include <ddrphy_phyinit.h> + +#include <lib/mmio.h> + +#include <platform_def.h> + +/* + * This function implements the register restore portion of S3/IO + * retention sequence. + * + * \note This function requiers the runtime_config.reten=1 to enable PhyInit exit retention feature. + * This variable can be set as in + * \return 0 on completion of the sequence, EXIT_FAILURE on error. + */ +int ddrphy_phyinit_restore_sequence(void) +{ + int ret; + + /* + * Before you call this functions perform the following: + * -------------------------------------------------------------------------- + * -# Bring up VDD, VDDQ should already be up + * -# Since the CKE* and MEMRESET pin state must be protected, special care + * must be taken to ensure that the following signals + * - atpg_mode = 1'b0 + * - PwrOkIn = 1'b0 + * + * -# The {BypassModeEn*, WRSTN} signals may be defined at VDD power-on, but + * must be driven to ZERO at least 10ns prior to the asserting edge of PwrOkIn. + * + * -# Start Clocks and Reset the PHY + * This step is identical to ddrphy_phyinit_usercustom_b_startclockresetphy() + */ + + /* Write the MicroContMuxSel CSR to 0x0 to allow access to the internal CSRs */ + mmio_write_16((uintptr_t)(DDRPHYC_BASE + (4U * (TAPBONLY | CSR_MICROCONTMUXSEL_ADDR))), + 0x0U); + + /* + * Write the UcclkHclkEnables CSR to 0x3 to enable all the clocks so the reads can + * complete. + */ + mmio_write_16((uintptr_t)(DDRPHYC_BASE + (4U * (TDRTUB | CSR_UCCLKHCLKENABLES_ADDR))), + 0x3U); + + /* + * Assert CalZap to force impedance calibration FSM to idle. + * De-asserted as part of dfi_init_start/complete handshake by the PIE when DfiClk is valid. + */ + mmio_write_16((uintptr_t)(DDRPHYC_BASE + (4U * (TMASTER | CSR_CALZAP_ADDR))), 0x1U); + + /* Issue register writes to restore registers values */ + ret = ddrphy_phyinit_reginterface(RESTOREREGS, 0U, 0U); + if (ret != 0) { + return ret; + } + + /* + * Write the UcclkHclkEnables CSR to disable the appropriate clocks after all reads done. + * Disabling Ucclk (PMU) and Hclk (training hardware). + */ + mmio_write_16((uintptr_t)(DDRPHYC_BASE + (4U * (TDRTUB | CSR_UCCLKHCLKENABLES_ADDR))), + 0x0U); + + /* Write the MicroContMuxSel CSR to 0x1 to isolate the internal CSRs during mission mode */ + mmio_write_16((uintptr_t)(DDRPHYC_BASE + (4U * (TAPBONLY | CSR_MICROCONTMUXSEL_ADDR))), + 0x1U); + + return 0; +} diff --git a/drivers/st/ddr/phy/phyinit/src/ddrphy_phyinit_sequence.c b/drivers/st/ddr/phy/phyinit/src/ddrphy_phyinit_sequence.c new file mode 100644 index 00000000..adc43773 --- /dev/null +++ b/drivers/st/ddr/phy/phyinit/src/ddrphy_phyinit_sequence.c @@ -0,0 +1,121 @@ +/* + * Copyright (C) 2021-2024, STMicroelectronics - All Rights Reserved + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <arch_helpers.h> + +#include <common/debug.h> + +#include <ddrphy_phyinit.h> + +/* + * This function implements the flow of PhyInit software to initialize the PHY. + * + * The execution sequence follows the overview figure provided in the Reference Manual. + * + * \returns 0 on completion of the sequence, EXIT_FAILURE on error. + */ +int ddrphy_phyinit_sequence(struct stm32mp_ddr_config *config, bool skip_training, bool reten) +{ + int ret; + uint32_t ardptrinitval; /* + * Represents the value stored in Step C into the register with the + * same name. Defined as a global variable so that implementation + * of ddrphy_phyinit_progcsrskiptrain() function does not require + * a PHY read register implementation. + */ + struct pmu_smb_ddr_1d mb_ddr_1d; /* Firmware 1D Message Block structure */ + + /* Check user input pstate number consistency vs. SW capabilities */ + if (config->uib.numpstates > 1U) { + return -1; + } + + /* Initialize structures */ + ddrphy_phyinit_initstruct(config, &mb_ddr_1d); + + /* Re-calculate Firmware Message Block input based on final user input */ + ret = ddrphy_phyinit_calcmb(config, &mb_ddr_1d); + if (ret != 0) { + return ret; + } + + /* (A) Bring up VDD, VDDQ, and VAA */ + /* ddrphy_phyinit_usercustom_a_bringuppower(); */ + + /* (B) Start Clocks and Reset the PHY */ + /* ddrphy_phyinit_usercustom_b_startclockresetphy(); */ + + /* (C) Initialize PHY Configuration */ + ret = ddrphy_phyinit_c_initphyconfig(config, &mb_ddr_1d, &ardptrinitval); + if (ret != 0) { + return ret; + } + /* + * Customize any register write desired; This can include any CSR not covered by PhyInit + * or user wish to override values calculated in step_C. + */ + ddrphy_phyinit_usercustom_custompretrain(config); + + /* Stop retention register tracking for training firmware related registers */ + ret = ddrphy_phyinit_reginterface(STOPTRACK, 0U, 0U); + if (ret != 0) { + return ret; + } + + if (skip_training) { + /* Skip running training firmware entirely */ + ddrphy_phyinit_progcsrskiptrain(config, &mb_ddr_1d, ardptrinitval); + } else { + /* (D) Load the IMEM Memory for 1D training */ + ddrphy_phyinit_d_loadimem(); + + /* (E) Set the PHY input clocks to the desired frequency */ + /* ddrphy_phyinit_usercustom_e_setdficlk(pstate); */ + + /* (F) Write the Message Block parameters for the training firmware */ + ret = ddrphy_phyinit_f_loaddmem(config, &mb_ddr_1d); + if (ret != 0) { + return ret; + } + + /* (G) Execute the Training Firmware */ + ret = ddrphy_phyinit_g_execfw(); + if (ret != 0) { + return ret; + } + + /* (H) Read the Message Block results */ + /* ddrphy_phyinit_h_readmsgblock(); */ + } + + /* Start retention register tracking for training firmware related registers */ + ret = ddrphy_phyinit_reginterface(STARTTRACK, 0U, 0U); + if (ret != 0) { + return ret; + } + + /* (I) Load PHY Init Engine Image */ + ddrphy_phyinit_i_loadpieimage(config, skip_training); + + /* + * Customize any CSR write desired to override values programmed by firmware or + * ddrphy_phyinit_i_loadpieimage() + */ + /* ddrphy_phyinit_usercustom_customposttrain(); */ + + if (reten) { + /* Save value of tracked registers for retention restore sequence. */ + ret = ddrphy_phyinit_usercustom_saveretregs(config); + if (ret != 0) { + return ret; + } + } + + /* (J) Initialize the PHY to Mission Mode through DFI Initialization */ + /* ddrphy_phyinit_usercustom_j_entermissionmode(); */ + + return 0; +} diff --git a/drivers/st/ddr/phy/phyinit/src/ddrphy_phyinit_softsetmb.c b/drivers/st/ddr/phy/phyinit/src/ddrphy_phyinit_softsetmb.c new file mode 100644 index 00000000..86b084d6 --- /dev/null +++ b/drivers/st/ddr/phy/phyinit/src/ddrphy_phyinit_softsetmb.c @@ -0,0 +1,100 @@ +/* + * Copyright (C) 2021-2024, STMicroelectronics - All Rights Reserved + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <assert.h> +#include <string.h> + +#include <common/debug.h> + +#include <ddrphy_phyinit.h> + +/* + * Set messageBlock variable only if not set by user + * + * This function is used by ddrphy_phyinit_calcmb() to set calculated + * messageBlock variables only when the user has not directly programmed them. + * + * @param[in] field A string representing the messageBlock field to be programed. + * @param[in] value filed value + * + * @return 0 on success. + * On error returns the following values based on error: + * - -1 : message block field specified by the input \c field string is not + * found in the message block data structure. + */ +int ddrphy_phyinit_softsetmb(struct pmu_smb_ddr_1d *mb_ddr_1d, enum message_block_field field, + uint32_t value) +{ + int ret = 0; + + if (field == MB_FIELD_DRAMFREQ) { + assert(value <= UINT16_MAX); + } else { + assert(value <= UINT8_MAX); + } + + switch (field) { + case MB_FIELD_PSTATE: + mb_ddr_1d->pstate = (uint8_t)value; + break; + case MB_FIELD_PLLBYPASSEN: + mb_ddr_1d->pllbypassen = (uint8_t)value; + break; + case MB_FIELD_DRAMFREQ: + mb_ddr_1d->dramfreq = (uint16_t)value; + break; + case MB_FIELD_DFIFREQRATIO: + mb_ddr_1d->dfifreqratio = (uint8_t)value; + break; + case MB_FIELD_BPZNRESVAL: + mb_ddr_1d->bpznresval = (uint8_t)value; + break; + case MB_FIELD_PHYODTIMPEDANCE: + mb_ddr_1d->phyodtimpedance = (uint8_t)value; + break; + case MB_FIELD_PHYDRVIMPEDANCE: + mb_ddr_1d->phydrvimpedance = (uint8_t)value; + break; +#if STM32MP_DDR3_TYPE || STM32MP_DDR4_TYPE + case MB_FIELD_DRAMTYPE: + mb_ddr_1d->dramtype = (uint8_t)value; + break; + case MB_FIELD_DISABLEDDBYTE: + mb_ddr_1d->disableddbyte = (uint8_t)value; + break; + case MB_FIELD_ENABLEDDQS: + mb_ddr_1d->enableddqs = (uint8_t)value; + break; + case MB_FIELD_PHYCFG: + mb_ddr_1d->phycfg = (uint8_t)value; + break; +#if STM32MP_DDR4_TYPE + case MB_FIELD_X16PRESENT: + mb_ddr_1d->x16present = (uint8_t)value; + break; +#endif /* STM32MP_DDR4_TYPE */ +#else /* STM32MP_LPDDR4_TYPE */ + case MB_FIELD_ENABLEDDQSCHA: + mb_ddr_1d->enableddqscha = (uint8_t)value; + break; + case MB_FIELD_CSPRESENTCHA: + mb_ddr_1d->cspresentcha = (uint8_t)value; + break; + case MB_FIELD_ENABLEDDQSCHB: + mb_ddr_1d->enableddqschb = (uint8_t)value; + break; + case MB_FIELD_CSPRESENTCHB: + mb_ddr_1d->cspresentchb = (uint8_t)value; + break; +#endif /* STM32MP_DDR3_TYPE || STM32MP_DDR4_TYPE */ + default: + ERROR("unknown message block field %u\n", field); + ret = -1; + break; + } + + return ret; +} diff --git a/drivers/st/ddr/phy/phyinit/src/ddrphy_phyinit_writeoutmem.c b/drivers/st/ddr/phy/phyinit/src/ddrphy_phyinit_writeoutmem.c new file mode 100644 index 00000000..868800e2 --- /dev/null +++ b/drivers/st/ddr/phy/phyinit/src/ddrphy_phyinit_writeoutmem.c @@ -0,0 +1,79 @@ +/* + * Copyright (C) 2021-2024, STMicroelectronics - All Rights Reserved + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <stdio.h> + +#include <common/debug.h> + +#include <ddrphy_phyinit.h> + +#include <lib/mmio.h> + +#include <platform_def.h> + +/* + * Writes local memory content into the SRAM via APB interface. + * + * This function issued APB writes commands to SRAM address based on values + * stored in a local PhyInit array that contains consolidated IMEM and DMEM + * data. + * @param[in] mem[] Local memory array. + * @param[in] mem_offset offset index. if provided, skips to the offset index + * from the local array and issues APB commands from mem_offset to mem_size. + * @param[in] mem_size size of the memroy (in mem array index) + * @returns void + */ +void ddrphy_phyinit_writeoutmem(uint32_t *mem, uint32_t mem_offset, uint32_t mem_size) +{ + uint32_t index; + + /* + * 1. Enable access to the internal CSRs by setting the MicroContMuxSel CSR to 0. + * This allows the memory controller unrestricted access to the configuration CSRs. + */ + mmio_write_16((uintptr_t)(DDRPHYC_BASE + (4U * (TAPBONLY | CSR_MICROCONTMUXSEL_ADDR))), + 0x0U); + + for (index = 0U; index < mem_size / sizeof(uint32_t); index++) { + uint32_t data = mem[index]; + + mmio_write_16((uintptr_t)(DDRPHYC_BASE + (4U * ((index * 2) + mem_offset))), + data & 0xFFFFU); + mmio_write_16((uintptr_t)(DDRPHYC_BASE + (4U * ((index * 2) + 1 + mem_offset))), + (data >> 16) & 0xFFFFU); + } + + /* + * 2. Isolate the APB access from the internal CSRs by setting the MicroContMuxSel CSR to 1. + * This allows the firmware unrestricted access to the configuration CSRs. + */ + mmio_write_16((uintptr_t)(DDRPHYC_BASE + (4U * (TAPBONLY | CSR_MICROCONTMUXSEL_ADDR))), + 0x1U); +} + +/* Similar function for message block */ +void ddrphy_phyinit_writeoutmsgblk(uint16_t *mem, uint32_t mem_offset, uint32_t mem_size) +{ + uint32_t index; + + /* + * 1. Enable access to the internal CSRs by setting the MicroContMuxSel CSR to 0. + * This allows the memory controller unrestricted access to the configuration CSRs. + */ + mmio_write_16((uintptr_t)(DDRPHYC_BASE + (4U * (TAPBONLY | CSR_MICROCONTMUXSEL_ADDR))), + 0x0U); + + for (index = 0U; index < mem_size / sizeof(uint16_t); index++) { + mmio_write_16((uintptr_t)(DDRPHYC_BASE + (4U * (index + mem_offset))), mem[index]); + } + + /* + * 2. Isolate the APB access from the internal CSRs by setting the MicroContMuxSel CSR to 1. + * This allows the firmware unrestricted access to the configuration CSRs. + */ + mmio_write_16((uintptr_t)(DDRPHYC_BASE + (4U * (TAPBONLY | CSR_MICROCONTMUXSEL_ADDR))), + 0x1U); +} diff --git a/drivers/st/ddr/phy/phyinit/usercustom/ddrphy_phyinit_usercustom_custompretrain.c b/drivers/st/ddr/phy/phyinit/usercustom/ddrphy_phyinit_usercustom_custompretrain.c new file mode 100644 index 00000000..6a200131 --- /dev/null +++ b/drivers/st/ddr/phy/phyinit/usercustom/ddrphy_phyinit_usercustom_custompretrain.c @@ -0,0 +1,90 @@ +/* + * Copyright (C) 2021-2024, STMicroelectronics - All Rights Reserved + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <stdio.h> +#include <stdlib.h> + +#include <common/debug.h> + +#include <ddrphy_phyinit.h> + +#include <lib/mmio.h> + +#include <platform_def.h> + +/* DDRDBG registers */ +#define DDRDBG_DDR34_AC_SWIZZLE_ADD3_0 U(0x100) + +/* + * This function is called before training firmware is executed. Any + * register override in this function might affect the firmware training + * results. + * + * This function is executed before firmware execution loop. Thus this function + * should be used only for the following: + * + * - Override PHY register values written by + * ddrphy_phyinit_c_initphyconfig. An example use case is when this + * function does not perform the exact programing desired by the user. + * - Write custom PHY registers that need to take effect before training + * firmware execution. + * + * User shall use mmio_write_16 to write PHY registers in order for the register + * to be tracked by PhyInit for retention restore. + * + * To override settings in the message block, users can assign values to the + * fields in the message block data structure directly. + * + * \ref examples/simple/ddrphy_phyinit_usercustom_custompretrain.c example of this function. + * + * @return Void + */ +void ddrphy_phyinit_usercustom_custompretrain(struct stm32mp_ddr_config *config) +{ + uint32_t byte __unused; + uint32_t i = 0U; + uint32_t j; + uintptr_t base; + +#if STM32MP_DDR3_TYPE || STM32MP_DDR4_TYPE + base = (uintptr_t)(DDRPHYC_BASE + (4U * (TMASTER | CSR_HWTSWIZZLEHWTADDRESS0_ADDR))); + + for (i = 0U; i < NB_HWT_SWIZZLE; i++) { + mmio_write_16(base + (i * sizeof(uint32_t)), + (uint16_t)config->uis.swizzle[i]); + } + + base = (uintptr_t)(stm32_ddrdbg_get_base() + DDRDBG_DDR34_AC_SWIZZLE_ADD3_0); + + for (j = 0U; j < NB_AC_SWIZZLE; j++, i++) { + mmio_write_32(base + (j * sizeof(uint32_t)), config->uis.swizzle[i]); + } +#else /* STM32MP_LPDDR4_TYPE */ + for (byte = 0U; byte < config->uib.numdbyte; byte++) { + base = (uintptr_t)(DDRPHYC_BASE + (4U * + ((byte << 12) | TDBYTE | CSR_DQ0LNSEL_ADDR))); + + for (j = 0U; j < NB_DQLNSEL_SWIZZLE_PER_BYTE; j++, i++) { + mmio_write_16(base + (j * sizeof(uint32_t)), + (uint16_t)config->uis.swizzle[i]); + } + } + + base = (uintptr_t)(DDRPHYC_BASE + (4U * (TMASTER | CSR_MAPCAA0TODFI_ADDR))); + + for (j = 0U; j < NB_MAPCAATODFI_SWIZZLE; j++, i++) { + mmio_write_16(base + (j * sizeof(uint32_t)), + (uint16_t)config->uis.swizzle[i]); + } + + base = (uintptr_t)(DDRPHYC_BASE + (4U * (TMASTER | CSR_MAPCAB0TODFI_ADDR))); + + for (j = 0U; j < NB_MAPCABTODFI_SWIZZLE; j++, i++) { + mmio_write_16(base + (j * sizeof(uint32_t)), + (uint16_t)config->uis.swizzle[i]); + } +#endif /* STM32MP_DDR3_TYPE || STM32MP_DDR4_TYPE */ +} diff --git a/drivers/st/ddr/phy/phyinit/usercustom/ddrphy_phyinit_usercustom_g_waitfwdone.c b/drivers/st/ddr/phy/phyinit/usercustom/ddrphy_phyinit_usercustom_g_waitfwdone.c new file mode 100644 index 00000000..3d00d3d4 --- /dev/null +++ b/drivers/st/ddr/phy/phyinit/usercustom/ddrphy_phyinit_usercustom_g_waitfwdone.c @@ -0,0 +1,183 @@ +/* + * Copyright (C) 2021-2024, STMicroelectronics - All Rights Reserved + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <common/debug.h> + +#include <ddrphy_phyinit.h> + +#include <drivers/delay_timer.h> + +#include <lib/mmio.h> + +#include <platform_def.h> + +/* Firmware major messages */ +#define FW_MAJ_MSG_TRAINING_SUCCESS 0x0000007U +#define FW_MAJ_MSG_START_STREAMING 0x0000008U +#define FW_MAJ_MSG_TRAINING_FAILED 0x00000FFU + +#define PHYINIT_DELAY_1US 1U +#define PHYINIT_DELAY_10US 10U +#define PHYINIT_TIMEOUT_US_1S 1000000U + +static int wait_uctwriteprotshadow(bool state) +{ + uint64_t timeout; + uint16_t read_data; + uint16_t value = state ? BIT(0) : 0U; + + timeout = timeout_init_us(PHYINIT_TIMEOUT_US_1S); + + do { + read_data = mmio_read_16((uintptr_t)(DDRPHYC_BASE + + (4U * (TAPBONLY | CSR_UCTSHADOWREGS_ADDR)))); + udelay(PHYINIT_DELAY_1US); + if (timeout_elapsed(timeout)) { + return -1; + } + } while ((read_data & BIT(0)) != value); + + return 0; +} + +static int ack_message_receipt(void) +{ + int ret; + + /* Acknowledge the receipt of the message */ + mmio_write_16((uintptr_t)(DDRPHYC_BASE + (4U * (TAPBONLY | CSR_DCTWRITEPROT_ADDR))), 0U); + + udelay(PHYINIT_DELAY_1US); + + ret = wait_uctwriteprotshadow(true); + if (ret != 0) { + return ret; + } + + /* Complete the 4-phase protocol */ + mmio_write_16((uintptr_t)(DDRPHYC_BASE + (4U * (TAPBONLY | CSR_DCTWRITEPROT_ADDR))), 1U); + + udelay(PHYINIT_DELAY_1US); + + return 0; +} + +static int get_major_message(uint32_t *msg) +{ + uint16_t message_number; + int ret; + + ret = wait_uctwriteprotshadow(false); + if (ret != 0) { + return ret; + } + + message_number = mmio_read_16((uintptr_t)(DDRPHYC_BASE + + (4U * (TAPBONLY | + CSR_UCTWRITEONLYSHADOW_ADDR)))); + + ret = ack_message_receipt(); + if (ret != 0) { + return ret; + } + + *msg = (uint32_t)message_number; + + return 0; +} + +static int get_streaming_message(uint32_t *msg) +{ + uint16_t stream_word_lower_part; + uint16_t stream_word_upper_part; + int ret; + + ret = wait_uctwriteprotshadow(false); + if (ret != 0) { + return ret; + } + + stream_word_lower_part = mmio_read_16((uintptr_t)(DDRPHYC_BASE + + (4U * (TAPBONLY | + CSR_UCTWRITEONLYSHADOW_ADDR)))); + + stream_word_upper_part = mmio_read_16((uintptr_t)(DDRPHYC_BASE + + (4U * (TAPBONLY | + CSR_UCTDATWRITEONLYSHADOW_ADDR)))); + + ret = ack_message_receipt(); + if (ret != 0) { + return ret; + } + + *msg = (uint32_t)stream_word_lower_part | ((uint32_t)stream_word_upper_part << 16); + + return 0; +} + +/* + * Implements the mechanism to wait for completion of training firmware execution. + * + * The purpose of user this function is to wait for firmware to finish training. + * The user can either implement a counter to wait or implement the polling + * mechanism (our choice here). The wait time is highly dependent on the training features + * enabled via sequencectrl input to the message block. + * + * The default behavior of this function is to print comments relating to this + * process. A function call of the same name will be printed in the output text + * file. + * + * The user can choose to leave this function as is, or implement mechanism to + * trigger mailbox poling event in simulation. + * + * \return 0 on success. + */ +int ddrphy_phyinit_usercustom_g_waitfwdone(void) +{ + uint32_t fw_major_message; + int ret; + + do { + ret = get_major_message(&fw_major_message); + if (ret != 0) { + return ret; + } + + VERBOSE("fw_major_message = %x\n", (unsigned int)fw_major_message); + + if (fw_major_message == FW_MAJ_MSG_START_STREAMING) { + uint32_t i; + uint32_t read_data; + uint32_t stream_len; + + ret = get_streaming_message(&read_data); + if (ret != 0) { + return ret; + } + + stream_len = read_data & 0xFFFFU; + + for (i = 0U; i < stream_len; i++) { + ret = get_streaming_message(&read_data); + if (ret != 0) { + return ret; + } + + VERBOSE("streaming message = %x\n", (unsigned int)read_data); + } + } + } while ((fw_major_message != FW_MAJ_MSG_TRAINING_SUCCESS) && + (fw_major_message != FW_MAJ_MSG_TRAINING_FAILED)); + + udelay(PHYINIT_DELAY_10US); + + if (fw_major_message == FW_MAJ_MSG_TRAINING_FAILED) { + ERROR("%s Training has failed.\n", __func__); + return -1; + } + + return 0; +} diff --git a/drivers/st/ddr/phy/phyinit/usercustom/ddrphy_phyinit_usercustom_saveretregs.c b/drivers/st/ddr/phy/phyinit/usercustom/ddrphy_phyinit_usercustom_saveretregs.c new file mode 100644 index 00000000..b573de32 --- /dev/null +++ b/drivers/st/ddr/phy/phyinit/usercustom/ddrphy_phyinit_usercustom_saveretregs.c @@ -0,0 +1,399 @@ +/* + * Copyright (C) 2021-2024, STMicroelectronics - All Rights Reserved + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <stdlib.h> + +#include <common/debug.h> + +#include <ddrphy_phyinit.h> + +#include <lib/mmio.h> + +#include <platform_def.h> + +/* + * This function can be used to implement saving of PHY registers to be + * restored on retention exit. + * + * The requirement of this function is to issue register reads and store the + * value to be recovered on retention exit. The following is an example + * implementation and the user may implement alternate methods that suit their + * specific SoC system needs. + * + * In this implementation PhyInit saves register values in an internal C array. + * During retention exit it restores register values from the array. The exact + * list of registers to save and later restore can be seen in the output txt + * file with an associated calls to mmio_read_16(). + * + * PhyInit provides a register interface and a tracking mechanism to minimize + * the number registers needing restore. Refer to source code for + * ddrphy_phyinit_reginterface() for detailed implementation of tracking + * mechanism. Tracking is disabled from step D to Step H as these involve + * loading, executing and checking the state of training firmware execution + * which are not required to implement the retention exit sequence. The registers + * specified representing training results are also saved in addition to registers + * written by PhyInit during PHY initialization. + * + * \return 0 on success. + */ +int ddrphy_phyinit_usercustom_saveretregs(struct stm32mp_ddr_config *config) +{ + uint32_t anib; + uint32_t byte; + uint32_t nibble; + uint32_t lane; + uint32_t c_addr; + uint32_t u_addr; + uint32_t b_addr; + uint32_t r_addr; + int ret; + + /* + * -------------------------------------------------------------------------- + * 1. Enable tracking of training firmware result registers + * + * \note The tagged registers in this step are in + * addition to what is automatically tagged during Steps C to I. + * + * -------------------------------------------------------------------------- + */ + + ret = ddrphy_phyinit_trackreg(TMASTER | CSR_PLLCTRL3_ADDR); + if (ret != 0) { + return ret; + } + + /* Non-PState Dbyte Registers */ + for (byte = 0U; byte < config->uib.numdbyte; byte++) { + c_addr = byte << 12; + + for (lane = 0U; lane <= R_MAX; lane++) { + r_addr = lane << 8; + + ret = ddrphy_phyinit_trackreg(TDBYTE | c_addr | r_addr | + CSR_RXPBDLYTG0_ADDR); + if (ret != 0) { + return ret; + } +#if STM32MP_LPDDR4_TYPE + ret = ddrphy_phyinit_trackreg(TDBYTE | c_addr | r_addr | + CSR_RXPBDLYTG1_ADDR); + if (ret != 0) { + return ret; + } +#endif /* STM32MP_LPDDR4_TYPE */ + } + +#if STM32MP_LPDDR4_TYPE + ret = ddrphy_phyinit_trackreg(TDBYTE | c_addr | CSR_PPTCTLSTATIC_ADDR); + if (ret != 0) { + return ret; + } + + ret = ddrphy_phyinit_trackreg(TDBYTE | c_addr | CSR_TRAININGINCDECDTSMEN_ADDR); + if (ret != 0) { + return ret; + } + + ret = ddrphy_phyinit_trackreg(TDBYTE | c_addr | CSR_TSMBYTE0_ADDR); + if (ret != 0) { + return ret; + } + + ret = ddrphy_phyinit_trackreg(TDBYTE | c_addr | CSR_DQ0LNSEL_ADDR); + if (ret != 0) { + return ret; + } + + ret = ddrphy_phyinit_trackreg(TDBYTE | c_addr | CSR_DQ1LNSEL_ADDR); + if (ret != 0) { + return ret; + } + + ret = ddrphy_phyinit_trackreg(TDBYTE | c_addr | CSR_DQ2LNSEL_ADDR); + if (ret != 0) { + return ret; + } + + ret = ddrphy_phyinit_trackreg(TDBYTE | c_addr | CSR_DQ3LNSEL_ADDR); + if (ret != 0) { + return ret; + } + + ret = ddrphy_phyinit_trackreg(TDBYTE | c_addr | CSR_DQ4LNSEL_ADDR); + if (ret != 0) { + return ret; + } + + ret = ddrphy_phyinit_trackreg(TDBYTE | c_addr | CSR_DQ5LNSEL_ADDR); + if (ret != 0) { + return ret; + } + + ret = ddrphy_phyinit_trackreg(TDBYTE | c_addr | CSR_DQ6LNSEL_ADDR); + if (ret != 0) { + return ret; + } + + ret = ddrphy_phyinit_trackreg(TDBYTE | c_addr | CSR_DQ7LNSEL_ADDR); + if (ret != 0) { + return ret; + } +#endif /* STM32MP_LPDDR4_TYPE */ + } + + ret = ddrphy_phyinit_trackreg(TMASTER | CSR_VREFINGLOBAL_ADDR); + if (ret != 0) { + return ret; + } + + /* Anib Registers */ + for (anib = 0U; anib < config->uib.numanib; anib++) { + c_addr = anib << 12; + + ret = ddrphy_phyinit_trackreg(TANIB | c_addr | CSR_ATXDLY_ADDR); + if (ret != 0) { + return ret; + } + } + + /* Dbyte Registers */ + for (byte = 0U; byte < config->uib.numdbyte; byte++) { + c_addr = byte << 12; + + ret = ddrphy_phyinit_trackreg(TDBYTE | c_addr | CSR_DFIMRL_ADDR); + if (ret != 0) { + return ret; + } + + for (nibble = 0U; nibble <= B_MAX; nibble++) { + b_addr = nibble << 8; + + ret = ddrphy_phyinit_trackreg(TDBYTE | c_addr | b_addr | + CSR_DQDQSRCVCNTRL_ADDR); + if (ret != 0) { + return ret; + } + } + + for (nibble = 0U; nibble < 2U; nibble++) { + u_addr = nibble << 8; + + ret = ddrphy_phyinit_trackreg(TDBYTE | c_addr | u_addr | + CSR_RXENDLYTG0_ADDR); + if (ret != 0) { + return ret; + } +#if STM32MP_LPDDR4_TYPE + ret = ddrphy_phyinit_trackreg(TDBYTE | c_addr | u_addr | + CSR_RXENDLYTG1_ADDR); + if (ret != 0) { + return ret; + } +#endif /* STM32MP_LPDDR4_TYPE */ + ret = ddrphy_phyinit_trackreg(TDBYTE | c_addr | u_addr | + CSR_TXDQSDLYTG0_ADDR); + if (ret != 0) { + return ret; + } +#if STM32MP_LPDDR4_TYPE + ret = ddrphy_phyinit_trackreg(TDBYTE | c_addr | u_addr | + CSR_TXDQSDLYTG1_ADDR); + if (ret != 0) { + return ret; + } +#endif /* STM32MP_LPDDR4_TYPE */ + ret = ddrphy_phyinit_trackreg(TDBYTE | c_addr | u_addr | + CSR_RXCLKDLYTG0_ADDR); + if (ret != 0) { + return ret; + } +#if STM32MP_LPDDR4_TYPE + ret = ddrphy_phyinit_trackreg(TDBYTE | c_addr | u_addr | + CSR_RXCLKDLYTG1_ADDR); + if (ret != 0) { + return ret; + } +#endif /* STM32MP_LPDDR4_TYPE */ + } + + for (lane = R_MIN; lane <= R_MAX; lane++) { + r_addr = lane << 8; + + ret = ddrphy_phyinit_trackreg(TDBYTE | c_addr | r_addr | + CSR_TXDQDLYTG0_ADDR); + if (ret != 0) { + return ret; + } +#if STM32MP_LPDDR4_TYPE + ret = ddrphy_phyinit_trackreg(TDBYTE | c_addr | r_addr | + CSR_TXDQDLYTG1_ADDR); + if (ret != 0) { + return ret; + } +#endif /* STM32MP_LPDDR4_TYPE */ + } + +#if STM32MP_LPDDR4_TYPE + ret = ddrphy_phyinit_trackreg(TDBYTE | c_addr | CSR_PPTDQSCNTINVTRNTG0_ADDR); + if (ret != 0) { + return ret; + } + ret = ddrphy_phyinit_trackreg(TDBYTE | c_addr | CSR_PPTDQSCNTINVTRNTG1_ADDR); + if (ret != 0) { + return ret; + } +#endif /* STM32MP_LPDDR4_TYPE */ + } + + /* PIE Registers */ + ret = ddrphy_phyinit_trackreg(TINITENG | CSR_SEQ0BGPR1_ADDR); + if (ret != 0) { + return ret; + } + + ret = ddrphy_phyinit_trackreg(TINITENG | CSR_SEQ0BGPR2_ADDR); + if (ret != 0) { + return ret; + } + + ret = ddrphy_phyinit_trackreg(TINITENG | CSR_SEQ0BGPR3_ADDR); + if (ret != 0) { + return ret; + } + + ret = ddrphy_phyinit_trackreg(TINITENG | CSR_SEQ0BGPR4_ADDR); + if (ret != 0) { + return ret; + } + + ret = ddrphy_phyinit_trackreg(TINITENG | CSR_SEQ0BGPR5_ADDR); + if (ret != 0) { + return ret; + } + + ret = ddrphy_phyinit_trackreg(TINITENG | CSR_SEQ0BGPR6_ADDR); + if (ret != 0) { + return ret; + } + + ret = ddrphy_phyinit_trackreg(TINITENG | CSR_SEQ0BGPR7_ADDR); + if (ret != 0) { + return ret; + } + + ret = ddrphy_phyinit_trackreg(TINITENG | CSR_SEQ0BGPR8_ADDR); + if (ret != 0) { + return ret; + } + + /* Master Registers */ + ret = ddrphy_phyinit_trackreg(TMASTER | CSR_DLLGAINCTL_ADDR); + if (ret != 0) { + return ret; + } + + ret = ddrphy_phyinit_trackreg(TMASTER | CSR_DLLLOCKPARAM_ADDR); + if (ret != 0) { + return ret; + } +#if STM32MP_LPDDR4_TYPE + ret = ddrphy_phyinit_trackreg(TMASTER | CSR_HWTMRL_ADDR); + if (ret != 0) { + return ret; + } + + /* INITENG Registers */ + ret = ddrphy_phyinit_trackreg(TINITENG | CSR_SEQ0BDISABLEFLAG6_ADDR); + if (ret != 0) { + return ret; + } +#endif /* STM32MP_LPDDR4_TYPE */ + + ret = ddrphy_phyinit_trackreg(TMASTER | CSR_HWTCAMODE_ADDR); + if (ret != 0) { + return ret; + } + +#if STM32MP_LPDDR4_TYPE + ret = ddrphy_phyinit_trackreg(TMASTER | CSR_HWTLPCSENA_ADDR); + if (ret != 0) { + return ret; + } + + ret = ddrphy_phyinit_trackreg(TMASTER | CSR_HWTLPCSENB_ADDR); + if (ret != 0) { + return ret; + } + + /* ACSM registers */ + ret = ddrphy_phyinit_trackreg(TACSM | CSR_ACSMCTRL13_ADDR); + if (ret != 0) { + return ret; + } + + ret = ddrphy_phyinit_trackreg(TACSM | CSR_ACSMCTRL23_ADDR); + if (ret != 0) { + return ret; + } +#endif /* STM32MP_LPDDR4_TYPE */ + + /* + * -------------------------------------------------------------------------- + * 2. Track any additional registers + * Register writes made using the any of the PhyInit functions are + * automatically tracked using the call to ddrphy_phyinit_trackreg() in + * mmio_write_16(). Use this section to track additional registers. + * -------------------------------------------------------------------------- + */ + + /* + * Example: + * ddrphy_phyinit_trackreg(<addr>); + */ + + /* + * -------------------------------------------------------------------------- + * 3. Prepare for register reads + * - Write the MicroContMuxSel CSR to 0x0 to allow access to the internal CSRs + * - Write the UcclkHclkEnables CSR to 0x3 to enable all the clocks so the reads + * can complete. + * -------------------------------------------------------------------------- + */ + mmio_write_16((uintptr_t)(DDRPHYC_BASE + (4U * (TAPBONLY | CSR_MICROCONTMUXSEL_ADDR))), + 0x0U); + mmio_write_16((uintptr_t)(DDRPHYC_BASE + (4U * (TDRTUB | CSR_UCCLKHCLKENABLES_ADDR))), + 0x3U); + + /* + * -------------------------------------------------------------------------- + * / 4. Read and save all the registers + * / - The list of registers differ depending on protocol and 1D training. + * -------------------------------------------------------------------------- + */ + + ret = ddrphy_phyinit_reginterface(SAVEREGS, 0U, 0U); + if (ret != 0) { + return ret; + } + + /* + * -------------------------------------------------------------------------- + * 5. Prepare for mission mode + * - Write the UcclkHclkEnables CSR to disable the appropriate clocks after all reads done. + * - Write the MicroContMuxSel CSR to 0x1 to isolate the internal CSRs during mission mode. + * -------------------------------------------------------------------------- + */ + + /* Disabling Ucclk (PMU) and Hclk (training hardware) */ + mmio_write_16((uintptr_t)(DDRPHYC_BASE + (4U * (TDRTUB | CSR_UCCLKHCLKENABLES_ADDR))), + 0x0U); + + mmio_write_16((uintptr_t)(DDRPHYC_BASE + (4U * (TAPBONLY | CSR_MICROCONTMUXSEL_ADDR))), + 0x1U); + + return 0; +} diff --git a/drivers/st/ddr/stm32mp1_ddr.c b/drivers/st/ddr/stm32mp1_ddr.c index 27d8b2c0..415d9e40 100644 --- a/drivers/st/ddr/stm32mp1_ddr.c +++ b/drivers/st/ddr/stm32mp1_ddr.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2018-2022, STMicroelectronics - All Rights Reserved + * Copyright (C) 2018-2024, STMicroelectronics - All Rights Reserved * * SPDX-License-Identifier: GPL-2.0+ OR BSD-3-Clause */ @@ -24,14 +24,12 @@ #define DDRCTL_REG(x, y) \ { \ - .name = #x, \ .offset = offsetof(struct stm32mp_ddrctl, x), \ .par_offset = offsetof(struct y, x) \ } #define DDRPHY_REG(x, y) \ { \ - .name = #x, \ .offset = offsetof(struct stm32mp_ddrphy, x), \ .par_offset = offsetof(struct y, x) \ } @@ -215,7 +213,7 @@ static void stm32mp1_ddrphy_idone_wait(struct stm32mp_ddrphy *phy) { uint32_t pgsr; int error = 0; - uint64_t timeout = timeout_init_us(TIMEOUT_US_1S); + uint64_t timeout = timeout_init_us(DDR_TIMEOUT_US_1S); do { pgsr = mmio_read_32((uintptr_t)&phy->pgsr); @@ -266,7 +264,7 @@ static void stm32mp1_ddrphy_init(struct stm32mp_ddrphy *phy, uint32_t pir) mmio_read_32((uintptr_t)&phy->pir)); /* Need to wait 10 configuration clock before start polling */ - udelay(10); + udelay(DDR_DELAY_10US); /* Wait DRAM initialization and Gate Training Evaluation complete */ stm32mp1_ddrphy_idone_wait(phy); @@ -279,7 +277,7 @@ static void stm32mp1_wait_operating_mode(struct stm32mp_ddr_priv *priv, uint32_t uint32_t stat; int break_loop = 0; - timeout = timeout_init_us(TIMEOUT_US_1S); + timeout = timeout_init_us(DDR_TIMEOUT_US_1S); for ( ; ; ) { uint32_t operating_mode; uint32_t selref_type; @@ -508,8 +506,7 @@ static void stm32mp1_ddr3_dll_off(struct stm32mp_ddr_priv *priv) #endif /* 12. Exit the self-refresh state by setting PWRCTL.selfref_sw = 0. */ - mmio_clrbits_32((uintptr_t)&priv->ctl->pwrctl, - DDRCTRL_PWRCTL_SELFREF_SW); + stm32mp_ddr_sw_selfref_exit(priv->ctl); stm32mp1_wait_operating_mode(priv, DDRCTRL_STAT_OPERATING_MODE_NORMAL); /* @@ -524,10 +521,7 @@ static void stm32mp1_ddr3_dll_off(struct stm32mp_ddr_priv *priv) */ /* 15. Write DBG1.dis_hif = 0 to re-enable reads and writes. */ - mmio_clrbits_32((uintptr_t)&priv->ctl->dbg1, DDRCTRL_DBG1_DIS_HIF); - VERBOSE("[0x%lx] dbg1 = 0x%x\n", - (uintptr_t)&priv->ctl->dbg1, - mmio_read_32((uintptr_t)&priv->ctl->dbg1)); + stm32mp_ddr_enable_host_interface(priv->ctl); } static void stm32mp1_refresh_disable(struct stm32mp_ddrctl *ctl) @@ -614,7 +608,7 @@ void stm32mp1_ddr_init(struct stm32mp_ddr_priv *priv, mmio_clrbits_32(priv->rcc + RCC_DDRITFCR, RCC_DDRITFCR_DDRCAPBRST); /* 1.4. wait 128 cycles to permit initialization of end logic */ - udelay(2); + udelay(DDR_DELAY_2US); /* For PCLK = 133MHz => 1 us is enough, 2 to allow lower frequency */ /* 1.5. initialize registers ddr_umctl2 */ diff --git a/drivers/st/ddr/stm32mp2_ddr.c b/drivers/st/ddr/stm32mp2_ddr.c new file mode 100644 index 00000000..5193d118 --- /dev/null +++ b/drivers/st/ddr/stm32mp2_ddr.c @@ -0,0 +1,479 @@ +/* + * Copyright (C) 2021-2024, STMicroelectronics - All Rights Reserved + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <errno.h> + +#include <common/debug.h> + +#include <ddrphy_phyinit.h> + +#include <drivers/delay_timer.h> +#include <drivers/st/stm32mp2_ddr_helpers.h> +#include <drivers/st/stm32mp2_ddr_regs.h> +#include <drivers/st/stm32mp_ddr.h> + +#include <lib/mmio.h> + +#include <platform_def.h> + +#define DDRDBG_FRAC_PLL_LOCK U(0x10) + +#define DDRCTL_REG(x, y, z) \ + { \ + .offset = offsetof(struct stm32mp_ddrctl, x), \ + .par_offset = offsetof(struct y, x), \ + .qd = z \ + } + +/* + * PARAMETERS: value get from device tree : + * size / order need to be aligned with binding + * modification NOT ALLOWED !!! + */ +#define DDRCTL_REG_REG_SIZE 48 /* st,ctl-reg */ +#define DDRCTL_REG_TIMING_SIZE 20 /* st,ctl-timing */ +#define DDRCTL_REG_MAP_SIZE 12 /* st,ctl-map */ +#if STM32MP_DDR_DUAL_AXI_PORT +#define DDRCTL_REG_PERF_SIZE 21 /* st,ctl-perf */ +#else /* !STM32MP_DDR_DUAL_AXI_PORT */ +#define DDRCTL_REG_PERF_SIZE 14 /* st,ctl-perf */ +#endif /* STM32MP_DDR_DUAL_AXI_PORT */ + +#define DDRPHY_REG_REG_SIZE 0 /* st,phy-reg */ +#define DDRPHY_REG_TIMING_SIZE 0 /* st,phy-timing */ + +#define DDRCTL_REG_REG(x, z) DDRCTL_REG(x, stm32mp2_ddrctrl_reg, z) +static const struct stm32mp_ddr_reg_desc ddr_reg[DDRCTL_REG_REG_SIZE] = { + DDRCTL_REG_REG(mstr, true), + DDRCTL_REG_REG(mrctrl0, false), + DDRCTL_REG_REG(mrctrl1, false), + DDRCTL_REG_REG(mrctrl2, false), + DDRCTL_REG_REG(derateen, true), + DDRCTL_REG_REG(derateint, false), + DDRCTL_REG_REG(deratectl, false), + DDRCTL_REG_REG(pwrctl, false), + DDRCTL_REG_REG(pwrtmg, true), + DDRCTL_REG_REG(hwlpctl, true), + DDRCTL_REG_REG(rfshctl0, false), + DDRCTL_REG_REG(rfshctl1, false), + DDRCTL_REG_REG(rfshctl3, true), + DDRCTL_REG_REG(crcparctl0, false), + DDRCTL_REG_REG(crcparctl1, false), + DDRCTL_REG_REG(init0, true), + DDRCTL_REG_REG(init1, false), + DDRCTL_REG_REG(init2, false), + DDRCTL_REG_REG(init3, true), + DDRCTL_REG_REG(init4, true), + DDRCTL_REG_REG(init5, false), + DDRCTL_REG_REG(init6, true), + DDRCTL_REG_REG(init7, true), + DDRCTL_REG_REG(dimmctl, false), + DDRCTL_REG_REG(rankctl, true), + DDRCTL_REG_REG(rankctl1, true), + DDRCTL_REG_REG(zqctl0, true), + DDRCTL_REG_REG(zqctl1, false), + DDRCTL_REG_REG(zqctl2, false), + DDRCTL_REG_REG(dfitmg0, true), + DDRCTL_REG_REG(dfitmg1, true), + DDRCTL_REG_REG(dfilpcfg0, false), + DDRCTL_REG_REG(dfilpcfg1, false), + DDRCTL_REG_REG(dfiupd0, true), + DDRCTL_REG_REG(dfiupd1, false), + DDRCTL_REG_REG(dfiupd2, false), + DDRCTL_REG_REG(dfimisc, true), + DDRCTL_REG_REG(dfitmg2, true), + DDRCTL_REG_REG(dfitmg3, false), + DDRCTL_REG_REG(dbictl, true), + DDRCTL_REG_REG(dfiphymstr, false), + DDRCTL_REG_REG(dbg0, false), + DDRCTL_REG_REG(dbg1, false), + DDRCTL_REG_REG(dbgcmd, false), + DDRCTL_REG_REG(swctl, false), /* forced qd value */ + DDRCTL_REG_REG(swctlstatic, false), + DDRCTL_REG_REG(poisoncfg, false), + DDRCTL_REG_REG(pccfg, false), +}; + +#define DDRCTL_REG_TIMING(x, z) DDRCTL_REG(x, stm32mp2_ddrctrl_timing, z) +static const struct stm32mp_ddr_reg_desc ddr_timing[DDRCTL_REG_TIMING_SIZE] = { + DDRCTL_REG_TIMING(rfshtmg, false), + DDRCTL_REG_TIMING(rfshtmg1, false), + DDRCTL_REG_TIMING(dramtmg0, true), + DDRCTL_REG_TIMING(dramtmg1, true), + DDRCTL_REG_TIMING(dramtmg2, true), + DDRCTL_REG_TIMING(dramtmg3, true), + DDRCTL_REG_TIMING(dramtmg4, true), + DDRCTL_REG_TIMING(dramtmg5, true), + DDRCTL_REG_TIMING(dramtmg6, true), + DDRCTL_REG_TIMING(dramtmg7, true), + DDRCTL_REG_TIMING(dramtmg8, true), + DDRCTL_REG_TIMING(dramtmg9, true), + DDRCTL_REG_TIMING(dramtmg10, true), + DDRCTL_REG_TIMING(dramtmg11, true), + DDRCTL_REG_TIMING(dramtmg12, true), + DDRCTL_REG_TIMING(dramtmg13, true), + DDRCTL_REG_TIMING(dramtmg14, true), + DDRCTL_REG_TIMING(dramtmg15, true), + DDRCTL_REG_TIMING(odtcfg, true), + DDRCTL_REG_TIMING(odtmap, false), +}; + +#define DDRCTL_REG_MAP(x) DDRCTL_REG(x, stm32mp2_ddrctrl_map, false) +static const struct stm32mp_ddr_reg_desc ddr_map[DDRCTL_REG_MAP_SIZE] = { + DDRCTL_REG_MAP(addrmap0), + DDRCTL_REG_MAP(addrmap1), + DDRCTL_REG_MAP(addrmap2), + DDRCTL_REG_MAP(addrmap3), + DDRCTL_REG_MAP(addrmap4), + DDRCTL_REG_MAP(addrmap5), + DDRCTL_REG_MAP(addrmap6), + DDRCTL_REG_MAP(addrmap7), + DDRCTL_REG_MAP(addrmap8), + DDRCTL_REG_MAP(addrmap9), + DDRCTL_REG_MAP(addrmap10), + DDRCTL_REG_MAP(addrmap11), +}; + +#define DDRCTL_REG_PERF(x, z) DDRCTL_REG(x, stm32mp2_ddrctrl_perf, z) +static const struct stm32mp_ddr_reg_desc ddr_perf[DDRCTL_REG_PERF_SIZE] = { + DDRCTL_REG_PERF(sched, true), + DDRCTL_REG_PERF(sched1, false), + DDRCTL_REG_PERF(perfhpr1, true), + DDRCTL_REG_PERF(perflpr1, true), + DDRCTL_REG_PERF(perfwr1, true), + DDRCTL_REG_PERF(sched3, false), + DDRCTL_REG_PERF(sched4, false), + DDRCTL_REG_PERF(pcfgr_0, false), + DDRCTL_REG_PERF(pcfgw_0, false), + DDRCTL_REG_PERF(pctrl_0, false), + DDRCTL_REG_PERF(pcfgqos0_0, true), + DDRCTL_REG_PERF(pcfgqos1_0, true), + DDRCTL_REG_PERF(pcfgwqos0_0, true), + DDRCTL_REG_PERF(pcfgwqos1_0, true), +#if STM32MP_DDR_DUAL_AXI_PORT + DDRCTL_REG_PERF(pcfgr_1, false), + DDRCTL_REG_PERF(pcfgw_1, false), + DDRCTL_REG_PERF(pctrl_1, false), + DDRCTL_REG_PERF(pcfgqos0_1, true), + DDRCTL_REG_PERF(pcfgqos1_1, true), + DDRCTL_REG_PERF(pcfgwqos0_1, true), + DDRCTL_REG_PERF(pcfgwqos1_1, true), +#endif /* STM32MP_DDR_DUAL_AXI_PORT */ +}; + +static const struct stm32mp_ddr_reg_desc ddrphy_reg[DDRPHY_REG_REG_SIZE] = {}; + +static const struct stm32mp_ddr_reg_desc ddrphy_timing[DDRPHY_REG_TIMING_SIZE] = {}; + +/* + * REGISTERS ARRAY: used to parse device tree and interactive mode + */ +static const struct stm32mp_ddr_reg_info ddr_registers[REG_TYPE_NB] __unused = { + [REG_REG] = { + .name = "static", + .desc = ddr_reg, + .size = DDRCTL_REG_REG_SIZE, + .base = DDR_BASE + }, + [REG_TIMING] = { + .name = "timing", + .desc = ddr_timing, + .size = DDRCTL_REG_TIMING_SIZE, + .base = DDR_BASE + }, + [REG_PERF] = { + .name = "perf", + .desc = ddr_perf, + .size = DDRCTL_REG_PERF_SIZE, + .base = DDR_BASE + }, + [REG_MAP] = { + .name = "map", + .desc = ddr_map, + .size = DDRCTL_REG_MAP_SIZE, + .base = DDR_BASE + }, + [REGPHY_REG] = { + .name = "static", + .desc = ddrphy_reg, + .size = DDRPHY_REG_REG_SIZE, + .base = DDRPHY_BASE + }, + [REGPHY_TIMING] = { + .name = "timing", + .desc = ddrphy_timing, + .size = DDRPHY_REG_TIMING_SIZE, + .base = DDRPHY_BASE + }, +}; + +static void ddr_reset(struct stm32mp_ddr_priv *priv) +{ + udelay(DDR_DELAY_1US); + + mmio_setbits_32(priv->rcc + RCC_DDRITFCFGR, RCC_DDRITFCFGR_DDRRST); + mmio_write_32(priv->rcc + RCC_DDRPHYCAPBCFGR, + RCC_DDRPHYCAPBCFGR_DDRPHYCAPBEN | RCC_DDRPHYCAPBCFGR_DDRPHYCAPBLPEN | + RCC_DDRPHYCAPBCFGR_DDRPHYCAPBRST); + mmio_write_32(priv->rcc + RCC_DDRCAPBCFGR, + RCC_DDRCAPBCFGR_DDRCAPBEN | RCC_DDRCAPBCFGR_DDRCAPBLPEN | + RCC_DDRCAPBCFGR_DDRCAPBRST); + mmio_write_32(priv->rcc + RCC_DDRCFGR, + RCC_DDRCFGR_DDRCFGEN | RCC_DDRCFGR_DDRCFGLPEN | RCC_DDRCFGR_DDRCFGRST); + + udelay(DDR_DELAY_1US); + + mmio_setbits_32(priv->rcc + RCC_DDRITFCFGR, RCC_DDRITFCFGR_DDRRST); + mmio_write_32(priv->rcc + RCC_DDRPHYCAPBCFGR, + RCC_DDRPHYCAPBCFGR_DDRPHYCAPBEN | RCC_DDRPHYCAPBCFGR_DDRPHYCAPBLPEN); + mmio_write_32(priv->rcc + RCC_DDRCAPBCFGR, + RCC_DDRCAPBCFGR_DDRCAPBEN | RCC_DDRCAPBCFGR_DDRCAPBLPEN); + mmio_write_32(priv->rcc + RCC_DDRCFGR, RCC_DDRCFGR_DDRCFGEN | RCC_DDRCFGR_DDRCFGLPEN); + + udelay(DDR_DELAY_1US); +} + +static void ddr_standby_reset(struct stm32mp_ddr_priv *priv) +{ + udelay(DDR_DELAY_1US); + + mmio_write_32(priv->rcc + RCC_DDRCPCFGR, + RCC_DDRCPCFGR_DDRCPEN | RCC_DDRCPCFGR_DDRCPLPEN | RCC_DDRCPCFGR_DDRCPRST); + mmio_setbits_32(priv->rcc + RCC_DDRITFCFGR, RCC_DDRITFCFGR_DDRRST); + mmio_write_32(priv->rcc + RCC_DDRPHYCAPBCFGR, + RCC_DDRPHYCAPBCFGR_DDRPHYCAPBEN | RCC_DDRPHYCAPBCFGR_DDRPHYCAPBLPEN | + RCC_DDRPHYCAPBCFGR_DDRPHYCAPBRST); + mmio_write_32(priv->rcc + RCC_DDRCAPBCFGR, + RCC_DDRCAPBCFGR_DDRCAPBEN | RCC_DDRCAPBCFGR_DDRCAPBLPEN | + RCC_DDRCAPBCFGR_DDRCAPBRST); + + mmio_clrbits_32(priv->rcc + RCC_DDRITFCFGR, RCC_DDRITFCFGR_DDRPHYDLP); + mmio_setbits_32(priv->rcc + RCC_DDRPHYCCFGR, RCC_DDRPHYCCFGR_DDRPHYCEN); + + udelay(DDR_DELAY_1US); +} + +static void ddr_standby_reset_release(struct stm32mp_ddr_priv *priv) +{ + udelay(DDR_DELAY_1US); + + mmio_write_32(priv->rcc + RCC_DDRCPCFGR, RCC_DDRCPCFGR_DDRCPEN | RCC_DDRCPCFGR_DDRCPLPEN); + mmio_clrbits_32(priv->rcc + RCC_DDRITFCFGR, RCC_DDRITFCFGR_DDRRST); + mmio_clrbits_32(priv->rcc + RCC_DDRPHYCAPBCFGR, RCC_DDRPHYCAPBCFGR_DDRPHYCAPBRST); + mmio_write_32(priv->rcc + RCC_DDRCFGR, RCC_DDRCFGR_DDRCFGEN | RCC_DDRCFGR_DDRCFGLPEN); + + udelay(DDR_DELAY_1US); +} + +static void ddr_sysconf_configuration(struct stm32mp_ddr_priv *priv, + struct stm32mp_ddr_config *config) +{ + mmio_write_32(stm32_ddrdbg_get_base() + DDRDBG_LP_DISABLE, + DDRDBG_LP_DISABLE_LPI_XPI_DISABLE | DDRDBG_LP_DISABLE_LPI_DDRC_DISABLE); + + mmio_write_32(stm32_ddrdbg_get_base() + DDRDBG_BYPASS_PCLKEN, + (uint32_t)config->uib.pllbypass); + + mmio_write_32(priv->rcc + RCC_DDRPHYCCFGR, RCC_DDRPHYCCFGR_DDRPHYCEN); + mmio_setbits_32(priv->rcc + RCC_DDRITFCFGR, RCC_DDRITFCFGR_DDRRST); + + udelay(DDR_DELAY_1US); +} + +static void set_dfi_init_complete_en(struct stm32mp_ddrctl *ctl, bool phy_init_done) +{ + /* + * Manage quasi-dynamic registers modification + * dfimisc.dfi_init_complete_en : Group 3 + */ + stm32mp_ddr_set_qd3_update_conditions(ctl); + + udelay(DDR_DELAY_1US); + + if (phy_init_done) { + /* Indicates to controller that PHY has completed initialization */ + mmio_setbits_32((uintptr_t)&ctl->dfimisc, DDRCTRL_DFIMISC_DFI_INIT_COMPLETE_EN); + } else { + /* PHY not initialized yet, wait for completion */ + mmio_clrbits_32((uintptr_t)&ctl->dfimisc, DDRCTRL_DFIMISC_DFI_INIT_COMPLETE_EN); + } + + udelay(DDR_DELAY_1US); + + stm32mp_ddr_unset_qd3_update_conditions(ctl); + +} + +static void disable_refresh(struct stm32mp_ddrctl *ctl) +{ + mmio_setbits_32((uintptr_t)&ctl->rfshctl3, DDRCTRL_RFSHCTL3_DIS_AUTO_REFRESH); + + stm32mp_ddr_wait_refresh_update_done_ack(ctl); + + udelay(DDR_DELAY_1US); + + mmio_clrbits_32((uintptr_t)&ctl->pwrctl, + DDRCTRL_PWRCTL_POWERDOWN_EN | DDRCTRL_PWRCTL_SELFREF_EN); + + udelay(DDR_DELAY_1US); + + set_dfi_init_complete_en(ctl, false); +} + +static void restore_refresh(struct stm32mp_ddrctl *ctl, uint32_t rfshctl3, uint32_t pwrctl) +{ + if ((rfshctl3 & DDRCTRL_RFSHCTL3_DIS_AUTO_REFRESH) == 0U) { + mmio_clrbits_32((uintptr_t)&ctl->rfshctl3, DDRCTRL_RFSHCTL3_DIS_AUTO_REFRESH); + + stm32mp_ddr_wait_refresh_update_done_ack(ctl); + + udelay(DDR_DELAY_1US); + } + + if ((pwrctl & DDRCTRL_PWRCTL_SELFREF_SW) != 0U) { + mmio_clrbits_32((uintptr_t)&ctl->pwrctl, DDRCTRL_PWRCTL_SELFREF_SW); + + udelay(DDR_DELAY_1US); + } + + if ((pwrctl & DDRCTRL_PWRCTL_POWERDOWN_EN) != 0U) { + mmio_setbits_32((uintptr_t)&ctl->pwrctl, DDRCTRL_PWRCTL_POWERDOWN_EN); + + udelay(DDR_DELAY_1US); + } + + if ((pwrctl & DDRCTRL_PWRCTL_SELFREF_EN) != 0U) { + mmio_setbits_32((uintptr_t)&ctl->pwrctl, DDRCTRL_PWRCTL_SELFREF_EN); + + udelay(DDR_DELAY_1US); + } + + set_dfi_init_complete_en(ctl, true); +} + +void stm32mp2_ddr_init(struct stm32mp_ddr_priv *priv, + struct stm32mp_ddr_config *config) +{ + int ret = -EINVAL; + uint32_t ddr_retdis; + enum ddr_type ddr_type; + + if ((config->c_reg.mstr & DDRCTRL_MSTR_DDR3) != 0U) { + ddr_type = STM32MP_DDR3; + } else if ((config->c_reg.mstr & DDRCTRL_MSTR_DDR4) != 0U) { + ddr_type = STM32MP_DDR4; + } else if ((config->c_reg.mstr & DDRCTRL_MSTR_LPDDR4) != 0U) { + ddr_type = STM32MP_LPDDR4; + } else { + ERROR("DDR type not supported\n"); + panic(); + } + + VERBOSE("name = %s\n", config->info.name); + VERBOSE("speed = %u kHz\n", config->info.speed); + VERBOSE("size = 0x%zx\n", config->info.size); + if (config->self_refresh) { + VERBOSE("sel-refresh exit (zdata = 0x%x)\n", config->zdata); + } + + /* Check DDR PHY pads retention */ + ddr_retdis = mmio_read_32(priv->pwr + PWR_CR11) & PWR_CR11_DDRRETDIS; + if (config->self_refresh) { + if (ddr_retdis == PWR_CR11_DDRRETDIS) { + VERBOSE("self-refresh aborted: no retention\n"); + config->self_refresh = false; + } + } + + if (config->self_refresh) { + ddr_standby_reset(priv); + + VERBOSE("disable DDR PHY retention\n"); + mmio_setbits_32(priv->pwr + PWR_CR11, PWR_CR11_DDRRETDIS); + + udelay(DDR_DELAY_1US); + + mmio_clrbits_32(priv->rcc + RCC_DDRCAPBCFGR, RCC_DDRCAPBCFGR_DDRCAPBRST); + + udelay(DDR_DELAY_1US); + + } else { + if (stm32mp_board_ddr_power_init(ddr_type) != 0) { + ERROR("DDR power init failed\n"); + panic(); + } + + VERBOSE("disable DDR PHY retention\n"); + mmio_setbits_32(priv->pwr + PWR_CR11, PWR_CR11_DDRRETDIS); + + ddr_reset(priv); + + ddr_sysconf_configuration(priv, config); + } + +#if STM32MP_LPDDR4_TYPE + /* + * Enable PWRCTL.SELFREF_SW to ensure correct setting of PWRCTL.LPDDR4_SR_ALLOWED. + * Later disabled in restore_refresh(). + */ + config->c_reg.pwrctl |= DDRCTRL_PWRCTL_SELFREF_SW; +#endif /* STM32MP_LPDDR4_TYPE */ + + stm32mp_ddr_set_reg(priv, REG_REG, &config->c_reg, ddr_registers); + stm32mp_ddr_set_reg(priv, REG_TIMING, &config->c_timing, ddr_registers); + stm32mp_ddr_set_reg(priv, REG_MAP, &config->c_map, ddr_registers); + stm32mp_ddr_set_reg(priv, REG_PERF, &config->c_perf, ddr_registers); + + if (!config->self_refresh) { + /* DDR core and PHY reset de-assert */ + mmio_clrbits_32(priv->rcc + RCC_DDRITFCFGR, RCC_DDRITFCFGR_DDRRST); + + disable_refresh(priv->ctl); + } + + if (config->self_refresh) { + ddr_standby_reset_release(priv); + + /* Initialize DDR by skipping training and disabling result saving */ + ret = ddrphy_phyinit_sequence(config, true, false); + + if (ret == 0) { + ret = ddrphy_phyinit_restore_sequence(); + } + + /* Poll on ddrphy_initeng0_phyinlpx.phyinlp3 = 0 */ + ddr_wait_lp3_mode(false); + } else { + /* Initialize DDR including training and result saving */ + ret = ddrphy_phyinit_sequence(config, false, true); + } + + if (ret != 0) { + ERROR("DDR PHY init: Error %d\n", ret); + panic(); + } + + ddr_activate_controller(priv->ctl, false); + + if (config->self_refresh) { + struct stm32mp_ddrctl *ctl = priv->ctl; + + /* SW self refresh exit prequested */ + mmio_clrbits_32((uintptr_t)&ctl->pwrctl, DDRCTRL_PWRCTL_SELFREF_SW); + + if (ddr_sr_exit_loop() != 0) { + ERROR("DDR Standby exit error\n"); + panic(); + } + + /* Re-enable DFI low-power interface */ + mmio_clrbits_32((uintptr_t)&ctl->dfilpcfg0, DDRCTRL_DFILPCFG0_DFI_LP_EN_SR); + } else { + restore_refresh(priv->ctl, config->c_reg.rfshctl3, config->c_reg.pwrctl); + } + + stm32mp_ddr_enable_axi_port(priv->ctl); +} diff --git a/drivers/st/ddr/stm32mp2_ddr_helpers.c b/drivers/st/ddr/stm32mp2_ddr_helpers.c new file mode 100644 index 00000000..a2a40822 --- /dev/null +++ b/drivers/st/ddr/stm32mp2_ddr_helpers.c @@ -0,0 +1,527 @@ +/* + * Copyright (c) 2024, STMicroelectronics - All Rights Reserved + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <errno.h> + +#include <arch_helpers.h> +#include <common/debug.h> + +#include <drivers/delay_timer.h> +#include <drivers/st/stm32mp2_ddr.h> +#include <drivers/st/stm32mp2_ddr_helpers.h> +#include <drivers/st/stm32mp2_ddr_regs.h> +#include <drivers/st/stm32mp_ddr.h> + +#include <lib/mmio.h> + +#include <platform_def.h> + +/* HW idle period (unit: Multiples of 32 DFI clock cycles) */ +#define HW_IDLE_PERIOD 0x3U + +static enum stm32mp2_ddr_sr_mode saved_ddr_sr_mode; + +#pragma weak stm32_ddrdbg_get_base +uintptr_t stm32_ddrdbg_get_base(void) +{ + return 0U; +} + +static void set_qd1_qd3_update_conditions(struct stm32mp_ddrctl *ctl) +{ + mmio_setbits_32((uintptr_t)&ctl->dbg1, DDRCTRL_DBG1_DIS_DQ); + + stm32mp_ddr_set_qd3_update_conditions(ctl); +} + +static void unset_qd1_qd3_update_conditions(struct stm32mp_ddrctl *ctl) +{ + stm32mp_ddr_unset_qd3_update_conditions(ctl); + + mmio_clrbits_32((uintptr_t)&ctl->dbg1, DDRCTRL_DBG1_DIS_DQ); +} + +static void wait_dfi_init_complete(struct stm32mp_ddrctl *ctl) +{ + uint64_t timeout; + uint32_t dfistat; + + timeout = timeout_init_us(DDR_TIMEOUT_US_1S); + do { + dfistat = mmio_read_32((uintptr_t)&ctl->dfistat); + VERBOSE("[0x%lx] dfistat = 0x%x ", (uintptr_t)&ctl->dfistat, dfistat); + + if (timeout_elapsed(timeout)) { + panic(); + } + } while ((dfistat & DDRCTRL_DFISTAT_DFI_INIT_COMPLETE) == 0U); + + VERBOSE("[0x%lx] dfistat = 0x%x\n", (uintptr_t)&ctl->dfistat, dfistat); +} + +static void disable_dfi_low_power_interface(struct stm32mp_ddrctl *ctl) +{ + uint64_t timeout; + uint32_t dfistat; + uint32_t stat; + + mmio_clrbits_32((uintptr_t)&ctl->dfilpcfg0, DDRCTRL_DFILPCFG0_DFI_LP_EN_SR); + + timeout = timeout_init_us(DDR_TIMEOUT_US_1S); + do { + dfistat = mmio_read_32((uintptr_t)&ctl->dfistat); + stat = mmio_read_32((uintptr_t)&ctl->stat); + VERBOSE("[0x%lx] dfistat = 0x%x ", (uintptr_t)&ctl->dfistat, dfistat); + VERBOSE("[0x%lx] stat = 0x%x ", (uintptr_t)&ctl->stat, stat); + + if (timeout_elapsed(timeout)) { + panic(); + } + } while (((dfistat & DDRCTRL_DFISTAT_DFI_LP_ACK) != 0U) || + ((stat & DDRCTRL_STAT_OPERATING_MODE_MASK) == DDRCTRL_STAT_OPERATING_MODE_SR)); + + VERBOSE("[0x%lx] dfistat = 0x%x\n", (uintptr_t)&ctl->dfistat, dfistat); + VERBOSE("[0x%lx] stat = 0x%x\n", (uintptr_t)&ctl->stat, stat); +} + +void ddr_activate_controller(struct stm32mp_ddrctl *ctl, bool sr_entry) +{ + /* + * Manage quasi-dynamic registers modification + * dfimisc.dfi_frequency : Group 1 + * dfimisc.dfi_init_complete_en and dfimisc.dfi_init_start : Group 3 + */ + set_qd1_qd3_update_conditions(ctl); + + if (sr_entry) { + mmio_setbits_32((uintptr_t)&ctl->dfimisc, DDRCTRL_DFIMISC_DFI_FREQUENCY); + } else { + mmio_clrbits_32((uintptr_t)&ctl->dfimisc, DDRCTRL_DFIMISC_DFI_FREQUENCY); + } + + mmio_setbits_32((uintptr_t)&ctl->dfimisc, DDRCTRL_DFIMISC_DFI_INIT_START); + mmio_clrbits_32((uintptr_t)&ctl->dfimisc, DDRCTRL_DFIMISC_DFI_INIT_START); + + wait_dfi_init_complete(ctl); + + udelay(DDR_DELAY_1US); + + if (sr_entry) { + mmio_clrbits_32((uintptr_t)&ctl->dfimisc, DDRCTRL_DFIMISC_DFI_INIT_COMPLETE_EN); + } else { + mmio_setbits_32((uintptr_t)&ctl->dfimisc, DDRCTRL_DFIMISC_DFI_INIT_COMPLETE_EN); + } + + udelay(DDR_DELAY_1US); + + unset_qd1_qd3_update_conditions(ctl); +} + +#if STM32MP_LPDDR4_TYPE +static void disable_phy_ddc(void) +{ + /* Enable APB access to internal CSR registers */ + mmio_write_32(stm32mp_ddrphyc_base() + DDRPHY_APBONLY0_MICROCONTMUXSEL, 0U); + mmio_write_32(stm32mp_ddrphyc_base() + DDRPHY_DRTUB0_UCCLKHCLKENABLES, + DDRPHY_DRTUB0_UCCLKHCLKENABLES_UCCLKEN | + DDRPHY_DRTUB0_UCCLKHCLKENABLES_HCLKEN); + + /* Disable DRAM drift compensation */ + mmio_write_32(stm32mp_ddrphyc_base() + DDRPHY_INITENG0_P0_SEQ0BDISABLEFLAG6, 0xFFFFU); + + /* Disable APB access to internal CSR registers */ + mmio_write_32(stm32mp_ddrphyc_base() + DDRPHY_DRTUB0_UCCLKHCLKENABLES, + DDRPHY_DRTUB0_UCCLKHCLKENABLES_HCLKEN); + mmio_write_32(stm32mp_ddrphyc_base() + DDRPHY_APBONLY0_MICROCONTMUXSEL, + DDRPHY_APBONLY0_MICROCONTMUXSEL_MICROCONTMUXSEL); +} +#endif /* STM32MP_LPDDR4_TYPE */ + +void ddr_wait_lp3_mode(bool sr_entry) +{ + uint64_t timeout; + bool repeat_loop = false; + + /* Enable APB access to internal CSR registers */ + mmio_write_32(stm32mp_ddrphyc_base() + DDRPHY_APBONLY0_MICROCONTMUXSEL, 0U); + mmio_write_32(stm32mp_ddrphyc_base() + DDRPHY_DRTUB0_UCCLKHCLKENABLES, + DDRPHY_DRTUB0_UCCLKHCLKENABLES_UCCLKEN | + DDRPHY_DRTUB0_UCCLKHCLKENABLES_HCLKEN); + + timeout = timeout_init_us(DDR_TIMEOUT_US_1S); + do { + uint16_t phyinlpx = mmio_read_32(stm32mp_ddrphyc_base() + + DDRPHY_INITENG0_P0_PHYINLPX); + + if (timeout_elapsed(timeout)) { + panic(); + } + + if (sr_entry) { + repeat_loop = (phyinlpx & DDRPHY_INITENG0_P0_PHYINLPX_PHYINLP3) == 0U; + } else { + repeat_loop = (phyinlpx & DDRPHY_INITENG0_P0_PHYINLPX_PHYINLP3) != 0U; + } + } while (repeat_loop); + + /* Disable APB access to internal CSR registers */ +#if STM32MP_DDR3_TYPE || STM32MP_DDR4_TYPE + mmio_write_32(stm32mp_ddrphyc_base() + DDRPHY_DRTUB0_UCCLKHCLKENABLES, 0U); +#else /* STM32MP_LPDDR4_TYPE */ + mmio_write_32(stm32mp_ddrphyc_base() + DDRPHY_DRTUB0_UCCLKHCLKENABLES, + DDRPHY_DRTUB0_UCCLKHCLKENABLES_HCLKEN); +#endif /* STM32MP_DDR3_TYPE || STM32MP_DDR4_TYPE */ + mmio_write_32(stm32mp_ddrphyc_base() + DDRPHY_APBONLY0_MICROCONTMUXSEL, + DDRPHY_APBONLY0_MICROCONTMUXSEL_MICROCONTMUXSEL); +} + +static int sr_loop(bool is_entry) +{ + uint32_t type; + uint32_t state __maybe_unused; + uint64_t timeout = timeout_init_us(DDR_TIMEOUT_US_1S); + bool repeat_loop = false; + + /* + * Wait for DDRCTRL to be out of or back to "normal/mission mode". + * Consider also SRPD mode for LPDDR4 only. + */ + do { + type = mmio_read_32(stm32mp_ddrctrl_base() + DDRCTRL_STAT) & + DDRCTRL_STAT_SELFREF_TYPE_MASK; +#if STM32MP_LPDDR4_TYPE + state = mmio_read_32(stm32mp_ddrctrl_base() + DDRCTRL_STAT) & + DDRCTRL_STAT_SELFREF_STATE_MASK; +#endif /* STM32MP_LPDDR4_TYPE */ + + if (timeout_elapsed(timeout)) { + return -ETIMEDOUT; + } + + if (is_entry) { +#if STM32MP_LPDDR4_TYPE + repeat_loop = (type == 0x0U) || (state != DDRCTRL_STAT_SELFREF_STATE_SRPD); +#else /* !STM32MP_LPDDR4_TYPE */ + repeat_loop = (type == 0x0U); +#endif /* STM32MP_LPDDR4_TYPE */ + } else { +#if STM32MP_LPDDR4_TYPE + repeat_loop = (type != 0x0U) || (state != 0x0U); +#else /* !STM32MP_LPDDR4_TYPE */ + repeat_loop = (type != 0x0U); +#endif /* STM32MP_LPDDR4_TYPE */ + } + } while (repeat_loop); + + return 0; +} + +static int sr_entry_loop(void) +{ + return sr_loop(true); +} + +int ddr_sr_exit_loop(void) +{ + return sr_loop(false); +} + +static int sr_ssr_set(void) +{ + uintptr_t ddrctrl_base = stm32mp_ddrctrl_base(); + + /* + * Disable Clock disable with LP modes + * (used in RUN mode for LPDDR2 with specific timing). + */ + mmio_clrbits_32(ddrctrl_base + DDRCTRL_PWRCTL, DDRCTRL_PWRCTL_EN_DFI_DRAM_CLK_DISABLE); + + /* Disable automatic Self-Refresh mode */ + mmio_clrbits_32(ddrctrl_base + DDRCTRL_PWRCTL, DDRCTRL_PWRCTL_SELFREF_EN); + + mmio_write_32(stm32_ddrdbg_get_base() + DDRDBG_LP_DISABLE, + DDRDBG_LP_DISABLE_LPI_XPI_DISABLE | DDRDBG_LP_DISABLE_LPI_DDRC_DISABLE); + + return 0; +} + +static int sr_ssr_entry(bool standby) +{ + uintptr_t ddrctrl_base = stm32mp_ddrctrl_base(); + uintptr_t rcc_base = stm32mp_rcc_base(); + + if (stm32mp_ddr_disable_axi_port((struct stm32mp_ddrctl *)ddrctrl_base) != 0) { + panic(); + } + +#if STM32MP_LPDDR4_TYPE + if (standby) { + /* Disable DRAM drift compensation */ + disable_phy_ddc(); + } +#endif /* STM32MP_LPDDR4_TYPE */ + + disable_dfi_low_power_interface((struct stm32mp_ddrctl *)ddrctrl_base); + + /* SW self refresh entry prequested */ + mmio_setbits_32(ddrctrl_base + DDRCTRL_PWRCTL, DDRCTRL_PWRCTL_SELFREF_SW); +#if STM32MP_LPDDR4_TYPE + mmio_clrbits_32(ddrctrl_base + DDRCTRL_PWRCTL, DDRCTRL_PWRCTL_STAY_IN_SELFREF); +#endif /* STM32MP_LPDDR4_TYPE */ + + if (sr_entry_loop() != 0) { + return -1; + } + + ddr_activate_controller((struct stm32mp_ddrctl *)ddrctrl_base, true); + + /* Poll on ddrphy_initeng0_phyinlpx.phyinlp3 = 1 */ + ddr_wait_lp3_mode(true); + + if (standby) { + mmio_clrbits_32(stm32mp_pwr_base() + PWR_CR11, PWR_CR11_DDRRETDIS); + } + + mmio_clrsetbits_32(rcc_base + RCC_DDRCPCFGR, RCC_DDRCPCFGR_DDRCPLPEN, + RCC_DDRCPCFGR_DDRCPEN); + mmio_setbits_32(rcc_base + RCC_DDRPHYCCFGR, RCC_DDRPHYCCFGR_DDRPHYCEN); + mmio_setbits_32(rcc_base + RCC_DDRITFCFGR, RCC_DDRITFCFGR_DDRPHYDLP); + + return 0; +} + +static int sr_ssr_exit(void) +{ + uintptr_t ddrctrl_base = stm32mp_ddrctrl_base(); + uintptr_t rcc_base = stm32mp_rcc_base(); + + mmio_setbits_32(rcc_base + RCC_DDRCPCFGR, + RCC_DDRCPCFGR_DDRCPLPEN | RCC_DDRCPCFGR_DDRCPEN); + mmio_clrbits_32(rcc_base + RCC_DDRITFCFGR, RCC_DDRITFCFGR_DDRPHYDLP); + mmio_setbits_32(rcc_base + RCC_DDRPHYCCFGR, RCC_DDRPHYCCFGR_DDRPHYCEN); + + udelay(DDR_DELAY_1US); + + ddr_activate_controller((struct stm32mp_ddrctl *)ddrctrl_base, false); + + /* Poll on ddrphy_initeng0_phyinlpx.phyinlp3 = 0 */ + ddr_wait_lp3_mode(false); + + /* SW self refresh exit prequested */ + mmio_clrbits_32(ddrctrl_base + DDRCTRL_PWRCTL, DDRCTRL_PWRCTL_SELFREF_SW); + + if (ddr_sr_exit_loop() != 0) { + return -1; + } + + /* Re-enable DFI low-power interface */ + mmio_setbits_32(ddrctrl_base + DDRCTRL_DFILPCFG0, DDRCTRL_DFILPCFG0_DFI_LP_EN_SR); + + stm32mp_ddr_enable_axi_port((struct stm32mp_ddrctl *)ddrctrl_base); + + return 0; +} + +static int sr_hsr_set(void) +{ + uintptr_t ddrctrl_base = stm32mp_ddrctrl_base(); + + mmio_clrsetbits_32(stm32mp_rcc_base() + RCC_DDRITFCFGR, + RCC_DDRITFCFGR_DDRCKMOD_MASK, RCC_DDRITFCFGR_DDRCKMOD_HSR); + + /* + * manage quasi-dynamic registers modification + * hwlpctl.hw_lp_en : Group 2 + */ + if (stm32mp_ddr_sw_selfref_entry((struct stm32mp_ddrctl *)ddrctrl_base) != 0) { + panic(); + } + stm32mp_ddr_start_sw_done((struct stm32mp_ddrctl *)ddrctrl_base); + + mmio_write_32(ddrctrl_base + DDRCTRL_HWLPCTL, + DDRCTRL_HWLPCTL_HW_LP_EN | DDRCTRL_HWLPCTL_HW_LP_EXIT_IDLE_EN | + (HW_IDLE_PERIOD << DDRCTRL_HWLPCTL_HW_LP_IDLE_X32_SHIFT)); + + stm32mp_ddr_wait_sw_done_ack((struct stm32mp_ddrctl *)ddrctrl_base); + stm32mp_ddr_sw_selfref_exit((struct stm32mp_ddrctl *)ddrctrl_base); + + return 0; +} + +static int sr_hsr_entry(void) +{ + mmio_write_32(stm32mp_rcc_base() + RCC_DDRCPCFGR, RCC_DDRCPCFGR_DDRCPLPEN); + + return sr_entry_loop(); /* read_data should be equal to 0x223 */ +} + +static int sr_hsr_exit(void) +{ + mmio_write_32(stm32mp_rcc_base() + RCC_DDRCPCFGR, + RCC_DDRCPCFGR_DDRCPLPEN | RCC_DDRCPCFGR_DDRCPEN); + + /* TODO: check if ddr_sr_exit_loop() is needed here */ + + return 0; +} + +static int sr_asr_set(void) +{ + mmio_write_32(stm32_ddrdbg_get_base() + DDRDBG_LP_DISABLE, 0U); + + return 0; +} + +static int sr_asr_entry(void) +{ + /* + * Automatically enter into self refresh when there is no ddr traffic + * for the delay programmed into SYSCONF_DDRC_AUTO_SR_DELAY register. + * Default value is 0x20 (unit: Multiples of 32 DFI clock cycles). + */ + return sr_entry_loop(); +} + +static int sr_asr_exit(void) +{ + return ddr_sr_exit_loop(); +} + +uint32_t ddr_get_io_calibration_val(void) +{ + /* TODO create related service */ + + return 0U; +} + +int ddr_sr_entry(bool standby) +{ + int ret = -EINVAL; + + switch (saved_ddr_sr_mode) { + case DDR_SSR_MODE: + ret = sr_ssr_entry(standby); + break; + case DDR_HSR_MODE: + ret = sr_hsr_entry(); + break; + case DDR_ASR_MODE: + ret = sr_asr_entry(); + break; + default: + break; + } + + return ret; +} + +int ddr_sr_exit(void) +{ + int ret = -EINVAL; + + switch (saved_ddr_sr_mode) { + case DDR_SSR_MODE: + ret = sr_ssr_exit(); + break; + case DDR_HSR_MODE: + ret = sr_hsr_exit(); + break; + case DDR_ASR_MODE: + ret = sr_asr_exit(); + break; + default: + break; + } + + return ret; +} + +enum stm32mp2_ddr_sr_mode ddr_read_sr_mode(void) +{ + uint32_t pwrctl = mmio_read_32(stm32mp_ddrctrl_base() + DDRCTRL_PWRCTL); + enum stm32mp2_ddr_sr_mode mode = DDR_SR_MODE_INVALID; + + switch (pwrctl & (DDRCTRL_PWRCTL_EN_DFI_DRAM_CLK_DISABLE | + DDRCTRL_PWRCTL_SELFREF_EN)) { + case 0U: + mode = DDR_SSR_MODE; + break; + case DDRCTRL_PWRCTL_EN_DFI_DRAM_CLK_DISABLE: + mode = DDR_HSR_MODE; + break; + case DDRCTRL_PWRCTL_EN_DFI_DRAM_CLK_DISABLE | DDRCTRL_PWRCTL_SELFREF_EN: + mode = DDR_ASR_MODE; + break; + default: + break; + } + + return mode; +} + +void ddr_set_sr_mode(enum stm32mp2_ddr_sr_mode mode) +{ + int ret = -EINVAL; + + if (mode == saved_ddr_sr_mode) { + return; + } + + switch (mode) { + case DDR_SSR_MODE: + ret = sr_ssr_set(); + break; + case DDR_HSR_MODE: + ret = sr_hsr_set(); + break; + case DDR_ASR_MODE: + ret = sr_asr_set(); + break; + default: + break; + } + + if (ret != 0) { + ERROR("Unknown Self Refresh mode\n"); + panic(); + } + + saved_ddr_sr_mode = mode; +} + +void ddr_save_sr_mode(void) +{ + saved_ddr_sr_mode = ddr_read_sr_mode(); +} + +void ddr_restore_sr_mode(void) +{ + ddr_set_sr_mode(saved_ddr_sr_mode); +} + +void ddr_sub_system_clk_init(void) +{ + mmio_write_32(stm32mp_rcc_base() + RCC_DDRCPCFGR, + RCC_DDRCPCFGR_DDRCPEN | RCC_DDRCPCFGR_DDRCPLPEN); +} + +void ddr_sub_system_clk_off(void) +{ + uintptr_t rcc_base = stm32mp_rcc_base(); + + /* Clear DDR IO retention */ + mmio_clrbits_32(stm32mp_pwr_base() + PWR_CR11, PWR_CR11_DDRRETDIS); + + /* Reset DDR sub system */ + mmio_write_32(rcc_base + RCC_DDRCPCFGR, RCC_DDRCPCFGR_DDRCPRST); + mmio_write_32(rcc_base + RCC_DDRITFCFGR, RCC_DDRITFCFGR_DDRRST); + mmio_write_32(rcc_base + RCC_DDRPHYCAPBCFGR, RCC_DDRPHYCAPBCFGR_DDRPHYCAPBRST); + mmio_write_32(rcc_base + RCC_DDRCAPBCFGR, RCC_DDRCAPBCFGR_DDRCAPBRST); + + /* Deactivate clocks and PLL2 */ + mmio_clrbits_32(rcc_base + RCC_DDRPHYCCFGR, RCC_DDRPHYCCFGR_DDRPHYCEN); + mmio_clrbits_32(rcc_base + RCC_PLL2CFGR1, RCC_PLL2CFGR1_PLLEN); +} diff --git a/drivers/st/ddr/stm32mp2_ram.c b/drivers/st/ddr/stm32mp2_ram.c new file mode 100644 index 00000000..95f05e7b --- /dev/null +++ b/drivers/st/ddr/stm32mp2_ram.c @@ -0,0 +1,210 @@ +/* + * Copyright (C) 2021-2024, STMicroelectronics - All Rights Reserved + * + * SPDX-License-Identifier: GPL-2.0+ OR BSD-3-Clause + */ + +#include <errno.h> + +#include <arch_helpers.h> +#include <common/debug.h> +#include <common/fdt_wrappers.h> +#include <drivers/clk.h> +#include <drivers/st/stm32mp2_ddr.h> +#include <drivers/st/stm32mp2_ddr_helpers.h> +#include <drivers/st/stm32mp2_ram.h> +#include <drivers/st/stm32mp_ddr.h> +#include <drivers/st/stm32mp_ddr_test.h> +#include <drivers/st/stm32mp_ram.h> + +#include <lib/mmio.h> +#include <libfdt.h> + +#include <platform_def.h> + +static struct stm32mp_ddr_priv ddr_priv_data; +static bool ddr_self_refresh; + +static int ddr_dt_get_ui_param(void *fdt, int node, struct stm32mp_ddr_config *config) +{ + int ret; + uint32_t size; + + size = sizeof(struct user_input_basic) / sizeof(int); + ret = fdt_read_uint32_array(fdt, node, "st,phy-basic", size, (uint32_t *)&config->uib); + + VERBOSE("%s: %s[0x%x] = %d\n", __func__, "st,phy-basic", size, ret); + if (ret != 0) { + ERROR("%s: can't read %s, error=%d\n", __func__, "st,phy-basic", ret); + return -EINVAL; + } + + size = sizeof(struct user_input_advanced) / sizeof(int); + ret = fdt_read_uint32_array(fdt, node, "st,phy-advanced", size, (uint32_t *)&config->uia); + + VERBOSE("%s: %s[0x%x] = %d\n", __func__, "st,phy-advanced", size, ret); + if (ret != 0) { + ERROR("%s: can't read %s, error=%d\n", __func__, "st,phy-advanced", ret); + return -EINVAL; + } + + size = sizeof(struct user_input_mode_register) / sizeof(int); + ret = fdt_read_uint32_array(fdt, node, "st,phy-mr", size, (uint32_t *)&config->uim); + + VERBOSE("%s: %s[0x%x] = %d\n", __func__, "st,phy-mr", size, ret); + if (ret != 0) { + ERROR("%s: can't read %s, error=%d\n", __func__, "st,phy-mr", ret); + return -EINVAL; + } + + size = sizeof(struct user_input_swizzle) / sizeof(int); + ret = fdt_read_uint32_array(fdt, node, "st,phy-swizzle", size, (uint32_t *)&config->uis); + + VERBOSE("%s: %s[0x%x] = %d\n", __func__, "st,phy-swizzle", size, ret); + if (ret != 0) { + ERROR("%s: can't read %s, error=%d\n", __func__, "st,phy-swizzle", ret); + return -EINVAL; + } + + return 0; +} + +static int stm32mp2_ddr_setup(void) +{ + struct stm32mp_ddr_priv *priv = &ddr_priv_data; + int ret; + struct stm32mp_ddr_config config; + int node; + uintptr_t uret; + void *fdt; + + const struct stm32mp_ddr_param param[] = { + CTL_PARAM(reg), + CTL_PARAM(timing), + CTL_PARAM(map), + CTL_PARAM(perf) + }; + + if (fdt_get_address(&fdt) == 0) { + return -ENOENT; + } + + node = fdt_node_offset_by_compatible(fdt, -1, DT_DDR_COMPAT); + if (node < 0) { + ERROR("%s: can't read DDR node in DT\n", __func__); + return -EINVAL; + } + + ret = stm32mp_ddr_dt_get_info(fdt, node, &config.info); + if (ret < 0) { + return ret; + } + + ret = stm32mp_ddr_dt_get_param(fdt, node, param, ARRAY_SIZE(param), (uintptr_t)&config); + if (ret < 0) { + return ret; + } + + ret = ddr_dt_get_ui_param(fdt, node, &config); + if (ret < 0) { + return ret; + } + + config.self_refresh = false; + + if (stm32mp_is_wakeup_from_standby()) { + config.self_refresh = true; + } + + /* Map dynamically RETRAM area to save or restore PHY retention registers */ + if (stm32mp_map_retram() != 0) { + panic(); + } + + stm32mp2_ddr_init(priv, &config); + + /* Unmap RETRAM, no more used until next DDR initialization call */ + if (stm32mp_unmap_retram() != 0) { + panic(); + } + + priv->info.size = config.info.size; + + VERBOSE("%s : ram size(%lx, %lx)\n", __func__, priv->info.base, priv->info.size); + + if (stm32mp_map_ddr_non_cacheable() != 0) { + panic(); + } + + if (config.self_refresh) { + uret = stm32mp_ddr_test_rw_access(); + if (uret != 0UL) { + ERROR("DDR rw test: can't access memory @ 0x%lx\n", uret); + panic(); + } + + /* TODO Restore area overwritten by training */ + //stm32_restore_ddr_training_area(); + } else { + size_t retsize; + + uret = stm32mp_ddr_test_data_bus(); + if (uret != 0UL) { + ERROR("DDR data bus test: can't access memory @ 0x%lx\n", uret); + panic(); + } + + uret = stm32mp_ddr_test_addr_bus(config.info.size); + if (uret != 0UL) { + ERROR("DDR addr bus test: can't access memory @ 0x%lx\n", uret); + panic(); + } + + retsize = stm32mp_ddr_check_size(); + if (retsize < config.info.size) { + ERROR("DDR size: 0x%zx does not match DT config: 0x%zx\n", + retsize, config.info.size); + panic(); + } + + INFO("Memory size = 0x%zx (%zu MB)\n", retsize, retsize / (1024U * 1024U)); + } + + /* + * Initialization sequence has configured DDR registers with settings. + * The Self Refresh (SR) mode corresponding to these settings has now + * to be set. + */ + ddr_set_sr_mode(ddr_read_sr_mode()); + + if (stm32mp_unmap_ddr() != 0) { + panic(); + } + + /* Save DDR self_refresh state */ + ddr_self_refresh = config.self_refresh; + + return 0; +} + +bool stm32mp2_ddr_is_restored(void) +{ + return ddr_self_refresh; +} + +int stm32mp2_ddr_probe(void) +{ + struct stm32mp_ddr_priv *priv = &ddr_priv_data; + + VERBOSE("STM32MP DDR probe\n"); + + priv->ctl = (struct stm32mp_ddrctl *)stm32mp_ddrctrl_base(); + priv->phy = (struct stm32mp_ddrphy *)stm32mp_ddrphyc_base(); + priv->pwr = stm32mp_pwr_base(); + priv->rcc = stm32mp_rcc_base(); + + priv->info.base = STM32MP_DDR_BASE; + priv->info.size = 0; + + return stm32mp2_ddr_setup(); +} diff --git a/drivers/st/ddr/stm32mp_ddr.c b/drivers/st/ddr/stm32mp_ddr.c index 6776e3ba..98968d57 100644 --- a/drivers/st/ddr/stm32mp_ddr.c +++ b/drivers/st/ddr/stm32mp_ddr.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2022, STMicroelectronics - All Rights Reserved + * Copyright (C) 2022-2024, STMicroelectronics - All Rights Reserved * * SPDX-License-Identifier: BSD-3-Clause */ @@ -8,13 +8,15 @@ #include <drivers/delay_timer.h> #include <drivers/st/stm32mp_ddr.h> #include <drivers/st/stm32mp_ddrctrl_regs.h> -#include <drivers/st/stm32mp_pmic.h> #include <lib/mmio.h> #include <platform_def.h> #define INVALID_OFFSET 0xFFU +static bool axi_port_reenable_request; +static bool host_interface_reenable_request; + static uintptr_t get_base_addr(const struct stm32mp_ddr_priv *priv, enum stm32mp_ddr_base_type base) { if (base == DDRPHY_BASE) { @@ -38,12 +40,23 @@ void stm32mp_ddr_set_reg(const struct stm32mp_ddr_priv *priv, enum stm32mp_ddr_r uintptr_t ptr = base_addr + desc[i].offset; if (desc[i].par_offset == INVALID_OFFSET) { - ERROR("invalid parameter offset for %s", desc[i].name); + ERROR("invalid parameter offset for %s - index %u", + ddr_registers[type].name, i); panic(); } else { +#if !STM32MP13 && !STM32MP15 + if (desc[i].qd) { + stm32mp_ddr_start_sw_done(priv->ctl); + } +#endif value = *((uint32_t *)((uintptr_t)param + desc[i].par_offset)); mmio_write_32(ptr, value); +#if !STM32MP13 && !STM32MP15 + if (desc[i].qd) { + stm32mp_ddr_wait_sw_done_ack(priv->ctl); + } +#endif } } } @@ -66,7 +79,7 @@ void stm32mp_ddr_wait_sw_done_ack(struct stm32mp_ddrctl *ctl) VERBOSE("[0x%lx] swctl = 0x%x\n", (uintptr_t)&ctl->swctl, mmio_read_32((uintptr_t)&ctl->swctl)); - timeout = timeout_init_us(TIMEOUT_US_1S); + timeout = timeout_init_us(DDR_TIMEOUT_US_1S); do { swstat = mmio_read_32((uintptr_t)&ctl->swstat); VERBOSE("[0x%lx] swstat = 0x%x ", @@ -93,14 +106,194 @@ void stm32mp_ddr_enable_axi_port(struct stm32mp_ddrctl *ctl) VERBOSE("[0x%lx] pctrl_1 = 0x%x\n", (uintptr_t)&ctl->pctrl_1, mmio_read_32((uintptr_t)&ctl->pctrl_1)); #endif +} + +int stm32mp_ddr_disable_axi_port(struct stm32mp_ddrctl *ctl) +{ + uint64_t timeout; + uint32_t pstat; + + /* Disable uMCTL2 AXI port 0 */ + mmio_clrbits_32((uintptr_t)&ctl->pctrl_0, DDRCTRL_PCTRL_N_PORT_EN); + VERBOSE("[0x%lx] pctrl_0 = 0x%x\n", (uintptr_t)&ctl->pctrl_0, + mmio_read_32((uintptr_t)&ctl->pctrl_0)); + +#if STM32MP_DDR_DUAL_AXI_PORT + /* Disable uMCTL2 AXI port 1 */ + mmio_clrbits_32((uintptr_t)&ctl->pctrl_1, DDRCTRL_PCTRL_N_PORT_EN); + VERBOSE("[0x%lx] pctrl_1 = 0x%x\n", (uintptr_t)&ctl->pctrl_1, + mmio_read_32((uintptr_t)&ctl->pctrl_1)); +#endif + + /* + * Waits until all AXI ports are idle + * Poll PSTAT.rd_port_busy_n = 0 + * Poll PSTAT.wr_port_busy_n = 0 + */ + timeout = timeout_init_us(DDR_TIMEOUT_US_1S); + do { + pstat = mmio_read_32((uintptr_t)&ctl->pstat); + VERBOSE("[0x%lx] pstat = 0x%x ", + (uintptr_t)&ctl->pstat, pstat); + if (timeout_elapsed(timeout)) { + return -1; + } + } while (pstat != 0U); + + return 0; +} + +static bool ddr_is_axi_port_enabled(struct stm32mp_ddrctl *ctl) +{ + return (mmio_read_32((uintptr_t)&ctl->pctrl_0) & DDRCTRL_PCTRL_N_PORT_EN) != 0U; +} + +void stm32mp_ddr_enable_host_interface(struct stm32mp_ddrctl *ctl) +{ + mmio_clrbits_32((uintptr_t)&ctl->dbg1, DDRCTRL_DBG1_DIS_HIF); + VERBOSE("[0x%lx] dbg1 = 0x%x\n", + (uintptr_t)&ctl->dbg1, + mmio_read_32((uintptr_t)&ctl->dbg1)); +} + +void stm32mp_ddr_disable_host_interface(struct stm32mp_ddrctl *ctl) +{ + uint64_t timeout; + uint32_t dbgcam; + int count = 0; + + mmio_setbits_32((uintptr_t)&ctl->dbg1, DDRCTRL_DBG1_DIS_HIF); + VERBOSE("[0x%lx] dbg1 = 0x%x\n", + (uintptr_t)&ctl->dbg1, + mmio_read_32((uintptr_t)&ctl->dbg1)); + + /* + * Waits until all queues and pipelines are empty + * Poll DBGCAM.dbg_wr_q_empty = 1 + * Poll DBGCAM.dbg_rd_q_empty = 1 + * Poll DBGCAM.dbg_wr_data_pipeline_empty = 1 + * Poll DBGCAM.dbg_rd_data_pipeline_empty = 1 + * + * data_pipeline fields must be polled twice to ensure + * value propoagation, so count is added to loop condition. + */ + timeout = timeout_init_us(DDR_TIMEOUT_US_1S); + do { + dbgcam = mmio_read_32((uintptr_t)&ctl->dbgcam); + VERBOSE("[0x%lx] dbgcam = 0x%x ", + (uintptr_t)&ctl->dbgcam, dbgcam); + if (timeout_elapsed(timeout)) { + panic(); + } + count++; + } while (((dbgcam & DDRCTRL_DBG_Q_AND_DATA_PIPELINE_EMPTY) != + DDRCTRL_DBG_Q_AND_DATA_PIPELINE_EMPTY) || (count < 2)); +} + +static bool ddr_is_host_interface_enabled(struct stm32mp_ddrctl *ctl) +{ + return (mmio_read_32((uintptr_t)&ctl->dbg1) & DDRCTRL_DBG1_DIS_HIF) == 0U; +} + +int stm32mp_ddr_sw_selfref_entry(struct stm32mp_ddrctl *ctl) +{ + uint64_t timeout; + uint32_t stat; + uint32_t operating_mode; + uint32_t selref_type; + + mmio_setbits_32((uintptr_t)&ctl->pwrctl, DDRCTRL_PWRCTL_SELFREF_SW); + VERBOSE("[0x%lx] pwrctl = 0x%x\n", + (uintptr_t)&ctl->pwrctl, + mmio_read_32((uintptr_t)&ctl->pwrctl)); + + /* + * Wait operating mode change in self-refresh mode + * with STAT.operating_mode[1:0]==11. + * Ensure transition to self-refresh was due to software + * by checking also that STAT.selfref_type[1:0]=2. + */ + timeout = timeout_init_us(DDR_TIMEOUT_500US); + while (!timeout_elapsed(timeout)) { + stat = mmio_read_32((uintptr_t)&ctl->stat); + operating_mode = stat & DDRCTRL_STAT_OPERATING_MODE_MASK; + selref_type = stat & DDRCTRL_STAT_SELFREF_TYPE_MASK; + + if ((operating_mode == DDRCTRL_STAT_OPERATING_MODE_SR) && + (selref_type == DDRCTRL_STAT_SELFREF_TYPE_SR)) { + return 0; + } + } + return -1; } -int stm32mp_board_ddr_power_init(enum ddr_type ddr_type) +void stm32mp_ddr_sw_selfref_exit(struct stm32mp_ddrctl *ctl) { - if (dt_pmic_status() > 0) { - return pmic_ddr_power_init(ddr_type); + mmio_clrbits_32((uintptr_t)&ctl->pwrctl, DDRCTRL_PWRCTL_SELFREF_SW); + VERBOSE("[0x%lx] pwrctl = 0x%x\n", + (uintptr_t)&ctl->pwrctl, + mmio_read_32((uintptr_t)&ctl->pwrctl)); +} + +void stm32mp_ddr_set_qd3_update_conditions(struct stm32mp_ddrctl *ctl) +{ + if (ddr_is_axi_port_enabled(ctl)) { + if (stm32mp_ddr_disable_axi_port(ctl) != 0) { + panic(); + } + axi_port_reenable_request = true; } - return 0; + if (ddr_is_host_interface_enabled(ctl)) { + stm32mp_ddr_disable_host_interface(ctl); + host_interface_reenable_request = true; + } + + stm32mp_ddr_start_sw_done(ctl); +} + +void stm32mp_ddr_unset_qd3_update_conditions(struct stm32mp_ddrctl *ctl) +{ + stm32mp_ddr_wait_sw_done_ack(ctl); + + if (host_interface_reenable_request) { + stm32mp_ddr_enable_host_interface(ctl); + host_interface_reenable_request = false; + } + + if (axi_port_reenable_request) { + stm32mp_ddr_enable_axi_port(ctl); + axi_port_reenable_request = false; + } +} + +void stm32mp_ddr_wait_refresh_update_done_ack(struct stm32mp_ddrctl *ctl) +{ + uint64_t timeout; + uint32_t rfshctl3; + uint32_t refresh_update_level = DDRCTRL_RFSHCTL3_REFRESH_UPDATE_LEVEL; + + /* Toggle rfshctl3.refresh_update_level */ + rfshctl3 = mmio_read_32((uintptr_t)&ctl->rfshctl3); + if ((rfshctl3 & refresh_update_level) == refresh_update_level) { + mmio_setbits_32((uintptr_t)&ctl->rfshctl3, refresh_update_level); + } else { + mmio_clrbits_32((uintptr_t)&ctl->rfshctl3, refresh_update_level); + refresh_update_level = 0U; + } + + VERBOSE("[0x%lx] rfshctl3 = 0x%x\n", + (uintptr_t)&ctl->rfshctl3, mmio_read_32((uintptr_t)&ctl->rfshctl3)); + + timeout = timeout_init_us(DDR_TIMEOUT_US_1S); + do { + rfshctl3 = mmio_read_32((uintptr_t)&ctl->rfshctl3); + VERBOSE("[0x%lx] rfshctl3 = 0x%x ", (uintptr_t)&ctl->rfshctl3, rfshctl3); + if (timeout_elapsed(timeout)) { + panic(); + } + } while ((rfshctl3 & DDRCTRL_RFSHCTL3_REFRESH_UPDATE_LEVEL) != refresh_update_level); + + VERBOSE("[0x%lx] rfshctl3 = 0x%x\n", (uintptr_t)&ctl->rfshctl3, rfshctl3); } diff --git a/drivers/st/ddr/stm32mp_ddr_test.c b/drivers/st/ddr/stm32mp_ddr_test.c index 0f6aff1d..707a6ff0 100644 --- a/drivers/st/ddr/stm32mp_ddr_test.c +++ b/drivers/st/ddr/stm32mp_ddr_test.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2022-2023, STMicroelectronics - All Rights Reserved + * Copyright (C) 2022-2024, STMicroelectronics - All Rights Reserved * * SPDX-License-Identifier: BSD-3-Clause */ @@ -10,8 +10,31 @@ #include <platform_def.h> +#ifdef __aarch64__ +#define DDR_PATTERN 0xAAAAAAAAAAAAAAAAUL +#define DDR_ANTIPATTERN 0x5555555555555555UL +#else /* !__aarch64__ */ #define DDR_PATTERN 0xAAAAAAAAU #define DDR_ANTIPATTERN 0x55555555U +#endif /* __aarch64__ */ + +static void mmio_write_pattern(uintptr_t addr, u_register_t value) +{ +#ifdef __aarch64__ + mmio_write_64(addr, (uint64_t)value); +#else /* !__aarch64__ */ + mmio_write_32(addr, (uint32_t)value); +#endif /* __aarch64__ */ +} + +static u_register_t mmio_read_pattern(uintptr_t addr) +{ +#ifdef __aarch64__ + return (u_register_t)mmio_read_64(addr); +#else /* !__aarch64__ */ + return (u_register_t)mmio_read_32(addr); +#endif /* __aarch64__ */ +} /******************************************************************************* * This function tests a simple read/write access to the DDR. @@ -20,15 +43,15 @@ ******************************************************************************/ uintptr_t stm32mp_ddr_test_rw_access(void) { - uint32_t saved_value = mmio_read_32(STM32MP_DDR_BASE); + u_register_t saved_value = mmio_read_pattern(STM32MP_DDR_BASE); - mmio_write_32(STM32MP_DDR_BASE, DDR_PATTERN); + mmio_write_pattern(STM32MP_DDR_BASE, DDR_PATTERN); - if (mmio_read_32(STM32MP_DDR_BASE) != DDR_PATTERN) { + if (mmio_read_pattern(STM32MP_DDR_BASE) != DDR_PATTERN) { return STM32MP_DDR_BASE; } - mmio_write_32(STM32MP_DDR_BASE, saved_value); + mmio_write_pattern(STM32MP_DDR_BASE, saved_value); return 0UL; } @@ -43,12 +66,12 @@ uintptr_t stm32mp_ddr_test_rw_access(void) ******************************************************************************/ uintptr_t stm32mp_ddr_test_data_bus(void) { - uint32_t pattern; + u_register_t pattern; for (pattern = 1U; pattern != 0U; pattern <<= 1U) { - mmio_write_32(STM32MP_DDR_BASE, pattern); + mmio_write_pattern(STM32MP_DDR_BASE, pattern); - if (mmio_read_32(STM32MP_DDR_BASE) != pattern) { + if (mmio_read_pattern(STM32MP_DDR_BASE) != pattern) { return STM32MP_DDR_BASE; } } @@ -72,41 +95,41 @@ uintptr_t stm32mp_ddr_test_addr_bus(size_t size) size_t testoffset = 0U; /* Write the default pattern at each of the power-of-two offsets. */ - for (offset = sizeof(uint32_t); (offset & addressmask) != 0U; + for (offset = sizeof(u_register_t); (offset & addressmask) != 0U; offset <<= 1U) { - mmio_write_32(STM32MP_DDR_BASE + offset, DDR_PATTERN); + mmio_write_pattern(STM32MP_DDR_BASE + offset, DDR_PATTERN); } /* Check for address bits stuck high. */ - mmio_write_32(STM32MP_DDR_BASE + testoffset, DDR_ANTIPATTERN); + mmio_write_pattern(STM32MP_DDR_BASE + testoffset, DDR_ANTIPATTERN); - for (offset = sizeof(uint32_t); (offset & addressmask) != 0U; + for (offset = sizeof(u_register_t); (offset & addressmask) != 0U; offset <<= 1U) { - if (mmio_read_32(STM32MP_DDR_BASE + offset) != DDR_PATTERN) { + if (mmio_read_pattern(STM32MP_DDR_BASE + offset) != DDR_PATTERN) { return STM32MP_DDR_BASE + offset; } } - mmio_write_32(STM32MP_DDR_BASE + testoffset, DDR_PATTERN); + mmio_write_pattern(STM32MP_DDR_BASE + testoffset, DDR_PATTERN); /* Check for address bits stuck low or shorted. */ - for (testoffset = sizeof(uint32_t); (testoffset & addressmask) != 0U; + for (testoffset = sizeof(u_register_t); (testoffset & addressmask) != 0U; testoffset <<= 1U) { - mmio_write_32(STM32MP_DDR_BASE + testoffset, DDR_ANTIPATTERN); + mmio_write_pattern(STM32MP_DDR_BASE + testoffset, DDR_ANTIPATTERN); - if (mmio_read_32(STM32MP_DDR_BASE) != DDR_PATTERN) { + if (mmio_read_pattern(STM32MP_DDR_BASE) != DDR_PATTERN) { return STM32MP_DDR_BASE; } - for (offset = sizeof(uint32_t); (offset & addressmask) != 0U; - offset <<= 1) { - if ((mmio_read_32(STM32MP_DDR_BASE + offset) != DDR_PATTERN) && + for (offset = sizeof(u_register_t); (offset & addressmask) != 0U; + offset <<= 1U) { + if ((mmio_read_pattern(STM32MP_DDR_BASE + offset) != DDR_PATTERN) && (offset != testoffset)) { return STM32MP_DDR_BASE + offset; } } - mmio_write_32(STM32MP_DDR_BASE + testoffset, DDR_PATTERN); + mmio_write_pattern(STM32MP_DDR_BASE + testoffset, DDR_PATTERN); } return 0UL; @@ -121,15 +144,15 @@ uintptr_t stm32mp_ddr_test_addr_bus(size_t size) ******************************************************************************/ size_t stm32mp_ddr_check_size(void) { - size_t offset = sizeof(uint32_t); + size_t offset = sizeof(u_register_t); - mmio_write_32(STM32MP_DDR_BASE, DDR_PATTERN); + mmio_write_pattern(STM32MP_DDR_BASE, DDR_PATTERN); while (offset < STM32MP_DDR_MAX_SIZE) { - mmio_write_32(STM32MP_DDR_BASE + offset, DDR_ANTIPATTERN); + mmio_write_pattern(STM32MP_DDR_BASE + offset, DDR_ANTIPATTERN); dsb(); - if (mmio_read_32(STM32MP_DDR_BASE) != DDR_PATTERN) { + if (mmio_read_pattern(STM32MP_DDR_BASE) != DDR_PATTERN) { break; } diff --git a/drivers/st/gpio/stm32_gpio.c b/drivers/st/gpio/stm32_gpio.c index a4a64ca7..44d7c095 100644 --- a/drivers/st/gpio/stm32_gpio.c +++ b/drivers/st/gpio/stm32_gpio.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016-2022, STMicroelectronics - All Rights Reserved + * Copyright (c) 2016-2024, STMicroelectronics - All Rights Reserved * * SPDX-License-Identifier: BSD-3-Clause */ @@ -282,6 +282,7 @@ static void set_gpio(uint32_t bank, uint32_t pin, uint32_t mode, uint32_t type, clk_disable(clock); +#if STM32MP13 || STM32MP15 if (status == DT_SECURE) { stm32mp_register_secure_gpio(bank, pin); #if !IMAGE_BL2 @@ -294,6 +295,9 @@ static void set_gpio(uint32_t bank, uint32_t pin, uint32_t mode, uint32_t type, set_gpio_secure_cfg(bank, pin, false); #endif } +#else /* !STM32MP13 && !STM32MP15 */ + set_gpio_secure_cfg(bank, pin, true); +#endif /* STM32MP13 || STM32MP15 */ } void set_gpio_secure_cfg(uint32_t bank, uint32_t pin, bool secure) @@ -321,3 +325,74 @@ void set_gpio_reset_cfg(uint32_t bank, uint32_t pin) GPIO_ALTERNATE_(0), DT_DISABLED); set_gpio_secure_cfg(bank, pin, stm32_gpio_is_secure_at_reset(bank)); } + +void set_gpio_level(uint32_t bank, uint32_t pin, enum gpio_level level) +{ + uintptr_t base = stm32_get_gpio_bank_base(bank); + unsigned long clock = stm32_get_gpio_bank_clock(bank); + + assert(pin <= GPIO_PIN_MAX); + + clk_enable(clock); + + if (level == GPIO_LEVEL_HIGH) { + mmio_write_32(base + GPIO_BSRR_OFFSET, BIT(pin)); + } else { + mmio_write_32(base + GPIO_BSRR_OFFSET, BIT(pin + 16U)); + } + + VERBOSE("GPIO %u level set to 0x%x\n", bank, + mmio_read_32(base + GPIO_IDR_OFFSET)); + + clk_disable(clock); +} + +enum gpio_level get_gpio_level(uint32_t bank, uint32_t pin) +{ + uintptr_t base = stm32_get_gpio_bank_base(bank); + unsigned long clock = stm32_get_gpio_bank_clock(bank); + enum gpio_level level = GPIO_LEVEL_LOW; + + assert(pin <= GPIO_PIN_MAX); + + clk_enable(clock); + + if (mmio_read_32(base + GPIO_IDR_OFFSET) & BIT(pin)) { + level = GPIO_LEVEL_HIGH; + } + + VERBOSE("GPIO %u get level 0x%x\n", bank, + mmio_read_32(base + GPIO_IDR_OFFSET)); + + clk_disable(clock); + + return level; +} + +void set_gpio_config(uint32_t bank, uint32_t pin, uint32_t config, uint8_t status) +{ + uint32_t mode = GPIO_MODE_OUTPUT; + uint32_t od = 0U; + uint32_t pull = GPIO_NO_PULL; + + VERBOSE("GPIO %u:%u set config to 0x%x\n", bank, pin, config); + + if (config & GPIOF_DIR_IN) { + mode = GPIO_MODE_INPUT; + } + + if (config & GPIOF_OUT_INIT_HIGH) { + od = 1U; + } + + if (config & GPIOF_PULL_UP) { + pull |= GPIO_PULL_UP; + } + + if (config & GPIOF_PULL_DOWN) { + pull |= GPIO_PULL_DOWN; + } + + set_gpio(bank, pin, mode, GPIO_TYPE_PUSH_PULL, GPIO_SPEED_LOW, + pull, od, GPIO_ALTERNATE_(0), status); +} diff --git a/drivers/st/i2c/stm32_i2c.c b/drivers/st/i2c/stm32_i2c.c index bf6c3eee..32cecff6 100644 --- a/drivers/st/i2c/stm32_i2c.c +++ b/drivers/st/i2c/stm32_i2c.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016-2021, STMicroelectronics - All Rights Reserved + * Copyright (c) 2016-2024, STMicroelectronics - All Rights Reserved * * SPDX-License-Identifier: BSD-3-Clause */ @@ -8,17 +8,17 @@ #include <stdbool.h> #include <stdlib.h> -#include <libfdt.h> - -#include <platform_def.h> - #include <common/debug.h> +#include <common/fdt_wrappers.h> #include <drivers/clk.h> #include <drivers/delay_timer.h> #include <drivers/st/stm32_gpio.h> #include <drivers/st/stm32_i2c.h> #include <lib/mmio.h> #include <lib/utils.h> +#include <libfdt.h> + +#include <platform_def.h> /* STM32 I2C registers offsets */ #define I2C_CR1 0x00U @@ -97,40 +97,29 @@ static int i2c_config_analog_filter(struct i2c_handle_s *hi2c, int stm32_i2c_get_setup_from_fdt(void *fdt, int node, struct stm32_i2c_init_s *init) { - const fdt32_t *cuint; - - cuint = fdt_getprop(fdt, node, "i2c-scl-rising-time-ns", NULL); - if (cuint == NULL) { - init->rise_time = STM32_I2C_RISE_TIME_DEFAULT; - } else { - init->rise_time = fdt32_to_cpu(*cuint); - } - - cuint = fdt_getprop(fdt, node, "i2c-scl-falling-time-ns", NULL); - if (cuint == NULL) { - init->fall_time = STM32_I2C_FALL_TIME_DEFAULT; - } else { - init->fall_time = fdt32_to_cpu(*cuint); - } - - cuint = fdt_getprop(fdt, node, "clock-frequency", NULL); - if (cuint == NULL) { - init->speed_mode = STM32_I2C_SPEED_DEFAULT; - } else { - switch (fdt32_to_cpu(*cuint)) { - case STANDARD_RATE: - init->speed_mode = I2C_SPEED_STANDARD; - break; - case FAST_RATE: - init->speed_mode = I2C_SPEED_FAST; - break; - case FAST_PLUS_RATE: - init->speed_mode = I2C_SPEED_FAST_PLUS; - break; - default: - init->speed_mode = STM32_I2C_SPEED_DEFAULT; - break; - } + uint32_t read_val; + + init->rise_time = fdt_read_uint32_default(fdt, node, + "i2c-scl-rising-time-ns", + STM32_I2C_RISE_TIME_DEFAULT); + + init->fall_time = fdt_read_uint32_default(fdt, node, + "i2c-scl-falling-time-ns", + STM32_I2C_FALL_TIME_DEFAULT); + + read_val = fdt_read_uint32_default(fdt, node, "clock-frequency", + STANDARD_RATE); + switch (read_val) { + case FAST_PLUS_RATE: + init->speed_mode = I2C_SPEED_FAST_PLUS; + break; + case FAST_RATE: + init->speed_mode = I2C_SPEED_FAST; + break; + case STANDARD_RATE: + default: + init->speed_mode = I2C_SPEED_STANDARD; + break; } return dt_set_pinctrl_config(node); diff --git a/drivers/st/mmc/stm32_sdmmc2.c b/drivers/st/mmc/stm32_sdmmc2.c index be722f32..66988d71 100644 --- a/drivers/st/mmc/stm32_sdmmc2.c +++ b/drivers/st/mmc/stm32_sdmmc2.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018-2023, STMicroelectronics - All Rights Reserved + * Copyright (c) 2018-2024, STMicroelectronics - All Rights Reserved * * SPDX-License-Identifier: BSD-3-Clause */ @@ -129,7 +129,11 @@ #define DT_SDMMC2_COMPAT "st,stm32-sdmmc2" #endif +#if STM32MP13 || STM32MP15 #define SDMMC_FIFO_SIZE 64U +#else +#define SDMMC_FIFO_SIZE 1024U +#endif #define STM32MP_MMC_INIT_FREQ U(400000) /*400 KHz*/ #define STM32MP_SD_NORMAL_SPEED_MAX_FREQ U(25000000) /*25 MHz*/ diff --git a/drivers/st/pmic/stm32mp_pmic.c b/drivers/st/pmic/stm32mp_pmic.c index 1e162877..58f97b3e 100644 --- a/drivers/st/pmic/stm32mp_pmic.c +++ b/drivers/st/pmic/stm32mp_pmic.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2022, STMicroelectronics - All Rights Reserved + * Copyright (c) 2017-2024, STMicroelectronics - All Rights Reserved * * SPDX-License-Identifier: BSD-3-Clause */ @@ -216,120 +216,6 @@ void print_pmic_info_and_debug(void) } #endif -int pmic_ddr_power_init(enum ddr_type ddr_type) -{ - int status; - uint16_t buck3_min_mv; - struct rdev *buck2, *buck3, *vref; - struct rdev *ldo3 __unused; - - buck2 = regulator_get_by_name("buck2"); - if (buck2 == NULL) { - return -ENOENT; - } - -#if STM32MP15 - ldo3 = regulator_get_by_name("ldo3"); - if (ldo3 == NULL) { - return -ENOENT; - } -#endif - - vref = regulator_get_by_name("vref_ddr"); - if (vref == NULL) { - return -ENOENT; - } - - switch (ddr_type) { - case STM32MP_DDR3: -#if STM32MP15 - status = regulator_set_flag(ldo3, REGUL_SINK_SOURCE); - if (status != 0) { - return status; - } -#endif - - status = regulator_set_min_voltage(buck2); - if (status != 0) { - return status; - } - - status = regulator_enable(buck2); - if (status != 0) { - return status; - } - - status = regulator_enable(vref); - if (status != 0) { - return status; - } - -#if STM32MP15 - status = regulator_enable(ldo3); - if (status != 0) { - return status; - } -#endif - break; - - case STM32MP_LPDDR2: - case STM32MP_LPDDR3: - /* - * Set LDO3 to 1.8V - * Set LDO3 to bypass mode if BUCK3 = 1.8V - * Set LDO3 to normal mode if BUCK3 != 1.8V - */ - buck3 = regulator_get_by_name("buck3"); - if (buck3 == NULL) { - return -ENOENT; - } - - regulator_get_range(buck3, &buck3_min_mv, NULL); - -#if STM32MP15 - if (buck3_min_mv != 1800) { - status = regulator_set_min_voltage(ldo3); - if (status != 0) { - return status; - } - } else { - status = regulator_set_flag(ldo3, REGUL_ENABLE_BYPASS); - if (status != 0) { - return status; - } - } -#endif - - status = regulator_set_min_voltage(buck2); - if (status != 0) { - return status; - } - -#if STM32MP15 - status = regulator_enable(ldo3); - if (status != 0) { - return status; - } -#endif - - status = regulator_enable(buck2); - if (status != 0) { - return status; - } - - status = regulator_enable(vref); - if (status != 0) { - return status; - } - break; - - default: - break; - }; - - return 0; -} - int pmic_voltages_init(void) { #if STM32MP13 diff --git a/drivers/st/pmic/stm32mp_pmic2.c b/drivers/st/pmic/stm32mp_pmic2.c new file mode 100644 index 00000000..c19d36a4 --- /dev/null +++ b/drivers/st/pmic/stm32mp_pmic2.c @@ -0,0 +1,499 @@ +/* + * Copyright (C) 2024, STMicroelectronics - All Rights Reserved + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <assert.h> +#include <errno.h> + +#include <common/debug.h> +#include <drivers/delay_timer.h> +#include <drivers/st/regulator.h> +#include <drivers/st/stm32_i2c.h> +#include <drivers/st/stm32mp_pmic2.h> +#include <drivers/st/stpmic2.h> +#include <lib/mmio.h> +#include <lib/spinlock.h> +#include <lib/utils_def.h> +#include <libfdt.h> + +#include <platform_def.h> + +#define PMIC_NODE_NOT_FOUND 1 + +struct regul_handle_s { + const uint32_t id; + uint16_t bypass_mv; +}; + +static struct pmic_handle_s pmic2_handle; +static struct i2c_handle_s i2c_handle; + +/* This driver is monoinstance */ +static struct pmic_handle_s *pmic2; + +static int dt_get_pmic_node(void *fdt) +{ + static int node = -FDT_ERR_BADOFFSET; + + if (node == -FDT_ERR_BADOFFSET) { + node = fdt_node_offset_by_compatible(fdt, -1, "st,stpmic2"); + } + + return node; +} + +int dt_pmic_status(void) +{ + static int status = -FDT_ERR_BADVALUE; + int node; + void *fdt; + + if (status != -FDT_ERR_BADVALUE) { + return status; + } + + if (fdt_get_address(&fdt) == 0) { + return -ENOENT; + } + + node = dt_get_pmic_node(fdt); + if (node <= 0) { + status = -FDT_ERR_NOTFOUND; + + return status; + } + + status = DT_SECURE; + + return status; +} + +/* + * Get PMIC and its I2C bus configuration from the device tree. + * Return 0 on success, negative on error, 1 if no PMIC node is defined. + */ +static int dt_pmic2_i2c_config(struct dt_node_info *i2c_info, + struct stm32_i2c_init_s *init, + uint32_t *i2c_addr) +{ + static int i2c_node = -FDT_ERR_NOTFOUND; + void *fdt; + + if (fdt_get_address(&fdt) == 0) { + return -FDT_ERR_NOTFOUND; + } + + if (i2c_node == -FDT_ERR_NOTFOUND) { + int pmic_node; + const fdt32_t *cuint; + + pmic_node = dt_get_pmic_node(fdt); + if (pmic_node < 0) { + return PMIC_NODE_NOT_FOUND; + } + + cuint = fdt_getprop(fdt, pmic_node, "reg", NULL); + if (cuint == NULL) { + return -FDT_ERR_NOTFOUND; + } + + *i2c_addr = fdt32_to_cpu(*cuint) << 1; + if (*i2c_addr > UINT16_MAX) { + return -FDT_ERR_BADVALUE; + } + + i2c_node = fdt_parent_offset(fdt, pmic_node); + if (i2c_node < 0) { + return -FDT_ERR_NOTFOUND; + } + } + + dt_fill_device_info(i2c_info, i2c_node); + if (i2c_info->base == 0U) { + return -FDT_ERR_NOTFOUND; + } + + i2c_info->status = DT_SECURE; + + return stm32_i2c_get_setup_from_fdt(fdt, i2c_node, init); +} + +bool initialize_pmic_i2c(void) +{ + int ret; + struct dt_node_info i2c_info; + struct i2c_handle_s *i2c = &i2c_handle; + uint32_t i2c_addr = 0U; + struct stm32_i2c_init_s i2c_init; + + ret = dt_pmic2_i2c_config(&i2c_info, &i2c_init, &i2c_addr); + if (ret < 0) { + ERROR("I2C configuration failed %d\n", ret); + panic(); + } + + if (ret != 0) { + return false; + } + + /* Initialize PMIC I2C */ + i2c->i2c_base_addr = i2c_info.base; + i2c->dt_status = i2c_info.status; + i2c->clock = i2c_info.clock; + i2c->i2c_state = I2C_STATE_RESET; + i2c_init.own_address1 = i2c_addr; + i2c_init.addressing_mode = I2C_ADDRESSINGMODE_7BIT; + i2c_init.dual_address_mode = I2C_DUALADDRESS_DISABLE; + i2c_init.own_address2 = 0; + i2c_init.own_address2_masks = I2C_OAR2_OA2NOMASK; + i2c_init.general_call_mode = I2C_GENERALCALL_DISABLE; + i2c_init.no_stretch_mode = I2C_NOSTRETCH_DISABLE; + i2c_init.analog_filter = 1; + i2c_init.digital_filter_coef = 0; + + ret = stm32_i2c_init(i2c, &i2c_init); + if (ret != 0) { + ERROR("Cannot initialize I2C %x (%d)\n", + i2c->i2c_base_addr, ret); + panic(); + } + + if (!stm32_i2c_is_device_ready(i2c, i2c_addr, 1, + I2C_TIMEOUT_BUSY_MS)) { + ERROR("I2C device not ready\n"); + panic(); + } + + pmic2 = &pmic2_handle; + pmic2->i2c_handle = &i2c_handle; + pmic2->i2c_addr = i2c_addr; + + return true; +} + +static int pmic2_set_state(const struct regul_description *desc, bool enable) +{ + struct regul_handle_s *regul = (struct regul_handle_s *)desc->driver_data; + + VERBOSE("%s: set state to %d\n", desc->node_name, enable); + + return stpmic2_regulator_set_state(pmic2, regul->id, enable); +} + +static int pmic2_get_state(const struct regul_description *desc) +{ + struct regul_handle_s *regul = (struct regul_handle_s *)desc->driver_data; + bool enabled; + + VERBOSE("%s: get state\n", desc->node_name); + + if (stpmic2_regulator_get_state(pmic2, regul->id, &enabled) < 0) { + panic(); + } + + return enabled; +} + +static int pmic2_get_voltage(const struct regul_description *desc) +{ + struct regul_handle_s *regul = (struct regul_handle_s *)desc->driver_data; + uint16_t mv; + + VERBOSE("%s: get volt\n", desc->node_name); + + if (regul->bypass_mv != 0U) { + int ret; + + /* If the regul is in bypass mode, return bypass value */ + ret = stpmic2_regulator_get_prop(pmic2, regul->id, STPMIC2_BYPASS); + if (ret < 0) { + return ret; + } + + if (ret == 1) { + return regul->bypass_mv; + } + }; + + if (stpmic2_regulator_get_voltage(pmic2, regul->id, &mv) < 0) { + panic(); + } + + return mv; +} + +static int pmic2_set_voltage(const struct regul_description *desc, uint16_t mv) +{ + struct regul_handle_s *regul = (struct regul_handle_s *)desc->driver_data; + + VERBOSE("%s: set volt\n", desc->node_name); + + if (regul->bypass_mv != 0U) { + int ret; + + /* If the regul is in bypass mode, authorize bypass mV */ + ret = stpmic2_regulator_get_prop(pmic2, regul->id, STPMIC2_BYPASS); + if (ret < 0) { + return ret; + } + + if ((ret == 1) && (mv != regul->bypass_mv)) { + return -EPERM; + } + }; + + return stpmic2_regulator_set_voltage(pmic2, regul->id, mv); +} + +static int pmic2_list_voltages(const struct regul_description *desc, + const uint16_t **levels, size_t *count) +{ + struct regul_handle_s *regul = (struct regul_handle_s *)desc->driver_data; + + VERBOSE("%s: list volt\n", desc->node_name); + + if (regul->bypass_mv != 0U) { + int ret; + + ret = stpmic2_regulator_get_prop(pmic2, regul->id, STPMIC2_BYPASS); + if (ret < 0) { + return ret; + } + + /* bypass is enabled, return a list with only bypass mV */ + if (ret == 1) { + if (count != NULL) { + *count = 1U; + } + if (levels != NULL) { + *levels = ®ul->bypass_mv; + } + return 0; + } + }; + + return stpmic2_regulator_levels_mv(pmic2, regul->id, levels, count); +} + +static int pmic2_set_flag(const struct regul_description *desc, uint16_t flag) +{ + struct regul_handle_s *regul = (struct regul_handle_s *)desc->driver_data; + uint32_t id = regul->id; + int ret = -EPERM; + + VERBOSE("%s: set_flag 0x%x\n", desc->node_name, flag); + + switch (flag) { + case REGUL_PULL_DOWN: + ret = stpmic2_regulator_set_prop(pmic2, id, STPMIC2_PULL_DOWN, 1U); + break; + case REGUL_OCP: + ret = stpmic2_regulator_set_prop(pmic2, id, STPMIC2_OCP, 1U); + break; + case REGUL_SINK_SOURCE: + ret = stpmic2_regulator_set_prop(pmic2, id, STPMIC2_SINK_SOURCE, 1U); + break; + case REGUL_ENABLE_BYPASS: + ret = stpmic2_regulator_set_prop(pmic2, id, STPMIC2_BYPASS, 1U); + break; + case REGUL_MASK_RESET: + ret = stpmic2_regulator_set_prop(pmic2, id, STPMIC2_MASK_RESET, 1U); + break; + default: + ERROR("Invalid flag %u", flag); + panic(); + } + + if (ret != 0) { + return -EPERM; + } + + return 0; +} + +int stpmic2_set_prop(const struct regul_description *desc, uint16_t prop, uint32_t value) +{ + struct regul_handle_s *regul = (struct regul_handle_s *)desc->driver_data; + int ret; + + VERBOSE("%s: set_prop 0x%x val=%u\n", desc->node_name, prop, value); + + ret = stpmic2_regulator_set_prop(pmic2, regul->id, prop, value); + if (ret != 0) + return -EPERM; + + return 0; +} + +static struct regul_ops pmic2_ops = { + .set_state = pmic2_set_state, + .get_state = pmic2_get_state, + .set_voltage = pmic2_set_voltage, + .get_voltage = pmic2_get_voltage, + .list_voltages = pmic2_list_voltages, + .set_flag = pmic2_set_flag, +}; + +#define DEFINE_PMIC_REGUL_HANDLE(rid) \ +[(rid)] = { \ + .id = (rid), \ +} + +static struct regul_handle_s pmic2_regul_handles[STPMIC2_NB_REG] = { + DEFINE_PMIC_REGUL_HANDLE(STPMIC2_BUCK1), + DEFINE_PMIC_REGUL_HANDLE(STPMIC2_BUCK2), + DEFINE_PMIC_REGUL_HANDLE(STPMIC2_BUCK3), + DEFINE_PMIC_REGUL_HANDLE(STPMIC2_BUCK4), + DEFINE_PMIC_REGUL_HANDLE(STPMIC2_BUCK5), + DEFINE_PMIC_REGUL_HANDLE(STPMIC2_BUCK6), + DEFINE_PMIC_REGUL_HANDLE(STPMIC2_BUCK7), + + DEFINE_PMIC_REGUL_HANDLE(STPMIC2_LDO1), + DEFINE_PMIC_REGUL_HANDLE(STPMIC2_LDO2), + DEFINE_PMIC_REGUL_HANDLE(STPMIC2_LDO3), + DEFINE_PMIC_REGUL_HANDLE(STPMIC2_LDO4), + DEFINE_PMIC_REGUL_HANDLE(STPMIC2_LDO5), + DEFINE_PMIC_REGUL_HANDLE(STPMIC2_LDO6), + DEFINE_PMIC_REGUL_HANDLE(STPMIC2_LDO7), + DEFINE_PMIC_REGUL_HANDLE(STPMIC2_LDO8), + + DEFINE_PMIC_REGUL_HANDLE(STPMIC2_REFDDR), +}; + +#define DEFINE_REGUL(rid, name) \ +[rid] = { \ + .node_name = name, \ + .ops = &pmic2_ops, \ + .driver_data = &pmic2_regul_handles[rid], \ +} + +static const struct regul_description pmic2_descs[STPMIC2_NB_REG] = { + DEFINE_REGUL(STPMIC2_BUCK1, "buck1"), + DEFINE_REGUL(STPMIC2_BUCK2, "buck2"), + DEFINE_REGUL(STPMIC2_BUCK3, "buck3"), + DEFINE_REGUL(STPMIC2_BUCK4, "buck4"), + DEFINE_REGUL(STPMIC2_BUCK5, "buck5"), + DEFINE_REGUL(STPMIC2_BUCK6, "buck6"), + DEFINE_REGUL(STPMIC2_BUCK7, "buck7"), + + DEFINE_REGUL(STPMIC2_LDO1, "ldo1"), + DEFINE_REGUL(STPMIC2_LDO2, "ldo2"), + DEFINE_REGUL(STPMIC2_LDO3, "ldo3"), + DEFINE_REGUL(STPMIC2_LDO4, "ldo4"), + DEFINE_REGUL(STPMIC2_LDO5, "ldo5"), + DEFINE_REGUL(STPMIC2_LDO6, "ldo6"), + DEFINE_REGUL(STPMIC2_LDO7, "ldo7"), + DEFINE_REGUL(STPMIC2_LDO8, "ldo8"), + + DEFINE_REGUL(STPMIC2_REFDDR, "refddr"), +}; + +static int register_pmic2(void) +{ + void *fdt; + int pmic_node, regulators_node, subnode; + + VERBOSE("Register pmic2\n"); + + if (fdt_get_address(&fdt) == 0) { + return -FDT_ERR_NOTFOUND; + } + + pmic_node = dt_get_pmic_node(fdt); + if (pmic_node < 0) { + return pmic_node; + } + + regulators_node = fdt_subnode_offset(fdt, pmic_node, "regulators"); + if (regulators_node < 0) { + return -ENOENT; + } + + fdt_for_each_subnode(subnode, fdt, regulators_node) { + const char *reg_name = fdt_get_name(fdt, subnode, NULL); + const struct regul_description *desc; + unsigned int i; + int ret; + const fdt32_t *cuint; + + for (i = 0; i < STPMIC2_NB_REG; i++) { + desc = &pmic2_descs[i]; + if (strcmp(desc->node_name, reg_name) == 0) { + break; + } + } + assert(i < STPMIC2_NB_REG); + + ret = regulator_register(desc, subnode); + if (ret != 0) { + WARN("%s:%d failed to register %s\n", __func__, + __LINE__, reg_name); + return ret; + } + + cuint = fdt_getprop(fdt, subnode, "st,regulator-bypass-microvolt", NULL); + if (cuint != NULL) { + struct regul_handle_s *regul = (struct regul_handle_s *)desc->driver_data; + + regul->bypass_mv = (uint16_t)(fdt32_to_cpu(*cuint) / 1000U); + VERBOSE("%s: bypass voltage=%umV\n", desc->node_name, + regul->bypass_mv); + } + + if (fdt_getprop(fdt, subnode, "st,mask-reset", NULL) != NULL) { + VERBOSE("%s: set mask-reset\n", desc->node_name); + ret = pmic2_set_flag(desc, REGUL_MASK_RESET); + if (ret != 0) { + ERROR("set mask-reset failed\n"); + return ret; + } + } + + if (fdt_getprop(fdt, subnode, "st,regulator-sink-source", NULL) != NULL) { + VERBOSE("%s: set regulator-sink-source\n", desc->node_name); + ret = pmic2_set_flag(desc, REGUL_SINK_SOURCE); + if (ret != 0) { + ERROR("set regulator-sink-source failed\n"); + return ret; + } + } + } + + return 0; +} + +void initialize_pmic(void) +{ + int ret; + uint8_t val; + + ret = initialize_pmic_i2c(); + if (!ret) { + VERBOSE("No PMIC2\n"); + return; + } + + if (stpmic2_get_version(pmic2, &val) != 0) { + ERROR("Failed to access PMIC\n"); + panic(); + } + INFO("PMIC2 version = 0x%02x\n", val); + + if (stpmic2_get_product_id(pmic2, &val) != 0) { + ERROR("Failed to access PMIC\n"); + panic(); + } + INFO("PMIC2 product ID = 0x%02x\n", val); + + ret = register_pmic2(); + if (ret < 0) { + ERROR("Register pmic2 failed\n"); + panic(); + } + +#if EVENT_LOG_LEVEL == LOG_LEVEL_VERBOSE + stpmic2_dump_regulators(pmic2); +#endif +} diff --git a/drivers/st/pmic/stpmic2.c b/drivers/st/pmic/stpmic2.c new file mode 100644 index 00000000..05a80ecf --- /dev/null +++ b/drivers/st/pmic/stpmic2.c @@ -0,0 +1,474 @@ +/* + * Copyright (C) 2024, STMicroelectronics - All Rights Reserved + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <assert.h> +#include <errno.h> +#include <string.h> + +#include <common/debug.h> +#include <drivers/st/stpmic2.h> + +#define RET_SUCCESS 0 +#define RET_ERROR_NOT_SUPPORTED -1 +#define RET_ERROR_GENERIC -2 +#define RET_ERROR_BAD_PARAMETERS -3 + +#define I2C_TIMEOUT_MS 25 + +#define VOLTAGE_INDEX_INVALID ((size_t)~0U) + +struct regul_struct { + const char *name; + const uint16_t *volt_table; + uint8_t volt_table_size; + uint8_t volt_cr; + uint8_t volt_shift; + uint8_t en_cr; + uint8_t alt_en_cr; + uint8_t msrt_reg; + uint8_t msrt_mask; + uint8_t pd_reg; + uint8_t pd_val; + uint8_t ocp_reg; + uint8_t ocp_mask; +}; + +/* Voltage tables in mV */ +static const uint16_t buck1236_volt_table[] = { + 500U, 510U, 520U, 530U, 540U, 550U, 560U, 570U, 580U, 590U, + 600U, 610U, 620U, 630U, 640U, 650U, 660U, 670U, 680U, 690U, + 700U, 710U, 720U, 730U, 740U, 750U, 760U, 770U, 780U, 790U, + 800U, 810U, 820U, 830U, 840U, 850U, 860U, 870U, 880U, 890U, + 900U, 910U, 920U, 930U, 940U, 950U, 960U, 970U, 980U, 990U, + 1000U, 1010U, 1020U, 1030U, 1040U, 1050U, 1060U, 1070U, 1080U, 1090U, + 1100U, 1110U, 1120U, 1130U, 1140U, 1150U, 1160U, 1170U, 1180U, 1190U, + 1200U, 1210U, 1220U, 1230U, 1240U, 1250U, 1260U, 1270U, 1280U, 1290U, + 1300U, 1310U, 1320U, 1330U, 1340U, 1350U, 1360U, 1370U, 1380U, 1390U, + 1400U, 1410U, 1420U, 1430U, 1440U, 1450U, 1460U, 1470U, 1480U, 1490U, + 1500U, 1500U, 1500U, 1500U, 1500U, 1500U, 1500U, 1500U, 1500U, 1500U, + 1500U, 1500U, 1500U, 1500U, 1500U, 1500U, 1500U, 1500U, 1500U, 1500U, + 1500U, 1500U, 1500U, 1500U, 1500U, 1500U, 1500U, 1500U +}; + +static const uint16_t buck457_volt_table[] = { + 1500U, 1500U, 1500U, 1500U, 1500U, 1500U, 1500U, 1500U, 1500U, 1500U, + 1500U, 1500U, 1500U, 1500U, 1500U, 1500U, 1500U, 1500U, 1500U, 1500U, + 1500U, 1500U, 1500U, 1500U, 1500U, 1500U, 1500U, 1500U, 1500U, 1500U, + 1500U, 1500U, 1500U, 1500U, 1500U, 1500U, 1500U, 1500U, 1500U, 1500U, + 1500U, 1500U, 1500U, 1500U, 1500U, 1500U, 1500U, 1500U, 1500U, 1500U, + 1500U, 1500U, 1500U, 1500U, 1500U, 1500U, 1500U, 1500U, 1500U, 1500U, + 1500U, 1500U, 1500U, 1500U, 1500U, 1500U, 1500U, 1500U, 1500U, 1500U, + 1500U, 1500U, 1500U, 1500U, 1500U, 1500U, 1500U, 1500U, 1500U, 1500U, + 1500U, 1500U, 1500U, 1500U, 1500U, 1500U, 1500U, 1500U, 1500U, 1500U, + 1500U, 1500U, 1500U, 1500U, 1500U, 1500U, 1500U, 1500U, 1500U, 1500U, + 1500U, 1600U, 1700U, 1800U, 1900U, 2000U, 2100U, 2200U, 2300U, 2400U, + 2500U, 2600U, 2700U, 2800U, 2900U, 3000U, 3100U, 3200U, 3300U, 3400U, + 3500U, 3600U, 3700U, 3800U, 3900U, 4000U, 4100U, 4200U +}; + +static const uint16_t ldo235678_volt_table[] = { + 900U, 1000U, 1100U, 1200U, 1300U, 1400U, 1500U, 1600U, 1700U, 1800U, + 1900U, 2000U, 2100U, 2200U, 2300U, 2400U, 2500U, 2600U, 2700U, 2800U, + 2900U, 3000U, 3100U, 3200U, 3300U, 3400U, 3500U, 3600U, 3700U, 3800U, + 3900U, 4000U +}; + +static const uint16_t ldo1_volt_table[] = { + 1800U, +}; + +static const uint16_t ldo4_volt_table[] = { + 3300U, +}; + +static const uint16_t refddr_volt_table[] = { + 0, +}; + +#define DEFINE_BUCK(regu_name, ID, pd, table) { \ + .name = regu_name, \ + .volt_table = table, \ + .volt_table_size = ARRAY_SIZE(table), \ + .en_cr = ID ## _MAIN_CR2, \ + .volt_cr = ID ## _MAIN_CR1, \ + .alt_en_cr = ID ## _ALT_CR2, \ + .msrt_reg = BUCKS_MRST_CR, \ + .msrt_mask = ID ## _MRST, \ + .pd_reg = pd, \ + .pd_val = ID ## _PD_FAST, \ + .ocp_reg = FS_OCP_CR1, \ + .ocp_mask = FS_OCP_ ## ID, \ +} + +#define DEFINE_LDOx(regu_name, ID, table) { \ + .name = regu_name, \ + .volt_table = table, \ + .volt_table_size = ARRAY_SIZE(table), \ + .volt_shift = LDO_VOLT_SHIFT, \ + .en_cr = ID ## _MAIN_CR, \ + .volt_cr = ID ## _MAIN_CR, \ + .alt_en_cr = ID ## _ALT_CR, \ + .msrt_reg = LDOS_MRST_CR, \ + .msrt_mask = ID ## _MRST, \ + .pd_reg = LDOS_PD_CR1, \ + .pd_val = ID ## _PD, \ + .ocp_reg = FS_OCP_CR2, \ + .ocp_mask = FS_OCP_ ## ID, \ +} + +#define DEFINE_REFDDR(regu_name, ID, table) { \ + .name = regu_name, \ + .volt_table = table, \ + .volt_table_size = ARRAY_SIZE(table), \ + .en_cr = ID ## _MAIN_CR, \ + .volt_cr = ID ## _MAIN_CR, \ + .alt_en_cr = ID ## _ALT_CR, \ + .msrt_reg = BUCKS_MRST_CR, \ + .msrt_mask = ID ## _MRST, \ + .pd_reg = LDOS_PD_CR2, \ + .pd_val = ID ## _PD, \ + .ocp_reg = FS_OCP_CR1, \ + .ocp_mask = FS_OCP_ ## ID, \ +} + +/* Table of Regulators in PMIC SoC */ +static const struct regul_struct regul_table[STPMIC2_NB_REG] = { + [STPMIC2_BUCK1] = DEFINE_BUCK("buck1", BUCK1, BUCKS_PD_CR1, + buck1236_volt_table), + [STPMIC2_BUCK2] = DEFINE_BUCK("buck2", BUCK2, BUCKS_PD_CR1, + buck1236_volt_table), + [STPMIC2_BUCK3] = DEFINE_BUCK("buck3", BUCK3, BUCKS_PD_CR1, + buck1236_volt_table), + [STPMIC2_BUCK4] = DEFINE_BUCK("buck4", BUCK4, BUCKS_PD_CR1, + buck457_volt_table), + [STPMIC2_BUCK5] = DEFINE_BUCK("buck5", BUCK5, BUCKS_PD_CR2, + buck457_volt_table), + [STPMIC2_BUCK6] = DEFINE_BUCK("buck6", BUCK6, BUCKS_PD_CR2, + buck1236_volt_table), + [STPMIC2_BUCK7] = DEFINE_BUCK("buck7", BUCK7, BUCKS_PD_CR2, + buck457_volt_table), + + [STPMIC2_REFDDR] = DEFINE_REFDDR("refddr", REFDDR, refddr_volt_table), + + [STPMIC2_LDO1] = DEFINE_LDOx("ldo1", LDO1, ldo1_volt_table), + [STPMIC2_LDO2] = DEFINE_LDOx("ldo2", LDO2, ldo235678_volt_table), + [STPMIC2_LDO3] = DEFINE_LDOx("ldo3", LDO3, ldo235678_volt_table), + [STPMIC2_LDO4] = DEFINE_LDOx("ldo4", LDO4, ldo4_volt_table), + [STPMIC2_LDO5] = DEFINE_LDOx("ldo5", LDO5, ldo235678_volt_table), + [STPMIC2_LDO6] = DEFINE_LDOx("ldo6", LDO6, ldo235678_volt_table), + [STPMIC2_LDO7] = DEFINE_LDOx("ldo7", LDO7, ldo235678_volt_table), + [STPMIC2_LDO8] = DEFINE_LDOx("ldo8", LDO8, ldo235678_volt_table), + +}; + +int stpmic2_register_read(struct pmic_handle_s *pmic, + uint8_t register_id, uint8_t *value) +{ + int ret = stm32_i2c_mem_read(pmic->i2c_handle, + pmic->i2c_addr, + (uint16_t)register_id, + I2C_MEMADD_SIZE_8BIT, value, + 1, I2C_TIMEOUT_MS); + if (ret != 0) { + ERROR("Failed to read reg:0x%x\n", register_id); + } + + return ret; +} + +int stpmic2_register_write(struct pmic_handle_s *pmic, + uint8_t register_id, uint8_t value) +{ + uint8_t val = value; + int ret = stm32_i2c_mem_write(pmic->i2c_handle, + pmic->i2c_addr, + (uint16_t)register_id, + I2C_MEMADD_SIZE_8BIT, &val, + 1, I2C_TIMEOUT_MS); + if (ret != 0) { + ERROR("Failed to write reg:0x%x\n", register_id); + } + + return ret; +} + +int stpmic2_register_update(struct pmic_handle_s *pmic, + uint8_t register_id, uint8_t value, uint8_t mask) +{ + int status; + uint8_t val = 0U; + + status = stpmic2_register_read(pmic, register_id, &val); + if (status != 0) { + return status; + } + + val = (val & ((uint8_t)~mask)) | (value & mask); + + VERBOSE("REG:0x%x v=0x%x mask=0x%x -> 0x%x\n", + register_id, value, mask, val); + + return stpmic2_register_write(pmic, register_id, val); +} + +int stpmic2_regulator_set_state(struct pmic_handle_s *pmic, + uint8_t id, bool enable) +{ + const struct regul_struct *regul = ®ul_table[id]; + + if (enable) { + return stpmic2_register_update(pmic, regul->en_cr, 1U, 1U); + } else { + return stpmic2_register_update(pmic, regul->en_cr, 0, 1U); + } +} + +int stpmic2_regulator_get_state(struct pmic_handle_s *pmic, + uint8_t id, bool *enabled) +{ + const struct regul_struct *regul = ®ul_table[id]; + uint8_t val; + + if (stpmic2_register_read(pmic, regul->en_cr, &val) != 0) { + return RET_ERROR_GENERIC; + } + + *enabled = (val & 1U) == 1U; + + return RET_SUCCESS; +} + +int stpmic2_regulator_levels_mv(struct pmic_handle_s *pmic, + uint8_t id, const uint16_t **levels, + size_t *levels_count) +{ + const struct regul_struct *regul = ®ul_table[id]; + + if (regul == NULL) { + return RET_ERROR_BAD_PARAMETERS; + } + + if (levels_count != NULL) { + *levels_count = regul->volt_table_size; + } + if (levels != NULL) { + *levels = regul->volt_table; + } + + return RET_SUCCESS; +} + +int stpmic2_regulator_get_voltage(struct pmic_handle_s *pmic, + uint8_t id, uint16_t *val) +{ + const struct regul_struct *regul = ®ul_table[id]; + uint8_t value = 0U; + uint8_t mask; + + if (regul->volt_table_size == 0U) { + return RET_ERROR_GENERIC; + } + + mask = regul->volt_table_size - 1U; + if (mask != 0U) { + if (stpmic2_register_read(pmic, regul->volt_cr, &value) != 0) { + return RET_ERROR_GENERIC; + } + + value = (value >> regul->volt_shift) & mask; + } + + if (value > regul->volt_table_size) { + return RET_ERROR_GENERIC; + } + + *val = regul->volt_table[value]; + + return RET_SUCCESS; +} + +static size_t voltage_to_index(const struct regul_struct *regul, + uint16_t millivolts) +{ + unsigned int i; + + assert(regul->volt_table); + for (i = 0U; i < regul->volt_table_size; i++) { + if (regul->volt_table[i] == millivolts) { + return i; + } + } + + return VOLTAGE_INDEX_INVALID; +} + +int stpmic2_regulator_set_voltage(struct pmic_handle_s *pmic, + uint8_t id, uint16_t millivolts) +{ + const struct regul_struct *regul = ®ul_table[id]; + size_t index; + uint8_t mask; + + if (!regul->volt_table_size) { + return RET_SUCCESS; + } + + mask = regul->volt_table_size - 1U; + + index = voltage_to_index(regul, millivolts); + if (index == VOLTAGE_INDEX_INVALID) { + return RET_ERROR_GENERIC; + } + + return stpmic2_register_update(pmic, regul->volt_cr, + index << regul->volt_shift, + mask << regul->volt_shift); +} + +/* update both normal and alternate register */ +static int stpmic2_update_en_crs(struct pmic_handle_s *pmic, uint8_t id, + uint8_t value, uint8_t mask) +{ + const struct regul_struct *regul = ®ul_table[id]; + + if (stpmic2_register_update(pmic, regul->en_cr, value, mask) != 0) { + return RET_ERROR_GENERIC; + } + + if (stpmic2_register_update(pmic, regul->alt_en_cr, value, mask) != 0) { + return RET_ERROR_GENERIC; + } + + return RET_SUCCESS; +} + +int stpmic2_regulator_get_prop(struct pmic_handle_s *pmic, uint8_t id, + enum stpmic2_prop_id prop) +{ + const struct regul_struct *regul = ®ul_table[id]; + uint8_t val; + + VERBOSE("%s: get prop 0x%x\n", regul->name, prop); + + switch (prop) { + case STPMIC2_BYPASS: + if ((id <= STPMIC2_BUCK7) || (id == STPMIC2_LDO1) || + (id == STPMIC2_LDO4) || (id == STPMIC2_REFDDR)) { + return 0; + } + + if (stpmic2_register_read(pmic, regul->en_cr, &val) != 0) { + return -EIO; + } + + if ((val & LDO_BYPASS) != 0) { + return 1; + } + + break; + default: + ERROR("Invalid prop %u\n", prop); + panic(); + } + + return 0; +} + +int stpmic2_regulator_set_prop(struct pmic_handle_s *pmic, uint8_t id, + enum stpmic2_prop_id prop, uint32_t arg) +{ + const struct regul_struct *regul = ®ul_table[id]; + + VERBOSE("%s: set prop 0x%x arg=%u\n", regul->name, prop, arg); + + switch (prop) { + case STPMIC2_PULL_DOWN: + return stpmic2_register_update(pmic, regul->pd_reg, + regul->pd_val, + regul->pd_val); + case STPMIC2_MASK_RESET: + if (!regul->msrt_mask) { + return RET_ERROR_NOT_SUPPORTED; + } + /* enable mask reset */ + return stpmic2_register_update(pmic, regul->msrt_reg, + regul->msrt_mask, + regul->msrt_mask); + case STPMIC2_BYPASS: + if ((id <= STPMIC2_BUCK7) || (id == STPMIC2_LDO1) || + (id == STPMIC2_LDO4) || (id == STPMIC2_REFDDR)) { + return RET_ERROR_NOT_SUPPORTED; + } + + /* clear sink source mode */ + if ((id == STPMIC2_LDO3) && (arg != 0U)) { + if (stpmic2_update_en_crs(pmic, id, 0, LDO3_SNK_SRC) != 0) { + return RET_ERROR_GENERIC; + } + } + + /* enable bypass mode */ + return stpmic2_update_en_crs(pmic, id, + (arg != 0U) ? LDO_BYPASS : 0, + LDO_BYPASS); + case STPMIC2_SINK_SOURCE: + if (id != STPMIC2_LDO3) { + return RET_ERROR_NOT_SUPPORTED; + } + + /* clear bypass mode */ + if (stpmic2_update_en_crs(pmic, id, 0, LDO_BYPASS) != 0) { + return RET_ERROR_GENERIC; + } + + return stpmic2_update_en_crs(pmic, id, LDO3_SNK_SRC, + LDO3_SNK_SRC); + case STPMIC2_OCP: + return stpmic2_register_update(pmic, regul->ocp_reg, + regul->ocp_mask, + regul->ocp_mask); + default: + ERROR("Invalid prop %u\n", prop); + panic(); + } + + return -EPERM; +} + +#if EVENT_LOG_LEVEL == LOG_LEVEL_VERBOSE +void stpmic2_dump_regulators(struct pmic_handle_s *pmic) +{ + size_t i; + char const *name; + + for (i = 0U; i < ARRAY_SIZE(regul_table); i++) { + uint16_t val; + bool state; + + if (!regul_table[i].volt_cr) { + continue; + } + + stpmic2_regulator_get_voltage(pmic, i, &val); + stpmic2_regulator_get_state(pmic, i, &state); + + name = regul_table[i].name; + + VERBOSE("PMIC regul %s: %s, %dmV\n", + name, state ? "EN" : "DIS", val); + } +} +#endif + +int stpmic2_get_version(struct pmic_handle_s *pmic, uint8_t *val) +{ + return stpmic2_register_read(pmic, VERSION_SR, val); +} + +int stpmic2_get_product_id(struct pmic_handle_s *pmic, uint8_t *val) +{ + return stpmic2_register_read(pmic, PRODUCT_ID, val); +} diff --git a/drivers/st/regulator/regulator_core.c b/drivers/st/regulator/regulator_core.c index 2a5d0f7a..b369acdb 100644 --- a/drivers/st/regulator/regulator_core.c +++ b/drivers/st/regulator/regulator_core.c @@ -215,14 +215,18 @@ int regulator_set_voltage(struct rdev *rdev, uint16_t mvolt) VERBOSE("%s: set mvolt\n", rdev->desc->node_name); - if (rdev->desc->ops->set_voltage == NULL) { - return -ENODEV; - } - if ((mvolt < rdev->min_mv) || (mvolt > rdev->max_mv)) { return -EPERM; } + if (regulator_get_voltage(rdev) == mvolt) { + return 0U; + } + + if (rdev->desc->ops->set_voltage == NULL) { + return -ENODEV; + } + lock_driver(rdev); ret = rdev->desc->ops->set_voltage(rdev->desc, mvolt); @@ -420,6 +424,7 @@ int regulator_set_flag(struct rdev *rdev, uint16_t flag) static int parse_properties(const void *fdt, struct rdev *rdev, int node) { + const fdt32_t *cuint; int ret; if (fdt_getprop(fdt, node, "regulator-always-on", NULL) != NULL) { @@ -430,6 +435,13 @@ static int parse_properties(const void *fdt, struct rdev *rdev, int node) } } + cuint = fdt_getprop(fdt, node, "regulator-enable-ramp-delay", NULL); + if (cuint != NULL) { + rdev->enable_ramp_delay = fdt32_to_cpu(*cuint); + VERBOSE("%s: enable_ramp_delay=%u\n", rdev->desc->node_name, + rdev->enable_ramp_delay); + } + return 0; } diff --git a/drivers/st/reset/stm32mp1_reset.c b/drivers/st/reset/stm32mp1_reset.c index 98c8dcf7..8b828a15 100644 --- a/drivers/st/reset/stm32mp1_reset.c +++ b/drivers/st/reset/stm32mp1_reset.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018-2019, STMicroelectronics - All Rights Reserved + * Copyright (c) 2018-2024, STMicroelectronics - All Rights Reserved * * SPDX-License-Identifier: BSD-3-Clause */ @@ -7,8 +7,6 @@ #include <errno.h> #include <limits.h> -#include <platform_def.h> - #include <common/bl_common.h> #include <common/debug.h> #include <drivers/delay_timer.h> @@ -16,6 +14,8 @@ #include <lib/mmio.h> #include <lib/utils_def.h> +#include <platform_def.h> + static uint32_t id2reg_offset(unsigned int reset_id) { return ((reset_id & GENMASK(31, 5)) >> 5) * sizeof(uint32_t); @@ -67,3 +67,16 @@ int stm32mp_reset_deassert(uint32_t id, unsigned int to_us) return 0; } + +void __dead2 stm32mp_system_reset(void) +{ + uintptr_t rcc_base = stm32mp_rcc_base(); + + mmio_setbits_32(rcc_base + RCC_MP_GRSTCSETR, + RCC_MP_GRSTCSETR_MPSYSRST); + + /* Loop in case system reset is not immediately caught */ + while (true) { + wfi(); + } +} diff --git a/drivers/st/reset/stm32mp2_reset.c b/drivers/st/reset/stm32mp2_reset.c new file mode 100644 index 00000000..0918df59 --- /dev/null +++ b/drivers/st/reset/stm32mp2_reset.c @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2024, STMicroelectronics - All Rights Reserved + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <errno.h> +#include <stdbool.h> + +#include <common/debug.h> +#include <drivers/delay_timer.h> +#include <drivers/st/stm32mp_reset.h> +#include <lib/mmio.h> +#include <lib/utils_def.h> + +#include <platform_def.h> + +static uint32_t id2reg_offset(unsigned int reset_id) +{ + return ((reset_id & GENMASK(31, 5)) >> 5) * sizeof(uint32_t); +} + +static uint8_t id2reg_bit_pos(unsigned int reset_id) +{ + return (uint8_t)(reset_id & GENMASK(4, 0)); +} + +static int reset_toggle(uint32_t id, unsigned int to_us, bool reset_status) +{ + uint32_t offset = id2reg_offset(id); + uint32_t bitmsk = BIT(id2reg_bit_pos(id)); + uint32_t bit_check; + uintptr_t rcc_base = stm32mp_rcc_base(); + + if (reset_status) { + mmio_setbits_32(rcc_base + offset, bitmsk); + bit_check = bitmsk; + } else { + mmio_clrbits_32(rcc_base + offset, bitmsk); + bit_check = 0U; + } + + if (to_us != 0U) { + uint64_t timeout_ref = timeout_init_us(to_us); + + while ((mmio_read_32(rcc_base + offset) & bitmsk) != bit_check) { + if (timeout_elapsed(timeout_ref)) { + return -ETIMEDOUT; + } + } + } + + return 0; +} + +int stm32mp_reset_assert(uint32_t id, unsigned int to_us) +{ + return reset_toggle(id, to_us, true); +} + +int stm32mp_reset_deassert(uint32_t id, unsigned int to_us) +{ + return reset_toggle(id, to_us, false); +} + +void __dead2 stm32mp_system_reset(void) +{ + uintptr_t rcc_base = stm32mp_rcc_base(); + + mmio_setbits_32(rcc_base + RCC_GRSTCSETR, RCC_GRSTCSETR_SYSRST); + + /* Loop in case system reset is not immediately caught */ + while (true) { + wfi(); + } +} diff --git a/drivers/ufs/ufs.c b/drivers/ufs/ufs.c index 19f894ff..33ceb263 100644 --- a/drivers/ufs/ufs.c +++ b/drivers/ufs/ufs.c @@ -966,8 +966,7 @@ static void ufs_get_device_info(struct ufs_dev_desc *card_data) { uint8_t desc_buf[DESC_DEVICE_MAX_SIZE]; - ufs_query(QUERY_READ_DESC, DESC_TYPE_DEVICE, 0, 0, - (uintptr_t)desc_buf, DESC_DEVICE_MAX_SIZE); + ufs_read_desc(DESC_TYPE_DEVICE, 0, (uintptr_t)desc_buf, DESC_DEVICE_MAX_SIZE); /* * getting vendor (manufacturerID) and Bank Index in big endian diff --git a/fdts/cca_cot_descriptors.dtsi b/fdts/cca_cot_descriptors.dtsi new file mode 100644 index 00000000..93d60ea3 --- /dev/null +++ b/fdts/cca_cot_descriptors.dtsi @@ -0,0 +1,269 @@ +/* + * Copyright (c) 2023-2024, Arm Limited. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <tools_share/cca_oid.h> +#include <common/tbbr/tbbr_img_def.h> +#include <common/nv_cntr_ids.h> + +cot { + manifests { + compatible = "arm, cert-descs"; + + cca_content_cert: cca_content_cert { + root-certificate; + image-id =<CCA_CONTENT_CERT_ID>; + antirollback-counter = <&cca_nv_ctr>; + + tb_fw_hash: tb_fw_hash { + oid = TRUSTED_BOOT_FW_HASH_OID; + }; + tb_fw_config_hash: tb_fw_config_hash { + oid = TRUSTED_BOOT_FW_CONFIG_HASH_OID; + }; + hw_config_hash: hw_config_hash { + oid = HW_CONFIG_HASH_OID; + }; + fw_config_hash: fw_config_hash { + oid = FW_CONFIG_HASH_OID; + }; + soc_fw_hash: soc_fw_hash { + oid = SOC_AP_FW_HASH_OID; + }; + soc_fw_config_hash: soc_fw_config_hash { + oid = SOC_FW_CONFIG_HASH_OID; + }; + rmm_hash: rmm_hash { + oid = RMM_HASH_OID; + }; + }; + + core_swd_key_cert: core_swd_key_cert { + root-certificate; + image-id = <CORE_SWD_KEY_CERT_ID>; + signing-key = <&swd_rot_pk>; + antirollback-counter = <&trusted_nv_ctr>; + + core_swd_pk: core_swd_pk { + oid = CORE_SWD_PK_OID; + }; + }; + + trusted_os_fw_content_cert: trusted_os_fw_content_cert { + image-id = <TRUSTED_OS_FW_CONTENT_CERT_ID>; + parent = <&core_swd_key_cert>; + signing-key = <&core_swd_pk>; + antirollback-counter = <&trusted_nv_ctr>; + + tos_fw_hash: tos_fw_hash { + oid = TRUSTED_OS_FW_HASH_OID; + }; + tos_fw_config_hash: tos_fw_config_hash { + oid = TRUSTED_OS_FW_CONFIG_HASH_OID; + }; + }; + + plat_key_cert: plat_key_cert { + root-certificate; + image-id = <PLAT_KEY_CERT_ID>; + signing-key = <&prot_pk>; + antirollback-counter = <&non_trusted_nv_ctr>; + + plat_pk: plat_pk { + oid = PLAT_PK_OID; + }; + }; + + non_trusted_fw_content_cert: non_trusted_fw_content_cert { + image-id = <NON_TRUSTED_FW_CONTENT_CERT_ID>; + parent = <&plat_key_cert>; + signing-key = <&plat_pk>; + antirollback-counter = <&non_trusted_nv_ctr>; + + nt_world_bl_hash: nt_world_bl_hash { + oid = NON_TRUSTED_WORLD_BOOTLOADER_HASH_OID; + }; + nt_fw_config_hash: nt_fw_config_hash { + oid = NON_TRUSTED_FW_CONFIG_HASH_OID; + }; + }; + +#if defined(SPD_spmd) + sip_sp_content_cert: sip_sp_content_cert { + image-id = <SIP_SP_CONTENT_CERT_ID>; + parent = <&core_swd_key_cert>; + signing-key = <&core_swd_pk>; + antirollback-counter = <&trusted_nv_ctr>; + + sp_pkg1_hash: sp_pkg1_hash { + oid = SP_PKG1_HASH_OID; + }; + sp_pkg2_hash: sp_pkg2_hash { + oid = SP_PKG2_HASH_OID; + }; + sp_pkg3_hash: sp_pkg3_hash { + oid = SP_PKG3_HASH_OID; + }; + sp_pkg4_hash: sp_pkg4_hash { + oid = SP_PKG4_HASH_OID; + }; + }; + + plat_sp_content_cert: plat_sp_content_cert { + image-id = <PLAT_SP_CONTENT_CERT_ID>; + parent = <&plat_key_cert>; + signing-key = <&plat_pk>; + antirollback-counter = <&non_trusted_nv_ctr>; + + sp_pkg5_hash: sp_pkg5_hash { + oid = SP_PKG5_HASH_OID; + }; + sp_pkg6_hash: sp_pkg6_hash { + oid = SP_PKG6_HASH_OID; + }; + sp_pkg7_hash: sp_pkg7_hash { + oid = SP_PKG7_HASH_OID; + }; + sp_pkg8_hash: sp_pkg8_hash { + oid = SP_PKG8_HASH_OID; + }; + }; +#endif + }; + + images { + compatible = "arm, img-descs"; + + hw_config { + image-id = <HW_CONFIG_ID>; + parent = <&cca_content_cert>; + hash = <&hw_config_hash>; + }; + + bl31_image { + image-id = <BL31_IMAGE_ID>; + parent = <&cca_content_cert>; + hash = <&soc_fw_hash>; + }; + + soc_fw_config { + image-id = <SOC_FW_CONFIG_ID>; + parent = <&cca_content_cert>; + hash = <&soc_fw_config_hash>; + }; + + rmm_image { + image-id = <RMM_IMAGE_ID>; + parent = <&cca_content_cert>; + hash = <&rmm_hash>; + }; + + bl32_image { + image-id = <BL32_IMAGE_ID>; + parent = <&trusted_os_fw_content_cert>; + hash = <&tos_fw_hash>; + }; + + tos_fw_config { + image-id = <TOS_FW_CONFIG_ID>; + parent = <&trusted_os_fw_content_cert>; + hash = <&tos_fw_config_hash>; + }; + + bl33_image { + image-id = <BL33_IMAGE_ID>; + parent = <&non_trusted_fw_content_cert>; + hash = <&nt_world_bl_hash>; + }; + + nt_fw_config { + image-id = <NT_FW_CONFIG_ID>; + parent = <&non_trusted_fw_content_cert>; + hash = <&nt_fw_config_hash>; + }; + +#if defined(SPD_spmd) + sp_pkg1 { + image-id = <SP_PKG1_ID>; + parent = <&sip_sp_content_cert>; + hash = <&sp_pkg1_hash>; + }; + + sp_pkg2 { + image-id = <SP_PKG2_ID>; + parent = <&sip_sp_content_cert>; + hash = <&sp_pkg2_hash>; + }; + + sp_pkg3 { + image-id = <SP_PKG3_ID>; + parent = <&sip_sp_content_cert>; + hash = <&sp_pkg3_hash>; + }; + + sp_pkg4 { + image-id = <SP_PKG4_ID>; + parent = <&sip_sp_content_cert>; + hash = <&sp_pkg4_hash>; + }; + + sp_pkg5 { + image-id = <SP_PKG5_ID>; + parent = <&plat_sp_content_cert>; + hash = <&sp_pkg5_hash>; + }; + + sp_pkg6 { + image-id = <SP_PKG6_ID>; + parent = <&plat_sp_content_cert>; + hash = <&sp_pkg6_hash>; + }; + + sp_pkg7 { + image-id = <SP_PKG7_ID>; + parent = <&plat_sp_content_cert>; + hash = <&sp_pkg7_hash>; + }; + + sp_pkg8 { + image-id = <SP_PKG8_ID>; + parent = <&plat_sp_content_cert>; + hash = <&sp_pkg8_hash>; + }; +#endif + }; +}; + +non_volatile_counters: non_volatile_counters { + compatible = "arm, non-volatile-counter"; + + #address-cells = <1>; + #size-cells = <0>; + + cca_nv_ctr: cca_nv_ctr { + id = <TRUSTED_NV_CTR_ID>; + oid = CCA_FW_NVCOUNTER_OID; + }; + + trusted_nv_ctr: trusted_nv_ctr { + id = <TRUSTED_NV_CTR_ID>; + oid = TRUSTED_FW_NVCOUNTER_OID; + }; + + non_trusted_nv_ctr: non_trusted_nv_ctr { + id = <NON_TRUSTED_NV_CTR_ID>; + oid = NON_TRUSTED_FW_NVCOUNTER_OID; + }; +}; + +rot_keys { + swd_rot_pk: swd_rot_pk { + oid = SWD_ROT_PK_OID; + }; + + prot_pk: prot_pk { + oid = PROT_PK_OID; + }; +}; diff --git a/fdts/dualroot_cot_descriptors.dtsi b/fdts/dualroot_cot_descriptors.dtsi new file mode 100644 index 00000000..bea7af5b --- /dev/null +++ b/fdts/dualroot_cot_descriptors.dtsi @@ -0,0 +1,314 @@ +/* + * Copyright (c) 2024, Arm Limited. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <tools_share/dualroot_oid.h> +#include <common/tbbr/tbbr_img_def.h> +#include <common/nv_cntr_ids.h> + +cot { + manifests { + compatible = "arm, cert-descs"; + + trusted_boot_fw_cert: trusted_boot_fw_cert { + root-certificate; + image-id =<TRUSTED_BOOT_FW_CERT_ID>; + antirollback-counter = <&trusted_nv_ctr>; + + tb_fw_hash: tb_fw_hash { + oid = TRUSTED_BOOT_FW_HASH_OID; + }; + tb_fw_config_hash: tb_fw_config_hash { + oid = TRUSTED_BOOT_FW_CONFIG_HASH_OID; + }; + hw_config_hash: hw_config_hash { + oid = HW_CONFIG_HASH_OID; + }; + fw_config_hash: fw_config_hash { + oid = FW_CONFIG_HASH_OID; + }; + }; + + trusted_key_cert: trusted_key_cert { + root-certificate; + image-id = <TRUSTED_KEY_CERT_ID>; + antirollback-counter = <&trusted_nv_ctr>; + + trusted_world_pk: trusted_world_pk { + oid = TRUSTED_WORLD_PK_OID; + }; + }; + + scp_fw_key_cert: scp_fw_key_cert { + image-id = <SCP_FW_KEY_CERT_ID>; + parent = <&trusted_key_cert>; + signing-key = <&trusted_world_pk>; + antirollback-counter = <&trusted_nv_ctr>; + + scp_fw_content_pk: scp_fw_content_pk { + oid = SCP_FW_CONTENT_CERT_PK_OID; + }; + }; + + scp_fw_content_cert: scp_fw_content_cert { + image-id = <SCP_FW_CONTENT_CERT_ID>; + parent = <&scp_fw_key_cert>; + signing-key = <&scp_fw_content_pk>; + antirollback-counter = <&trusted_nv_ctr>; + + scp_fw_hash: scp_fw_hash { + oid = SCP_FW_HASH_OID; + }; + }; + + soc_fw_key_cert: soc_fw_key_cert { + image-id = <SOC_FW_KEY_CERT_ID>; + parent = <&trusted_key_cert>; + signing-key = <&trusted_world_pk>; + antirollback-counter = <&trusted_nv_ctr>; + soc_fw_content_pk: soc_fw_content_pk { + oid = SOC_FW_CONTENT_CERT_PK_OID; + }; + }; + + soc_fw_content_cert: soc_fw_content_cert { + image-id = <SOC_FW_CONTENT_CERT_ID>; + parent = <&soc_fw_key_cert>; + signing-key = <&soc_fw_content_pk>; + antirollback-counter = <&trusted_nv_ctr>; + + soc_fw_hash: soc_fw_hash { + oid = SOC_AP_FW_HASH_OID; + }; + soc_fw_config_hash: soc_fw_config_hash { + oid = SOC_FW_CONFIG_HASH_OID; + }; + }; + + trusted_os_fw_key_cert: trusted_os_fw_key_cert { + image-id = <TRUSTED_OS_FW_KEY_CERT_ID>; + parent = <&trusted_key_cert>; + signing-key = <&trusted_world_pk>; + antirollback-counter = <&trusted_nv_ctr>; + + tos_fw_content_pk: tos_fw_content_pk { + oid = TRUSTED_OS_FW_CONTENT_CERT_PK_OID; + }; + }; + + trusted_os_fw_content_cert: trusted_os_fw_content_cert { + image-id = <TRUSTED_OS_FW_CONTENT_CERT_ID>; + parent = <&trusted_os_fw_key_cert>; + signing-key = <&tos_fw_content_pk>; + antirollback-counter = <&trusted_nv_ctr>; + + tos_fw_hash: tos_fw_hash { + oid = TRUSTED_OS_FW_HASH_OID; + }; + tos_fw_extra1_hash: tos_fw_extra1_hash { + oid = TRUSTED_OS_FW_EXTRA1_HASH_OID; + }; + tos_fw_extra2_hash: tos_fw_extra2_hash { + oid = TRUSTED_OS_FW_EXTRA2_HASH_OID; + }; + tos_fw_config_hash: tos_fw_config_hash { + oid = TRUSTED_OS_FW_CONFIG_HASH_OID; + }; + }; + + non_trusted_fw_content_cert: non_trusted_fw_content_cert { + root-certificate; + image-id = <NON_TRUSTED_FW_CONTENT_CERT_ID>; + signing-key = <&prot_pk>; + antirollback-counter = <&non_trusted_nv_ctr>; + + nt_world_bl_hash: nt_world_bl_hash { + oid = NON_TRUSTED_WORLD_BOOTLOADER_HASH_OID; + }; + nt_fw_config_hash: nt_fw_config_hash { + oid = NON_TRUSTED_FW_CONFIG_HASH_OID; + }; + }; + +#if defined(SPD_spmd) + sip_sp_content_cert: sip_sp_content_cert { + image-id = <SIP_SP_CONTENT_CERT_ID>; + parent = <&trusted_key_cert>; + signing-key = <&trusted_world_pk>; + antirollback-counter = <&trusted_nv_ctr>; + + sp_pkg1_hash: sp_pkg1_hash { + oid = SP_PKG1_HASH_OID; + }; + sp_pkg2_hash: sp_pkg2_hash { + oid = SP_PKG2_HASH_OID; + }; + sp_pkg3_hash: sp_pkg3_hash { + oid = SP_PKG3_HASH_OID; + }; + sp_pkg4_hash: sp_pkg4_hash { + oid = SP_PKG4_HASH_OID; + }; + }; + + plat_sp_content_cert: plat_sp_content_cert { + root-certificate; + image-id = <PLAT_SP_CONTENT_CERT_ID>; + signing-key = <&prot_pk>; + antirollback-counter = <&non_trusted_nv_ctr>; + + sp_pkg5_hash: sp_pkg5_hash { + oid = SP_PKG5_HASH_OID; + }; + sp_pkg6_hash: sp_pkg6_hash { + oid = SP_PKG6_HASH_OID; + }; + sp_pkg7_hash: sp_pkg7_hash { + oid = SP_PKG7_HASH_OID; + }; + sp_pkg8_hash: sp_pkg8_hash { + oid = SP_PKG8_HASH_OID; + }; + }; +#endif + }; + + images { + compatible = "arm, img-descs"; + + hw_config { + image-id = <HW_CONFIG_ID>; + parent = <&trusted_boot_fw_cert>; + hash = <&hw_config_hash>; + }; + + scp_bl2_image { + image-id = <SCP_BL2_IMAGE_ID>; + parent = <&scp_fw_content_cert>; + hash = <&scp_fw_hash>; + }; + + bl31_image { + image-id = <BL31_IMAGE_ID>; + parent = <&soc_fw_content_cert>; + hash = <&soc_fw_hash>; + }; + + soc_fw_config { + image-id = <SOC_FW_CONFIG_ID>; + parent = <&soc_fw_content_cert>; + hash = <&soc_fw_config_hash>; + }; + + bl32_image { + image-id = <BL32_IMAGE_ID>; + parent = <&trusted_os_fw_content_cert>; + hash = <&tos_fw_hash>; + }; + + bl32_extra1_image { + image-id = <BL32_EXTRA1_IMAGE_ID>; + parent = <&trusted_os_fw_content_cert>; + hash = <&tos_fw_extra1_hash>; + }; + + bl32_extra2_image { + image-id = <BL32_EXTRA2_IMAGE_ID>; + parent = <&trusted_os_fw_content_cert>; + hash = <&tos_fw_extra2_hash>; + }; + + tos_fw_config { + image-id = <TOS_FW_CONFIG_ID>; + parent = <&trusted_os_fw_content_cert>; + hash = <&tos_fw_config_hash>; + }; + + bl33_image { + image-id = <BL33_IMAGE_ID>; + parent = <&non_trusted_fw_content_cert>; + hash = <&nt_world_bl_hash>; + }; + + nt_fw_config { + image-id = <NT_FW_CONFIG_ID>; + parent = <&non_trusted_fw_content_cert>; + hash = <&nt_fw_config_hash>; + }; + +#if defined(SPD_spmd) + sp_pkg1 { + image-id = <SP_PKG1_ID>; + parent = <&sip_sp_content_cert>; + hash = <&sp_pkg1_hash>; + }; + + sp_pkg2 { + image-id = <SP_PKG2_ID>; + parent = <&sip_sp_content_cert>; + hash = <&sp_pkg2_hash>; + }; + + sp_pkg3 { + image-id = <SP_PKG3_ID>; + parent = <&sip_sp_content_cert>; + hash = <&sp_pkg3_hash>; + }; + + sp_pkg4 { + image-id = <SP_PKG4_ID>; + parent = <&sip_sp_content_cert>; + hash = <&sp_pkg4_hash>; + }; + + sp_pkg5 { + image-id = <SP_PKG5_ID>; + parent = <&plat_sp_content_cert>; + hash = <&sp_pkg5_hash>; + }; + + sp_pkg6 { + image-id = <SP_PKG6_ID>; + parent = <&plat_sp_content_cert>; + hash = <&sp_pkg6_hash>; + }; + + sp_pkg7 { + image-id = <SP_PKG7_ID>; + parent = <&plat_sp_content_cert>; + hash = <&sp_pkg7_hash>; + }; + + sp_pkg8 { + image-id = <SP_PKG8_ID>; + parent = <&plat_sp_content_cert>; + hash = <&sp_pkg8_hash>; + }; +#endif + }; +}; + +non_volatile_counters: non_volatile_counters { + compatible = "arm, non-volatile-counter"; + + #address-cells = <1>; + #size-cells = <0>; + + trusted_nv_ctr: trusted_nv_ctr { + id = <TRUSTED_NV_CTR_ID>; + oid = TRUSTED_FW_NVCOUNTER_OID; + }; + + non_trusted_nv_ctr: non_trusted_nv_ctr { + id = <NON_TRUSTED_NV_CTR_ID>; + oid = NON_TRUSTED_FW_NVCOUNTER_OID; + }; +}; + +rot_keys { + prot_pk: prot_pk { + oid = PROT_PK_OID; + }; +}; diff --git a/fdts/fvp-base-psci-common.dtsi b/fdts/fvp-base-psci-common.dtsi index 79cf37d3..583bba70 100644 --- a/fdts/fvp-base-psci-common.dtsi +++ b/fdts/fvp-base-psci-common.dtsi @@ -27,11 +27,12 @@ #address-cells = <2>; #size-cells = <2>; + chosen { + stdout-path = "serial0:115200n8"; #if (ENABLE_RME == 1) - chosen { bootargs = "console=ttyAMA0 earlycon=pl011,0x1c090000 root=/dev/vda ip=on";}; -#else - chosen {}; + bootargs = "console=ttyAMA0 earlycon=pl011,0x1c090000 root=/dev/vda ip=on"; #endif + }; aliases { serial0 = &v2m_serial0; @@ -243,6 +244,9 @@ <0 0 39 &gic 0 GIC_SPI 39 IRQ_TYPE_LEVEL_HIGH>, <0 0 40 &gic 0 GIC_SPI 40 IRQ_TYPE_LEVEL_HIGH>, <0 0 41 &gic 0 GIC_SPI 41 IRQ_TYPE_LEVEL_HIGH>, - <0 0 42 &gic 0 GIC_SPI 42 IRQ_TYPE_LEVEL_HIGH>; + <0 0 42 &gic 0 GIC_SPI 42 IRQ_TYPE_LEVEL_HIGH>, + <0 0 43 &gic 0 GIC_SPI 43 IRQ_TYPE_LEVEL_HIGH>, + <0 0 44 &gic 0 GIC_SPI 44 IRQ_TYPE_LEVEL_HIGH>, + <0 0 46 &gic 0 GIC_SPI 46 IRQ_TYPE_LEVEL_HIGH>; }; }; diff --git a/fdts/fvp-foundation-gicv2-psci.dts b/fdts/fvp-foundation-gicv2-psci.dts index 5a82c460..653c75fb 100644 --- a/fdts/fvp-foundation-gicv2-psci.dts +++ b/fdts/fvp-foundation-gicv2-psci.dts @@ -26,7 +26,9 @@ #address-cells = <2>; #size-cells = <2>; - chosen { }; + chosen { + stdout-path = "serial0:115200n8"; + }; aliases { serial0 = &v2m_serial0; diff --git a/fdts/fvp-foundation-gicv3-psci.dts b/fdts/fvp-foundation-gicv3-psci.dts index e1249d45..28272970 100644 --- a/fdts/fvp-foundation-gicv3-psci.dts +++ b/fdts/fvp-foundation-gicv3-psci.dts @@ -26,7 +26,9 @@ #address-cells = <2>; #size-cells = <2>; - chosen { }; + chosen { + stdout-path = "serial0:115200n8"; + }; aliases { serial0 = &v2m_serial0; diff --git a/fdts/rd1ae.dts b/fdts/rd1ae.dts new file mode 100644 index 00000000..3060b5a7 --- /dev/null +++ b/fdts/rd1ae.dts @@ -0,0 +1,416 @@ +/* + * Copyright (c) 2024, Arm Limited. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +/dts-v1/; + +#include <dt-bindings/interrupt-controller/arm-gic.h> + +/ { + model = "RD-1 AE"; + compatible = "arm,rd1ae", "arm,neoverse"; + interrupt-parent = <&gic>; + #address-cells = <2>; + #size-cells = <2>; + + chosen { + stdout-path = &soc_serial0; + }; + + cpus { + #address-cells = <2>; + #size-cells = <0>; + + cpu0: cpu@0 { + device_type = "cpu"; + compatible = "arm,neoverse-v3"; + reg = <0x0 0x0>; + enable-method = "psci"; + i-cache-size = <0x10000>; + i-cache-line-size = <0x40>; + i-cache-sets = <0x100>; + d-cache-size = <0x10000>; + d-cache-line-size = <0x40>; + d-cache-sets = <0x100>; + }; + cpu1: cpu@10000 { + device_type = "cpu"; + compatible = "arm,neoverse-v3"; + reg = <0x0 0x10000>; + enable-method = "psci"; + i-cache-size = <0x10000>; + i-cache-line-size = <0x40>; + i-cache-sets = <0x100>; + d-cache-size = <0x10000>; + d-cache-line-size = <0x40>; + d-cache-sets = <0x100>; + }; + cpu2: cpu@20000 { + device_type = "cpu"; + compatible = "arm,neoverse-v3"; + reg = <0x0 0x20000>; + enable-method = "psci"; + i-cache-size = <0x10000>; + i-cache-line-size = <0x40>; + i-cache-sets = <0x100>; + d-cache-size = <0x10000>; + d-cache-line-size = <0x40>; + d-cache-sets = <0x100>; + }; + cpu3: cpu@30000 { + device_type = "cpu"; + compatible = "arm,neoverse-v3"; + reg = <0x0 0x30000>; + enable-method = "psci"; + i-cache-size = <0x10000>; + i-cache-line-size = <0x40>; + i-cache-sets = <0x100>; + d-cache-size = <0x10000>; + d-cache-line-size = <0x40>; + d-cache-sets = <0x100>; + }; + cpu4: cpu@40000 { + device_type = "cpu"; + compatible = "arm,neoverse-v3"; + reg = <0x0 0x40000>; + enable-method = "psci"; + i-cache-size = <0x10000>; + i-cache-line-size = <0x40>; + i-cache-sets = <0x100>; + d-cache-size = <0x10000>; + d-cache-line-size = <0x40>; + d-cache-sets = <0x100>; + }; + cpu5: cpu@50000 { + device_type = "cpu"; + compatible = "arm,neoverse-v3"; + reg = <0x0 0x50000>; + enable-method = "psci"; + i-cache-size = <0x10000>; + i-cache-line-size = <0x40>; + i-cache-sets = <0x100>; + d-cache-size = <0x10000>; + d-cache-line-size = <0x40>; + d-cache-sets = <0x100>; + }; + cpu6: cpu@60000 { + device_type = "cpu"; + compatible = "arm,neoverse-v3"; + reg = <0x0 0x60000>; + enable-method = "psci"; + i-cache-size = <0x10000>; + i-cache-line-size = <0x40>; + i-cache-sets = <0x100>; + d-cache-size = <0x10000>; + d-cache-line-size = <0x40>; + d-cache-sets = <0x100>; + }; + cpu7: cpu@70000 { + device_type = "cpu"; + compatible = "arm,neoverse-v3"; + reg = <0x0 0x70000>; + enable-method = "psci"; + i-cache-size = <0x10000>; + i-cache-line-size = <0x40>; + i-cache-sets = <0x100>; + d-cache-size = <0x10000>; + d-cache-line-size = <0x40>; + d-cache-sets = <0x100>; + }; + cpu8: cpu@80000 { + device_type = "cpu"; + compatible = "arm,neoverse-v3"; + reg = <0x0 0x80000>; + enable-method = "psci"; + i-cache-size = <0x10000>; + i-cache-line-size = <0x40>; + i-cache-sets = <0x100>; + d-cache-size = <0x10000>; + d-cache-line-size = <0x40>; + d-cache-sets = <0x100>; + }; + cpu9: cpu@90000 { + device_type = "cpu"; + compatible = "arm,neoverse-v3"; + reg = <0x0 0x90000>; + enable-method = "psci"; + i-cache-size = <0x10000>; + i-cache-line-size = <0x40>; + i-cache-sets = <0x100>; + d-cache-size = <0x10000>; + d-cache-line-size = <0x40>; + d-cache-sets = <0x100>; + }; + cpu10: cpu@a0000 { + device_type = "cpu"; + compatible = "arm,neoverse-v3"; + reg = <0x0 0xa0000>; + enable-method = "psci"; + i-cache-size = <0x10000>; + i-cache-line-size = <0x40>; + i-cache-sets = <0x100>; + d-cache-size = <0x10000>; + d-cache-line-size = <0x40>; + d-cache-sets = <0x100>; + }; + cpu11: cpu@b0000 { + device_type = "cpu"; + compatible = "arm,neoverse-v3"; + reg = <0x0 0xb0000>; + enable-method = "psci"; + i-cache-size = <0x10000>; + i-cache-line-size = <0x40>; + i-cache-sets = <0x100>; + d-cache-size = <0x10000>; + d-cache-line-size = <0x40>; + d-cache-sets = <0x100>; + }; + cpu12: cpu@c0000 { + device_type = "cpu"; + compatible = "arm,neoverse-v3"; + reg = <0x0 0xc0000>; + enable-method = "psci"; + i-cache-size = <0x10000>; + i-cache-line-size = <0x40>; + i-cache-sets = <0x100>; + d-cache-size = <0x10000>; + d-cache-line-size = <0x40>; + d-cache-sets = <0x100>; + }; + cpu13: cpu@d0000 { + device_type = "cpu"; + compatible = "arm,neoverse-v3"; + reg = <0x0 0xd0000>; + enable-method = "psci"; + i-cache-size = <0x10000>; + i-cache-line-size = <0x40>; + i-cache-sets = <0x100>; + d-cache-size = <0x10000>; + d-cache-line-size = <0x40>; + d-cache-sets = <0x100>; + }; + cpu14: cpu@e0000 { + device_type = "cpu"; + compatible = "arm,neoverse-v3"; + reg = <0x0 0xe0000>; + enable-method = "psci"; + i-cache-size = <0x10000>; + i-cache-line-size = <0x40>; + i-cache-sets = <0x100>; + d-cache-size = <0x10000>; + d-cache-line-size = <0x40>; + d-cache-sets = <0x100>; + }; + cpu15: cpu@f0000 { + device_type = "cpu"; + compatible = "arm,neoverse-v3"; + reg = <0x0 0xf0000>; + enable-method = "psci"; + i-cache-size = <0x10000>; + i-cache-line-size = <0x40>; + i-cache-sets = <0x100>; + d-cache-size = <0x10000>; + d-cache-line-size = <0x40>; + d-cache-sets = <0x100>; + }; + }; + + memory@80000000 { + device_type = "memory"; + /* + * 0x7fc0 0000 - 0x7fff ffff : BL32 + * 0x7fbf 0000 - 0x7fbf ffff : FFA_SHARED_MM_BUF + */ + reg = <0x00000000 0x80000000 0 0x7fbf0000>, + <0x00000080 0x80000000 0 0x80000000>; + }; + + timer { + compatible = "arm,armv8-timer"; + interrupts = <GIC_PPI 13 IRQ_TYPE_LEVEL_LOW>, + <GIC_PPI 14 IRQ_TYPE_LEVEL_LOW>, + <GIC_PPI 11 IRQ_TYPE_LEVEL_LOW>, + <GIC_PPI 10 IRQ_TYPE_LEVEL_LOW>; + }; + + soc_clk24mhz: clk24mhz { + compatible = "fixed-clock"; + #clock-cells = <0>; + clock-frequency = <24000000>; + clock-output-names = "refclk24mhz"; + }; + + soc_refclk1mhz: refclk1mhz { + compatible = "fixed-clock"; + #clock-cells = <0>; + clock-frequency = <1000000>; + clock-output-names = "refclk1mhz"; + }; + + soc { + compatible = "simple-bus"; + #address-cells = <2>; + #size-cells = <2>; + ranges; + + gic: interrupt-controller@30000000 { + compatible = "arm,gic-v3"; + reg = <0x0 0x30000000 0 0x10000>, // GICD + <0x0 0x301c0000 0 0x8000000>; // GICR + #interrupt-cells = <3>; + #address-cells = <2>; + #size-cells = <2>; + ranges; + interrupt-controller; + interrupts = <GIC_PPI 9 IRQ_TYPE_LEVEL_HIGH>; + + its1: msi-controller@30040000 { + compatible = "arm,gic-v3-its"; + reg = <0x0 0x30040000 0x0 0x40000>; + msi-controller; + #msi-cells = <1>; + }; + its2: msi-controller@30080000 { + compatible = "arm,gic-v3-its"; + reg = <0x0 0x30080000 0x0 0x40000>; + msi-controller; + #msi-cells = <1>; + }; + its3: msi-controller@300c0000 { + compatible = "arm,gic-v3-its"; + reg = <0x0 0x300c0000 0x0 0x40000>; + msi-controller; + #msi-cells = <1>; + }; + its4: msi-controller@30100000 { + compatible = "arm,gic-v3-its"; + reg = <0x0 0x30100000 0x0 0x40000>; + msi-controller; + #msi-cells = <1>; + }; + its5: msi-controller@30140000 { + compatible = "arm,gic-v3-its"; + reg = <0x0 0x30140000 0x0 0x40000>; + msi-controller; + #msi-cells = <1>; + }; + its6: msi-controller@30180000 { + compatible = "arm,gic-v3-its"; + reg = <0x0 0x30180000 0x0 0x40000>; + msi-controller; + #msi-cells = <1>; + }; + }; + + soc_serial0: serial@2a400000 { + compatible = "arm,pl011", "arm,primecell"; + reg = <0x0 0x2a400000 0x0 0x10000>; + interrupts = <GIC_SPI 80 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&soc_clk24mhz>, <&soc_clk24mhz>; + clock-names = "uartclk", "apb_pclk"; + }; + + watchdog@2a440000 { + compatible = "arm,sbsa-gwdt"; + reg = <0x0 0x2a440000 0 0x1000>, + <0x0 0x2a450000 0 0x1000>; + interrupts = <GIC_SPI 78 IRQ_TYPE_LEVEL_HIGH>; + }; + + rtc@c170000 { + compatible = "arm,pl031", "arm,primecell"; + reg = <0x0 0x0c170000 0x0 0x10000>; + interrupts = <GIC_SPI 132 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&soc_clk24mhz>; + clock-names = "apb_pclk"; + }; + + virtio-net@c150000 { + compatible = "virtio,mmio"; + reg = <0x0 0xc150000 0x0 0x200>; + interrupts = <GIC_SPI 154 IRQ_TYPE_LEVEL_HIGH>; + }; + + virtio-block@c130000 { + compatible = "virtio,mmio"; + reg = <0x0 0xc130000 0x0 0x200>; + interrupts = <GIC_SPI 152 IRQ_TYPE_LEVEL_HIGH>; + }; + + virtio-rng@c140000 { + compatible = "virtio,mmio"; + reg = <0x0 0xc140000 0x0 0x200>; + interrupts = <GIC_SPI 156 IRQ_TYPE_LEVEL_HIGH>; + }; + + pci@4000000000 { + #address-cells = <0x03>; + #size-cells = <0x02>; + compatible = "pci-host-ecam-generic"; + device_type = "pci"; + bus-range = <0x00 0x11>; + reg = <0x40 0x00 0x00 0x04000000>; + ranges = <0x43000000 0x40 0x40000000 0x40 0x40000000 0x10 0x00000000 + 0x02000000 0x00 0x60000000 0x00 0x60000000 0x00 0x08000000 + 0x01000000 0x00 0x00 0x00 0x77800000 0x00 0x800000>; + msi-map = <0x00 &its1 0x40000 0x10000>; + iommu-map = <0x00 &smmu 0x40000 0x10000>; + dma-coherent; + }; + + smmu: iommu@280000000 { + compatible = "arm,smmu-v3"; + reg = <0x2 0x80000000 0x0 0x100000>; + dma-coherent; + #iommu-cells = <1>; + interrupts = <1 210 1>, + <1 211 1>, + <1 212 1>, + <1 213 1>; + interrupt-names = "eventq", "priq", "cmdq-sync", "gerror"; + msi-parent = <&its1 0x10000>; + }; + + sysreg: sysreg@c010000 { + compatible = "arm,vexpress-sysreg"; + reg = <0x0 0xc010000 0x0 0x1000>; + gpio-controller; + #gpio-cells = <2>; + }; + + fixed_3v3: v2m-3v3@c011000 { + compatible = "regulator-fixed"; + reg = <0x0 0xc011000 0x0 0x1000>; + regulator-name = "3V3"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + regulator-always-on; + }; + + mmci@c050000 { + compatible = "arm,pl180", "arm,primecell"; + reg = <0x0 0xc050000 0x0 0x1000>; + interrupts = <0 0x8B 0x4>, + <0 0x8C 0x4>; + cd-gpios = <&sysreg 0 0>; + wp-gpios = <&sysreg 1 0>; + bus-width = <8>; + max-frequency = <12000000>; + vmmc-supply = <&fixed_3v3>; + clocks = <&soc_clk24mhz>, <&soc_clk24mhz>; + clock-names = "mclk", "apb_pclk"; + }; + + }; + + psci { + compatible = "arm,psci-1.0", "arm,psci-0.2", "arm,psci"; + method = "smc"; + cpu_suspend = <0xc4000001>; + cpu_off = <0x84000002>; + cpu_on = <0x84000003>; + }; + +}; diff --git a/fdts/rtsm_ve-motherboard.dtsi b/fdts/rtsm_ve-motherboard.dtsi index 0a824b34..5a34aae4 100644 --- a/fdts/rtsm_ve-motherboard.dtsi +++ b/fdts/rtsm_ve-motherboard.dtsi @@ -230,6 +230,25 @@ interrupts = <42>; }; + virtio@140000 { + compatible = "virtio,mmio"; + reg = <0x140000 0x200>; + interrupts = <43>; + }; + + virtio@150000 { + compatible = "virtio,mmio"; + reg = <0x150000 0x200>; + interrupts = <44>; + }; + + virtio@200000 { + compatible = "virtio,mmio"; + reg = <0x200000 0x200>; + interrupts = <46>; + status = "disabled"; + }; + rtc@170000 { compatible = "arm,pl031", "arm,primecell"; reg = <0x170000 0x1000>; diff --git a/fdts/stm32mp1-cot-descriptors.dtsi b/fdts/stm32mp1-cot-descriptors.dtsi index eb632ffa..05326be7 100644 --- a/fdts/stm32mp1-cot-descriptors.dtsi +++ b/fdts/stm32mp1-cot-descriptors.dtsi @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020-2022, ARM Limited. All rights reserved. + * Copyright (c) 2020-2024, ARM Limited. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -15,7 +15,7 @@ cot { stm32mp_cfg_cert: stm32mp_cfg_cert { root-certificate; image-id = <STM32MP_CONFIG_CERT_ID>; - antirollback-counter = <&trusted_nv_counter>; + antirollback-counter = <&trusted_nv_ctr>; hw_config_hash: hw_config_hash { oid = HW_CONFIG_HASH_OID; @@ -29,7 +29,7 @@ cot { trusted_key_cert: trusted_key_cert { root-certificate; image-id = <TRUSTED_KEY_CERT_ID>; - antirollback-counter = <&trusted_nv_counter>; + antirollback-counter = <&trusted_nv_ctr>; trusted_world_pk: trusted_world_pk { oid = TRUSTED_WORLD_PK_OID; @@ -43,7 +43,7 @@ cot { image-id = <TRUSTED_OS_FW_KEY_CERT_ID>; parent = <&trusted_key_cert>; signing-key = <&trusted_world_pk>; - antirollback-counter = <&trusted_nv_counter>; + antirollback-counter = <&trusted_nv_ctr>; tos_fw_content_pk: tos_fw_content_pk { oid = TRUSTED_OS_FW_CONTENT_CERT_PK_OID; @@ -54,7 +54,7 @@ cot { image-id = <TRUSTED_OS_FW_CONTENT_CERT_ID>; parent = <&trusted_os_fw_key_cert>; signing-key = <&tos_fw_content_pk>; - antirollback-counter = <&trusted_nv_counter>; + antirollback-counter = <&trusted_nv_ctr>; tos_fw_hash: tos_fw_hash { oid = TRUSTED_OS_FW_HASH_OID; @@ -74,7 +74,7 @@ cot { image-id = <NON_TRUSTED_FW_KEY_CERT_ID>; parent = <&trusted_key_cert>; signing-key = <&non_trusted_world_pk>; - antirollback-counter = <&non_trusted_nv_counter>; + antirollback-counter = <&non_trusted_nv_ctr>; nt_fw_content_pk: nt_fw_content_pk { oid = NON_TRUSTED_FW_CONTENT_CERT_PK_OID; @@ -85,7 +85,7 @@ cot { image-id = <NON_TRUSTED_FW_CONTENT_CERT_ID>; parent = <&non_trusted_fw_key_cert>; signing-key = <&nt_fw_content_pk>; - antirollback-counter = <&non_trusted_nv_counter>; + antirollback-counter = <&non_trusted_nv_ctr>; nt_world_bl_hash: nt_world_bl_hash { oid = NON_TRUSTED_WORLD_BOOTLOADER_HASH_OID; @@ -144,12 +144,12 @@ non_volatile_counters: non_volatile_counters { #address-cells = <1>; #size-cells = <0>; - trusted_nv_counter: trusted_nv_counter { + trusted_nv_ctr: trusted_nv_ctr { id = <TRUSTED_NV_CTR_ID>; oid = TRUSTED_FW_NVCOUNTER_OID; }; - non_trusted_nv_counter: non_trusted_nv_counter { + non_trusted_nv_ctr: non_trusted_nv_ctr { id = <NON_TRUSTED_NV_CTR_ID>; oid = NON_TRUSTED_FW_NVCOUNTER_OID; }; diff --git a/fdts/stm32mp131.dtsi b/fdts/stm32mp131.dtsi index 8bcf363b..520d90be 100644 --- a/fdts/stm32mp131.dtsi +++ b/fdts/stm32mp131.dtsi @@ -1,6 +1,6 @@ // SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) /* - * Copyright (C) 2022-2023, STMicroelectronics - All Rights Reserved + * Copyright (C) 2022-2024, STMicroelectronics - All Rights Reserved * Author: Alexandre Torgue <alexandre.torgue@foss.st.com> for STMicroelectronics. */ #include <dt-bindings/clock/stm32mp13-clks.h> @@ -420,25 +420,25 @@ #address-cells = <1>; #size-cells = <1>; - cfg0_otp: cfg0_otp@0 { + cfg0_otp: cfg0-otp@0 { reg = <0x0 0x2>; }; part_number_otp: part-number-otp@4 { reg = <0x4 0x2>; }; - monotonic_otp: monotonic_otp@10 { + monotonic_otp: monotonic-otp@10 { reg = <0x10 0x4>; }; - nand_otp: cfg9_otp@24 { + nand_otp: cfg9-otp@24 { reg = <0x24 0x4>; }; - nand2_otp: cfg10_otp@28 { + nand2_otp: cfg10-otp@28 { reg = <0x28 0x4>; }; - uid_otp: uid_otp@34 { + uid_otp: uid-otp@34 { reg = <0x34 0xc>; }; - hw2_otp: hw2_otp@48 { + hw2_otp: hw2-otp@48 { reg = <0x48 0x4>; }; ts_cal1: calib@5c { @@ -447,14 +447,14 @@ ts_cal2: calib@5e { reg = <0x5e 0x2>; }; - pkh_otp: pkh_otp@60 { + pkh_otp: pkh-otp@60 { reg = <0x60 0x20>; }; - mac_addr: mac_addr@e4 { + mac_addr: mac@e4 { reg = <0xe4 0xc>; st,non-secure-otp; }; - enckey_otp: enckey_otp@170 { + oem_enc_key: oem-enc-key@170 { reg = <0x170 0x10>; }; }; diff --git a/fdts/stm32mp135f-dk.dts b/fdts/stm32mp135f-dk.dts index 12046920..08fbbb9a 100644 --- a/fdts/stm32mp135f-dk.dts +++ b/fdts/stm32mp135f-dk.dts @@ -1,6 +1,6 @@ // SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) /* - * Copyright (C) STMicroelectronics 2022 - All Rights Reserved + * Copyright (C) 2022-2024, STMicroelectronics - All Rights Reserved * Author: Alexandre Torgue <alexandre.torgue@foss.st.com> for STMicroelectronics. */ @@ -50,7 +50,7 @@ }; &bsec { - board_id: board_id@f0 { + board_id: board-id@f0 { reg = <0xf0 0x4>; st,non-secure-otp; }; @@ -190,7 +190,6 @@ CLK_AXI_PLL2P CLK_MLAHBS_PLL3 CLK_CKPER_HSE - CLK_RTC_LSE CLK_SDMMC1_PLL4P CLK_SDMMC2_PLL4P CLK_STGEN_HSE @@ -212,16 +211,9 @@ DIV(DIV_APB4, 1) DIV(DIV_APB5, 2) DIV(DIV_APB6, 1) - DIV(DIV_RTC, 0) >; st,pll_vco { - pll1_vco_1300Mhz: pll1-vco-1300Mhz { - src = < CLK_PLL12_HSE >; - divmn = < 2 80 >; - frac = < 0x800 >; - }; - pll2_vco_1066Mhz: pll2-vco-1066Mhz { src = <CLK_PLL12_HSE>; divmn = <2 65>; @@ -240,19 +232,6 @@ }; }; - /* VCO = 1300.0 MHz => P = 650 (CPU) */ - pll1:st,pll@0 { - compatible = "st,stm32mp1-pll"; - reg = <0>; - - st,pll = < &pll1_cfg1 >; - - pll1_cfg1: pll1_cfg1 { - st,pll_vco = < &pll1_vco_1300Mhz >; - st,pll_div_pqr = < 0 1 1 >; - }; - }; - /* VCO = 1066.0 MHz => P = 266 (AXI), Q = 266, R = 533 (DDR) */ pll2:st,pll@1 { compatible = "st,stm32mp1-pll"; diff --git a/fdts/stm32mp15-fw-config.dtsi b/fdts/stm32mp15-fw-config.dtsi index d5836729..6f478d49 100644 --- a/fdts/stm32mp15-fw-config.dtsi +++ b/fdts/stm32mp15-fw-config.dtsi @@ -1,6 +1,6 @@ // SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) /* - * Copyright (c) 2021-2022, STMicroelectronics - All Rights Reserved + * Copyright (c) 2021-2024, STMicroelectronics - All Rights Reserved */ #include <common/tbbr/tbbr_img_def.h> @@ -14,12 +14,9 @@ #define DDR_NS_BASE STM32MP_DDR_BASE #ifdef AARCH32_SP_OPTEE -/* OP-TEE reserved shared memory: located at DDR top or null size */ -#define DDR_SHARE_SIZE STM32MP_DDR_SHMEM_SIZE -#define DDR_SHARE_BASE (STM32MP_DDR_BASE + (DDR_SIZE - DDR_SHARE_SIZE)) -/* OP-TEE secure memory: located right below OP-TEE reserved shared memory */ +/* OP-TEE secure memory: located at DDR top */ #define DDR_SEC_SIZE STM32MP_DDR_S_SIZE -#define DDR_SEC_BASE (DDR_SHARE_BASE - DDR_SEC_SIZE) +#define DDR_SEC_BASE (STM32MP_DDR_BASE + (DDR_SIZE - DDR_SEC_SIZE)) #define DDR_NS_SIZE (DDR_SEC_BASE - DDR_NS_BASE) #else /* !AARCH32_SP_OPTEE */ #define DDR_NS_SIZE DDR_SIZE @@ -70,10 +67,6 @@ memory-ranges = < DDR_NS_BASE DDR_NS_SIZE TZC_REGION_S_NONE TZC_REGION_NSEC_ALL_ACCESS_RDWR DDR_SEC_BASE DDR_SEC_SIZE TZC_REGION_S_RDWR 0 -#if STM32MP15_OPTEE_RSV_SHM - DDR_SHARE_BASE DDR_SHARE_SIZE TZC_REGION_S_NONE - TZC_REGION_ACCESS_RDWR(STM32MP1_TZC_A7_ID) -#endif >; #else memory-ranges = < diff --git a/fdts/stm32mp151.dtsi b/fdts/stm32mp151.dtsi index 7a22a1c7..449ddbbf 100644 --- a/fdts/stm32mp151.dtsi +++ b/fdts/stm32mp151.dtsi @@ -1,6 +1,6 @@ // SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) /* - * Copyright (c) 2017-2023, STMicroelectronics - All Rights Reserved + * Copyright (c) 2017-2024, STMicroelectronics - All Rights Reserved * Author: Ludovic Barre <ludovic.barre@st.com> for STMicroelectronics. */ #include <dt-bindings/interrupt-controller/arm-gic.h> @@ -458,25 +458,25 @@ #address-cells = <1>; #size-cells = <1>; - cfg0_otp: cfg0_otp@0 { + cfg0_otp: cfg0-otp@0 { reg = <0x0 0x1>; }; part_number_otp: part-number-otp@4 { reg = <0x4 0x1>; }; - monotonic_otp: monotonic_otp@10 { + monotonic_otp: monotonic-otp@10 { reg = <0x10 0x4>; }; - nand_otp: nand_otp@24 { + nand_otp: nand-otp@24 { reg = <0x24 0x4>; }; - uid_otp: uid_otp@34 { + uid_otp: uid-otp@34 { reg = <0x34 0xc>; }; - package_otp: package_otp@40 { + package_otp: package-otp@40 { reg = <0x40 0x4>; }; - hw2_otp: hw2_otp@48 { + hw2_otp: hw2-otp@48 { reg = <0x48 0x4>; }; ts_cal1: calib@5c { @@ -485,10 +485,10 @@ ts_cal2: calib@5e { reg = <0x5e 0x2>; }; - pkh_otp: pkh_otp@60 { + pkh_otp: pkh-otp@60 { reg = <0x60 0x20>; }; - mac_addr: mac_addr@e4 { + ethernet_mac_address: mac@e4 { reg = <0xe4 0x8>; st,non-secure-otp; }; diff --git a/fdts/stm32mp151a-prtt1a.dts b/fdts/stm32mp151a-prtt1a.dts index 36346208..9742dcb6 100644 --- a/fdts/stm32mp151a-prtt1a.dts +++ b/fdts/stm32mp151a-prtt1a.dts @@ -1,6 +1,7 @@ // SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) /* * Copyright (C) 2023, Protonic Holland - All Rights Reserved + * Copyright (C) 2024, STMicroelectronics - All Rights Reserved * Author: David Jander <david@protonic.nl> */ /dts-v1/; @@ -123,7 +124,7 @@ CLK_SAI2_PLL3Q CLK_SAI3_PLL3Q CLK_SAI4_PLL3Q - CLK_RNG1_LSI + CLK_RNG1_CSI CLK_RNG2_LSI CLK_LPTIM1_PCLK1 CLK_LPTIM23_PCLK3 diff --git a/fdts/stm32mp157a-avenger96.dts b/fdts/stm32mp157a-avenger96.dts index f0da350b..7135970c 100644 --- a/fdts/stm32mp157a-avenger96.dts +++ b/fdts/stm32mp157a-avenger96.dts @@ -175,29 +175,9 @@ CLK_MPU_PLL1P CLK_AXI_PLL2P CLK_MCU_PLL3P - CLK_PLL12_HSE - CLK_PLL3_HSE - CLK_PLL4_HSE CLK_RTC_LSE CLK_MCO1_DISABLED CLK_MCO2_DISABLED - >; - - st,clkdiv = < - 1 /*MPU*/ - 0 /*AXI*/ - 0 /*MCU*/ - 1 /*APB1*/ - 1 /*APB2*/ - 1 /*APB3*/ - 1 /*APB4*/ - 2 /*APB5*/ - 23 /*RTC*/ - 0 /*MCO1*/ - 0 /*MCO2*/ - >; - - st,pkcs = < CLK_CKPER_HSE CLK_FMC_ACLK CLK_QSPI_ACLK @@ -228,42 +208,83 @@ CLK_SAI2_PLL3Q CLK_SAI3_PLL3Q CLK_SAI4_PLL3Q - CLK_RNG1_LSI + CLK_RNG1_CSI CLK_RNG2_LSI CLK_LPTIM1_PCLK1 CLK_LPTIM23_PCLK3 CLK_LPTIM45_LSE >; - /* VCO = 1300.0 MHz => P = 650 (CPU) */ - pll1: st,pll@0 { - compatible = "st,stm32mp1-pll"; - reg = <0>; - cfg = <2 80 0 0 0 PQR(1,0,0)>; - frac = <0x800>; + st,clkdiv = < + DIV(DIV_MPU, 1) + DIV(DIV_AXI, 0) + DIV(DIV_MCU, 0) + DIV(DIV_APB1, 1) + DIV(DIV_APB2, 1) + DIV(DIV_APB3, 1) + DIV(DIV_APB4, 1) + DIV(DIV_APB5, 2) + DIV(DIV_RTC, 23) + DIV(DIV_MCO1, 0) + DIV(DIV_MCO2, 0) + >; + + st,pll_vco { + pll2_vco_1066Mhz: pll2-vco-1066Mhz { + src = <CLK_PLL12_HSE>; + divmn = <2 65>; + frac = <0x1400>; + }; + + pll3_vco_417Mhz: pll3-vco-417Mhz { + src = <CLK_PLL3_HSE>; + divmn = <1 33>; + frac = <0x1a04>; + }; + + pll4_vco_480Mhz: pll4-vco-480Mhz { + src = <CLK_PLL4_HSE>; + divmn = <1 39>; + }; }; /* VCO = 1066.0 MHz => P = 266 (AXI), Q = 533 (GPU), R = 533 (DDR) */ pll2: st,pll@1 { compatible = "st,stm32mp1-pll"; reg = <1>; - cfg = <2 65 1 0 0 PQR(1,1,1)>; - frac = <0x1400>; + + st,pll = <&pll2_cfg1>; + + pll2_cfg1: pll2_cfg1 { + st,pll_vco = <&pll2_vco_1066Mhz>; + st,pll_div_pqr = <1 0 0>; + }; }; /* VCO = 417.8 MHz => P = 209, Q = 24, R = 11 */ pll3: st,pll@2 { compatible = "st,stm32mp1-pll"; reg = <2>; - cfg = <1 33 1 16 36 PQR(1,1,1)>; - frac = <0x1a04>; + + st,pll = <&pll3_cfg1>; + + pll3_cfg1: pll3_cfg1 { + st,pll_vco = <&pll3_vco_417Mhz>; + st,pll_div_pqr = <1 16 36>; + }; }; /* VCO = 480.0 MHz => P = 120, Q = 40, R = 96 */ pll4: st,pll@3 { compatible = "st,stm32mp1-pll"; reg = <3>; - cfg = <1 39 3 11 4 PQR(1,1,1)>; + + st,pll = <&pll4_cfg1>; + + pll4_cfg1: pll4_cfg1 { + st,pll_vco = <&pll4_vco_480Mhz>; + st,pll_div_pqr = <3 11 4>; + }; }; }; diff --git a/fdts/stm32mp157c-ed1.dts b/fdts/stm32mp157c-ed1.dts index 949c929a..d85221b0 100644 --- a/fdts/stm32mp157c-ed1.dts +++ b/fdts/stm32mp157c-ed1.dts @@ -1,6 +1,6 @@ // SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) /* - * Copyright (c) 2017-2023, STMicroelectronics - All Rights Reserved + * Copyright (c) 2017-2024, STMicroelectronics - All Rights Reserved * Author: Ludovic Barre <ludovic.barre@st.com> for STMicroelectronics. */ /dts-v1/; @@ -31,7 +31,7 @@ }; &bsec { - board_id: board_id@ec { + board_id: board-id@ec { reg = <0xec 0x4>; st,non-secure-otp; }; @@ -194,29 +194,8 @@ CLK_MPU_PLL1P CLK_AXI_PLL2P CLK_MCU_PLL3P - CLK_PLL12_HSE - CLK_PLL3_HSE - CLK_PLL4_HSE - CLK_RTC_LSE CLK_MCO1_DISABLED CLK_MCO2_DISABLED - >; - - st,clkdiv = < - 1 /*MPU*/ - 0 /*AXI*/ - 0 /*MCU*/ - 1 /*APB1*/ - 1 /*APB2*/ - 1 /*APB3*/ - 1 /*APB4*/ - 2 /*APB5*/ - 23 /*RTC*/ - 0 /*MCO1*/ - 0 /*MCO2*/ - >; - - st,pkcs = < CLK_CKPER_HSE CLK_FMC_ACLK CLK_QSPI_ACLK @@ -247,42 +226,82 @@ CLK_SAI2_PLL3Q CLK_SAI3_PLL3Q CLK_SAI4_PLL3Q - CLK_RNG1_LSI + CLK_RNG1_CSI CLK_RNG2_LSI CLK_LPTIM1_PCLK1 CLK_LPTIM23_PCLK3 CLK_LPTIM45_LSE >; - /* VCO = 1300.0 MHz => P = 650 (CPU) */ - pll1: st,pll@0 { - compatible = "st,stm32mp1-pll"; - reg = <0>; - cfg = <2 80 0 0 0 PQR(1,0,0)>; - frac = <0x800>; + st,clkdiv = < + DIV(DIV_MPU, 1) + DIV(DIV_AXI, 0) + DIV(DIV_MCU, 0) + DIV(DIV_APB1, 1) + DIV(DIV_APB2, 1) + DIV(DIV_APB3, 1) + DIV(DIV_APB4, 1) + DIV(DIV_APB5, 2) + DIV(DIV_MCO1, 0) + DIV(DIV_MCO2, 0) + >; + + st,pll_vco { + pll2_vco_1066Mhz: pll2-vco-1066Mhz { + src = <CLK_PLL12_HSE>; + divmn = <2 65>; + frac = <0x1400>; + }; + + pll3_vco_417Mhz: pll3-vco-417Mhz { + src = <CLK_PLL3_HSE>; + divmn = <1 33>; + frac = <0x1a04>; + }; + + pll4_vco_594Mhz: pll4-vco-594Mhz { + src = <CLK_PLL4_HSE>; + divmn = <3 98>; + }; }; /* VCO = 1066.0 MHz => P = 266 (AXI), Q = 533 (GPU), R = 533 (DDR) */ pll2: st,pll@1 { compatible = "st,stm32mp1-pll"; reg = <1>; - cfg = <2 65 1 0 0 PQR(1,1,1)>; - frac = <0x1400>; + + st,pll = <&pll2_cfg1>; + + pll2_cfg1: pll2_cfg1 { + st,pll_vco = <&pll2_vco_1066Mhz>; + st,pll_div_pqr = <1 0 0>; + }; }; /* VCO = 417.8 MHz => P = 209, Q = 24, R = 11 */ pll3: st,pll@2 { compatible = "st,stm32mp1-pll"; reg = <2>; - cfg = <1 33 1 16 36 PQR(1,1,1)>; - frac = <0x1a04>; + + st,pll = <&pll3_cfg1>; + + pll3_cfg1: pll3_cfg1 { + st,pll_vco = <&pll3_vco_417Mhz>; + st,pll_div_pqr = <1 16 36>; + }; }; /* VCO = 594.0 MHz => P = 99, Q = 74, R = 74 */ pll4: st,pll@3 { compatible = "st,stm32mp1-pll"; reg = <3>; - cfg = <3 98 5 7 7 PQR(1,1,1)>; + + st,pll = <&pll4_cfg1>; + + pll4_cfg1: pll4_cfg1 { + st,pll_vco = <&pll4_vco_594Mhz>; + st,pll_div_pqr = <5 7 7>; + }; }; }; diff --git a/fdts/stm32mp157c-odyssey-som.dtsi b/fdts/stm32mp157c-odyssey-som.dtsi index 091e327c..e5d41fc7 100644 --- a/fdts/stm32mp157c-odyssey-som.dtsi +++ b/fdts/stm32mp157c-odyssey-som.dtsi @@ -1,5 +1,5 @@ /* - * Copyright (C) 2019, STMicroelectronics. All Rights Reserved. + * Copyright (C) 2019-2024, STMicroelectronics. All Rights Reserved. * Copyright (C) 2021, Grzegorz Szymaszek. * * SPDX-License-Identifier: (GPL-2.0-or-later OR BSD-3-Clause) @@ -28,7 +28,7 @@ }; &bsec { - board_id: board_id@ec { + board_id: board-id@ec { reg = <0xec 0x4>; st,non-secure-otp; }; @@ -207,29 +207,9 @@ CLK_MPU_PLL1P CLK_AXI_PLL2P CLK_MCU_PLL3P - CLK_PLL12_HSE - CLK_PLL3_HSE - CLK_PLL4_HSE CLK_RTC_LSE CLK_MCO1_DISABLED CLK_MCO2_DISABLED - >; - - st,clkdiv = < - 1 /*MPU*/ - 0 /*AXI*/ - 0 /*MCU*/ - 1 /*APB1*/ - 1 /*APB2*/ - 1 /*APB3*/ - 1 /*APB4*/ - 2 /*APB5*/ - 23 /*RTC*/ - 0 /*MCO1*/ - 0 /*MCO2*/ - >; - - st,pkcs = < CLK_CKPER_HSE CLK_FMC_ACLK CLK_QSPI_ACLK @@ -260,42 +240,83 @@ CLK_SAI2_PLL3Q CLK_SAI3_PLL3Q CLK_SAI4_PLL3Q - CLK_RNG1_LSI + CLK_RNG1_CSI CLK_RNG2_LSI CLK_LPTIM1_PCLK1 CLK_LPTIM23_PCLK3 CLK_LPTIM45_LSE >; - /* VCO = 1300.0 MHz => P = 650 (CPU) */ - pll1: st,pll@0 { - compatible = "st,stm32mp1-pll"; - reg = <0>; - cfg = <2 80 0 0 0 PQR(1,0,0)>; - frac = <0x800>; + st,clkdiv = < + DIV(DIV_MPU, 1) + DIV(DIV_AXI, 0) + DIV(DIV_MCU, 0) + DIV(DIV_APB1, 1) + DIV(DIV_APB2, 1) + DIV(DIV_APB3, 1) + DIV(DIV_APB4, 1) + DIV(DIV_APB5, 2) + DIV(DIV_RTC, 23) + DIV(DIV_MCO1, 0) + DIV(DIV_MCO2, 0) + >; + + st,pll_vco { + pll2_vco_1066Mhz: pll2-vco-1066Mhz { + src = <CLK_PLL12_HSE>; + divmn = <2 65>; + frac = <0x1400>; + }; + + pll3_vco_417Mhz: pll3-vco-417Mhz { + src = <CLK_PLL3_HSE>; + divmn = <1 33>; + frac = <0x1a04>; + }; + + pll4_vco_594Mhz: pll4-vco-594Mhz { + src = <CLK_PLL4_HSE>; + divmn = <3 98>; + }; }; /* VCO = 1066.0 MHz => P = 266 (AXI), Q = 533 (GPU), R = 533 (DDR) */ pll2: st,pll@1 { compatible = "st,stm32mp1-pll"; reg = <1>; - cfg = <2 65 1 0 0 PQR(1,1,1)>; - frac = <0x1400>; + + st,pll = <&pll2_cfg1>; + + pll2_cfg1: pll2_cfg1 { + st,pll_vco = <&pll2_vco_1066Mhz>; + st,pll_div_pqr = <1 0 0>; + }; }; /* VCO = 417.8 MHz => P = 209, Q = 24, R = 11 */ pll3: st,pll@2 { compatible = "st,stm32mp1-pll"; reg = <2>; - cfg = <1 33 1 16 36 PQR(1,1,1)>; - frac = <0x1a04>; + + st,pll = <&pll3_cfg1>; + + pll3_cfg1: pll3_cfg1 { + st,pll_vco = <&pll3_vco_417Mhz>; + st,pll_div_pqr = <1 16 36>; + }; }; /* VCO = 594.0 MHz => P = 99, Q = 74, R = 74 */ pll4: st,pll@3 { compatible = "st,stm32mp1-pll"; reg = <3>; - cfg = <3 98 5 7 7 PQR(1,1,1)>; + + st,pll = <&pll4_cfg1>; + + pll4_cfg1: pll4_cfg1 { + st,pll_vco = <&pll4_vco_594Mhz>; + st,pll_div_pqr = <5 7 7>; + }; }; }; diff --git a/fdts/stm32mp15xx-dhcom-som.dtsi b/fdts/stm32mp15xx-dhcom-som.dtsi index 7737a447..46ef0f05 100644 --- a/fdts/stm32mp15xx-dhcom-som.dtsi +++ b/fdts/stm32mp15xx-dhcom-som.dtsi @@ -2,7 +2,7 @@ /* * Copyright (C) 2019-2020 Marek Vasut <marex@denx.de> * Copyright (C) 2022 DH electronics GmbH - * Copyright (C) 2023, STMicroelectronics - All Rights Reserved + * Copyright (C) 2023-2024, STMicroelectronics - All Rights Reserved */ #include "stm32mp15-pinctrl.dtsi" @@ -18,7 +18,7 @@ }; &bsec { - board_id: board_id@ec { + board_id: board-id@ec { reg = <0xec 0x4>; st,non-secure-otp; }; @@ -193,29 +193,9 @@ CLK_MPU_PLL1P CLK_AXI_PLL2P CLK_MCU_PLL3P - CLK_PLL12_HSE - CLK_PLL3_HSE - CLK_PLL4_HSE CLK_RTC_LSE CLK_MCO1_DISABLED - CLK_MCO2_PLL4P - >; - - st,clkdiv = < - 1 /*MPU*/ - 0 /*AXI*/ - 0 /*MCU*/ - 1 /*APB1*/ - 1 /*APB2*/ - 1 /*APB3*/ - 1 /*APB4*/ - 2 /*APB5*/ - 23 /*RTC*/ - 0 /*MCO1*/ - 1 /*MCO2*/ - >; - - st,pkcs = < + CLK_MCO2_PLL4 CLK_CKPER_HSE CLK_FMC_ACLK CLK_QSPI_ACLK @@ -246,42 +226,83 @@ CLK_SAI2_PLL3Q CLK_SAI3_PLL3Q CLK_SAI4_PLL3Q - CLK_RNG1_LSI + CLK_RNG1_CSI CLK_RNG2_LSI CLK_LPTIM1_PCLK1 CLK_LPTIM23_PCLK3 CLK_LPTIM45_LSE >; - /* VCO = 1300.0 MHz => P = 650 (CPU) */ - pll1: st,pll@0 { - compatible = "st,stm32mp1-pll"; - reg = <0>; - cfg = <2 80 0 0 0 PQR(1,0,0)>; - frac = <0x800>; + st,clkdiv = < + DIV(DIV_MPU, 1) + DIV(DIV_AXI, 0) + DIV(DIV_MCU, 0) + DIV(DIV_APB1, 1) + DIV(DIV_APB2, 1) + DIV(DIV_APB3, 1) + DIV(DIV_APB4, 1) + DIV(DIV_APB5, 2) + DIV(DIV_RTC, 23) + DIV(DIV_MCO1, 0) + DIV(DIV_MCO2, 1) + >; + + st,pll_vco { + pll2_vco_1066Mhz: pll2-vco-1066Mhz { + src = <CLK_PLL12_HSE>; + divmn = <2 65>; + frac = <0x1400>; + }; + + pll3_vco_417Mhz: pll3-vco-417Mhz { + src = <CLK_PLL3_HSE>; + divmn = <1 33>; + frac = <0x1a04>; + }; + + pll4_vco_600Mhz: pll4-vco-600hz { + src = <CLK_PLL4_HSE>; + divmn = <1 49>; + }; }; /* VCO = 1066.0 MHz => P = 266 (AXI), Q = 533 (GPU), R = 533 (DDR) */ pll2: st,pll@1 { compatible = "st,stm32mp1-pll"; reg = <1>; - cfg = <2 65 1 0 0 PQR(1,1,1)>; - frac = <0x1400>; + + st,pll = <&pll2_cfg1>; + + pll2_cfg1: pll2_cfg1 { + st,pll_vco = <&pll2_vco_1066Mhz>; + st,pll_div_pqr = <1 0 0>; + }; }; /* VCO = 417.8 MHz => P = 209, Q = 24, R = 11 */ pll3: st,pll@2 { compatible = "st,stm32mp1-pll"; reg = <2>; - cfg = <1 33 1 16 36 PQR(1,1,1)>; - frac = <0x1a04>; + + st,pll = <&pll3_cfg1>; + + pll3_cfg1: pll3_cfg1 { + st,pll_vco = <&pll3_vco_417Mhz>; + st,pll_div_pqr = <1 16 36>; + }; }; /* VCO = 600.0 MHz => P = 50, Q = 50, R = 50 */ pll4: st,pll@3 { compatible = "st,stm32mp1-pll"; reg = <3>; - cfg = <1 49 5 11 11 PQR(1,1,1)>; + + st,pll = <&pll4_cfg1>; + + pll4_cfg1: pll4_cfg1 { + st,pll_vco = <&pll4_vco_600Mhz>; + st,pll_div_pqr = <5 11 11>; + }; }; }; diff --git a/fdts/stm32mp15xx-dhcor-som.dtsi b/fdts/stm32mp15xx-dhcor-som.dtsi index 8d829a41..2ebfb2de 100644 --- a/fdts/stm32mp15xx-dhcor-som.dtsi +++ b/fdts/stm32mp15xx-dhcor-som.dtsi @@ -4,7 +4,7 @@ * Author: Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org> * Copyright (C) 2020 Marek Vasut <marex@denx.de> * Copyright (C) 2022 DH electronics GmbH - * Copyright (C) 2023, STMicroelectronics - All Rights Reserved + * Copyright (C) 2023-2024, STMicroelectronics - All Rights Reserved */ #include "stm32mp15-pinctrl.dtsi" @@ -188,29 +188,9 @@ CLK_MPU_PLL1P CLK_AXI_PLL2P CLK_MCU_PLL3P - CLK_PLL12_HSE - CLK_PLL3_HSE - CLK_PLL4_HSE CLK_RTC_LSE CLK_MCO1_DISABLED CLK_MCO2_DISABLED - >; - - st,clkdiv = < - 1 /*MPU*/ - 0 /*AXI*/ - 0 /*MCU*/ - 1 /*APB1*/ - 1 /*APB2*/ - 1 /*APB3*/ - 1 /*APB4*/ - 2 /*APB5*/ - 23 /*RTC*/ - 0 /*MCO1*/ - 0 /*MCO2*/ - >; - - st,pkcs = < CLK_CKPER_HSE CLK_FMC_ACLK CLK_QSPI_ACLK @@ -241,42 +221,84 @@ CLK_SAI2_PLL3Q CLK_SAI3_PLL3Q CLK_SAI4_PLL3Q - CLK_RNG1_LSI + CLK_RNG1_CSI CLK_RNG2_LSI CLK_LPTIM1_PCLK1 CLK_LPTIM23_PCLK3 CLK_LPTIM45_LSE >; - /* VCO = 1300.0 MHz => P = 650 (CPU) */ - pll1: st,pll@0 { - compatible = "st,stm32mp1-pll"; - reg = <0>; - cfg = <2 80 0 0 0 PQR(1,0,0)>; - frac = <0x800>; + st,clkdiv = < + DIV(DIV_MPU, 1) + DIV(DIV_AXI, 0) + DIV(DIV_MCU, 0) + DIV(DIV_APB1, 1) + DIV(DIV_APB2, 1) + DIV(DIV_APB3, 1) + DIV(DIV_APB4, 1) + DIV(DIV_APB5, 2) + DIV(DIV_RTC, 23) + DIV(DIV_MCO1, 0) + DIV(DIV_MCO2, 0) + >; + + st,pll_vco { + pll2_vco_1066Mhz: pll2-vco-1066Mhz { + src = <CLK_PLL12_HSE>; + divmn = <2 65>; + frac = <0x1400>; + }; + + pll3_vco_417Mhz: pll3-vco-417Mhz { + src = <CLK_PLL3_HSE>; + divmn = <1 33>; + frac = <0x1a04>; + }; + + pll4_vco_594Mhz: pll4-vco-594Mhz { + src = <CLK_PLL4_HSE>; + divmn = <3 98>; + }; }; /* VCO = 1066.0 MHz => P = 266 (AXI), Q = 533 (GPU), R = 533 (DDR) */ pll2: st,pll@1 { compatible = "st,stm32mp1-pll"; reg = <1>; - cfg = <2 65 1 0 0 PQR(1,1,1)>; - frac = <0x1400>; + + st,pll = <&pll2_cfg1>; + + pll2_cfg1: pll2_cfg1 { + st,pll_vco = <&pll2_vco_1066Mhz>; + st,pll_div_pqr = <1 0 0>; + }; }; /* VCO = 417.8 MHz => P = 209, Q = 24, R = 11 */ pll3: st,pll@2 { compatible = "st,stm32mp1-pll"; reg = <2>; - cfg = <1 33 1 16 36 PQR(1,1,1)>; - frac = <0x1a04>; + + st,pll = <&pll3_cfg1>; + + pll3_cfg1: pll3_cfg1 { + st,pll_vco = <&pll3_vco_417Mhz>; + st,pll_div_pqr = <1 16 36>; + }; }; - /* VCO = 600.0 MHz => P = 99, Q = 74, R = 99 */ + /* VCO = 600.0 MHz => P = 99, Q = 74, R = 99 */ /* @TOCHECK */ + /* VCO = 594.0 MHz => P = 99, Q = 74, R = 74 */ pll4: st,pll@3 { compatible = "st,stm32mp1-pll"; reg = <3>; - cfg = <3 98 5 7 5 PQR(1,1,1)>; + + st,pll = <&pll4_cfg1>; + + pll4_cfg1: pll4_cfg1 { + st,pll_vco = <&pll4_vco_594Mhz>; + st,pll_div_pqr = <5 7 7>; + }; }; }; diff --git a/fdts/stm32mp15xx-dkx.dtsi b/fdts/stm32mp15xx-dkx.dtsi index f8baa9d4..bac9e053 100644 --- a/fdts/stm32mp15xx-dkx.dtsi +++ b/fdts/stm32mp15xx-dkx.dtsi @@ -1,6 +1,6 @@ // SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) /* - * Copyright (c) 2019-2022, STMicroelectronics - All Rights Reserved + * Copyright (c) 2019-2024, STMicroelectronics - All Rights Reserved * Author: Alexandre Torgue <alexandre.torgue@st.com> for STMicroelectronics. */ @@ -29,7 +29,7 @@ }; &bsec { - board_id: board_id@ec { + board_id: board-id@ec { reg = <0xec 0x4>; st,non-secure-otp; }; @@ -198,29 +198,8 @@ CLK_MPU_PLL1P CLK_AXI_PLL2P CLK_MCU_PLL3P - CLK_PLL12_HSE - CLK_PLL3_HSE - CLK_PLL4_HSE - CLK_RTC_LSE CLK_MCO1_DISABLED CLK_MCO2_DISABLED - >; - - st,clkdiv = < - 1 /*MPU*/ - 0 /*AXI*/ - 0 /*MCU*/ - 1 /*APB1*/ - 1 /*APB2*/ - 1 /*APB3*/ - 1 /*APB4*/ - 2 /*APB5*/ - 23 /*RTC*/ - 0 /*MCO1*/ - 0 /*MCO2*/ - >; - - st,pkcs = < CLK_CKPER_HSE CLK_FMC_ACLK CLK_QSPI_ACLK @@ -251,42 +230,82 @@ CLK_SAI2_PLL3Q CLK_SAI3_PLL3Q CLK_SAI4_PLL3Q - CLK_RNG1_LSI + CLK_RNG1_CSI CLK_RNG2_LSI CLK_LPTIM1_PCLK1 CLK_LPTIM23_PCLK3 CLK_LPTIM45_LSE >; - /* VCO = 1300.0 MHz => P = 650 (CPU) */ - pll1: st,pll@0 { - compatible = "st,stm32mp1-pll"; - reg = <0>; - cfg = < 2 80 0 0 0 PQR(1,0,0) >; - frac = < 0x800 >; + st,clkdiv = < + DIV(DIV_MPU, 1) + DIV(DIV_AXI, 0) + DIV(DIV_MCU, 0) + DIV(DIV_APB1, 1) + DIV(DIV_APB2, 1) + DIV(DIV_APB3, 1) + DIV(DIV_APB4, 1) + DIV(DIV_APB5, 2) + DIV(DIV_MCO1, 0) + DIV(DIV_MCO2, 0) + >; + + st,pll_vco { + pll2_vco_1066Mhz: pll2-vco-1066Mhz { + src = <CLK_PLL12_HSE>; + divmn = <2 65>; + frac = <0x1400>; + }; + + pll3_vco_417Mhz: pll3-vco-417Mhz { + src = <CLK_PLL3_HSE>; + divmn = <1 33>; + frac = <0x1a04>; + }; + + pll4_vco_594Mhz: pll4-vco-594Mhz { + src = <CLK_PLL4_HSE>; + divmn = <3 98>; + }; }; /* VCO = 1066.0 MHz => P = 266 (AXI), Q = 533 (GPU), R = 533 (DDR) */ pll2: st,pll@1 { compatible = "st,stm32mp1-pll"; reg = <1>; - cfg = <2 65 1 0 0 PQR(1,1,1)>; - frac = <0x1400>; + + st,pll = <&pll2_cfg1>; + + pll2_cfg1: pll2_cfg1 { + st,pll_vco = <&pll2_vco_1066Mhz>; + st,pll_div_pqr = <1 0 0>; + }; }; /* VCO = 417.8 MHz => P = 209, Q = 24, R = 11 */ pll3: st,pll@2 { compatible = "st,stm32mp1-pll"; reg = <2>; - cfg = <1 33 1 16 36 PQR(1,1,1)>; - frac = <0x1a04>; + + st,pll = <&pll3_cfg1>; + + pll3_cfg1: pll3_cfg1 { + st,pll_vco = <&pll3_vco_417Mhz>; + st,pll_div_pqr = <1 16 36>; + }; }; /* VCO = 594.0 MHz => P = 99, Q = 74, R = 74 */ pll4: st,pll@3 { compatible = "st,stm32mp1-pll"; reg = <3>; - cfg = <3 98 5 7 7 PQR(1,1,1)>; + + st,pll = <&pll4_cfg1>; + + pll4_cfg1: pll4_cfg1 { + st,pll_vco = <&pll4_vco_594Mhz>; + st,pll_div_pqr = <5 7 7>; + }; }; }; diff --git a/fdts/stm32mp15xx-osd32.dtsi b/fdts/stm32mp15xx-osd32.dtsi index 52a5d380..6e27b413 100644 --- a/fdts/stm32mp15xx-osd32.dtsi +++ b/fdts/stm32mp15xx-osd32.dtsi @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: (GPL-2.0-or-later OR BSD-3-Clause) */ /* - * Copyright (C) 2020 STMicroelectronics - All Rights Reserved + * Copyright (C) 2020-2024 STMicroelectronics - All Rights Reserved * Copyright (C) 2020 Ahmad Fatoum, Pengutronix */ @@ -157,7 +157,7 @@ }; &bsec { - board_id: board_id@ec { + board_id: board-id@ec { reg = <0xec 0x4>; st,non-secure-otp; }; @@ -185,29 +185,9 @@ CLK_MPU_PLL1P CLK_AXI_PLL2P CLK_MCU_PLL3P - CLK_PLL12_HSE - CLK_PLL3_HSE - CLK_PLL4_HSE CLK_RTC_LSE CLK_MCO1_DISABLED CLK_MCO2_DISABLED - >; - - st,clkdiv = < - 1 /*MPU*/ - 0 /*AXI*/ - 0 /*MCU*/ - 1 /*APB1*/ - 1 /*APB2*/ - 1 /*APB3*/ - 1 /*APB4*/ - 2 /*APB5*/ - 23 /*RTC*/ - 0 /*MCO1*/ - 0 /*MCO2*/ - >; - - st,pkcs = < CLK_CKPER_HSE CLK_FMC_ACLK CLK_QSPI_ACLK @@ -238,41 +218,82 @@ CLK_SAI2_PLL3Q CLK_SAI3_PLL3Q CLK_SAI4_PLL3Q - CLK_RNG1_LSI + CLK_RNG1_CSI CLK_RNG2_LSI CLK_LPTIM1_PCLK1 CLK_LPTIM23_PCLK3 CLK_LPTIM45_LSE >; - /* VCO = 1300.0 MHz => P = 650 (CPU) */ - pll1: st,pll@0 { - compatible = "st,stm32mp1-pll"; - reg = <0>; - cfg = < 2 80 0 0 0 PQR(1,0,0) >; - frac = < 0x800 >; + st,clkdiv = < + DIV(DIV_MPU, 1) + DIV(DIV_AXI, 0) + DIV(DIV_MCU, 0) + DIV(DIV_APB1, 1) + DIV(DIV_APB2, 1) + DIV(DIV_APB3, 1) + DIV(DIV_APB4, 1) + DIV(DIV_APB5, 2) + DIV(DIV_RTC, 23) + DIV(DIV_MCO1, 0) + DIV(DIV_MCO2, 0) + >; + + st,pll_vco { + pll2_vco_1066Mhz: pll2-vco-1066Mhz { + src = <CLK_PLL12_HSE>; + divmn = <2 65>; + frac = <0x1400>; + }; + + pll3_vco_417Mhz: pll3-vco-417Mhz { + src = <CLK_PLL3_HSE>; + divmn = <1 33>; + frac = <0x1a04>; + }; + + pll4_vco_594Mhz: pll4-vco-594Mhz { + src = <CLK_PLL4_HSE>; + divmn = <3 98>; + }; }; /* VCO = 1066.0 MHz => P = 266 (AXI), Q = 533 (GPU), R = 533 (DDR) */ pll2: st,pll@1 { compatible = "st,stm32mp1-pll"; reg = <1>; - cfg = <2 65 1 0 0 PQR(1,1,1)>; - frac = <0x1400>; + + st,pll = <&pll2_cfg1>; + + pll2_cfg1: pll2_cfg1 { + st,pll_vco = <&pll2_vco_1066Mhz>; + st,pll_div_pqr = <1 0 0>; + }; }; /* VCO = 417.8 MHz => P = 209, Q = 24, R = 11 */ pll3: st,pll@2 { compatible = "st,stm32mp1-pll"; reg = <2>; - cfg = <1 33 1 16 36 PQR(1,1,1)>; - frac = <0x1a04>; + + st,pll = <&pll3_cfg1>; + + pll3_cfg1: pll3_cfg1 { + st,pll_vco = <&pll3_vco_417Mhz>; + st,pll_div_pqr = <1 16 36>; + }; }; /* VCO = 594.0 MHz => P = 99, Q = 74, R = 74 */ pll4: st,pll@3 { compatible = "st,stm32mp1-pll"; reg = <3>; - cfg = <3 98 5 7 7 PQR(1,1,1)>; + + st,pll = <&pll4_cfg1>; + + pll4_cfg1: pll4_cfg1 { + st,pll_vco = <&pll4_vco_594Mhz>; + st,pll_div_pqr = <5 7 7>; + }; }; }; diff --git a/fdts/stm32mp25-bl2.dtsi b/fdts/stm32mp25-bl2.dtsi index 438a58ca..e6662d07 100644 --- a/fdts/stm32mp25-bl2.dtsi +++ b/fdts/stm32mp25-bl2.dtsi @@ -1,4 +1,39 @@ // SPDX-License-Identifier: (GPL-2.0-or-later OR BSD-3-Clause) /* - * Copyright (C) 2023, STMicroelectronics - All Rights Reserved + * Copyright (C) 2023-2024, STMicroelectronics - All Rights Reserved */ + +/ { + soc@0 { +#if !STM32MP_EMMC && !STM32MP_SDMMC + rifsc@42080000 { + /delete-node/ mmc@48220000; + /delete-node/ mmc@48230000; + }; +#endif + }; + + /* + * UUID's here are UUID RFC 4122 compliant meaning fieds are stored in + * network order (big endian) + */ + + st-io_policies { + fip-handles { + compatible = "st,io-fip-handle"; +#if STM32MP_DDR_FIP_IO_STORAGE + ddr_fw_uuid = "b11249be-92dd-4b10-867c-2c6a4b47a7fb"; +#endif + fw_cfg_uuid = "5807e16a-8459-47be-8ed5-648e8dddab0e"; + bl31_uuid = "47d4086d-4cfe-9846-9b95-2950cbbd5a00"; + bl32_uuid = "05d0e189-53dc-1347-8d2b-500a4b7a3e38"; + bl32_extra1_uuid = "0b70c29b-2a5a-7840-9f65-0a5682738288"; + bl32_extra2_uuid = "8ea87bb1-cfa2-3f4d-85fd-e7bba50220d9"; + bl33_uuid = "d6d0eea7-fcea-d54b-9782-9934f234b6e4"; + hw_cfg_uuid = "08b8f1d9-c9cf-9349-a962-6fbc6b7265cc"; + soc_fw_cfg_uuid = "9979814b-0376-fb46-8c8e-8d267f7859e0"; + tos_fw_cfg_uuid = "26257c1a-dbc6-7f47-8d96-c4c4b0248021"; + nt_fw_cfg_uuid = "28da9815-93e8-7e44-ac66-1aaf801550f9"; + }; + }; +}; diff --git a/fdts/stm32mp25-bl31.dtsi b/fdts/stm32mp25-bl31.dtsi new file mode 100644 index 00000000..fa63f639 --- /dev/null +++ b/fdts/stm32mp25-bl31.dtsi @@ -0,0 +1,13 @@ +// SPDX-License-Identifier: (GPL-2.0-or-later OR BSD-3-Clause) +/* + * Copyright (C) 2024, STMicroelectronics - All Rights Reserved + */ + +/ { + soc@0 { + rifsc@42080000 { + /delete-node/ mmc@48220000; + /delete-node/ mmc@48230000; + }; + }; +}; diff --git a/fdts/stm32mp25-ddr.dtsi b/fdts/stm32mp25-ddr.dtsi new file mode 100644 index 00000000..1fcd13dd --- /dev/null +++ b/fdts/stm32mp25-ddr.dtsi @@ -0,0 +1,253 @@ +// SPDX-License-Identifier: GPL-2.0+ OR BSD-3-Clause +/* + * Copyright (C) 2024, STMicroelectronics - All Rights Reserved + */ + +&ddr{ + st,mem-name = DDR_MEM_NAME; + st,mem-speed = <DDR_MEM_SPEED>; + st,mem-size = <(DDR_MEM_SIZE >> 32) (DDR_MEM_SIZE & 0xFFFFFFFF)>; + + st,ctl-reg = < + DDR_MSTR + DDR_MRCTRL0 + DDR_MRCTRL1 + DDR_MRCTRL2 + DDR_DERATEEN + DDR_DERATEINT + DDR_DERATECTL + DDR_PWRCTL + DDR_PWRTMG + DDR_HWLPCTL + DDR_RFSHCTL0 + DDR_RFSHCTL1 + DDR_RFSHCTL3 + DDR_CRCPARCTL0 + DDR_CRCPARCTL1 + DDR_INIT0 + DDR_INIT1 + DDR_INIT2 + DDR_INIT3 + DDR_INIT4 + DDR_INIT5 + DDR_INIT6 + DDR_INIT7 + DDR_DIMMCTL + DDR_RANKCTL + DDR_RANKCTL1 + DDR_ZQCTL0 + DDR_ZQCTL1 + DDR_ZQCTL2 + DDR_DFITMG0 + DDR_DFITMG1 + DDR_DFILPCFG0 + DDR_DFILPCFG1 + DDR_DFIUPD0 + DDR_DFIUPD1 + DDR_DFIUPD2 + DDR_DFIMISC + DDR_DFITMG2 + DDR_DFITMG3 + DDR_DBICTL + DDR_DFIPHYMSTR + DDR_DBG0 + DDR_DBG1 + DDR_DBGCMD + DDR_SWCTL + DDR_SWCTLSTATIC + DDR_POISONCFG + DDR_PCCFG + >; + + st,ctl-timing = < + DDR_RFSHTMG + DDR_RFSHTMG1 + DDR_DRAMTMG0 + DDR_DRAMTMG1 + DDR_DRAMTMG2 + DDR_DRAMTMG3 + DDR_DRAMTMG4 + DDR_DRAMTMG5 + DDR_DRAMTMG6 + DDR_DRAMTMG7 + DDR_DRAMTMG8 + DDR_DRAMTMG9 + DDR_DRAMTMG10 + DDR_DRAMTMG11 + DDR_DRAMTMG12 + DDR_DRAMTMG13 + DDR_DRAMTMG14 + DDR_DRAMTMG15 + DDR_ODTCFG + DDR_ODTMAP + >; + + st,ctl-map = < + DDR_ADDRMAP0 + DDR_ADDRMAP1 + DDR_ADDRMAP2 + DDR_ADDRMAP3 + DDR_ADDRMAP4 + DDR_ADDRMAP5 + DDR_ADDRMAP6 + DDR_ADDRMAP7 + DDR_ADDRMAP8 + DDR_ADDRMAP9 + DDR_ADDRMAP10 + DDR_ADDRMAP11 + >; + + st,ctl-perf = < + DDR_SCHED + DDR_SCHED1 + DDR_PERFHPR1 + DDR_PERFLPR1 + DDR_PERFWR1 + DDR_SCHED3 + DDR_SCHED4 + DDR_PCFGR_0 + DDR_PCFGW_0 + DDR_PCTRL_0 + DDR_PCFGQOS0_0 + DDR_PCFGQOS1_0 + DDR_PCFGWQOS0_0 + DDR_PCFGWQOS1_0 + DDR_PCFGR_1 + DDR_PCFGW_1 + DDR_PCTRL_1 + DDR_PCFGQOS0_1 + DDR_PCFGQOS1_1 + DDR_PCFGWQOS0_1 + DDR_PCFGWQOS1_1 + >; + + st,phy-basic = < + DDR_UIB_DRAMTYPE + DDR_UIB_DIMMTYPE + DDR_UIB_LP4XMODE + DDR_UIB_NUMDBYTE + DDR_UIB_NUMACTIVEDBYTEDFI0 + DDR_UIB_NUMACTIVEDBYTEDFI1 + DDR_UIB_NUMANIB + DDR_UIB_NUMRANK_DFI0 + DDR_UIB_NUMRANK_DFI1 + DDR_UIB_DRAMDATAWIDTH + DDR_UIB_NUMPSTATES + DDR_UIB_FREQUENCY_0 + DDR_UIB_PLLBYPASS_0 + DDR_UIB_DFIFREQRATIO_0 + DDR_UIB_DFI1EXISTS + DDR_UIB_TRAIN2D + DDR_UIB_HARDMACROVER + DDR_UIB_READDBIENABLE_0 + DDR_UIB_DFIMODE + >; + + st,phy-advanced = < + DDR_UIA_LP4RXPREAMBLEMODE_0 + DDR_UIA_LP4POSTAMBLEEXT_0 + DDR_UIA_D4RXPREAMBLELENGTH_0 + DDR_UIA_D4TXPREAMBLELENGTH_0 + DDR_UIA_EXTCALRESVAL + DDR_UIA_IS2TTIMING_0 + DDR_UIA_ODTIMPEDANCE_0 + DDR_UIA_TXIMPEDANCE_0 + DDR_UIA_ATXIMPEDANCE + DDR_UIA_MEMALERTEN + DDR_UIA_MEMALERTPUIMP + DDR_UIA_MEMALERTVREFLEVEL + DDR_UIA_MEMALERTSYNCBYPASS + DDR_UIA_DISDYNADRTRI_0 + DDR_UIA_PHYMSTRTRAININTERVAL_0 + DDR_UIA_PHYMSTRMAXREQTOACK_0 + DDR_UIA_WDQSEXT + DDR_UIA_CALINTERVAL + DDR_UIA_CALONCE + DDR_UIA_LP4RL_0 + DDR_UIA_LP4WL_0 + DDR_UIA_LP4WLS_0 + DDR_UIA_LP4DBIRD_0 + DDR_UIA_LP4DBIWR_0 + DDR_UIA_LP4NWR_0 + DDR_UIA_LP4LOWPOWERDRV + DDR_UIA_DRAMBYTESWAP + DDR_UIA_RXENBACKOFF + DDR_UIA_TRAINSEQUENCECTRL + DDR_UIA_SNPSUMCTLOPT + DDR_UIA_SNPSUMCTLF0RC5X_0 + DDR_UIA_TXSLEWRISEDQ_0 + DDR_UIA_TXSLEWFALLDQ_0 + DDR_UIA_TXSLEWRISEAC + DDR_UIA_TXSLEWFALLAC + DDR_UIA_DISABLERETRAINING + DDR_UIA_DISABLEPHYUPDATE + DDR_UIA_ENABLEHIGHCLKSKEWFIX + DDR_UIA_DISABLEUNUSEDADDRLNS + DDR_UIA_PHYINITSEQUENCENUM + DDR_UIA_ENABLEDFICSPOLARITYFIX + DDR_UIA_PHYVREF + DDR_UIA_SEQUENCECTRL_0 + >; + + st,phy-mr = < + DDR_UIM_MR0_0 + DDR_UIM_MR1_0 + DDR_UIM_MR2_0 + DDR_UIM_MR3_0 + DDR_UIM_MR4_0 + DDR_UIM_MR5_0 + DDR_UIM_MR6_0 + DDR_UIM_MR11_0 + DDR_UIM_MR12_0 + DDR_UIM_MR13_0 + DDR_UIM_MR14_0 + DDR_UIM_MR22_0 + >; + + st,phy-swizzle = < + DDR_UIS_SWIZZLE_0 + DDR_UIS_SWIZZLE_1 + DDR_UIS_SWIZZLE_2 + DDR_UIS_SWIZZLE_3 + DDR_UIS_SWIZZLE_4 + DDR_UIS_SWIZZLE_5 + DDR_UIS_SWIZZLE_6 + DDR_UIS_SWIZZLE_7 + DDR_UIS_SWIZZLE_8 + DDR_UIS_SWIZZLE_9 + DDR_UIS_SWIZZLE_10 + DDR_UIS_SWIZZLE_11 + DDR_UIS_SWIZZLE_12 + DDR_UIS_SWIZZLE_13 + DDR_UIS_SWIZZLE_14 + DDR_UIS_SWIZZLE_15 + DDR_UIS_SWIZZLE_16 + DDR_UIS_SWIZZLE_17 + DDR_UIS_SWIZZLE_18 + DDR_UIS_SWIZZLE_19 + DDR_UIS_SWIZZLE_20 + DDR_UIS_SWIZZLE_21 + DDR_UIS_SWIZZLE_22 + DDR_UIS_SWIZZLE_23 + DDR_UIS_SWIZZLE_24 + DDR_UIS_SWIZZLE_25 + DDR_UIS_SWIZZLE_26 + DDR_UIS_SWIZZLE_27 + DDR_UIS_SWIZZLE_28 + DDR_UIS_SWIZZLE_29 + DDR_UIS_SWIZZLE_30 + DDR_UIS_SWIZZLE_31 + DDR_UIS_SWIZZLE_32 + DDR_UIS_SWIZZLE_33 + DDR_UIS_SWIZZLE_34 + DDR_UIS_SWIZZLE_35 + DDR_UIS_SWIZZLE_36 + DDR_UIS_SWIZZLE_37 + DDR_UIS_SWIZZLE_38 + DDR_UIS_SWIZZLE_39 + DDR_UIS_SWIZZLE_40 + DDR_UIS_SWIZZLE_41 + DDR_UIS_SWIZZLE_42 + DDR_UIS_SWIZZLE_43 + >; +}; diff --git a/fdts/stm32mp25-ddr4-2x16Gbits-2x16bits-1200MHz.dtsi b/fdts/stm32mp25-ddr4-2x16Gbits-2x16bits-1200MHz.dtsi new file mode 100644 index 00000000..3d69448a --- /dev/null +++ b/fdts/stm32mp25-ddr4-2x16Gbits-2x16bits-1200MHz.dtsi @@ -0,0 +1,249 @@ +// SPDX-License-Identifier: GPL-2.0+ OR BSD-3-Clause +/* + * Copyright (C) 2022-2024, STMicroelectronics - All Rights Reserved + */ + +/* + * STM32MP25 DDR4 board configuration + * DDR4 2x16Gbits 2x16bits 1200MHz + * + * version 2 + * package 1 Package selection (14x14 and 18x18) + * memclk 1200MHz (2x DFI clock) + range check + * Speed_Bin Worse from JEDEC + * device_width 16 x16 by default + * width 32 32: full width / 16: half width + * density 16Gbits (per 16bit device) + * Addressing RBC row/bank interleaving + * RDBI No Read DBI + */ + +#define DDR_MEM_NAME "DDR4 2x16Gbits 2x16bits 1200MHz" +#define DDR_MEM_SPEED 1200000 +#define DDR_MEM_SIZE 0x100000000 + +#define DDR_MSTR 0x01040010 +#define DDR_MRCTRL0 0x00000030 +#define DDR_MRCTRL1 0x00000000 +#define DDR_MRCTRL2 0x00000000 +#define DDR_DERATEEN 0x00000000 +#define DDR_DERATEINT 0x00000000 +#define DDR_DERATECTL 0x00000000 +#define DDR_PWRCTL 0x00000000 +#define DDR_PWRTMG 0x00130001 +#define DDR_HWLPCTL 0x00000002 +#define DDR_RFSHCTL0 0x00210010 +#define DDR_RFSHCTL1 0x00000000 +#define DDR_RFSHCTL3 0x00000000 +#define DDR_RFSHTMG 0x0092014A +#define DDR_RFSHTMG1 0x008C0000 +#define DDR_CRCPARCTL0 0x00000000 +#define DDR_CRCPARCTL1 0x00001000 +#define DDR_INIT0 0xC0020002 +#define DDR_INIT1 0x00010002 +#define DDR_INIT2 0x00000D00 +#define DDR_INIT3 0x09400103 +#define DDR_INIT4 0x00180000 +#define DDR_INIT5 0x00100004 +#define DDR_INIT6 0x00080460 +#define DDR_INIT7 0x00000C16 +#define DDR_DIMMCTL 0x00000000 +#define DDR_RANKCTL 0x0000066F +#define DDR_RANKCTL1 0x0000000D +#define DDR_DRAMTMG0 0x11152815 +#define DDR_DRAMTMG1 0x0004051E +#define DDR_DRAMTMG2 0x0609060D +#define DDR_DRAMTMG3 0x0050400C +#define DDR_DRAMTMG4 0x0904050A +#define DDR_DRAMTMG5 0x06060403 +#define DDR_DRAMTMG6 0x02020005 +#define DDR_DRAMTMG7 0x00000202 +#define DDR_DRAMTMG8 0x0606100B +#define DDR_DRAMTMG9 0x0002040A +#define DDR_DRAMTMG10 0x001C180A +#define DDR_DRAMTMG11 0x4408021C +#define DDR_DRAMTMG12 0x0C020010 +#define DDR_DRAMTMG13 0x1C200004 +#define DDR_DRAMTMG14 0x000000A0 +#define DDR_DRAMTMG15 0x00000000 +#define DDR_ZQCTL0 0x01000040 +#define DDR_ZQCTL1 0x2000493E +#define DDR_ZQCTL2 0x00000000 +#define DDR_DFITMG0 0x038F8209 +#define DDR_DFITMG1 0x00080303 +#define DDR_DFILPCFG0 0x07004111 +#define DDR_DFILPCFG1 0x00000000 +#define DDR_DFIUPD0 0xC0300018 +#define DDR_DFIUPD1 0x005700B4 +#define DDR_DFIUPD2 0x80000000 +#define DDR_DFIMISC 0x00000041 +#define DDR_DFITMG2 0x00000F09 +#define DDR_DFITMG3 0x00000000 +#define DDR_DBICTL 0x00000001 +#define DDR_DFIPHYMSTR 0x80000000 +#define DDR_ADDRMAP0 0x0000001F +#define DDR_ADDRMAP1 0x003F0909 +#define DDR_ADDRMAP2 0x00000700 +#define DDR_ADDRMAP3 0x00000000 +#define DDR_ADDRMAP4 0x00001F1F +#define DDR_ADDRMAP5 0x070F0707 +#define DDR_ADDRMAP6 0x07070707 +#define DDR_ADDRMAP7 0x00000F07 +#define DDR_ADDRMAP8 0x00003F01 +#define DDR_ADDRMAP9 0x07070707 +#define DDR_ADDRMAP10 0x07070707 +#define DDR_ADDRMAP11 0x00000007 +#define DDR_ODTCFG 0x06000618 +#define DDR_ODTMAP 0x00000001 +#define DDR_SCHED 0x80001B00 +#define DDR_SCHED1 0x00000000 +#define DDR_PERFHPR1 0x04000200 +#define DDR_PERFLPR1 0x08000080 +#define DDR_PERFWR1 0x08000400 +#define DDR_SCHED3 0x04040208 +#define DDR_SCHED4 0x08400810 +#define DDR_DBG0 0x00000000 +#define DDR_DBG1 0x00000000 +#define DDR_DBGCMD 0x00000000 +#define DDR_SWCTL 0x00000000 +#define DDR_SWCTLSTATIC 0x00000000 +#define DDR_POISONCFG 0x00000000 +#define DDR_PCCFG 0x00000000 +#define DDR_PCFGR_0 0x00704100 +#define DDR_PCFGW_0 0x00004100 +#define DDR_PCTRL_0 0x00000000 +#define DDR_PCFGQOS0_0 0x0021000C +#define DDR_PCFGQOS1_0 0x01000080 +#define DDR_PCFGWQOS0_0 0x01100C07 +#define DDR_PCFGWQOS1_0 0x04000200 +#define DDR_PCFGR_1 0x00704100 +#define DDR_PCFGW_1 0x00004100 +#define DDR_PCTRL_1 0x00000000 +#define DDR_PCFGQOS0_1 0x00100007 +#define DDR_PCFGQOS1_1 0x01000080 +#define DDR_PCFGWQOS0_1 0x01100C07 +#define DDR_PCFGWQOS1_1 0x04000200 + +#define DDR_UIB_DRAMTYPE 0x00000000 +#define DDR_UIB_DIMMTYPE 0x00000004 +#define DDR_UIB_LP4XMODE 0x00000000 +#define DDR_UIB_NUMDBYTE 0x00000004 +#define DDR_UIB_NUMACTIVEDBYTEDFI0 0x00000004 +#define DDR_UIB_NUMACTIVEDBYTEDFI1 0x00000000 +#define DDR_UIB_NUMANIB 0x00000008 +#define DDR_UIB_NUMRANK_DFI0 0x00000001 +#define DDR_UIB_NUMRANK_DFI1 0x00000001 +#define DDR_UIB_DRAMDATAWIDTH 0x00000010 +#define DDR_UIB_NUMPSTATES 0x00000001 +#define DDR_UIB_FREQUENCY_0 0x000004B0 +#define DDR_UIB_PLLBYPASS_0 0x00000000 +#define DDR_UIB_DFIFREQRATIO_0 0x00000001 +#define DDR_UIB_DFI1EXISTS 0x00000001 +#define DDR_UIB_TRAIN2D 0x00000000 +#define DDR_UIB_HARDMACROVER 0x00000003 +#define DDR_UIB_READDBIENABLE_0 0x00000000 +#define DDR_UIB_DFIMODE 0x00000000 + +#define DDR_UIA_LP4RXPREAMBLEMODE_0 0x00000000 +#define DDR_UIA_LP4POSTAMBLEEXT_0 0x00000000 +#define DDR_UIA_D4RXPREAMBLELENGTH_0 0x00000000 +#define DDR_UIA_D4TXPREAMBLELENGTH_0 0x00000000 +#define DDR_UIA_EXTCALRESVAL 0x00000000 +#define DDR_UIA_IS2TTIMING_0 0x00000000 +#define DDR_UIA_ODTIMPEDANCE_0 0x00000035 +#define DDR_UIA_TXIMPEDANCE_0 0x00000028 +#define DDR_UIA_ATXIMPEDANCE 0x00000028 +#define DDR_UIA_MEMALERTEN 0x00000000 +#define DDR_UIA_MEMALERTPUIMP 0x00000000 +#define DDR_UIA_MEMALERTVREFLEVEL 0x00000000 +#define DDR_UIA_MEMALERTSYNCBYPASS 0x00000000 +#define DDR_UIA_DISDYNADRTRI_0 0x00000001 +#define DDR_UIA_PHYMSTRTRAININTERVAL_0 0x00000000 +#define DDR_UIA_PHYMSTRMAXREQTOACK_0 0x00000000 +#define DDR_UIA_WDQSEXT 0x00000000 +#define DDR_UIA_CALINTERVAL 0x00000009 +#define DDR_UIA_CALONCE 0x00000000 +#define DDR_UIA_LP4RL_0 0x00000000 +#define DDR_UIA_LP4WL_0 0x00000000 +#define DDR_UIA_LP4WLS_0 0x00000000 +#define DDR_UIA_LP4DBIRD_0 0x00000000 +#define DDR_UIA_LP4DBIWR_0 0x00000000 +#define DDR_UIA_LP4NWR_0 0x00000000 +#define DDR_UIA_LP4LOWPOWERDRV 0x00000000 +#define DDR_UIA_DRAMBYTESWAP 0x00000000 +#define DDR_UIA_RXENBACKOFF 0x00000000 +#define DDR_UIA_TRAINSEQUENCECTRL 0x00000000 +#define DDR_UIA_SNPSUMCTLOPT 0x00000000 +#define DDR_UIA_SNPSUMCTLF0RC5X_0 0x00000000 +#define DDR_UIA_TXSLEWRISEDQ_0 0x0000000F +#define DDR_UIA_TXSLEWFALLDQ_0 0x0000000F +#define DDR_UIA_TXSLEWRISEAC 0x0000000F +#define DDR_UIA_TXSLEWFALLAC 0x0000000F +#define DDR_UIA_DISABLERETRAINING 0x00000001 +#define DDR_UIA_DISABLEPHYUPDATE 0x00000000 +#define DDR_UIA_ENABLEHIGHCLKSKEWFIX 0x00000000 +#define DDR_UIA_DISABLEUNUSEDADDRLNS 0x00000001 +#define DDR_UIA_PHYINITSEQUENCENUM 0x00000000 +#define DDR_UIA_ENABLEDFICSPOLARITYFIX 0x00000000 +#define DDR_UIA_PHYVREF 0x0000005E +#define DDR_UIA_SEQUENCECTRL_0 0x0000031F + +#define DDR_UIM_MR0_0 0x00000940 +#define DDR_UIM_MR1_0 0x00000103 +#define DDR_UIM_MR2_0 0x00000018 +#define DDR_UIM_MR3_0 0x00000000 +#define DDR_UIM_MR4_0 0x00000008 +#define DDR_UIM_MR5_0 0x00000460 +#define DDR_UIM_MR6_0 0x00000C16 +#define DDR_UIM_MR11_0 0x00000000 +#define DDR_UIM_MR12_0 0x00000000 +#define DDR_UIM_MR13_0 0x00000000 +#define DDR_UIM_MR14_0 0x00000000 +#define DDR_UIM_MR22_0 0x00000000 + +#define DDR_UIS_SWIZZLE_0 0x0000000C +#define DDR_UIS_SWIZZLE_1 0x00000005 +#define DDR_UIS_SWIZZLE_2 0x00000013 +#define DDR_UIS_SWIZZLE_3 0x0000001A +#define DDR_UIS_SWIZZLE_4 0x00000009 +#define DDR_UIS_SWIZZLE_5 0x00000003 +#define DDR_UIS_SWIZZLE_6 0x00000001 +#define DDR_UIS_SWIZZLE_7 0x00000019 +#define DDR_UIS_SWIZZLE_8 0x00000007 +#define DDR_UIS_SWIZZLE_9 0x00000004 +#define DDR_UIS_SWIZZLE_10 0x0000000A +#define DDR_UIS_SWIZZLE_11 0x0000000D +#define DDR_UIS_SWIZZLE_12 0x00000014 +#define DDR_UIS_SWIZZLE_13 0x00000000 +#define DDR_UIS_SWIZZLE_14 0x00000000 +#define DDR_UIS_SWIZZLE_15 0x00000000 +#define DDR_UIS_SWIZZLE_16 0x00000000 +#define DDR_UIS_SWIZZLE_17 0x00000000 +#define DDR_UIS_SWIZZLE_18 0x00000006 +#define DDR_UIS_SWIZZLE_19 0x0000000B +#define DDR_UIS_SWIZZLE_20 0x00000000 +#define DDR_UIS_SWIZZLE_21 0x00000000 +#define DDR_UIS_SWIZZLE_22 0x00000000 +#define DDR_UIS_SWIZZLE_23 0x00000008 +#define DDR_UIS_SWIZZLE_24 0x00000002 +#define DDR_UIS_SWIZZLE_25 0x00000018 +#define DDR_UIS_SWIZZLE_26 0x1A13050C +#define DDR_UIS_SWIZZLE_27 0x19010309 +#define DDR_UIS_SWIZZLE_28 0x0D0A0407 +#define DDR_UIS_SWIZZLE_29 0x00000014 +#define DDR_UIS_SWIZZLE_30 0x000B0600 +#define DDR_UIS_SWIZZLE_31 0x02080000 +#define DDR_UIS_SWIZZLE_32 0x00000018 +#define DDR_UIS_SWIZZLE_33 0x00000000 +#define DDR_UIS_SWIZZLE_34 0x00000000 +#define DDR_UIS_SWIZZLE_35 0x00000000 +#define DDR_UIS_SWIZZLE_36 0x00000000 +#define DDR_UIS_SWIZZLE_37 0x00000000 +#define DDR_UIS_SWIZZLE_38 0x00000000 +#define DDR_UIS_SWIZZLE_39 0x00000000 +#define DDR_UIS_SWIZZLE_40 0x00000000 +#define DDR_UIS_SWIZZLE_41 0x00000000 +#define DDR_UIS_SWIZZLE_42 0x00000000 +#define DDR_UIS_SWIZZLE_43 0x00000000 + +#include "stm32mp25-ddr.dtsi" diff --git a/fdts/stm32mp25-ddr4-2x8Gbits-2x16bits-1200MHz.dtsi b/fdts/stm32mp25-ddr4-2x8Gbits-2x16bits-1200MHz.dtsi new file mode 100644 index 00000000..674cb3da --- /dev/null +++ b/fdts/stm32mp25-ddr4-2x8Gbits-2x16bits-1200MHz.dtsi @@ -0,0 +1,245 @@ +// SPDX-License-Identifier: GPL-2.0+ OR BSD-3-Clause +/* + * Copyright (C) 2022-2024, STMicroelectronics - All Rights Reserved + */ + +/* + * STM32MP25 DDR4 board configuration + * DDR4 2x8Gbits 2x16bits 1200MHz + * + * version 1 + * package 1 Package selection (14x14 and 18x18) + * memclk 1200MHz (2x DFI clock) + range check + * Speed_Bin Worse from JEDEC + * width 32 32: full width / 16: half width + * ranks 1 Single or Dual rank + * density 8Gbits (per 16bit device) + * Addressing RBC row/bank interleaving + * RDBI No Read DBI + */ + +#define DDR_MEM_NAME "DDR4 2x8Gbits 2x16bits 1200MHz" +#define DDR_MEM_SPEED 1200000 +#define DDR_MEM_SIZE 0x80000000 + +#define DDR_MSTR 0x01040010 +#define DDR_MRCTRL0 0x00000030 +#define DDR_MRCTRL1 0x00000000 +#define DDR_MRCTRL2 0x00000000 +#define DDR_DERATEEN 0x00000000 +#define DDR_DERATEINT 0x00000000 +#define DDR_DERATECTL 0x00000000 +#define DDR_PWRCTL 0x00000000 +#define DDR_PWRTMG 0x00130001 +#define DDR_HWLPCTL 0x00000002 +#define DDR_RFSHCTL0 0x00210010 +#define DDR_RFSHCTL1 0x00000000 +#define DDR_RFSHCTL3 0x00000000 +#define DDR_RFSHTMG 0x009200D2 +#define DDR_RFSHTMG1 0x008C0000 +#define DDR_CRCPARCTL0 0x00000000 +#define DDR_CRCPARCTL1 0x00001000 +#define DDR_INIT0 0xC0020002 +#define DDR_INIT1 0x00010002 +#define DDR_INIT2 0x00000D00 +#define DDR_INIT3 0x09400103 +#define DDR_INIT4 0x00180000 +#define DDR_INIT5 0x00100004 +#define DDR_INIT6 0x00080460 +#define DDR_INIT7 0x00000C16 +#define DDR_DIMMCTL 0x00000000 +#define DDR_RANKCTL 0x0000066F +#define DDR_DRAMTMG0 0x11152815 +#define DDR_DRAMTMG1 0x0004051E +#define DDR_DRAMTMG2 0x0609060D +#define DDR_DRAMTMG3 0x0050400C +#define DDR_DRAMTMG4 0x0904050A +#define DDR_DRAMTMG5 0x06060403 +#define DDR_DRAMTMG6 0x02020005 +#define DDR_DRAMTMG7 0x00000202 +#define DDR_DRAMTMG8 0x04041007 +#define DDR_DRAMTMG9 0x0002040A +#define DDR_DRAMTMG10 0x001C180A +#define DDR_DRAMTMG11 0x4408021C +#define DDR_DRAMTMG12 0x0C020010 +#define DDR_DRAMTMG13 0x1C200004 +#define DDR_DRAMTMG14 0x000000A0 +#define DDR_DRAMTMG15 0x00000000 +#define DDR_ZQCTL0 0x01000040 +#define DDR_ZQCTL1 0x2000493E +#define DDR_ZQCTL2 0x00000000 +#define DDR_DFITMG0 0x038F8209 +#define DDR_DFITMG1 0x00080303 +#define DDR_DFILPCFG0 0x07004111 +#define DDR_DFILPCFG1 0x00000000 +#define DDR_DFIUPD0 0xC0300018 +#define DDR_DFIUPD1 0x005700B4 +#define DDR_DFIUPD2 0x80000000 +#define DDR_DFIMISC 0x00000041 +#define DDR_DFITMG2 0x00000F09 +#define DDR_DFITMG3 0x00000000 +#define DDR_DBICTL 0x00000001 +#define DDR_DFIPHYMSTR 0x80000000 +#define DDR_ADDRMAP0 0x0000001F +#define DDR_ADDRMAP1 0x003F0909 +#define DDR_ADDRMAP2 0x00000700 +#define DDR_ADDRMAP3 0x00000000 +#define DDR_ADDRMAP4 0x00001F1F +#define DDR_ADDRMAP5 0x070F0707 +#define DDR_ADDRMAP6 0x07070707 +#define DDR_ADDRMAP7 0x00000F0F +#define DDR_ADDRMAP8 0x00003F01 +#define DDR_ADDRMAP9 0x07070707 +#define DDR_ADDRMAP10 0x07070707 +#define DDR_ADDRMAP11 0x00000007 +#define DDR_ODTCFG 0x06000618 +#define DDR_ODTMAP 0x00000001 +#define DDR_SCHED 0x00000F00 +#define DDR_SCHED1 0x00000000 +#define DDR_PERFHPR1 0x0F000001 +#define DDR_PERFLPR1 0x0F000080 +#define DDR_PERFWR1 0x01000200 +#define DDR_DBG0 0x00000000 +#define DDR_DBG1 0x00000000 +#define DDR_DBGCMD 0x00000000 +#define DDR_SWCTL 0x00000000 +#define DDR_POISONCFG 0x00000000 +#define DDR_PCCFG 0x00000000 +#define DDR_PCFGR_0 0x00004100 +#define DDR_PCFGW_0 0x00004100 +#define DDR_PCTRL_0 0x00000000 +#define DDR_PCFGQOS0_0 0x00200007 +#define DDR_PCFGQOS1_0 0x01000100 +#define DDR_PCFGWQOS0_0 0x00000C07 +#define DDR_PCFGWQOS1_0 0x02000200 +#define DDR_PCFGR_1 0x00004100 +#define DDR_PCFGW_1 0x00004100 +#define DDR_PCTRL_1 0x00000000 +#define DDR_PCFGQOS0_1 0x00200007 +#define DDR_PCFGQOS1_1 0x01000180 +#define DDR_PCFGWQOS0_1 0x00000C07 +#define DDR_PCFGWQOS1_1 0x04000400 + +#define DDR_UIB_DRAMTYPE 0x00000000 +#define DDR_UIB_DIMMTYPE 0x00000004 +#define DDR_UIB_LP4XMODE 0x00000000 +#define DDR_UIB_NUMDBYTE 0x00000004 +#define DDR_UIB_NUMACTIVEDBYTEDFI0 0x00000004 +#define DDR_UIB_NUMACTIVEDBYTEDFI1 0x00000000 +#define DDR_UIB_NUMANIB 0x00000008 +#define DDR_UIB_NUMRANK_DFI0 0x00000001 +#define DDR_UIB_NUMRANK_DFI1 0x00000001 +#define DDR_UIB_DRAMDATAWIDTH 0x00000010 +#define DDR_UIB_NUMPSTATES 0x00000001 +#define DDR_UIB_FREQUENCY_0 0x000004B0 +#define DDR_UIB_PLLBYPASS_0 0x00000000 +#define DDR_UIB_DFIFREQRATIO_0 0x00000001 +#define DDR_UIB_DFI1EXISTS 0x00000001 +#define DDR_UIB_TRAIN2D 0x00000000 +#define DDR_UIB_HARDMACROVER 0x00000003 +#define DDR_UIB_READDBIENABLE_0 0x00000000 +#define DDR_UIB_DFIMODE 0x00000000 + +#define DDR_UIA_LP4RXPREAMBLEMODE_0 0x00000000 +#define DDR_UIA_LP4POSTAMBLEEXT_0 0x00000000 +#define DDR_UIA_D4RXPREAMBLELENGTH_0 0x00000000 +#define DDR_UIA_D4TXPREAMBLELENGTH_0 0x00000000 +#define DDR_UIA_EXTCALRESVAL 0x00000000 +#define DDR_UIA_IS2TTIMING_0 0x00000000 +#define DDR_UIA_ODTIMPEDANCE_0 0x00000035 +#define DDR_UIA_TXIMPEDANCE_0 0x00000028 +#define DDR_UIA_ATXIMPEDANCE 0x00000028 +#define DDR_UIA_MEMALERTEN 0x00000000 +#define DDR_UIA_MEMALERTPUIMP 0x00000000 +#define DDR_UIA_MEMALERTVREFLEVEL 0x00000000 +#define DDR_UIA_MEMALERTSYNCBYPASS 0x00000000 +#define DDR_UIA_DISDYNADRTRI_0 0x00000001 +#define DDR_UIA_PHYMSTRTRAININTERVAL_0 0x00000000 +#define DDR_UIA_PHYMSTRMAXREQTOACK_0 0x00000000 +#define DDR_UIA_WDQSEXT 0x00000000 +#define DDR_UIA_CALINTERVAL 0x00000009 +#define DDR_UIA_CALONCE 0x00000000 +#define DDR_UIA_LP4RL_0 0x00000000 +#define DDR_UIA_LP4WL_0 0x00000000 +#define DDR_UIA_LP4WLS_0 0x00000000 +#define DDR_UIA_LP4DBIRD_0 0x00000000 +#define DDR_UIA_LP4DBIWR_0 0x00000000 +#define DDR_UIA_LP4NWR_0 0x00000000 +#define DDR_UIA_LP4LOWPOWERDRV 0x00000000 +#define DDR_UIA_DRAMBYTESWAP 0x00000000 +#define DDR_UIA_RXENBACKOFF 0x00000000 +#define DDR_UIA_TRAINSEQUENCECTRL 0x00000000 +#define DDR_UIA_SNPSUMCTLOPT 0x00000000 +#define DDR_UIA_SNPSUMCTLF0RC5X_0 0x00000000 +#define DDR_UIA_TXSLEWRISEDQ_0 0x0000000F +#define DDR_UIA_TXSLEWFALLDQ_0 0x0000000F +#define DDR_UIA_TXSLEWRISEAC 0x0000000F +#define DDR_UIA_TXSLEWFALLAC 0x0000000F +#define DDR_UIA_DISABLERETRAINING 0x00000001 +#define DDR_UIA_DISABLEPHYUPDATE 0x00000000 +#define DDR_UIA_ENABLEHIGHCLKSKEWFIX 0x00000000 +#define DDR_UIA_DISABLEUNUSEDADDRLNS 0x00000001 +#define DDR_UIA_PHYINITSEQUENCENUM 0x00000000 +#define DDR_UIA_ENABLEDFICSPOLARITYFIX 0x00000000 +#define DDR_UIA_PHYVREF 0x0000005E +#define DDR_UIA_SEQUENCECTRL_0 0x0000031F + +#define DDR_UIM_MR0_0 0x00000940 +#define DDR_UIM_MR1_0 0x00000103 +#define DDR_UIM_MR2_0 0x00000018 +#define DDR_UIM_MR3_0 0x00000000 +#define DDR_UIM_MR4_0 0x00000008 +#define DDR_UIM_MR5_0 0x00000460 +#define DDR_UIM_MR6_0 0x00000C16 +#define DDR_UIM_MR11_0 0x00000000 +#define DDR_UIM_MR12_0 0x00000000 +#define DDR_UIM_MR13_0 0x00000000 +#define DDR_UIM_MR14_0 0x00000000 +#define DDR_UIM_MR22_0 0x00000000 + +#define DDR_UIS_SWIZZLE_0 0x0000000C +#define DDR_UIS_SWIZZLE_1 0x00000005 +#define DDR_UIS_SWIZZLE_2 0x00000013 +#define DDR_UIS_SWIZZLE_3 0x0000001A +#define DDR_UIS_SWIZZLE_4 0x00000009 +#define DDR_UIS_SWIZZLE_5 0x00000003 +#define DDR_UIS_SWIZZLE_6 0x00000001 +#define DDR_UIS_SWIZZLE_7 0x00000019 +#define DDR_UIS_SWIZZLE_8 0x00000007 +#define DDR_UIS_SWIZZLE_9 0x00000004 +#define DDR_UIS_SWIZZLE_10 0x0000000A +#define DDR_UIS_SWIZZLE_11 0x0000000D +#define DDR_UIS_SWIZZLE_12 0x00000014 +#define DDR_UIS_SWIZZLE_13 0x00000000 +#define DDR_UIS_SWIZZLE_14 0x00000000 +#define DDR_UIS_SWIZZLE_15 0x00000000 +#define DDR_UIS_SWIZZLE_16 0x00000000 +#define DDR_UIS_SWIZZLE_17 0x00000000 +#define DDR_UIS_SWIZZLE_18 0x00000006 +#define DDR_UIS_SWIZZLE_19 0x0000000B +#define DDR_UIS_SWIZZLE_20 0x00000000 +#define DDR_UIS_SWIZZLE_21 0x00000000 +#define DDR_UIS_SWIZZLE_22 0x00000000 +#define DDR_UIS_SWIZZLE_23 0x00000008 +#define DDR_UIS_SWIZZLE_24 0x00000002 +#define DDR_UIS_SWIZZLE_25 0x00000018 +#define DDR_UIS_SWIZZLE_26 0x1A13050C +#define DDR_UIS_SWIZZLE_27 0x19010309 +#define DDR_UIS_SWIZZLE_28 0x0D0A0407 +#define DDR_UIS_SWIZZLE_29 0x00000014 +#define DDR_UIS_SWIZZLE_30 0x000B0600 +#define DDR_UIS_SWIZZLE_31 0x02080000 +#define DDR_UIS_SWIZZLE_32 0x00000018 +#define DDR_UIS_SWIZZLE_33 0x00000000 +#define DDR_UIS_SWIZZLE_34 0x00000000 +#define DDR_UIS_SWIZZLE_35 0x00000000 +#define DDR_UIS_SWIZZLE_36 0x00000000 +#define DDR_UIS_SWIZZLE_37 0x00000000 +#define DDR_UIS_SWIZZLE_38 0x00000000 +#define DDR_UIS_SWIZZLE_39 0x00000000 +#define DDR_UIS_SWIZZLE_40 0x00000000 +#define DDR_UIS_SWIZZLE_41 0x00000000 +#define DDR_UIS_SWIZZLE_42 0x00000000 +#define DDR_UIS_SWIZZLE_43 0x00000000 + +#include "stm32mp25-ddr.dtsi" diff --git a/fdts/stm32mp25-fw-config.dtsi b/fdts/stm32mp25-fw-config.dtsi new file mode 100644 index 00000000..2f83f24e --- /dev/null +++ b/fdts/stm32mp25-fw-config.dtsi @@ -0,0 +1,42 @@ +// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) +/* + * Copyright (c) 2024, STMicroelectronics - All Rights Reserved + */ + +#include <common/tbbr/tbbr_img_def.h> + +#include <platform_def.h> + +/dts-v1/; + +/ { + dtb-registry { + compatible = "fconf,dyn_cfg-dtb_registry"; + + hw-config { + load-address = <0x0 STM32MP_HW_CONFIG_BASE>; + max-size = <STM32MP_HW_CONFIG_MAX_SIZE>; + id = <HW_CONFIG_ID>; + }; + + nt_fw { + load-address = <0x0 STM32MP_BL33_BASE>; + max-size = <STM32MP_BL33_MAX_SIZE>; + id = <BL33_IMAGE_ID>; + }; + + soc_fw { + load-address = <0x0 STM32MP_SYSRAM_BASE>; + max-size = <STM32MP_BL31_SIZE>; + id = <BL31_IMAGE_ID>; + }; + + soc_fw-config { + id = <SOC_FW_CONFIG_ID>; + }; + + tos_fw { + id = <BL32_IMAGE_ID>; + }; + }; +}; diff --git a/fdts/stm32mp25-pinctrl.dtsi b/fdts/stm32mp25-pinctrl.dtsi index 05876a39..a22c8239 100644 --- a/fdts/stm32mp25-pinctrl.dtsi +++ b/fdts/stm32mp25-pinctrl.dtsi @@ -1,11 +1,75 @@ // SPDX-License-Identifier: (GPL-2.0-or-later OR BSD-3-Clause) /* - * Copyright (C) 2023, STMicroelectronics - All Rights Reserved + * Copyright (C) 2023-2024, STMicroelectronics - All Rights Reserved * Author: Alexandre Torgue <alexandre.torgue@foss.st.com> for STMicroelectronics. */ #include <dt-bindings/pinctrl/stm32-pinfunc.h> &pinctrl { + /omit-if-no-ref/ + i2c7_pins_a: i2c7-0 { + pins1 { + pinmux = <STM32_PINMUX('D', 15, AF10)>, /* I2C7_SCL */ + <STM32_PINMUX('D', 14, AF10)>; /* I2C7_SDA */ + bias-disable; + drive-open-drain; + slew-rate = <0>; + }; + }; + + /omit-if-no-ref/ + sdmmc1_b4_pins_a: sdmmc1-b4-0 { + pins1 { + pinmux = <STM32_PINMUX('E', 4, AF10)>, /* SDMMC1_D0 */ + <STM32_PINMUX('E', 5, AF10)>, /* SDMMC1_D1 */ + <STM32_PINMUX('E', 0, AF10)>, /* SDMMC1_D2 */ + <STM32_PINMUX('E', 1, AF10)>, /* SDMMC1_D3 */ + <STM32_PINMUX('E', 2, AF10)>; /* SDMMC1_CMD */ + slew-rate = <2>; + drive-push-pull; + bias-disable; + }; + pins2 { + pinmux = <STM32_PINMUX('E', 3, AF10)>; /* SDMMC1_CK */ + slew-rate = <3>; + drive-push-pull; + bias-disable; + }; + }; + + /omit-if-no-ref/ + sdmmc2_b4_pins_a: sdmmc2-b4-0 { + pins1 { + pinmux = <STM32_PINMUX('E', 13, AF12)>, /* SDMMC2_D0 */ + <STM32_PINMUX('E', 11, AF12)>, /* SDMMC2_D1 */ + <STM32_PINMUX('E', 8, AF12)>, /* SDMMC2_D2 */ + <STM32_PINMUX('E', 12, AF12)>, /* SDMMC2_D3 */ + <STM32_PINMUX('E', 15, AF12)>; /* SDMMC2_CMD */ + slew-rate = <2>; + drive-push-pull; + bias-pull-up; + }; + pins2 { + pinmux = <STM32_PINMUX('E', 14, AF12)>; /* SDMMC2_CK */ + slew-rate = <3>; + drive-push-pull; + bias-pull-up; + }; + }; + + /omit-if-no-ref/ + sdmmc2_d47_pins_a: sdmmc2-d47-0 { + pins { + pinmux = <STM32_PINMUX('E', 10, AF12)>, /* SDMMC2_D4 */ + <STM32_PINMUX('E', 9, AF12)>, /* SDMMC2_D5 */ + <STM32_PINMUX('E', 6, AF12)>, /* SDMMC2_D6 */ + <STM32_PINMUX('E', 7, AF12)>; /* SDMMC2_D7 */ + slew-rate = <2>; + drive-push-pull; + bias-pull-up; + }; + }; + /omit-if-no-ref/ usart2_pins_a: usart2-0 { pins1 { diff --git a/fdts/stm32mp251.dtsi b/fdts/stm32mp251.dtsi index f55a3b97..c2c27644 100644 --- a/fdts/stm32mp251.dtsi +++ b/fdts/stm32mp251.dtsi @@ -1,6 +1,6 @@ // SPDX-License-Identifier: (GPL-2.0-or-later OR BSD-3-Clause) /* - * Copyright (C) 2023, STMicroelectronics - All Rights Reserved + * Copyright (C) 2023-2024, STMicroelectronics - All Rights Reserved * Author: Alexandre Torgue <alexandre.torgue@foss.st.com> for STMicroelectronics. */ @@ -97,6 +97,198 @@ resets = <&rcc USART2_R>; status = "disabled"; }; + + usart3: serial@400f0000 { + compatible = "st,stm32h7-uart"; + reg = <0x400f0000 0x400>; + clocks = <&rcc CK_KER_USART3>; + resets = <&rcc USART3_R>; + status = "disabled"; + }; + + uart4: serial@40100000 { + compatible = "st,stm32h7-uart"; + reg = <0x40100000 0x400>; + clocks = <&rcc CK_KER_UART4>; + resets = <&rcc UART4_R>; + status = "disabled"; + }; + + uart5: serial@40110000 { + compatible = "st,stm32h7-uart"; + reg = <0x40110000 0x400>; + clocks = <&rcc CK_KER_UART5>; + resets = <&rcc UART5_R>; + status = "disabled"; + }; + + i2c1: i2c@40120000 { + compatible = "st,stm32mp25-i2c"; + reg = <0x40120000 0x400>; + clocks = <&rcc CK_KER_I2C1>; + resets = <&rcc I2C1_R>; + status = "disabled"; + }; + + i2c2: i2c@40130000 { + compatible = "st,stm32mp25-i2c"; + reg = <0x40130000 0x400>; + clocks = <&rcc CK_KER_I2C2>; + resets = <&rcc I2C2_R>; + status = "disabled"; + }; + + i2c3: i2c@40140000 { + compatible = "st,stm32mp25-i2c"; + reg = <0x40140000 0x400>; + clocks = <&rcc CK_KER_I2C3>; + resets = <&rcc I2C3_R>; + status = "disabled"; + }; + + i2c4: i2c@40150000 { + compatible = "st,stm32mp25-i2c"; + reg = <0x40150000 0x400>; + clocks = <&rcc CK_KER_I2C4>; + resets = <&rcc I2C4_R>; + status = "disabled"; + }; + + i2c5: i2c@40160000 { + compatible = "st,stm32mp25-i2c"; + reg = <0x40160000 0x400>; + clocks = <&rcc CK_KER_I2C5>; + resets = <&rcc I2C5_R>; + status = "disabled"; + }; + + i2c6: i2c@40170000 { + compatible = "st,stm32mp25-i2c"; + reg = <0x40170000 0x400>; + clocks = <&rcc CK_KER_I2C6>; + resets = <&rcc I2C6_R>; + status = "disabled"; + }; + + i2c7: i2c@40180000 { + compatible = "st,stm32mp25-i2c"; + reg = <0x40180000 0x400>; + clocks = <&rcc CK_KER_I2C7>; + resets = <&rcc I2C7_R>; + status = "disabled"; + }; + + usart6: serial@40220000 { + compatible = "st,stm32h7-uart"; + reg = <0x40220000 0x400>; + clocks = <&rcc CK_KER_USART6>; + resets = <&rcc USART6_R>; + status = "disabled"; + }; + + uart9: serial@402c0000 { + compatible = "st,stm32h7-uart"; + reg = <0x402c0000 0x400>; + clocks = <&rcc CK_KER_UART9>; + resets = <&rcc UART9_R>; + status = "disabled"; + }; + + usart1: serial@40330000 { + compatible = "st,stm32h7-uart"; + reg = <0x40330000 0x400>; + clocks = <&rcc CK_KER_USART1>; + resets = <&rcc USART1_R>; + status = "disabled"; + }; + + uart7: serial@40370000 { + compatible = "st,stm32h7-uart"; + reg = <0x40370000 0x400>; + clocks = <&rcc CK_KER_UART7>; + resets = <&rcc UART7_R>; + status = "disabled"; + }; + + uart8: serial@40380000 { + compatible = "st,stm32h7-uart"; + reg = <0x40380000 0x400>; + clocks = <&rcc CK_KER_UART8>; + resets = <&rcc UART8_R>; + status = "disabled"; + }; + + i2c8: i2c@46040000 { + compatible = "st,stm32mp25-i2c"; + reg = <0x46040000 0x400>; + clocks = <&rcc CK_KER_I2C8>; + resets = <&rcc I2C8_R>; + status = "disabled"; + }; + + sdmmc1: mmc@48220000 { + compatible = "st,stm32mp25-sdmmc2", "arm,pl18x", "arm,primecell"; + arm,primecell-periphid = <0x00353180>; + reg = <0x48220000 0x400>, <0x44230400 0x8>; + clocks = <&rcc CK_KER_SDMMC1>; + clock-names = "apb_pclk"; + resets = <&rcc SDMMC1_R>; + cap-sd-highspeed; + cap-mmc-highspeed; + max-frequency = <120000000>; + status = "disabled"; + }; + + sdmmc2: mmc@48230000 { + compatible = "st,stm32mp25-sdmmc2", "arm,pl18x", "arm,primecell"; + arm,primecell-periphid = <0x00353180>; + reg = <0x48230000 0x400>, <0x44230800 0x8>; + clocks = <&rcc CK_KER_SDMMC2>; + clock-names = "apb_pclk"; + resets = <&rcc SDMMC2_R>; + cap-sd-highspeed; + cap-mmc-highspeed; + max-frequency = <120000000>; + status = "disabled"; + }; + }; + + bsec: efuse@44000000 { + compatible = "st,stm32mp25-bsec"; + reg = <0x44000000 0x400>; + #address-cells = <1>; + #size-cells = <1>; + + uid_otp: uid-otp@14 { + reg = <0x14 0xc>; + }; + part_number_otp: part-number-otp@24 { + reg = <0x24 0x4>; + }; + nand_otp: otp16@40 { + reg = <0x40 0x4>; + }; + lifecycle2_otp: otp18@48 { + reg = <0x48 0x4>; + }; + nand2_otp: otp20@50 { + reg = <0x50 0x4>; + }; + rev_otp@198 { + reg = <0x198 0x4>; + }; + package_otp: package-otp@1e8 { + reg = <0x1e8 0x1>; + }; + hconf1_otp: otp124@1f0 { + reg = <0x1f0 0x4>; + }; + pkh_otp: otp144@240 { + reg = <0x240 0x20>; + }; + oem_fip_enc_key: otp260@410 { + reg = <0x410 0x20>; + }; }; rcc: rcc@44200000 { @@ -136,12 +328,18 @@ reg = <0x44230000 0x10000>; }; + ddr: ddr@48040000 { + compatible = "st,stm32mp2-ddr"; + reg = <0x48040000 0x10000>, + <0x48c00000 0x400000>; + status = "okay"; + }; + pinctrl: pinctrl@44240000 { #address-cells = <1>; #size-cells = <1>; compatible = "st,stm32mp257-pinctrl"; ranges = <0 0x44240000 0xa0400>; - pins-are-numbered; gpioa: gpio@44240000 { gpio-controller; @@ -270,7 +468,6 @@ #size-cells = <1>; compatible = "st,stm32mp257-z-pinctrl"; ranges = <0 0x46200000 0x400>; - pins-are-numbered; gpioz: gpio@46200000 { gpio-controller; diff --git a/fdts/stm32mp257f-ev1-ca35tdcid-fw-config.dtsi b/fdts/stm32mp257f-ev1-ca35tdcid-fw-config.dtsi new file mode 100644 index 00000000..5bfcf8dd --- /dev/null +++ b/fdts/stm32mp257f-ev1-ca35tdcid-fw-config.dtsi @@ -0,0 +1,23 @@ +// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) +/* + * Copyright (C) STMicroelectronics 2024 - All Rights Reserved + */ + +/* + * STM32MP25 tf-a firmware config + * Project : open + * Generated by XLmx tool version 2.2 - 2/27/2024 11:46:17 AM + */ + +/ { + dtb-registry { + soc_fw-config { + load-address = <0x0 0x81ff0000>; + max-size = <0x10000>; + }; + tos_fw { + load-address = <0x0 0x82000000>; + max-size = <0x2000000>; + }; + }; +}; diff --git a/fdts/stm32mp257f-ev1-ca35tdcid-rcc.dtsi b/fdts/stm32mp257f-ev1-ca35tdcid-rcc.dtsi new file mode 100644 index 00000000..3e84df5e --- /dev/null +++ b/fdts/stm32mp257f-ev1-ca35tdcid-rcc.dtsi @@ -0,0 +1,97 @@ +// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) +/* + * Copyright (C) STMicroelectronics 2024 - All Rights Reserved + * Author: Loic Pallardy loic.pallardy@foss.st.com for STMicroelectronics. + */ + +/* + * STM32MP25 Clock tree device tree configuration + * Project : open + * Generated by XLmx tool version 2.2 - 2/27/2024 11:46:16 AM + */ + +&clk_hse { + clock-frequency = <40000000>; +}; + +&clk_hsi { + clock-frequency = <64000000>; +}; + +&clk_lse { + clock-frequency = <32768>; +}; + +&clk_lsi { + clock-frequency = <32000>; +}; + +&clk_msi { + clock-frequency = <16000000>; +}; + +&rcc { + st,busclk = < + DIV_CFG(DIV_LSMCU, 1) + DIV_CFG(DIV_APB1, 0) + DIV_CFG(DIV_APB2, 0) + DIV_CFG(DIV_APB3, 0) + DIV_CFG(DIV_APB4, 0) + DIV_CFG(DIV_APBDBG, 0) + >; + + st,flexgen = < + FLEXGEN_CFG(0, XBAR_SRC_PLL4, 0, 2) + FLEXGEN_CFG(1, XBAR_SRC_PLL4, 0, 5) + FLEXGEN_CFG(2, XBAR_SRC_PLL4, 0, 1) + FLEXGEN_CFG(4, XBAR_SRC_PLL4, 0, 3) + FLEXGEN_CFG(5, XBAR_SRC_PLL4, 0, 2) + FLEXGEN_CFG(8, XBAR_SRC_HSI_KER, 0, 0) + FLEXGEN_CFG(48, XBAR_SRC_PLL5, 0, 3) + FLEXGEN_CFG(51, XBAR_SRC_PLL4, 0, 5) + FLEXGEN_CFG(52, XBAR_SRC_PLL4, 0, 5) + FLEXGEN_CFG(58, XBAR_SRC_HSE, 0, 1) + FLEXGEN_CFG(63, XBAR_SRC_PLL4, 0, 2) + >; + + st,kerclk = < + MUX_CFG(MUX_USB2PHY1, MUX_USB2PHY1_FLEX57) + MUX_CFG(MUX_USB2PHY2, MUX_USB2PHY2_FLEX58) + >; + + pll1: st,pll-1 { + st,pll = <&pll1_cfg_1200Mhz>; + + pll1_cfg_1200Mhz: pll1-cfg-1200Mhz { + cfg = <30 1 1 1>; + src = <MUX_CFG(MUX_MUXSEL5, MUXSEL_HSE)>; + }; + }; + + pll2: st,pll-2 { + st,pll = <&pll2_cfg_600Mhz>; + + pll2_cfg_600Mhz: pll2-cfg-600Mhz { + cfg = <30 1 1 2>; + src = <MUX_CFG(MUX_MUXSEL6, MUXSEL_HSE)>; + }; + }; + + pll4: st,pll-4 { + st,pll = <&pll4_cfg_1200Mhz>; + + pll4_cfg_1200Mhz: pll4-cfg-1200Mhz { + cfg = <30 1 1 1>; + src = <MUX_CFG(MUX_MUXSEL0, MUXSEL_HSE)>; + }; + }; + + pll5: st,pll-5 { + st,pll = <&pll5_cfg_532Mhz>; + + pll5_cfg_532Mhz: pll5-cfg-532Mhz { + cfg = <133 5 1 2>; + src = <MUX_CFG(MUX_MUXSEL1, MUXSEL_HSE)>; + }; + }; +}; diff --git a/fdts/stm32mp257f-ev1-fw-config.dts b/fdts/stm32mp257f-ev1-fw-config.dts new file mode 100644 index 00000000..9424f493 --- /dev/null +++ b/fdts/stm32mp257f-ev1-fw-config.dts @@ -0,0 +1,7 @@ +// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) +/* + * Copyright (c) 2024, STMicroelectronics - All Rights Reserved + */ + +#include "stm32mp25-fw-config.dtsi" +#include "stm32mp257f-ev1-ca35tdcid-fw-config.dtsi" diff --git a/fdts/stm32mp257f-ev1.dts b/fdts/stm32mp257f-ev1.dts index b7e92e47..5d5e35de 100644 --- a/fdts/stm32mp257f-ev1.dts +++ b/fdts/stm32mp257f-ev1.dts @@ -1,13 +1,16 @@ // SPDX-License-Identifier: (GPL-2.0-or-later OR BSD-3-Clause) /* - * Copyright (C) 2023, STMicroelectronics - All Rights Reserved + * Copyright (C) 2023-2024, STMicroelectronics - All Rights Reserved * Author: Alexandre Torgue <alexandre.torgue@foss.st.com> for STMicroelectronics. */ /dts-v1/; +#include <dt-bindings/clock/stm32mp25-clksrc.h> #include "stm32mp257.dtsi" #include "stm32mp25xf.dtsi" +#include "stm32mp257f-ev1-ca35tdcid-rcc.dtsi" +#include "stm32mp25-ddr4-2x16Gbits-2x16bits-1200MHz.dtsi" #include "stm32mp25-pinctrl.dtsi" #include "stm32mp25xxai-pinctrl.dtsi" @@ -29,6 +32,163 @@ }; }; +&bsec { + board_id: board-id@3d8 { + reg = <0x3d8 0x4>; + }; +}; + +&ddr { + vdd-supply = <&vdd_ddr>; + vtt-supply = <&vtt_ddr>; + vpp-supply = <&vpp_ddr>; + vref-supply = <&vref_ddr>; +}; + +&i2c7 { + pinctrl-names = "default"; + pinctrl-0 = <&i2c7_pins_a>; + i2c-scl-rising-time-ns = <185>; + i2c-scl-falling-time-ns = <20>; + clock-frequency = <400000>; + status = "okay"; + #address-cells = <1>; + #size-cells = <0>; + + pmic2: stpmic@33 { + compatible = "st,stpmic2"; + reg = <0x33>; + status = "okay"; + + regulators { + compatible = "st,stpmic2-regulators"; + + vddcpu: buck1 { + regulator-name = "vddcpu"; + regulator-min-microvolt = <800000>; + regulator-max-microvolt = <910000>; + regulator-always-on; + }; + vddcore: buck2 { + regulator-name = "vddcore"; + regulator-min-microvolt = <820000>; + regulator-max-microvolt = <820000>; + regulator-always-on; + }; + vddgpu: buck3 { + regulator-name = "vddgpu"; + regulator-min-microvolt = <800000>; + regulator-max-microvolt = <900000>; + regulator-always-on; + }; + vddio_pmic: buck4 { + regulator-name = "vddio_pmic"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + regulator-always-on; + }; + v1v8: buck5 { + regulator-name = "v1v8"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + regulator-always-on; + }; + vdd_ddr: buck6 { + regulator-name = "vdd_ddr"; + regulator-min-microvolt = <1200000>; + regulator-max-microvolt = <1200000>; + }; + v3v3: buck7 { + regulator-name = "v3v3"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + regulator-always-on; + }; + vdda1v8_aon: ldo1 { + regulator-name = "vdda1v8_aon"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + regulator-always-on; + }; + vdd_emmc: ldo2 { + regulator-name = "vdd_emmc"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + regulator-always-on; + }; + vtt_ddr: ldo3 { + regulator-name = "vtt_ddr"; + st,regulator-sink-source; + }; + vdd3v3_usb: ldo4 { + regulator-name = "vdd3v3_usb"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + regulator-always-on; + }; + vpp_ddr: ldo5 { + regulator-name = "vpp_ddr"; + regulator-min-microvolt = <2500000>; + regulator-max-microvolt = <2500000>; + regulator-enable-ramp-delay = <1000>; + }; + vdd_sdcard: ldo7 { + regulator-name = "vdd_sdcard"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + regulator-always-on; + }; + vddio_sdcard: ldo8 { + regulator-name = "vddio_sdcard"; + st,regulator-bypass-microvolt = <3300000>; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <3300000>; + regulator-always-on; + }; + vref_ddr: refddr { + regulator-name = "vref_ddr"; + }; + }; + }; +}; + +&pwr { + vddio1: vddio1 { + vddio1-supply = <&vddio_sdcard>; + }; + vddio2: vddio2 { + vddio2-supply = <&v1v8>; + }; + vddio3: vddio3 { + vddio3-supply = <&vddio_pmic>; + }; + vddio4: vddio4 { + vddio4-supply = <&vddio_pmic>; + }; + vddio: vddio { + vdd-supply = <&vddio_pmic>; + }; +}; + +&sdmmc1 { + pinctrl-names = "default"; + pinctrl-0 = <&sdmmc1_b4_pins_a>; + st,neg-edge; + bus-width = <4>; + status = "okay"; +}; + +&sdmmc2 { + pinctrl-names = "default"; + pinctrl-0 = <&sdmmc2_b4_pins_a &sdmmc2_d47_pins_a>; + non-removable; + no-sd; + no-sdio; + st,neg-edge; + bus-width = <8>; + status = "okay"; +}; + &usart2 { pinctrl-names = "default"; pinctrl-0 = <&usart2_pins_a>; diff --git a/fdts/cot_descriptors.dtsi b/fdts/tbbr_cot_descriptors.dtsi similarity index 89% rename from fdts/cot_descriptors.dtsi rename to fdts/tbbr_cot_descriptors.dtsi index 411bae6c..253297f3 100644 --- a/fdts/cot_descriptors.dtsi +++ b/fdts/tbbr_cot_descriptors.dtsi @@ -1,10 +1,15 @@ /* - * Copyright (c) 2020, ARM Limited. All rights reserved. + * Copyright (c) 2020-2024, Arm Limited. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ +#if USE_TBBR_DEFS #include <tools_share/tbbr_oid.h> +#else +#include <platform_oid.h> +#endif + #include <common/tbbr/tbbr_img_def.h> #include <common/nv_cntr_ids.h> @@ -15,7 +20,7 @@ cot { trusted_boot_fw_cert: trusted_boot_fw_cert { root-certificate; image-id =<TRUSTED_BOOT_FW_CERT_ID>; - antirollback-counter = <&trusted_nv_counter>; + antirollback-counter = <&trusted_nv_ctr>; tb_fw_hash: tb_fw_hash { oid = TRUSTED_BOOT_FW_HASH_OID; @@ -34,7 +39,7 @@ cot { trusted_key_cert: trusted_key_cert { root-certificate; image-id = <TRUSTED_KEY_CERT_ID>; - antirollback-counter = <&trusted_nv_counter>; + antirollback-counter = <&trusted_nv_ctr>; trusted_world_pk: trusted_world_pk { oid = TRUSTED_WORLD_PK_OID; @@ -48,7 +53,7 @@ cot { image-id = <SCP_FW_KEY_CERT_ID>; parent = <&trusted_key_cert>; signing-key = <&trusted_world_pk>; - antirollback-counter = <&trusted_nv_counter>; + antirollback-counter = <&trusted_nv_ctr>; scp_fw_content_pk: scp_fw_content_pk { oid = SCP_FW_CONTENT_CERT_PK_OID; @@ -59,7 +64,7 @@ cot { image-id = <SCP_FW_CONTENT_CERT_ID>; parent = <&scp_fw_key_cert>; signing-key = <&scp_fw_content_pk>; - antirollback-counter = <&trusted_nv_counter>; + antirollback-counter = <&trusted_nv_ctr>; scp_fw_hash: scp_fw_hash { oid = SCP_FW_HASH_OID; @@ -70,7 +75,7 @@ cot { image-id = <SOC_FW_KEY_CERT_ID>; parent = <&trusted_key_cert>; signing-key = <&trusted_world_pk>; - antirollback-counter = <&trusted_nv_counter>; + antirollback-counter = <&trusted_nv_ctr>; soc_fw_content_pk: soc_fw_content_pk { oid = SOC_FW_CONTENT_CERT_PK_OID; }; @@ -80,7 +85,7 @@ cot { image-id = <SOC_FW_CONTENT_CERT_ID>; parent = <&soc_fw_key_cert>; signing-key = <&soc_fw_content_pk>; - antirollback-counter = <&trusted_nv_counter>; + antirollback-counter = <&trusted_nv_ctr>; soc_fw_hash: soc_fw_hash { oid = SOC_AP_FW_HASH_OID; @@ -94,7 +99,7 @@ cot { image-id = <TRUSTED_OS_FW_KEY_CERT_ID>; parent = <&trusted_key_cert>; signing-key = <&trusted_world_pk>; - antirollback-counter = <&trusted_nv_counter>; + antirollback-counter = <&trusted_nv_ctr>; tos_fw_content_pk: tos_fw_content_pk { oid = TRUSTED_OS_FW_CONTENT_CERT_PK_OID; @@ -105,7 +110,7 @@ cot { image-id = <TRUSTED_OS_FW_CONTENT_CERT_ID>; parent = <&trusted_os_fw_key_cert>; signing-key = <&tos_fw_content_pk>; - antirollback-counter = <&trusted_nv_counter>; + antirollback-counter = <&trusted_nv_ctr>; tos_fw_hash: tos_fw_hash { oid = TRUSTED_OS_FW_HASH_OID; @@ -125,7 +130,7 @@ cot { image-id = <NON_TRUSTED_FW_KEY_CERT_ID>; parent = <&trusted_key_cert>; signing-key = <&non_trusted_world_pk>; - antirollback-counter = <&non_trusted_nv_counter>; + antirollback-counter = <&non_trusted_nv_ctr>; nt_fw_content_pk: nt_fw_content_pk { oid = NON_TRUSTED_FW_CONTENT_CERT_PK_OID; @@ -136,7 +141,7 @@ cot { image-id = <NON_TRUSTED_FW_CONTENT_CERT_ID>; parent = <&non_trusted_fw_key_cert>; signing-key = <&nt_fw_content_pk>; - antirollback-counter = <&non_trusted_nv_counter>; + antirollback-counter = <&non_trusted_nv_ctr>; nt_world_bl_hash: nt_world_bl_hash { oid = NON_TRUSTED_WORLD_BOOTLOADER_HASH_OID; @@ -151,7 +156,7 @@ cot { image-id = <SIP_SP_CONTENT_CERT_ID>; parent = <&trusted_key_cert>; signing-key = <&trusted_world_pk>; - antirollback-counter = <&trusted_nv_counter>; + antirollback-counter = <&trusted_nv_ctr>; sp_pkg1_hash: sp_pkg1_hash { oid = SP_PKG1_HASH_OID; @@ -190,10 +195,10 @@ cot { hash = <&hw_config_hash>; }; - tb_fw_config { - image-id = <TB_FW_CONFIG_ID>; + fw_config { + image-id = <FW_CONFIG_ID>; parent = <&trusted_boot_fw_cert>; - hash = <&tb_fw_config_hash>; + hash = <&fw_config_hash>; }; scp_bl2_image { @@ -308,12 +313,12 @@ non_volatile_counters: non_volatile_counters { #address-cells = <1>; #size-cells = <0>; - trusted_nv_counter: trusted_nv_counter { + trusted_nv_ctr: trusted_nv_ctr { id = <TRUSTED_NV_CTR_ID>; oid = TRUSTED_FW_NVCOUNTER_OID; }; - non_trusted_nv_counter: non_trusted_nv_counter { + non_trusted_nv_ctr: non_trusted_nv_ctr { id = <NON_TRUSTED_NV_CTR_ID>; oid = NON_TRUSTED_FW_NVCOUNTER_OID; }; diff --git a/fdts/tc-base.dtsi b/fdts/tc-base.dtsi new file mode 100644 index 00000000..735d429a --- /dev/null +++ b/fdts/tc-base.dtsi @@ -0,0 +1,683 @@ +/* + * Copyright (c) 2020-2024, Arm Limited. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +/* If SCMI power domain control is enabled */ +#if TC_SCMI_PD_CTRL_EN +#define GPU_SCMI_PD_IDX (PLAT_MAX_CPUS_PER_CLUSTER + 1) +#define DPU_SCMI_PD_IDX (PLAT_MAX_CPUS_PER_CLUSTER + 2) +#endif /* TC_SCMI_PD_CTRL_EN */ + +/* Use SCMI controlled clocks */ +#if TC_DPU_USE_SCMI_CLK +#define DPU_CLK_ATTR1 \ + clocks = <&scmi_clk 0>; \ + clock-names = "aclk" + +#define DPU_CLK_ATTR2 \ + clocks = <&scmi_clk 1>; \ + clock-names = "pxclk" + +#define DPU_CLK_ATTR3 \ + clocks = <&scmi_clk 2>; \ + clock-names = "pxclk" \ +/* Use fixed clocks */ +#else /* !TC_DPU_USE_SCMI_CLK */ +#define DPU_CLK_ATTR1 \ + clocks = <&dpu_aclk>; \ + clock-names = "aclk" + +#define DPU_CLK_ATTR2 \ + clocks = <&dpu_pixel_clk>, <&dpu_aclk>; \ + clock-names = "pxclk", "aclk" + +#define DPU_CLK_ATTR3 DPU_CLK_ATTR2 +#endif /* !TC_DPU_USE_SCMI_CLK */ + +/ { + compatible = "arm,tc"; + interrupt-parent = <&gic>; + #address-cells = <2>; + #size-cells = <2>; + + aliases { + serial0 = &os_uart; + }; + + chosen { + /* + * Add some dummy entropy for Linux so it + * doesn't delay the boot waiting for it. + */ + rng-seed = <0x01 0x02 0x04 0x05 0x06 0x07 0x08 \ + 0x01 0x02 0x04 0x05 0x06 0x07 0x08 \ + 0x01 0x02 0x04 0x05 0x06 0x07 0x08 \ + 0x01 0x02 0x04 0x05 0x06 0x07 0x08 \ + 0x01 0x02 0x04 0x05 0x06 0x07 0x08 \ + 0x01 0x02 0x04 0x05 0x06 0x07 0x08 \ + 0x01 0x02 0x04 0x05 0x06 0x07 0x08 \ + 0x01 0x02 0x04 0x05 0x06 0x07 0x08 >; + }; + + cpus { + #address-cells = <1>; + #size-cells = <0>; + + cpu-map { + cluster0 { + core0 { + cpu = <&CPU0>; + }; + core1 { + cpu = <&CPU1>; + }; + core2 { + cpu = <&CPU2>; + }; + core3 { + cpu = <&CPU3>; + }; + core4 { + cpu = <&CPU4>; + }; + core5 { + cpu = <&CPU5>; + }; + core6 { + cpu = <&CPU6>; + }; + core7 { + cpu = <&CPU7>; + }; + }; + }; + + /* + * The timings below are just to demonstrate working cpuidle. + * These values may be inaccurate. + */ + idle-states { + entry-method = "psci"; + + CPU_SLEEP_0: cpu-sleep-0 { + compatible = "arm,idle-state"; + arm,psci-suspend-param = <0x0010000>; + local-timer-stop; + entry-latency-us = <300>; + exit-latency-us = <1200>; + min-residency-us = <2000>; + }; + CLUSTER_SLEEP_0: cluster-sleep-0 { + compatible = "arm,idle-state"; + arm,psci-suspend-param = <0x1010000>; + local-timer-stop; + entry-latency-us = <400>; + exit-latency-us = <1200>; + min-residency-us = <2500>; + }; + }; + + amus { + amu: amu-0 { + #address-cells = <1>; + #size-cells = <0>; + + mpmm_gear0: counter@0 { + reg = <0>; + enable-at-el3; + }; + + mpmm_gear1: counter@1 { + reg = <1>; + enable-at-el3; + }; + + mpmm_gear2: counter@2 { + reg = <2>; + enable-at-el3; + }; + }; + }; + + CPU0:cpu@0 { + device_type = "cpu"; + compatible = "arm,armv8"; + reg = <0x0>; + enable-method = "psci"; + clocks = <&scmi_dvfs 0>; + cpu-idle-states = <&CPU_SLEEP_0 &CLUSTER_SLEEP_0>; + capacity-dmips-mhz = <LIT_CAPACITY>; + amu = <&amu>; + supports-mpmm; + }; + + CPU1:cpu@100 { + device_type = "cpu"; + compatible = "arm,armv8"; + reg = <0x100>; + enable-method = "psci"; + clocks = <&scmi_dvfs 0>; + cpu-idle-states = <&CPU_SLEEP_0 &CLUSTER_SLEEP_0>; + capacity-dmips-mhz = <LIT_CAPACITY>; + amu = <&amu>; + supports-mpmm; + }; + + CPU2:cpu@200 { + device_type = "cpu"; + compatible = "arm,armv8"; + reg = <0x200>; + enable-method = "psci"; + cpu-idle-states = <&CPU_SLEEP_0 &CLUSTER_SLEEP_0>; + amu = <&amu>; + supports-mpmm; + }; + + CPU3:cpu@300 { + device_type = "cpu"; + compatible = "arm,armv8"; + reg = <0x300>; + enable-method = "psci"; + cpu-idle-states = <&CPU_SLEEP_0 &CLUSTER_SLEEP_0>; + amu = <&amu>; + supports-mpmm; + }; + + CPU4:cpu@400 { + device_type = "cpu"; + compatible = "arm,armv8"; + reg = <0x400>; + enable-method = "psci"; + clocks = <&scmi_dvfs 1>; + cpu-idle-states = <&CPU_SLEEP_0 &CLUSTER_SLEEP_0>; + capacity-dmips-mhz = <MID_CAPACITY>; + amu = <&amu>; + supports-mpmm; + }; + + CPU5:cpu@500 { + device_type = "cpu"; + compatible = "arm,armv8"; + reg = <0x500>; + enable-method = "psci"; + clocks = <&scmi_dvfs 1>; + cpu-idle-states = <&CPU_SLEEP_0 &CLUSTER_SLEEP_0>; + capacity-dmips-mhz = <MID_CAPACITY>; + amu = <&amu>; + supports-mpmm; + }; + + CPU6:cpu@600 { + device_type = "cpu"; + compatible = "arm,armv8"; + reg = <0x600>; + enable-method = "psci"; + cpu-idle-states = <&CPU_SLEEP_0 &CLUSTER_SLEEP_0>; + amu = <&amu>; + supports-mpmm; + }; + + CPU7:cpu@700 { + device_type = "cpu"; + compatible = "arm,armv8"; + reg = <0x700>; + enable-method = "psci"; + cpu-idle-states = <&CPU_SLEEP_0 &CLUSTER_SLEEP_0>; + amu = <&amu>; + supports-mpmm; + }; + }; + + reserved-memory { + #address-cells = <2>; + #size-cells = <2>; + ranges; + + linux,cma { + compatible = "shared-dma-pool"; + reusable; + size = <0x0 0x8000000>; + linux,cma-default; + }; + + optee { + compatible = "restricted-dma-pool"; + reg = <0x0 TC_NS_OPTEE_BASE 0x0 TC_NS_OPTEE_SIZE>; + }; + + }; + + memory { + device_type = "memory"; + reg = <0x0 TC_NS_DRAM1_BASE 0x0 TC_NS_DRAM1_SIZE>, + <HI(PLAT_ARM_DRAM2_BASE) LO(PLAT_ARM_DRAM2_BASE) + HI(TC_NS_DRAM2_SIZE) LO(TC_NS_DRAM2_SIZE)>; + }; + + psci { + compatible = "arm,psci-1.0", "arm,psci-0.2"; + method = "smc"; + }; + + cpu-pmu-little { + compatible = LIT_CPU_PMU_COMPATIBLE; + interrupts = <GIC_PPI 7 IRQ_TYPE_LEVEL_HIGH &ppi_partition_little>; + status = "okay"; + }; + + cpu-pmu-mid { + compatible = MID_CPU_PMU_COMPATIBLE; + interrupts = <GIC_PPI 7 IRQ_TYPE_LEVEL_HIGH &ppi_partition_mid>; + status = "okay"; + }; + + cpu-pmu-big { + compatible = BIG_CPU_PMU_COMPATIBLE; + interrupts = <GIC_PPI 7 IRQ_TYPE_LEVEL_HIGH &ppi_partition_big>; + status = "okay"; + }; + + sram: sram@6000000 { + compatible = "mmio-sram"; + reg = <0x0 PLAT_ARM_NSRAM_BASE 0x0 PLAT_ARM_NSRAM_SIZE>; + + #address-cells = <1>; + #size-cells = <1>; + ranges = <0 0x0 PLAT_ARM_NSRAM_BASE PLAT_ARM_NSRAM_SIZE>; + + cpu_scp_scmi_a2p: scp-shmem@0 { + compatible = "arm,scmi-shmem"; + reg = <0x0 0x80>; + }; + }; + + mbox_db_rx: mhu@MHU_RX_ADDR { + compatible = MHU_RX_COMPAT; + reg = <0x0 ADDRESSIFY(MHU_RX_ADDR) 0x0 MHU_OFFSET>; + clocks = <&soc_refclk>; + clock-names = "apb_pclk"; + #mbox-cells = <MHU_MBOX_CELLS>; + interrupts = <GIC_SPI MHU_RX_INT_NUM IRQ_TYPE_LEVEL_HIGH 0>; + interrupt-names = MHU_RX_INT_NAME; + }; + + mbox_db_tx: mhu@MHU_TX_ADDR { + compatible = MHU_TX_COMPAT; + reg = <0x0 ADDRESSIFY(MHU_TX_ADDR) 0x0 MHU_OFFSET>; + clocks = <&soc_refclk>; + clock-names = "apb_pclk"; + #mbox-cells = <MHU_MBOX_CELLS>; + interrupt-names = MHU_TX_INT_NAME; + }; + + firmware { + scmi { + compatible = "arm,scmi"; + mbox-names = "tx", "rx"; + #address-cells = <1>; + #size-cells = <0>; + +#if TC_SCMI_PD_CTRL_EN + scmi_devpd: protocol@11 { + reg = <0x11>; + #power-domain-cells = <1>; + }; +#endif /* TC_SCMI_PD_CTRL_EN */ + + scmi_dvfs: protocol@13 { + reg = <0x13>; + #clock-cells = <1>; + }; + + scmi_clk: protocol@14 { + reg = <0x14>; + #clock-cells = <1>; + }; + }; + }; + + gic: interrupt-controller@GIC_CTRL_ADDR { + compatible = "arm,gic-v3"; + #address-cells = <2>; + #interrupt-cells = <4>; + #size-cells = <2>; + ranges; + interrupt-controller; + reg = <0x0 0x30000000 0 0x10000>, /* GICD */ + <0x0 0x30080000 0 GIC_GICR_OFFSET>; /* GICR */ + interrupts = <GIC_PPI 0x9 IRQ_TYPE_LEVEL_LOW 0>; + }; + + timer { + compatible = "arm,armv8-timer"; + interrupts = <GIC_PPI 13 IRQ_TYPE_LEVEL_LOW 0>, + <GIC_PPI 14 IRQ_TYPE_LEVEL_LOW 0>, + <GIC_PPI 11 IRQ_TYPE_LEVEL_LOW 0>, + <GIC_PPI 10 IRQ_TYPE_LEVEL_LOW 0>; + }; + + spe-pmu-mid { + compatible = "arm,statistical-profiling-extension-v1"; + interrupts = <GIC_PPI 1 IRQ_TYPE_LEVEL_HIGH &ppi_partition_mid>; + status = "disabled"; + }; + + spe-pmu-big { + compatible = "arm,statistical-profiling-extension-v1"; + interrupts = <GIC_PPI 1 IRQ_TYPE_LEVEL_HIGH &ppi_partition_big>; + status = "disabled"; + }; + + soc_refclk: refclk { + compatible = "fixed-clock"; + #clock-cells = <0>; + clock-frequency = <1000000000>; + clock-output-names = "apb_pclk"; + }; + + soc_refclk60mhz: refclk60mhz { + compatible = "fixed-clock"; + #clock-cells = <0>; + clock-frequency = <60000000>; + clock-output-names = "iofpga_clk"; + }; + + soc_uartclk: uartclk { + compatible = "fixed-clock"; + #clock-cells = <0>; + clock-frequency = <UARTCLK_FREQ>; + clock-output-names = "uartclk"; + }; + + /* soc_uart0 on FPGA, ap_ns_uart on FVP */ + os_uart: serial@2a400000 { + compatible = "arm,pl011", "arm,primecell"; + reg = <0x0 0x2A400000 0x0 UART_OFFSET>; + interrupts = <GIC_SPI 63 IRQ_TYPE_LEVEL_HIGH 0>; + clocks = <&soc_uartclk>, <&soc_refclk>; + clock-names = "uartclk", "apb_pclk"; + status = "okay"; + }; + +#if !TC_DPU_USE_SCMI_CLK + dpu_aclk: dpu_aclk { + compatible = "fixed-clock"; + #clock-cells = <0>; + clock-frequency = <VENCODER_TIMING_CLK>; + clock-output-names = "fpga:dpu_aclk"; + }; + + dpu_pixel_clk: dpu-pixel-clk { + compatible = "fixed-clock"; + #clock-cells = <0>; + clock-frequency = <VENCODER_TIMING_CLK>; + clock-output-names = "pxclk"; + }; +#endif /* !TC_DPU_USE_SCMI_CLK */ + + vencoder { + compatible = "drm,virtual-encoder"; + port { + vencoder_in: endpoint { + remote-endpoint = <&dp_pl0_out0>; + }; + }; + + display-timings { + timing-panel { + VENCODER_TIMING; + }; + }; + + }; + + ethernet: ethernet@ETHERNET_ADDR { + reg = <0x0 ADDRESSIFY(ETHERNET_ADDR) 0x0 0x10000>; + interrupts = <GIC_SPI ETHERNET_INT IRQ_TYPE_LEVEL_HIGH 0>; + + reg-io-width = <2>; + smsc,irq-push-pull; + }; + + bp_clock24mhz: clock24mhz { + compatible = "fixed-clock"; + #clock-cells = <0>; + clock-frequency = <24000000>; + clock-output-names = "bp:clock24mhz"; + }; + + sysreg: sysreg@SYS_REGS_ADDR { + compatible = "arm,vexpress-sysreg"; + reg = <0x0 ADDRESSIFY(SYS_REGS_ADDR) 0x0 0x1000>; + gpio-controller; + #gpio-cells = <2>; + }; + + fixed_3v3: v2m-3v3 { + compatible = "regulator-fixed"; + regulator-name = "3V3"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + regulator-always-on; + }; + + mmci: mmci@MMC_ADDR { + compatible = "arm,pl180", "arm,primecell"; + reg = <0x0 ADDRESSIFY(MMC_ADDR) 0x0 0x1000>; + interrupts = <GIC_SPI MMC_INT_0 IRQ_TYPE_LEVEL_HIGH 0>, + <GIC_SPI MMC_INT_1 IRQ_TYPE_LEVEL_HIGH 0>; + wp-gpios = <&sysreg 1 0>; + bus-width = <4>; + max-frequency = <25000000>; + vmmc-supply = <&fixed_3v3>; + clocks = <&bp_clock24mhz>, <&bp_clock24mhz>; + clock-names = "mclk", "apb_pclk"; + }; + + gpu_clk: gpu_clk { + compatible = "fixed-clock"; + #clock-cells = <0>; + clock-frequency = <1000000000>; + }; + + gpu_core_clk: gpu_core_clk { + compatible = "fixed-clock"; + #clock-cells = <0>; + clock-frequency = <1000000000>; + }; + + gpu: gpu@2d000000 { + compatible = "arm,mali-midgard"; + reg = <0x0 0x2d000000 0x0 0x200000>; + clocks = <&gpu_core_clk>; + clock-names = "shadercores"; +#if TC_SCMI_PD_CTRL_EN + power-domains = <&scmi_devpd GPU_SCMI_PD_IDX>; + scmi-perf-domain = <3>; +#endif /* TC_SCMI_PD_CTRL_EN */ + + pbha { + int-id-override = <0 0x22>, <2 0x23>, <4 0x23>, <7 0x22>, + <8 0x22>, <9 0x22>, <10 0x22>, <11 0x22>, + <12 0x22>, <13 0x22>, <16 0x22>, <17 0x32>, + <18 0x32>, <19 0x22>, <20 0x22>, <21 0x32>, + <22 0x32>, <24 0x22>, <28 0x32>; + propagate-bits = <0x0f>; + }; + }; + + power_model_simple { + /* + * Numbers used are irrelevant to Titan, + * it helps suppressing the kernel warnings. + */ + compatible = "arm,mali-simple-power-model"; + static-coefficient = <2427750>; + dynamic-coefficient = <4687>; + ts = <20000 2000 (-20) 2>; + thermal-zone = ""; + }; + + smmu_600: smmu@2ce00000 { + compatible = "arm,smmu-v3"; + reg = <0 0x2ce00000 0 0x20000>; + interrupts = <GIC_SPI 75 IRQ_TYPE_EDGE_RISING 0>, + <GIC_SPI 74 IRQ_TYPE_EDGE_RISING 0>, + <GIC_SPI 76 IRQ_TYPE_EDGE_RISING 0>, + <GIC_SPI 77 IRQ_TYPE_EDGE_RISING 0>; + interrupt-names = "eventq", "priq", "cmdq-sync", "gerror"; + #iommu-cells = <1>; + status = "disabled"; + }; + + smmu_700: iommu@3f000000 { + #iommu-cells = <1>; + compatible = "arm,smmu-v3"; + reg = <0x0 0x3f000000 0x0 0x5000000>; + interrupts = <GIC_SPI 228 IRQ_TYPE_EDGE_RISING 0>, + <GIC_SPI 229 IRQ_TYPE_EDGE_RISING 0>, + <GIC_SPI 230 IRQ_TYPE_EDGE_RISING 0>; + interrupt-names = "eventq", "cmdq-sync", "gerror"; + dma-coherent; + status = "disabled"; + }; + + smmu_700_dpu: iommu@4002a00000 { + #iommu-cells = <1>; + compatible = "arm,smmu-v3"; + reg = <HI(0x4002a00000) LO(0x4002a00000) 0x0 0x5000000>; + interrupts = <GIC_SPI 481 IRQ_TYPE_EDGE_RISING 0>, + <GIC_SPI 482 IRQ_TYPE_EDGE_RISING 0>, + <GIC_SPI 483 IRQ_TYPE_EDGE_RISING 0>; + interrupt-names = "eventq", "cmdq-sync", "gerror"; + dma-coherent; + status = "disabled"; + }; + + dp0: display@DPU_ADDR { + #address-cells = <1>; + #size-cells = <0>; + compatible = "arm,mali-d71"; + reg = <HI(ADDRESSIFY(DPU_ADDR)) LO(ADDRESSIFY(DPU_ADDR)) 0 0x20000>; + interrupts = <GIC_SPI DPU_IRQ IRQ_TYPE_LEVEL_HIGH 0>; + interrupt-names = "DPU"; + DPU_CLK_ATTR1; + + pl0: pipeline@0 { + reg = <0>; + DPU_CLK_ATTR2; + pl_id = <0>; + ports { + #address-cells = <1>; + #size-cells = <0>; + port@0 { + reg = <0>; + dp_pl0_out0: endpoint { + remote-endpoint = <&vencoder_in>; + }; + }; + }; + }; + + pl1: pipeline@1 { + reg = <1>; + DPU_CLK_ATTR3; + pl_id = <1>; + ports { + #address-cells = <1>; + #size-cells = <0>; + port@0 { + reg = <0>; + }; + }; + }; + }; + + /* + * L3 cache in the DSU is the Memory System Component (MSC) + * The MPAM registers are accessed through utility bus in the DSU + */ + msc0 { + compatible = "arm,mpam-msc"; + reg = <MPAM_ADDR 0x0 0x2000>; + }; + + ete0 { + compatible = "arm,embedded-trace-extension"; + cpu = <&CPU0>; + }; + + ete1 { + compatible = "arm,embedded-trace-extension"; + cpu = <&CPU1>; + }; + + ete2 { + compatible = "arm,embedded-trace-extension"; + cpu = <&CPU2>; + }; + + ete3 { + compatible = "arm,embedded-trace-extension"; + cpu = <&CPU3>; + }; + + ete4 { + compatible = "arm,embedded-trace-extension"; + cpu = <&CPU4>; + }; + + ete5 { + compatible = "arm,embedded-trace-extension"; + cpu = <&CPU5>; + }; + + ete6 { + compatible = "arm,embedded-trace-extension"; + cpu = <&CPU6>; + }; + + ete7 { + compatible = "arm,embedded-trace-extension"; + cpu = <&CPU7>; + }; + + trbe { + compatible = "arm,trace-buffer-extension"; + interrupts = <GIC_PPI 2 IRQ_TYPE_LEVEL_LOW 0>; + }; + + trusty { + #size-cells = <0x02>; + #address-cells = <0x02>; + ranges = <0x00>; + compatible = "android,trusty-v1"; + + virtio { + compatible = "android,trusty-virtio-v1"; + }; + + test { + compatible = "android,trusty-test-v1"; + }; + + log { + compatible = "android,trusty-log-v1"; + }; + + irq { + ipi-range = <0x08 0x0f 0x08>; + interrupt-ranges = <0x00 0x0f 0x00 0x10 0x1f 0x01 0x20 0x3f 0x02>; + interrupt-templates = <0x01 0x00 0x8001 0x01 0x01 0x04 0x8001 0x01 0x00 0x04>; + compatible = "android,trusty-irq-v1"; + }; + }; + + /* used in U-boot, Linux doesn't care */ + arm_ffa { + compatible = "arm,ffa"; + method = "smc"; + }; +}; diff --git a/fdts/tc-common.dtsi b/fdts/tc-common.dtsi new file mode 100644 index 00000000..c3311930 --- /dev/null +++ b/fdts/tc-common.dtsi @@ -0,0 +1,9 @@ +/* + * Copyright (c) 2023-2024, Arm Limited. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#define PASTER(x, y) x ## y +#define EVALUATOR(x, y) PASTER(x, y) +#define ADDRESSIFY(addr) EVALUATOR(0x, addr) diff --git a/fdts/tc-fpga.dtsi b/fdts/tc-fpga.dtsi new file mode 100644 index 00000000..08b9ae56 --- /dev/null +++ b/fdts/tc-fpga.dtsi @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2023-2024, Arm Limited. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#define GIC_CTRL_ADDR 30000000 +#define GIC_GICR_OFFSET 0x1000000 +#define UART_OFFSET 0x10000 +/* 1440x3200@120 framebuffer */ +#define VENCODER_TIMING_CLK 836000000 +#define VENCODER_TIMING \ + clock-frequency = <VENCODER_TIMING_CLK>; \ + hactive = <1440>; \ + vactive = <3200>; \ + hfront-porch = <136>; \ + hback-porch = <296>; \ + hsync-len = <160>; \ + vfront-porch = <3>; \ + vback-porch = <217>; \ + vsync-len = <10> + +/ { + chosen { + stdout-path = "serial0:38400n8"; + }; + + ethernet: ethernet@ETHERNET_ADDR { + compatible = "smsc,lan9115"; + phy-mode = "mii"; + }; + + mmci: mmci@MMC_ADDR { + non-removable; + }; +}; diff --git a/fdts/tc-fvp.dtsi b/fdts/tc-fvp.dtsi new file mode 100644 index 00000000..f57e21da --- /dev/null +++ b/fdts/tc-fvp.dtsi @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2023-2024, Arm Limited. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#define GIC_CTRL_ADDR 2c010000 +#define GIC_GICR_OFFSET 0x200000 +#define UART_OFFSET 0x1000 + +#ifdef TC_RESOLUTION_1920X1080P60 + +#define VENCODER_TIMING_CLK 148500000 +#define VENCODER_TIMING \ + clock-frequency = <VENCODER_TIMING_CLK>; \ + hactive = <1920>; \ + vactive = <1080>; \ + hfront-porch = <88>; \ + hback-porch = <148>; \ + hsync-len = <44>; \ + vfront-porch = <4>; \ + vback-porch = <36>; \ + vsync-len = <5> + +#else /* TC_RESOLUTION_640X480P60 */ + +#define VENCODER_TIMING_CLK 25175000 +#define VENCODER_TIMING \ + clock-frequency = <VENCODER_TIMING_CLK>; \ + hactive = <640>; \ + vactive = <480>; \ + hfront-porch = <16>; \ + hback-porch = <48>; \ + hsync-len = <96>; \ + vfront-porch = <10>; \ + vback-porch = <33>; \ + vsync-len = <2> + +#endif + +/ { + chosen { + stdout-path = "serial0:115200n8"; + }; + + ethernet: ethernet@ETHERNET_ADDR { + compatible = "smsc,lan91c111"; + }; + + mmci: mmci@MMC_ADDR { + cd-gpios = <&sysreg 0 0>; + }; + + rtc@RTC_ADDR { + compatible = "arm,pl031", "arm,primecell"; + reg = <0x0 ADDRESSIFY(RTC_ADDR) 0x0 0x1000>; + interrupts = <GIC_SPI RTC_INT IRQ_TYPE_LEVEL_HIGH 0>; + clocks = <&soc_refclk>; + clock-names = "apb_pclk"; + }; + + kmi@KMI_0_ADDR { + compatible = "arm,pl050", "arm,primecell"; + reg = <0x0 ADDRESSIFY(KMI_0_ADDR) 0x0 0x1000>; + interrupts = <GIC_SPI KMI_0_INT IRQ_TYPE_LEVEL_HIGH 0>; + clocks = <&bp_clock24mhz>, <&bp_clock24mhz>; + clock-names = "KMIREFCLK", "apb_pclk"; + }; + + kmi@1c070000 { + compatible = "arm,pl050", "arm,primecell"; + reg = <0x0 0x001c070000 0x0 0x1000>; + interrupts = <GIC_SPI 103 IRQ_TYPE_LEVEL_HIGH 0>; + clocks = <&bp_clock24mhz>, <&bp_clock24mhz>; + clock-names = "KMIREFCLK", "apb_pclk"; + }; + + virtio_block@VIRTIO_BLOCK_ADDR { + compatible = "virtio,mmio"; + reg = <0x0 ADDRESSIFY(VIRTIO_BLOCK_ADDR) 0x0 0x200>; + /* spec lists this wrong */ + interrupts = <GIC_SPI VIRTIO_BLOCK_INT IRQ_TYPE_LEVEL_HIGH 0>; + }; +}; diff --git a/fdts/tc.dts b/fdts/tc.dts deleted file mode 100644 index 4f275895..00000000 --- a/fdts/tc.dts +++ /dev/null @@ -1,599 +0,0 @@ -/* - * Copyright (c) 2020-2023, Arm Limited. All rights reserved. - * - * SPDX-License-Identifier: BSD-3-Clause - */ - -/dts-v1/; - -/ { - compatible = "arm,tc"; - interrupt-parent = <&gic>; - #address-cells = <2>; - #size-cells = <2>; - - aliases { - serial0 = &soc_uart0; - }; - - chosen { - stdout-path = "serial0:115200n8"; - }; - - cpus { - #address-cells = <1>; - #size-cells = <0>; - - cpu-map { - cluster0 { - core0 { - cpu = <&CPU0>; - }; - core1 { - cpu = <&CPU1>; - }; - core2 { - cpu = <&CPU2>; - }; - core3 { - cpu = <&CPU3>; - }; - core4 { - cpu = <&CPU4>; - }; - core5 { - cpu = <&CPU5>; - }; - core6 { - cpu = <&CPU6>; - }; - core7 { - cpu = <&CPU7>; - }; - }; - }; - - /* - * The timings below are just to demonstrate working cpuidle. - * These values may be inaccurate. - */ - idle-states { - entry-method = "arm,psci"; - - CPU_SLEEP_0: cpu-sleep-0 { - compatible = "arm,idle-state"; - arm,psci-suspend-param = <0x0010000>; - local-timer-stop; - entry-latency-us = <300>; - exit-latency-us = <1200>; - min-residency-us = <2000>; - }; - CLUSTER_SLEEP_0: cluster-sleep-0 { - compatible = "arm,idle-state"; - arm,psci-suspend-param = <0x1010000>; - local-timer-stop; - entry-latency-us = <400>; - exit-latency-us = <1200>; - min-residency-us = <2500>; - }; - }; - - amus { - amu: amu-0 { - #address-cells = <1>; - #size-cells = <0>; - - mpmm_gear0: counter@0 { - reg = <0>; - - enable-at-el3; - }; - - mpmm_gear1: counter@1 { - reg = <1>; - - enable-at-el3; - }; - - mpmm_gear2: counter@2 { - reg = <2>; - - enable-at-el3; - }; - }; - }; - - CPU0:cpu@0 { - device_type = "cpu"; - compatible = "arm,armv8"; - reg = <0x0>; - enable-method = "psci"; - clocks = <&scmi_dvfs 0>; - cpu-idle-states = <&CPU_SLEEP_0 &CLUSTER_SLEEP_0>; - capacity-dmips-mhz = <406>; - amu = <&amu>; - supports-mpmm; - }; - - CPU1:cpu@100 { - device_type = "cpu"; - compatible = "arm,armv8"; - reg = <0x100>; - enable-method = "psci"; - clocks = <&scmi_dvfs 0>; - cpu-idle-states = <&CPU_SLEEP_0 &CLUSTER_SLEEP_0>; - capacity-dmips-mhz = <406>; - amu = <&amu>; - supports-mpmm; - }; - - CPU2:cpu@200 { - device_type = "cpu"; - compatible = "arm,armv8"; - reg = <0x200>; - enable-method = "psci"; - clocks = <&scmi_dvfs 0>; - cpu-idle-states = <&CPU_SLEEP_0 &CLUSTER_SLEEP_0>; - capacity-dmips-mhz = <406>; - amu = <&amu>; - supports-mpmm; - }; - - CPU3:cpu@300 { - device_type = "cpu"; - compatible = "arm,armv8"; - reg = <0x300>; - enable-method = "psci"; - clocks = <&scmi_dvfs 0>; - cpu-idle-states = <&CPU_SLEEP_0 &CLUSTER_SLEEP_0>; - capacity-dmips-mhz = <406>; - amu = <&amu>; - supports-mpmm; - }; - - CPU4:cpu@400 { - device_type = "cpu"; - compatible = "arm,armv8"; - reg = <0x400>; - enable-method = "psci"; - clocks = <&scmi_dvfs 1>; - cpu-idle-states = <&CPU_SLEEP_0 &CLUSTER_SLEEP_0>; - capacity-dmips-mhz = <912>; - amu = <&amu>; - supports-mpmm; - }; - - CPU5:cpu@500 { - device_type = "cpu"; - compatible = "arm,armv8"; - reg = <0x500>; - enable-method = "psci"; - clocks = <&scmi_dvfs 1>; - cpu-idle-states = <&CPU_SLEEP_0 &CLUSTER_SLEEP_0>; - capacity-dmips-mhz = <912>; - amu = <&amu>; - supports-mpmm; - }; - - CPU6:cpu@600 { - device_type = "cpu"; - compatible = "arm,armv8"; - reg = <0x600>; - enable-method = "psci"; - clocks = <&scmi_dvfs 1>; - cpu-idle-states = <&CPU_SLEEP_0 &CLUSTER_SLEEP_0>; - capacity-dmips-mhz = <912>; - amu = <&amu>; - supports-mpmm; - }; - - CPU7:cpu@700 { - device_type = "cpu"; - compatible = "arm,armv8"; - reg = <0x700>; - enable-method = "psci"; - clocks = <&scmi_dvfs 2>; - cpu-idle-states = <&CPU_SLEEP_0 &CLUSTER_SLEEP_0>; - capacity-dmips-mhz = <1024>; - amu = <&amu>; - supports-mpmm; - }; - - }; - - reserved-memory { - #address-cells = <2>; - #size-cells = <2>; - ranges; - - linux,cma { - compatible = "shared-dma-pool"; - reusable; - size = <0x0 0x8000000>; - linux,cma-default; - }; - - optee@0xf8e00000 { - compatible = "restricted-dma-pool"; - reg = <0x00000000 0xf8e00000 0 0x00200000>; - }; - }; - - psci { - compatible = "arm,psci-1.0", "arm,psci-0.2"; - method = "smc"; - }; - - sram: sram@6000000 { - compatible = "mmio-sram"; - reg = <0x0 0x06000000 0x0 0x8000>; - - #address-cells = <1>; - #size-cells = <1>; - ranges = <0 0x0 0x06000000 0x8000>; - - cpu_scp_scmi_mem: scp-shmem@0 { - compatible = "arm,scmi-shmem"; - reg = <0x0 0x80>; - }; - }; - - mbox_db_rx: mhu@45010000 { - compatible = "arm,mhuv2-rx","arm,primecell"; - reg = <0x0 0x45010000 0x0 0x1000>; - clocks = <&soc_refclk100mhz>; - clock-names = "apb_pclk"; - #mbox-cells = <2>; - interrupts = <0 317 4>; - interrupt-names = "mhu_rx"; - mhu-protocol = "doorbell"; - arm,mhuv2-protocols = <0 1>; - }; - - mbox_db_tx: mhu@45000000 { - compatible = "arm,mhuv2-tx","arm,primecell"; - reg = <0x0 0x45000000 0x0 0x1000>; - clocks = <&soc_refclk100mhz>; - clock-names = "apb_pclk"; - #mbox-cells = <2>; - interrupt-names = "mhu_tx"; - mhu-protocol = "doorbell"; - arm,mhuv2-protocols = <0 1>; - }; - - cmn-pmu { - compatible = "arm,ci-700"; - reg = <0x0 0x50000000 0x0 0x10000000>; - interrupts = <0x0 460 0x4>; - }; - - scmi { - compatible = "arm,scmi"; - mbox-names = "tx", "rx"; - mboxes = <&mbox_db_tx 0 0 &mbox_db_rx 0 0 >; - shmem = <&cpu_scp_scmi_mem &cpu_scp_scmi_mem>; - #address-cells = <1>; - #size-cells = <0>; - - scmi_dvfs: protocol@13 { - reg = <0x13>; - #clock-cells = <1>; - }; - - scmi_clk: protocol@14 { - reg = <0x14>; - #clock-cells = <1>; - }; - }; - - gic: interrupt-controller@2c010000 { - compatible = "arm,gic-600", "arm,gic-v3"; - #address-cells = <2>; - #interrupt-cells = <3>; - #size-cells = <2>; - ranges; - interrupt-controller; - reg = <0x0 0x30000000 0 0x10000>, /* GICD */ - <0x0 0x30080000 0 0x200000>; /* GICR */ - interrupts = <0x1 0x9 0x4>; - }; - - timer { - compatible = "arm,armv8-timer"; - interrupts = <0x1 13 0x8>, - <0x1 14 0x8>, - <0x1 11 0x8>, - <0x1 10 0x8>; - }; - - soc_refclk100mhz: refclk100mhz { - compatible = "fixed-clock"; - #clock-cells = <0>; - clock-frequency = <100000000>; - clock-output-names = "apb_pclk"; - }; - - soc_refclk60mhz: refclk60mhz { - compatible = "fixed-clock"; - #clock-cells = <0>; - clock-frequency = <60000000>; - clock-output-names = "iofpga_clk"; - }; - - soc_uartclk: uartclk { - compatible = "fixed-clock"; - #clock-cells = <0>; - clock-frequency = <50000000>; - clock-output-names = "uartclk"; - }; - - soc_uart0: uart@7ff80000 { - compatible = "arm,pl011", "arm,primecell"; - reg = <0x0 0x7ff80000 0x0 0x1000>; - interrupts = <0x0 116 0x4>; - clocks = <&soc_uartclk>, <&soc_refclk100mhz>; - clock-names = "uartclk", "apb_pclk"; - status = "okay"; - }; - - rtc0: rtc@1C170000 { - compatible = "arm,pl031", "arm,primecell"; - reg = <0x0 0x1C170000 0x0 0x1000>; - interrupts = <0x0 100 0x4>; - clocks = <&soc_refclk100mhz>; - clock-names = "apb_pclk"; - wakeup-source; - }; - - vencoder { - compatible = "drm,virtual-encoder"; - - port { - vencoder_in: endpoint { - remote-endpoint = <&dp_pl0_out0>; - }; - }; - - display-timings { - panel-timing { - clock-frequency = <25175000>; - hactive = <640>; - vactive = <480>; - hfront-porch = <16>; - hback-porch = <48>; - hsync-len = <96>; - vfront-porch = <10>; - vback-porch = <33>; - vsync-len = <2>; - }; - }; - - }; - - hdlcd: hdlcd@7ff60000 { - compatible = "arm,hdlcd"; - reg = <0x0 0x7ff60000 0x0 0x1000>; - interrupts = <0x0 117 0x4>; - clocks = <&fake_hdlcd_clk>; - clock-names = "pxlclk"; - status = "disabled"; - - port { - hdlcd_out: endpoint { - remote-endpoint = <&vencoder_in>; - }; - }; - }; - - fake_hdlcd_clk: fake-hdlcd-clk { - compatible = "fixed-clock"; - #clock-cells = <0>; - clock-frequency = <25175000>; - clock-output-names = "pxlclk"; - }; - - ethernet@18000000 { - compatible = "smsc,lan91c111"; - reg = <0x0 0x18000000 0x0 0x10000>; - interrupts = <0 109 4>; - }; - - kmi@1c060000 { - compatible = "arm,pl050", "arm,primecell"; - reg = <0x0 0x001c060000 0x0 0x1000>; - interrupts = <0 197 4>; - clocks = <&bp_clock24mhz>, <&bp_clock24mhz>; - clock-names = "KMIREFCLK", "apb_pclk"; - }; - - kmi@1c070000 { - compatible = "arm,pl050", "arm,primecell"; - reg = <0x0 0x001c070000 0x0 0x1000>; - interrupts = <0 103 4>; - clocks = <&bp_clock24mhz>, <&bp_clock24mhz>; - clock-names = "KMIREFCLK", "apb_pclk"; - }; - - bp_clock24mhz: clock24mhz { - compatible = "fixed-clock"; - #clock-cells = <0>; - clock-frequency = <24000000>; - clock-output-names = "bp:clock24mhz"; - }; - - virtio_block@1c130000 { - compatible = "virtio,mmio"; - reg = <0x0 0x1c130000 0x0 0x200>; - interrupts = <0 204 4>; - }; - - sysreg: sysreg@1c010000 { - compatible = "arm,vexpress-sysreg"; - reg = <0x0 0x001c010000 0x0 0x1000>; - gpio-controller; - #gpio-cells = <2>; - }; - - fixed_3v3: v2m-3v3 { - compatible = "regulator-fixed"; - regulator-name = "3V3"; - regulator-min-microvolt = <3300000>; - regulator-max-microvolt = <3300000>; - regulator-always-on; - }; - - mmci@1c050000 { - compatible = "arm,pl180", "arm,primecell"; - reg = <0x0 0x001c050000 0x0 0x1000>; - interrupts = <0 107 0x4>, - <0 108 0x4>; - cd-gpios = <&sysreg 0 0>; - wp-gpios = <&sysreg 1 0>; - bus-width = <8>; - max-frequency = <12000000>; - vmmc-supply = <&fixed_3v3>; - clocks = <&bp_clock24mhz>, <&bp_clock24mhz>; - clock-names = "mclk", "apb_pclk"; - }; - - gpu_clk: gpu_clk { - compatible = "fixed-clock"; - #clock-cells = <0>; - clock-frequency = <1000000000>; - }; - - gpu_core_clk: gpu_core_clk { - compatible = "fixed-clock"; - #clock-cells = <0>; - clock-frequency = <1000000000>; - }; - - gpu: gpu@2d000000 { - compatible = "arm,mali-midgard"; - reg = <0x0 0x2d000000 0x0 0x200000>; - interrupts = <0 66 4>, <0 67 4>, <0 65 4>; - interrupt-names = "JOB", "MMU", "GPU"; - clocks = <&gpu_clk>, <&gpu_core_clk>; - clock-names = "clk_mali", "shadercores"; - iommus = <&smmu_700 0x200>; - operating-points = < - /* KHz uV */ - 50000 820000 - >; - }; - - power_model@simple { - /* - * Numbers used are irrelevant to Titan, - * it helps suppressing the kernel warnings. - */ - compatible = "arm,mali-simple-power-model"; - static-coefficient = <2427750>; - dynamic-coefficient = <4687>; - ts = <20000 2000 (-20) 2>; - thermal-zone = ""; - }; - - smmu_700: smmu_700@3f000000 { - #iommu-cells = <1>; - compatible = "arm,smmu-v3"; - reg = <0x0 0x3f000000 0x0 0x5000000>; - dma-coherent; - }; - - dp0: display@2cc00000 { - #address-cells = <1>; - #size-cells = <0>; - compatible = "arm,mali-d71"; - reg = <0 0x2cc00000 0 0x20000>; - interrupts = <0 69 4>; - interrupt-names = "DPU"; - clocks = <&scmi_clk 0>; - clock-names = "aclk"; - iommus = <&smmu_700 0x100>; - pl0: pipeline@0 { - reg = <0>; - clocks = <&scmi_clk 1>; - clock-names = "pxclk"; - pl_id = <0>; - ports { - #address-cells = <1>; - #size-cells = <0>; - port@0 { - reg = <0>; - dp_pl0_out0: endpoint { - remote-endpoint = <&vencoder_in>; - }; - }; - }; - }; - - pl1: pipeline@1 { - reg = <1>; - clocks = <&scmi_clk 2>; - clock-names = "pxclk"; - pl_id = <1>; - ports { - #address-cells = <1>; - #size-cells = <0>; - port@0 { - reg = <0>; - }; - }; - }; - }; - - /* - * L3 cache in the DSU is the Memory System Component (MSC) - * The MPAM registers are accessed through utility bus in the DSU - */ - msc0 { - compatible = "arm,mpam-msc"; - reg = <0x1 0x00010000 0x0 0x2000>; - }; - - ete0 { - compatible = "arm,embedded-trace-extension"; - cpu = <&CPU0>; - }; - - ete1 { - compatible = "arm,embedded-trace-extension"; - cpu = <&CPU1>; - }; - - ete2 { - compatible = "arm,embedded-trace-extension"; - cpu = <&CPU2>; - }; - - ete3 { - compatible = "arm,embedded-trace-extension"; - cpu = <&CPU3>; - }; - - ete4 { - compatible = "arm,embedded-trace-extension"; - cpu = <&CPU4>; - }; - - ete5 { - compatible = "arm,embedded-trace-extension"; - cpu = <&CPU5>; - }; - - ete6 { - compatible = "arm,embedded-trace-extension"; - cpu = <&CPU6>; - }; - - ete7 { - compatible = "arm,embedded-trace-extension"; - cpu = <&CPU7>; - }; - - trbe0 { - compatible = "arm,trace-buffer-extension"; - interrupts = <1 2 4>; - }; -}; diff --git a/fdts/tc2.dts b/fdts/tc2.dts new file mode 100644 index 00000000..c4922749 --- /dev/null +++ b/fdts/tc2.dts @@ -0,0 +1,300 @@ +/* + * Copyright (c) 2020-2024, Arm Limited. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ +/dts-v1/; + +#include <dt-bindings/interrupt-controller/arm-gic.h> +#include <dt-bindings/interrupt-controller/irq.h> +#include <platform_def.h> + +#if TARGET_FLAVOUR_FVP +#define LIT_CAPACITY 406 +#define MID_CAPACITY 912 +#else /* TARGET_FLAVOUR_FPGA */ +#define LIT_CAPACITY 280 +#define MID_CAPACITY 775 +/* this is an area optimized configuration of the big core */ +#define BIG2_CAPACITY 930 +#endif /* TARGET_FLAVOUR_FPGA */ +#define BIG_CAPACITY 1024 + +#define MHU_TX_ADDR 45000000 /* hex */ +#define MHU_TX_COMPAT "arm,mhuv2-tx","arm,primecell" +#define MHU_TX_INT_NAME "mhu_tx" + +#define MHU_RX_ADDR 45010000 /* hex */ +#define MHU_RX_COMPAT "arm,mhuv2-rx","arm,primecell" +#define MHU_OFFSET 0x1000 +#define MHU_MBOX_CELLS 2 +#define MHU_RX_INT_NUM 317 +#define MHU_RX_INT_NAME "mhu_rx" + +#define LIT_CPU_PMU_COMPATIBLE "arm,cortex-a520-pmu" +#define MID_CPU_PMU_COMPATIBLE "arm,cortex-a720-pmu" +#define BIG_CPU_PMU_COMPATIBLE "arm,cortex-x4-pmu" + +#define MPAM_ADDR 0x1 0x00010000 /* 0x1_0001_0000 */ +#define UARTCLK_FREQ 5000000 + +#define DPU_ADDR 2cc00000 +#define DPU_IRQ 69 + +#define ETHERNET_ADDR 18000000 +#define ETHERNET_INT 109 + +#define SYS_REGS_ADDR 1c010000 + +#define MMC_ADDR 1c050000 +#define MMC_INT_0 107 +#define MMC_INT_1 108 + +#define RTC_ADDR 1c170000 +#define RTC_INT 100 + +#define KMI_0_ADDR 1c060000 +#define KMI_0_INT 197 +#define KMI_1_ADDR 1c070000 +#define KMI_1_INT 103 + +#define VIRTIO_BLOCK_ADDR 1c130000 +#define VIRTIO_BLOCK_INT 204 + +#include "tc-common.dtsi" +#if TARGET_FLAVOUR_FVP +#include "tc-fvp.dtsi" +#else +#include "tc-fpga.dtsi" +#endif /* TARGET_FLAVOUR_FVP */ +#include "tc-base.dtsi" + +/ { + cpus { +#if TARGET_FLAVOUR_FPGA + cpu-map { + cluster0 { + core8 { + cpu = <&CPU8>; + }; + core9 { + cpu = <&CPU9>; + }; + core10 { + cpu = <&CPU10>; + }; + core11 { + cpu = <&CPU11>; + }; + core12 { + cpu = <&CPU12>; + }; + core13 { + cpu = <&CPU13>; + }; + }; + }; +#endif + + CPU2:cpu@200 { + clocks = <&scmi_dvfs 0>; + capacity-dmips-mhz = <LIT_CAPACITY>; + }; + + CPU3:cpu@300 { + clocks = <&scmi_dvfs 0>; + capacity-dmips-mhz = <LIT_CAPACITY>; + }; + + CPU6:cpu@600 { + clocks = <&scmi_dvfs 1>; + capacity-dmips-mhz = <MID_CAPACITY>; + }; + + CPU7:cpu@700 { + clocks = <&scmi_dvfs 1>; + capacity-dmips-mhz = <MID_CAPACITY>; + }; + +#if TARGET_FLAVOUR_FPGA + CPU8:cpu@800 { + device_type = "cpu"; + compatible = "arm,armv8"; + reg = <0x800>; + enable-method = "psci"; + clocks = <&scmi_dvfs 1>; + capacity-dmips-mhz = <MID_CAPACITY>; + amu = <&amu>; + supports-mpmm; + }; + + CPU9:cpu@900 { + device_type = "cpu"; + compatible = "arm,armv8"; + reg = <0x900>; + enable-method = "psci"; + clocks = <&scmi_dvfs 2>; + capacity-dmips-mhz = <BIG2_CAPACITY>; + amu = <&amu>; + supports-mpmm; + }; + + CPU10:cpu@A00 { + device_type = "cpu"; + compatible = "arm,armv8"; + reg = <0xA00>; + enable-method = "psci"; + clocks = <&scmi_dvfs 2>; + capacity-dmips-mhz = <BIG2_CAPACITY>; + amu = <&amu>; + supports-mpmm; + }; + + CPU11:cpu@B00 { + device_type = "cpu"; + compatible = "arm,armv8"; + reg = <0xB00>; + enable-method = "psci"; + clocks = <&scmi_dvfs 2>; + capacity-dmips-mhz = <BIG2_CAPACITY>; + amu = <&amu>; + supports-mpmm; + }; + + CPU12:cpu@C00 { + device_type = "cpu"; + compatible = "arm,armv8"; + reg = <0xC00>; + enable-method = "psci"; + clocks = <&scmi_dvfs 3>; + capacity-dmips-mhz = <BIG_CAPACITY>; + amu = <&amu>; + supports-mpmm; + }; + + CPU13:cpu@D00 { + device_type = "cpu"; + compatible = "arm,armv8"; + reg = <0xD00>; + enable-method = "psci"; + clocks = <&scmi_dvfs 3>; + capacity-dmips-mhz = <BIG_CAPACITY>; + amu = <&amu>; + supports-mpmm; + }; +#endif + }; + +#if TARGET_FLAVOUR_FPGA + ete8 { + compatible = "arm,embedded-trace-extension"; + cpu = <&CPU8>; + }; + + ete9 { + compatible = "arm,embedded-trace-extension"; + cpu = <&CPU9>; + }; + + ete10 { + compatible = "arm,embedded-trace-extension"; + cpu = <&CPU10>; + }; + + ete11 { + compatible = "arm,embedded-trace-extension"; + cpu = <&CPU11>; + }; + + ete12 { + compatible = "arm,embedded-trace-extension"; + cpu = <&CPU12>; + }; + + ete13 { + compatible = "arm,embedded-trace-extension"; + cpu = <&CPU13>; + }; +#endif /* TARGET_FLAVOUR_FPGA */ + + cmn-pmu { + compatible = "arm,ci-700"; + reg = <0x0 0x50000000 0x0 0x10000000>; + interrupts = <GIC_SPI 460 IRQ_TYPE_LEVEL_HIGH 0>; + }; + + mbox_db_rx: mhu@MHU_RX_ADDR { + arm,mhuv2-protocols = <0 1>; + }; + + mbox_db_tx: mhu@MHU_TX_ADDR { + arm,mhuv2-protocols = <0 1>; + }; + + firmware { + /* + * TC2 does not have a P2A channel, but wiring one was needed to make Linux work + * (by chance). At the time the SCMI driver did not support bidirectional + * mailboxes so as a workaround, the A2P channel was wired for TX communication + * and the synchronous replies would be read asyncrhonously as if coming from + * the P2A channel, while being the actual A2P channel. + * + * This will not work with kernels > 5.15, but keep it around to keep TC2 + * working with its target kernel. Newer kernels will still work, but SCMI + * won't as they check that the two regions are distinct. + */ + scmi { + mboxes = <&mbox_db_tx 0 0 &mbox_db_rx 0 0>; + shmem = <&cpu_scp_scmi_a2p &cpu_scp_scmi_a2p>; + }; + }; + + gic: interrupt-controller@GIC_CTRL_ADDR { + ppi-partitions { + ppi_partition_little: interrupt-partition-0 { + affinity = <&CPU0>, <&CPU1>, <&CPU2>, <&CPU3>; + }; + +#if TARGET_FLAVOUR_FVP + ppi_partition_mid: interrupt-partition-1 { + affinity = <&CPU4>, <&CPU5>, <&CPU6>; + }; + + ppi_partition_big: interrupt-partition-2 { + affinity = <&CPU7>; + }; +#elif TARGET_FLAVOUR_FPGA + ppi_partition_mid: interrupt-partition-1 { + affinity = <&CPU4>, <&CPU5>, <&CPU6>, <&CPU7>, <&CPU8>; + }; + + ppi_partition_big: interrupt-partition-2 { + affinity = <&CPU9>, <&CPU10>, <&CPU11>, <&CPU12>, <&CPU13>; + }; +#endif + }; + }; + + spe-pmu-big { + status = "okay"; + }; + + smmu_700: iommu@3f000000 { + status = "okay"; + }; + + dp0: display@DPU_ADDR { +#if TC_SCMI_PD_CTRL_EN + power-domains = <&scmi_devpd (PLAT_MAX_CPUS_PER_CLUSTER + 2)>; +#endif + iommus = <&smmu_700 0x100>; + }; + + gpu: gpu@2d000000 { + interrupts = <GIC_SPI 66 IRQ_TYPE_LEVEL_HIGH 0>, + <GIC_SPI 67 IRQ_TYPE_LEVEL_HIGH 0>, + <GIC_SPI 65 IRQ_TYPE_LEVEL_HIGH 0>; + interrupt-names = "JOB", "MMU", "GPU"; + iommus = <&smmu_700 0x200>; + }; +}; diff --git a/fdts/tc3-4-base.dtsi b/fdts/tc3-4-base.dtsi new file mode 100644 index 00000000..2de5fd3a --- /dev/null +++ b/fdts/tc3-4-base.dtsi @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2020-2024, Arm Limited. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#define LIT_CAPACITY 239 +#define MID_CAPACITY 686 +#define BIG_CAPACITY 1024 + +#define MHU_TX_COMPAT "arm,mhuv3" +#define MHU_TX_INT_NAME "" + +#define MHU_RX_COMPAT "arm,mhuv3" +#define MHU_OFFSET 0x10000 +#define MHU_MBOX_CELLS 3 +#define MHU_RX_INT_NUM 300 +#define MHU_RX_INT_NAME "combined" + +#define MPAM_ADDR 0x0 0x5f010000 /* 0x5f01_0000 */ +#define UARTCLK_FREQ 3750000 + +#if TARGET_FLAVOUR_FVP +#define DPU_ADDR 4000000000 +#define DPU_IRQ 579 +#elif TARGET_FLAVOUR_FPGA +#define DPU_ADDR 2cc00000 +#define DPU_IRQ 69 +#endif +#include "tc-base.dtsi" + +/ { + cpus { + CPU2:cpu@200 { + clocks = <&scmi_dvfs 1>; + capacity-dmips-mhz = <MID_CAPACITY>; + }; + + CPU3:cpu@300 { + clocks = <&scmi_dvfs 1>; + capacity-dmips-mhz = <MID_CAPACITY>; + }; + + CPU6:cpu@600 { + clocks = <&scmi_dvfs 2>; + capacity-dmips-mhz = <BIG_CAPACITY>; + }; + + CPU7:cpu@700 { + clocks = <&scmi_dvfs 2>; + capacity-dmips-mhz = <BIG_CAPACITY>; + }; + }; + + gic: interrupt-controller@GIC_CTRL_ADDR { + ppi-partitions { + ppi_partition_little: interrupt-partition-0 { + affinity = <&CPU0>, <&CPU1>; + }; + + ppi_partition_mid: interrupt-partition-1 { + affinity = <&CPU2>, <&CPU3>, <&CPU4>, <&CPU5>; + }; + + ppi_partition_big: interrupt-partition-2 { + affinity = <&CPU6>, <&CPU7>; + }; + }; + }; + + sram: sram@6000000 { + cpu_scp_scmi_p2a: scp-shmem@80 { + compatible = "arm,scmi-shmem"; + reg = <0x80 0x80>; + }; + }; + + firmware { + scmi { + mboxes = <&mbox_db_tx 0 0 0 &mbox_db_rx 0 0 0 &mbox_db_rx 0 0 1>; + shmem = <&cpu_scp_scmi_a2p &cpu_scp_scmi_p2a>; + }; + }; +}; diff --git a/fdts/tc3.dts b/fdts/tc3.dts new file mode 100644 index 00000000..ffe3b6d9 --- /dev/null +++ b/fdts/tc3.dts @@ -0,0 +1,121 @@ +/* + * Copyright (c) 2020-2024, Arm Limited. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +/dts-v1/; + +#include <dt-bindings/interrupt-controller/arm-gic.h> +#include <dt-bindings/interrupt-controller/irq.h> +#include <platform_def.h> + +#define MHU_TX_ADDR 46040000 /* hex */ +#define MHU_RX_ADDR 46140000 /* hex */ + +#define LIT_CPU_PMU_COMPATIBLE "arm,cortex-a520-pmu" +#define MID_CPU_PMU_COMPATIBLE "arm,cortex-a725-pmu" +#define BIG_CPU_PMU_COMPATIBLE "arm,cortex-x925-pmu" + +#define ETHERNET_ADDR 18000000 +#define ETHERNET_INT 109 + +#define SYS_REGS_ADDR 1c010000 + +#define MMC_ADDR 1c050000 +#define MMC_INT_0 107 +#define MMC_INT_1 108 + +#define RTC_ADDR 1c170000 +#define RTC_INT 100 + +#define KMI_0_ADDR 1c060000 +#define KMI_0_INT 197 +#define KMI_1_ADDR 1c070000 +#define KMI_1_INT 103 + +#define VIRTIO_BLOCK_ADDR 1c130000 +#define VIRTIO_BLOCK_INT 204 + +#include "tc-common.dtsi" +#if TARGET_FLAVOUR_FVP +#include "tc-fvp.dtsi" +#else +#include "tc-fpga.dtsi" +#endif /* TARGET_FLAVOUR_FVP */ +#include "tc3-4-base.dtsi" + +/ { + cs-pmu@0 { + compatible = "arm,coresight-pmu"; + reg = <0x0 MCN_PMU_ADDR(0) 0x0 0xffc>; + }; + + cs-pmu@1 { + compatible = "arm,coresight-pmu"; + reg = <0x0 MCN_PMU_ADDR(1) 0x0 0xffc>; + }; + + cs-pmu@2 { + compatible = "arm,coresight-pmu"; + reg = <0x0 MCN_PMU_ADDR(2) 0x0 0xffc>; + }; + + cs-pmu@3 { + compatible = "arm,coresight-pmu"; + reg = <0x0 MCN_PMU_ADDR(3) 0x0 0xffc>; + }; + + spe-pmu-mid { + status = "okay"; + }; + + spe-pmu-big { + status = "okay"; + }; + + dsu-pmu { + compatible = "arm,dsu-pmu"; + cpus = <&CPU0>, <&CPU1>, <&CPU2>, <&CPU3>, <&CPU4>, <&CPU5>, <&CPU6>, <&CPU7>; + }; + + ni-pmu { + compatible = "arm,ni-tower"; + reg = <0x0 0x4f000000 0x0 0x4000000>; + }; + +#if TARGET_FLAVOUR_FVP + smmu_700: iommu@3f000000 { + status = "okay"; + }; + + smmu_700_dpu: iommu@4002a00000 { + status = "okay"; + }; +#else + smmu_600: smmu@2ce00000 { + status = "okay"; + }; +#endif + + dp0: display@DPU_ADDR { +#if TARGET_FLAVOUR_FVP + iommus = <&smmu_700_dpu 0x000>, <&smmu_700_dpu 0x100>, + <&smmu_700_dpu 0x200>, <&smmu_700_dpu 0x600>; +#else /* TARGET_FLAVOUR_FPGA */ + iommus = <&smmu_600 0>, <&smmu_600 1>, <&smmu_600 2>, <&smmu_600 3>, + <&smmu_600 4>, <&smmu_600 5>, <&smmu_600 6>, <&smmu_600 7>, + <&smmu_600 8>, <&smmu_600 9>; +#endif + }; + + gpu: gpu@2d000000 { + interrupts = <GIC_SPI 66 IRQ_TYPE_LEVEL_HIGH 0>, + <GIC_SPI 67 IRQ_TYPE_LEVEL_HIGH 0>, + <GIC_SPI 65 IRQ_TYPE_LEVEL_HIGH 0>; + interrupt-names = "JOB", "MMU", "GPU"; +#if TARGET_FLAVOUR_FVP + iommus = <&smmu_700 0x200>; +#endif + }; +}; diff --git a/fdts/tc4.dts b/fdts/tc4.dts new file mode 100644 index 00000000..135d30a2 --- /dev/null +++ b/fdts/tc4.dts @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2020-2024, Arm Limited. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +/dts-v1/; + +#include <dt-bindings/interrupt-controller/arm-gic.h> +#include <dt-bindings/interrupt-controller/irq.h> +#include <platform_def.h> + +#define MHU_TX_ADDR 46240000 /* hex */ +#define MHU_RX_ADDR 46250000 /* hex */ + +#define LIT_CPU_PMU_COMPATIBLE "arm,armv8-pmuv3" +#define MID_CPU_PMU_COMPATIBLE "arm,armv8-pmuv3" +#define BIG_CPU_PMU_COMPATIBLE "arm,armv8-pmuv3" + +#define ETHERNET_ADDR 64000000 +#define ETHERNET_INT 799 + +#define SYS_REGS_ADDR 60080000 + +#define MMC_ADDR 600b0000 +#define MMC_INT_0 778 +#define MMC_INT_1 779 + +#define RTC_ADDR 600a0000 +#define RTC_INT 777 + +#define KMI_0_ADDR 60100000 +#define KMI_0_INT 784 +#define KMI_1_ADDR 60110000 +#define KMI_1_INT 785 + +#define VIRTIO_BLOCK_ADDR 60020000 +#define VIRTIO_BLOCK_INT 769 + +#include "tc-common.dtsi" +#if TARGET_FLAVOUR_FVP +#include "tc-fvp.dtsi" +#else +#include "tc-fpga.dtsi" +#endif /* TARGET_FLAVOUR_FVP */ +#include "tc3-4-base.dtsi" + +/ { + smmu_700: iommu@3f000000 { + status = "okay"; + }; + + smmu_700_dpu: iommu@4002a00000 { + status = "okay"; + }; + + dp0: display@DPU_ADDR { + iommus = <&smmu_700_dpu 0x000>, <&smmu_700_dpu 0x100>, + <&smmu_700_dpu 0x200>, <&smmu_700_dpu 0x600>; + }; + + gpu: gpu@2d000000 { + interrupts = <GIC_SPI 242 IRQ_TYPE_LEVEL_HIGH 0>; + interrupt-names = "IRQAW"; + iommus = <&smmu_700 0x200>; + }; +}; diff --git a/include/arch/aarch32/arch.h b/include/arch/aarch32/arch.h index a7117532..d2591ddb 100644 --- a/include/arch/aarch32/arch.h +++ b/include/arch/aarch32/arch.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016-2023, Arm Limited and Contributors. All rights reserved. + * Copyright (c) 2016-2024, Arm Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -111,18 +111,18 @@ #define ID_DFR0_PERFMON_PMUV3P5 U(6) #define ID_DFR0_COPTRC_SHIFT U(12) #define ID_DFR0_COPTRC_MASK U(0xf) -#define ID_DFR0_COPTRC_SUPPORTED U(1) +#define COPTRC_IMPLEMENTED U(1) #define ID_DFR0_COPTRC_LENGTH U(4) #define ID_DFR0_TRACEFILT_SHIFT U(28) #define ID_DFR0_TRACEFILT_MASK U(0xf) -#define ID_DFR0_TRACEFILT_SUPPORTED U(1) +#define TRACEFILT_IMPLEMENTED U(1) #define ID_DFR0_TRACEFILT_LENGTH U(4) /* ID_DFR1_EL1 definitions */ #define ID_DFR1_MTPMU_SHIFT U(0) #define ID_DFR1_MTPMU_MASK U(0xf) -#define ID_DFR1_MTPMU_SUPPORTED U(1) -#define ID_DFR1_MTPMU_DISABLED U(15) +#define MTPMU_IMPLEMENTED U(1) +#define MTPMU_NOT_IMPLEMENTED U(15) /* ID_MMFR3 definitions */ #define ID_MMFR3_PAN_SHIFT U(16) @@ -141,14 +141,13 @@ #define ID_PFR0_AMU_SHIFT U(20) #define ID_PFR0_AMU_LENGTH U(4) #define ID_PFR0_AMU_MASK U(0xf) -#define ID_PFR0_AMU_NOT_SUPPORTED U(0x0) #define ID_PFR0_AMU_V1 U(0x1) #define ID_PFR0_AMU_V1P1 U(0x2) #define ID_PFR0_DIT_SHIFT U(24) #define ID_PFR0_DIT_LENGTH U(4) #define ID_PFR0_DIT_MASK U(0xf) -#define ID_PFR0_DIT_SUPPORTED (U(1) << ID_PFR0_DIT_SHIFT) +#define DIT_IMPLEMENTED (U(1) << ID_PFR0_DIT_SHIFT) /* ID_PFR1 definitions */ #define ID_PFR1_VIRTEXT_SHIFT U(12) @@ -163,6 +162,11 @@ #define ID_PFR1_SEC_MASK U(0xf) #define ID_PFR1_ELx_ENABLED U(1) +/* ID_PFR2 definitions */ +#define ID_PFR2_SSBS_SHIFT U(4) +#define ID_PFR2_SSBS_MASK U(0xf) +#define SSBS_NOT_IMPLEMENTED U(0) + /* SCTLR definitions */ #define SCTLR_RES1_DEF ((U(1) << 23) | (U(1) << 22) | (U(1) << 4) | \ (U(1) << 3)) @@ -552,6 +556,7 @@ #define ID_DFR1 p15, 0, c0, c3, 5 #define ID_PFR0 p15, 0, c0, c1, 0 #define ID_PFR1 p15, 0, c0, c1, 1 +#define ID_PFR2 p15, 0, c0, c3, 4 #define MAIR0 p15, 0, c10, c2, 0 #define MAIR1 p15, 0, c10, c2, 1 #define TTBCR p15, 0, c2, c0, 2 @@ -692,8 +697,7 @@ /* PAR fields */ #define PAR_F_SHIFT U(0) #define PAR_F_MASK ULL(0x1) -#define PAR_ADDR_SHIFT U(12) -#define PAR_ADDR_MASK (BIT_64(40) - ULL(1)) /* 40-bits-wide page address */ +#define PAR_ADDR_MASK GENMASK_64(39, 12) /* 28-bits-wide page address */ /******************************************************************************* * Definitions for system register interface to AMU for FEAT_AMUv1 @@ -790,7 +794,21 @@ /******************************************************************************* * Definitions for DynamicIQ Shared Unit registers ******************************************************************************/ -#define CLUSTERPWRDN p15, 0, c15, c3, 6 +#define CLUSTERPWRDN p15, 0, c15, c3, 6 +#define CLUSTERPMCR p15, 0, c15, c5, 0 +#define CLUSTERPMCNTENSET p15, 0, c15, c5, 1 +#define CLUSTERPMCCNTR p15, 0, c15, c6, 0 +#define CLUSTERPMOVSSET p15, 0, c15, c5, 3 +#define CLUSTERPMOVSCLR p15, 0, c15, c5, 4 +#define CLUSTERPMSELR p15, 0, c15, c5, 5 +#define CLUSTERPMXEVTYPER p15, 0, c15, c6, 1 +#define CLUSTERPMXEVCNTR p15, 0, c15, c6, 2 + +/* CLUSTERPMCR register definitions */ +#define CLUSTERPMCR_E_BIT BIT(0) +#define CLUSTERPMCR_N_SHIFT U(11) +#define CLUSTERPMCR_N_MASK U(0x1f) + /* CLUSTERPWRDN register definitions */ #define DSU_CLUSTER_PWR_OFF 0 diff --git a/include/arch/aarch32/arch_features.h b/include/arch/aarch32/arch_features.h index f19c4c2f..e3472401 100644 --- a/include/arch/aarch32/arch_features.h +++ b/include/arch/aarch32/arch_features.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019-2023, Arm Limited. All rights reserved. + * Copyright (c) 2019-2024, Arm Limited. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -12,179 +12,191 @@ #include <arch_helpers.h> #include <common/feat_detect.h> -#define ISOLATE_FIELD(reg, feat) \ - ((unsigned int)(((reg) >> (feat ## _SHIFT)) & (feat ## _MASK))) +#define ISOLATE_FIELD(reg, feat, mask) \ + ((unsigned int)(((reg) >> (feat)) & mask)) -static inline bool is_armv7_gentimer_present(void) -{ - return ISOLATE_FIELD(read_id_pfr1(), ID_PFR1_GENTIMER) != 0U; -} - -static inline bool is_armv8_2_ttcnp_present(void) -{ - return ISOLATE_FIELD(read_id_mmfr4(), ID_MMFR4_CNP) != 0U; +#define CREATE_FEATURE_SUPPORTED(name, read_func, guard) \ +__attribute__((always_inline)) \ +static inline bool is_ ## name ## _supported(void) \ +{ \ + if ((guard) == FEAT_STATE_DISABLED) { \ + return false; \ + } \ + if ((guard) == FEAT_STATE_ALWAYS) { \ + return true; \ + } \ + return read_func(); \ } -static unsigned int read_feat_amu_id_field(void) -{ - return ISOLATE_FIELD(read_id_pfr0(), ID_PFR0_AMU); +#define CREATE_FEATURE_PRESENT(name, idreg, idfield, mask, idval) \ +__attribute__((always_inline)) \ +static inline bool is_ ## name ## _present(void) \ +{ \ + return (ISOLATE_FIELD(read_ ## idreg(), idfield, mask) >= idval) \ + ? true : false; \ } -static inline bool is_feat_amu_supported(void) -{ - if (ENABLE_FEAT_AMU == FEAT_STATE_DISABLED) { - return false; - } +#define CREATE_FEATURE_FUNCS(name, idreg, idfield, mask, idval, guard) \ +CREATE_FEATURE_PRESENT(name, idreg, idfield, mask, idval) \ +CREATE_FEATURE_SUPPORTED(name, is_ ## name ## _present, guard) - if (ENABLE_FEAT_AMU == FEAT_STATE_ALWAYS) { - return true; - } - return read_feat_amu_id_field() >= ID_PFR0_AMU_V1; -} +/* + * +----------------------------+ + * | Features supported | + * +----------------------------+ + * | GENTIMER | + * +----------------------------+ + * | FEAT_TTCNP | + * +----------------------------+ + * | FEAT_AMU | + * +----------------------------+ + * | FEAT_AMUV1P1 | + * +----------------------------+ + * | FEAT_TRF | + * +----------------------------+ + * | FEAT_SYS_REG_TRACE | + * +----------------------------+ + * | FEAT_DIT | + * +----------------------------+ + * | FEAT_PAN | + * +----------------------------+ + * | FEAT_SSBS | + * +----------------------------+ + * | FEAT_PMUV3 | + * +----------------------------+ + * | FEAT_MTPMU | + * +----------------------------+ + */ -static inline bool is_feat_amuv1p1_supported(void) +/* GENTIMER */ +__attribute__((always_inline)) +static inline bool is_armv7_gentimer_present(void) { - if (ENABLE_FEAT_AMUv1p1 == FEAT_STATE_DISABLED) { - return false; - } - - if (ENABLE_FEAT_AMUv1p1 == FEAT_STATE_ALWAYS) { - return true; - } - - return read_feat_amu_id_field() >= ID_PFR0_AMU_V1P1; + return ISOLATE_FIELD(read_id_pfr1(), ID_PFR1_GENTIMER_SHIFT, + ID_PFR1_GENTIMER_MASK) != 0U; } -static inline unsigned int read_feat_trf_id_field(void) -{ - return ISOLATE_FIELD(read_id_dfr0(), ID_DFR0_TRACEFILT); -} +/* FEAT_TTCNP: Translation table common not private */ +CREATE_FEATURE_PRESENT(feat_ttcnp, id_mmfr4, ID_MMFR4_CNP_SHIFT, + ID_MMFR4_CNP_MASK, 1U) -static inline bool is_feat_trf_supported(void) -{ - if (ENABLE_TRF_FOR_NS == FEAT_STATE_DISABLED) { - return false; - } +/* FEAT_AMU: Activity Monitors Extension */ +CREATE_FEATURE_FUNCS(feat_amu, id_pfr0, ID_PFR0_AMU_SHIFT, + ID_PFR0_AMU_MASK, ID_PFR0_AMU_V1, ENABLE_FEAT_AMU) - if (ENABLE_TRF_FOR_NS == FEAT_STATE_ALWAYS) { - return true; - } +/* FEAT_AMUV1P1: AMU Extension v1.1 */ +CREATE_FEATURE_FUNCS(feat_amuv1p1, id_pfr0, ID_PFR0_AMU_SHIFT, + ID_PFR0_AMU_MASK, ID_PFR0_AMU_V1P1, ENABLE_FEAT_AMUv1p1) - return read_feat_trf_id_field() != 0U; -} +/* FEAT_TRF: Tracefilter */ +CREATE_FEATURE_FUNCS(feat_trf, id_dfr0, ID_DFR0_TRACEFILT_SHIFT, + ID_DFR0_TRACEFILT_MASK, 1U, ENABLE_TRF_FOR_NS) -static inline unsigned int read_feat_coptrc_id_field(void) -{ - return ISOLATE_FIELD(read_id_dfr0(), ID_DFR0_COPTRC); -} +/* FEAT_SYS_REG_TRACE */ +CREATE_FEATURE_FUNCS(feat_sys_reg_trace, id_dfr0, ID_DFR0_COPTRC_SHIFT, + ID_DFR0_COPTRC_MASK, 1U, ENABLE_SYS_REG_TRACE_FOR_NS) -static inline bool is_feat_sys_reg_trace_supported(void) -{ - if (ENABLE_SYS_REG_TRACE_FOR_NS == FEAT_STATE_DISABLED) { - return false; - } +/* FEAT_DIT: Data independent timing */ +CREATE_FEATURE_FUNCS(feat_dit, id_pfr0, ID_PFR0_DIT_SHIFT, + ID_PFR0_DIT_MASK, 1U, ENABLE_FEAT_DIT) - if (ENABLE_SYS_REG_TRACE_FOR_NS == FEAT_STATE_ALWAYS) { - return true; - } +/* FEAT_PAN: Privileged access never */ +CREATE_FEATURE_FUNCS(feat_pan, id_mmfr3, ID_MMFR3_PAN_SHIFT, + ID_MMFR3_PAN_MASK, 1U, ENABLE_FEAT_PAN) - return read_feat_coptrc_id_field() != 0U; -} +/* FEAT_SSBS: Speculative store bypass safe */ +CREATE_FEATURE_PRESENT(feat_ssbs, id_pfr2, ID_PFR2_SSBS_SHIFT, + ID_PFR2_SSBS_MASK, 1U) -static inline unsigned int read_feat_dit_id_field(void) -{ - return ISOLATE_FIELD(read_id_pfr0(), ID_PFR0_DIT); -} +/* FEAT_PMUV3 */ +CREATE_FEATURE_PRESENT(feat_pmuv3, id_dfr0, ID_DFR0_PERFMON_SHIFT, + ID_DFR0_PERFMON_MASK, 3U) -static inline bool is_feat_dit_supported(void) +/* FEAT_MTPMU */ +__attribute__((always_inline)) +static inline bool is_feat_mtpmu_present(void) { - if (ENABLE_FEAT_DIT == FEAT_STATE_DISABLED) { - return false; - } - - if (ENABLE_FEAT_DIT == FEAT_STATE_ALWAYS) { - return true; - } - - return read_feat_dit_id_field() != 0U; -} - -static inline unsigned int read_feat_pan_id_field(void) -{ - return ISOLATE_FIELD(read_id_mmfr3(), ID_MMFR3_PAN); -} - -static inline bool is_feat_pan_supported(void) -{ - if (ENABLE_FEAT_PAN == FEAT_STATE_DISABLED) { - return false; - } - - if (ENABLE_FEAT_PAN == FEAT_STATE_ALWAYS) { - return true; - } - - return read_feat_pan_id_field() != 0U; + unsigned int mtpmu = ISOLATE_FIELD(read_id_dfr1(), ID_DFR1_MTPMU_SHIFT, + ID_DFR1_MTPMU_MASK); + return (mtpmu != 0U) && (mtpmu != MTPMU_NOT_IMPLEMENTED); } +CREATE_FEATURE_SUPPORTED(feat_mtpmu, is_feat_mtpmu_present, DISABLE_MTPMU) /* * TWED, ECV, CSV2, RAS are only used by the AArch64 EL2 context switch * code. In fact, EL2 context switching is only needed for AArch64 (since * there is no secure AArch32 EL2), so just disable these features here. */ +__attribute__((always_inline)) static inline bool is_feat_twed_supported(void) { return false; } +__attribute__((always_inline)) static inline bool is_feat_ecv_supported(void) { return false; } +__attribute__((always_inline)) static inline bool is_feat_ecv_v2_supported(void) { return false; } +__attribute__((always_inline)) static inline bool is_feat_csv2_2_supported(void) { return false; } +__attribute__((always_inline)) +static inline bool is_feat_csv2_3_supported(void) { return false; } +__attribute__((always_inline)) static inline bool is_feat_ras_supported(void) { return false; } /* The following features are supported in AArch64 only. */ +__attribute__((always_inline)) static inline bool is_feat_vhe_supported(void) { return false; } +__attribute__((always_inline)) static inline bool is_feat_sel2_supported(void) { return false; } +__attribute__((always_inline)) static inline bool is_feat_fgt_supported(void) { return false; } +__attribute__((always_inline)) static inline bool is_feat_tcr2_supported(void) { return false; } +__attribute__((always_inline)) static inline bool is_feat_spe_supported(void) { return false; } +__attribute__((always_inline)) static inline bool is_feat_rng_supported(void) { return false; } +__attribute__((always_inline)) static inline bool is_feat_gcs_supported(void) { return false; } +__attribute__((always_inline)) +static inline bool is_feat_mte2_supported(void) { return false; } +__attribute__((always_inline)) static inline bool is_feat_mpam_supported(void) { return false; } +__attribute__((always_inline)) static inline bool is_feat_hcx_supported(void) { return false; } +__attribute__((always_inline)) static inline bool is_feat_sve_supported(void) { return false; } +__attribute__((always_inline)) static inline bool is_feat_brbe_supported(void) { return false; } +__attribute__((always_inline)) static inline bool is_feat_trbe_supported(void) { return false; } +__attribute__((always_inline)) static inline bool is_feat_nv2_supported(void) { return false; } +__attribute__((always_inline)) static inline bool is_feat_sme_supported(void) { return false; } +__attribute__((always_inline)) static inline bool is_feat_sme2_supported(void) { return false; } +__attribute__((always_inline)) static inline bool is_feat_s2poe_supported(void) { return false; } +__attribute__((always_inline)) static inline bool is_feat_s1poe_supported(void) { return false; } +__attribute__((always_inline)) static inline bool is_feat_sxpoe_supported(void) { return false; } +__attribute__((always_inline)) static inline bool is_feat_s2pie_supported(void) { return false; } +__attribute__((always_inline)) static inline bool is_feat_s1pie_supported(void) { return false; } +__attribute__((always_inline)) static inline bool is_feat_sxpie_supported(void) { return false; } - -static inline unsigned int read_feat_pmuv3_id_field(void) -{ - return ISOLATE_FIELD(read_id_dfr0(), ID_DFR0_PERFMON); -} - -static inline unsigned int read_feat_mtpmu_id_field(void) -{ - return ISOLATE_FIELD(read_id_dfr1(), ID_DFR1_MTPMU); -} - -static inline bool is_feat_mtpmu_supported(void) -{ - if (DISABLE_MTPMU == FEAT_STATE_DISABLED) { - return false; - } - - if (DISABLE_MTPMU == FEAT_STATE_ALWAYS) { - return true; - } - - unsigned int mtpmu = read_feat_mtpmu_id_field(); - - return mtpmu != 0U && mtpmu != ID_DFR1_MTPMU_DISABLED; -} +__attribute__((always_inline)) +static inline bool is_feat_uao_present(void) { return false; } +__attribute__((always_inline)) +static inline bool is_feat_nmi_present(void) { return false; } +__attribute__((always_inline)) +static inline bool is_feat_ebep_present(void) { return false; } +__attribute__((always_inline)) +static inline bool is_feat_sebep_present(void) { return false; } +__attribute__((always_inline)) +static inline bool is_feat_d128_present(void) { return false; } +__attribute__((always_inline)) +static inline bool is_feat_ls64_accdata_present(void) { return false; } #endif /* ARCH_FEATURES_H */ diff --git a/include/arch/aarch32/arch_helpers.h b/include/arch/aarch32/arch_helpers.h index 3a7c7680..adc96ae0 100644 --- a/include/arch/aarch32/arch_helpers.h +++ b/include/arch/aarch32/arch_helpers.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016-2021, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2016-2024, ARM Limited and Contributors. All rights reserved. * Portions copyright (c) 2021-2022, ProvenRun S.A.S. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause @@ -224,6 +224,7 @@ DEFINE_COPROCR_READ_FUNC(id_dfr0, ID_DFR0) DEFINE_COPROCR_READ_FUNC(id_dfr1, ID_DFR1) DEFINE_COPROCR_READ_FUNC(id_pfr0, ID_PFR0) DEFINE_COPROCR_READ_FUNC(id_pfr1, ID_PFR1) +DEFINE_COPROCR_READ_FUNC(id_pfr2, ID_PFR2) DEFINE_COPROCR_READ_FUNC(isr, ISR) DEFINE_COPROCR_READ_FUNC(clidr, CLIDR) DEFINE_COPROCR_READ_FUNC_64(cntpct, CNTPCT_64) @@ -353,6 +354,14 @@ DEFINE_DCOP_PARAM_FUNC(cvac, DCCMVAC) * DynamIQ Shared Unit power management */ DEFINE_COPROCR_RW_FUNCS(clusterpwrdn, CLUSTERPWRDN) +DEFINE_COPROCR_RW_FUNCS(clusterpmcr, CLUSTERPMCR) +DEFINE_COPROCR_RW_FUNCS(clusterpmcntenset, CLUSTERPMCNTENSET) +DEFINE_COPROCR_RW_FUNCS(clusterpmccntr, CLUSTERPMCCNTR) +DEFINE_COPROCR_RW_FUNCS(clusterpmovsset, CLUSTERPMOVSSET) +DEFINE_COPROCR_RW_FUNCS(clusterpmovsclr, CLUSTERPMOVSCLR) +DEFINE_COPROCR_RW_FUNCS(clusterpmselr, CLUSTERPMSELR) +DEFINE_COPROCR_RW_FUNCS(clusterpmxevcntr, CLUSTERPMXEVCNTR) +DEFINE_COPROCR_RW_FUNCS(clusterpmxevtyper, CLUSTERPMXEVTYPER) /* * RNDR is AArch64 only, so just provide a placeholder here to make the diff --git a/include/arch/aarch32/asm_macros.S b/include/arch/aarch32/asm_macros.S index 3ba86e95..bccd2a7d 100644 --- a/include/arch/aarch32/asm_macros.S +++ b/include/arch/aarch32/asm_macros.S @@ -241,4 +241,13 @@ div2: cmp \temp, \bot bhs div2 .endm + + /* + * Helper macro to instruction adr <reg>, <symbol> where <symbol> is + * within the range +/- 4 GB. + */ + .macro adr_l, dst, sym + adrp \dst, \sym + add \dst, \dst, :lo12:\sym + .endm #endif /* ASM_MACROS_S */ diff --git a/include/arch/aarch32/el3_common_macros.S b/include/arch/aarch32/el3_common_macros.S index 697eb82c..41eeabb5 100644 --- a/include/arch/aarch32/el3_common_macros.S +++ b/include/arch/aarch32/el3_common_macros.S @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016-2023, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2016-2024, ARM Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -76,7 +76,7 @@ orr r0, r0, #(NSACR_RESET_VAL | NSACR_ENABLE_FP_ACCESS) ldcopr r1, ID_DFR0 ubfx r1, r1, #ID_DFR0_COPTRC_SHIFT, #ID_DFR0_COPTRC_LENGTH - cmp r1, #ID_DFR0_COPTRC_SUPPORTED + cmp r1, #COPTRC_IMPLEMENTED bne 1f orr r0, r0, #NSTRCDIS_BIT 1: @@ -143,7 +143,7 @@ SDCR_SCCD_BIT) & ~SDCR_TTRF_BIT) ldcopr r1, ID_DFR0 ubfx r1, r1, #ID_DFR0_TRACEFILT_SHIFT, #ID_DFR0_TRACEFILT_LENGTH - cmp r1, #ID_DFR0_TRACEFILT_SUPPORTED + cmp r1, #TRACEFILT_IMPLEMENTED bne 1f orr r0, r0, #SDCR_TTRF_BIT 1: @@ -182,7 +182,7 @@ */ ldcopr r0, ID_PFR0 and r0, r0, #(ID_PFR0_DIT_MASK << ID_PFR0_DIT_SHIFT) - cmp r0, #ID_PFR0_DIT_SUPPORTED + cmp r0, #DIT_IMPLEMENTED bne 1f mrs r0, cpsr orr r0, r0, #CPSR_DIT_BIT diff --git a/include/arch/aarch64/arch.h b/include/arch/aarch64/arch.h index e9d22b61..737d07ad 100644 --- a/include/arch/aarch64/arch.h +++ b/include/arch/aarch64/arch.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013-2023, Arm Limited and Contributors. All rights reserved. + * Copyright (c) 2013-2024, Arm Limited and Contributors. All rights reserved. * Copyright (c) 2020-2022, NVIDIA Corporation. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause @@ -24,6 +24,9 @@ #define MIDR_PN_MASK U(0xfff) #define MIDR_PN_SHIFT U(0x4) +/* Extracts the CPU part number from MIDR for checking CPU match */ +#define EXTRACT_PARTNUM(x) ((x >> MIDR_PN_SHIFT) & MIDR_PN_MASK) + /******************************************************************************* * MPIDR macros ******************************************************************************/ @@ -74,6 +77,19 @@ */ #define INVALID_MPID U(0xFFFFFFFF) +/******************************************************************************* + * Definitions for Exception vector offsets + ******************************************************************************/ +#define CURRENT_EL_SP0 0x0 +#define CURRENT_EL_SPX 0x200 +#define LOWER_EL_AARCH64 0x400 +#define LOWER_EL_AARCH32 0x600 + +#define SYNC_EXCEPTION 0x0 +#define IRQ_EXCEPTION 0x80 +#define FIQ_EXCEPTION 0x100 +#define SERROR_EXCEPTION 0x180 + /******************************************************************************* * Definitions for CPU system register interface to GICv3 ******************************************************************************/ @@ -101,9 +117,14 @@ * Definitions for EL2 system registers for save/restore routine ******************************************************************************/ #define CNTPOFF_EL2 S3_4_C14_C0_6 -#define HAFGRTR_EL2 S3_4_C3_C1_6 +#define HDFGRTR2_EL2 S3_4_C3_C1_0 +#define HDFGWTR2_EL2 S3_4_C3_C1_1 +#define HFGRTR2_EL2 S3_4_C3_C1_2 +#define HFGWTR2_EL2 S3_4_C3_C1_3 #define HDFGRTR_EL2 S3_4_C3_C1_4 #define HDFGWTR_EL2 S3_4_C3_C1_5 +#define HAFGRTR_EL2 S3_4_C3_C1_6 +#define HFGITR2_EL2 S3_4_C3_C1_7 #define HFGITR_EL2 S3_4_C1_C1_6 #define HFGRTR_EL2 S3_4_C1_C1_4 #define HFGWTR_EL2 S3_4_C1_C1_5 @@ -118,7 +139,6 @@ #define MPAMVPM6_EL2 S3_4_C10_C6_6 #define MPAMVPM7_EL2 S3_4_C10_C6_7 #define MPAMVPMV_EL2 S3_4_C10_C4_1 -#define TRFCR_EL2 S3_4_C1_C2_1 #define VNCR_EL2 S3_4_C2_C2_0 #define PMSCR_EL2 S3_4_C9_C9_0 #define TFSR_EL2 S3_4_C5_C6_0 @@ -167,7 +187,6 @@ #define ID_AA64PFR0_AMU_SHIFT U(44) #define ID_AA64PFR0_AMU_MASK ULL(0xf) -#define ID_AA64PFR0_AMU_NOT_SUPPORTED U(0x0) #define ID_AA64PFR0_AMU_V1 ULL(0x1) #define ID_AA64PFR0_AMU_V1P1 U(0x2) @@ -179,8 +198,8 @@ #define ID_AA64PFR0_SVE_SHIFT U(32) #define ID_AA64PFR0_SVE_MASK ULL(0xf) -#define ID_AA64PFR0_SVE_SUPPORTED ULL(0x1) #define ID_AA64PFR0_SVE_LENGTH U(4) +#define SVE_IMPLEMENTED ULL(0x1) #define ID_AA64PFR0_SEL2_SHIFT U(36) #define ID_AA64PFR0_SEL2_MASK ULL(0xf) @@ -191,22 +210,21 @@ #define ID_AA64PFR0_DIT_SHIFT U(48) #define ID_AA64PFR0_DIT_MASK ULL(0xf) #define ID_AA64PFR0_DIT_LENGTH U(4) -#define ID_AA64PFR0_DIT_SUPPORTED U(1) +#define DIT_IMPLEMENTED ULL(1) #define ID_AA64PFR0_CSV2_SHIFT U(56) #define ID_AA64PFR0_CSV2_MASK ULL(0xf) #define ID_AA64PFR0_CSV2_LENGTH U(4) -#define ID_AA64PFR0_CSV2_2_SUPPORTED ULL(0x2) +#define CSV2_2_IMPLEMENTED ULL(0x2) +#define CSV2_3_IMPLEMENTED ULL(0x3) #define ID_AA64PFR0_FEAT_RME_SHIFT U(52) #define ID_AA64PFR0_FEAT_RME_MASK ULL(0xf) #define ID_AA64PFR0_FEAT_RME_LENGTH U(4) -#define ID_AA64PFR0_FEAT_RME_NOT_SUPPORTED U(0) -#define ID_AA64PFR0_FEAT_RME_V1 U(1) +#define RME_NOT_IMPLEMENTED ULL(0) #define ID_AA64PFR0_RAS_SHIFT U(28) #define ID_AA64PFR0_RAS_MASK ULL(0xf) -#define ID_AA64PFR0_RAS_NOT_SUPPORTED ULL(0x0) #define ID_AA64PFR0_RAS_LENGTH U(4) /* Exception level handling */ @@ -214,43 +232,59 @@ #define EL_IMPL_A64ONLY ULL(1) #define EL_IMPL_A64_A32 ULL(2) +/* ID_AA64DFR0_EL1.DebugVer definitions */ +#define ID_AA64DFR0_DEBUGVER_SHIFT U(0) +#define ID_AA64DFR0_DEBUGVER_MASK ULL(0xf) +#define DEBUGVER_V8P9_IMPLEMENTED ULL(0xb) + /* ID_AA64DFR0_EL1.TraceVer definitions */ #define ID_AA64DFR0_TRACEVER_SHIFT U(4) #define ID_AA64DFR0_TRACEVER_MASK ULL(0xf) -#define ID_AA64DFR0_TRACEVER_SUPPORTED ULL(1) #define ID_AA64DFR0_TRACEVER_LENGTH U(4) + #define ID_AA64DFR0_TRACEFILT_SHIFT U(40) #define ID_AA64DFR0_TRACEFILT_MASK U(0xf) -#define ID_AA64DFR0_TRACEFILT_SUPPORTED U(1) #define ID_AA64DFR0_TRACEFILT_LENGTH U(4) +#define TRACEFILT_IMPLEMENTED ULL(1) + #define ID_AA64DFR0_PMUVER_LENGTH U(4) #define ID_AA64DFR0_PMUVER_SHIFT U(8) #define ID_AA64DFR0_PMUVER_MASK U(0xf) #define ID_AA64DFR0_PMUVER_PMUV3 U(1) -#define ID_AA64DFR0_PMUVER_PMUV3P7 U(7) +#define ID_AA64DFR0_PMUVER_PMUV3P8 U(8) #define ID_AA64DFR0_PMUVER_IMP_DEF U(0xf) +/* ID_AA64DFR0_EL1.SEBEP definitions */ +#define ID_AA64DFR0_SEBEP_SHIFT U(24) +#define ID_AA64DFR0_SEBEP_MASK ULL(0xf) +#define SEBEP_IMPLEMENTED ULL(1) + /* ID_AA64DFR0_EL1.PMS definitions (for ARMv8.2+) */ #define ID_AA64DFR0_PMS_SHIFT U(32) #define ID_AA64DFR0_PMS_MASK ULL(0xf) -#define ID_AA64DFR0_SPE_SUPPORTED ULL(0x1) -#define ID_AA64DFR0_SPE_NOT_SUPPORTED ULL(0x0) +#define SPE_IMPLEMENTED ULL(0x1) +#define SPE_NOT_IMPLEMENTED ULL(0x0) /* ID_AA64DFR0_EL1.TraceBuffer definitions */ #define ID_AA64DFR0_TRACEBUFFER_SHIFT U(44) #define ID_AA64DFR0_TRACEBUFFER_MASK ULL(0xf) -#define ID_AA64DFR0_TRACEBUFFER_SUPPORTED ULL(1) +#define TRACEBUFFER_IMPLEMENTED ULL(1) /* ID_AA64DFR0_EL1.MTPMU definitions (for ARMv8.6+) */ #define ID_AA64DFR0_MTPMU_SHIFT U(48) #define ID_AA64DFR0_MTPMU_MASK ULL(0xf) -#define ID_AA64DFR0_MTPMU_SUPPORTED ULL(1) -#define ID_AA64DFR0_MTPMU_DISABLED ULL(15) +#define MTPMU_IMPLEMENTED ULL(1) +#define MTPMU_NOT_IMPLEMENTED ULL(15) /* ID_AA64DFR0_EL1.BRBE definitions */ #define ID_AA64DFR0_BRBE_SHIFT U(52) #define ID_AA64DFR0_BRBE_MASK ULL(0xf) -#define ID_AA64DFR0_BRBE_SUPPORTED ULL(1) +#define BRBE_IMPLEMENTED ULL(1) + +/* ID_AA64DFR1_EL1 definitions */ +#define ID_AA64DFR1_EBEP_SHIFT U(48) +#define ID_AA64DFR1_EBEP_MASK ULL(0xf) +#define EBEP_IMPLEMENTED ULL(1) /* ID_AA64ISAR0_EL1 definitions */ #define ID_AA64ISAR0_RNDR_SHIFT U(60) @@ -259,6 +293,18 @@ /* ID_AA64ISAR1_EL1 definitions */ #define ID_AA64ISAR1_EL1 S3_0_C0_C6_1 +#define ID_AA64ISAR1_LS64_SHIFT U(60) +#define ID_AA64ISAR1_LS64_MASK ULL(0xf) +#define LS64_ACCDATA_IMPLEMENTED ULL(0x3) +#define LS64_V_IMPLEMENTED ULL(0x2) +#define LS64_IMPLEMENTED ULL(0x1) +#define LS64_NOT_IMPLEMENTED ULL(0x0) + +#define ID_AA64ISAR1_SB_SHIFT U(36) +#define ID_AA64ISAR1_SB_MASK ULL(0xf) +#define SB_IMPLEMENTED ULL(0x1) +#define SB_NOT_IMPLEMENTED ULL(0x0) + #define ID_AA64ISAR1_GPI_SHIFT U(28) #define ID_AA64ISAR1_GPI_MASK ULL(0xf) #define ID_AA64ISAR1_GPA_SHIFT U(24) @@ -269,11 +315,6 @@ #define ID_AA64ISAR1_APA_SHIFT U(4) #define ID_AA64ISAR1_APA_MASK ULL(0xf) -#define ID_AA64ISAR1_SB_SHIFT U(36) -#define ID_AA64ISAR1_SB_MASK ULL(0xf) -#define ID_AA64ISAR1_SB_SUPPORTED ULL(0x1) -#define ID_AA64ISAR1_SB_NOT_SUPPORTED ULL(0x0) - /* ID_AA64ISAR2_EL1 definitions */ #define ID_AA64ISAR2_EL1 S3_0_C0_C6_2 @@ -297,55 +338,46 @@ #define PARANGE_0100 U(44) #define PARANGE_0101 U(48) #define PARANGE_0110 U(52) +#define PARANGE_0111 U(56) #define ID_AA64MMFR0_EL1_ECV_SHIFT U(60) #define ID_AA64MMFR0_EL1_ECV_MASK ULL(0xf) -#define ID_AA64MMFR0_EL1_ECV_NOT_SUPPORTED ULL(0x0) -#define ID_AA64MMFR0_EL1_ECV_SUPPORTED ULL(0x1) -#define ID_AA64MMFR0_EL1_ECV_SELF_SYNCH ULL(0x2) +#define ID_AA64MMFR0_EL1_ECV_SELF_SYNCH ULL(0x2) +#define ECV_IMPLEMENTED ULL(0x1) #define ID_AA64MMFR0_EL1_FGT_SHIFT U(56) #define ID_AA64MMFR0_EL1_FGT_MASK ULL(0xf) -#define ID_AA64MMFR0_EL1_FGT_SUPPORTED ULL(0x1) -#define ID_AA64MMFR0_EL1_FGT_NOT_SUPPORTED ULL(0x0) +#define FGT2_IMPLEMENTED ULL(0x2) +#define FGT_IMPLEMENTED ULL(0x1) +#define FGT_NOT_IMPLEMENTED ULL(0x0) #define ID_AA64MMFR0_EL1_TGRAN4_SHIFT U(28) #define ID_AA64MMFR0_EL1_TGRAN4_MASK ULL(0xf) -#define ID_AA64MMFR0_EL1_TGRAN4_SUPPORTED ULL(0x0) -#define ID_AA64MMFR0_EL1_TGRAN4_52B_SUPPORTED ULL(0x1) -#define ID_AA64MMFR0_EL1_TGRAN4_NOT_SUPPORTED ULL(0xf) #define ID_AA64MMFR0_EL1_TGRAN64_SHIFT U(24) #define ID_AA64MMFR0_EL1_TGRAN64_MASK ULL(0xf) -#define ID_AA64MMFR0_EL1_TGRAN64_SUPPORTED ULL(0x0) -#define ID_AA64MMFR0_EL1_TGRAN64_NOT_SUPPORTED ULL(0xf) #define ID_AA64MMFR0_EL1_TGRAN16_SHIFT U(20) #define ID_AA64MMFR0_EL1_TGRAN16_MASK ULL(0xf) -#define ID_AA64MMFR0_EL1_TGRAN16_SUPPORTED ULL(0x1) -#define ID_AA64MMFR0_EL1_TGRAN16_NOT_SUPPORTED ULL(0x0) -#define ID_AA64MMFR0_EL1_TGRAN16_52B_SUPPORTED ULL(0x2) +#define TGRAN16_IMPLEMENTED ULL(0x1) /* ID_AA64MMFR1_EL1 definitions */ #define ID_AA64MMFR1_EL1_TWED_SHIFT U(32) #define ID_AA64MMFR1_EL1_TWED_MASK ULL(0xf) -#define ID_AA64MMFR1_EL1_TWED_SUPPORTED ULL(0x1) -#define ID_AA64MMFR1_EL1_TWED_NOT_SUPPORTED ULL(0x0) +#define TWED_IMPLEMENTED ULL(0x1) #define ID_AA64MMFR1_EL1_PAN_SHIFT U(20) #define ID_AA64MMFR1_EL1_PAN_MASK ULL(0xf) -#define ID_AA64MMFR1_EL1_PAN_NOT_SUPPORTED ULL(0x0) -#define ID_AA64MMFR1_EL1_PAN_SUPPORTED ULL(0x1) -#define ID_AA64MMFR1_EL1_PAN2_SUPPORTED ULL(0x2) -#define ID_AA64MMFR1_EL1_PAN3_SUPPORTED ULL(0x3) +#define PAN_IMPLEMENTED ULL(0x1) +#define PAN2_IMPLEMENTED ULL(0x2) +#define PAN3_IMPLEMENTED ULL(0x3) #define ID_AA64MMFR1_EL1_VHE_SHIFT U(8) #define ID_AA64MMFR1_EL1_VHE_MASK ULL(0xf) #define ID_AA64MMFR1_EL1_HCX_SHIFT U(40) #define ID_AA64MMFR1_EL1_HCX_MASK ULL(0xf) -#define ID_AA64MMFR1_EL1_HCX_SUPPORTED ULL(0x1) -#define ID_AA64MMFR1_EL1_HCX_NOT_SUPPORTED ULL(0x0) +#define HCX_IMPLEMENTED ULL(0x1) /* ID_AA64MMFR2_EL1 definitions */ #define ID_AA64MMFR2_EL1 S3_0_C0_C7_2 @@ -357,18 +389,23 @@ #define ID_AA64MMFR2_EL1_CCIDX_MASK ULL(0xf) #define ID_AA64MMFR2_EL1_CCIDX_LENGTH U(4) +#define ID_AA64MMFR2_EL1_UAO_SHIFT U(4) +#define ID_AA64MMFR2_EL1_UAO_MASK ULL(0xf) + #define ID_AA64MMFR2_EL1_CNP_SHIFT U(0) #define ID_AA64MMFR2_EL1_CNP_MASK ULL(0xf) #define ID_AA64MMFR2_EL1_NV_SHIFT U(24) #define ID_AA64MMFR2_EL1_NV_MASK ULL(0xf) -#define ID_AA64MMFR2_EL1_NV_NOT_SUPPORTED ULL(0x0) -#define ID_AA64MMFR2_EL1_NV_SUPPORTED ULL(0x1) -#define ID_AA64MMFR2_EL1_NV2_SUPPORTED ULL(0x2) +#define NV2_IMPLEMENTED ULL(0x2) /* ID_AA64MMFR3_EL1 definitions */ #define ID_AA64MMFR3_EL1 S3_0_C0_C7_3 +#define ID_AA64MMFR3_EL1_D128_SHIFT U(32) +#define ID_AA64MMFR3_EL1_D128_MASK ULL(0xf) +#define D128_IMPLEMENTED ULL(0x1) + #define ID_AA64MMFR3_EL1_S2POE_SHIFT U(20) #define ID_AA64MMFR3_EL1_S2POE_MASK ULL(0xf) @@ -381,22 +418,22 @@ #define ID_AA64MMFR3_EL1_S1PIE_SHIFT U(8) #define ID_AA64MMFR3_EL1_S1PIE_MASK ULL(0xf) +#define ID_AA64MMFR3_EL1_SCTLR2_SHIFT U(4) +#define ID_AA64MMFR3_EL1_SCTLR2_MASK ULL(0xf) +#define SCTLR2_IMPLEMENTED ULL(1) + #define ID_AA64MMFR3_EL1_TCRX_SHIFT U(0) #define ID_AA64MMFR3_EL1_TCRX_MASK ULL(0xf) /* ID_AA64PFR1_EL1 definitions */ -#define ID_AA64PFR1_EL1_GCS_SHIFT U(44) -#define ID_AA64PFR1_EL1_GCS_MASK ULL(0xf) - -#define ID_AA64PFR1_EL1_SSBS_SHIFT U(4) -#define ID_AA64PFR1_EL1_SSBS_MASK ULL(0xf) - -#define SSBS_UNAVAILABLE ULL(0) /* No architectural SSBS support */ #define ID_AA64PFR1_EL1_BT_SHIFT U(0) #define ID_AA64PFR1_EL1_BT_MASK ULL(0xf) +#define BTI_IMPLEMENTED ULL(1) /* The BTI mechanism is implemented */ -#define BTI_IMPLEMENTED ULL(1) /* The BTI mechanism is implemented */ +#define ID_AA64PFR1_EL1_SSBS_SHIFT U(4) +#define ID_AA64PFR1_EL1_SSBS_MASK ULL(0xf) +#define SSBS_NOT_IMPLEMENTED ULL(0) /* No architectural SSBS support */ #define ID_AA64PFR1_EL1_MTE_SHIFT U(8) #define ID_AA64PFR1_EL1_MTE_MASK ULL(0xf) @@ -404,8 +441,19 @@ #define ID_AA64PFR1_EL1_RNDR_TRAP_SHIFT U(28) #define ID_AA64PFR1_EL1_RNDR_TRAP_MASK U(0xf) -#define ID_AA64PFR1_EL1_RNG_TRAP_SUPPORTED ULL(0x1) -#define ID_AA64PFR1_EL1_RNG_TRAP_NOT_SUPPORTED ULL(0x0) +#define ID_AA64PFR1_EL1_NMI_SHIFT U(36) +#define ID_AA64PFR1_EL1_NMI_MASK ULL(0xf) +#define NMI_IMPLEMENTED ULL(1) + +#define ID_AA64PFR1_EL1_GCS_SHIFT U(44) +#define ID_AA64PFR1_EL1_GCS_MASK ULL(0xf) +#define GCS_IMPLEMENTED ULL(1) + +#define ID_AA64PFR1_EL1_THE_SHIFT U(48) +#define ID_AA64PFR1_EL1_THE_MASK ULL(0xf) +#define THE_IMPLEMENTED ULL(1) + +#define RNG_TRAP_IMPLEMENTED ULL(0x1) /* ID_AA64PFR2_EL1 definitions */ #define ID_AA64PFR2_EL1_MTEPERM_SHIFT U(0) @@ -438,9 +486,9 @@ #define ID_AA64PFR1_EL1_SME_SHIFT U(24) #define ID_AA64PFR1_EL1_SME_MASK ULL(0xf) #define ID_AA64PFR1_EL1_SME_WIDTH U(4) -#define ID_AA64PFR1_EL1_SME_NOT_SUPPORTED ULL(0x0) -#define ID_AA64PFR1_EL1_SME_SUPPORTED ULL(0x1) -#define ID_AA64PFR1_EL1_SME2_SUPPORTED ULL(0x2) +#define SME_IMPLEMENTED ULL(0x1) +#define SME2_IMPLEMENTED ULL(0x2) +#define SME_NOT_IMPLEMENTED ULL(0x0) /* ID_PFR1_EL1 definitions */ #define ID_PFR1_VIRTEXT_SHIFT U(12) @@ -502,6 +550,7 @@ #define SCTLR_TCF0_SHIFT U(38) #define SCTLR_TCF0_MASK ULL(3) #define SCTLR_ENTP2_BIT (ULL(1) << 60) +#define SCTLR_SPINTMASK_BIT (ULL(1) << 62) /* Tag Check Faults in EL0 have no effect on the PE */ #define SCTLR_TCF0_NO_EFFECT U(0) @@ -554,17 +603,23 @@ /* SCR definitions */ #define SCR_RES1_BITS ((U(1) << 4) | (U(1) << 5)) #define SCR_NSE_SHIFT U(62) +#define SCR_FGTEN2_BIT (UL(1) << 59) #define SCR_NSE_BIT (ULL(1) << SCR_NSE_SHIFT) #define SCR_GPF_BIT (UL(1) << 48) +#define SCR_D128En_BIT (UL(1) << 47) #define SCR_TWEDEL_SHIFT U(30) #define SCR_TWEDEL_MASK ULL(0xf) #define SCR_PIEN_BIT (UL(1) << 45) +#define SCR_SCTLR2En_BIT (UL(1) << 44) #define SCR_TCR2EN_BIT (UL(1) << 43) +#define SCR_RCWMASKEn_BIT (UL(1) << 42) +#define SCR_ENTP2_SHIFT U(41) +#define SCR_ENTP2_BIT (UL(1) << SCR_ENTP2_SHIFT) #define SCR_TRNDR_BIT (UL(1) << 40) #define SCR_GCSEn_BIT (UL(1) << 39) #define SCR_HXEn_BIT (UL(1) << 38) -#define SCR_ENTP2_SHIFT U(41) -#define SCR_ENTP2_BIT (UL(1) << SCR_ENTP2_SHIFT) +#define SCR_ADEn_BIT (UL(1) << 37) +#define SCR_EnAS0_BIT (UL(1) << 36) #define SCR_AMVOFFEN_SHIFT U(35) #define SCR_AMVOFFEN_BIT (UL(1) << SCR_AMVOFFEN_SHIFT) #define SCR_TWEDEn_BIT (UL(1) << 29) @@ -592,6 +647,9 @@ #define SCR_RESET_VAL SCR_RES1_BITS /* MDCR_EL3 definitions */ +#define MDCR_EBWE_BIT (ULL(1) << 43) +#define MDCR_E3BREC (ULL(1) << 38) +#define MDCR_E3BREW (ULL(1) << 37) #define MDCR_EnPMSN_BIT (ULL(1) << 36) #define MDCR_MPMX_BIT (ULL(1) << 35) #define MDCR_MCCD_BIT (ULL(1) << 34) @@ -729,6 +787,10 @@ #define DAIF_IRQ_BIT (U(1) << 1) #define DAIF_ABT_BIT (U(1) << 2) #define DAIF_DBG_BIT (U(1) << 3) +#define SPSR_V_BIT (U(1) << 28) +#define SPSR_C_BIT (U(1) << 29) +#define SPSR_Z_BIT (U(1) << 30) +#define SPSR_N_BIT (U(1) << 31) #define SPSR_DAIF_SHIFT U(6) #define SPSR_DAIF_MASK U(0xf) @@ -749,25 +811,32 @@ #define SPSR_M_MASK U(0x1) #define SPSR_M_AARCH64 U(0x0) #define SPSR_M_AARCH32 U(0x1) +#define SPSR_M_EL1H U(0x5) #define SPSR_M_EL2H U(0x9) #define SPSR_EL_SHIFT U(2) #define SPSR_EL_WIDTH U(2) -#define SPSR_SSBS_SHIFT_AARCH64 U(12) +#define SPSR_BTYPE_SHIFT_AARCH64 U(10) +#define SPSR_BTYPE_MASK_AARCH64 U(0x3) +#define SPSR_SSBS_SHIFT_AARCH64 U(12) #define SPSR_SSBS_BIT_AARCH64 (ULL(1) << SPSR_SSBS_SHIFT_AARCH64) #define SPSR_SSBS_SHIFT_AARCH32 U(23) #define SPSR_SSBS_BIT_AARCH32 (ULL(1) << SPSR_SSBS_SHIFT_AARCH32) - +#define SPSR_ALLINT_BIT_AARCH64 BIT_64(13) +#define SPSR_IL_BIT BIT_64(20) +#define SPSR_SS_BIT BIT_64(21) #define SPSR_PAN_BIT BIT_64(22) - +#define SPSR_UAO_BIT_AARCH64 BIT_64(23) #define SPSR_DIT_BIT BIT(24) - #define SPSR_TCO_BIT_AARCH64 BIT_64(25) +#define SPSR_PM_BIT_AARCH64 BIT_64(32) +#define SPSR_PPEND_BIT BIT(33) +#define SPSR_EXLOCK_BIT_AARCH64 BIT_64(34) +#define SPSR_NZCV (SPSR_V_BIT | SPSR_C_BIT | SPSR_Z_BIT | SPSR_N_BIT) #define DISABLE_ALL_EXCEPTIONS \ (DAIF_FIQ_BIT | DAIF_IRQ_BIT | DAIF_ABT_BIT | DAIF_DBG_BIT) - #define DISABLE_INTERRUPTS (DAIF_FIQ_BIT | DAIF_IRQ_BIT) /* @@ -945,6 +1014,7 @@ #define ESR_EC_LENGTH U(6) #define ESR_ISS_SHIFT U(0) #define ESR_ISS_LENGTH U(25) +#define ESR_IL_BIT (U(1) << 25) #define EC_UNKNOWN U(0x0) #define EC_WFE_WFI U(0x1) #define EC_AARCH32_CP15_MRC_MCR U(0x3) @@ -1059,11 +1129,11 @@ /* ID_AA64SMFR0_EL1 definitions */ #define ID_AA64SMFR0_EL1_SME_FA64_SHIFT U(63) #define ID_AA64SMFR0_EL1_SME_FA64_MASK U(0x1) -#define ID_AA64SMFR0_EL1_SME_FA64_SUPPORTED U(0x1) +#define SME_FA64_IMPLEMENTED U(0x1) #define ID_AA64SMFR0_EL1_SME_VER_SHIFT U(55) #define ID_AA64SMFR0_EL1_SME_VER_MASK ULL(0xf) -#define ID_AA64SMFR0_EL1_SME_INST_SUPPORTED ULL(0x0) -#define ID_AA64SMFR0_EL1_SME2_INST_SUPPORTED ULL(0x1) +#define SME_INST_IMPLEMENTED ULL(0x0) +#define SME2_INST_IMPLEMENTED ULL(0x1) /* SMCR_ELx definitions */ #define SMCR_ELX_LEN_SHIFT U(0) @@ -1124,8 +1194,9 @@ /* PAR_EL1 fields */ #define PAR_F_SHIFT U(0) #define PAR_F_MASK ULL(0x1) -#define PAR_ADDR_SHIFT U(12) -#define PAR_ADDR_MASK (BIT(40) - ULL(1)) /* 40-bits-wide page address */ + +#define PAR_D128_ADDR_MASK GENMASK(55, 12) /* 44-bits-wide page address */ +#define PAR_ADDR_MASK GENMASK(51, 12) /* 40-bits-wide page address */ /******************************************************************************* * Definitions for system register interface to SPE @@ -1293,6 +1364,8 @@ #define GPTBR_EL3 S3_6_C2_C1_4 #define SCXTNUM_EL2 S3_4_C13_C0_7 +#define SCXTNUM_EL1 S3_0_C13_C0_7 +#define SCXTNUM_EL0 S3_3_C13_C0_7 /******************************************************************************* * RAS system registers @@ -1357,6 +1430,8 @@ #define RGSR_EL1 S3_0_C1_C0_5 #define GCR_EL1 S3_0_C1_C0_6 +#define GCR_EL1_RRND_BIT (UL(1) << 16) + /******************************************************************************* * Armv8.5 - Random Number Generator Registers ******************************************************************************/ @@ -1389,24 +1464,58 @@ #define HFGWTR_EL2_INIT_VAL ULL(0xC4000000000000) /******************************************************************************* - * FEAT_TCR2 - Extended Translation Control Register + * FEAT_TCR2 - Extended Translation Control Registers ******************************************************************************/ +#define TCR2_EL1 S3_0_C2_C0_3 #define TCR2_EL2 S3_4_C2_C0_3 /******************************************************************************* - * Permission indirection and overlay + * Permission indirection and overlay Registers ******************************************************************************/ +#define PIRE0_EL1 S3_0_C10_C2_2 #define PIRE0_EL2 S3_4_C10_C2_2 +#define PIR_EL1 S3_0_C10_C2_3 #define PIR_EL2 S3_4_C10_C2_3 +#define POR_EL1 S3_0_C10_C2_4 #define POR_EL2 S3_4_C10_C2_4 #define S2PIR_EL2 S3_4_C10_C2_5 +#define S2POR_EL1 S3_0_C10_C2_5 /******************************************************************************* * FEAT_GCS - Guarded Control Stack Registers ******************************************************************************/ #define GCSCR_EL2 S3_4_C2_C5_0 #define GCSPR_EL2 S3_4_C2_C5_1 +#define GCSCR_EL1 S3_0_C2_C5_0 +#define GCSCRE0_EL1 S3_0_C2_C5_2 +#define GCSPR_EL1 S3_0_C2_C5_1 +#define GCSPR_EL0 S3_3_C2_C5_1 + +#define GCSCR_EXLOCK_EN_BIT (UL(1) << 6) + +/******************************************************************************* + * FEAT_TRF - Trace Filter Control Registers + ******************************************************************************/ +#define TRFCR_EL2 S3_4_C1_C2_1 +#define TRFCR_EL1 S3_0_C1_C2_1 + +/******************************************************************************* + * FEAT_THE - Translation Hardening Extension Registers + ******************************************************************************/ +#define RCWMASK_EL1 S3_0_C13_C0_6 +#define RCWSMASK_EL1 S3_0_C13_C0_3 + +/******************************************************************************* + * FEAT_SCTLR2 - Extension to SCTLR_ELx Registers + ******************************************************************************/ +#define SCTLR2_EL2 S3_4_C1_C0_3 +#define SCTLR2_EL1 S3_0_C1_C0_3 + +/******************************************************************************* + * FEAT_LS64_ACCDATA - LoadStore64B with status data + ******************************************************************************/ +#define ACCDATA_EL1 S3_0_C13_C0_5 /******************************************************************************* * Definitions for DynamicIQ Shared Unit registers @@ -1434,4 +1543,17 @@ /* alternative system register encoding for the "sb" speculation barrier */ #define SYSREG_SB S0_3_C3_C0_7 +#define CLUSTERPMCR_EL1 S3_0_C15_C5_0 +#define CLUSTERPMCNTENSET_EL1 S3_0_C15_C5_1 +#define CLUSTERPMCCNTR_EL1 S3_0_C15_C6_0 +#define CLUSTERPMOVSSET_EL1 S3_0_C15_C5_3 +#define CLUSTERPMOVSCLR_EL1 S3_0_C15_C5_4 +#define CLUSTERPMSELR_EL1 S3_0_C15_C5_5 +#define CLUSTERPMXEVTYPER_EL1 S3_0_C15_C6_1 +#define CLUSTERPMXEVCNTR_EL1 S3_0_C15_C6_2 + +#define CLUSTERPMCR_E_BIT BIT(0) +#define CLUSTERPMCR_N_SHIFT U(11) +#define CLUSTERPMCR_N_MASK U(0x1f) + #endif /* ARCH_H */ diff --git a/include/arch/aarch64/arch_features.h b/include/arch/aarch64/arch_features.h index cf8da5e8..59188da3 100644 --- a/include/arch/aarch64/arch_features.h +++ b/include/arch/aarch64/arch_features.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019-2023, Arm Limited. All rights reserved. + * Copyright (c) 2019-2024, Arm Limited. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -12,55 +12,168 @@ #include <arch_helpers.h> #include <common/feat_detect.h> -#define ISOLATE_FIELD(reg, feat) \ - ((unsigned int)(((reg) >> (feat)) & ID_REG_FIELD_MASK)) - -#define CREATE_FEATURE_FUNCS_VER(name, read_func, idvalue, guard) \ -static inline bool is_ ## name ## _supported(void) \ -{ \ - if ((guard) == FEAT_STATE_DISABLED) { \ - return false; \ - } \ - if ((guard) == FEAT_STATE_ALWAYS) { \ - return true; \ - } \ - return read_func() >= (idvalue); \ +#define ISOLATE_FIELD(reg, feat, mask) \ + ((unsigned int)(((reg) >> (feat)) & mask)) + +#define CREATE_FEATURE_SUPPORTED(name, read_func, guard) \ +__attribute__((always_inline)) \ +static inline bool is_ ## name ## _supported(void) \ +{ \ + if ((guard) == FEAT_STATE_DISABLED) { \ + return false; \ + } \ + if ((guard) == FEAT_STATE_ALWAYS) { \ + return true; \ + } \ + return read_func(); \ } -#define CREATE_FEATURE_FUNCS(name, idreg, idfield, guard) \ -static unsigned int read_ ## name ## _id_field(void) \ -{ \ - return ISOLATE_FIELD(read_ ## idreg(), idfield); \ -} \ -CREATE_FEATURE_FUNCS_VER(name, read_ ## name ## _id_field, 1U, guard) +#define CREATE_FEATURE_PRESENT(name, idreg, idfield, mask, idval) \ +__attribute__((always_inline)) \ +static inline bool is_ ## name ## _present(void) \ +{ \ + return (ISOLATE_FIELD(read_ ## idreg(), idfield, mask) >= idval) \ + ? true : false; \ +} + +#define CREATE_FEATURE_FUNCS(name, idreg, idfield, mask, idval, guard) \ +CREATE_FEATURE_PRESENT(name, idreg, idfield, mask, idval) \ +CREATE_FEATURE_SUPPORTED(name, is_ ## name ## _present, guard) + + +/* +----------------------------+ + * | Features supported | + * +----------------------------+ + * | GENTIMER | + * +----------------------------+ + * | FEAT_PAN | + * +----------------------------+ + * | FEAT_VHE | + * +----------------------------+ + * | FEAT_TTCNP | + * +----------------------------+ + * | FEAT_UAO | + * +----------------------------+ + * | FEAT_PACQARMA3 | + * +----------------------------+ + * | FEAT_PAUTH | + * +----------------------------+ + * | FEAT_TTST | + * +----------------------------+ + * | FEAT_BTI | + * +----------------------------+ + * | FEAT_MTE2 | + * +----------------------------+ + * | FEAT_SSBS | + * +----------------------------+ + * | FEAT_NMI | + * +----------------------------+ + * | FEAT_GCS | + * +----------------------------+ + * | FEAT_EBEP | + * +----------------------------+ + * | FEAT_SEBEP | + * +----------------------------+ + * | FEAT_SEL2 | + * +----------------------------+ + * | FEAT_TWED | + * +----------------------------+ + * | FEAT_FGT | + * +----------------------------+ + * | FEAT_EC/ECV2 | + * +----------------------------+ + * | FEAT_RNG | + * +----------------------------+ + * | FEAT_TCR2 | + * +----------------------------+ + * | FEAT_S2POE | + * +----------------------------+ + * | FEAT_S1POE | + * +----------------------------+ + * | FEAT_S2PIE | + * +----------------------------+ + * | FEAT_S1PIE | + * +----------------------------+ + * | FEAT_AMU/AMUV1P1 | + * +----------------------------+ + * | FEAT_MPAM | + * +----------------------------+ + * | FEAT_HCX | + * +----------------------------+ + * | FEAT_RNG_TRAP | + * +----------------------------+ + * | FEAT_RME | + * +----------------------------+ + * | FEAT_SB | + * +----------------------------+ + * | FEAT_CSV2/CSV3 | + * +----------------------------+ + * | FEAT_SPE | + * +----------------------------+ + * | FEAT_SVE | + * +----------------------------+ + * | FEAT_RAS | + * +----------------------------+ + * | FEAT_DIT | + * +----------------------------+ + * | FEAT_SYS_REG_TRACE | + * +----------------------------+ + * | FEAT_TRF | + * +----------------------------+ + * | FEAT_NV/NV2 | + * +----------------------------+ + * | FEAT_BRBE | + * +----------------------------+ + * | FEAT_TRBE | + * +----------------------------+ + * | FEAT_SME/SME2 | + * +----------------------------+ + * | FEAT_PMUV3 | + * +----------------------------+ + * | FEAT_MTPMU | + * +----------------------------+ + * | FEAT_FGT2 | + * +----------------------------+ + * | FEAT_THE | + * +----------------------------+ + * | FEAT_SCTLR2 | + * +----------------------------+ + * | FEAT_D128 | + * +----------------------------+ + * | FEAT_LS64_ACCDATA | + * +----------------------------+ + */ +__attribute__((always_inline)) static inline bool is_armv7_gentimer_present(void) { /* The Generic Timer is always present in an ARMv8-A implementation */ return true; } +/* FEAT_PAN: Privileged access never */ CREATE_FEATURE_FUNCS(feat_pan, id_aa64mmfr1_el1, ID_AA64MMFR1_EL1_PAN_SHIFT, - ENABLE_FEAT_PAN) + ID_AA64MMFR1_EL1_PAN_MASK, 1U, ENABLE_FEAT_PAN) + +/* FEAT_VHE: Virtualization Host Extensions */ CREATE_FEATURE_FUNCS(feat_vhe, id_aa64mmfr1_el1, ID_AA64MMFR1_EL1_VHE_SHIFT, - ENABLE_FEAT_VHE) + ID_AA64MMFR1_EL1_VHE_MASK, 1U, ENABLE_FEAT_VHE) -static inline bool is_armv8_2_ttcnp_present(void) -{ - return ((read_id_aa64mmfr2_el1() >> ID_AA64MMFR2_EL1_CNP_SHIFT) & - ID_AA64MMFR2_EL1_CNP_MASK) != 0U; -} +/* FEAT_TTCNP: Translation table common not private */ +CREATE_FEATURE_PRESENT(feat_ttcnp, id_aa64mmfr2_el1, ID_AA64MMFR2_EL1_CNP_SHIFT, + ID_AA64MMFR2_EL1_CNP_MASK, 1U) -static inline bool is_feat_pacqarma3_present(void) -{ - uint64_t mask_id_aa64isar2 = - (ID_AA64ISAR2_GPA3_MASK << ID_AA64ISAR2_GPA3_SHIFT) | - (ID_AA64ISAR2_APA3_MASK << ID_AA64ISAR2_APA3_SHIFT); +/* FEAT_UAO: User access override */ +CREATE_FEATURE_PRESENT(feat_uao, id_aa64mmfr2_el1, ID_AA64MMFR2_EL1_UAO_SHIFT, + ID_AA64MMFR2_EL1_UAO_MASK, 1U) - /* If any of the fields is not zero, QARMA3 algorithm is present */ - return (read_id_aa64isar2_el1() & mask_id_aa64isar2) != 0U; -} +/* If any of the fields is not zero, QARMA3 algorithm is present */ +CREATE_FEATURE_PRESENT(feat_pacqarma3, id_aa64isar2_el1, 0, + ((ID_AA64ISAR2_GPA3_MASK << ID_AA64ISAR2_GPA3_SHIFT) | + (ID_AA64ISAR2_APA3_MASK << ID_AA64ISAR2_APA3_SHIFT)), 1U) +/* PAUTH */ +__attribute__((always_inline)) static inline bool is_armv8_3_pauth_present(void) { uint64_t mask_id_aa64isar1 = @@ -77,55 +190,101 @@ static inline bool is_armv8_3_pauth_present(void) is_feat_pacqarma3_present()); } -static inline bool is_armv8_4_ttst_present(void) -{ - return ((read_id_aa64mmfr2_el1() >> ID_AA64MMFR2_EL1_ST_SHIFT) & - ID_AA64MMFR2_EL1_ST_MASK) == 1U; -} +/* FEAT_TTST: Small translation tables */ +CREATE_FEATURE_PRESENT(feat_ttst, id_aa64mmfr2_el1, ID_AA64MMFR2_EL1_ST_SHIFT, + ID_AA64MMFR2_EL1_ST_MASK, 1U) -static inline bool is_armv8_5_bti_present(void) -{ - return ((read_id_aa64pfr1_el1() >> ID_AA64PFR1_EL1_BT_SHIFT) & - ID_AA64PFR1_EL1_BT_MASK) == BTI_IMPLEMENTED; -} +/* FEAT_BTI: Branch target identification */ +CREATE_FEATURE_PRESENT(feat_bti, id_aa64pfr1_el1, ID_AA64PFR1_EL1_BT_SHIFT, + ID_AA64PFR1_EL1_BT_MASK, BTI_IMPLEMENTED) -static inline unsigned int get_armv8_5_mte_support(void) -{ - return ((read_id_aa64pfr1_el1() >> ID_AA64PFR1_EL1_MTE_SHIFT) & - ID_AA64PFR1_EL1_MTE_MASK); -} +/* FEAT_MTE2: Memory tagging extension */ +CREATE_FEATURE_FUNCS(feat_mte2, id_aa64pfr1_el1, ID_AA64PFR1_EL1_MTE_SHIFT, + ID_AA64PFR1_EL1_MTE_MASK, MTE_IMPLEMENTED_ELX, ENABLE_FEAT_MTE2) + +/* FEAT_SSBS: Speculative store bypass safe */ +CREATE_FEATURE_PRESENT(feat_ssbs, id_aa64pfr1_el1, ID_AA64PFR1_EL1_SSBS_SHIFT, + ID_AA64PFR1_EL1_SSBS_MASK, 1U) + +/* FEAT_NMI: Non-maskable interrupts */ +CREATE_FEATURE_PRESENT(feat_nmi, id_aa64pfr1_el1, ID_AA64PFR1_EL1_NMI_SHIFT, + ID_AA64PFR1_EL1_NMI_MASK, NMI_IMPLEMENTED) +/* FEAT_EBEP */ +CREATE_FEATURE_PRESENT(feat_ebep, id_aa64dfr1_el1, ID_AA64DFR1_EBEP_SHIFT, + ID_AA64DFR1_EBEP_MASK, EBEP_IMPLEMENTED) + +/* FEAT_SEBEP */ +CREATE_FEATURE_PRESENT(feat_sebep, id_aa64dfr0_el1, ID_AA64DFR0_SEBEP_SHIFT, + ID_AA64DFR0_SEBEP_MASK, SEBEP_IMPLEMENTED) + +/* FEAT_SEL2: Secure EL2 */ CREATE_FEATURE_FUNCS(feat_sel2, id_aa64pfr0_el1, ID_AA64PFR0_SEL2_SHIFT, - ENABLE_FEAT_SEL2) + ID_AA64PFR0_SEL2_MASK, 1U, ENABLE_FEAT_SEL2) + +/* FEAT_TWED: Delayed trapping of WFE */ CREATE_FEATURE_FUNCS(feat_twed, id_aa64mmfr1_el1, ID_AA64MMFR1_EL1_TWED_SHIFT, - ENABLE_FEAT_TWED) + ID_AA64MMFR1_EL1_TWED_MASK, 1U, ENABLE_FEAT_TWED) + +/* FEAT_FGT: Fine-grained traps */ CREATE_FEATURE_FUNCS(feat_fgt, id_aa64mmfr0_el1, ID_AA64MMFR0_EL1_FGT_SHIFT, - ENABLE_FEAT_FGT) -CREATE_FEATURE_FUNCS(feat_mte_perm, id_aa64pfr2_el1, - ID_AA64PFR2_EL1_MTEPERM_SHIFT, ENABLE_FEAT_MTE_PERM) + ID_AA64MMFR0_EL1_FGT_MASK, 1U, ENABLE_FEAT_FGT) + +/* FEAT_FGT2: Fine-grained traps extended */ +CREATE_FEATURE_FUNCS(feat_fgt2, id_aa64mmfr0_el1, ID_AA64MMFR0_EL1_FGT_SHIFT, + ID_AA64MMFR0_EL1_FGT_MASK, FGT2_IMPLEMENTED, ENABLE_FEAT_FGT2) + +/* FEAT_ECV: Enhanced Counter Virtualization */ CREATE_FEATURE_FUNCS(feat_ecv, id_aa64mmfr0_el1, ID_AA64MMFR0_EL1_ECV_SHIFT, - ENABLE_FEAT_ECV) -CREATE_FEATURE_FUNCS_VER(feat_ecv_v2, read_feat_ecv_id_field, - ID_AA64MMFR0_EL1_ECV_SELF_SYNCH, ENABLE_FEAT_ECV) + ID_AA64MMFR0_EL1_ECV_MASK, 1U, ENABLE_FEAT_ECV) +CREATE_FEATURE_FUNCS(feat_ecv_v2, id_aa64mmfr0_el1, ID_AA64MMFR0_EL1_ECV_SHIFT, + ID_AA64MMFR0_EL1_ECV_MASK, ID_AA64MMFR0_EL1_ECV_SELF_SYNCH, ENABLE_FEAT_ECV) +/* FEAT_RNG: Random number generator */ CREATE_FEATURE_FUNCS(feat_rng, id_aa64isar0_el1, ID_AA64ISAR0_RNDR_SHIFT, - ENABLE_FEAT_RNG) + ID_AA64ISAR0_RNDR_MASK, 1U, ENABLE_FEAT_RNG) + +/* FEAT_TCR2: Support TCR2_ELx regs */ CREATE_FEATURE_FUNCS(feat_tcr2, id_aa64mmfr3_el1, ID_AA64MMFR3_EL1_TCRX_SHIFT, - ENABLE_FEAT_TCR2) + ID_AA64MMFR3_EL1_TCRX_MASK, 1U, ENABLE_FEAT_TCR2) +/* FEAT_S2POE */ CREATE_FEATURE_FUNCS(feat_s2poe, id_aa64mmfr3_el1, ID_AA64MMFR3_EL1_S2POE_SHIFT, - ENABLE_FEAT_S2POE) + ID_AA64MMFR3_EL1_S2POE_MASK, 1U, ENABLE_FEAT_S2POE) + +/* FEAT_S1POE */ CREATE_FEATURE_FUNCS(feat_s1poe, id_aa64mmfr3_el1, ID_AA64MMFR3_EL1_S1POE_SHIFT, - ENABLE_FEAT_S1POE) + ID_AA64MMFR3_EL1_S1POE_MASK, 1U, ENABLE_FEAT_S1POE) + +__attribute__((always_inline)) static inline bool is_feat_sxpoe_supported(void) { return is_feat_s1poe_supported() || is_feat_s2poe_supported(); } +/* FEAT_S2PIE */ CREATE_FEATURE_FUNCS(feat_s2pie, id_aa64mmfr3_el1, ID_AA64MMFR3_EL1_S2PIE_SHIFT, - ENABLE_FEAT_S2PIE) + ID_AA64MMFR3_EL1_S2PIE_MASK, 1U, ENABLE_FEAT_S2PIE) + +/* FEAT_S1PIE */ CREATE_FEATURE_FUNCS(feat_s1pie, id_aa64mmfr3_el1, ID_AA64MMFR3_EL1_S1PIE_SHIFT, - ENABLE_FEAT_S1PIE) + ID_AA64MMFR3_EL1_S1PIE_MASK, 1U, ENABLE_FEAT_S1PIE) + +/* FEAT_THE: Translation Hardening Extension */ +CREATE_FEATURE_FUNCS(feat_the, id_aa64pfr1_el1, ID_AA64PFR1_EL1_THE_SHIFT, + ID_AA64PFR1_EL1_THE_MASK, THE_IMPLEMENTED, ENABLE_FEAT_THE) + +/* FEAT_SCTLR2 */ +CREATE_FEATURE_FUNCS(feat_sctlr2, id_aa64mmfr3_el1, ID_AA64MMFR3_EL1_SCTLR2_SHIFT, + ID_AA64MMFR3_EL1_SCTLR2_MASK, SCTLR2_IMPLEMENTED, + ENABLE_FEAT_SCTLR2) + +/* FEAT_D128 */ +CREATE_FEATURE_FUNCS(feat_d128, id_aa64mmfr3_el1, ID_AA64MMFR3_EL1_D128_SHIFT, + ID_AA64MMFR3_EL1_D128_MASK, D128_IMPLEMENTED, + ENABLE_FEAT_D128) + +__attribute__((always_inline)) static inline bool is_feat_sxpie_supported(void) { return is_feat_s1pie_supported() || is_feat_s2pie_supported(); @@ -133,13 +292,15 @@ static inline bool is_feat_sxpie_supported(void) /* FEAT_GCS: Guarded Control Stack */ CREATE_FEATURE_FUNCS(feat_gcs, id_aa64pfr1_el1, ID_AA64PFR1_EL1_GCS_SHIFT, - ENABLE_FEAT_GCS) + ID_AA64PFR1_EL1_GCS_MASK, 1U, ENABLE_FEAT_GCS) /* FEAT_AMU: Activity Monitors Extension */ CREATE_FEATURE_FUNCS(feat_amu, id_aa64pfr0_el1, ID_AA64PFR0_AMU_SHIFT, - ENABLE_FEAT_AMU) -CREATE_FEATURE_FUNCS_VER(feat_amuv1p1, read_feat_amu_id_field, - ID_AA64PFR0_AMU_V1P1, ENABLE_FEAT_AMUv1p1) + ID_AA64PFR0_AMU_MASK, 1U, ENABLE_FEAT_AMU) + +/* FEAT_AMUV1P1: AMU Extension v1.1 */ +CREATE_FEATURE_FUNCS(feat_amuv1p1, id_aa64pfr0_el1, ID_AA64PFR0_AMU_SHIFT, + ID_AA64PFR0_AMU_MASK, ID_AA64PFR0_AMU_V1P1, ENABLE_FEAT_AMUv1p1) /* * Return MPAM version: @@ -150,144 +311,159 @@ CREATE_FEATURE_FUNCS_VER(feat_amuv1p1, read_feat_amu_id_field, * 0x11: v1.1 Armv8.4 or later * */ -static inline unsigned int read_feat_mpam_version(void) +__attribute__((always_inline)) +static inline bool is_feat_mpam_present(void) { - return (unsigned int)((((read_id_aa64pfr0_el1() >> + unsigned int ret = (unsigned int)((((read_id_aa64pfr0_el1() >> ID_AA64PFR0_MPAM_SHIFT) & ID_AA64PFR0_MPAM_MASK) << 4) | - ((read_id_aa64pfr1_el1() >> - ID_AA64PFR1_MPAM_FRAC_SHIFT) & ID_AA64PFR1_MPAM_FRAC_MASK)); + ((read_id_aa64pfr1_el1() >> ID_AA64PFR1_MPAM_FRAC_SHIFT) + & ID_AA64PFR1_MPAM_FRAC_MASK)); + return ret; } -CREATE_FEATURE_FUNCS_VER(feat_mpam, read_feat_mpam_version, 1U, - ENABLE_FEAT_MPAM) +CREATE_FEATURE_SUPPORTED(feat_mpam, is_feat_mpam_present, ENABLE_FEAT_MPAM) + +/* + * FEAT_DebugV8P9: Debug extension. This function checks the field 3:0 of + * ID_AA64DFR0 Aarch64 Debug Feature Register 0 for the version of + * Feat_Debug supported. The value of the field determines feature presence + * + * 0b0110 - Arm v8.0 debug + * 0b0111 - Arm v8.0 debug architecture with Virtualization host extensions + * 0x1000 - FEAT_Debugv8p2 is supported + * 0x1001 - FEAT_Debugv8p4 is supported + * 0x1010 - FEAT_Debugv8p8 is supported + * 0x1011 - FEAT_Debugv8p9 is supported + * + */ +CREATE_FEATURE_FUNCS(feat_debugv8p9, id_aa64dfr0_el1, ID_AA64DFR0_DEBUGVER_SHIFT, + ID_AA64DFR0_DEBUGVER_MASK, DEBUGVER_V8P9_IMPLEMENTED, + ENABLE_FEAT_DEBUGV8P9) /* FEAT_HCX: Extended Hypervisor Configuration Register */ CREATE_FEATURE_FUNCS(feat_hcx, id_aa64mmfr1_el1, ID_AA64MMFR1_EL1_HCX_SHIFT, - ENABLE_FEAT_HCX) + ID_AA64MMFR1_EL1_HCX_MASK, 1U, ENABLE_FEAT_HCX) -static inline bool is_feat_rng_trap_present(void) -{ - return (((read_id_aa64pfr1_el1() >> ID_AA64PFR1_EL1_RNDR_TRAP_SHIFT) & - ID_AA64PFR1_EL1_RNDR_TRAP_MASK) - == ID_AA64PFR1_EL1_RNG_TRAP_SUPPORTED); -} +/* FEAT_RNG_TRAP: Trapping support */ +CREATE_FEATURE_PRESENT(feat_rng_trap, id_aa64pfr1_el1, ID_AA64PFR1_EL1_RNDR_TRAP_SHIFT, + ID_AA64PFR1_EL1_RNDR_TRAP_MASK, RNG_TRAP_IMPLEMENTED) -static inline unsigned int get_armv9_2_feat_rme_support(void) -{ - /* - * Return the RME version, zero if not supported. This function can be - * used as both an integer value for the RME version or compared to zero - * to detect RME presence. - */ - return (unsigned int)(read_id_aa64pfr0_el1() >> - ID_AA64PFR0_FEAT_RME_SHIFT) & ID_AA64PFR0_FEAT_RME_MASK; -} +/* Return the RME version, zero if not supported. */ +CREATE_FEATURE_FUNCS(feat_rme, id_aa64pfr0_el1, ID_AA64PFR0_FEAT_RME_SHIFT, + ID_AA64PFR0_FEAT_RME_MASK, 1U, ENABLE_RME) -/********************************************************************************* - * Function to identify the presence of FEAT_SB (Speculation Barrier Instruction) - ********************************************************************************/ -static inline unsigned int read_feat_sb_id_field(void) -{ - return ISOLATE_FIELD(read_id_aa64isar1_el1(), ID_AA64ISAR1_SB_SHIFT); -} +/* FEAT_SB: Speculation barrier instruction */ +CREATE_FEATURE_PRESENT(feat_sb, id_aa64isar1_el1, ID_AA64ISAR1_SB_SHIFT, + ID_AA64ISAR1_SB_MASK, 1U) + +/* + * FEAT_CSV2: Cache Speculation Variant 2. This checks bit fields[56-59] + * of id_aa64pfr0_el1 register and can be used to check for below features: + * FEAT_CSV2_2: Cache Speculation Variant CSV2_2. + * FEAT_CSV2_3: Cache Speculation Variant CSV2_3. + * 0b0000 - Feature FEAT_CSV2 is not implemented. + * 0b0001 - Feature FEAT_CSV2 is implemented, but FEAT_CSV2_2 and FEAT_CSV2_3 + * are not implemented. + * 0b0010 - Feature FEAT_CSV2_2 is implemented but FEAT_CSV2_3 is not + * implemented. + * 0b0011 - Feature FEAT_CSV2_3 is implemented. + */ -/* FEAT_CSV2_2: Cache Speculation Variant 2 */ -CREATE_FEATURE_FUNCS(feat_csv2, id_aa64pfr0_el1, ID_AA64PFR0_CSV2_SHIFT, 0) -CREATE_FEATURE_FUNCS_VER(feat_csv2_2, read_feat_csv2_id_field, - ID_AA64PFR0_CSV2_2_SUPPORTED, ENABLE_FEAT_CSV2_2) +CREATE_FEATURE_FUNCS(feat_csv2_2, id_aa64pfr0_el1, ID_AA64PFR0_CSV2_SHIFT, + ID_AA64PFR0_CSV2_MASK, CSV2_2_IMPLEMENTED, ENABLE_FEAT_CSV2_2) +CREATE_FEATURE_FUNCS(feat_csv2_3, id_aa64pfr0_el1, ID_AA64PFR0_CSV2_SHIFT, + ID_AA64PFR0_CSV2_MASK, CSV2_3_IMPLEMENTED, ENABLE_FEAT_CSV2_3) /* FEAT_SPE: Statistical Profiling Extension */ CREATE_FEATURE_FUNCS(feat_spe, id_aa64dfr0_el1, ID_AA64DFR0_PMS_SHIFT, - ENABLE_SPE_FOR_NS) + ID_AA64DFR0_PMS_MASK, 1U, ENABLE_SPE_FOR_NS) /* FEAT_SVE: Scalable Vector Extension */ CREATE_FEATURE_FUNCS(feat_sve, id_aa64pfr0_el1, ID_AA64PFR0_SVE_SHIFT, - ENABLE_SVE_FOR_NS) + ID_AA64PFR0_SVE_MASK, 1U, ENABLE_SVE_FOR_NS) /* FEAT_RAS: Reliability, Accessibility, Serviceability */ -CREATE_FEATURE_FUNCS(feat_ras, id_aa64pfr0_el1, - ID_AA64PFR0_RAS_SHIFT, ENABLE_FEAT_RAS) +CREATE_FEATURE_FUNCS(feat_ras, id_aa64pfr0_el1, ID_AA64PFR0_RAS_SHIFT, + ID_AA64PFR0_RAS_MASK, 1U, ENABLE_FEAT_RAS) /* FEAT_DIT: Data Independent Timing instructions */ -CREATE_FEATURE_FUNCS(feat_dit, id_aa64pfr0_el1, - ID_AA64PFR0_DIT_SHIFT, ENABLE_FEAT_DIT) +CREATE_FEATURE_FUNCS(feat_dit, id_aa64pfr0_el1, ID_AA64PFR0_DIT_SHIFT, + ID_AA64PFR0_DIT_MASK, 1U, ENABLE_FEAT_DIT) -CREATE_FEATURE_FUNCS(feat_sys_reg_trace, id_aa64dfr0_el1, - ID_AA64DFR0_TRACEVER_SHIFT, ENABLE_SYS_REG_TRACE_FOR_NS) +/* FEAT_SYS_REG_TRACE */ +CREATE_FEATURE_FUNCS(feat_sys_reg_trace, id_aa64dfr0_el1, ID_AA64DFR0_TRACEVER_SHIFT, + ID_AA64DFR0_TRACEVER_MASK, 1U, ENABLE_SYS_REG_TRACE_FOR_NS) /* FEAT_TRF: TraceFilter */ CREATE_FEATURE_FUNCS(feat_trf, id_aa64dfr0_el1, ID_AA64DFR0_TRACEFILT_SHIFT, - ENABLE_TRF_FOR_NS) + ID_AA64DFR0_TRACEFILT_MASK, 1U, ENABLE_TRF_FOR_NS) /* FEAT_NV2: Enhanced Nested Virtualization */ -CREATE_FEATURE_FUNCS(feat_nv, id_aa64mmfr2_el1, ID_AA64MMFR2_EL1_NV_SHIFT, 0) -CREATE_FEATURE_FUNCS_VER(feat_nv2, read_feat_nv_id_field, - ID_AA64MMFR2_EL1_NV2_SUPPORTED, CTX_INCLUDE_NEVE_REGS) +CREATE_FEATURE_FUNCS(feat_nv, id_aa64mmfr2_el1, ID_AA64MMFR2_EL1_NV_SHIFT, + ID_AA64MMFR2_EL1_NV_MASK, 1U, 0U) +CREATE_FEATURE_FUNCS(feat_nv2, id_aa64mmfr2_el1, ID_AA64MMFR2_EL1_NV_SHIFT, + ID_AA64MMFR2_EL1_NV_MASK, NV2_IMPLEMENTED, CTX_INCLUDE_NEVE_REGS) /* FEAT_BRBE: Branch Record Buffer Extension */ CREATE_FEATURE_FUNCS(feat_brbe, id_aa64dfr0_el1, ID_AA64DFR0_BRBE_SHIFT, - ENABLE_BRBE_FOR_NS) + ID_AA64DFR0_BRBE_MASK, 1U, ENABLE_BRBE_FOR_NS) /* FEAT_TRBE: Trace Buffer Extension */ CREATE_FEATURE_FUNCS(feat_trbe, id_aa64dfr0_el1, ID_AA64DFR0_TRACEBUFFER_SHIFT, - ENABLE_TRBE_FOR_NS) + ID_AA64DFR0_TRACEBUFFER_MASK, 1U, ENABLE_TRBE_FOR_NS) + +/* FEAT_SME_FA64: Full A64 Instruction support in streaming SVE mode */ +CREATE_FEATURE_PRESENT(feat_sme_fa64, id_aa64smfr0_el1, ID_AA64SMFR0_EL1_SME_FA64_SHIFT, + ID_AA64SMFR0_EL1_SME_FA64_MASK, 1U) -static inline unsigned int read_feat_sme_fa64_id_field(void) -{ - return ISOLATE_FIELD(read_id_aa64smfr0_el1(), - ID_AA64SMFR0_EL1_SME_FA64_SHIFT); -} /* FEAT_SMEx: Scalar Matrix Extension */ CREATE_FEATURE_FUNCS(feat_sme, id_aa64pfr1_el1, ID_AA64PFR1_EL1_SME_SHIFT, - ENABLE_SME_FOR_NS) -CREATE_FEATURE_FUNCS_VER(feat_sme2, read_feat_sme_id_field, - ID_AA64PFR1_EL1_SME2_SUPPORTED, ENABLE_SME2_FOR_NS) + ID_AA64PFR1_EL1_SME_MASK, 1U, ENABLE_SME_FOR_NS) + +CREATE_FEATURE_FUNCS(feat_sme2, id_aa64pfr1_el1, ID_AA64PFR1_EL1_SME_SHIFT, + ID_AA64PFR1_EL1_SME_MASK, SME2_IMPLEMENTED, ENABLE_SME2_FOR_NS) + +/* FEAT_LS64_ACCDATA: */ +CREATE_FEATURE_FUNCS(feat_ls64_accdata, id_aa64isar1_el1, ID_AA64ISAR1_LS64_SHIFT, + ID_AA64ISAR1_LS64_MASK, LS64_ACCDATA_IMPLEMENTED, + ENABLE_FEAT_LS64_ACCDATA) /******************************************************************************* * Function to get hardware granularity support ******************************************************************************/ -static inline unsigned int read_id_aa64mmfr0_el0_tgran4_field(void) +__attribute__((always_inline)) +static inline bool is_feat_tgran4K_present(void) { - return ISOLATE_FIELD(read_id_aa64mmfr0_el1(), - ID_AA64MMFR0_EL1_TGRAN4_SHIFT); + unsigned int tgranx = ISOLATE_FIELD(read_id_aa64mmfr0_el1(), + ID_AA64MMFR0_EL1_TGRAN4_SHIFT, ID_REG_FIELD_MASK); + return (tgranx < 8U); } -static inline unsigned int read_id_aa64mmfr0_el0_tgran16_field(void) -{ - return ISOLATE_FIELD(read_id_aa64mmfr0_el1(), - ID_AA64MMFR0_EL1_TGRAN16_SHIFT); -} +CREATE_FEATURE_PRESENT(feat_tgran16K, id_aa64mmfr0_el1, ID_AA64MMFR0_EL1_TGRAN16_SHIFT, + ID_AA64MMFR0_EL1_TGRAN16_MASK, TGRAN16_IMPLEMENTED) -static inline unsigned int read_id_aa64mmfr0_el0_tgran64_field(void) +__attribute__((always_inline)) +static inline bool is_feat_tgran64K_present(void) { - return ISOLATE_FIELD(read_id_aa64mmfr0_el1(), - ID_AA64MMFR0_EL1_TGRAN64_SHIFT); + unsigned int tgranx = ISOLATE_FIELD(read_id_aa64mmfr0_el1(), + ID_AA64MMFR0_EL1_TGRAN64_SHIFT, ID_REG_FIELD_MASK); + return (tgranx < 8U); } -static inline unsigned int read_feat_pmuv3_id_field(void) -{ - return ISOLATE_FIELD(read_id_aa64dfr0_el1(), ID_AA64DFR0_PMUVER_SHIFT); -} +/* FEAT_PMUV3 */ +CREATE_FEATURE_PRESENT(feat_pmuv3, id_aa64dfr0_el1, ID_AA64DFR0_PMUVER_SHIFT, + ID_AA64DFR0_PMUVER_MASK, 1U) -static inline unsigned int read_feat_mtpmu_id_field(void) +/* FEAT_MTPMU */ +__attribute__((always_inline)) +static inline bool is_feat_mtpmu_present(void) { - return ISOLATE_FIELD(read_id_aa64dfr0_el1(), ID_AA64DFR0_MTPMU_SHIFT); + unsigned int mtpmu = ISOLATE_FIELD(read_id_aa64dfr0_el1(), ID_AA64DFR0_MTPMU_SHIFT, + ID_AA64DFR0_MTPMU_MASK); + return (mtpmu != 0U) && (mtpmu != MTPMU_NOT_IMPLEMENTED); } -static inline bool is_feat_mtpmu_supported(void) -{ - if (DISABLE_MTPMU == FEAT_STATE_DISABLED) { - return false; - } - - if (DISABLE_MTPMU == FEAT_STATE_ALWAYS) { - return true; - } - - unsigned int mtpmu = read_feat_mtpmu_id_field(); - - return (mtpmu != 0U) && (mtpmu != ID_AA64DFR0_MTPMU_DISABLED); -} +CREATE_FEATURE_SUPPORTED(feat_mtpmu, is_feat_mtpmu_present, DISABLE_MTPMU) #endif /* ARCH_FEATURES_H */ diff --git a/include/arch/aarch64/arch_helpers.h b/include/arch/aarch64/arch_helpers.h index 6fdc7e8a..119c4289 100644 --- a/include/arch/aarch64/arch_helpers.h +++ b/include/arch/aarch64/arch_helpers.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013-2023, Arm Limited and Contributors. All rights reserved. + * Copyright (c) 2013-2024, Arm Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -13,6 +13,7 @@ #include <string.h> #include <arch.h> +#include <lib/extensions/sysreg128.h> /********************************************************************** * Macros which create inline functions to read or write CPU system @@ -241,6 +242,7 @@ DEFINE_SYSOP_PARAM_FUNC(xpaci) void flush_dcache_range(uintptr_t addr, size_t size); void flush_dcache_to_popa_range(uintptr_t addr, size_t size); +void flush_dcache_to_popa_range_mte2(uintptr_t addr, size_t size); void clean_dcache_range(uintptr_t addr, size_t size); void inv_dcache_range(uintptr_t addr, size_t size); bool is_dcache_enabled(void); @@ -262,7 +264,12 @@ void disable_mpu_icache_el2(void); #define write_daifclr(val) SYSREG_WRITE_CONST(daifclr, val) #define write_daifset(val) SYSREG_WRITE_CONST(daifset, val) +#if ENABLE_FEAT_D128 +DECLARE_SYSREG128_RW_FUNCS(par_el1) +#else DEFINE_SYSREG_RW_FUNCS(par_el1) +#endif + DEFINE_IDREG_READ_FUNC(id_pfr1_el1) DEFINE_IDREG_READ_FUNC(id_aa64isar0_el1) DEFINE_IDREG_READ_FUNC(id_aa64isar1_el1) @@ -271,6 +278,7 @@ DEFINE_IDREG_READ_FUNC(id_aa64pfr0_el1) DEFINE_IDREG_READ_FUNC(id_aa64pfr1_el1) DEFINE_RENAME_IDREG_READ_FUNC(id_aa64pfr2_el1, ID_AA64PFR2_EL1) DEFINE_IDREG_READ_FUNC(id_aa64dfr0_el1) +DEFINE_IDREG_READ_FUNC(id_aa64dfr1_el1) DEFINE_IDREG_READ_FUNC(id_afr0_el1) DEFINE_SYSREG_READ_FUNC(CurrentEl) DEFINE_SYSREG_READ_FUNC(ctr_el0) @@ -282,6 +290,7 @@ DEFINE_SYSREG_RW_FUNCS(elr_el1) DEFINE_SYSREG_RW_FUNCS(elr_el2) DEFINE_SYSREG_RW_FUNCS(elr_el3) DEFINE_SYSREG_RW_FUNCS(mdccsr_el0) +DEFINE_SYSREG_RW_FUNCS(mdccint_el1) DEFINE_SYSREG_RW_FUNCS(dbgdtrrx_el0) DEFINE_SYSREG_RW_FUNCS(dbgdtrtx_el0) DEFINE_SYSREG_RW_FUNCS(sp_el1) @@ -440,13 +449,21 @@ DEFINE_SYSREG_RW_FUNCS(tcr_el1) DEFINE_SYSREG_RW_FUNCS(tcr_el2) DEFINE_SYSREG_RW_FUNCS(tcr_el3) +#if ENABLE_FEAT_D128 +DECLARE_SYSREG128_RW_FUNCS(ttbr0_el1) +DECLARE_SYSREG128_RW_FUNCS(ttbr1_el1) +DECLARE_SYSREG128_RW_FUNCS(ttbr0_el2) +DECLARE_SYSREG128_RW_FUNCS(ttbr1_el2) +DECLARE_SYSREG128_RW_FUNCS(vttbr_el2) +#else DEFINE_SYSREG_RW_FUNCS(ttbr0_el1) -DEFINE_SYSREG_RW_FUNCS(ttbr0_el2) -DEFINE_SYSREG_RW_FUNCS(ttbr0_el3) - DEFINE_SYSREG_RW_FUNCS(ttbr1_el1) - +DEFINE_SYSREG_RW_FUNCS(ttbr0_el2) +DEFINE_RENAME_SYSREG_RW_FUNCS(ttbr1_el2, TTBR1_EL2) DEFINE_SYSREG_RW_FUNCS(vttbr_el2) +#endif + +DEFINE_SYSREG_RW_FUNCS(ttbr0_el3) DEFINE_SYSREG_RW_FUNCS(cptr_el2) DEFINE_SYSREG_RW_FUNCS(cptr_el3) @@ -464,6 +481,9 @@ DEFINE_SYSREG_RW_FUNCS(cntp_tval_el0) DEFINE_SYSREG_RW_FUNCS(cntp_cval_el0) DEFINE_SYSREG_READ_FUNC(cntpct_el0) DEFINE_SYSREG_RW_FUNCS(cnthctl_el2) +DEFINE_SYSREG_RW_FUNCS(cntv_ctl_el0) +DEFINE_SYSREG_RW_FUNCS(cntv_cval_el0) +DEFINE_SYSREG_RW_FUNCS(cntkctl_el1) DEFINE_SYSREG_RW_FUNCS(vtcr_el2) @@ -480,6 +500,9 @@ DEFINE_SYSREG_RW_FUNCS(vtcr_el2) #define clr_cntp_ctl_enable(x) ((x) &= ~(U(1) << CNTP_CTL_ENABLE_SHIFT)) #define clr_cntp_ctl_imask(x) ((x) &= ~(U(1) << CNTP_CTL_IMASK_SHIFT)) +DEFINE_SYSREG_RW_FUNCS(tpidr_el0) +DEFINE_SYSREG_RW_FUNCS(tpidr_el1) +DEFINE_SYSREG_RW_FUNCS(tpidr_el2) DEFINE_SYSREG_RW_FUNCS(tpidr_el3) DEFINE_SYSREG_RW_FUNCS(cntvoff_el2) @@ -489,18 +512,29 @@ DEFINE_SYSREG_RW_FUNCS(vmpidr_el2) DEFINE_SYSREG_RW_FUNCS(hacr_el2) DEFINE_SYSREG_RW_FUNCS(hpfar_el2) -DEFINE_SYSREG_RW_FUNCS(tpidr_el2) + DEFINE_SYSREG_RW_FUNCS(dbgvcr32_el2) DEFINE_RENAME_SYSREG_RW_FUNCS(ich_hcr_el2, ICH_HCR_EL2) DEFINE_RENAME_SYSREG_RW_FUNCS(ich_vmcr_el2, ICH_VMCR_EL2) DEFINE_SYSREG_READ_FUNC(isr_el1) +DEFINE_SYSREG_RW_FUNCS(mdscr_el1) DEFINE_SYSREG_RW_FUNCS(mdcr_el2) DEFINE_SYSREG_RW_FUNCS(mdcr_el3) DEFINE_SYSREG_RW_FUNCS(hstr_el2) DEFINE_SYSREG_RW_FUNCS(pmcr_el0) +DEFINE_SYSREG_RW_FUNCS(csselr_el1) +DEFINE_SYSREG_RW_FUNCS(tpidrro_el0) +DEFINE_SYSREG_RW_FUNCS(contextidr_el1) +DEFINE_SYSREG_RW_FUNCS(spsr_abt) +DEFINE_SYSREG_RW_FUNCS(spsr_und) +DEFINE_SYSREG_RW_FUNCS(spsr_irq) +DEFINE_SYSREG_RW_FUNCS(spsr_fiq) +DEFINE_SYSREG_RW_FUNCS(dacr32_el2) +DEFINE_SYSREG_RW_FUNCS(ifsr32_el2) + /* GICv3 System Registers */ DEFINE_RENAME_SYSREG_RW_FUNCS(icc_sre_el1, ICC_SRE_EL1) @@ -549,15 +583,17 @@ DEFINE_RENAME_SYSREG_READ_FUNC(erxmisc0_el1, ERXMISC0_EL1) DEFINE_RENAME_SYSREG_READ_FUNC(erxmisc1_el1, ERXMISC1_EL1) DEFINE_RENAME_SYSREG_RW_FUNCS(scxtnum_el2, SCXTNUM_EL2) +DEFINE_RENAME_SYSREG_RW_FUNCS(scxtnum_el1, SCXTNUM_EL1) +DEFINE_RENAME_SYSREG_RW_FUNCS(scxtnum_el0, SCXTNUM_EL0) /* Armv8.1 VHE Registers */ DEFINE_RENAME_SYSREG_RW_FUNCS(contextidr_el2, CONTEXTIDR_EL2) -DEFINE_RENAME_SYSREG_RW_FUNCS(ttbr1_el2, TTBR1_EL2) /* Armv8.2 ID Registers */ DEFINE_RENAME_IDREG_READ_FUNC(id_aa64mmfr2_el1, ID_AA64MMFR2_EL1) /* Armv8.2 RAS Registers */ +DEFINE_RENAME_SYSREG_RW_FUNCS(disr_el1, DISR_EL1) DEFINE_RENAME_SYSREG_RW_FUNCS(vdisr_el2, VDISR_EL2) DEFINE_RENAME_SYSREG_RW_FUNCS(vsesr_el2, VSESR_EL2) @@ -585,6 +621,7 @@ DEFINE_RENAME_SYSREG_RW_FUNCS(dit, DIT) /* Armv8.4 FEAT_TRF Register */ DEFINE_RENAME_SYSREG_RW_FUNCS(trfcr_el2, TRFCR_EL2) +DEFINE_RENAME_SYSREG_RW_FUNCS(trfcr_el1, TRFCR_EL1) DEFINE_RENAME_SYSREG_RW_FUNCS(vncr_el2, VNCR_EL2) /* Armv8.5 MTE Registers */ @@ -615,23 +652,63 @@ DEFINE_RENAME_SYSREG_RW_FUNCS(hcrx_el2, HCRX_EL2) /* Armv8.9 system registers */ DEFINE_RENAME_IDREG_READ_FUNC(id_aa64mmfr3_el1, ID_AA64MMFR3_EL1) +/* Armv8.9 FEAT_FGT2 Registers */ +DEFINE_RENAME_SYSREG_RW_FUNCS(hdfgrtr2_el2, HDFGRTR2_EL2) +DEFINE_RENAME_SYSREG_RW_FUNCS(hdfgwtr2_el2, HDFGWTR2_EL2) +DEFINE_RENAME_SYSREG_RW_FUNCS(hfgitr2_el2, HFGITR2_EL2) +DEFINE_RENAME_SYSREG_RW_FUNCS(hfgrtr2_el2, HFGRTR2_EL2) +DEFINE_RENAME_SYSREG_RW_FUNCS(hfgwtr2_el2, HFGWTR2_EL2) + /* FEAT_TCR2 Register */ +DEFINE_RENAME_SYSREG_RW_FUNCS(tcr2_el1, TCR2_EL1) DEFINE_RENAME_SYSREG_RW_FUNCS(tcr2_el2, TCR2_EL2) /* FEAT_SxPIE Registers */ +DEFINE_RENAME_SYSREG_RW_FUNCS(pire0_el1, PIRE0_EL1) DEFINE_RENAME_SYSREG_RW_FUNCS(pire0_el2, PIRE0_EL2) +DEFINE_RENAME_SYSREG_RW_FUNCS(pir_el1, PIR_EL1) DEFINE_RENAME_SYSREG_RW_FUNCS(pir_el2, PIR_EL2) DEFINE_RENAME_SYSREG_RW_FUNCS(s2pir_el2, S2PIR_EL2) /* FEAT_SxPOE Registers */ +DEFINE_RENAME_SYSREG_RW_FUNCS(por_el1, POR_EL1) DEFINE_RENAME_SYSREG_RW_FUNCS(por_el2, POR_EL2) +DEFINE_RENAME_SYSREG_RW_FUNCS(s2por_el1, S2POR_EL1) /* FEAT_GCS Registers */ DEFINE_RENAME_SYSREG_RW_FUNCS(gcscr_el2, GCSCR_EL2) DEFINE_RENAME_SYSREG_RW_FUNCS(gcspr_el2, GCSPR_EL2) +DEFINE_RENAME_SYSREG_RW_FUNCS(gcscr_el1, GCSCR_EL1) +DEFINE_RENAME_SYSREG_RW_FUNCS(gcscre0_el1, GCSCRE0_EL1) +DEFINE_RENAME_SYSREG_RW_FUNCS(gcspr_el1, GCSPR_EL1) +DEFINE_RENAME_SYSREG_RW_FUNCS(gcspr_el0, GCSPR_EL0) + +/* FEAT_THE Registers */ +#if ENABLE_FEAT_D128 +DECLARE_SYSREG128_RW_FUNCS(rcwmask_el1) +DECLARE_SYSREG128_RW_FUNCS(rcwsmask_el1) +#else +DEFINE_RENAME_SYSREG_RW_FUNCS(rcwmask_el1, RCWMASK_EL1) +DEFINE_RENAME_SYSREG_RW_FUNCS(rcwsmask_el1, RCWSMASK_EL1) +#endif + +/* FEAT_SCTLR2 Registers */ +DEFINE_RENAME_SYSREG_RW_FUNCS(sctlr2_el1, SCTLR2_EL1) +DEFINE_RENAME_SYSREG_RW_FUNCS(sctlr2_el2, SCTLR2_EL2) -/* DynamIQ Shared Unit power management */ +/* FEAT_LS64_ACCDATA Registers */ +DEFINE_RENAME_SYSREG_RW_FUNCS(accdata_el1, ACCDATA_EL1) + +/* DynamIQ Control registers */ DEFINE_RENAME_SYSREG_RW_FUNCS(clusterpwrdn_el1, CLUSTERPWRDN_EL1) +DEFINE_RENAME_SYSREG_RW_FUNCS(clusterpmcr_el1, CLUSTERPMCR_EL1) +DEFINE_RENAME_SYSREG_RW_FUNCS(clusterpmcntenset_el1, CLUSTERPMCNTENSET_EL1) +DEFINE_RENAME_SYSREG_RW_FUNCS(clusterpmccntr_el1, CLUSTERPMCCNTR_EL1) +DEFINE_RENAME_SYSREG_RW_FUNCS(clusterpmovsset_el1, CLUSTERPMOVSSET_EL1) +DEFINE_RENAME_SYSREG_RW_FUNCS(clusterpmovsclr_el1, CLUSTERPMOVSCLR_EL1) +DEFINE_RENAME_SYSREG_RW_FUNCS(clusterpmselr_el1, CLUSTERPMSELR_EL1) +DEFINE_RENAME_SYSREG_RW_FUNCS(clusterpmxevcntr_el1, CLUSTERPMXEVCNTR_EL1) +DEFINE_RENAME_SYSREG_RW_FUNCS(clusterpmxevtyper_el1, CLUSTERPMXEVTYPER_EL1) /* CPU Power/Performance Management registers */ DEFINE_RENAME_SYSREG_RW_FUNCS(cpuppmcr_el3, CPUPPMCR_EL3) @@ -685,24 +762,86 @@ static inline uint64_t el_implemented(unsigned int el) } /* - * TLBIPAALLOS instruction - * (TLB Inivalidate GPT Information by PA, - * All Entries, Outer Shareable) + * TLBI PAALLOS instruction + * (TLB Invalidate GPT Information by PA, All Entries, Outer Shareable) */ static inline void tlbipaallos(void) { - __asm__("SYS #6,c8,c1,#4"); + __asm__("sys #6, c8, c1, #4"); } /* - * Invalidate TLBs of GPT entries by Physical address, last level. + * TLBI RPALOS instructions + * (TLB Range Invalidate GPT Information by PA, Last level, Outer Shareable) * - * @pa: the starting address for the range - * of invalidation - * @size: size of the range of invalidation + * command SIZE, bits [47:44] field: + * 0b0000 4KB + * 0b0001 16KB + * 0b0010 64KB + * 0b0011 2MB + * 0b0100 32MB + * 0b0101 512MB + * 0b0110 1GB + * 0b0111 16GB + * 0b1000 64GB + * 0b1001 512GB */ -void gpt_tlbi_by_pa_ll(uint64_t pa, size_t size); +#define TLBI_SZ_4K 0UL +#define TLBI_SZ_16K 1UL +#define TLBI_SZ_64K 2UL +#define TLBI_SZ_2M 3UL +#define TLBI_SZ_32M 4UL +#define TLBI_SZ_512M 5UL +#define TLBI_SZ_1G 6UL +#define TLBI_SZ_16G 7UL +#define TLBI_SZ_64G 8UL +#define TLBI_SZ_512G 9UL + +#define TLBI_ADDR_SHIFT U(12) +#define TLBI_SIZE_SHIFT U(44) + +#define TLBIRPALOS(_addr, _size) \ +{ \ + u_register_t arg = ((_addr) >> TLBI_ADDR_SHIFT) | \ + ((_size) << TLBI_SIZE_SHIFT); \ + __asm__("sys #6, c8, c4, #7, %0" : : "r" (arg)); \ +} +/* Note: addr must be aligned to 4KB */ +static inline void tlbirpalos_4k(uintptr_t addr) +{ + TLBIRPALOS(addr, TLBI_SZ_4K); +} + +/* Note: addr must be aligned to 16KB */ +static inline void tlbirpalos_16k(uintptr_t addr) +{ + TLBIRPALOS(addr, TLBI_SZ_16K); +} + +/* Note: addr must be aligned to 64KB */ +static inline void tlbirpalos_64k(uintptr_t addr) +{ + TLBIRPALOS(addr, TLBI_SZ_64K); +} + +/* Note: addr must be aligned to 2MB */ +static inline void tlbirpalos_2m(uintptr_t addr) +{ + TLBIRPALOS(addr, TLBI_SZ_2M); +} + +/* Note: addr must be aligned to 32MB */ +static inline void tlbirpalos_32m(uintptr_t addr) +{ + TLBIRPALOS(addr, TLBI_SZ_32M); +} + +/* Note: addr must be aligned to 512MB */ +static inline void tlbirpalos_512m(uintptr_t addr) +{ + TLBIRPALOS(addr, TLBI_SZ_512M); +} /* Previously defined accessor functions with incomplete register names */ @@ -723,8 +862,32 @@ void gpt_tlbi_by_pa_ll(uint64_t pa, size_t size); #define read_cpacr() read_cpacr_el1() #define write_cpacr(_v) write_cpacr_el1(_v) -#define read_clusterpwrdn() read_clusterpwrdn_el1() -#define write_clusterpwrdn(_v) write_clusterpwrdn_el1(_v) +#define read_clusterpwrdn() read_clusterpwrdn_el1() +#define write_clusterpwrdn(_v) write_clusterpwrdn_el1(_v) + +#define read_clusterpmcr() read_clusterpmcr_el1() +#define write_clusterpmcr(_v) write_clusterpmcr_el1(_v) + +#define read_clusterpmcntenset() read_clusterpmcntenset_el1() +#define write_clusterpmcntenset(_v) write_clusterpmcntenset_el1(_v) + +#define read_clusterpmccntr() read_clusterpmccntr_el1() +#define write_clusterpmccntr(_v) write_clusterpmccntr_el1(_v) + +#define read_clusterpmovsset() read_clusterpmovsset_el1() +#define write_clusterpmovsset(_v) write_clusterpmovsset_el1(_v) + +#define read_clusterpmovsclr() read_clusterpmovsclr_el1() +#define write_clusterpmovsclr(_v) write_clusterpmovsclr_el1(_v) + +#define read_clusterpmselr() read_clusterpmselr_el1() +#define write_clusterpmselr(_v) write_clusterpmselr_el1(_v) + +#define read_clusterpmxevcntr() read_clusterpmxevcntr_el1() +#define write_clusterpmxevcntr(_v) write_clusterpmxevcntr_el1(_v) + +#define read_clusterpmxevtyper() read_clusterpmxevtyper_el1() +#define write_clusterpmxevtyper(_v) write_clusterpmxevtyper_el1(_v) #if ERRATA_SPECULATIVE_AT /* diff --git a/include/arch/aarch64/asm_macros.S b/include/arch/aarch64/asm_macros.S index d09ad0f0..ec2acd56 100644 --- a/include/arch/aarch64/asm_macros.S +++ b/include/arch/aarch64/asm_macros.S @@ -317,4 +317,12 @@ #endif .endm + /* + * Helper macro to instruction adr <reg>, <symbol> where <symbol> is + * within the range +/- 4 GB. + */ + .macro adr_l, dst, sym + adrp \dst, \sym + add \dst, \dst, :lo12:\sym + .endm #endif /* ASM_MACROS_S */ diff --git a/include/arch/aarch64/el2_common_macros.S b/include/arch/aarch64/el2_common_macros.S index 9609c0d8..b9b0e3db 100644 --- a/include/arch/aarch64/el2_common_macros.S +++ b/include/arch/aarch64/el2_common_macros.S @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021-2023, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2021-2024, ARM Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -157,7 +157,7 @@ */ mrs x0, id_aa64pfr0_el1 ubfx x0, x0, #ID_AA64PFR0_DIT_SHIFT, #ID_AA64PFR0_DIT_LENGTH - cmp x0, #ID_AA64PFR0_DIT_SUPPORTED + cmp x0, #DIT_IMPLEMENTED bne 1f mov x0, #DIT_BIT msr DIT, x0 @@ -408,7 +408,7 @@ * ----------------------------------------------------------- */ isb - ldp x28, x29, [sp, #CTX_EL1_SYSREGS_OFFSET + CTX_SCTLR_EL1] + ldp x28, x29, [sp, #CTX_ERRATA_SPEC_AT_OFFSET + CTX_ERRATA_SPEC_AT_SCTLR_EL1] msr sctlr_el1, x28 isb msr tcr_el1, x29 diff --git a/include/arch/aarch64/el3_common_macros.S b/include/arch/aarch64/el3_common_macros.S index a78837f2..204625ce 100644 --- a/include/arch/aarch64/el3_common_macros.S +++ b/include/arch/aarch64/el3_common_macros.S @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015-2023, Arm Limited and Contributors. All rights reserved. + * Copyright (c) 2015-2024, Arm Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -59,47 +59,29 @@ * zero here but are updated ahead of transitioning to a lower EL in the * function cm_init_context_common(). * - * SCR_EL3.SIF: Set to one to disable instruction fetches from - * Non-secure memory. + * SCR_EL3.EEL2: Set to one if S-EL2 is present and enabled. * - * SCR_EL3.EA: Set to one to route External Aborts and SError Interrupts - * to EL3 when executing at any EL. + * NOTE: Modifying EEL2 bit along with EA bit ensures that we mitigate + * against ERRATA_V2_3099206. * --------------------------------------------------------------------- */ - mov_imm x0, (SCR_RESET_VAL | SCR_EA_BIT | SCR_SIF_BIT) + mov_imm x0, SCR_RESET_VAL +#if IMAGE_BL31 && defined(SPD_spmd) && SPMD_SPM_AT_SEL2 + mrs x1, id_aa64pfr0_el1 + and x1, x1, #(ID_AA64PFR0_SEL2_MASK << ID_AA64PFR0_SEL2_SHIFT) + cbz x1, 1f + orr x0, x0, #SCR_EEL2_BIT +#endif +1: msr scr_el3, x0 /* --------------------------------------------------------------------- * Initialise MDCR_EL3, setting all fields rather than relying on hw. * Some fields are architecturally UNKNOWN on reset. - * - * MDCR_EL3.SDD: Set to one to disable AArch64 Secure self-hosted debug. - * Debug exceptions, other than Breakpoint Instruction exceptions, are - * disabled from all ELs in Secure state. - * - * MDCR_EL3.SPD32: Set to 0b10 to disable AArch32 Secure self-hosted - * privileged debug from S-EL1. - * - * MDCR_EL3.TDOSA: Set to zero so that EL2 and EL2 System register - * access to the powerdown debug registers do not trap to EL3. - * - * MDCR_EL3.TDA: Set to zero to allow EL0, EL1 and EL2 access to the - * debug registers, other than those registers that are controlled by - * MDCR_EL3.TDOSA. */ - mov_imm x0, ((MDCR_EL3_RESET_VAL | MDCR_SDD_BIT | \ - MDCR_SPD32(MDCR_SPD32_DISABLE)) & \ - ~(MDCR_TDOSA_BIT | MDCR_TDA_BIT)) - + mov_imm x0, MDCR_EL3_RESET_VAL msr mdcr_el3, x0 - /* --------------------------------------------------------------------- - * Enable External Aborts and SError Interrupts now that the exception - * vectors have been setup. - * --------------------------------------------------------------------- - */ - msr daifclr, #DAIF_ABT_BIT - /* --------------------------------------------------------------------- * Initialise CPTR_EL3, setting all fields rather than relying on hw. * All fields are architecturally UNKNOWN on reset. @@ -108,28 +90,6 @@ mov_imm x0, CPTR_EL3_RESET_VAL msr cptr_el3, x0 - /* - * If Data Independent Timing (DIT) functionality is implemented, - * always enable DIT in EL3. - * First assert that the FEAT_DIT build flag matches the feature id - * register value for DIT. - */ -#if ENABLE_FEAT_DIT -#if ENABLE_ASSERTIONS || ENABLE_FEAT_DIT > 1 - mrs x0, id_aa64pfr0_el1 - ubfx x0, x0, #ID_AA64PFR0_DIT_SHIFT, #ID_AA64PFR0_DIT_LENGTH -#if ENABLE_FEAT_DIT > 1 - cbz x0, 1f -#else - cmp x0, #ID_AA64PFR0_DIT_SUPPORTED - ASM_ASSERT(eq) -#endif - -#endif /* ENABLE_ASSERTIONS */ - mov x0, #DIT_BIT - msr DIT, x0 -1: -#endif .endm /* ----------------------------------------------------------------------------- @@ -271,6 +231,12 @@ el3_arch_init_common + /* --------------------------------------------------------------------- + * Set the el3 execution context(i.e. root_context). + * --------------------------------------------------------------------- + */ + setup_el3_execution_context + .if \_secondary_cold_boot /* ------------------------------------------------------------- * Check if this is a primary or secondary CPU cold boot. @@ -374,7 +340,9 @@ #endif #if defined(IMAGE_BL1) || \ - (defined(IMAGE_BL2) && RESET_TO_BL2 && BL2_IN_XIP_MEM) + (defined(IMAGE_BL2) && RESET_TO_BL2 && BL2_IN_XIP_MEM) || \ + (defined(IMAGE_BL31) && SEPARATE_RWDATA_REGION) + adrp x0, __DATA_RAM_START__ add x0, x0, :lo12:__DATA_RAM_START__ adrp x1, __DATA_ROM_START__ @@ -438,7 +406,7 @@ * ----------------------------------------------------------- */ isb - ldp x28, x29, [sp, #CTX_EL1_SYSREGS_OFFSET + CTX_SCTLR_EL1] + ldp x28, x29, [sp, #CTX_ERRATA_SPEC_AT_OFFSET + CTX_ERRATA_SPEC_AT_SCTLR_EL1] msr sctlr_el1, x28 isb msr tcr_el1, x29 @@ -461,4 +429,68 @@ end: .endm +/*----------------------------------------------------------------------------- + * Helper macro to configure EL3 registers we care about, while executing + * at EL3/Root world. Root world has its own execution environment and + * needs to have its settings configured to be independent of other worlds. + * ----------------------------------------------------------------------------- + */ + .macro setup_el3_execution_context + + /* --------------------------------------------------------------------- + * The following registers need to be part of separate root context + * as their values are of importance during EL3 execution. + * Hence these registers are overwritten to their intital values, + * irrespective of whichever world they return from to ensure EL3 has a + * consistent execution context throughout the lifetime of TF-A. + * + * DAIF.A: Enable External Aborts and SError Interrupts at EL3. + * + * MDCR_EL3.SDD: Set to one to disable AArch64 Secure self-hosted debug. + * Debug exceptions, other than Breakpoint Instruction exceptions, are + * disabled from all ELs in Secure state. + * + * SCR_EL3.EA: Set to one to enable SError interrupts at EL3. + * + * SCR_EL3.SIF: Set to one to disable instruction fetches from + * Non-secure memory. + * + * PMCR_EL0.DP: Set to one so that the cycle counter, + * PMCCNTR_EL0 does not count when event counting is prohibited. + * Necessary on PMUv3 <= p7 where MDCR_EL3.{SCCD,MCCD} are not + * available. + * + * PSTATE.DIT: Set to one to enable the Data Independent Timing (DIT) + * functionality, if implemented in EL3. + * --------------------------------------------------------------------- + */ + msr daifclr, #DAIF_ABT_BIT + + mrs x15, mdcr_el3 + orr x15, x15, #MDCR_SDD_BIT + msr mdcr_el3, x15 + + mrs x15, scr_el3 + orr x15, x15, #SCR_EA_BIT + orr x15, x15, #SCR_SIF_BIT + msr scr_el3, x15 + + mrs x15, pmcr_el0 + orr x15, x15, #PMCR_EL0_DP_BIT + msr pmcr_el0, x15 + +#if ENABLE_FEAT_DIT +#if ENABLE_FEAT_DIT > 1 + mrs x15, id_aa64pfr0_el1 + ubfx x15, x15, #ID_AA64PFR0_DIT_SHIFT, #ID_AA64PFR0_DIT_LENGTH + cbz x15, 1f +#endif + mov x15, #DIT_BIT + msr DIT, x15 + 1: +#endif + + isb + .endm + #endif /* EL3_COMMON_MACROS_S */ diff --git a/include/bl1/bl1.h b/include/bl1/bl1.h index 7cd7e727..3ab88de9 100644 --- a/include/bl1/bl1.h +++ b/include/bl1/bl1.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015-2020, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2015-2024, Arm Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -94,9 +94,5 @@ CASSERT(FWU_NUM_SMC_CALLS == (FWU_SMC_FID_END - FWU_SMC_FID_START + 1), assert_FWU_NUM_SMC_CALLS_mismatch); -/* Utility functions */ -void bl1_calc_bl2_mem_layout(const meminfo_t *bl1_mem_layout, - meminfo_t *bl2_mem_layout); - #endif /* __ASSEMBLER__ */ #endif /* BL1_H */ diff --git a/include/bl31/bl31.h b/include/bl31/bl31.h index 1d58ef96..ed5374e0 100644 --- a/include/bl31/bl31.h +++ b/include/bl31/bl31.h @@ -22,6 +22,5 @@ void bl31_register_bl32_init(int32_t (*func)(void)); void bl31_register_rmm_init(int32_t (*func)(void)); void bl31_warm_entrypoint(void); void bl31_main(void); -void bl31_lib_init(void); #endif /* BL31_H */ diff --git a/include/bl31/interrupt_mgmt.h b/include/bl31/interrupt_mgmt.h index 21af112a..8b9dfb64 100644 --- a/include/bl31/interrupt_mgmt.h +++ b/include/bl31/interrupt_mgmt.h @@ -107,10 +107,10 @@ static inline int32_t validate_ns_interrupt_rm(uint32_t x) static inline int32_t validate_el3_interrupt_rm(uint32_t x) { -#if EL3_EXCEPTION_HANDLING && !(defined(SPD_spmd) && (SPMD_SPM_AT_SEL2 == 1)) +#if EL3_EXCEPTION_HANDLING && SPM_MM /* * With EL3 exception handling, EL3 interrupts are always routed to EL3 - * from both Secure and Non-secure, when the SPMC does not live in S-EL2. + * from Non-secure and from secure only if SPM_MM is present. * Therefore INTR_EL3_VALID_RM1 is the only valid routing model. */ if (x == INTR_EL3_VALID_RM1) diff --git a/include/bl31/sync_handle.h b/include/bl31/sync_handle.h index 1ac4f98c..394252b7 100644 --- a/include/bl31/sync_handle.h +++ b/include/bl31/sync_handle.h @@ -55,6 +55,11 @@ static inline bool is_sysreg_iss_write(uint64_t esr) */ int handle_sysreg_trap(uint64_t esr_el3, cpu_context_t *ctx); +/* Handler for injecting UNDEF exception to lower EL */ +void inject_undef64(cpu_context_t *ctx); + +u_register_t create_spsr(u_register_t old_spsr, unsigned int target_el); + /* Prototypes for system register emulation handlers provided by platforms. */ int plat_handle_impdef_trap(uint64_t esr_el3, cpu_context_t *ctx); int plat_handle_rng_trap(uint64_t esr_el3, cpu_context_t *ctx); diff --git a/include/bl32/tsp/tsp.h b/include/bl32/tsp/tsp.h index 285bfbe2..a63abf12 100644 --- a/include/bl32/tsp/tsp.h +++ b/include/bl32/tsp/tsp.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013-2022, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2013-2024, Arm Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -36,12 +36,13 @@ * Identifiers for various TSP services. Corresponding function IDs (whether * fast or yielding) are generated by macros defined below */ -#define TSP_ADD 0x2000 -#define TSP_SUB 0x2001 -#define TSP_MUL 0x2002 -#define TSP_DIV 0x2003 +#define TSP_ADD 0x2000 +#define TSP_SUB 0x2001 +#define TSP_MUL 0x2002 +#define TSP_DIV 0x2003 #define TSP_HANDLE_SEL1_INTR_AND_RETURN 0x2004 -#define TSP_CHECK_DIT 0x2005 +#define TSP_CHECK_DIT 0x2005 +#define TSP_MODIFY_EL1_CTX 0x2006 /* * Identify a TSP service from function ID filtering the last 16 bits from the diff --git a/include/bl32/tsp/tsp_el1_context.h b/include/bl32/tsp/tsp_el1_context.h new file mode 100644 index 00000000..64fde637 --- /dev/null +++ b/include/bl32/tsp/tsp_el1_context.h @@ -0,0 +1,16 @@ +/* + * Copyright (c) 2024, Arm Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef TSP_EL1_CONTEXT_H +#define TSP_EL1_CONTEXT_H + +#define TSP_CORRUPT_EL1_REGS 1 +#define TSP_RESTORE_EL1_REGS 0 + +/* Public helper function to handle EL1 ctx registers at S-EL1(TSP) */ +void modify_el1_ctx_regs(const bool modify_option); + +#endif /* TSP_EL1_CONTEXT_H */ diff --git a/include/common/bl_common.h b/include/common/bl_common.h index 4c8a17c5..647ae853 100644 --- a/include/common/bl_common.h +++ b/include/common/bl_common.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013-2022, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2013-2024, Arm Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -180,8 +180,6 @@ int load_auth_image(unsigned int image_id, image_info_t *image_data); void dyn_disable_auth(void); #endif -extern const char build_message[]; -extern const char version_string[]; const char *get_version(void); void print_entry_point_info(const entry_point_info_t *ep_info); diff --git a/include/common/build_message.h b/include/common/build_message.h new file mode 100644 index 00000000..b7c2f724 --- /dev/null +++ b/include/common/build_message.h @@ -0,0 +1,14 @@ +/* + * Copyright (c) 2024, Arm Limited. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef BUILD_MESSAGE_H +#define BUILD_MESSAGE_H + +static const char build_message[] = "Built : " BUILD_MESSAGE_TIMESTAMP; +static const char build_version_string[] = BUILD_MESSAGE_VERSION_STRING; +static const char build_version[] = BUILD_MESSAGE_VERSION; + +#endif /* BUILD_MESSAGE_H */ diff --git a/include/common/debug.h b/include/common/debug.h index 5ea541da..0ddb4007 100644 --- a/include/common/debug.h +++ b/include/common/debug.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013-2023, Arm Limited and Contributors. All rights reserved. + * Copyright (c) 2013-2024, Arm Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -91,6 +91,12 @@ # define VERBOSE(...) no_tf_log(LOG_MARKER_VERBOSE __VA_ARGS__) #endif +#if EARLY_CONSOLE +#define EARLY_ERROR(...) ERROR(__VA_ARGS__) +#else /* !EARLY_CONSOLE */ +#define EARLY_ERROR(...) no_tf_log(LOG_MARKER_ERROR __VA_ARGS__) +#endif /* EARLY_CONSOLE */ + const char *get_el_str(unsigned int el); #if ENABLE_BACKTRACE diff --git a/include/common/fdt_wrappers.h b/include/common/fdt_wrappers.h index abbf9764..de08f1db 100644 --- a/include/common/fdt_wrappers.h +++ b/include/common/fdt_wrappers.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018-2023, Arm Limited and Contributors. All rights reserved. + * Copyright (c) 2018-2024, Arm Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -21,6 +21,8 @@ uint32_t fdt_read_uint32_default(const void *dtb, int node, const char *prop_name, uint32_t dflt_value); int fdt_read_uint64(const void *dtb, int node, const char *prop_name, uint64_t *value); +uint64_t fdt_read_uint64_default(const void *dtb, int node, + const char *prop_name, uint64_t dflt_value); int fdt_read_uint32_array(const void *dtb, int node, const char *prop_name, unsigned int cells, uint32_t *value); int fdtw_read_string(const void *dtb, int node, const char *prop, diff --git a/include/common/feat_detect.h b/include/common/feat_detect.h index 788dfb31..b85e1ce9 100644 --- a/include/common/feat_detect.h +++ b/include/common/feat_detect.h @@ -11,8 +11,9 @@ void detect_arch_features(void); /* Macro Definitions */ -#define FEAT_STATE_DISABLED 0 -#define FEAT_STATE_ALWAYS 1 -#define FEAT_STATE_CHECK 2 +#define FEAT_STATE_DISABLED 0 +#define FEAT_STATE_ALWAYS 1 +#define FEAT_STATE_CHECK 2 +#define FEAT_STATE_CHECK_ASYMMETRIC 3 #endif /* FEAT_DETECT_H */ diff --git a/include/common/par.h b/include/common/par.h new file mode 100644 index 00000000..c8d67a38 --- /dev/null +++ b/include/common/par.h @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2024, Arm Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef PAR_H +#define PAR_H + +#include<arch_features.h> +#include<lib/extensions/sysreg128.h> + +static inline uint64_t get_par_el1_pa(sysreg_t par) +{ + uint64_t pa = par & UINT64_MAX; + /* PA, bits [51:12] is Output address */ + uint64_t mask = PAR_ADDR_MASK; + +#if ENABLE_FEAT_D128 + /* If D128 is in use, the PA is in the upper 64-bit word of PAR_EL1 */ + if (is_feat_d128_supported() && (par & PAR_EL1_D128)) { + pa = (par >> 64) & UINT64_MAX; + /* PA, bits [55:12] is Output address */ + mask = PAR_D128_ADDR_MASK; + } +#endif + return pa & mask; +} + +#endif /* PAR_H */ diff --git a/include/common/sha_common_macros.h b/include/common/sha_common_macros.h new file mode 100644 index 00000000..a419488f --- /dev/null +++ b/include/common/sha_common_macros.h @@ -0,0 +1,19 @@ +/* + * Copyright (c) 2024, NVIDIA Corporation. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef SHA_COMMON_MACROS_H +#define SHA_COMMON_MACROS_H + +#define MD5_DIGEST_SIZE 16U +#define SHA1_DIGEST_SIZE 20U +#define SHA224_DIGEST_SIZE 28U +#define SHA256_DIGEST_SIZE 32U +#define SHA384_DIGEST_SIZE 48U +#define SHA512_224_DIGEST_SIZE 28U +#define SHA512_256_DIGEST_SIZE 32U +#define SHA512_DIGEST_SIZE 64U + +#endif /* SHA_COMMON_MACROS_H */ diff --git a/include/drivers/arm/css/css_mhu_doorbell.h b/include/drivers/arm/css/css_mhu_doorbell.h index 88302fd7..d6c1a2a8 100644 --- a/include/drivers/arm/css/css_mhu_doorbell.h +++ b/include/drivers/arm/css/css_mhu_doorbell.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2020, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2014-2024, Arm Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -22,6 +22,10 @@ #define SENDER_REG_STAT(_channel) (0x20 * (_channel)) #define SENDER_REG_SET(_channel) ((0x20 * (_channel)) + 0xC) +#define MHU_V3_PBX_PDBCW_PAGE_OFFSET UL(0x1000) +#define MHU_V3_SENDER_REG_SET(_channel) (MHU_V3_PBX_PDBCW_PAGE_OFFSET + \ + SENDER_REG_SET(_channel)) + /* Helper macro to ring doorbell */ #define MHU_RING_DOORBELL(addr, modify_mask, preserve_mask) do { \ uint32_t db = mmio_read_32(addr) & (preserve_mask); \ diff --git a/include/drivers/arm/css/dsu.h b/include/drivers/arm/css/dsu.h new file mode 100644 index 00000000..4d7822b5 --- /dev/null +++ b/include/drivers/arm/css/dsu.h @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2024, Arm Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef DSU_H +#define DSU_H + +#define PMCR_N_MAX 0x1f + +#define save_pmu_reg(state, reg) state->reg = read_##reg() + +#define restore_pmu_reg(context, reg) write_##reg(context->reg) + +typedef struct cluster_pmu_state{ + uint64_t clusterpmcr; + uint64_t clusterpmcntenset; + uint64_t clusterpmccntr; + uint64_t clusterpmovsset; + uint64_t clusterpmselr; + uint64_t clusterpmsevtyper; + uint64_t counter_val[PMCR_N_MAX]; + uint64_t counter_type[PMCR_N_MAX]; +} cluster_pmu_state_t; + +static inline unsigned int read_cluster_eventctr_num(void) +{ + return ((read_clusterpmcr() >> CLUSTERPMCR_N_SHIFT) & + CLUSTERPMCR_N_MASK); +} + + +void save_dsu_pmu_state(cluster_pmu_state_t *cluster_pmu_context); + +void restore_dsu_pmu_state(cluster_pmu_state_t *cluster_pmu_context); + +void cluster_on_dsu_pmu_context_restore(void); + +void cluster_off_dsu_pmu_context_save(void); + +#endif /* DSU_H */ diff --git a/include/drivers/arm/css/scmi.h b/include/drivers/arm/css/scmi.h index 356012bf..96e19246 100644 --- a/include/drivers/arm/css/scmi.h +++ b/include/drivers/arm/css/scmi.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2021, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2017-2024, Arm Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -123,6 +123,8 @@ typedef struct scmi_channel_plat_info { void (*ring_doorbell)(struct scmi_channel_plat_info *plat_info); /* cookie is unused now. But added for future enhancements. */ void *cookie; + /* Delay in micro-seconds while polling the channel status. */ + uint32_t delay; } scmi_channel_plat_info_t; diff --git a/include/drivers/arm/css/sds.h b/include/drivers/arm/css/sds.h index db4cbaaf..ab957751 100644 --- a/include/drivers/arm/css/sds.h +++ b/include/drivers/arm/css/sds.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2017-2024, Arm Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -79,12 +79,33 @@ typedef enum { SDS_ACCESS_MODE_CACHED, } sds_access_mode_t; -int sds_init(void); -int sds_struct_exists(unsigned int structure_id); -int sds_struct_read(uint32_t structure_id, unsigned int fld_off, void *data, - size_t size, sds_access_mode_t mode); -int sds_struct_write(uint32_t structure_id, unsigned int fld_off, void *data, - size_t size, sds_access_mode_t mode); +/* + * The following structure describes a SDS memory region. Its items are used + * to track and maintain the state of the memory region reserved for usage + * by the SDS framework. + * + * The base address of the SDS memory region is platform specific. The + * SDS description structure must already contain the address when it is + * returned by the plat_sds_get_regions() platform API during SDS region + * initialization. + * The size of the SDS memory region is dynamically discovered during the + * initialization of the region and written into the 'size' item of the + * SDS description structure. + */ +typedef struct { + uintptr_t base; /* Pointer to the base of the SDS memory region */ + size_t size; /* Size of the SDS memory region in bytes */ +} sds_region_desc_t; + +/* API to get the platform specific SDS region description(s) */ +sds_region_desc_t *plat_sds_get_regions(unsigned int *region_count); + +int sds_init(unsigned int region_id); +int sds_struct_exists(unsigned int region_id, unsigned int structure_id); +int sds_struct_read(unsigned int region_id, uint32_t structure_id, + unsigned int fld_off, void *data, size_t size, sds_access_mode_t mode); +int sds_struct_write(unsigned int region_id, uint32_t structure_id, + unsigned int fld_off, void *data, size_t size, sds_access_mode_t mode); #endif /*__ASSEMBLER__ */ #endif /* SDS_H */ diff --git a/include/drivers/arm/dcc.h b/include/drivers/arm/dcc.h index 072bed52..7f71932f 100644 --- a/include/drivers/arm/dcc.h +++ b/include/drivers/arm/dcc.h @@ -1,5 +1,6 @@ /* - * Copyright (c) 2021, Xilinx Inc. + * Copyright (c) 2021-2022, Xilinx Inc. + * Copyright (c) 2022-2024, Advanced Micro Devices, Inc. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -14,7 +15,7 @@ * Initialize a new dcc console instance and register it with the console * framework. */ -int console_dcc_register(void); -void console_dcc_unregister(void); +int console_dcc_register(console_t *console); +void console_dcc_unregister(console_t *console); -#endif /* DCC */ +#endif /* DCC_H */ diff --git a/include/drivers/arm/fvp/fvp_cpu_pwr.h b/include/drivers/arm/fvp/fvp_cpu_pwr.h new file mode 100644 index 00000000..488be18c --- /dev/null +++ b/include/drivers/arm/fvp/fvp_cpu_pwr.h @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2024, ARM Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef FVP_CPU_PWR_H +#define FVP_CPU_PWR_H + +#ifndef __ASSEMBLER__ +#include <stdbool.h> +#include <stdint.h> + +#if __aarch64__ +bool check_cpupwrctrl_el1_is_available(void); +#endif /* __aarch64__ */ +#endif /* __ASSEMBLER__ */ + +/******************************************************************************* + * CPU Power Control register specific definitions + ******************************************************************************/ +#define CPUPWRCTLR_EL1 S3_0_C15_C2_7 +#define CPUPWRCTLR_EL1_CORE_PWRDN_BIT U(1) + +#endif /* FVP_CPU_PWR_H */ diff --git a/include/drivers/arm/gicv2.h b/include/drivers/arm/gicv2.h index bebd9cef..c7c441d7 100644 --- a/include/drivers/arm/gicv2.h +++ b/include/drivers/arm/gicv2.h @@ -51,7 +51,7 @@ #define SGIR_TGTLSTFLT_MASK U(0x3) #define SGIR_TGTLST_SHIFT 16 #define SGIR_TGTLST_MASK U(0xff) -#define SGIR_NSATT (U(0x1) << 16) +#define SGIR_NSATT (U(0x1) << 15) #define SGIR_INTID_MASK ULL(0xf) #define SGIR_TGT_SPECIFIC U(0) diff --git a/include/drivers/arm/gicv3.h b/include/drivers/arm/gicv3.h index cf6a7465..0e6137d1 100644 --- a/include/drivers/arm/gicv3.h +++ b/include/drivers/arm/gicv3.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015-2023, Arm Limited and Contributors. All rights reserved. + * Copyright (c) 2015-2024, Arm Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -150,6 +150,7 @@ /* GICD_TYPER shifts and masks */ #define TYPER_ESPI U(1 << 8) #define TYPER_DVIS U(1 << 18) +#define TYPER_LPIS U(1 << 17) #define TYPER_ESPI_RANGE_MASK U(0x1f) #define TYPER_ESPI_RANGE_SHIFT U(27) #define TYPER_ESPI_RANGE U(TYPER_ESPI_MASK << TYPER_ESPI_SHIFT) @@ -340,7 +341,7 @@ /* GITS_CTLR bit definitions */ #define GITS_CTLR_ENABLED_BIT BIT_32(0) -#define GITS_CTLR_QUIESCENT_BIT BIT_32(1) +#define GITS_CTLR_QUIESCENT_BIT BIT_32(31) #define GITS_TYPER_VSGI BIT_64(39) @@ -588,6 +589,7 @@ void gicv3_set_spi_routing(unsigned int id, unsigned int irm, void gicv3_set_interrupt_pending(unsigned int id, unsigned int proc_num); void gicv3_clear_interrupt_pending(unsigned int id, unsigned int proc_num); unsigned int gicv3_set_pmr(unsigned int mask); +unsigned int gicv3_deactivate_priority(unsigned int mask); void gicv3_get_component_prodid_rev(const uintptr_t gicd_base, unsigned int *gic_prod_id, diff --git a/include/drivers/arm/rss_comms.h b/include/drivers/arm/rse_comms.h similarity index 53% rename from include/drivers/arm/rss_comms.h rename to include/drivers/arm/rse_comms.h index b96c79f7..e4169a51 100644 --- a/include/drivers/arm/rss_comms.h +++ b/include/drivers/arm/rse_comms.h @@ -5,11 +5,11 @@ * */ -#ifndef RSS_COMMS_H -#define RSS_COMMS_H +#ifndef RSE_COMMS_H +#define RSE_COMMS_H #include <stdint.h> -int rss_comms_init(uintptr_t mhu_sender_base, uintptr_t mhu_receiver_base); +int rse_comms_init(uintptr_t mhu_sender_base, uintptr_t mhu_receiver_base); -#endif /* RSS_COMMS_H */ +#endif /* RSE_COMMS_H */ diff --git a/include/drivers/auth/mbedtls/mbedtls_config-2.h b/include/drivers/auth/mbedtls/mbedtls_config-2.h deleted file mode 100644 index 01e261a9..00000000 --- a/include/drivers/auth/mbedtls/mbedtls_config-2.h +++ /dev/null @@ -1,152 +0,0 @@ -/* - * Copyright (c) 2015-2022, Arm Limited. All rights reserved. - * - * SPDX-License-Identifier: BSD-3-Clause - */ -#ifndef MBEDTLS_CONFIG_H -#define MBEDTLS_CONFIG_H - -/* - * Key algorithms currently supported on mbed TLS libraries - */ -#define TF_MBEDTLS_RSA 1 -#define TF_MBEDTLS_ECDSA 2 -#define TF_MBEDTLS_RSA_AND_ECDSA 3 - -#define TF_MBEDTLS_USE_RSA (TF_MBEDTLS_KEY_ALG_ID == TF_MBEDTLS_RSA \ - || TF_MBEDTLS_KEY_ALG_ID == TF_MBEDTLS_RSA_AND_ECDSA) -#define TF_MBEDTLS_USE_ECDSA (TF_MBEDTLS_KEY_ALG_ID == TF_MBEDTLS_ECDSA \ - || TF_MBEDTLS_KEY_ALG_ID == TF_MBEDTLS_RSA_AND_ECDSA) - -/* - * Hash algorithms currently supported on mbed TLS libraries - */ -#define TF_MBEDTLS_SHA256 1 -#define TF_MBEDTLS_SHA384 2 -#define TF_MBEDTLS_SHA512 3 - -/* - * Configuration file to build mbed TLS with the required features for - * Trusted Boot - */ - -#define MBEDTLS_PLATFORM_MEMORY -#define MBEDTLS_PLATFORM_NO_STD_FUNCTIONS -/* Prevent mbed TLS from using snprintf so that it can use tf_snprintf. */ -#define MBEDTLS_PLATFORM_SNPRINTF_ALT - -#define MBEDTLS_PKCS1_V21 - -#define MBEDTLS_X509_ALLOW_UNSUPPORTED_CRITICAL_EXTENSION -#define MBEDTLS_X509_CHECK_KEY_USAGE -#define MBEDTLS_X509_CHECK_EXTENDED_KEY_USAGE - -#define MBEDTLS_ASN1_PARSE_C -#define MBEDTLS_ASN1_WRITE_C - -#define MBEDTLS_BASE64_C -#define MBEDTLS_BIGNUM_C - -#define MBEDTLS_ERROR_C -#define MBEDTLS_MD_C - -#define MBEDTLS_MEMORY_BUFFER_ALLOC_C -#define MBEDTLS_OID_C - -#define MBEDTLS_PK_C -#define MBEDTLS_PK_PARSE_C -#define MBEDTLS_PK_WRITE_C - -#define MBEDTLS_PLATFORM_C - -#if TF_MBEDTLS_USE_ECDSA -#define MBEDTLS_ECDSA_C -#define MBEDTLS_ECP_C -#define MBEDTLS_ECP_DP_SECP256R1_ENABLED -#define MBEDTLS_ECP_NO_INTERNAL_RNG -#endif -#if TF_MBEDTLS_USE_RSA -#define MBEDTLS_RSA_C -#define MBEDTLS_X509_RSASSA_PSS_SUPPORT -#endif - -#define MBEDTLS_SHA256_C - -/* - * If either Trusted Boot or Measured Boot require a stronger algorithm than - * SHA-256, pull in SHA-512 support. - */ -#if (TF_MBEDTLS_HASH_ALG_ID != TF_MBEDTLS_SHA256) /* TBB hash algo */ -#define MBEDTLS_SHA512_C -#else - /* TBB uses SHA-256, what about measured boot? */ -#if defined(TF_MBEDTLS_MBOOT_USE_SHA512) -#define MBEDTLS_SHA512_C -#endif -#endif - -#define MBEDTLS_VERSION_C - -#define MBEDTLS_X509_USE_C -#define MBEDTLS_X509_CRT_PARSE_C - -#if TF_MBEDTLS_USE_AES_GCM -#define MBEDTLS_AES_C -#define MBEDTLS_CIPHER_C -#define MBEDTLS_GCM_C -#endif - -/* MPI / BIGNUM options */ -#define MBEDTLS_MPI_WINDOW_SIZE 2 - -#if TF_MBEDTLS_USE_RSA -#if TF_MBEDTLS_KEY_SIZE <= 2048 -#define MBEDTLS_MPI_MAX_SIZE 256 -#else -#define MBEDTLS_MPI_MAX_SIZE 512 -#endif -#else -#define MBEDTLS_MPI_MAX_SIZE 256 -#endif - -/* Memory buffer allocator options */ -#define MBEDTLS_MEMORY_ALIGN_MULTIPLE 8 - -/* - * Prevent the use of 128-bit division which - * creates dependency on external libraries. - */ -#define MBEDTLS_NO_UDBL_DIVISION - -#ifndef __ASSEMBLER__ -/* System headers required to build mbed TLS with the current configuration */ -#include <stdlib.h> -#include <mbedtls/check_config.h> -#endif - -/* - * Determine Mbed TLS heap size - * 13312 = 13*1024 - * 11264 = 11*1024 - * 7168 = 7*1024 - */ -#if TF_MBEDTLS_USE_ECDSA -#define TF_MBEDTLS_HEAP_SIZE U(13312) -#elif TF_MBEDTLS_USE_RSA -#if TF_MBEDTLS_KEY_SIZE <= 2048 -#define TF_MBEDTLS_HEAP_SIZE U(7168) -#else -#define TF_MBEDTLS_HEAP_SIZE U(11264) -#endif -#endif - -/* - * Warn if errors from certain functions are ignored. - * - * The warnings are always enabled (where supported) for critical functions - * where ignoring the return value is almost always a bug. This macro extends - * the warnings to more functions. - */ -#define MBEDTLS_CHECK_RETURN_WARNING - -#endif /* MBEDTLS_CONFIG_H */ diff --git a/include/drivers/auth/mbedtls/mbedtls_config-3.h b/include/drivers/auth/mbedtls/mbedtls_config-3.h index 923fc546..6ed93978 100644 --- a/include/drivers/auth/mbedtls/mbedtls_config-3.h +++ b/include/drivers/auth/mbedtls/mbedtls_config-3.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023, Arm Limited. All rights reserved. + * Copyright (c) 2023-2024, Arm Limited. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -73,23 +73,17 @@ #define MBEDTLS_X509_RSASSA_PSS_SUPPORT #endif -/* The library does not currently support enabling SHA-256 without SHA-224. */ -#define MBEDTLS_SHA224_C -#define MBEDTLS_SHA256_C -/* - * If either Trusted Boot or Measured Boot require a stronger algorithm than - * SHA-256, pull in SHA-512 support. Library currently needs to have SHA_384 - * support when enabling SHA-512. - */ -#if (TF_MBEDTLS_HASH_ALG_ID != TF_MBEDTLS_SHA256) /* TBB hash algo */ -#define MBEDTLS_SHA384_C -#define MBEDTLS_SHA512_C -#else - /* TBB uses SHA-256, what about measured boot? */ -#if defined(TF_MBEDTLS_MBOOT_USE_SHA512) -#define MBEDTLS_SHA384_C -#define MBEDTLS_SHA512_C +/* Enable hash algorithms based on TBB or Measured Boot */ +#if (TF_MBEDTLS_HASH_ALG_ID == TF_MBEDTLS_SHA256) || defined(TF_MBEDTLS_MBOOT_USE_SHA256) + #define MBEDTLS_SHA256_C #endif + +#if (TF_MBEDTLS_HASH_ALG_ID == TF_MBEDTLS_SHA384) || defined(TF_MBEDTLS_MBOOT_USE_SHA384) + #define MBEDTLS_SHA384_C +#endif + +#if (TF_MBEDTLS_HASH_ALG_ID == TF_MBEDTLS_SHA512) || defined(TF_MBEDTLS_MBOOT_USE_SHA512) + #define MBEDTLS_SHA512_C #endif #define MBEDTLS_VERSION_C @@ -104,7 +98,9 @@ #endif /* MPI / BIGNUM options */ -#define MBEDTLS_MPI_WINDOW_SIZE 2 + +/* Note: Lower numbers trade longer execution time for less RAM allocation */ +#define MBEDTLS_MPI_WINDOW_SIZE 1 #if TF_MBEDTLS_USE_RSA #if TF_MBEDTLS_KEY_SIZE <= 2048 @@ -128,7 +124,6 @@ #ifndef __ASSEMBLER__ /* System headers required to build mbed TLS with the current configuration */ #include <stdlib.h> -#include <mbedtls/check_config.h> #endif /* diff --git a/include/drivers/auth/mbedtls/psa_mbedtls_config.h b/include/drivers/auth/mbedtls/psa_mbedtls_config.h index ad825f0a..1001d895 100644 --- a/include/drivers/auth/mbedtls/psa_mbedtls_config.h +++ b/include/drivers/auth/mbedtls/psa_mbedtls_config.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023, Arm Ltd. All rights reserved. + * Copyright (c) 2023-2024, Arm Ltd. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -10,6 +10,7 @@ #include "mbedtls_config-3.h" #define MBEDTLS_PSA_CRYPTO_C +#define MBEDTLS_PSA_ASSUME_EXCLUSIVE_BUFFERS /* * Using PSA crypto API requires an RNG right now. If we don't define the macro diff --git a/include/drivers/auth/tbbr_cot_common.h b/include/drivers/auth/tbbr_cot_common.h index b4f2d220..019ae176 100644 --- a/include/drivers/auth/tbbr_cot_common.h +++ b/include/drivers/auth/tbbr_cot_common.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020,2023, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2020-2024, Arm Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -21,10 +21,10 @@ extern auth_param_type_desc_t sig_alg; extern auth_param_type_desc_t raw_data; extern auth_param_type_desc_t tb_fw_hash; +extern auth_param_type_desc_t hw_config_hash; extern auth_param_type_desc_t tb_fw_config_hash; extern auth_param_type_desc_t fw_config_hash; extern const auth_img_desc_t trusted_boot_fw_cert; -extern const auth_img_desc_t hw_config; #endif /* TBBR_COT_COMMON_H */ diff --git a/include/drivers/cadence/cdns_nand.h b/include/drivers/cadence/cdns_nand.h index 64ba2676..f20627ba 100644 --- a/include/drivers/cadence/cdns_nand.h +++ b/include/drivers/cadence/cdns_nand.h @@ -198,6 +198,7 @@ typedef struct cnf_dev_info { #define CNF_OPR_WORK_MODE_RES 3 /* Mini controller common settings register field offsets */ +#define CNF_CMN_SETTINGS_OPR_MASK 0x00000003 #define CNF_CMN_SETTINGS_WR_WUP 20 #define CNF_CMN_SETTINGS_RD_WUP 16 #define CNF_CMN_SETTINGS_DEV16 8 diff --git a/include/drivers/cadence/cdns_sdmmc.h b/include/drivers/cadence/cdns_sdmmc.h index 64527253..f8d616ff 100644 --- a/include/drivers/cadence/cdns_sdmmc.h +++ b/include/drivers/cadence/cdns_sdmmc.h @@ -1,6 +1,7 @@ /* * Copyright (c) 2022, ARM Limited and Contributors. All rights reserved. * Copyright (c) 2022-2023, Intel Corporation. All rights reserved. + * Copyright (c) 2024, Altera Corporation. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -10,23 +11,26 @@ #include <drivers/cadence/cdns_combo_phy.h> #include <drivers/mmc.h> -#include "socfpga_plat_def.h" #if MMC_DEVICE_TYPE == 0 -#define CONFIG_DMA_ADDR_T_64BIT 0 +#define CONFIG_DMA_ADDR_T_64BIT 0 #endif -#define MMC_REG_BASE SOCFPGA_MMC_REG_BASE -#define COMBO_PHY_REG 0x0 -#define SDHC_EXTENDED_WR_MODE_MASK 0xFFFFFFF7 -#define SDHC_DLL_RESET_MASK 0x00000001 +#define MMC_REG_BASE SOCFPGA_MMC_REG_BASE +#define COMBO_PHY_REG 0x0 +#define SDHC_EXTENDED_WR_MODE_MASK 0xFFFFFFF7 +#define SDHC_DLL_RESET_MASK 0x00000001 +#define MMC_MAX_BLOCK_LEN 512U + /* HRS09 */ #define SDHC_PHY_SW_RESET BIT(0) -#define SDHC_PHY_INIT_COMPLETE BIT(1) -#define SDHC_EXTENDED_RD_MODE(x) ((x) << 2) +#define SDHC_PHY_INIT_COMPLETE BIT(1) +#define SDHC_EXTENDED_RD_MODE(x) ((x) << 2) #define EXTENDED_WR_MODE 3 -#define SDHC_EXTENDED_WR_MODE(x) ((x) << 3) -#define RDCMD_EN 15 +#define SDHC_EXTENDED_WR_MODE(x) ((x) << 3) +#define RDCMD_EN (3 << 15) +#define PHY_SW_RESET_EN (1 << 0) +#define PHY_INIT_COMPLETE_BIT (1 << 1) #define SDHC_RDCMD_EN(x) ((x) << 15) #define SDHC_RDDATA_EN(x) ((x) << 16) @@ -38,9 +42,9 @@ /* • 1111b - Reserved */ /* • 1110b - t_sdmclk*2(27+2) */ /* • 1101b - t_sdmclk*2(26+2) */ -#define READ_CLK 0xa << 16 -#define WRITE_CLK 0xe << 16 -#define DTC_VAL 0xE +#define READ_CLK 0xa << 16 +#define WRITE_CLK 0xe << 16 +#define DTC_VAL 0xE /* SRS00 */ /* System Address / Argument 2 / 32-bit block count @@ -49,18 +53,18 @@ * • SDMA system memory address * • Auto CMD23 Argument */ -#define SAAR (1) +#define SAAR (1) /* SRS01 */ /* Transfer Block Size * This field defines block size for block data transfers */ -#define BLOCK_SIZE 0 +#define BLOCK_SIZE 0 /* SDMA Buffer Boundary * System address boundary can be set for SDMA engine. */ -#define SDMA_BUF 7 << 12 +#define SDMA_BUF 7 << 12 /* Block Count For Current Transfer * To set the number of data blocks can be defined for next transfer @@ -68,93 +72,108 @@ #define BLK_COUNT_CT 16 /* SRS03 */ -#define CMD_START (U(1) << 31) +#define CMD_START (U(1) << 31) #define CMD_USE_HOLD_REG (1 << 29) #define CMD_UPDATE_CLK_ONLY (1 << 21) #define CMD_SEND_INIT (1 << 15) #define CMD_STOP_ABORT_CMD (4 << 22) #define CMD_RESUME_CMD (2 << 22) #define CMD_SUSPEND_CMD (1 << 22) -#define DATA_PRESENT (1 << 21) -#define CMD_IDX_CHK_ENABLE (1 << 20) -#define CMD_WRITE (0 << 4) -#define CMD_READ (1 << 4) +#define DATA_PRESENT (0x20) +#define CMD_IDX_CHK_ENABLE (0x10) +#define CMD_WRITE (0 << 4) +#define CMD_READ (1 << 4) #define MULTI_BLK_READ (1 << 5) -#define RESP_ERR (1 << 7) -#define CMD_CHECK_RESP_CRC (1 << 19) -#define RES_TYPE_SEL_48 (2 << 16) -#define RES_TYPE_SEL_136 (1 << 16) -#define RES_TYPE_SEL_48_B (3 << 16) -#define RES_TYPE_SEL_NO (0 << 16) -#define DMA_ENABLED (1 << 0) -#define BLK_CNT_EN (1 << 1) -#define AUTO_CMD_EN (2 << 2) -#define COM_IDX 24 -#define ERROR_INT (1 << 15) -#define INT_SBE (1 << 13) -#define INT_HLE (1 << 12) -#define INT_FRUN (1 << 11) -#define INT_DRT (1 << 9) -#define INT_RTO (1 << 8) -#define INT_DCRC (1 << 7) -#define INT_RCRC (1 << 6) -#define INT_RXDR (1 << 5) -#define INT_TXDR (1 << 4) -#define INT_DTO (1 << 3) +#define RESP_ERR (1 << 7) +#define CMD_CHECK_RESP_CRC (0x08) +#define RES_TYPE_SEL_48 (0x2) +#define RES_TYPE_SEL_136 (0x1) +#define RES_TYPE_SEL_48_B (0x3) +#define RES_TYPE_SEL_NO (0x3) +#define DMA_ENABLED (1 << 0) +#define BLK_CNT_EN (1 << 1) +#define AUTO_CMD_EN (2 << 2) +#define COM_IDX 24 +#define ERROR_INT (1 << 15) +#define INT_SBE (1 << 13) +#define INT_HLE (1 << 12) +#define INT_FRUN (1 << 11) +#define INT_DRT (1 << 9) +#define INT_RTO (1 << 8) +#define INT_DCRC (1 << 7) +#define INT_RCRC (1 << 6) +#define INT_RXDR (1 << 5) +#define INT_TXDR (1 << 4) +#define INT_DTO (1 << 3) #define INT_CMD_DONE (1 << 0) -#define TRAN_COMP (1 << 1) +#define TRAN_COMP (1 << 1) /* SRS09 */ #define STATUS_DATA_BUSY BIT(2) +#define CI 16 +#define CHECK_CARD BIT(CI) /* SRS10 */ +#define BIT1 (0 << 1) +#define BIT4 (1 << 1) +#define BIT8 (1 << 5) + /* LED Control * State of this bit directly drives led port of the host * in order to control the external LED diode * Default value 0 << 1 */ -#define LEDC BIT(0) -#define LEDC_OFF 0 << 1 +#define LEDC BIT(0) +#define LEDC_OFF (0 << 1) /* Data Transfer Width * Bit used to configure DAT bus width to 1 or 4 * Default value 1 << 1 */ -#define DT_WIDTH BIT(1) -#define DTW_4BIT 1 << 1 +#define DT_WIDTH BIT(1) +#define DTW_4BIT (1 << 1) /* Extended Data Transfer Width * This bit is to enable/disable 8-bit DAT bus width mode * Default value 1 << 5 */ -#define EDTW_8BIT 1 << 5 +#define EDTW_8BIT BIT(5) /* High Speed Enable * Selects operating mode to Default Speed (HSE=0) or High Speed (HSE=1) */ -#define HS_EN BIT(2) +#define HS_EN BIT(2) /* here 0 defines the 64 Kb size */ #define MAX_64KB_PAGE 0 -#define EMMC_DESC_SIZE (1<<20) - +#define EMMC_DESC_SIZE (1<<20) +#define DTCV_OFFSET (0x22E) +#define DTCV_VAL (0xE) +#define CICE_OFFSET (0x20E) +#define SRS_12_CC_EN (1 << 0) /* SRS11 */ /* Software Reset For All * When set to 1, the entire slot is reset * After completing the reset operation, SRFA bit is automatically cleared */ -#define SRFA BIT(24) +#define SRFA BIT(24) /* Software Reset For CMD Line * When set to 1, resets the logic related to the command generation and response checking */ -#define SRCMD BIT(25) +#define SRCMD BIT(25) /* Software Reset For DAT Line * When set to 1, resets the logic related to the data path, * including data buffers and the DMA logic */ -#define SRDAT BIT(26) +#define SRDAT BIT(26) + + +/* SRS12 */ +/* Error mask */ +#define SRS12_ERR_MASK 0xFFFF8000U +#define CDNS_CSD_BYTE_MASK 0x000000FFU /* SRS15 */ /* UHS Mode Select @@ -165,40 +184,43 @@ * • 011b - SDR104 * • 100b - DDR50 */ -#define SDR12_MODE 0 << 16 -#define SDR25_MODE 1 << 16 -#define SDR50_MODE 2 << 16 -#define SDR104_MODE 3 << 16 -#define DDR50_MODE 4 << 16 +#define SDR12_MODE 0 << 16 +#define SDR25_MODE 1 << 16 +#define SDR50_MODE 2 << 16 +#define SDR104_MODE 3 << 16 +#define DDR50_MODE 4 << 16 /* 1.8V Signaling Enable * • 0 - for Default Speed, High Speed mode * • 1 - for UHS-I mode */ -#define V18SE BIT(19) +#define V18SE BIT(19) /* CMD23 Enable * In result of Card Identification process, * Host Driver set this bit to 1 if Card supports CMD23 */ -#define CMD23_EN BIT(27) +#define CMD23_EN BIT(27) /* Host Version 4.00 Enable * • 0 - Version 3.00 * • 1 - Version 4.00 */ -#define HV4E BIT(28) +#define HV4E BIT(28) /* Conf depends on SRS15.HV4E */ -#define SDMA 0 << 3 -#define ADMA2_32 2 << 3 -#define ADMA2_64 3 << 3 +#define SDMA 0 << 3 +#define ADMA2_32 2 << 3 +#define ADMA2_64 3 << 3 +#define DMA_SEL_BIT 3 << 3 +#define DMA_SEL_BIT_2 2 << 3 +#define DMA_SEL_BIT_3 3 << 3 /* Preset Value Enable * Setting this bit to 1 triggers an automatically update of SRS11 */ -#define PVE BIT(31) +#define PVE BIT(31) -#define BIT_AD_32 0 << 29 -#define BIT_AD_64 1 << 29 +#define BIT_AD_32 0 << 29 +#define BIT_AD_64 1 << 29 /* SW RESET REG*/ #define SDHC_CDNS_HRS00 (0x00) @@ -206,7 +228,7 @@ /* PHY access port */ #define SDHC_CDNS_HRS04 0x10 -#define SDHC_CDNS_HRS04_ADDR GENMASK(5, 0) +#define SDHC_CDNS_HRS04_ADDR GENMASK(5, 0) /* PHY data access port */ #define SDHC_CDNS_HRS05 0x14 @@ -233,14 +255,51 @@ #define SDHC_CDNS_SRS13 0x234 #define SDHC_CDNS_SRS14 0x238 #define SDHC_CDNS_SRS15 0x23c +#define SDHC_CDNS_SRS16 0x240 #define SDHC_CDNS_SRS21 0x254 #define SDHC_CDNS_SRS22 0x258 #define SDHC_CDNS_SRS23 0x25c +#define SDHC_CDNS_SRS24 0x260 +#define SDHC_CDNS_SRS25 0x264 + +/* SRS00 */ +#define SAAR (1) + +/* SRS03 */ +#define CMD_START (U(1) << 31) +#define CMD_USE_HOLD_REG (1 << 29) +#define CMD_UPDATE_CLK_ONLY (1 << 21) +#define CMD_SEND_INIT (1 << 15) +#define CMD_STOP_ABORT_CMD (4 << 22) +#define CMD_RESUME_CMD (2 << 22) +#define CMD_SUSPEND_CMD (1 << 22) +#define DMA_ENABLED (1 << 0) +#define BLK_CNT_EN (1 << 1) +#define AUTO_CMD_EN (2 << 2) +#define COM_IDX 24 +#define ERROR_INT (1 << 15) +#define INT_SBE (1 << 13) +#define INT_HLE (1 << 12) +#define INT_FRUN (1 << 11) +#define INT_DRT (1 << 9) +#define INT_RTO (1 << 8) +#define INT_DCRC (1 << 7) +#define INT_RCRC (1 << 6) +#define INT_RXDR (1 << 5) +#define INT_TXDR (1 << 4) +#define INT_DTO (1 << 3) +#define INT_CMD_DONE (1 << 0) +#define TRAN_COMP (1 << 1) +#define CDNS_HOST_CMD_INHIBIT (BIT(0)) +#define CDNS_HOST_DATA_INHIBIT (BIT(1)) +#define ACE_CMD_12 (BIT(2)) + +#define PAGE_BUFFER_LEN (64 * 1024) /* HRS07 */ #define SDHC_CDNS_HRS07 0x1c #define SDHC_IDELAY_VAL(x) ((x) << 0) -#define SDHC_RW_COMPENSATE(x) ((x) << 16) +#define SDHC_RW_COMPENSATE(x) ((x) << 16) /* PHY reset port */ #define SDHC_CDNS_HRS09 0x24 @@ -254,53 +313,49 @@ /* Pinmux headers will reomove after ATF driver implementation */ #define PINMUX_SDMMC_SEL 0x0 -#define PIN0SEL 0x00 -#define PIN1SEL 0x04 -#define PIN2SEL 0x08 -#define PIN3SEL 0x0C -#define PIN4SEL 0x10 -#define PIN5SEL 0x14 -#define PIN6SEL 0x18 -#define PIN7SEL 0x1C -#define PIN8SEL 0x20 -#define PIN9SEL 0x24 -#define PIN10SEL 0x28 +#define PIN0SEL 0x00 +#define PIN1SEL 0x04 +#define PIN2SEL 0x08 +#define PIN3SEL 0x0C +#define PIN4SEL 0x10 +#define PIN5SEL 0x14 +#define PIN6SEL 0x18 +#define PIN7SEL 0x1C +#define PIN8SEL 0x20 +#define PIN9SEL 0x24 +#define PIN10SEL 0x28 /* HRS16 */ #define SDHC_WRCMD0_DLY(x) ((x) << 0) #define SDHC_WRCMD1_DLY(x) ((x) << 4) #define SDHC_WRDATA0_DLY(x) ((x) << 8) #define SDHC_WRDATA1_DLY(x) ((x) << 12) -#define SDHC_WRCMD0_SDCLK_DLY(x) ((x) << 16) -#define SDHC_WRCMD1_SDCLK_DLY(x) ((x) << 20) -#define SDHC_WRDATA0_SDCLK_DLY(x) ((x) << 24) -#define SDHC_WRDATA1_SDCLK_DLY(x) ((x) << 28) +#define SDHC_WRCMD0_SDCLK_DLY(x) ((x) << 16) +#define SDHC_WRCMD1_SDCLK_DLY(x) ((x) << 20) +#define SDHC_WRDATA0_SDCLK_DLY(x) ((x) << 24) +#define SDHC_WRDATA1_SDCLK_DLY(x) ((x) << 28) /* Shared Macros */ #define SDMMC_CDN(_reg) (SDMMC_CDN_REG_BASE + \ (SDMMC_CDN_##_reg)) -/* Refer to atf/tools/cert_create/include/debug.h */ -#define BIT_32(nr) (U(1) << (nr)) - /* MMC Peripheral Definition */ -#define SOCFPGA_MMC_BLOCK_SIZE U(8192) -#define SOCFPGA_MMC_BLOCK_MASK (SOCFPGA_MMC_BLOCK_SIZE - U(1)) -#define SOCFPGA_MMC_BOOT_CLK_RATE (400 * 1000) +#define SOCFPGA_MMC_BLOCK_MASK (SOCFPGA_MMC_BLOCK_SIZE - U(1)) +#define SOCFPGA_MMC_BOOT_CLK_RATE (400 * 1000) #define MMC_RESPONSE_NONE 0 -#define SDHC_CDNS_SRS03_VALUE 0x01020013 +#define SDHC_CDNS_SRS03_VALUE 0x01020013 /* Value randomly chosen for eMMC RCA, it should be > 1 */ -#define MMC_FIX_RCA 6 +#define MMC_FIX_RCA 6 #define RCA_SHIFT_OFFSET 16 -#define CMD_EXTCSD_PARTITION_CONFIG 179 -#define CMD_EXTCSD_BUS_WIDTH 183 -#define CMD_EXTCSD_HS_TIMING 185 +#define CMD_EXTCSD_PARTITION_CONFIG 179 +#define CMD_EXTCSD_BUS_WIDTH 183 +#define CMD_EXTCSD_HS_TIMING 185 #define CMD_EXTCSD_SEC_CNT 212 -#define PART_CFG_BOOT_PARTITION1_ENABLE (U(1) << 3) -#define PART_CFG_PARTITION1_ACCESS (U(1) << 0) +#define PART_CFG_BOOT_PARTITION1_ENABLE (U(1) << 3) +#define PART_CFG_PARTITION1_ACCESS (U(1) << 0) /* Values in EXT CSD register */ #define MMC_BUS_WIDTH_1 U(0) @@ -308,8 +363,8 @@ #define MMC_BUS_WIDTH_8 U(2) #define MMC_BUS_WIDTH_DDR_4 U(5) #define MMC_BUS_WIDTH_DDR_8 U(6) -#define MMC_BOOT_MODE_BACKWARD (U(0) << 3) -#define MMC_BOOT_MODE_HS_TIMING (U(1) << 3) +#define MMC_BOOT_MODE_BACKWARD (U(0) << 3) +#define MMC_BOOT_MODE_HS_TIMING (U(1) << 3) #define MMC_BOOT_MODE_DDR (U(2) << 3) #define EXTCSD_SET_CMD (U(0) << 24) @@ -318,14 +373,14 @@ #define EXTCSD_WRITE_BYTES (U(3) << 24) #define EXTCSD_CMD(x) (((x) & 0xff) << 16) #define EXTCSD_VALUE(x) (((x) & 0xff) << 8) -#define EXTCSD_CMD_SET_NORMAL U(1) +#define EXTCSD_CMD_SET_NORMAL U(1) -#define CSD_TRAN_SPEED_UNIT_MASK GENMASK(2, 0) -#define CSD_TRAN_SPEED_MULT_MASK GENMASK(6, 3) -#define CSD_TRAN_SPEED_MULT_SHIFT 3 +#define CSD_TRAN_SPEED_UNIT_MASK GENMASK(2, 0) +#define CSD_TRAN_SPEED_MULT_MASK GENMASK(6, 3) +#define CSD_TRAN_SPEED_MULT_SHIFT 3 -#define STATUS_CURRENT_STATE(x) (((x) & 0xf) << 9) -#define STATUS_READY_FOR_DATA BIT(8) +#define STATUS_CURRENT_STATE(x) (((x) & 0xf) << 9) +#define STATUS_READY_FOR_DATA BIT(8) #define STATUS_SWITCH_ERROR BIT(7) #define MMC_GET_STATE(x) (((x) >> 9) & 0xf) #define MMC_STATE_IDLE 0 @@ -346,12 +401,51 @@ #define VHS_2_7_3_6_V BIT(8) /*ADMA table component*/ -#define ADMA_DESC_ATTR_VALID BIT(0) +#define ADMA_DESC_ATTR_VALID BIT(0) #define ADMA_DESC_ATTR_END BIT(1) #define ADMA_DESC_ATTR_INT BIT(2) #define ADMA_DESC_ATTR_ACT1 BIT(4) #define ADMA_DESC_ATTR_ACT2 BIT(5) -#define ADMA_DESC_TRANSFER_DATA ADMA_DESC_ATTR_ACT2 +#define ADMA_DESC_TRANSFER_DATA ADMA_DESC_ATTR_ACT2 + +#define HRS_09_EXTENDED_RD_MODE (1 << 2) +#define HRS_09_EXTENDED_WR_MODE (1 << 3) +#define HRS_09_RDCMD_EN (1 << 15) +#define HRS_09_RDDATA_EN (1 << 16) +#define HRS_10_HCSDCLKADJ_VAL (3) + +#define SRS11_SRFA (1 << 24) +#define SRS11_SRFA_CHK(x) (x >> 24) +#define CDNS_TIMEOUT (5000) + +#define SDHCI_MAKE_CMD(c, f) (((c & 0xff) << 8) | (f & 0xff)) + +/* Card busy and present */ +#define CARD_BUSY 1 +#define CARD_NOT_BUSY 0 + +/* 500 ms delay to read the RINST register */ +#define DELAY_MS_SRS_READ 500 +#define DELAY_RES 10 + +/* Check DV dfi_init val=0 */ +#define IO_MASK_END_DATA 0x0 + +/* Check DV dfi_init val=2; DDR Mode */ +#define IO_MASK_END_DATA_DDR 0x2 +#define IO_MASK_START_DATA 0x0 +#define DATA_SELECT_OE_END_DATA 0x1 + +#define TIMEOUT 100000 + +/* General define */ +#define SDHC_REG_MASK UINT_MAX +#define SD_HOST_BLOCK_SIZE 0x200 +#define DTCVVAL_DEFAULT_VAL 0xE +#define CDMMC_DMA_MAX_BUFFER_SIZE 64*1024 +#define CDNSMMC_ADDRESS_MASK U(0x0f) +#define CONFIG_CDNS_DESC_COUNT 8 +#define SD_HOST_CLK 200000000 enum sd_opcode { SD_GO_IDLE_STATE = 0, @@ -392,6 +486,16 @@ enum sd_app_cmd { SD_APP_SEND_SCR = 51, }; +enum sd_opr_modes { + SD_HOST_OPR_MODE_HV4E_0_SDMA_32 = 0, + SD_HOST_OPR_MODE_HV4E_1_SDMA_32, + SD_HOST_OPR_MODE_HV4E_1_SDMA_64, + SD_HOST_OPR_MODE_HV4E_0_ADMA_32, + SD_HOST_OPR_MODE_HV4E_0_ADMA_64, + SD_HOST_OPR_MODE_HV4E_1_ADMA_32, + SD_HOST_OPR_MODE_HV4E_1_ADMA_64, +}; + struct cdns_sdmmc_sdhc { uint32_t sdhc_extended_rd_mode; uint32_t sdhc_extended_wr_mode; @@ -443,9 +547,6 @@ struct cdns_sdmmc_params { uint32_t combophy; }; -/* read and write API */ -size_t sdmmc_read_blocks(int lba, uintptr_t buf, size_t size); -size_t sdmmc_write_blocks(int lba, const uintptr_t buf, size_t size); struct cdns_idmac_desc { /*8 bit attribute*/ @@ -471,4 +572,8 @@ int cdns_sd_host_init(struct cdns_sdmmc_combo_phy *mmc_combo_phy_reg, struct cdns_sdmmc_sdhc *mmc_sdhc_reg); void cdns_set_sdmmc_var(struct cdns_sdmmc_combo_phy *combo_phy_reg, struct cdns_sdmmc_sdhc *sdhc_reg); +int cdns_mmc_init(struct cdns_sdmmc_params *params, struct mmc_device_info *info); +int cdns_program_phy_reg(struct cdns_sdmmc_combo_phy *combo_phy_reg, + struct cdns_sdmmc_sdhc *sdhc_reg); +void cdns_host_set_clk(uint32_t clk); #endif diff --git a/include/drivers/clk.h b/include/drivers/clk.h index a18f41ff..4db20f8e 100644 --- a/include/drivers/clk.h +++ b/include/drivers/clk.h @@ -13,15 +13,20 @@ struct clk_ops { int (*enable)(unsigned long id); void (*disable)(unsigned long id); unsigned long (*get_rate)(unsigned long id); + int (*set_rate)(unsigned long id, unsigned long rate, + unsigned long *orate); int (*get_parent)(unsigned long id); + int (*set_parent)(unsigned long id, unsigned long parent_id); bool (*is_enabled)(unsigned long id); }; int clk_enable(unsigned long id); void clk_disable(unsigned long id); unsigned long clk_get_rate(unsigned long id); +int clk_set_rate(unsigned long id, unsigned long rate, unsigned long *orate); bool clk_is_enabled(unsigned long id); int clk_get_parent(unsigned long id); +int clk_set_parent(unsigned long id, unsigned long parent_id); void clk_register(const struct clk_ops *ops); diff --git a/include/drivers/delay_timer.h b/include/drivers/delay_timer.h index 20a55435..e9fdfb78 100644 --- a/include/drivers/delay_timer.h +++ b/include/drivers/delay_timer.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015-2019, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2015-2024, Arm Limited and Contributors. All rights reserved. * Copyright (c) 2019, Linaro Limited * * SPDX-License-Identifier: BSD-3-Clause @@ -25,27 +25,12 @@ typedef struct timer_ops { uint32_t (*get_timer_value)(void); uint32_t clk_mult; uint32_t clk_div; + uint64_t (*timeout_init_us)(uint32_t usec); + bool (*timeout_elapsed)(uint64_t cnt); } timer_ops_t; -static inline uint64_t timeout_cnt_us2cnt(uint32_t us) -{ - return ((uint64_t)us * (uint64_t)read_cntfrq_el0()) / 1000000ULL; -} - -static inline uint64_t timeout_init_us(uint32_t us) -{ - uint64_t cnt = timeout_cnt_us2cnt(us); - - cnt += read_cntpct_el0(); - - return cnt; -} - -static inline bool timeout_elapsed(uint64_t expire_cnt) -{ - return read_cntpct_el0() > expire_cnt; -} - +uint64_t timeout_init_us(uint32_t usec); +bool timeout_elapsed(uint64_t cnt); void mdelay(uint32_t msec); void udelay(uint32_t usec); void timer_init(const timer_ops_t *ops_ptr); diff --git a/include/drivers/fwu/fwu.h b/include/drivers/fwu/fwu.h index 9f18e221..18e8a316 100644 --- a/include/drivers/fwu/fwu.h +++ b/include/drivers/fwu/fwu.h @@ -9,8 +9,15 @@ #include <stdbool.h> +#define FWU_BANK_STATE_ACCEPTED 0xFCU +#define FWU_BANK_STATE_VALID 0xFEU +#define FWU_BANK_STATE_INVALID 0xFFU + +#define INVALID_BOOT_IDX 0xFFFFFFFFU + void fwu_init(void); -bool fwu_is_trial_run_state(void); +uint32_t fwu_get_active_bank_state(void); +uint32_t fwu_get_alternate_boot_bank(void); const struct fwu_metadata *fwu_get_metadata(void); #endif /* FWU_H */ diff --git a/include/drivers/fwu/fwu_metadata.h b/include/drivers/fwu/fwu_metadata.h index 2e88de5e..b441300e 100644 --- a/include/drivers/fwu/fwu_metadata.h +++ b/include/drivers/fwu/fwu_metadata.h @@ -4,7 +4,7 @@ * SPDX-License-Identifier: BSD-3-Clause * * FWU metadata information as per the specification section 4.1: - * https://developer.arm.com/documentation/den0118/a/ + * https://developer.arm.com/documentation/den0118/latest/ * */ @@ -14,11 +14,13 @@ #include <stdint.h> #include <tools_share/uuid.h> +#define NR_OF_MAX_FW_BANKS 4 + /* Properties of image in a bank */ -struct fwu_image_properties { +struct fwu_image_bank_info { - /* UUID of the image in this bank */ - uuid_t img_uuid; + /* GUID of the image in this bank */ + struct efi_guid img_guid; /* [0]: bit describing the image acceptance status – * 1 means the image is accepted @@ -34,14 +36,37 @@ struct fwu_image_properties { /* Image entry information */ struct fwu_image_entry { - /* UUID identifying the image type */ - uuid_t img_type_uuid; + /* GUID identifying the image type */ + struct efi_guid img_type_guid; - /* UUID of the storage volume where the image is located */ - uuid_t location_uuid; + /* GUID of the storage volume where the image is located */ + struct efi_guid location_guid; - /* Properties of images with img_type_uuid in the different FW banks */ - struct fwu_image_properties img_props[NR_OF_FW_BANKS]; + /* Properties of images with img_type_guid in the different FW banks */ + struct fwu_image_bank_info img_bank_info[NR_OF_FW_BANKS]; + +} __packed; + +/* Firmware Image descriptor */ +struct fwu_fw_store_descriptor { + + /* Number of Banks */ + uint8_t num_banks; + + /* Reserved */ + uint8_t reserved; + + /* Number of images per bank */ + uint16_t num_images; + + /* Size of image_entry(all banks) in bytes */ + uint16_t img_entry_size; + + /* Size of image bank info structure in bytes */ + uint16_t bank_info_entry_size; + + /* Array of fwu_image_entry structs */ + struct fwu_image_entry img_entry[NR_OF_IMAGES_IN_FW_BANK]; } __packed; @@ -66,8 +91,25 @@ struct fwu_metadata { /* Previous bank index with which device booted successfully */ uint32_t previous_active_index; + /* Size of the entire metadata in bytes */ + uint32_t metadata_size; + + /* Offset of the image descriptor structure */ + uint16_t desc_offset; + + /* Reserved */ + uint16_t reserved1; + + /* Bank state */ + uint8_t bank_state[NR_OF_MAX_FW_BANKS]; + + /* Reserved */ + uint32_t reserved2; + +#if PSA_FWU_METADATA_FW_STORE_DESC /* Image entry information */ - struct fwu_image_entry img_entry[NR_OF_IMAGES_IN_FW_BANK]; + struct fwu_fw_store_descriptor fw_desc; +#endif } __packed; diff --git a/include/drivers/measured_boot/event_log/event_log.h b/include/drivers/measured_boot/event_log/event_log.h index 794d6133..b44526aa 100644 --- a/include/drivers/measured_boot/event_log/event_log.h +++ b/include/drivers/measured_boot/event_log/event_log.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020-2022, Arm Limited. All rights reserved. + * Copyright (c) 2020-2024, Arm Limited. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -43,51 +43,6 @@ #define MEMBER_SIZE(type, member) sizeof(((type *)0)->member) -/* - * Each event log entry has some metadata (i.e. a string) that identifies - * what is measured.These macros define these strings. - * Note that these strings follow the standardization recommendations - * defined in the Arm Server Base Security Guide (a.k.a. SBSG, Arm DEN 0086), - * where applicable. They should not be changed in the code. - * Where the SBSG does not make recommendations, we are free to choose any - * naming convention. - * The key thing is to choose meaningful strings so that when the TPM event - * log is used in attestation, the different components can be identified. - */ -#define EVLOG_BL2_STRING "BL_2" -#define EVLOG_BL31_STRING "SECURE_RT_EL3" -#if defined(SPD_opteed) -#define EVLOG_BL32_STRING "SECURE_RT_EL1_OPTEE" -#elif defined(SPD_tspd) -#define EVLOG_BL32_STRING "SECURE_RT_EL1_TSPD" -#elif defined(SPD_tlkd) -#define EVLOG_BL32_STRING "SECURE_RT_EL1_TLKD" -#elif defined(SPD_trusty) -#define EVLOG_BL32_STRING "SECURE_RT_EL1_TRUSTY" -#else -#define EVLOG_BL32_STRING "SECURE_RT_EL1_UNKNOWN" -#endif -#define EVLOG_BL32_EXTRA1_STRING "SECURE_RT_EL1_OPTEE_EXTRA1" -#define EVLOG_BL32_EXTRA2_STRING "SECURE_RT_EL1_OPTEE_EXTRA2" -#define EVLOG_BL33_STRING "BL_33" -#define EVLOG_FW_CONFIG_STRING "FW_CONFIG" -#define EVLOG_HW_CONFIG_STRING "HW_CONFIG" -#define EVLOG_NT_FW_CONFIG_STRING "NT_FW_CONFIG" -#define EVLOG_SCP_BL2_STRING "SYS_CTRL_2" -#define EVLOG_SOC_FW_CONFIG_STRING "SOC_FW_CONFIG" -#define EVLOG_STM32_STRING "STM32" -#define EVLOG_TB_FW_CONFIG_STRING "TB_FW_CONFIG" -#define EVLOG_TOS_FW_CONFIG_STRING "TOS_FW_CONFIG" -#define EVLOG_RMM_STRING "RMM" -#define EVLOG_SP1_STRING "SP1" -#define EVLOG_SP2_STRING "SP2" -#define EVLOG_SP3_STRING "SP3" -#define EVLOG_SP4_STRING "SP4" -#define EVLOG_SP5_STRING "SP5" -#define EVLOG_SP6_STRING "SP6" -#define EVLOG_SP7_STRING "SP7" -#define EVLOG_SP8_STRING "SP8" - typedef struct { unsigned int id; const char *name; diff --git a/include/drivers/measured_boot/event_log/tcg.h b/include/drivers/measured_boot/event_log/tcg.h index 4ac2c2ff..653f9c27 100644 --- a/include/drivers/measured_boot/event_log/tcg.h +++ b/include/drivers/measured_boot/event_log/tcg.h @@ -8,6 +8,7 @@ #define TCG_H #include <stdint.h> +#include <common/sha_common_macros.h> #define TCG_ID_EVENT_SIGNATURE_03 "Spec ID Event03" #define TCG_STARTUP_LOCALITY_SIGNATURE "StartupLocality" @@ -66,12 +67,6 @@ #define PLATFORM_CLASS_CLIENT 0 #define PLATFORM_CLASS_SERVER 1 -/* SHA digest sizes in bytes */ -#define SHA1_DIGEST_SIZE 20 -#define SHA256_DIGEST_SIZE 32 -#define SHA384_DIGEST_SIZE 48 -#define SHA512_DIGEST_SIZE 64 - enum { /* * SRTM, BIOS, Host Platform Extensions, Embedded diff --git a/include/drivers/measured_boot/metadata.h b/include/drivers/measured_boot/metadata.h new file mode 100644 index 00000000..5e17a835 --- /dev/null +++ b/include/drivers/measured_boot/metadata.h @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2024, Arm Limited. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef METADATA_H +#define METADATA_H + +/* Minimum measurement value size that can be requested to store */ +#define MEASUREMENT_VALUE_MIN_SIZE 32U +/* Maximum measurement value size that can be requested to store */ +#define MEASUREMENT_VALUE_MAX_SIZE 64U +/* Minimum signer id size that can be requested to store */ +#define SIGNER_ID_MIN_SIZE MEASUREMENT_VALUE_MIN_SIZE +/* Maximum signer id size that can be requested to store */ +#define SIGNER_ID_MAX_SIZE MEASUREMENT_VALUE_MAX_SIZE +/* The theoretical maximum image version is: "255.255.65535\0" */ +#define VERSION_MAX_SIZE 14U +/* Example sw_type: "BL_2, BL_33, etc." */ +#define SW_TYPE_MAX_SIZE 32U + +/* + * Images, measured during the boot process, have some associated metadata. + * One of these types of metadata is the image identifier strings. These macros + * define these strings. They are used across the different measured boot + * backends. + * Note that these strings follow the standardization recommendations + * defined in the Arm Server Base Security Guide (a.k.a. SBSG, Arm DEN 0086), + * where applicable. They should not be changed in the code. + * Where the SBSG does not make recommendations, we are free to choose any + * naming convention. + * The key thing is to choose meaningful strings so that when the measured boot + * metadata is used in attestation, the different components can be identified. + */ +#define MBOOT_BL2_IMAGE_STRING "BL_2" +#define MBOOT_BL31_IMAGE_STRING "SECURE_RT_EL3" +#if defined(SPD_opteed) +#define MBOOT_BL32_IMAGE_STRING "SECURE_RT_EL1_OPTEE" +#elif defined(SPD_tspd) +#define MBOOT_BL32_IMAGE_STRING "SECURE_RT_EL1_TSPD" +#elif defined(SPD_tlkd) +#define MBOOT_BL32_IMAGE_STRING "SECURE_RT_EL1_TLKD" +#elif defined(SPD_trusty) +#define MBOOT_BL32_IMAGE_STRING "SECURE_RT_EL1_TRUSTY" +#elif defined(SPD_spmd) +#define MBOOT_BL32_IMAGE_STRING "SECURE_RT_EL1_SPMD" +#else +#define MBOOT_BL32_IMAGE_STRING "SECURE_RT_EL1_UNKNOWN" +#endif /* SPD_opteed */ +#define MBOOT_BL32_EXTRA1_IMAGE_STRING "SECURE_RT_EL1_OPTEE_EXTRA1" +#define MBOOT_BL32_EXTRA2_IMAGE_STRING "SECURE_RT_EL1_OPTEE_EXTRA2" +#define MBOOT_BL33_IMAGE_STRING "BL_33" +#define MBOOT_FW_CONFIG_STRING "FW_CONFIG" +#define MBOOT_HW_CONFIG_STRING "HW_CONFIG" +#define MBOOT_NT_FW_CONFIG_STRING "NT_FW_CONFIG" +#define MBOOT_SCP_BL2_IMAGE_STRING "SYS_CTRL_2" +#define MBOOT_SOC_FW_CONFIG_STRING "SOC_FW_CONFIG" +#define MBOOT_STM32_STRING "STM32" +#define MBOOT_TB_FW_CONFIG_STRING "TB_FW_CONFIG" +#define MBOOT_TOS_FW_CONFIG_STRING "TOS_FW_CONFIG" +#define MBOOT_RMM_IMAGE_STRING "RMM" +#define MBOOT_SP1_STRING "SP1" +#define MBOOT_SP2_STRING "SP2" +#define MBOOT_SP3_STRING "SP3" +#define MBOOT_SP4_STRING "SP4" +#define MBOOT_SP5_STRING "SP5" +#define MBOOT_SP6_STRING "SP6" +#define MBOOT_SP7_STRING "SP7" +#define MBOOT_SP8_STRING "SP8" + +#endif /* METADATA_H */ diff --git a/include/drivers/measured_boot/rse/dice_prot_env.h b/include/drivers/measured_boot/rse/dice_prot_env.h new file mode 100644 index 00000000..e5aef516 --- /dev/null +++ b/include/drivers/measured_boot/rse/dice_prot_env.h @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2024, Arm Limited. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef DICE_PROT_ENV_H +#define DICE_PROT_ENV_H + +#include <stdbool.h> +#include <stddef.h> +#include <stdint.h> + +#include <drivers/measured_boot/metadata.h> + +#define DPE_INVALID_ID UINT32_MAX + +struct dpe_metadata { + unsigned int id; + uint32_t cert_id; + uint8_t signer_id[SIGNER_ID_MAX_SIZE]; + size_t signer_id_size; + uint8_t version[VERSION_MAX_SIZE]; + size_t version_size; + uint8_t sw_type[SW_TYPE_MAX_SIZE]; + size_t sw_type_size; + bool allow_new_context_to_derive; + bool retain_parent_context; + bool create_certificate; + int target_locality; + void *pk_oid; +}; + +void dpe_init(struct dpe_metadata *metadata); + +/* Returns 0 in case of success otherwise -1. */ +int dpe_measure_and_record(struct dpe_metadata *metadata, + uintptr_t data_base, uint32_t data_size, + uint32_t data_id); + +int dpe_set_signer_id(struct dpe_metadata *metadata, + const void *pk_oid, const void *pk_ptr, size_t pk_len); + +/* Child components inherit their first valid context handle from their parents. + * How to share context handle is platform specific. + */ +void plat_dpe_share_context_handle(int *ctx_handle, int *parent_ctx_handle); +void plat_dpe_get_context_handle(int *ctx_handle); + +#endif /* DICE_PROT_ENV_H */ diff --git a/include/drivers/measured_boot/rse/rse_measured_boot.h b/include/drivers/measured_boot/rse/rse_measured_boot.h new file mode 100644 index 00000000..2f605d71 --- /dev/null +++ b/include/drivers/measured_boot/rse/rse_measured_boot.h @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2022-2024, Arm Limited. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef RSE_MEASURED_BOOT_H +#define RSE_MEASURED_BOOT_H + +#include <stdint.h> + +#include <common/debug.h> +#include <drivers/measured_boot/metadata.h> + +#define RSE_MBOOT_INVALID_ID UINT32_MAX + +struct rse_mboot_metadata { + unsigned int id; + uint8_t slot; + uint8_t signer_id[SIGNER_ID_MAX_SIZE]; + size_t signer_id_size; + uint8_t version[VERSION_MAX_SIZE]; + size_t version_size; + uint8_t sw_type[SW_TYPE_MAX_SIZE]; + size_t sw_type_size; + void *pk_oid; + bool lock_measurement; +}; + +/* Functions' declarations */ +void rse_measured_boot_init(struct rse_mboot_metadata *metadata_ptr); +int rse_mboot_measure_and_record(struct rse_mboot_metadata *metadata_ptr, + uintptr_t data_base, uint32_t data_size, + uint32_t data_id); + +int rse_mboot_set_signer_id(struct rse_mboot_metadata *metadata_ptr, + const void *pk_oid, const void *pk_ptr, + size_t pk_len); + +#endif /* RSE_MEASURED_BOOT_H */ diff --git a/include/drivers/measured_boot/rss/rss_measured_boot.h b/include/drivers/measured_boot/rss/rss_measured_boot.h deleted file mode 100644 index 7ab517c1..00000000 --- a/include/drivers/measured_boot/rss/rss_measured_boot.h +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright (c) 2022-2023, Arm Limited. All rights reserved. - * - * SPDX-License-Identifier: BSD-3-Clause - */ - -#ifndef RSS_MEASURED_BOOT_H -#define RSS_MEASURED_BOOT_H - -#include <stdint.h> - -#include <common/debug.h> -#include <measured_boot.h> - -#define RSS_MBOOT_INVALID_ID UINT32_MAX - -/* - * Each boot measurement has some metadata (i.e. a string) that identifies - * what was measured and how. The sw_type field of the rss_mboot_metadata - * structure represents the role of the software component that was measured. - * The below macros define strings suitable for the sw_type. - * The key thing is to choose meaningful strings so that when the attestation - * token is verified, then the different components can be identified. - */ -#define RSS_MBOOT_BL2_STRING "BL_2" -#define RSS_MBOOT_BL31_STRING "SECURE_RT_EL3" -#define RSS_MBOOT_HW_CONFIG_STRING "HW_CONFIG" -#define RSS_MBOOT_FW_CONFIG_STRING "FW_CONFIG" -#define RSS_MBOOT_TB_FW_CONFIG_STRING "TB_FW_CONFIG" -#define RSS_MBOOT_SOC_FW_CONFIG_STRING "SOC_FW_CONFIG" -#define RSS_MBOOT_RMM_STRING "RMM" - - -struct rss_mboot_metadata { - unsigned int id; - uint8_t slot; - uint8_t signer_id[SIGNER_ID_MAX_SIZE]; - size_t signer_id_size; - uint8_t version[VERSION_MAX_SIZE]; - size_t version_size; - uint8_t sw_type[SW_TYPE_MAX_SIZE]; - size_t sw_type_size; - void *pk_oid; - bool lock_measurement; -}; - -/* Functions' declarations */ -void rss_measured_boot_init(struct rss_mboot_metadata *metadata_ptr); -int rss_mboot_measure_and_record(struct rss_mboot_metadata *metadata_ptr, - uintptr_t data_base, uint32_t data_size, - uint32_t data_id); - -int rss_mboot_set_signer_id(struct rss_mboot_metadata *metadata_ptr, - const void *pk_oid, const void *pk_ptr, - size_t pk_len); - -#endif /* RSS_MEASURED_BOOT_H */ diff --git a/include/drivers/nxp/clk/s32cc/s32cc-clk-drv.h b/include/drivers/nxp/clk/s32cc/s32cc-clk-drv.h new file mode 100644 index 00000000..d879f5be --- /dev/null +++ b/include/drivers/nxp/clk/s32cc/s32cc-clk-drv.h @@ -0,0 +1,11 @@ +/* + * Copyright 2024 NXP + * + * SPDX-License-Identifier: BSD-3-Clause + */ +#ifndef S32CC_CLK_DRV_H +#define S32CC_CLK_DRV_H + +int s32cc_init_early_clks(void); + +#endif diff --git a/include/drivers/nxp/clk/s32cc/s32cc-clk-ids.h b/include/drivers/nxp/clk/s32cc/s32cc-clk-ids.h new file mode 100644 index 00000000..d34dc22f --- /dev/null +++ b/include/drivers/nxp/clk/s32cc/s32cc-clk-ids.h @@ -0,0 +1,106 @@ +/* SPDX-License-Identifier: BSD-3-Clause */ +/* + * Copyright 2024 NXP + */ +#ifndef S32CC_CLK_IDS_H +#define S32CC_CLK_IDS_H + +#include <stdint.h> +#include <lib/utils_def.h> + +/** + * Clock ID encoding: + * 31:30 bits = Type of the clock + * 29:0 bits = Clock ID within the clock category + */ +#define S32CC_CLK_ID_MASK GENMASK_64(29U, 0U) +#define S32CC_CLK_TYPE_MASK GENMASK_64(31U, 30U) +#define S32CC_CLK_ID(ID) (((unsigned long)(ID)) & S32CC_CLK_ID_MASK) +#define S32CC_CLK_TYPE(ID) (((unsigned long)(ID)) & S32CC_CLK_TYPE_MASK) +#define S32CC_CLK(TAG, ID) (S32CC_CLK_ID(ID) | (S32CC_CLK_TYPE((TAG) << 30U))) +#define S32CC_HW_CLK(ID) S32CC_CLK(0UL, U(ID)) +#define S32CC_SW_CLK(SUB, ID) S32CC_CLK(2UL | ((SUB) & 1UL), U(ID)) + +/* SW clocks subcategories */ +#define S32CC_ARCH_CLK(ID) S32CC_SW_CLK(0UL, ID) +#define S32CC_PLAT_CLK(ID) S32CC_SW_CLK(1UL, ID) + +/* IDs for clock selectors listed in S32CC Reference Manuals */ +#define S32CC_CLK_FIRC S32CC_HW_CLK(0) +#define S32CC_CLK_SIRC S32CC_HW_CLK(1) +#define S32CC_CLK_FXOSC S32CC_HW_CLK(2) +#define S32CC_CLK_ARM_PLL_PHI0 S32CC_HW_CLK(4) +#define S32CC_CLK_ARM_PLL_PHI1 S32CC_HW_CLK(5) +#define S32CC_CLK_ARM_PLL_PHI2 S32CC_HW_CLK(6) +#define S32CC_CLK_ARM_PLL_PHI3 S32CC_HW_CLK(7) +#define S32CC_CLK_ARM_PLL_PHI4 S32CC_HW_CLK(8) +#define S32CC_CLK_ARM_PLL_PHI5 S32CC_HW_CLK(9) +#define S32CC_CLK_ARM_PLL_PHI6 S32CC_HW_CLK(10) +#define S32CC_CLK_ARM_PLL_PHI7 S32CC_HW_CLK(11) +#define S32CC_CLK_ARM_PLL_DFS1 S32CC_HW_CLK(12) +#define S32CC_CLK_ARM_PLL_DFS2 S32CC_HW_CLK(13) +#define S32CC_CLK_ARM_PLL_DFS3 S32CC_HW_CLK(14) +#define S32CC_CLK_ARM_PLL_DFS4 S32CC_HW_CLK(15) +#define S32CC_CLK_ARM_PLL_DFS5 S32CC_HW_CLK(16) +#define S32CC_CLK_ARM_PLL_DFS6 S32CC_HW_CLK(17) +#define S32CC_CLK_PERIPH_PLL_PHI0 S32CC_HW_CLK(18) +#define S32CC_CLK_PERIPH_PLL_PHI1 S32CC_HW_CLK(19) +#define S32CC_CLK_PERIPH_PLL_PHI2 S32CC_HW_CLK(20) +#define S32CC_CLK_PERIPH_PLL_PHI3 S32CC_HW_CLK(21) +#define S32CC_CLK_PERIPH_PLL_PHI4 S32CC_HW_CLK(22) +#define S32CC_CLK_PERIPH_PLL_PHI5 S32CC_HW_CLK(23) +#define S32CC_CLK_PERIPH_PLL_PHI6 S32CC_HW_CLK(24) +#define S32CC_CLK_PERIPH_PLL_PHI7 S32CC_HW_CLK(25) +#define S32CC_CLK_PERIPH_PLL_DFS1 S32CC_HW_CLK(26) +#define S32CC_CLK_PERIPH_PLL_DFS2 S32CC_HW_CLK(27) +#define S32CC_CLK_PERIPH_PLL_DFS3 S32CC_HW_CLK(28) +#define S32CC_CLK_PERIPH_PLL_DFS4 S32CC_HW_CLK(29) +#define S32CC_CLK_PERIPH_PLL_DFS5 S32CC_HW_CLK(30) +#define S32CC_CLK_PERIPH_PLL_DFS6 S32CC_HW_CLK(31) +#define S32CC_CLK_FTM0_EXT_REF S32CC_HW_CLK(34) +#define S32CC_CLK_FTM1_EXT_REF S32CC_HW_CLK(35) +#define S32CC_CLK_DDR_PLL_PHI0 S32CC_HW_CLK(36) +#define S32CC_CLK_GMAC0_EXT_TX S32CC_HW_CLK(37) +#define S32CC_CLK_GMAC0_EXT_RX S32CC_HW_CLK(38) +#define S32CC_CLK_GMAC0_EXT_REF S32CC_HW_CLK(39) +#define S32CC_CLK_SERDES0_LANE0_TX S32CC_HW_CLK(40) +#define S32CC_CLK_SERDES0_LANE0_CDR S32CC_HW_CLK(41) +#define S32CC_CLK_GMAC0_EXT_TS S32CC_HW_CLK(44) +#define S32CC_CLK_GMAC0_REF_DIV S32CC_HW_CLK(45) + +/* Software defined clock IDs */ +#define S32CC_CLK_ARM_PLL_MUX S32CC_ARCH_CLK(0) +#define S32CC_CLK_ARM_PLL_VCO S32CC_ARCH_CLK(1) + +/* ARM CGM1 clocks */ +#define S32CC_CLK_MC_CGM1_MUX0 S32CC_ARCH_CLK(2) +#define S32CC_CLK_A53_CORE S32CC_ARCH_CLK(3) +#define S32CC_CLK_A53_CORE_DIV2 S32CC_ARCH_CLK(4) +#define S32CC_CLK_A53_CORE_DIV10 S32CC_ARCH_CLK(5) + +/* XBAR clock*/ +#define S32CC_CLK_MC_CGM0_MUX0 S32CC_ARCH_CLK(6) +#define S32CC_CLK_XBAR_2X S32CC_ARCH_CLK(7) +#define S32CC_CLK_XBAR S32CC_ARCH_CLK(8) +#define S32CC_CLK_XBAR_DIV2 S32CC_ARCH_CLK(9) +#define S32CC_CLK_XBAR_DIV3 S32CC_ARCH_CLK(10) +#define S32CC_CLK_XBAR_DIV4 S32CC_ARCH_CLK(11) +#define S32CC_CLK_XBAR_DIV6 S32CC_ARCH_CLK(12) + +/* Periph PLL */ +#define S32CC_CLK_PERIPH_PLL_MUX S32CC_ARCH_CLK(13) +#define S32CC_CLK_PERIPH_PLL_VCO S32CC_ARCH_CLK(14) + +#define S32CC_CLK_MC_CGM0_MUX8 S32CC_ARCH_CLK(15) +#define S32CC_CLK_LINFLEX_BAUD S32CC_ARCH_CLK(16) +#define S32CC_CLK_LINFLEX S32CC_ARCH_CLK(17) + +/* DDR PLL */ +#define S32CC_CLK_DDR_PLL_MUX S32CC_ARCH_CLK(18) +#define S32CC_CLK_DDR_PLL_VCO S32CC_ARCH_CLK(19) + +/* DDR clock */ +#define S32CC_CLK_MC_CGM5_MUX0 S32CC_ARCH_CLK(20) +#define S32CC_CLK_DDR S32CC_ARCH_CLK(21) + +#endif /* S32CC_CLK_IDS_H */ diff --git a/include/drivers/nxp/clk/s32cc/s32cc-clk-modules.h b/include/drivers/nxp/clk/s32cc/s32cc-clk-modules.h new file mode 100644 index 00000000..4837f795 --- /dev/null +++ b/include/drivers/nxp/clk/s32cc/s32cc-clk-modules.h @@ -0,0 +1,398 @@ +/* SPDX-License-Identifier: BSD-3-Clause */ +/* + * Copyright 2020-2024 NXP + */ +#ifndef S32CC_CLK_MODULES_H +#define S32CC_CLK_MODULES_H + +#include <inttypes.h> +#include <stdbool.h> +#include <stddef.h> + +#define MHZ UL(1000000) +#define GHZ (UL(1000) * MHZ) + +enum s32cc_clkm_type { + s32cc_osc_t, + s32cc_clk_t, + s32cc_pll_t, + s32cc_pll_out_div_t, + s32cc_dfs_t, + s32cc_dfs_div_t, + s32cc_clkmux_t, + s32cc_shared_clkmux_t, + s32cc_fixed_div_t, + s32cc_part_t, + s32cc_part_block_t, + s32cc_part_block_link_t, +}; + +enum s32cc_clk_source { + S32CC_FIRC, + S32CC_FXOSC, + S32CC_SIRC, + S32CC_ARM_PLL, + S32CC_ARM_DFS, + S32CC_PERIPH_PLL, + S32CC_CGM0, + S32CC_CGM1, + S32CC_DDR_PLL, + S32CC_CGM5, +}; + +struct s32cc_clk_obj { + enum s32cc_clkm_type type; + uint32_t refcount; +}; + +struct s32cc_osc { + struct s32cc_clk_obj desc; + enum s32cc_clk_source source; + unsigned long freq; + void *base; +}; + +#define S32CC_OSC_INIT(SOURCE) \ +{ \ + .desc = { \ + .type = s32cc_osc_t, \ + }, \ + .source = (SOURCE), \ +} + +struct s32cc_clkmux { + struct s32cc_clk_obj desc; + enum s32cc_clk_source module; + uint8_t index; /* Mux index in parent module */ + unsigned long source_id; /* Selected source */ + uint8_t nclks; /* Number of input clocks */ + unsigned long clkids[5]; /* IDs of the input clocks */ +}; + +#define S32CC_CLKMUX_TYPE_INIT(TYPE, MODULE, INDEX, NCLKS, ...) \ +{ \ + .desc = { \ + .type = (TYPE), \ + }, \ + .module = (MODULE), \ + .index = (INDEX), \ + .nclks = (NCLKS), \ + .clkids = {__VA_ARGS__}, \ +} + +#define S32CC_CLKMUX_INIT(MODULE, INDEX, NCLKS, ...) \ + S32CC_CLKMUX_TYPE_INIT(s32cc_clkmux_t, MODULE, \ + INDEX, NCLKS, __VA_ARGS__) + +#define S32CC_SHARED_CLKMUX_INIT(MODULE, INDEX, NCLKS, ...) \ + S32CC_CLKMUX_TYPE_INIT(s32cc_shared_clkmux_t, MODULE, \ + INDEX, NCLKS, __VA_ARGS__) + +struct s32cc_pll { + struct s32cc_clk_obj desc; + struct s32cc_clk_obj *source; + enum s32cc_clk_source instance; + unsigned long vco_freq; + uint32_t ndividers; + uintptr_t base; +}; + +#define S32CC_PLL_INIT(PLL_MUX_CLK, INSTANCE, NDIVIDERS) \ +{ \ + .desc = { \ + .type = s32cc_pll_t, \ + }, \ + .source = &(PLL_MUX_CLK).desc, \ + .instance = (INSTANCE), \ + .ndividers = (NDIVIDERS), \ +} + +struct s32cc_pll_out_div { + struct s32cc_clk_obj desc; + struct s32cc_clk_obj *parent; + uint32_t index; + unsigned long freq; +}; + +#define S32CC_PLL_OUT_DIV_INIT(PARENT, INDEX) \ +{ \ + .desc = { \ + .type = s32cc_pll_out_div_t, \ + }, \ + .parent = &(PARENT).desc, \ + .index = (INDEX), \ +} + +#define S32CC_PLL_OUT_DIV_INIT(PARENT, INDEX) \ +{ \ + .desc = { \ + .type = s32cc_pll_out_div_t, \ + }, \ + .parent = &(PARENT).desc, \ + .index = (INDEX), \ +} + +struct s32cc_dfs { + struct s32cc_clk_obj desc; + struct s32cc_clk_obj *parent; + enum s32cc_clk_source instance; + uintptr_t base; +}; + +#define S32CC_DFS_INIT(PARENT, INSTANCE) \ +{ \ + .desc = { \ + .type = s32cc_dfs_t, \ + }, \ + .parent = &(PARENT).desc, \ + .instance = (INSTANCE), \ +} + +struct s32cc_dfs_div { + struct s32cc_clk_obj desc; + struct s32cc_clk_obj *parent; + uint32_t index; + unsigned long freq; +}; + +#define S32CC_DFS_DIV_INIT(PARENT, INDEX) \ +{ \ + .desc = { \ + .type = s32cc_dfs_div_t, \ + }, \ + .parent = &(PARENT).desc, \ + .index = (INDEX), \ +} + +struct s32cc_fixed_div { + struct s32cc_clk_obj desc; + struct s32cc_clk_obj *parent; + uint32_t rate_div; +}; + +#define S32CC_FIXED_DIV_INIT(PARENT, RATE_DIV) \ +{ \ + .desc = { \ + .type = s32cc_fixed_div_t, \ + }, \ + .parent = &(PARENT).desc, \ + .rate_div = (RATE_DIV), \ +} + +struct s32cc_clk { + struct s32cc_clk_obj desc; + struct s32cc_clk_obj *module; + struct s32cc_clk *pclock; + unsigned long min_freq; + unsigned long max_freq; +}; + +struct s32cc_clk_array { + unsigned long type_mask; + struct s32cc_clk **clks; + size_t n_clks; +}; + +#define S32CC_FREQ_CLK(PARENT_MODULE, PARENT, MIN_F, MAX_F) \ +{ \ + .desc = { \ + .type = s32cc_clk_t, \ + }, \ + .pclock = (PARENT), \ + .module = (PARENT_MODULE), \ + .min_freq = (MIN_F), \ + .max_freq = (MAX_F), \ +} + +#define S32CC_FREQ_MODULE_CLK(PARENT_MODULE, MIN_F, MAX_F) \ + S32CC_FREQ_CLK(&(PARENT_MODULE).desc, NULL, MIN_F, MAX_F) + +#define S32CC_MODULE_CLK(PARENT_MODULE) \ + S32CC_FREQ_MODULE_CLK(PARENT_MODULE, 0, 0) + +#define S32CC_CHILD_CLK(PARENT, MIN_F, MAX_F) \ + S32CC_FREQ_CLK(NULL, &(PARENT), MIN_F, MAX_F) + +struct s32cc_part { + struct s32cc_clk_obj desc; + uint32_t partition_id; +}; + +#define S32CC_PART(PART_NUM) \ +{ \ + .desc = { \ + .type = s32cc_part_t, \ + }, \ + .partition_id = (PART_NUM), \ +} + +enum s32cc_part_block_type { + s32cc_part_block0, + s32cc_part_block1, + s32cc_part_block2, + s32cc_part_block3, + s32cc_part_block4, + s32cc_part_block5, + s32cc_part_block6, + s32cc_part_block7, + s32cc_part_block8, + s32cc_part_block9, + s32cc_part_block10, + s32cc_part_block11, + s32cc_part_block12, + s32cc_part_block13, + s32cc_part_block14, + s32cc_part_block15, +}; + +struct s32cc_part_block { + struct s32cc_clk_obj desc; + struct s32cc_part *part; + enum s32cc_part_block_type block; + bool status; +}; + +#define S32CC_PART_BLOCK_STATUS(PART_META, BLOCK_TYPE, STATUS) \ +{ \ + .desc = { \ + .type = s32cc_part_block_t, \ + }, \ + .part = (PART_META), \ + .block = (BLOCK_TYPE), \ + .status = (STATUS), \ +} + +#define S32CC_PART_BLOCK(PARENT, BLOCK_TYPE) \ + S32CC_PART_BLOCK_STATUS(PARENT, BLOCK_TYPE, true) + +#define S32CC_PART_BLOCK_NO_STATUS(PARENT, BLOCK_TYPE) \ + S32CC_PART_BLOCK_STATUS(PARENT, BLOCK_TYPE, false) + +struct s32cc_part_block_link { + struct s32cc_clk_obj desc; + struct s32cc_clk_obj *parent; + struct s32cc_part_block *block; +}; + +#define S32CC_PART_BLOCK_LINK(PARENT, BLOCK) \ +{ \ + .desc = { \ + .type = s32cc_part_block_link_t, \ + }, \ + .parent = &(PARENT).desc, \ + .block = (BLOCK), \ +} + +static inline struct s32cc_osc *s32cc_obj2osc(const struct s32cc_clk_obj *mod) +{ + uintptr_t osc_addr; + + osc_addr = ((uintptr_t)mod) - offsetof(struct s32cc_osc, desc); + return (struct s32cc_osc *)osc_addr; +} + +static inline struct s32cc_clk *s32cc_obj2clk(const struct s32cc_clk_obj *mod) +{ + uintptr_t clk_addr; + + clk_addr = ((uintptr_t)mod) - offsetof(struct s32cc_clk, desc); + return (struct s32cc_clk *)clk_addr; +} + +static inline bool is_s32cc_clk_mux(const struct s32cc_clk *clk) +{ + const struct s32cc_clk_obj *module; + + module = clk->module; + if (module == NULL) { + return false; + } + + return (module->type == s32cc_clkmux_t) || + (module->type == s32cc_shared_clkmux_t); +} + +static inline struct s32cc_clkmux *s32cc_obj2clkmux(const struct s32cc_clk_obj *mod) +{ + uintptr_t cmux_addr; + + cmux_addr = ((uintptr_t)mod) - offsetof(struct s32cc_clkmux, desc); + return (struct s32cc_clkmux *)cmux_addr; +} + +static inline struct s32cc_clkmux *s32cc_clk2mux(const struct s32cc_clk *clk) +{ + if (!is_s32cc_clk_mux(clk)) { + return NULL; + } + + return s32cc_obj2clkmux(clk->module); +} + +static inline struct s32cc_pll *s32cc_obj2pll(const struct s32cc_clk_obj *mod) +{ + uintptr_t pll_addr; + + pll_addr = ((uintptr_t)mod) - offsetof(struct s32cc_pll, desc); + return (struct s32cc_pll *)pll_addr; +} + +static inline struct s32cc_pll_out_div *s32cc_obj2plldiv(const struct s32cc_clk_obj *mod) +{ + uintptr_t plldiv_addr; + + plldiv_addr = ((uintptr_t)mod) - offsetof(struct s32cc_pll_out_div, desc); + return (struct s32cc_pll_out_div *)plldiv_addr; +} + +static inline struct s32cc_fixed_div *s32cc_obj2fixeddiv(const struct s32cc_clk_obj *mod) +{ + uintptr_t fdiv_addr; + + fdiv_addr = ((uintptr_t)mod) - offsetof(struct s32cc_fixed_div, desc); + return (struct s32cc_fixed_div *)fdiv_addr; +} + +static inline struct s32cc_dfs *s32cc_obj2dfs(const struct s32cc_clk_obj *mod) +{ + uintptr_t dfs_addr; + + dfs_addr = ((uintptr_t)mod) - offsetof(struct s32cc_dfs, desc); + return (struct s32cc_dfs *)dfs_addr; +} + +static inline struct s32cc_dfs_div *s32cc_obj2dfsdiv(const struct s32cc_clk_obj *mod) +{ + uintptr_t dfs_div_addr; + + dfs_div_addr = ((uintptr_t)mod) - offsetof(struct s32cc_dfs_div, desc); + return (struct s32cc_dfs_div *)dfs_div_addr; +} + +static inline struct s32cc_part *s32cc_obj2part(const struct s32cc_clk_obj *mod) +{ + uintptr_t part_addr; + + part_addr = ((uintptr_t)mod) - offsetof(struct s32cc_part, desc); + return (struct s32cc_part *)part_addr; +} + +static inline struct s32cc_part_block * +s32cc_obj2partblock(const struct s32cc_clk_obj *mod) +{ + uintptr_t part_blk_addr; + + part_blk_addr = ((uintptr_t)mod) - offsetof(struct s32cc_part_block, desc); + return (struct s32cc_part_block *)part_blk_addr; +} + +static inline struct s32cc_part_block_link * +s32cc_obj2partblocklink(const struct s32cc_clk_obj *mod) +{ + uintptr_t blk_link; + + blk_link = ((uintptr_t)mod) - offsetof(struct s32cc_part_block_link, desc); + return (struct s32cc_part_block_link *)blk_link; +} + +#endif /* S32CC_CLK_MODULES_H */ diff --git a/include/drivers/nxp/clk/s32cc/s32cc-clk-utils.h b/include/drivers/nxp/clk/s32cc/s32cc-clk-utils.h new file mode 100644 index 00000000..e6adeccd --- /dev/null +++ b/include/drivers/nxp/clk/s32cc/s32cc-clk-utils.h @@ -0,0 +1,23 @@ +/* SPDX-License-Identifier: BSD-3-Clause */ +/* + * Copyright 2024 NXP + */ +#ifndef S32CC_CLK_UTILS_H +#define S32CC_CLK_UTILS_H + +#include <s32cc-clk-modules.h> + +struct s32cc_clk *s32cc_get_clk_from_table(const struct s32cc_clk_array *const *clk_arr, + size_t size, + unsigned long clk_id); + +int s32cc_get_id_from_table(const struct s32cc_clk_array *const *clk_arr, + size_t size, const struct s32cc_clk *clk, + unsigned long *clk_index); + +struct s32cc_clk *s32cc_get_arch_clk(unsigned long id); +int s32cc_get_clk_id(const struct s32cc_clk *clk, unsigned long *id); + +void s32cc_clk_register_drv(void); + +#endif /* S32CC_CLK_UTILS_H */ diff --git a/include/drivers/nxp/console/linflex.h b/include/drivers/nxp/console/linflex.h new file mode 100644 index 00000000..2b4e0d71 --- /dev/null +++ b/include/drivers/nxp/console/linflex.h @@ -0,0 +1,18 @@ +/* + * Copyright 2024 NXP + * + * SPDX-License-Identifier: BSD-3-Clause + */ +#ifndef LINFLEX_H +#define LINFLEX_H + +#ifndef __ASSEMBLER__ +#include <drivers/console.h> + +int console_linflex_core_init(uintptr_t baseaddr, uint32_t clock, + uint32_t baud); +int console_linflex_register(uintptr_t baseaddr, uint32_t clock, + uint32_t baud, console_t *console); +#endif + +#endif /* LINFLEX_H */ diff --git a/include/drivers/nxp/crypto/caam/hash.h b/include/drivers/nxp/crypto/caam/hash.h index 9136dca2..6201d232 100644 --- a/include/drivers/nxp/crypto/caam/hash.h +++ b/include/drivers/nxp/crypto/caam/hash.h @@ -9,6 +9,7 @@ #define __HASH_H__ #include <stdbool.h> +#include <common/sha_common_macros.h> /* List of hash algorithms */ enum hash_algo { @@ -16,9 +17,6 @@ enum hash_algo { SHA256 }; -/* number of bytes in the SHA256-256 digest */ -#define SHA256_DIGEST_SIZE 32 - /* * number of words in the digest - Digest is kept internally * as 8 32-bit words diff --git a/include/drivers/partition/partition.h b/include/drivers/partition/partition.h index d567d4cb..9e22d34c 100644 --- a/include/drivers/partition/partition.h +++ b/include/drivers/partition/partition.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016-2023, Arm Limited and Contributors. All rights reserved. + * Copyright (c) 2016-2024, Arm Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -41,13 +41,15 @@ typedef struct partition_entry { typedef struct partition_entry_list { partition_entry_t list[PLAT_PARTITION_MAX_ENTRIES]; - int entry_count; + unsigned int entry_count; } partition_entry_list_t; int load_partition_table(unsigned int image_id); const partition_entry_t *get_partition_entry(const char *name); -const partition_entry_t *get_partition_entry_by_type(const uuid_t *type_guid); -const partition_entry_t *get_partition_entry_by_uuid(const uuid_t *part_uuid); +const partition_entry_t *get_partition_entry_by_type( + const struct efi_guid *type_guid); +const partition_entry_t *get_partition_entry_by_guid( + const struct efi_guid *part_guid); const partition_entry_list_t *get_partition_entry_list(void); void partition_init(unsigned int image_id); int gpt_partition_init(void); diff --git a/include/drivers/rpi3/mailbox/rpi3_mbox.h b/include/drivers/rpi3/mailbox/rpi3_mbox.h index c1074402..33458e38 100644 --- a/include/drivers/rpi3/mailbox/rpi3_mbox.h +++ b/include/drivers/rpi3/mailbox/rpi3_mbox.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, Arm Limited and Contributors. All rights reserved. + * Copyright (c) 2019-2024, Arm Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -16,6 +16,22 @@ typedef struct __packed __aligned(16) rpi3_mbox_request { uint32_t tags[0]; } rpi3_mbox_request_t; +/* VideoCore -> ARM */ +#define RPI3_MBOX0_READ_OFFSET ULL(0x00000000) +#define RPI3_MBOX0_PEEK_OFFSET ULL(0x00000010) +#define RPI3_MBOX0_SENDER_OFFSET ULL(0x00000014) +#define RPI3_MBOX0_STATUS_OFFSET ULL(0x00000018) +#define RPI3_MBOX0_CONFIG_OFFSET ULL(0x0000001C) +/* ARM -> VideoCore */ +#define RPI3_MBOX1_WRITE_OFFSET ULL(0x00000020) +#define RPI3_MBOX1_PEEK_OFFSET ULL(0x00000030) +#define RPI3_MBOX1_SENDER_OFFSET ULL(0x00000034) +#define RPI3_MBOX1_STATUS_OFFSET ULL(0x00000038) +#define RPI3_MBOX1_CONFIG_OFFSET ULL(0x0000003C) +/* Mailbox status constants */ +#define RPI3_MBOX_STATUS_FULL_MASK U(0x80000000) /* Set if full */ +#define RPI3_MBOX_STATUS_EMPTY_MASK U(0x40000000) /* Set if empty */ + #define RPI3_MBOX_BUFFER_SIZE U(256) /* Constants to perform a request/check the status of a request. */ diff --git a/include/drivers/st/bsec.h b/include/drivers/st/bsec.h index 60dcf3c1..4a1517af 100644 --- a/include/drivers/st/bsec.h +++ b/include/drivers/st/bsec.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2022, STMicroelectronics - All Rights Reserved + * Copyright (c) 2017-2024, STMicroelectronics - All Rights Reserved * * SPDX-License-Identifier: BSD-3-Clause */ @@ -12,13 +12,6 @@ #include <lib/utils_def.h> -/* - * IP configuration - */ -#define BSEC_OTP_MASK GENMASK(4, 0) -#define BSEC_OTP_BANK_SHIFT 5 -#define BSEC_TIMEOUT_VALUE 0xFFFF - /* * Return status */ @@ -32,98 +25,49 @@ #define BSEC_RETRY 0xFFFFFFF8U #define BSEC_NOT_SUPPORTED 0xFFFFFFF7U #define BSEC_WRITE_LOCKED 0xFFFFFFF6U -#define BSEC_ERROR_INVALID_FVR 0xFFFFFFF5U - -/* - * OTP MODE - */ -#define BSEC_MODE_OPEN1 0x00U -#define BSEC_MODE_SECURED 0x01U -#define BSEC_MODE_OPEN2 0x02U -#define BSEC_MODE_INVALID 0x04U - -/* - * OTP Lock services definition. - * Value must corresponding to the bit number in the register. - * Special case: (bit number << 1) for BSEC3. - */ -#define BSEC_LOCK_UPPER_OTP 0x00 -#define BSEC_LOCK_GWLOCK 0x01 -#define BSEC_LOCK_DEBUG 0x02 -#define BSEC_LOCK_PROGRAM 0x03 -#define BSEC_LOCK_KVLOCK 0x04 /* - * Values for struct bsec_config::freq + * get BSEC global state: result for bsec_get_secure_state() + * @state: global state + * [1:0] BSEC state + * 00b: Sec Open + * 01b: Sec Closed + * 11b: Invalid + * [8]: Hardware Key set = 1b */ -#define FREQ_10_20_MHZ 0x0 -#define FREQ_20_30_MHZ 0x1 -#define FREQ_30_45_MHZ 0x2 -#define FREQ_45_67_MHZ 0x3 - -/* - * Device info structure, providing device-specific functions and a means of - * adding driver-specific state. - */ -struct bsec_config { - uint8_t den_lock; /* - * Debug enable sticky lock - * 1 debug enable is locked until next reset - */ - - /* BSEC2 only */ - uint8_t tread; /* SAFMEM Reading current level default 0 */ - uint8_t pulse_width; /* SAFMEM Programming pulse width default 1 */ - uint8_t freq; /* - * SAFMEM CLOCK see freq value define - * default FREQ_45_67_MHZ - */ - uint8_t power; /* Power up SAFMEM. 1 power up, 0 power off */ - uint8_t prog_lock; /* - * Programming Sticky lock - * 1 programming is locked until next reset - */ - uint8_t upper_otp_lock; /* - * Shadowing of upper OTP sticky lock - * 1 shadowing of upper OTP is locked - * until next reset - */ -}; +#define BSEC_STATE_SEC_OPEN U(0x0) +#define BSEC_STATE_SEC_CLOSED U(0x1) +#define BSEC_STATE_INVALID U(0x3) +#define BSEC_STATE_MASK GENMASK_32(1, 0) uint32_t bsec_probe(void); -uint32_t bsec_get_base(void); - -uint32_t bsec_set_config(struct bsec_config *cfg); -uint32_t bsec_get_config(struct bsec_config *cfg); -uint32_t bsec_shadow_register(uint32_t otp); uint32_t bsec_read_otp(uint32_t *val, uint32_t otp); +uint32_t bsec_shadow_read_otp(uint32_t *val, uint32_t otp); uint32_t bsec_write_otp(uint32_t val, uint32_t otp); uint32_t bsec_program_otp(uint32_t val, uint32_t otp); -uint32_t bsec_permanent_lock_otp(uint32_t otp); -void bsec_write_debug_conf(uint32_t val); uint32_t bsec_read_debug_conf(void); void bsec_write_scratch(uint32_t val); -uint32_t bsec_read_scratch(void); - -uint32_t bsec_get_status(void); -uint32_t bsec_get_hw_conf(void); -uint32_t bsec_get_version(void); -uint32_t bsec_get_id(void); -uint32_t bsec_get_magic_id(void); +/* Sticky lock support */ uint32_t bsec_set_sr_lock(uint32_t otp); uint32_t bsec_read_sr_lock(uint32_t otp, bool *value); uint32_t bsec_set_sw_lock(uint32_t otp); uint32_t bsec_read_sw_lock(uint32_t otp, bool *value); uint32_t bsec_set_sp_lock(uint32_t otp); uint32_t bsec_read_sp_lock(uint32_t otp, bool *value); -uint32_t bsec_read_permanent_lock(uint32_t otp, bool *value); -uint32_t bsec_otp_lock(uint32_t service); -uint32_t bsec_shadow_read_otp(uint32_t *otp_value, uint32_t word); +uint32_t bsec_get_secure_state(void); +static inline bool bsec_mode_is_closed_device(void) +{ + return (bsec_get_secure_state() & BSEC_STATE_MASK) == BSEC_STATE_SEC_CLOSED; +} + +#if defined(IMAGE_BL32) +uint32_t bsec_permanent_lock_otp(uint32_t otp); uint32_t bsec_check_nsec_access_rights(uint32_t otp); +#endif #endif /* BSEC_H */ diff --git a/include/drivers/st/bsec2_reg.h b/include/drivers/st/bsec2_reg.h index f8950205..fa44cf15 100644 --- a/include/drivers/st/bsec2_reg.h +++ b/include/drivers/st/bsec2_reg.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, STMicroelectronics - All Rights Reserved + * Copyright (c) 2022-2024, STMicroelectronics - All Rights Reserved * * SPDX-License-Identifier: BSD-3-Clause */ @@ -80,22 +80,17 @@ #define GPLOCK_LOCK_SHIFT 4 /* BSEC_OTP_STATUS Register */ -#define BSEC_MODE_STATUS_MASK GENMASK(2, 0) -#define BSEC_MODE_SECURE_MASK BIT(0) -#define BSEC_MODE_FULLDBG_MASK BIT(1) -#define BSEC_MODE_INVALID_MASK BIT(2) -#define BSEC_MODE_BUSY_MASK BIT(3) -#define BSEC_MODE_PROGFAIL_MASK BIT(4) -#define BSEC_MODE_PWR_MASK BIT(5) -#define BSEC_MODE_BIST1_LOCK_MASK BIT(6) -#define BSEC_MODE_BIST2_LOCK_MASK BIT(7) +#define BSEC_OTP_STATUS_SECURE BIT(0) +#define BSEC_OTP_STATUS_INVALID BIT(2) +#define BSEC_OTP_STATUS_BUSY BIT(3) +#define BSEC_OTP_STATUS_PROGFAIL BIT(4) +#define BSEC_OTP_STATUS_PWRON BIT(5) /* BSEC_DENABLE Register */ #define BSEC_HDPEN BIT(4) #define BSEC_SPIDEN BIT(5) #define BSEC_SPINDEN BIT(6) #define BSEC_DBGSWGEN BIT(10) -#define BSEC_DEN_ALL_MSK GENMASK(10, 0) /* BSEC_FENABLE Register */ #define BSEC_FEN_ALL_MSK GENMASK(14, 0) diff --git a/include/drivers/st/bsec3_reg.h b/include/drivers/st/bsec3_reg.h new file mode 100644 index 00000000..177e30ba --- /dev/null +++ b/include/drivers/st/bsec3_reg.h @@ -0,0 +1,103 @@ +/* + * Copyright (c) 2024, STMicroelectronics - All Rights Reserved + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef BSEC3_REG_H +#define BSEC3_REG_H + +#include <lib/utils_def.h> + +/* BSEC REGISTER OFFSET (base relative) */ +#define BSEC_FVR(x) (U(0x000) + 4U * (x)) +#define BSEC_SPLOCK(x) (U(0x800) + 4U * (x)) +#define BSEC_SWLOCK(x) (U(0x840) + 4U * (x)) +#define BSEC_SRLOCK(x) (U(0x880) + 4U * (x)) +#define BSEC_OTPVLDR(x) (U(0x8C0) + 4U * (x)) +#define BSEC_SFSR(x) (U(0x940) + 4U * (x)) +#define BSEC_OTPCR U(0xC04) +#define BSEC_WDR U(0xC08) +#define BSEC_SCRATCHR0 U(0xE00) +#define BSEC_SCRATCHR1 U(0xE04) +#define BSEC_SCRATCHR2 U(0xE08) +#define BSEC_SCRATCHR3 U(0xE0C) +#define BSEC_LOCKR U(0xE10) +#define BSEC_JTAGINR U(0xE14) +#define BSEC_JTAGOUTR U(0xE18) +#define BSEC_DENR U(0xE20) +#define BSEC_UNMAPR U(0xE24) +#define BSEC_SR U(0xE40) +#define BSEC_OTPSR U(0xE44) +#define BSEC_WRCR U(0xF00) +#define BSEC_HWCFGR U(0xFF0) +#define BSEC_VERR U(0xFF4) +#define BSEC_IPIDR U(0xFF8) +#define BSEC_SIDR U(0xFFC) + +/* BSEC_OTPCR register fields */ +#define BSEC_OTPCR_ADDR_MASK GENMASK_32(8, 0) +#define BSEC_OTPCR_ADDR_SHIFT U(0) +#define BSEC_OTPCR_PROG BIT_32(13) +#define BSEC_OTPCR_PPLOCK BIT_32(14) +#define BSEC_OTPCR_LASTCID_MASK GENMASK_32(21, 19) +#define BSEC_OTPCR_LASTCID_SHIFT U(19) + +/* BSEC_LOCKR register fields */ +#define BSEC_LOCKR_GWLOCK_MASK BIT_32(0) +#define BSEC_LOCKR_GWLOCK_SHIFT U(0) +#define BSEC_LOCKR_DENLOCK_MASK BIT_32(1) +#define BSEC_LOCKR_DENLOCK_SHIFT U(1) +#define BSEC_LOCKR_HKLOCK_MASK BIT_32(2) +#define BSEC_LOCKR_HKLOCK_SHIFT U(2) + +/* BSEC_DENR register fields */ +#define BSEC_DENR_LPDBGEN BIT_32(0) +#define BSEC_DENR_DBGENA BIT_32(1) +#define BSEC_DENR_NIDENA BIT_32(2) +#define BSEC_DENR_DEVICEEN BIT_32(3) +#define BSEC_DENR_HDPEN BIT_32(4) +#define BSEC_DENR_SPIDENA BIT_32(5) +#define BSEC_DENR_SPNIDENA BIT_32(6) +#define BSEC_DENR_DBGSWEN BIT_32(7) +#define BSEC_DENR_DBGENM BIT_32(8) +#define BSEC_DENR_NIDENM BIT_32(9) +#define BSEC_DENR_SPIDENM BIT_32(10) +#define BSEC_DENR_SPNIDENM BIT_32(11) +#define BSEC_DENR_CFGSDIS BIT_32(12) +#define BSEC_DENR_CP15SDIS_MASK GENMASK_32(14, 13) +#define BSEC_DENR_CP15SDIS_SHIFT U(13) +#define BSEC_DENR_LPDBGDIS BIT_32(15) +#define BSEC_DENR_ALL_MSK GENMASK_32(15, 0) + +/* BSEC_SR register fields */ +#define BSEC_SR_BUSY BIT_32(0) +#define BSEC_SR_HVALID BIT_32(1) +#define BSEC_SR_RNGERR BIT_32(2) +#define BSEC_SR_HKWW_MASK GENMASK_32(15, 8) +#define BSEC_SR_HKWW_SHIFT U(8) +#define BSEC_SR_NVSTATE_MASK GENMASK_32(31, 26) +#define BSEC_SR_NVSTATE_SHIFT U(26) +#define BSEC_SR_NVSTATE_OPEN U(0x16) +#define BSEC_SR_NVSTATE_CLOSED U(0x0D) +#define BSEC_SR_NVSTATE_OTP_LOCKED U(0x23) + +/* BSEC_OTPSR register fields */ +#define BSEC_OTPSR_BUSY BIT_32(0) +#define BSEC_OTPSR_FUSEOK BIT_32(1) +#define BSEC_OTPSR_HIDEUP BIT_32(2) +#define BSEC_OTPSR_OTPNVIR BIT_32(4) +#define BSEC_OTPSR_OTPERR BIT_32(5) +#define BSEC_OTPSR_OTPSEC BIT_32(6) +#define BSEC_OTPSR_PROGFAIL BIT_32(16) +#define BSEC_OTPSR_DISTURBF BIT_32(17) +#define BSEC_OTPSR_DEDF BIT_32(18) +#define BSEC_OTPSR_SECF BIT_32(19) +#define BSEC_OTPSR_PPLF BIT_32(20) +#define BSEC_OTPSR_PPLMF BIT_32(21) +#define BSEC_OTPSR_AMEF BIT_32(22) + +/* BSEC_VERR register fields */ +#define BSEC_VERR_MASK GENMASK_32(7, 0) + +#endif /* BSEC3_REG_H */ diff --git a/include/drivers/st/stm32_gpio.h b/include/drivers/st/stm32_gpio.h index eeef9da5..ef4eb04a 100644 --- a/include/drivers/st/stm32_gpio.h +++ b/include/drivers/st/stm32_gpio.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015-2022, STMicroelectronics - All Rights Reserved + * Copyright (c) 2015-2024, STMicroelectronics - All Rights Reserved * * SPDX-License-Identifier: BSD-3-Clause */ @@ -13,6 +13,7 @@ #define GPIO_TYPE_OFFSET U(0x04) #define GPIO_SPEED_OFFSET U(0x08) #define GPIO_PUPD_OFFSET U(0x0C) +#define GPIO_IDR_OFFSET U(0x10) #define GPIO_OD_OFFSET U(0x14) #define GPIO_BSRR_OFFSET U(0x18) #define GPIO_AFRL_OFFSET U(0x20) @@ -58,6 +59,16 @@ int dt_set_pinctrl_config(int node); void set_gpio_secure_cfg(uint32_t bank, uint32_t pin, bool secure); void set_gpio_reset_cfg(uint32_t bank, uint32_t pin); + +enum gpio_level { + GPIO_LEVEL_LOW, + GPIO_LEVEL_HIGH +}; + +void set_gpio_level(uint32_t bank, uint32_t pin, enum gpio_level level); +enum gpio_level get_gpio_level(uint32_t bank, uint32_t pin); + +void set_gpio_config(uint32_t bank, uint32_t pin, uint32_t config, uint8_t status); #endif /*__ASSEMBLER__*/ #endif /* STM32_GPIO_H */ diff --git a/include/drivers/st/stm32_i2c.h b/include/drivers/st/stm32_i2c.h index 170d4cf8..ccb574b3 100644 --- a/include/drivers/st/stm32_i2c.h +++ b/include/drivers/st/stm32_i2c.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016-2019, STMicroelectronics - All Rights Reserved + * Copyright (c) 2016-2024, STMicroelectronics - All Rights Reserved * * SPDX-License-Identifier: BSD-3-Clause */ @@ -294,7 +294,6 @@ struct i2c_handle_s { /* STM32 specific defines */ #define STM32_I2C_RISE_TIME_DEFAULT 25 /* ns */ #define STM32_I2C_FALL_TIME_DEFAULT 10 /* ns */ -#define STM32_I2C_SPEED_DEFAULT I2C_SPEED_STANDARD #define STM32_I2C_ANALOG_FILTER_DELAY_MIN 50 /* ns */ #define STM32_I2C_ANALOG_FILTER_DELAY_MAX 260 /* ns */ #define STM32_I2C_DIGITAL_FILTER_MAX 16 diff --git a/include/drivers/st/stm32mp1_clk.h b/include/drivers/st/stm32mp1_clk.h index e2395bc5..93ec1c59 100644 --- a/include/drivers/st/stm32mp1_clk.h +++ b/include/drivers/st/stm32mp1_clk.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018-2022, STMicroelectronics - All Rights Reserved + * Copyright (c) 2018-2024, STMicroelectronics - All Rights Reserved * * SPDX-License-Identifier: BSD-3-Clause */ @@ -32,6 +32,8 @@ bool stm32mp1_rcc_is_mckprot(void); void stm32mp1_clk_rcc_regs_lock(void); void stm32mp1_clk_rcc_regs_unlock(void); +void stm32mp1_clk_mcuss_protect(bool enable); + #ifdef STM32MP_SHARED_RESOURCES void stm32mp1_register_clock_parents_secure(unsigned long id); #endif diff --git a/include/drivers/st/stm32mp25_rcc.h b/include/drivers/st/stm32mp25_rcc.h index 9dd25f3c..d5d228cb 100644 --- a/include/drivers/st/stm32mp25_rcc.h +++ b/include/drivers/st/stm32mp25_rcc.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023, STMicroelectronics - All Rights Reserved + * Copyright (c) 2023-2024, STMicroelectronics - All Rights Reserved * * SPDX-License-Identifier: BSD-3-Clause */ @@ -426,7 +426,7 @@ #define RCC_USB2CFGR U(0x7FC) #define RCC_USB2PHY1CFGR U(0x800) #define RCC_USB2PHY2CFGR U(0x804) -#define RCC_USB3DRDCFGR U(0x808) +#define RCC_USB3DRCFGR U(0x808) #define RCC_USB3PCIEPHYCFGR U(0x80C) #define RCC_PCIECFGR U(0x810) #define RCC_USBTCCFGR U(0x814) @@ -459,7 +459,6 @@ #define RCC_IWDG5CFGR U(0x898) #define RCC_WWDG1CFGR U(0x89C) #define RCC_WWDG2CFGR U(0x8A0) -#define RCC_BUSPERFMCFGR U(0x8A4) #define RCC_VREFCFGR U(0x8A8) #define RCC_TMPSENSCFGR U(0x8AC) #define RCC_CRCCFGR U(0x8B4) @@ -2352,11 +2351,13 @@ /* RCC_C1SREQSETR register fields */ #define RCC_C1SREQSETR_STPREQ_P0 BIT(0) #define RCC_C1SREQSETR_STPREQ_P1 BIT(1) +#define RCC_C1SREQSETR_STPREQ_MASK GENMASK_32(1, 0) #define RCC_C1SREQSETR_ESLPREQ BIT(16) /* RCC_C1SREQCLRR register fields */ #define RCC_C1SREQCLRR_STPREQ_P0 BIT(0) #define RCC_C1SREQCLRR_STPREQ_P1 BIT(1) +#define RCC_C1SREQCLRR_STPREQ_MASK GENMASK_32(1, 0) #define RCC_C1SREQCLRR_ESLPREQ BIT(16) /* RCC_CPUBOOTCR register fields */ @@ -2401,12 +2402,12 @@ #define RCC_BDCR_LSEDRV_WIDTH 2 /* RCC_D3DCR register fields */ -#define RCC_D3DCR_CSION BIT(0) -#define RCC_D3DCR_CSIKERON BIT(1) -#define RCC_D3DCR_CSIRDY BIT(2) +#define RCC_D3DCR_MSION BIT(0) +#define RCC_D3DCR_MSIKERON BIT(1) +#define RCC_D3DCR_MSIRDY BIT(2) #define RCC_D3DCR_D3PERCKSEL_MASK GENMASK_32(17, 16) #define RCC_D3DCR_D3PERCKSEL_SHIFT 16 -#define RCC_D3DCR_CSIRDY_BIT 2 +#define RCC_D3DCR_MSIRDY_BIT 2 /* RCC_D3DSR register fields */ #define RCC_D3DSR_D3STATE_MASK GENMASK_32(1, 0) @@ -3458,11 +3459,11 @@ #define RCC_USB2PHYxCFGR_USB2PHY1STPEN BIT(4) #define RCC_USB2PHYxCFGR_USB2PHY1CKREFSEL BIT(15) -/* RCC_USB3DRDCFGR register fields */ -#define RCC_USB3DRDCFGR_USB3DRDRST BIT(0) -#define RCC_USB3DRDCFGR_USB3DRDEN BIT(1) -#define RCC_USB3DRDCFGR_USB3DRDLPEN BIT(2) -#define RCC_USB3DRDCFGR_USB3DRDSTPEN BIT(4) +/* RCC_USB3DRCFGR register fields */ +#define RCC_USB3DRCFGR_USB3DRRST BIT(0) +#define RCC_USB3DRCFGR_USB3DREN BIT(1) +#define RCC_USB3DRCFGR_USB3DRLPEN BIT(2) +#define RCC_USB3DRCFGR_USB3DRSTPEN BIT(4) /* RCC_USB3PCIEPHYCFGR register fields */ #define RCC_USB3PCIEPHYCFGR_USB3PCIEPHYRST BIT(0) @@ -3647,11 +3648,6 @@ #define RCC_WWDG2CFGR_WWDG2LPEN BIT(2) #define RCC_WWDG2CFGR_WWDG2AMEN BIT(3) -/* RCC_BUSPERFMCFGR register fields */ -#define RCC_BUSPERFMCFGR_BUSPERFMRST BIT(0) -#define RCC_BUSPERFMCFGR_BUSPERFMEN BIT(1) -#define RCC_BUSPERFMCFGR_BUSPERFMLPEN BIT(2) - /* RCC_VREFCFGR register fields */ #define RCC_VREFCFGR_VREFRST BIT(0) #define RCC_VREFCFGR_VREFEN BIT(1) diff --git a/include/drivers/st/stm32mp2_clk.h b/include/drivers/st/stm32mp2_clk.h new file mode 100644 index 00000000..b9226cdc --- /dev/null +++ b/include/drivers/st/stm32mp2_clk.h @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2024, STMicroelectronics - All Rights Reserved + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef STM32MP2_CLK_H +#define STM32MP2_CLK_H + +#include <platform_def.h> + +enum stm32mp_osc_id { + _HSI, + _HSE, + _CSI, + _LSI, + _LSE, + _I2S_CKIN, + _SPDIF_SYMB, + NB_OSC, + _UNKNOWN_OSC_ID = 0xFF +}; + +extern const char *stm32mp_osc_node_label[NB_OSC]; + +enum pll_cfg { + FBDIV, + REFDIV, + POSTDIV1, + POSTDIV2, + PLLCFG_NB +}; + +enum pll_csg { + DIVVAL, + SPREAD, + DOWNSPREAD, + PLLCSG_NB +}; + +int stm32mp2_clk_init(void); +int stm32mp2_pll1_disable(void); + +#endif /* STM32MP2_CLK_H */ diff --git a/include/drivers/st/stm32mp2_ddr.h b/include/drivers/st/stm32mp2_ddr.h new file mode 100644 index 00000000..6b0462c8 --- /dev/null +++ b/include/drivers/st/stm32mp2_ddr.h @@ -0,0 +1,147 @@ +/* + * Copyright (C) 2021-2024, STMicroelectronics - All Rights Reserved + * + * SPDX-License-Identifier: GPL-2.0+ OR BSD-3-Clause + */ + +#ifndef STM32MP2_DDR_H +#define STM32MP2_DDR_H + +#include <stdbool.h> + +#include <ddrphy_phyinit_struct.h> + +#include <drivers/st/stm32mp_ddr.h> + +struct stm32mp2_ddrctrl_reg { + uint32_t mstr; + uint32_t mrctrl0; + uint32_t mrctrl1; + uint32_t mrctrl2; + uint32_t derateen; + uint32_t derateint; + uint32_t deratectl; + uint32_t pwrctl; + uint32_t pwrtmg; + uint32_t hwlpctl; + uint32_t rfshctl0; + uint32_t rfshctl1; + uint32_t rfshctl3; + uint32_t crcparctl0; + uint32_t crcparctl1; + uint32_t init0; + uint32_t init1; + uint32_t init2; + uint32_t init3; + uint32_t init4; + uint32_t init5; + uint32_t init6; + uint32_t init7; + uint32_t dimmctl; + uint32_t rankctl; + uint32_t rankctl1; + uint32_t zqctl0; + uint32_t zqctl1; + uint32_t zqctl2; + uint32_t dfitmg0; + uint32_t dfitmg1; + uint32_t dfilpcfg0; + uint32_t dfilpcfg1; + uint32_t dfiupd0; + uint32_t dfiupd1; + uint32_t dfiupd2; + uint32_t dfimisc; + uint32_t dfitmg2; + uint32_t dfitmg3; + uint32_t dbictl; + uint32_t dfiphymstr; + uint32_t dbg0; + uint32_t dbg1; + uint32_t dbgcmd; + uint32_t swctl; + uint32_t swctlstatic; + uint32_t poisoncfg; + uint32_t pccfg; +}; + +struct stm32mp2_ddrctrl_timing { + uint32_t rfshtmg; + uint32_t rfshtmg1; + uint32_t dramtmg0; + uint32_t dramtmg1; + uint32_t dramtmg2; + uint32_t dramtmg3; + uint32_t dramtmg4; + uint32_t dramtmg5; + uint32_t dramtmg6; + uint32_t dramtmg7; + uint32_t dramtmg8; + uint32_t dramtmg9; + uint32_t dramtmg10; + uint32_t dramtmg11; + uint32_t dramtmg12; + uint32_t dramtmg13; + uint32_t dramtmg14; + uint32_t dramtmg15; + uint32_t odtcfg; + uint32_t odtmap; +}; + +struct stm32mp2_ddrctrl_map { + uint32_t addrmap0; + uint32_t addrmap1; + uint32_t addrmap2; + uint32_t addrmap3; + uint32_t addrmap4; + uint32_t addrmap5; + uint32_t addrmap6; + uint32_t addrmap7; + uint32_t addrmap8; + uint32_t addrmap9; + uint32_t addrmap10; + uint32_t addrmap11; +}; + +struct stm32mp2_ddrctrl_perf { + uint32_t sched; + uint32_t sched1; + uint32_t perfhpr1; + uint32_t perflpr1; + uint32_t perfwr1; + uint32_t sched3; + uint32_t sched4; + uint32_t pcfgr_0; + uint32_t pcfgw_0; + uint32_t pctrl_0; + uint32_t pcfgqos0_0; + uint32_t pcfgqos1_0; + uint32_t pcfgwqos0_0; + uint32_t pcfgwqos1_0; +#if STM32MP_DDR_DUAL_AXI_PORT + uint32_t pcfgr_1; + uint32_t pcfgw_1; + uint32_t pctrl_1; + uint32_t pcfgqos0_1; + uint32_t pcfgqos1_1; + uint32_t pcfgwqos0_1; + uint32_t pcfgwqos1_1; +#endif /* STM32MP_DDR_DUAL_AXI_PORT */ +}; + +struct stm32mp_ddr_config { + struct stm32mp_ddr_info info; + struct stm32mp2_ddrctrl_reg c_reg; + struct stm32mp2_ddrctrl_timing c_timing; + struct stm32mp2_ddrctrl_map c_map; + struct stm32mp2_ddrctrl_perf c_perf; + bool self_refresh; + uint32_t zdata; + struct user_input_basic uib; + struct user_input_advanced uia; + struct user_input_mode_register uim; + struct user_input_swizzle uis; +}; + +void stm32mp2_ddr_init(struct stm32mp_ddr_priv *priv, struct stm32mp_ddr_config *config); + +#endif /* STM32MP2_DDR_H */ diff --git a/include/drivers/st/stm32mp2_ddr_helpers.h b/include/drivers/st/stm32mp2_ddr_helpers.h new file mode 100644 index 00000000..9329fff2 --- /dev/null +++ b/include/drivers/st/stm32mp2_ddr_helpers.h @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2024, STMicroelectronics - All Rights Reserved + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef STM32MP2_DDR_HELPERS_H +#define STM32MP2_DDR_HELPERS_H + +#include <stdbool.h> +#include <stdint.h> + +#include <drivers/st/stm32mp2_ddr_regs.h> + +enum stm32mp2_ddr_sr_mode { + DDR_SR_MODE_INVALID = 0, + DDR_SSR_MODE, + DDR_HSR_MODE, + DDR_ASR_MODE, +}; + +void ddr_activate_controller(struct stm32mp_ddrctl *ctl, bool sr_entry); +void ddr_wait_lp3_mode(bool state); +int ddr_sr_exit_loop(void); +uint32_t ddr_get_io_calibration_val(void); +int ddr_sr_entry(bool standby); +int ddr_sr_exit(void); +enum stm32mp2_ddr_sr_mode ddr_read_sr_mode(void); +void ddr_set_sr_mode(enum stm32mp2_ddr_sr_mode mode); +void ddr_save_sr_mode(void); +void ddr_restore_sr_mode(void); +void ddr_sub_system_clk_init(void); +void ddr_sub_system_clk_off(void); + +#endif /* STM32MP2_DDR_HELPERS_H */ diff --git a/include/drivers/st/stm32mp2_ddr_regs.h b/include/drivers/st/stm32mp2_ddr_regs.h new file mode 100644 index 00000000..9370f1ca --- /dev/null +++ b/include/drivers/st/stm32mp2_ddr_regs.h @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2021-2024, STMicroelectronics - All Rights Reserved + * + * SPDX-License-Identifier: GPL-2.0+ OR BSD-3-Clause + */ + +#ifndef STM32MP2_DDR_REGS_H +#define STM32MP2_DDR_REGS_H + +#include <drivers/st/stm32mp_ddrctrl_regs.h> +#include <lib/utils_def.h> + +/* DDR Physical Interface Control (DDRPHYC) registers*/ +struct stm32mp_ddrphy { + uint32_t dummy; +} __packed; + +/* DDRPHY registers offsets */ +#define DDRPHY_INITENG0_P0_SEQ0BDISABLEFLAG6 U(0x240004) +#define DDRPHY_INITENG0_P0_PHYINLPX U(0x2400A0) +#define DDRPHY_DRTUB0_UCCLKHCLKENABLES U(0x300200) +#define DDRPHY_APBONLY0_MICROCONTMUXSEL U(0x340000) + +/* DDRPHY registers fields */ +#define DDRPHY_INITENG0_P0_PHYINLPX_PHYINLP3 BIT(0) +#define DDRPHY_DRTUB0_UCCLKHCLKENABLES_UCCLKEN BIT(0) +#define DDRPHY_DRTUB0_UCCLKHCLKENABLES_HCLKEN BIT(1) +#define DDRPHY_APBONLY0_MICROCONTMUXSEL_MICROCONTMUXSEL BIT(0) + +/* DDRDBG registers offsets */ +#define DDRDBG_LP_DISABLE U(0x0) +#define DDRDBG_BYPASS_PCLKEN U(0x4) + +/* DDRDBG registers fields */ +#define DDRDBG_LP_DISABLE_LPI_XPI_DISABLE BIT(0) +#define DDRDBG_LP_DISABLE_LPI_DDRC_DISABLE BIT(8) + +#endif /* STM32MP2_DDR_REGS_H */ diff --git a/include/drivers/st/stm32mp2_pwr.h b/include/drivers/st/stm32mp2_pwr.h new file mode 100644 index 00000000..356399af --- /dev/null +++ b/include/drivers/st/stm32mp2_pwr.h @@ -0,0 +1,478 @@ +/* + * Copyright (c) 2018-2024, STMicroelectronics - All Rights Reserved + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef STM32MP2_PWR_H +#define STM32MP2_PWR_H + +#include <lib/utils_def.h> + +#define PWR_CR1 U(0x00) +#define PWR_CR2 U(0x04) +#define PWR_CR3 U(0x08) +#define PWR_CR4 U(0x0C) +#define PWR_CR5 U(0x10) +#define PWR_CR6 U(0x14) +#define PWR_CR7 U(0x18) +#define PWR_CR8 U(0x1C) +#define PWR_CR9 U(0x20) +#define PWR_CR10 U(0x24) +#define PWR_CR11 U(0x28) +#define PWR_CR12 U(0x2C) +#define PWR_UCPDR U(0x30) +#define PWR_BDCR1 U(0x38) +#define PWR_BDCR2 U(0x3C) +#define PWR_CPU1CR U(0x40) +#define PWR_CPU2CR U(0x44) +#define PWR_CPU3CR U(0x48) +#define PWR_D1CR U(0x4C) +#define PWR_D2CR U(0x50) +#define PWR_D3CR U(0x54) +#define PWR_WKUPCR1 U(0x60) +#define PWR_WKUPCR2 U(0x64) +#define PWR_WKUPCR3 U(0x68) +#define PWR_WKUPCR4 U(0x6C) +#define PWR_WKUPCR5 U(0x70) +#define PWR_WKUPCR6 U(0x74) +#define PWR_D3WKUPENR U(0x98) +#define PWR_RSECCFGR U(0x100) +#define PWR_RPRIVCFGR U(0x104) +#define PWR_R0CIDCFGR U(0x108) +#define PWR_R1CIDCFGR U(0x10C) +#define PWR_R2CIDCFGR U(0x110) +#define PWR_R3CIDCFGR U(0x114) +#define PWR_R4CIDCFGR U(0x118) +#define PWR_R5CIDCFGR U(0x11C) +#define PWR_R6CIDCFGR U(0x120) +#define PWR_WIOSECCFGR U(0x180) +#define PWR_WIOPRIVCFGR U(0x184) +#define PWR_WIO1CIDCFGR U(0x188) +#define PWR_WIO1SEMCR U(0x18C) +#define PWR_WIO2CIDCFGR U(0x190) +#define PWR_WIO2SEMCR U(0x194) +#define PWR_WIO3CIDCFGR U(0x198) +#define PWR_WIO3SEMCR U(0x19C) +#define PWR_WIO4CIDCFGR U(0x1A0) +#define PWR_WIO4SEMCR U(0x1A4) +#define PWR_WIO5CIDCFGR U(0x1A8) +#define PWR_WIO5SEMCR U(0x1AC) +#define PWR_WIO6CIDCFGR U(0x1B0) +#define PWR_WIO6SEMCR U(0x1B4) +#define PWR_CPU1D1SR U(0x200) +#define PWR_CPU2D2SR U(0x204) +#define PWR_CPU3D3SR U(0x208) +#define PWR_DBGR U(0x308) +#define PWR_VERR U(0x3F4) +#define PWR_IPIDR U(0x3F8) +#define PWR_SIDR U(0x3FC) + +/* PWR_CR1 register fields */ +#define PWR_CR1_VDDIO3VMEN BIT_32(0) +#define PWR_CR1_VDDIO4VMEN BIT_32(1) +#define PWR_CR1_USB33VMEN BIT_32(2) +#define PWR_CR1_UCPDVMEN BIT_32(3) +#define PWR_CR1_AVMEN BIT_32(4) +#define PWR_CR1_VDDIO3SV BIT_32(8) +#define PWR_CR1_VDDIO4SV BIT_32(9) +#define PWR_CR1_USB33SV BIT_32(10) +#define PWR_CR1_UCPDSV BIT_32(11) +#define PWR_CR1_ASV BIT_32(12) +#define PWR_CR1_VDDIO3RDY BIT_32(16) +#define PWR_CR1_VDDIO4RDY BIT_32(17) +#define PWR_CR1_USB33RDY BIT_32(18) +#define PWR_CR1_UCPDRDY BIT_32(19) +#define PWR_CR1_ARDY BIT_32(20) +#define PWR_CR1_VDDIOVRSEL BIT_32(24) +#define PWR_CR1_VDDIO3VRSEL BIT_32(25) +#define PWR_CR1_VDDIO4VRSEL BIT_32(26) +#define PWR_CR1_GPVMO BIT_32(31) + +/* PWR_CR2 register fields */ +#define PWR_CR2_MONEN BIT_32(0) +#define PWR_CR2_VBATL BIT_32(8) +#define PWR_CR2_VBATH BIT_32(9) +#define PWR_CR2_TEMPL BIT_32(10) +#define PWR_CR2_TEMPH BIT_32(11) + +/* PWR_CR3 register fields */ +#define PWR_CR3_PVDEN BIT_32(0) +#define PWR_CR3_PVDO BIT_32(8) + +/* PWR_CR5 register fields */ +#define PWR_CR5_VCOREMONEN BIT_32(0) +#define PWR_CR5_VCOREL BIT_32(8) +#define PWR_CR5_VCOREH BIT_32(9) + +/* PWR_CR6 register fields */ +#define PWR_CR6_VCPUMONEN BIT_32(0) +#define PWR_CR6_VCPULLS BIT_32(4) +#define PWR_CR6_VCPUL BIT_32(8) +#define PWR_CR6_VCPUH BIT_32(9) + +/* PWR_CR7 register fields */ +#define PWR_CR7_VDDIO2VMEN BIT_32(0) +#define PWR_CR7_VDDIO2SV BIT_32(8) +#define PWR_CR7_VDDIO2RDY BIT_32(16) +#define PWR_CR7_VDDIO2VRSEL BIT_32(24) +#define PWR_CR7_VDDIO2VRSTBY BIT_32(25) + +/* PWR_CR8 register fields */ +#define PWR_CR8_VDDIO1VMEN BIT_32(0) +#define PWR_CR8_VDDIO1SV BIT_32(8) +#define PWR_CR8_VDDIO1RDY BIT_32(16) +#define PWR_CR8_VDDIO1VRSEL BIT_32(24) +#define PWR_CR8_VDDIO1VRSTBY BIT_32(25) + +/* PWR_CR9 register fields */ +#define PWR_CR9_BKPRBSEN BIT_32(0) +#define PWR_CR9_LPR1BSEN BIT_32(4) + +/* PWR_CR10 register fields */ +#define PWR_CR10_RETRBSEN_MASK GENMASK_32(1, 0) +#define PWR_CR10_RETRBSEN_SHIFT U(0) + +/* PWR_CR11 register fields */ +#define PWR_CR11_DDRRETDIS BIT_32(0) + +/* PWR_CR12 register fields */ +#define PWR_CR12_GPUVMEN BIT_32(0) +#define PWR_CR12_GPULVTEN BIT_32(1) +#define PWR_CR12_GPUSV BIT_32(8) +#define PWR_CR12_VDDGPURDY BIT_32(16) + +/* PWR_UCPDR register fields */ +#define PWR_UCPDR_UCPD_DBDIS BIT_32(0) +#define PWR_UCPDR_UCPD_STBY BIT_32(1) + +/* PWR_BDCR1 register fields */ +#define PWR_BDCR1_DBD3P BIT_32(0) + +/* PWR_BDCR2 register fields */ +#define PWR_BDCR2_DBP BIT_32(0) + +/* PWR_CPU1CR register fields */ +#define PWR_CPU1CR_PDDS_D2 BIT_32(0) +#define PWR_CPU1CR_PDDS_D1 BIT_32(1) +#define PWR_CPU1CR_VBF BIT_32(4) +#define PWR_CPU1CR_STOPF BIT_32(5) +#define PWR_CPU1CR_SBF BIT_32(6) +#define PWR_CPU1CR_SBF_D1 BIT_32(7) +#define PWR_CPU1CR_SBF_D3 BIT_32(8) +#define PWR_CPU1CR_CSSF BIT_32(9) +#define PWR_CPU1CR_STANDBYWFIL2 BIT_32(15) +#define PWR_CPU1CR_LPDS_D1 BIT_32(16) +#define PWR_CPU1CR_LVDS_D1 BIT_32(17) + +/* PWR_CPU2CR register fields */ +#define PWR_CPU2CR_PDDS_D2 BIT_32(0) +#define PWR_CPU2CR_VBF BIT_32(4) +#define PWR_CPU2CR_STOPF BIT_32(5) +#define PWR_CPU2CR_SBF BIT_32(6) +#define PWR_CPU2CR_SBF_D2 BIT_32(7) +#define PWR_CPU2CR_SBF_D3 BIT_32(8) +#define PWR_CPU2CR_CSSF BIT_32(9) +#define PWR_CPU2CR_DEEPSLEEP BIT_32(15) +#define PWR_CPU2CR_LPDS_D2 BIT_32(16) +#define PWR_CPU2CR_LVDS_D2 BIT_32(17) + +/* PWR_CPU3CR register fields */ +#define PWR_CPU3CR_VBF BIT_32(4) +#define PWR_CPU3CR_SBF_D3 BIT_32(8) +#define PWR_CPU3CR_CSSF BIT_32(9) +#define PWR_CPU3CR_DEEPSLEEP BIT_32(15) + +/* PWR_D1CR register fields */ +#define PWR_D1CR_LPCFG_D1 BIT_32(0) +#define PWR_D1CR_POPL_D1_MASK GENMASK_32(12, 8) +#define PWR_D1CR_POPL_D1_SHIFT U(8) + +/* PWR_D2CR register fields */ +#define PWR_D2CR_LPCFG_D2 BIT_32(0) +#define PWR_D2CR_POPL_D2_MASK GENMASK_32(12, 8) +#define PWR_D2CR_POPL_D2_SHIFT U(8) +#define PWR_D2CR_LPLVDLY_D2_MASK GENMASK_32(18, 16) +#define PWR_D2CR_LPLVDLY_D2_SHIFT U(16) +#define PWR_D2CR_PODH_D2_MASK GENMASK_32(27, 24) +#define PWR_D2CR_PODH_D2_SHIFT U(24) + +/* PWR_D3CR register fields */ +#define PWR_D3CR_PDDS_D3 BIT_32(0) +#define PWR_D3CR_D3RDY BIT_32(31) + +/* PWR_WKUPCR1 register fields */ +#define PWR_WKUPCR1_WKUPC BIT_32(0) +#define PWR_WKUPCR1_WKUPP BIT_32(8) +#define PWR_WKUPCR1_WKUPPUPD_MASK GENMASK_32(13, 12) +#define PWR_WKUPCR1_WKUPPUPD_SHIFT U(12) +#define PWR_WKUPCR1_WKUPENCPU1 BIT_32(16) +#define PWR_WKUPCR1_WKUPENCPU2 BIT_32(17) +#define PWR_WKUPCR1_WKUPF BIT_32(31) + +/* PWR_WKUPCR2 register fields */ +#define PWR_WKUPCR2_WKUPC BIT_32(0) +#define PWR_WKUPCR2_WKUPP BIT_32(8) +#define PWR_WKUPCR2_WKUPPUPD_MASK GENMASK_32(13, 12) +#define PWR_WKUPCR2_WKUPPUPD_SHIFT U(12) +#define PWR_WKUPCR2_WKUPENCPU1 BIT_32(16) +#define PWR_WKUPCR2_WKUPENCPU2 BIT_32(17) +#define PWR_WKUPCR2_WKUPF BIT_32(31) + +/* PWR_WKUPCR3 register fields */ +#define PWR_WKUPCR3_WKUPC BIT_32(0) +#define PWR_WKUPCR3_WKUPP BIT_32(8) +#define PWR_WKUPCR3_WKUPPUPD_MASK GENMASK_32(13, 12) +#define PWR_WKUPCR3_WKUPPUPD_SHIFT U(12) +#define PWR_WKUPCR3_WKUPENCPU1 BIT_32(16) +#define PWR_WKUPCR3_WKUPENCPU2 BIT_32(17) +#define PWR_WKUPCR3_WKUPF BIT_32(31) + +/* PWR_WKUPCR4 register fields */ +#define PWR_WKUPCR4_WKUPC BIT_32(0) +#define PWR_WKUPCR4_WKUPP BIT_32(8) +#define PWR_WKUPCR4_WKUPPUPD_MASK GENMASK_32(13, 12) +#define PWR_WKUPCR4_WKUPPUPD_SHIFT U(12) +#define PWR_WKUPCR4_WKUPENCPU1 BIT_32(16) +#define PWR_WKUPCR4_WKUPENCPU2 BIT_32(17) +#define PWR_WKUPCR4_WKUPF BIT_32(31) + +/* PWR_WKUPCR5 register fields */ +#define PWR_WKUPCR5_WKUPC BIT_32(0) +#define PWR_WKUPCR5_WKUPP BIT_32(8) +#define PWR_WKUPCR5_WKUPPUPD_MASK GENMASK_32(13, 12) +#define PWR_WKUPCR5_WKUPPUPD_SHIFT U(12) +#define PWR_WKUPCR5_WKUPENCPU1 BIT_32(16) +#define PWR_WKUPCR5_WKUPENCPU2 BIT_32(17) +#define PWR_WKUPCR5_WKUPF BIT_32(31) + +/* PWR_WKUPCR6 register fields */ +#define PWR_WKUPCR6_WKUPC BIT_32(0) +#define PWR_WKUPCR6_WKUPP BIT_32(8) +#define PWR_WKUPCR6_WKUPPUPD_MASK GENMASK_32(13, 12) +#define PWR_WKUPCR6_WKUPPUPD_SHIFT U(12) +#define PWR_WKUPCR6_WKUPENCPU1 BIT_32(16) +#define PWR_WKUPCR6_WKUPENCPU2 BIT_32(17) +#define PWR_WKUPCR6_WKUPF BIT_32(31) + +/* PWR_D3WKUPENR register fields */ +#define PWR_D3WKUPENR_TAMP_WKUPEN_D3 BIT_32(0) + +/* PWR_RSECCFGR register fields */ +#define PWR_RSECCFGR_RSEC0 BIT_32(0) +#define PWR_RSECCFGR_RSEC1 BIT_32(1) +#define PWR_RSECCFGR_RSEC2 BIT_32(2) +#define PWR_RSECCFGR_RSEC3 BIT_32(3) +#define PWR_RSECCFGR_RSEC4 BIT_32(4) +#define PWR_RSECCFGR_RSEC5 BIT_32(5) +#define PWR_RSECCFGR_RSEC6 BIT_32(6) + +/* PWR_RPRIVCFGR register fields */ +#define PWR_RPRIVCFGR_RPRIV0 BIT_32(0) +#define PWR_RPRIVCFGR_RPRIV1 BIT_32(1) +#define PWR_RPRIVCFGR_RPRIV2 BIT_32(2) +#define PWR_RPRIVCFGR_RPRIV3 BIT_32(3) +#define PWR_RPRIVCFGR_RPRIV4 BIT_32(4) +#define PWR_RPRIVCFGR_RPRIV5 BIT_32(5) +#define PWR_RPRIVCFGR_RPRIV6 BIT_32(6) + +/* PWR_R0CIDCFGR register fields */ +#define PWR_R0CIDCFGR_CFEN BIT_32(0) +#define PWR_R0CIDCFGR_SCID_MASK GENMASK_32(6, 4) +#define PWR_R0CIDCFGR_SCID_SHIFT U(4) + +/* PWR_R1CIDCFGR register fields */ +#define PWR_R1CIDCFGR_CFEN BIT_32(0) +#define PWR_R1CIDCFGR_SCID_MASK GENMASK_32(6, 4) +#define PWR_R1CIDCFGR_SCID_SHIFT U(4) + +/* PWR_R2CIDCFGR register fields */ +#define PWR_R2CIDCFGR_CFEN BIT_32(0) +#define PWR_R2CIDCFGR_SCID_MASK GENMASK_32(6, 4) +#define PWR_R2CIDCFGR_SCID_SHIFT U(4) + +/* PWR_R3CIDCFGR register fields */ +#define PWR_R3CIDCFGR_CFEN BIT_32(0) +#define PWR_R3CIDCFGR_SCID_MASK GENMASK_32(6, 4) +#define PWR_R3CIDCFGR_SCID_SHIFT U(4) + +/* PWR_R4CIDCFGR register fields */ +#define PWR_R4CIDCFGR_CFEN BIT_32(0) +#define PWR_R4CIDCFGR_SCID_MASK GENMASK_32(6, 4) +#define PWR_R4CIDCFGR_SCID_SHIFT U(4) + +/* PWR_R5CIDCFGR register fields */ +#define PWR_R5CIDCFGR_CFEN BIT_32(0) +#define PWR_R5CIDCFGR_SCID_MASK GENMASK_32(6, 4) +#define PWR_R5CIDCFGR_SCID_SHIFT U(4) + +/* PWR_R6CIDCFGR register fields */ +#define PWR_R6CIDCFGR_CFEN BIT_32(0) +#define PWR_R6CIDCFGR_SCID_MASK GENMASK_32(6, 4) +#define PWR_R6CIDCFGR_SCID_SHIFT U(4) + +/* PWR_WIOSECCFGR register fields */ +#define PWR_WIOSECCFGR_WIOSEC1 BIT_32(0) +#define PWR_WIOSECCFGR_WIOSEC2 BIT_32(1) +#define PWR_WIOSECCFGR_WIOSEC3 BIT_32(2) +#define PWR_WIOSECCFGR_WIOSEC4 BIT_32(3) +#define PWR_WIOSECCFGR_WIOSEC5 BIT_32(4) +#define PWR_WIOSECCFGR_WIOSEC6 BIT_32(5) + +/* PWR_WIOPRIVCFGR register fields */ +#define PWR_WIOPRIVCFGR_WIOPRIV1 BIT_32(0) +#define PWR_WIOPRIVCFGR_WIOPRIV2 BIT_32(1) +#define PWR_WIOPRIVCFGR_WIOPRIV3 BIT_32(2) +#define PWR_WIOPRIVCFGR_WIOPRIV4 BIT_32(3) +#define PWR_WIOPRIVCFGR_WIOPRIV5 BIT_32(4) +#define PWR_WIOPRIVCFGR_WIOPRIV6 BIT_32(5) + +/* PWR_WIO1CIDCFGR register fields */ +#define PWR_WIO1CIDCFGR_CFEN BIT_32(0) +#define PWR_WIO1CIDCFGR_SEM_EN BIT_32(1) +#define PWR_WIO1CIDCFGR_SCID_MASK GENMASK_32(6, 4) +#define PWR_WIO1CIDCFGR_SCID_SHIFT U(4) +#define PWR_WIO1CIDCFGR_SEMWLC0 BIT_32(16) +#define PWR_WIO1CIDCFGR_SEMWLC1 BIT_32(17) +#define PWR_WIO1CIDCFGR_SEMWLC2 BIT_32(18) +#define PWR_WIO1CIDCFGR_SEMWLC3 BIT_32(19) +#define PWR_WIO1CIDCFGR_SEMWLC4 BIT_32(20) +#define PWR_WIO1CIDCFGR_SEMWLC5 BIT_32(21) +#define PWR_WIO1CIDCFGR_SEMWLC6 BIT_32(22) +#define PWR_WIO1CIDCFGR_SEMWLC7 BIT_32(23) + +/* PWR_WIO1SEMCR register fields */ +#define PWR_WIO1SEMCR_SEM_MUTEX BIT_32(0) +#define PWR_WIO1SEMCR_SEMCID_MASK GENMASK_32(6, 4) +#define PWR_WIO1SEMCR_SEMCID_SHIFT U(4) + +/* PWR_WIO2CIDCFGR register fields */ +#define PWR_WIO2CIDCFGR_CFEN BIT_32(0) +#define PWR_WIO2CIDCFGR_SEM_EN BIT_32(1) +#define PWR_WIO2CIDCFGR_SCID_MASK GENMASK_32(6, 4) +#define PWR_WIO2CIDCFGR_SCID_SHIFT U(4) +#define PWR_WIO2CIDCFGR_SEMWLC0 BIT_32(16) +#define PWR_WIO2CIDCFGR_SEMWLC1 BIT_32(17) +#define PWR_WIO2CIDCFGR_SEMWLC2 BIT_32(18) +#define PWR_WIO2CIDCFGR_SEMWLC3 BIT_32(19) +#define PWR_WIO2CIDCFGR_SEMWLC4 BIT_32(20) +#define PWR_WIO2CIDCFGR_SEMWLC5 BIT_32(21) +#define PWR_WIO2CIDCFGR_SEMWLC6 BIT_32(22) +#define PWR_WIO2CIDCFGR_SEMWLC7 BIT_32(23) + +/* PWR_WIO2SEMCR register fields */ +#define PWR_WIO2SEMCR_SEM_MUTEX BIT_32(0) +#define PWR_WIO2SEMCR_SEMCID_MASK GENMASK_32(6, 4) +#define PWR_WIO2SEMCR_SEMCID_SHIFT U(4) + +/* PWR_WIO3CIDCFGR register fields */ +#define PWR_WIO3CIDCFGR_CFEN BIT_32(0) +#define PWR_WIO3CIDCFGR_SEM_EN BIT_32(1) +#define PWR_WIO3CIDCFGR_SCID_MASK GENMASK_32(6, 4) +#define PWR_WIO3CIDCFGR_SCID_SHIFT U(4) +#define PWR_WIO3CIDCFGR_SEMWLC0 BIT_32(16) +#define PWR_WIO3CIDCFGR_SEMWLC1 BIT_32(17) +#define PWR_WIO3CIDCFGR_SEMWLC2 BIT_32(18) +#define PWR_WIO3CIDCFGR_SEMWLC3 BIT_32(19) +#define PWR_WIO3CIDCFGR_SEMWLC4 BIT_32(20) +#define PWR_WIO3CIDCFGR_SEMWLC5 BIT_32(21) +#define PWR_WIO3CIDCFGR_SEMWLC6 BIT_32(22) +#define PWR_WIO3CIDCFGR_SEMWLC7 BIT_32(23) + +/* PWR_WIO3SEMCR register fields */ +#define PWR_WIO3SEMCR_SEM_MUTEX BIT_32(0) +#define PWR_WIO3SEMCR_SEMCID_MASK GENMASK_32(6, 4) +#define PWR_WIO3SEMCR_SEMCID_SHIFT U(4) + +/* PWR_WIO4CIDCFGR register fields */ +#define PWR_WIO4CIDCFGR_CFEN BIT_32(0) +#define PWR_WIO4CIDCFGR_SEM_EN BIT_32(1) +#define PWR_WIO4CIDCFGR_SCID_MASK GENMASK_32(6, 4) +#define PWR_WIO4CIDCFGR_SCID_SHIFT U(4) +#define PWR_WIO4CIDCFGR_SEMWLC0 BIT_32(16) +#define PWR_WIO4CIDCFGR_SEMWLC1 BIT_32(17) +#define PWR_WIO4CIDCFGR_SEMWLC2 BIT_32(18) +#define PWR_WIO4CIDCFGR_SEMWLC3 BIT_32(19) +#define PWR_WIO4CIDCFGR_SEMWLC4 BIT_32(20) +#define PWR_WIO4CIDCFGR_SEMWLC5 BIT_32(21) +#define PWR_WIO4CIDCFGR_SEMWLC6 BIT_32(22) +#define PWR_WIO4CIDCFGR_SEMWLC7 BIT_32(23) + +/* PWR_WIO4SEMCR register fields */ +#define PWR_WIO4SEMCR_SEM_MUTEX BIT_32(0) +#define PWR_WIO4SEMCR_SEMCID_MASK GENMASK_32(6, 4) +#define PWR_WIO4SEMCR_SEMCID_SHIFT U(4) + +/* PWR_WIO5CIDCFGR register fields */ +#define PWR_WIO5CIDCFGR_CFEN BIT_32(0) +#define PWR_WIO5CIDCFGR_SEM_EN BIT_32(1) +#define PWR_WIO5CIDCFGR_SCID_MASK GENMASK_32(6, 4) +#define PWR_WIO5CIDCFGR_SCID_SHIFT U(4) +#define PWR_WIO5CIDCFGR_SEMWLC0 BIT_32(16) +#define PWR_WIO5CIDCFGR_SEMWLC1 BIT_32(17) +#define PWR_WIO5CIDCFGR_SEMWLC2 BIT_32(18) +#define PWR_WIO5CIDCFGR_SEMWLC3 BIT_32(19) +#define PWR_WIO5CIDCFGR_SEMWLC4 BIT_32(20) +#define PWR_WIO5CIDCFGR_SEMWLC5 BIT_32(21) +#define PWR_WIO5CIDCFGR_SEMWLC6 BIT_32(22) +#define PWR_WIO5CIDCFGR_SEMWLC7 BIT_32(23) + +/* PWR_WIO5SEMCR register fields */ +#define PWR_WIO5SEMCR_SEM_MUTEX BIT_32(0) +#define PWR_WIO5SEMCR_SEMCID_MASK GENMASK_32(6, 4) +#define PWR_WIO5SEMCR_SEMCID_SHIFT U(4) + +/* PWR_WIO6CIDCFGR register fields */ +#define PWR_WIO6CIDCFGR_CFEN BIT_32(0) +#define PWR_WIO6CIDCFGR_SEM_EN BIT_32(1) +#define PWR_WIO6CIDCFGR_SCID_MASK GENMASK_32(6, 4) +#define PWR_WIO6CIDCFGR_SCID_SHIFT U(4) +#define PWR_WIO6CIDCFGR_SEMWLC0 BIT_32(16) +#define PWR_WIO6CIDCFGR_SEMWLC1 BIT_32(17) +#define PWR_WIO6CIDCFGR_SEMWLC2 BIT_32(18) +#define PWR_WIO6CIDCFGR_SEMWLC3 BIT_32(19) +#define PWR_WIO6CIDCFGR_SEMWLC4 BIT_32(20) +#define PWR_WIO6CIDCFGR_SEMWLC5 BIT_32(21) +#define PWR_WIO6CIDCFGR_SEMWLC6 BIT_32(22) +#define PWR_WIO6CIDCFGR_SEMWLC7 BIT_32(23) + +/* PWR_WIO6SEMCR register fields */ +#define PWR_WIO6SEMCR_SEM_MUTEX BIT_32(0) +#define PWR_WIO6SEMCR_SEMCID_MASK GENMASK_32(6, 4) +#define PWR_WIO6SEMCR_SEMCID_SHIFT U(4) + +/* PWR_CPU1D1SR register fields */ +#define PWR_CPU1D1SR_HOLD_BOOT BIT_32(0) +#define PWR_CPU1D1SR_CSTATE_MASK GENMASK_32(3, 2) +#define PWR_CPU1D1SR_CSTATE_SHIFT U(2) +#define PWR_CPU1D1SR_DSTATE_MASK GENMASK_32(10, 8) +#define PWR_CPU1D1SR_DSTATE_SHIFT U(8) + +/* PWR_CPU2D2SR register fields */ +#define PWR_CPU2D2SR_HOLD_BOOT BIT_32(0) +#define PWR_CPU2D2SR_WFBEN BIT_32(1) +#define PWR_CPU2D2SR_CSTATE_MASK GENMASK_32(3, 2) +#define PWR_CPU2D2SR_CSTATE_SHIFT U(2) +#define PWR_CPU2D2SR_DSTATE_MASK GENMASK_32(10, 8) +#define PWR_CPU2D2SR_DSTATE_SHIFT U(8) + +/* PWR_CPU3D3SR register fields */ +#define PWR_CPU3D3SR_CSTATE_MASK GENMASK_32(3, 2) +#define PWR_CPU3D3SR_CSTATE_SHIFT U(2) +#define PWR_CPU3D3SR_DSTATE_MASK GENMASK_32(10, 8) +#define PWR_CPU3D3SR_DSTATE_SHIFT U(8) + +/* PWR_DBGR register fields */ +#define PWR_DBGR_FD3S BIT_32(0) +#define PWR_DBGR_VDDIOKRETRAM BIT_32(16) +#define PWR_DBGR_VDDIOKBKPRAM BIT_32(17) +#define PWR_DBGR_VDDIOKD3 BIT_32(18) +#define PWR_DBGR_VDDIOKLPSRAM1 BIT_32(19) + +/* PWR_VERR register fields */ +#define PWR_VERR_MINREV_MASK GENMASK_32(3, 0) +#define PWR_VERR_MINREV_SHIFT U(0) +#define PWR_VERR_MAJREV_MASK GENMASK_32(7, 4) +#define PWR_VERR_MAJREV_SHIFT U(4) + +#endif /* STM32MP2_PWR_H */ diff --git a/include/drivers/st/stm32mp2_ram.h b/include/drivers/st/stm32mp2_ram.h new file mode 100644 index 00000000..b6fa9288 --- /dev/null +++ b/include/drivers/st/stm32mp2_ram.h @@ -0,0 +1,13 @@ +/* + * Copyright (c) 2024, STMicroelectronics - All Rights Reserved + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef STM32MP2_RAM_H +#define STM32MP2_RAM_H + +bool stm32mp2_ddr_is_restored(void); +int stm32mp2_ddr_probe(void); + +#endif /* STM32MP2_RAM_H */ diff --git a/include/drivers/st/stm32mp_clkfunc.h b/include/drivers/st/stm32mp_clkfunc.h index 61286b22..8fd5ed14 100644 --- a/include/drivers/st/stm32mp_clkfunc.h +++ b/include/drivers/st/stm32mp_clkfunc.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2022, STMicroelectronics - All Rights Reserved + * Copyright (c) 2017-2024, STMicroelectronics - All Rights Reserved * * SPDX-License-Identifier: BSD-3-Clause */ @@ -29,8 +29,6 @@ int fdt_get_clock_id(int node); unsigned long fdt_get_uart_clock_freq(uintptr_t instance); void stm32mp_stgen_config(unsigned long rate); -void stm32mp_stgen_restore_counter(unsigned long long value, - unsigned long long offset_in_ms); -unsigned long long stm32mp_stgen_get_counter(void); +void stm32mp_stgen_restore_rate(void); #endif /* STM32MP_CLKFUNC_H */ diff --git a/include/drivers/st/stm32mp_ddr.h b/include/drivers/st/stm32mp_ddr.h index 4535e3cb..57b06686 100644 --- a/include/drivers/st/stm32mp_ddr.h +++ b/include/drivers/st/stm32mp_ddr.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2022-2023, STMicroelectronics - All Rights Reserved + * Copyright (C) 2022-2024, STMicroelectronics - All Rights Reserved * * SPDX-License-Identifier: GPL-2.0+ OR BSD-3-Clause */ @@ -26,9 +26,11 @@ enum stm32mp_ddr_reg_type { }; struct stm32mp_ddr_reg_desc { - const char *name; uint16_t offset; /* Offset for base address */ uint8_t par_offset; /* Offset for parameter array */ +#if !STM32MP13 && !STM32MP15 + bool qd; /* quasi-dynamic register if true */ +#endif }; struct stm32mp_ddr_reg_info { @@ -57,13 +59,26 @@ struct stm32mp_ddr_info { size_t size; /* Memory size in byte = col * row * width */ }; -#define TIMEOUT_US_1S 1000000U +#define DDR_DELAY_1US 1U +#define DDR_DELAY_2US 2U +#define DDR_DELAY_10US 10U +#define DDR_DELAY_50US 50U +#define DDR_TIMEOUT_500US 500U +#define DDR_TIMEOUT_US_1S 1000000U void stm32mp_ddr_set_reg(const struct stm32mp_ddr_priv *priv, enum stm32mp_ddr_reg_type type, const void *param, const struct stm32mp_ddr_reg_info *ddr_registers); void stm32mp_ddr_start_sw_done(struct stm32mp_ddrctl *ctl); void stm32mp_ddr_wait_sw_done_ack(struct stm32mp_ddrctl *ctl); void stm32mp_ddr_enable_axi_port(struct stm32mp_ddrctl *ctl); +int stm32mp_ddr_disable_axi_port(struct stm32mp_ddrctl *ctl); +void stm32mp_ddr_enable_host_interface(struct stm32mp_ddrctl *ctl); +void stm32mp_ddr_disable_host_interface(struct stm32mp_ddrctl *ctl); +int stm32mp_ddr_sw_selfref_entry(struct stm32mp_ddrctl *ctl); +void stm32mp_ddr_sw_selfref_exit(struct stm32mp_ddrctl *ctl); +void stm32mp_ddr_set_qd3_update_conditions(struct stm32mp_ddrctl *ctl); +void stm32mp_ddr_unset_qd3_update_conditions(struct stm32mp_ddrctl *ctl); +void stm32mp_ddr_wait_refresh_update_done_ack(struct stm32mp_ddrctl *ctl); int stm32mp_board_ddr_power_init(enum ddr_type ddr_type); #endif /* STM32MP_DDR_H */ diff --git a/include/drivers/st/stm32mp_ddrctrl_regs.h b/include/drivers/st/stm32mp_ddrctrl_regs.h index 79de86b2..f9f46aa4 100644 --- a/include/drivers/st/stm32mp_ddrctrl_regs.h +++ b/include/drivers/st/stm32mp_ddrctrl_regs.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, STMicroelectronics - All Rights Reserved + * Copyright (c) 2022-2024, STMicroelectronics - All Rights Reserved * * SPDX-License-Identifier: GPL-2.0+ OR BSD-3-Clause */ @@ -51,7 +51,8 @@ struct stm32mp_ddrctl { uint32_t init7; /* 0xec SDRAM Initialization 7 */ uint32_t dimmctl; /* 0xf0 DIMM Control */ uint32_t rankctl; /* 0xf4 Rank Control */ - uint8_t reserved0f4[0x100 - 0xf8]; + uint32_t rankctl1; /* 0xf8 Rank Control 1 */ + uint8_t reserved0fc[0x100 - 0xfc]; uint32_t dramtmg0; /* 0x100 SDRAM Timing 0 */ uint32_t dramtmg1; /* 0x104 SDRAM Timing 1 */ uint32_t dramtmg2; /* 0x108 SDRAM Timing 2 */ @@ -112,7 +113,9 @@ struct stm32mp_ddrctl { uint32_t perflpr1; /* 0x264 Low Priority Read CAM 1 */ uint32_t reserved268; uint32_t perfwr1; /* 0x26c Write CAM 1 */ - uint8_t reserved27c[0x300 - 0x270]; + uint32_t sched3; /* 0x270 Scheduler Control 3 */ + uint32_t sched4; /* 0x274 Scheduler Control 4 */ + uint8_t reserved278[0x300 - 0x278]; uint32_t dbg0; /* 0x300 Debug 0 */ uint32_t dbg1; /* 0x304 Debug 1 */ uint32_t dbgcam; /* 0x308 CAM Debug */ @@ -121,7 +124,8 @@ struct stm32mp_ddrctl { uint8_t reserved314[0x320 - 0x314]; uint32_t swctl; /* 0x320 Software Programming Control Enable */ uint32_t swstat; /* 0x324 Software Programming Control Status */ - uint8_t reserved328[0x36c - 0x328]; + uint32_t swctlstatic; /* 0x328 Statics Write Enable */ + uint8_t reserved32c[0x36c - 0x32c]; uint32_t poisoncfg; /* 0x36c AXI Poison Configuration Register */ uint32_t poisonstat; /* 0x370 AXI Poison Status Register */ uint8_t reserved374[0x3f0 - 0x374]; @@ -153,7 +157,7 @@ struct stm32mp_ddrctl { uint32_t pcfgqos1_1; /* 0x548 Read QoS Configuration 1 */ uint32_t pcfgwqos0_1; /* 0x54c Write QoS Configuration 0 */ uint32_t pcfgwqos1_1; /* 0x550 Write QoS Configuration 1 */ -#endif +#endif /* STM32MP_DDR_DUAL_AXI_PORT */ uint8_t reserved554[0xff0 - 0x554]; uint32_t umctl2_ver_number; /* 0xff0 UMCTL2 Version Number */ @@ -170,6 +174,7 @@ struct stm32mp_ddrctl { #define DDRCTRL_RFSHCTL3 0x060 #define DDRCTRL_RFSHTMG 0x064 #define DDRCTRL_INIT0 0x0D0 +#define DDRCTRL_DFILPCFG0 0x198 #define DDRCTRL_DFIMISC 0x1B0 #define DDRCTRL_DBG1 0x304 #define DDRCTRL_DBGCAM 0x308 @@ -181,7 +186,7 @@ struct stm32mp_ddrctl { #define DDRCTRL_PCTRL_0 0x490 #if STM32MP_DDR_DUAL_AXI_PORT #define DDRCTRL_PCTRL_1 0x540 -#endif +#endif /* STM32MP_DDR_DUAL_AXI_PORT */ /* DDR Controller Register fields */ #define DDRCTRL_MSTR_DDR3 BIT(0) @@ -201,6 +206,8 @@ struct stm32mp_ddrctl { #define DDRCTRL_STAT_SELFREF_TYPE_MASK GENMASK(5, 4) #define DDRCTRL_STAT_SELFREF_TYPE_ASR (BIT(4) | BIT(5)) #define DDRCTRL_STAT_SELFREF_TYPE_SR BIT(5) +#define DDRCTRL_STAT_SELFREF_STATE_MASK GENMASK(9, 8) +#define DDRCTRL_STAT_SELFREF_STATE_SRPD BIT(9) #define DDRCTRL_MRCTRL0_MR_TYPE_WRITE U(0) /* Only one rank supported */ @@ -217,6 +224,7 @@ struct stm32mp_ddrctl { #define DDRCTRL_PWRCTL_POWERDOWN_EN BIT(1) #define DDRCTRL_PWRCTL_EN_DFI_DRAM_CLK_DISABLE BIT(3) #define DDRCTRL_PWRCTL_SELFREF_SW BIT(5) +#define DDRCTRL_PWRCTL_STAY_IN_SELFREF BIT(6) #define DDRCTRL_PWRTMG_SELFREF_TO_X32_MASK GENMASK(23, 16) #define DDRCTRL_PWRTMG_SELFREF_TO_X32_0 BIT(16) @@ -225,6 +233,9 @@ struct stm32mp_ddrctl { #define DDRCTRL_RFSHCTL3_REFRESH_UPDATE_LEVEL BIT(1) #define DDRCTRL_HWLPCTL_HW_LP_EN BIT(0) +#define DDRCTRL_HWLPCTL_HW_LP_EXIT_IDLE_EN BIT(1) +#define DDRCTRL_HWLPCTL_HW_LP_IDLE_X32_MASK GENMASK(27, 16) +#define DDRCTRL_HWLPCTL_HW_LP_IDLE_X32_SHIFT 16 #define DDRCTRL_RFSHTMG_T_RFC_NOM_X1_X32_MASK GENMASK(27, 16) #define DDRCTRL_RFSHTMG_T_RFC_NOM_X1_X32_SHIFT 16 @@ -232,21 +243,31 @@ struct stm32mp_ddrctl { #define DDRCTRL_INIT0_SKIP_DRAM_INIT_MASK GENMASK(31, 30) #define DDRCTRL_INIT0_SKIP_DRAM_INIT_NORMAL BIT(30) +#define DDRCTRL_DFILPCFG0_DFI_LP_EN_SR BIT(8) + #define DDRCTRL_DFIMISC_DFI_INIT_COMPLETE_EN BIT(0) #define DDRCTRL_DFIMISC_DFI_INIT_START BIT(5) +#define DDRCTRL_DFIMISC_DFI_FREQUENCY GENMASK(12, 8) #define DDRCTRL_DFISTAT_DFI_INIT_COMPLETE BIT(0) +#define DDRCTRL_DFISTAT_DFI_LP_ACK BIT(1) +#define DDRCTRL_DBG1_DIS_DQ BIT(0) #define DDRCTRL_DBG1_DIS_HIF BIT(1) #define DDRCTRL_DBGCAM_WR_DATA_PIPELINE_EMPTY BIT(29) #define DDRCTRL_DBGCAM_RD_DATA_PIPELINE_EMPTY BIT(28) #define DDRCTRL_DBGCAM_DBG_WR_Q_EMPTY BIT(26) +#define DDRCTRL_DBGCAM_DBG_RD_Q_EMPTY BIT(25) #define DDRCTRL_DBGCAM_DBG_LPR_Q_DEPTH GENMASK(12, 8) #define DDRCTRL_DBGCAM_DBG_HPR_Q_DEPTH GENMASK(4, 0) #define DDRCTRL_DBGCAM_DATA_PIPELINE_EMPTY \ (DDRCTRL_DBGCAM_WR_DATA_PIPELINE_EMPTY | \ DDRCTRL_DBGCAM_RD_DATA_PIPELINE_EMPTY) +#define DDRCTRL_DBG_Q_AND_DATA_PIPELINE_EMPTY \ + (DDRCTRL_DBGCAM_DBG_WR_Q_EMPTY | \ + DDRCTRL_DBGCAM_DBG_RD_Q_EMPTY | \ + DDRCTRL_DBGCAM_DATA_PIPELINE_EMPTY) #define DDRCTRL_DBGCAM_DBG_Q_DEPTH \ (DDRCTRL_DBGCAM_DBG_WR_Q_EMPTY | \ DDRCTRL_DBGCAM_DBG_LPR_Q_DEPTH | \ diff --git a/include/drivers/st/stm32mp_pmic.h b/include/drivers/st/stm32mp_pmic.h index 303c5714..73845712 100644 --- a/include/drivers/st/stm32mp_pmic.h +++ b/include/drivers/st/stm32mp_pmic.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2022, STMicroelectronics - All Rights Reserved + * Copyright (c) 2017-2024, STMicroelectronics - All Rights Reserved * * SPDX-License-Identifier: BSD-3-Clause */ @@ -41,13 +41,6 @@ static inline void print_pmic_info_and_debug(void) } #endif -/* - * pmic_ddr_power_init - Initialize regulators required for DDR - * - * Returns 0 on success, and negative values on errors - */ -int pmic_ddr_power_init(enum ddr_type ddr_type); - /* * pmic_voltages_init - Update voltages for platform init * diff --git a/include/drivers/st/stm32mp_pmic2.h b/include/drivers/st/stm32mp_pmic2.h new file mode 100644 index 00000000..51eba38c --- /dev/null +++ b/include/drivers/st/stm32mp_pmic2.h @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2024, STMicroelectronics - All Rights Reserved + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef STM32MP_PMIC2_H +#define STM32MP_PMIC2_H + +#include <stdbool.h> +#include <drivers/st/regulator.h> + +#include <platform_def.h> + +/* + * dt_pmic_status - Check PMIC status from device tree + * + * Returns the status of the PMIC (secure, non-secure), or a negative value on + * error + */ +int dt_pmic_status(void); + +/* + * initialize_pmic_i2c - Initialize I2C for the PMIC control + * + * Returns true if PMIC is available, false if not found, panics on errors + */ +bool initialize_pmic_i2c(void); + +/* + * initialize_pmic - Main PMIC initialization function, called at platform init + * + * Panics on errors + */ +void initialize_pmic(void); + +/* + * stpmic2_set_prop - Set PMIC2 proprietary property + * + * Returns non zero on errors + */ +int stpmic2_set_prop(const struct regul_description *desc, uint16_t prop, uint32_t value); + +/* + * pmic_switch_off - switch off the platform with PMIC + * + * Panics on errors + */ +void pmic_switch_off(void); + +#endif /* STM32MP_PMIC2_H */ diff --git a/include/drivers/st/stm32mp_reset.h b/include/drivers/st/stm32mp_reset.h index 84448050..a8648b43 100644 --- a/include/drivers/st/stm32mp_reset.h +++ b/include/drivers/st/stm32mp_reset.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018-2019, STMicroelectronics - All Rights Reserved + * Copyright (c) 2018-2024, STMicroelectronics - All Rights Reserved * * SPDX-License-Identifier: BSD-3-Clause */ @@ -47,4 +47,9 @@ static inline void stm32mp_reset_release(uint32_t reset_id) (void)stm32mp_reset_deassert(reset_id, 0U); } +/* + * Manage system reset control + */ +void __dead2 stm32mp_system_reset(void); + #endif /* STM32MP_RESET_H */ diff --git a/include/drivers/st/stm32mp_risab_regs.h b/include/drivers/st/stm32mp_risab_regs.h new file mode 100644 index 00000000..1f49bf69 --- /dev/null +++ b/include/drivers/st/stm32mp_risab_regs.h @@ -0,0 +1,271 @@ +/* + * Copyright (C) 2024, STMicroelectronics - All Rights Reserved + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef STM32MP_RISAB_REGS_H +#define STM32MP_RISAB_REGS_H + +#define RISAB_CR U(0x00) +#define RISAB_IASR U(0x08) +#define RISAB_IACR U(0x0C) +#define RISAB_RIFLOCKR U(0x10) +#define RISAB_IAESR U(0x20) +#define RISAB_IADDR U(0x24) +#define RISAB_PG0_SECCFGR U(0x100) +#define RISAB_PG1_SECCFGR U(0x104) +#define RISAB_PG2_SECCFGR U(0x108) +#define RISAB_PG3_SECCFGR U(0x10C) +#define RISAB_PG4_SECCFGR U(0x110) +#define RISAB_PG5_SECCFGR U(0x114) +#define RISAB_PG6_SECCFGR U(0x118) +#define RISAB_PG7_SECCFGR U(0x11C) +#define RISAB_PG8_SECCFGR U(0x120) +#define RISAB_PG9_SECCFGR U(0x124) +#define RISAB_PG10_SECCFGR U(0x128) +#define RISAB_PG11_SECCFGR U(0x12C) +#define RISAB_PG12_SECCFGR U(0x130) +#define RISAB_PG13_SECCFGR U(0x134) +#define RISAB_PG14_SECCFGR U(0x138) +#define RISAB_PG15_SECCFGR U(0x13C) +#define RISAB_PG16_SECCFGR U(0x140) +#define RISAB_PG17_SECCFGR U(0x144) +#define RISAB_PG18_SECCFGR U(0x148) +#define RISAB_PG19_SECCFGR U(0x14C) +#define RISAB_PG20_SECCFGR U(0x150) +#define RISAB_PG21_SECCFGR U(0x154) +#define RISAB_PG22_SECCFGR U(0x158) +#define RISAB_PG23_SECCFGR U(0x15C) +#define RISAB_PG24_SECCFGR U(0x160) +#define RISAB_PG25_SECCFGR U(0x164) +#define RISAB_PG26_SECCFGR U(0x168) +#define RISAB_PG27_SECCFGR U(0x16C) +#define RISAB_PG28_SECCFGR U(0x170) +#define RISAB_PG29_SECCFGR U(0x174) +#define RISAB_PG30_SECCFGR U(0x178) +#define RISAB_PG31_SECCFGR U(0x17C) +#define RISAB_PG0_PRIVCFGR U(0x200) +#define RISAB_PG1_PRIVCFGR U(0x204) +#define RISAB_PG2_PRIVCFGR U(0x208) +#define RISAB_PG3_PRIVCFGR U(0x20C) +#define RISAB_PG4_PRIVCFGR U(0x210) +#define RISAB_PG5_PRIVCFGR U(0x214) +#define RISAB_PG6_PRIVCFGR U(0x218) +#define RISAB_PG7_PRIVCFGR U(0x21C) +#define RISAB_PG8_PRIVCFGR U(0x220) +#define RISAB_PG9_PRIVCFGR U(0x224) +#define RISAB_PG10_PRIVCFGR U(0x228) +#define RISAB_PG11_PRIVCFGR U(0x22C) +#define RISAB_PG12_PRIVCFGR U(0x230) +#define RISAB_PG13_PRIVCFGR U(0x234) +#define RISAB_PG14_PRIVCFGR U(0x238) +#define RISAB_PG15_PRIVCFGR U(0x23C) +#define RISAB_PG16_PRIVCFGR U(0x240) +#define RISAB_PG17_PRIVCFGR U(0x244) +#define RISAB_PG18_PRIVCFGR U(0x248) +#define RISAB_PG19_PRIVCFGR U(0x24C) +#define RISAB_PG20_PRIVCFGR U(0x250) +#define RISAB_PG21_PRIVCFGR U(0x254) +#define RISAB_PG22_PRIVCFGR U(0x258) +#define RISAB_PG23_PRIVCFGR U(0x25C) +#define RISAB_PG24_PRIVCFGR U(0x260) +#define RISAB_PG25_PRIVCFGR U(0x264) +#define RISAB_PG26_PRIVCFGR U(0x268) +#define RISAB_PG27_PRIVCFGR U(0x26C) +#define RISAB_PG28_PRIVCFGR U(0x270) +#define RISAB_PG29_PRIVCFGR U(0x274) +#define RISAB_PG30_PRIVCFGR U(0x278) +#define RISAB_PG31_PRIVCFGR U(0x27C) +#define RISAB_PG0_C2PRIVCFGR U(0x600) +#define RISAB_PG1_C2PRIVCFGR U(0x604) +#define RISAB_PG2_C2PRIVCFGR U(0x608) +#define RISAB_PG3_C2PRIVCFGR U(0x60C) +#define RISAB_PG4_C2PRIVCFGR U(0x610) +#define RISAB_PG5_C2PRIVCFGR U(0x614) +#define RISAB_PG6_C2PRIVCFGR U(0x618) +#define RISAB_PG7_C2PRIVCFGR U(0x61C) +#define RISAB_PG8_C2PRIVCFGR U(0x620) +#define RISAB_PG9_C2PRIVCFGR U(0x624) +#define RISAB_PG10_C2PRIVCFGR U(0x628) +#define RISAB_PG11_C2PRIVCFGR U(0x62C) +#define RISAB_PG12_C2PRIVCFGR U(0x630) +#define RISAB_PG13_C2PRIVCFGR U(0x634) +#define RISAB_PG14_C2PRIVCFGR U(0x638) +#define RISAB_PG15_C2PRIVCFGR U(0x63C) +#define RISAB_PG16_C2PRIVCFGR U(0x640) +#define RISAB_PG17_C2PRIVCFGR U(0x644) +#define RISAB_PG18_C2PRIVCFGR U(0x648) +#define RISAB_PG19_C2PRIVCFGR U(0x64C) +#define RISAB_PG20_C2PRIVCFGR U(0x650) +#define RISAB_PG21_C2PRIVCFGR U(0x654) +#define RISAB_PG22_C2PRIVCFGR U(0x658) +#define RISAB_PG23_C2PRIVCFGR U(0x65C) +#define RISAB_PG24_C2PRIVCFGR U(0x660) +#define RISAB_PG25_C2PRIVCFGR U(0x664) +#define RISAB_PG26_C2PRIVCFGR U(0x668) +#define RISAB_PG27_C2PRIVCFGR U(0x66C) +#define RISAB_PG28_C2PRIVCFGR U(0x670) +#define RISAB_PG29_C2PRIVCFGR U(0x674) +#define RISAB_PG30_C2PRIVCFGR U(0x678) +#define RISAB_PG31_C2PRIVCFGR U(0x67C) +#define RISAB_CID0PRIVCFGR U(0x800) +#define RISAB_CID0RDCFGR U(0x808) +#define RISAB_CID0WRCFGR U(0x810) +#define RISAB_CID1PRIVCFGR U(0x820) +#define RISAB_CID1RDCFGR U(0x828) +#define RISAB_CID1WRCFGR U(0x830) +#define RISAB_CID2PRIVCFGR U(0x840) +#define RISAB_CID2RDCFGR U(0x848) +#define RISAB_CID2WRCFGR U(0x850) +#define RISAB_CID3PRIVCFGR U(0x860) +#define RISAB_CID3RDCFGR U(0x868) +#define RISAB_CID3WRCFGR U(0x870) +#define RISAB_CID4PRIVCFGR U(0x880) +#define RISAB_CID4RDCFGR U(0x888) +#define RISAB_CID4WRCFGR U(0x890) +#define RISAB_CID5PRIVCFGR U(0x8A0) +#define RISAB_CID5RDCFGR U(0x8A8) +#define RISAB_CID5WRCFGR U(0x8B0) +#define RISAB_CID6PRIVCFGR U(0x8C0) +#define RISAB_CID6RDCFGR U(0x8C8) +#define RISAB_CID6WRCFGR U(0x8D0) +#define RISAB_PG0_CIDCFGR U(0xA00) +#define RISAB_PG1_CIDCFGR U(0xA04) +#define RISAB_PG2_CIDCFGR U(0xA08) +#define RISAB_PG3_CIDCFGR U(0xA0C) +#define RISAB_PG4_CIDCFGR U(0xA10) +#define RISAB_PG5_CIDCFGR U(0xA14) +#define RISAB_PG6_CIDCFGR U(0xA18) +#define RISAB_PG7_CIDCFGR U(0xA1C) +#define RISAB_PG8_CIDCFGR U(0xA20) +#define RISAB_PG9_CIDCFGR U(0xA24) +#define RISAB_PG10_CIDCFGR U(0xA28) +#define RISAB_PG11_CIDCFGR U(0xA2C) +#define RISAB_PG12_CIDCFGR U(0xA30) +#define RISAB_PG13_CIDCFGR U(0xA34) +#define RISAB_PG14_CIDCFGR U(0xA38) +#define RISAB_PG15_CIDCFGR U(0xA3C) +#define RISAB_PG16_CIDCFGR U(0xA40) +#define RISAB_PG17_CIDCFGR U(0xA44) +#define RISAB_PG18_CIDCFGR U(0xA48) +#define RISAB_PG19_CIDCFGR U(0xA4C) +#define RISAB_PG20_CIDCFGR U(0xA50) +#define RISAB_PG21_CIDCFGR U(0xA54) +#define RISAB_PG22_CIDCFGR U(0xA58) +#define RISAB_PG23_CIDCFGR U(0xA5C) +#define RISAB_PG24_CIDCFGR U(0xA60) +#define RISAB_PG25_CIDCFGR U(0xA64) +#define RISAB_PG26_CIDCFGR U(0xA68) +#define RISAB_PG27_CIDCFGR U(0xA6C) +#define RISAB_PG28_CIDCFGR U(0xA70) +#define RISAB_PG29_CIDCFGR U(0xA74) +#define RISAB_PG30_CIDCFGR U(0xA78) +#define RISAB_PG31_CIDCFGR U(0xA7C) +#define RISAB_HWCFGR3 U(0xFE8) +#define RISAB_HWCFGR2 U(0xFEC) +#define RISAB_HWCFGR1 U(0xFF0) +#define RISAB_VERR U(0xFF4) +#define RISAB_IPIDR U(0xFF8) +#define RISAB_SIDR U(0xFFC) + +/* RISAB_CR register fields */ +#define RISAB_CR_GLOCK BIT(0) +#define RISAB_CR_SRWIAD BIT(31) + +/* RISAB_IASR register fields */ +#define RISAB_IASR_CAEF BIT(0) +#define RISAB_IASR_IAEF BIT(1) + +/* RISAB_IACR register fields */ +#define RISAB_IACR_CAEF BIT(0) +#define RISAB_IACR_IAEF BIT(1) + +/* RISAB_RIFLOCKR register fields */ +#define RISAB_RIFLOCKR_RLOCK0 BIT(0) +#define RISAB_RIFLOCKR_RLOCK1 BIT(1) +#define RISAB_RIFLOCKR_RLOCK2 BIT(2) +#define RISAB_RIFLOCKR_RLOCK3 BIT(3) +#define RISAB_RIFLOCKR_RLOCK4 BIT(4) +#define RISAB_RIFLOCKR_RLOCK5 BIT(5) +#define RISAB_RIFLOCKR_RLOCK6 BIT(6) +#define RISAB_RIFLOCKR_RLOCK7 BIT(7) +#define RISAB_RIFLOCKR_RLOCK8 BIT(8) +#define RISAB_RIFLOCKR_RLOCK9 BIT(9) +#define RISAB_RIFLOCKR_RLOCK10 BIT(10) +#define RISAB_RIFLOCKR_RLOCK11 BIT(11) +#define RISAB_RIFLOCKR_RLOCK12 BIT(12) +#define RISAB_RIFLOCKR_RLOCK13 BIT(13) +#define RISAB_RIFLOCKR_RLOCK14 BIT(14) +#define RISAB_RIFLOCKR_RLOCK15 BIT(15) +#define RISAB_RIFLOCKR_RLOCK16 BIT(16) +#define RISAB_RIFLOCKR_RLOCK17 BIT(17) +#define RISAB_RIFLOCKR_RLOCK18 BIT(18) +#define RISAB_RIFLOCKR_RLOCK19 BIT(19) +#define RISAB_RIFLOCKR_RLOCK20 BIT(20) +#define RISAB_RIFLOCKR_RLOCK21 BIT(21) +#define RISAB_RIFLOCKR_RLOCK22 BIT(22) +#define RISAB_RIFLOCKR_RLOCK23 BIT(23) +#define RISAB_RIFLOCKR_RLOCK24 BIT(24) +#define RISAB_RIFLOCKR_RLOCK25 BIT(25) +#define RISAB_RIFLOCKR_RLOCK26 BIT(26) +#define RISAB_RIFLOCKR_RLOCK27 BIT(27) +#define RISAB_RIFLOCKR_RLOCK28 BIT(28) +#define RISAB_RIFLOCKR_RLOCK29 BIT(29) +#define RISAB_RIFLOCKR_RLOCK30 BIT(30) +#define RISAB_RIFLOCKR_RLOCK31 BIT(31) + +/* RISAB_IAESR register fields */ +#define RISAB_IAESR_IACID_MASK GENMASK(2, 0) +#define RISAB_IAESR_IACID_SHIFT 0 +#define RISAB_IAESR_IAPRIV BIT(4) +#define RISAB_IAESR_IASEC BIT(5) +#define RISAB_IAESR_IANRW BIT(7) + +/* RISAB_PGx_SECCFGR register fields */ +#define RISAB_PGx_SECCFGR_SEC(_y) BIT(_y) + +/* RISAB_PGx_PRIVCFGR register fields */ +#define RISAB_PGx_PRIVCFGR_PRIV(_y) BIT(_y) + +/* RISAB_PGx_CmPRIVCFGR register fields */ +#define RISAB_PGx_CmPRIVCFGR_PRIV(_y) BIT(_y) + +/* RISAB_CIDxPRIVCFGR register fields */ +#define RISAB_CIDxPRIVCFGR_PPRIV(_y) BIT(_y) + +/* RISAB_CIDxRDCFGR register fields */ +#define RISAB_CIDxRDCFGR_PRDEN(_y) BIT(_y) + +/* RISAB_CIDxWRCFGR register fields */ +#define RISAB_CIDxWRCFGR_PWREN(_y) BIT(_y) + +/* RISAB_PGx_CIDCFGR register fields */ +#define RISAB_PGx_CIDCFGR_CFEN BIT(0) +#define RISAB_PGx_CIDCFGR_DCEN BIT(2) +#define RISAB_PGx_CIDCFGR_DCCID_MASK GENMASK(6, 4) +#define RISAB_PGx_CIDCFGR_DCCID_SHIFT 4 + +/* RISAB_HWCFGR1 register fields */ +#define RISAB_HWCFGR1_CFG1_MASK GENMASK(3, 0) +#define RISAB_HWCFGR1_CFG1_SHIFT 0 +#define RISAB_HWCFGR1_CFG2_MASK GENMASK(7, 4) +#define RISAB_HWCFGR1_CFG2_SHIFT 4 +#define RISAB_HWCFGR1_CFG3_MASK GENMASK(11, 8) +#define RISAB_HWCFGR1_CFG3_SHIFT 8 +#define RISAB_HWCFGR1_CFG4_MASK GENMASK(15, 12) +#define RISAB_HWCFGR1_CFG4_SHIFT 12 +#define RISAB_HWCFGR1_CFG5_MASK GENMASK(19, 16) +#define RISAB_HWCFGR1_CFG5_SHIFT 16 +#define RISAB_HWCFGR1_CFG6_MASK GENMASK(23, 20) +#define RISAB_HWCFGR1_CFG6_SHIFT 20 +#define RISAB_HWCFGR1_CFG7_MASK GENMASK(27, 24) +#define RISAB_HWCFGR1_CFG7_SHIFT 24 + +/* RISAB_VERR register fields */ +#define RISAB_VERR_MINREV_MASK GENMASK(3, 0) +#define RISAB_VERR_MINREV_SHIFT 0 +#define RISAB_VERR_MAJREV_MASK GENMASK(7, 4) +#define RISAB_VERR_MAJREV_SHIFT 4 + +#endif /* STM32MP_RISAB_REGS_H */ diff --git a/include/drivers/st/stpmic2.h b/include/drivers/st/stpmic2.h new file mode 100644 index 00000000..58ba64ae --- /dev/null +++ b/include/drivers/st/stpmic2.h @@ -0,0 +1,307 @@ +/* + * Copyright (C) 2024, STMicroelectronics - All Rights Reserved + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef STPMIC2_H +#define STPMIC2_H + +#include <drivers/st/stm32_i2c.h> +#include <lib/utils_def.h> + +enum { + STPMIC2_BUCK1 = 0, + STPMIC2_BUCK2, + STPMIC2_BUCK3, + STPMIC2_BUCK4, + STPMIC2_BUCK5, + STPMIC2_BUCK6, + STPMIC2_BUCK7, + STPMIC2_REFDDR, + STPMIC2_LDO1, + STPMIC2_LDO2, + STPMIC2_LDO3, + STPMIC2_LDO4, + STPMIC2_LDO5, + STPMIC2_LDO6, + STPMIC2_LDO7, + STPMIC2_LDO8, + STPMIC2_NB_REG +}; + +/* Status Registers */ +#define PRODUCT_ID 0x00 +#define VERSION_SR 0x01 +#define TURN_ON_SR 0x02 +#define TURN_OFF_SR 0x03 +#define RESTART_SR 0x04 +#define OCP_SR1 0x05 +#define OCP_SR2 0x06 +#define EN_SR1 0x07 +#define EN_SR2 0x08 +#define FS_CNT_SR1 0x09 +#define FS_CNT_SR2 0x0A +#define FS_CNT_SR3 0x0B +#define MODE_SR 0x0C +/* Control Registers */ +#define MAIN_CR 0x10 +#define VINLOW_CR 0x11 +#define PKEY_LKP_CR 0x12 +#define WDG_CR 0x13 +#define WDG_TMR_CR 0x14 +#define WDG_TMR_SR 0x15 +#define FS_OCP_CR1 0x16 +#define FS_OCP_CR2 0x17 +#define PADS_PULL_CR 0x18 +#define BUCKS_PD_CR1 0x19 +#define BUCKS_PD_CR2 0x1A +#define LDOS_PD_CR1 0x1B +#define LDOS_PD_CR2 0x1C +#define BUCKS_MRST_CR 0x1D +#define LDOS_MRST_CR 0x1E +/* Buck CR */ +#define BUCK1_MAIN_CR1 0x20 +#define BUCK1_MAIN_CR2 0x21 +#define BUCK1_ALT_CR1 0x22 +#define BUCK1_ALT_CR2 0x23 +#define BUCK1_PWRCTRL_CR 0x24 +#define BUCK2_MAIN_CR1 0x25 +#define BUCK2_MAIN_CR2 0x26 +#define BUCK2_ALT_CR1 0x27 +#define BUCK2_ALT_CR2 0x28 +#define BUCK2_PWRCTRL_CR 0x29 +#define BUCK3_MAIN_CR1 0x2A +#define BUCK3_MAIN_CR2 0x2B +#define BUCK3_ALT_CR1 0x2C +#define BUCK3_ALT_CR2 0x2D +#define BUCK3_PWRCTRL_CR 0x2E +#define BUCK4_MAIN_CR1 0x2F +#define BUCK4_MAIN_CR2 0x30 +#define BUCK4_ALT_CR1 0x31 +#define BUCK4_ALT_CR2 0x32 +#define BUCK4_PWRCTRL_CR 0x33 +#define BUCK5_MAIN_CR1 0x34 +#define BUCK5_MAIN_CR2 0x35 +#define BUCK5_ALT_CR1 0x36 +#define BUCK5_ALT_CR2 0x37 +#define BUCK5_PWRCTRL_CR 0x38 +#define BUCK6_MAIN_CR1 0x39 +#define BUCK6_MAIN_CR2 0x3A +#define BUCK6_ALT_CR1 0x3B +#define BUCK6_ALT_CR2 0x3C +#define BUCK6_PWRCTRL_CR 0x3D +#define BUCK7_MAIN_CR1 0x3E +#define BUCK7_MAIN_CR2 0x3F +#define BUCK7_ALT_CR1 0x40 +#define BUCK7_ALT_CR2 0x41 +#define BUCK7_PWRCTRL_CR 0x42 +/* LDO CR */ +#define LDO1_MAIN_CR 0x4C +#define LDO1_ALT_CR 0x4D +#define LDO1_PWRCTRL_CR 0x4E +#define LDO2_MAIN_CR 0x4F +#define LDO2_ALT_CR 0x50 +#define LDO2_PWRCTRL_CR 0x51 +#define LDO3_MAIN_CR 0x52 +#define LDO3_ALT_CR 0x53 +#define LDO3_PWRCTRL_CR 0x54 +#define LDO4_MAIN_CR 0x55 +#define LDO4_ALT_CR 0x56 +#define LDO4_PWRCTRL_CR 0x57 +#define LDO5_MAIN_CR 0x58 +#define LDO5_ALT_CR 0x59 +#define LDO5_PWRCTRL_CR 0x5A +#define LDO6_MAIN_CR 0x5B +#define LDO6_ALT_CR 0x5C +#define LDO6_PWRCTRL_CR 0x5D +#define LDO7_MAIN_CR 0x5E +#define LDO7_ALT_CR 0x5F +#define LDO7_PWRCTRL_CR 0x60 +#define LDO8_MAIN_CR 0x61 +#define LDO8_ALT_CR 0x62 +#define LDO8_PWRCTRL_CR 0x63 +#define REFDDR_MAIN_CR 0x64 +#define REFDDR_ALT_CR 0x65 +#define REFDDR_PWRCTRL_CR 0x66 +/* INTERRUPT CR */ +#define INT_PENDING_R1 0x70 +#define INT_PENDING_R2 0x71 +#define INT_PENDING_R3 0x72 +#define INT_PENDING_R4 0x73 +#define INT_CLEAR_R1 0x74 +#define INT_CLEAR_R2 0x75 +#define INT_CLEAR_R3 0x76 +#define INT_CLEAR_R4 0x77 +#define INT_MASK_R1 0x78 +#define INT_MASK_R2 0x79 +#define INT_MASK_R3 0x7A +#define INT_MASK_R4 0x7B +#define INT_SRC_R1 0x7C +#define INT_SRC_R2 0x7D +#define INT_SRC_R3 0x7E +#define INT_SRC_R4 0x7F +#define INT_DBG_LATCH_R1 0x80 +#define INT_DBG_LATCH_R2 0x81 +#define INT_DBG_LATCH_R3 0x82 +#define INT_DBG_LATCH_R4 0x83 + +/* BUCKS_MRST_CR bits definition */ +#define BUCK1_MRST BIT(0) +#define BUCK2_MRST BIT(1) +#define BUCK3_MRST BIT(2) +#define BUCK4_MRST BIT(3) +#define BUCK5_MRST BIT(4) +#define BUCK6_MRST BIT(5) +#define BUCK7_MRST BIT(6) +#define REFDDR_MRST BIT(7) + +/* LDOS_MRST_CR bits definition */ +#define LDO1_MRST BIT(0) +#define LDO2_MRST BIT(1) +#define LDO3_MRST BIT(2) +#define LDO4_MRST BIT(3) +#define LDO5_MRST BIT(4) +#define LDO6_MRST BIT(5) +#define LDO7_MRST BIT(6) +#define LDO8_MRST BIT(7) + +/* LDOx_MAIN_CR */ +#define LDO_VOLT_SHIFT 1 +#define LDO_BYPASS BIT(6) +#define LDO1_INPUT_SRC BIT(7) +#define LDO3_SNK_SRC BIT(7) +#define LDO4_INPUT_SRC_SHIFT 6 +#define LDO4_INPUT_SRC_MASK GENMASK_32(7, 6) + +/* PWRCTRL register bit definition */ +#define PWRCTRL_EN BIT(0) +#define PWRCTRL_RS BIT(1) +#define PWRCTRL_SEL_SHIFT 2 +#define PWRCTRL_SEL_MASK GENMASK_32(3, 2) + +/* BUCKx_MAIN_CR2 */ +#define PREG_MODE_SHIFT 1 +#define PREG_MODE_MASK GENMASK_32(2, 1) + +/* BUCKS_PD_CR1 */ +#define BUCK1_PD_MASK GENMASK_32(1, 0) +#define BUCK2_PD_MASK GENMASK_32(3, 2) +#define BUCK3_PD_MASK GENMASK_32(5, 4) +#define BUCK4_PD_MASK GENMASK_32(7, 6) + +#define BUCK1_PD_FAST BIT(1) +#define BUCK2_PD_FAST BIT(3) +#define BUCK3_PD_FAST BIT(5) +#define BUCK4_PD_FAST BIT(7) + +/* BUCKS_PD_CR2 */ +#define BUCK5_PD_MASK GENMASK_32(1, 0) +#define BUCK6_PD_MASK GENMASK_32(3, 2) +#define BUCK7_PD_MASK GENMASK_32(5, 4) + +#define BUCK5_PD_FAST BIT(1) +#define BUCK6_PD_FAST BIT(3) +#define BUCK7_PD_FAST BIT(5) + +/* LDOS_PD_CR1 */ +#define LDO1_PD BIT(0) +#define LDO2_PD BIT(1) +#define LDO3_PD BIT(2) +#define LDO4_PD BIT(3) +#define LDO5_PD BIT(4) +#define LDO6_PD BIT(5) +#define LDO7_PD BIT(6) +#define LDO8_PD BIT(7) + +/* LDOS_PD_CR2 */ +#define REFDDR_PD BIT(0) + +/* FS_OCP_CR1 */ +#define FS_OCP_BUCK1 BIT(0) +#define FS_OCP_BUCK2 BIT(1) +#define FS_OCP_BUCK3 BIT(2) +#define FS_OCP_BUCK4 BIT(3) +#define FS_OCP_BUCK5 BIT(4) +#define FS_OCP_BUCK6 BIT(5) +#define FS_OCP_BUCK7 BIT(6) +#define FS_OCP_REFDDR BIT(7) + +/* FS_OCP_CR2 */ +#define FS_OCP_LDO1 BIT(0) +#define FS_OCP_LDO2 BIT(1) +#define FS_OCP_LDO3 BIT(2) +#define FS_OCP_LDO4 BIT(3) +#define FS_OCP_LDO5 BIT(4) +#define FS_OCP_LDO6 BIT(5) +#define FS_OCP_LDO7 BIT(6) +#define FS_OCP_LDO8 BIT(7) + +/* IRQ definitions */ +#define IT_PONKEY_F 0 +#define IT_PONKEY_R 1 +#define IT_BUCK1_OCP 16 +#define IT_BUCK2_OCP 17 +#define IT_BUCK3_OCP 18 +#define IT_BUCK4_OCP 19 +#define IT_BUCK5_OCP 20 +#define IT_BUCK6_OCP 21 +#define IT_BUCK7_OCP 22 +#define IT_REFDDR_OCP 23 +#define IT_LDO1_OCP 24 +#define IT_LDO2_OCP 25 +#define IT_LDO3_OCP 26 +#define IT_LDO4_OCP 27 +#define IT_LDO5_OCP 28 +#define IT_LDO6_OCP 29 +#define IT_LDO7_OCP 30 +#define IT_LDO8_OCP 31 + +enum stpmic2_prop_id { + STPMIC2_MASK_RESET = 0, + STPMIC2_PULL_DOWN, + STPMIC2_BYPASS, /* arg: 1=set 0=reset */ + STPMIC2_SINK_SOURCE, + STPMIC2_OCP, +}; + +struct pmic_handle_s { + struct i2c_handle_s *i2c_handle; + uint32_t i2c_addr; + unsigned int pmic_status; +}; + +int stpmic2_register_read(struct pmic_handle_s *pmic, + uint8_t register_id, uint8_t *value); +int stpmic2_register_write(struct pmic_handle_s *pmic, + uint8_t register_id, uint8_t value); +int stpmic2_register_update(struct pmic_handle_s *pmic, + uint8_t register_id, uint8_t value, uint8_t mask); + +int stpmic2_regulator_set_state(struct pmic_handle_s *pmic, + uint8_t id, bool enable); +int stpmic2_regulator_get_state(struct pmic_handle_s *pmic, + uint8_t id, bool *enabled); + +int stpmic2_regulator_levels_mv(struct pmic_handle_s *pmic, + uint8_t id, const uint16_t **levels, + size_t *levels_count); +int stpmic2_regulator_get_voltage(struct pmic_handle_s *pmic, + uint8_t id, uint16_t *val); +int stpmic2_regulator_set_voltage(struct pmic_handle_s *pmic, + uint8_t id, uint16_t millivolts); + +#if EVENT_LOG_LEVEL == LOG_LEVEL_VERBOSE +void stpmic2_dump_regulators(struct pmic_handle_s *pmic); +#endif + +int stpmic2_get_version(struct pmic_handle_s *pmic, uint8_t *val); +int stpmic2_get_product_id(struct pmic_handle_s *pmic, uint8_t *val); + +int stpmic2_regulator_get_prop(struct pmic_handle_s *pmic, uint8_t id, + enum stpmic2_prop_id prop); + +int stpmic2_regulator_set_prop(struct pmic_handle_s *pmic, uint8_t id, + enum stpmic2_prop_id prop, uint32_t arg); + +#endif /*STPMIC2_H*/ diff --git a/include/drivers/usb_device.h b/include/drivers/usb_device.h index 8fdb6ae1..d4c491c4 100644 --- a/include/drivers/usb_device.h +++ b/include/drivers/usb_device.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, STMicroelectronics - All Rights Reserved + * Copyright (c) 2021-2024, STMicroelectronics - All Rights Reserved * * SPDX-License-Identifier: BSD-3-Clause */ @@ -7,6 +7,7 @@ #ifndef USB_DEVICE_H #define USB_DEVICE_H +#include <stdbool.h> #include <stdint.h> #include <lib/utils_def.h> diff --git a/include/dt-bindings/clock/stm32mp13-clks.h b/include/dt-bindings/clock/stm32mp13-clks.h index 1d5bb783..031e4323 100644 --- a/include/dt-bindings/clock/stm32mp13-clks.h +++ b/include/dt-bindings/clock/stm32mp13-clks.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0+ or BSD-3-Clause */ /* - * Copyright (C) STMicroelectronics 2022 - All Rights Reserved + * Copyright (C) STMicroelectronics 2022-2024 - All Rights Reserved * Author: Gabriel Fernandez <gabriel.fernandez@st.com> for STMicroelectronics. */ @@ -193,7 +193,13 @@ #define SAI1 160 #define SAI2 161 -#define STM32MP1_LAST_CLK 162 +#define SPI1 162 +#define SPI2 163 +#define SPI3 164 +#define SPI4 165 +#define SPI5 166 + +#define STM32MP1_LAST_CLK 167 /* SCMI clock identifiers */ #define CK_SCMI0_HSE 0 diff --git a/include/dt-bindings/clock/stm32mp15-clksrc.h b/include/dt-bindings/clock/stm32mp15-clksrc.h index 3a3792da..e601b484 100644 --- a/include/dt-bindings/clock/stm32mp15-clksrc.h +++ b/include/dt-bindings/clock/stm32mp15-clksrc.h @@ -1,273 +1,413 @@ /* SPDX-License-Identifier: GPL-2.0+ OR BSD-3-Clause */ /* - * Copyright (C) 2017-2022, STMicroelectronics - All Rights Reserved + * Copyright (C) 2017-2024, STMicroelectronics - All Rights Reserved */ #ifndef _DT_BINDINGS_CLOCK_STM32MP15_CLKSRC_H_ #define _DT_BINDINGS_CLOCK_STM32MP15_CLKSRC_H_ +#include <lib/utils_def.h> + +#define CMD_DIV 0 +#define CMD_MUX 1 +#define CMD_CLK 2 + +#define CMD_ADDR_BIT BIT(31) + +#define CMD_SHIFT 26 +#define CMD_MASK GENMASK_32(31, 26) +#define CMD_DATA_MASK GENMASK_32(25, 0) + +#define DIV_ID_SHIFT 8 +#define DIV_ID_MASK GENMASK_32(15, 8) + +#define DIV_DIVN_SHIFT 0 +#define DIV_DIVN_MASK GENMASK_32(7, 0) + +#define MUX_ID_SHIFT 4 +#define MUX_ID_MASK GENMASK_32(11, 4) + +#define MUX_SEL_SHIFT 0 +#define MUX_SEL_MASK GENMASK_32(3, 0) + +#define CLK_ID_MASK GENMASK_32(19, 11) +#define CLK_ID_SHIFT 11 +#define CLK_ON_MASK 0x00000400 +#define CLK_ON_SHIFT 10 +#define CLK_DIV_MASK GENMASK_32(9, 4) +#define CLK_DIV_SHIFT 4 +#define CLK_SEL_MASK GENMASK_32(3, 0) +#define CLK_SEL_SHIFT 0 + +#define DIV(div_id, div) ((CMD_DIV << CMD_SHIFT) |\ + ((div_id) << DIV_ID_SHIFT) |\ + (div)) + +#define CLKSRC(mux_id, sel) ((CMD_MUX << CMD_SHIFT) |\ + ((mux_id) << MUX_ID_SHIFT) |\ + (sel)) + +/* CLK output is enable */ +#define CLK_SRC(clk_id, sel) ((CMD_CLK << CMD_SHIFT) |\ + ((clk_id) << CLK_ID_SHIFT) |\ + (sel) | CLK_ON_MASK) + +#define CLK_DISABLED(clk_id) ((CMD_CLK << CMD_SHIFT) |\ + ((clk_id) << CLK_ID_SHIFT)) + +#define CLK_ADDR_SHIFT 16 +#define CLK_ADDR_MASK GENMASK_32(30, 16) +#define CLK_ADDR_VAL_MASK GENMASK_32(15, 0) + +#define DIV_PLL1DIVP 0 +#define DIV_PLL2DIVP 1 +#define DIV_PLL2DIVQ 2 +#define DIV_PLL2DIVR 3 +#define DIV_PLL3DIVP 4 +#define DIV_PLL3DIVQ 5 +#define DIV_PLL3DIVR 6 +#define DIV_PLL4DIVP 7 +#define DIV_PLL4DIVQ 8 +#define DIV_PLL4DIVR 9 +#define DIV_MPU 10 +#define DIV_AXI 11 +#define DIV_MCU 12 +#define DIV_APB1 13 +#define DIV_APB2 14 +#define DIV_APB3 15 +#define DIV_APB4 16 +#define DIV_APB5 17 +#define DIV_RTC 19 +#define DIV_MCO1 20 +#define DIV_MCO2 21 +#define DIV_HSI 22 +#define DIV_TRACE 23 +#define DIV_ETHPTP 24 +#define DIV_NB 25 + +#define MUX_MPU 0 +#define MUX_AXI 1 +#define MUX_MCU 2 +#define MUX_PLL12 3 +#define MUX_PLL3 4 +#define MUX_PLL4 5 +#define MUX_CKPER 6 +#define MUX_RTC 7 +#define MUX_SDMMC12 8 +#define MUX_SDMMC3 9 +#define MUX_FMC 10 +#define MUX_QSPI 11 +#define MUX_RNG1 12 +#define MUX_RNG2 13 +#define MUX_USBPHY 14 +#define MUX_USBO 15 +#define MUX_STGEN 16 +#define MUX_SPDIF 17 +#define MUX_SPI2S1 18 +#define MUX_SPI2S23 19 +#define MUX_SPI45 20 +#define MUX_SPI6 21 +#define MUX_CEC 22 +#define MUX_I2C12 23 +#define MUX_I2C35 24 +#define MUX_I2C46 25 +#define MUX_LPTIM1 26 +#define MUX_LPTIM23 27 +#define MUX_LPTIM45 28 +#define MUX_UART1 29 +#define MUX_UART24 30 +#define MUX_UART35 31 +#define MUX_UART6 32 +#define MUX_UART78 33 +#define MUX_SAI1 34 +#define MUX_SAI2 35 +#define MUX_SAI3 36 +#define MUX_SAI4 37 +#define MUX_DSI 38 +#define MUX_FDCAN 39 +#define MUX_ADC 40 +#define MUX_ETH 41 +#define MUX_MCO1 42 +#define MUX_MCO2 43 +#define MUX_NB 44 + /* PLL output is enable when x=1, with x=p,q or r */ #define PQR(p, q, r) (((p) & 1) | (((q) & 1) << 1) | (((r) & 1) << 2)) -/* st,clksrc: mandatory clock source */ -#define CLK_MPU_HSI 0x00000200 -#define CLK_MPU_HSE 0x00000201 -#define CLK_MPU_PLL1P 0x00000202 -#define CLK_MPU_PLL1P_DIV 0x00000203 - -#define CLK_AXI_HSI 0x00000240 -#define CLK_AXI_HSE 0x00000241 -#define CLK_AXI_PLL2P 0x00000242 - -#define CLK_MCU_HSI 0x00000480 -#define CLK_MCU_HSE 0x00000481 -#define CLK_MCU_CSI 0x00000482 -#define CLK_MCU_PLL3P 0x00000483 - -#define CLK_PLL12_HSI 0x00000280 -#define CLK_PLL12_HSE 0x00000281 - -#define CLK_PLL3_HSI 0x00008200 -#define CLK_PLL3_HSE 0x00008201 -#define CLK_PLL3_CSI 0x00008202 - -#define CLK_PLL4_HSI 0x00008240 -#define CLK_PLL4_HSE 0x00008241 -#define CLK_PLL4_CSI 0x00008242 -#define CLK_PLL4_I2SCKIN 0x00008243 - -#define CLK_RTC_DISABLED 0x00001400 -#define CLK_RTC_LSE 0x00001401 -#define CLK_RTC_LSI 0x00001402 -#define CLK_RTC_HSE 0x00001403 - -#define CLK_MCO1_HSI 0x00008000 -#define CLK_MCO1_HSE 0x00008001 -#define CLK_MCO1_CSI 0x00008002 -#define CLK_MCO1_LSI 0x00008003 -#define CLK_MCO1_LSE 0x00008004 -#define CLK_MCO1_DISABLED 0x0000800F - -#define CLK_MCO2_MPU 0x00008040 -#define CLK_MCO2_AXI 0x00008041 -#define CLK_MCO2_MCU 0x00008042 -#define CLK_MCO2_PLL4P 0x00008043 -#define CLK_MCO2_HSE 0x00008044 -#define CLK_MCO2_HSI 0x00008045 -#define CLK_MCO2_DISABLED 0x0000804F - -/* st,pkcs: peripheral kernel clock source */ - -#define CLK_I2C12_PCLK1 0x00008C00 -#define CLK_I2C12_PLL4R 0x00008C01 -#define CLK_I2C12_HSI 0x00008C02 -#define CLK_I2C12_CSI 0x00008C03 -#define CLK_I2C12_DISABLED 0x00008C07 - -#define CLK_I2C35_PCLK1 0x00008C40 -#define CLK_I2C35_PLL4R 0x00008C41 -#define CLK_I2C35_HSI 0x00008C42 -#define CLK_I2C35_CSI 0x00008C43 -#define CLK_I2C35_DISABLED 0x00008C47 - -#define CLK_I2C46_PCLK5 0x00000C00 -#define CLK_I2C46_PLL3Q 0x00000C01 -#define CLK_I2C46_HSI 0x00000C02 -#define CLK_I2C46_CSI 0x00000C03 -#define CLK_I2C46_DISABLED 0x00000C07 - -#define CLK_SAI1_PLL4Q 0x00008C80 -#define CLK_SAI1_PLL3Q 0x00008C81 -#define CLK_SAI1_I2SCKIN 0x00008C82 -#define CLK_SAI1_CKPER 0x00008C83 -#define CLK_SAI1_PLL3R 0x00008C84 -#define CLK_SAI1_DISABLED 0x00008C87 - -#define CLK_SAI2_PLL4Q 0x00008CC0 -#define CLK_SAI2_PLL3Q 0x00008CC1 -#define CLK_SAI2_I2SCKIN 0x00008CC2 -#define CLK_SAI2_CKPER 0x00008CC3 -#define CLK_SAI2_SPDIF 0x00008CC4 -#define CLK_SAI2_PLL3R 0x00008CC5 -#define CLK_SAI2_DISABLED 0x00008CC7 - -#define CLK_SAI3_PLL4Q 0x00008D00 -#define CLK_SAI3_PLL3Q 0x00008D01 -#define CLK_SAI3_I2SCKIN 0x00008D02 -#define CLK_SAI3_CKPER 0x00008D03 -#define CLK_SAI3_PLL3R 0x00008D04 -#define CLK_SAI3_DISABLED 0x00008D07 - -#define CLK_SAI4_PLL4Q 0x00008D40 -#define CLK_SAI4_PLL3Q 0x00008D41 -#define CLK_SAI4_I2SCKIN 0x00008D42 -#define CLK_SAI4_CKPER 0x00008D43 -#define CLK_SAI4_PLL3R 0x00008D44 -#define CLK_SAI4_DISABLED 0x00008D47 - -#define CLK_SPI2S1_PLL4P 0x00008D80 -#define CLK_SPI2S1_PLL3Q 0x00008D81 -#define CLK_SPI2S1_I2SCKIN 0x00008D82 -#define CLK_SPI2S1_CKPER 0x00008D83 -#define CLK_SPI2S1_PLL3R 0x00008D84 -#define CLK_SPI2S1_DISABLED 0x00008D87 - -#define CLK_SPI2S23_PLL4P 0x00008DC0 -#define CLK_SPI2S23_PLL3Q 0x00008DC1 -#define CLK_SPI2S23_I2SCKIN 0x00008DC2 -#define CLK_SPI2S23_CKPER 0x00008DC3 -#define CLK_SPI2S23_PLL3R 0x00008DC4 -#define CLK_SPI2S23_DISABLED 0x00008DC7 - -#define CLK_SPI45_PCLK2 0x00008E00 -#define CLK_SPI45_PLL4Q 0x00008E01 -#define CLK_SPI45_HSI 0x00008E02 -#define CLK_SPI45_CSI 0x00008E03 -#define CLK_SPI45_HSE 0x00008E04 -#define CLK_SPI45_DISABLED 0x00008E07 - -#define CLK_SPI6_PCLK5 0x00000C40 -#define CLK_SPI6_PLL4Q 0x00000C41 -#define CLK_SPI6_HSI 0x00000C42 -#define CLK_SPI6_CSI 0x00000C43 -#define CLK_SPI6_HSE 0x00000C44 -#define CLK_SPI6_PLL3Q 0x00000C45 -#define CLK_SPI6_DISABLED 0x00000C47 - -#define CLK_UART6_PCLK2 0x00008E40 -#define CLK_UART6_PLL4Q 0x00008E41 -#define CLK_UART6_HSI 0x00008E42 -#define CLK_UART6_CSI 0x00008E43 -#define CLK_UART6_HSE 0x00008E44 -#define CLK_UART6_DISABLED 0x00008E47 - -#define CLK_UART24_PCLK1 0x00008E80 -#define CLK_UART24_PLL4Q 0x00008E81 -#define CLK_UART24_HSI 0x00008E82 -#define CLK_UART24_CSI 0x00008E83 -#define CLK_UART24_HSE 0x00008E84 -#define CLK_UART24_DISABLED 0x00008E87 - -#define CLK_UART35_PCLK1 0x00008EC0 -#define CLK_UART35_PLL4Q 0x00008EC1 -#define CLK_UART35_HSI 0x00008EC2 -#define CLK_UART35_CSI 0x00008EC3 -#define CLK_UART35_HSE 0x00008EC4 -#define CLK_UART35_DISABLED 0x00008EC7 - -#define CLK_UART78_PCLK1 0x00008F00 -#define CLK_UART78_PLL4Q 0x00008F01 -#define CLK_UART78_HSI 0x00008F02 -#define CLK_UART78_CSI 0x00008F03 -#define CLK_UART78_HSE 0x00008F04 -#define CLK_UART78_DISABLED 0x00008F07 - -#define CLK_UART1_PCLK5 0x00000C80 -#define CLK_UART1_PLL3Q 0x00000C81 -#define CLK_UART1_HSI 0x00000C82 -#define CLK_UART1_CSI 0x00000C83 -#define CLK_UART1_PLL4Q 0x00000C84 -#define CLK_UART1_HSE 0x00000C85 -#define CLK_UART1_DISABLED 0x00000C87 - -#define CLK_SDMMC12_HCLK6 0x00008F40 -#define CLK_SDMMC12_PLL3R 0x00008F41 -#define CLK_SDMMC12_PLL4P 0x00008F42 -#define CLK_SDMMC12_HSI 0x00008F43 -#define CLK_SDMMC12_DISABLED 0x00008F47 - -#define CLK_SDMMC3_HCLK2 0x00008F80 -#define CLK_SDMMC3_PLL3R 0x00008F81 -#define CLK_SDMMC3_PLL4P 0x00008F82 -#define CLK_SDMMC3_HSI 0x00008F83 -#define CLK_SDMMC3_DISABLED 0x00008F87 - -#define CLK_ETH_PLL4P 0x00008FC0 -#define CLK_ETH_PLL3Q 0x00008FC1 -#define CLK_ETH_DISABLED 0x00008FC3 - -#define CLK_QSPI_ACLK 0x00009000 -#define CLK_QSPI_PLL3R 0x00009001 -#define CLK_QSPI_PLL4P 0x00009002 -#define CLK_QSPI_CKPER 0x00009003 - -#define CLK_FMC_ACLK 0x00009040 -#define CLK_FMC_PLL3R 0x00009041 -#define CLK_FMC_PLL4P 0x00009042 -#define CLK_FMC_CKPER 0x00009043 - -#define CLK_FDCAN_HSE 0x000090C0 -#define CLK_FDCAN_PLL3Q 0x000090C1 -#define CLK_FDCAN_PLL4Q 0x000090C2 -#define CLK_FDCAN_PLL4R 0x000090C3 - -#define CLK_SPDIF_PLL4P 0x00009140 -#define CLK_SPDIF_PLL3Q 0x00009141 -#define CLK_SPDIF_HSI 0x00009142 -#define CLK_SPDIF_DISABLED 0x00009143 - -#define CLK_CEC_LSE 0x00009180 -#define CLK_CEC_LSI 0x00009181 -#define CLK_CEC_CSI_DIV122 0x00009182 -#define CLK_CEC_DISABLED 0x00009183 - -#define CLK_USBPHY_HSE 0x000091C0 -#define CLK_USBPHY_PLL4R 0x000091C1 -#define CLK_USBPHY_HSE_DIV2 0x000091C2 -#define CLK_USBPHY_DISABLED 0x000091C3 - -#define CLK_USBO_PLL4R 0x800091C0 -#define CLK_USBO_USBPHY 0x800091C1 - -#define CLK_RNG1_CSI 0x00000CC0 -#define CLK_RNG1_PLL4R 0x00000CC1 -#define CLK_RNG1_LSE 0x00000CC2 -#define CLK_RNG1_LSI 0x00000CC3 - -#define CLK_RNG2_CSI 0x00009200 -#define CLK_RNG2_PLL4R 0x00009201 -#define CLK_RNG2_LSE 0x00009202 -#define CLK_RNG2_LSI 0x00009203 - -#define CLK_CKPER_HSI 0x00000D00 -#define CLK_CKPER_CSI 0x00000D01 -#define CLK_CKPER_HSE 0x00000D02 -#define CLK_CKPER_DISABLED 0x00000D03 - -#define CLK_STGEN_HSI 0x00000D40 -#define CLK_STGEN_HSE 0x00000D41 -#define CLK_STGEN_DISABLED 0x00000D43 - -#define CLK_DSI_DSIPLL 0x00009240 -#define CLK_DSI_PLL4P 0x00009241 - -#define CLK_ADC_PLL4R 0x00009280 -#define CLK_ADC_CKPER 0x00009281 -#define CLK_ADC_PLL3Q 0x00009282 -#define CLK_ADC_DISABLED 0x00009283 - -#define CLK_LPTIM45_PCLK3 0x000092C0 -#define CLK_LPTIM45_PLL4P 0x000092C1 -#define CLK_LPTIM45_PLL3Q 0x000092C2 -#define CLK_LPTIM45_LSE 0x000092C3 -#define CLK_LPTIM45_LSI 0x000092C4 -#define CLK_LPTIM45_CKPER 0x000092C5 -#define CLK_LPTIM45_DISABLED 0x000092C7 - -#define CLK_LPTIM23_PCLK3 0x00009300 -#define CLK_LPTIM23_PLL4Q 0x00009301 -#define CLK_LPTIM23_CKPER 0x00009302 -#define CLK_LPTIM23_LSE 0x00009303 -#define CLK_LPTIM23_LSI 0x00009304 -#define CLK_LPTIM23_DISABLED 0x00009307 - -#define CLK_LPTIM1_PCLK1 0x00009340 -#define CLK_LPTIM1_PLL4P 0x00009341 -#define CLK_LPTIM1_PLL3Q 0x00009342 -#define CLK_LPTIM1_LSE 0x00009343 -#define CLK_LPTIM1_LSI 0x00009344 -#define CLK_LPTIM1_CKPER 0x00009345 -#define CLK_LPTIM1_DISABLED 0x00009347 +/* st,clksrc: clock sources */ +#define CLK_MPU_HSI CLKSRC(MUX_MPU, 0) +#define CLK_MPU_HSE CLKSRC(MUX_MPU, 1) +#define CLK_MPU_PLL1P CLKSRC(MUX_MPU, 2) +#define CLK_MPU_PLL1P_DIV CLKSRC(MUX_MPU, 3) + +#define CLK_AXI_HSI CLKSRC(MUX_AXI, 0) +#define CLK_AXI_HSE CLKSRC(MUX_AXI, 1) +#define CLK_AXI_PLL2P CLKSRC(MUX_AXI, 2) + +#define CLK_MCU_HSI CLKSRC(MUX_MCU, 0) +#define CLK_MCU_HSE CLKSRC(MUX_MCU, 1) +#define CLK_MCU_CSI CLKSRC(MUX_MCU, 2) +#define CLK_MCU_PLL3P CLKSRC(MUX_MCU, 3) + +#define CLK_PLL12_HSI CLKSRC(MUX_PLL12, 0) +#define CLK_PLL12_HSE CLKSRC(MUX_PLL12, 1) + +#define CLK_PLL3_HSI CLKSRC(MUX_PLL3, 0) +#define CLK_PLL3_HSE CLKSRC(MUX_PLL3, 1) +#define CLK_PLL3_CSI CLKSRC(MUX_PLL3, 2) + +#define CLK_PLL4_HSI CLKSRC(MUX_PLL4, 0) +#define CLK_PLL4_HSE CLKSRC(MUX_PLL4, 1) +#define CLK_PLL4_CSI CLKSRC(MUX_PLL4, 2) +#define CLK_PLL4_I2SCKIN CLKSRC(MUX_PLL4, 3) + +#define CLK_RTC_DISABLED CLK_DISABLED(RTC) +#define CLK_RTC_LSE CLK_SRC(RTC, 1) +#define CLK_RTC_LSI CLK_SRC(RTC, 2) +#define CLK_RTC_HSE CLK_SRC(RTC, 3) + +/* Register addresses of MCO1 & MCO2 */ +#define MCO1 0x800 +#define MCO2 0x804 + +#define MCO_OFF 0 +#define MCO_ON 1 +#define MCO_STATUS_SHIFT 12 + +#define MCO_ON_CFG(addr, sel) (CMD_ADDR_BIT |\ + ((addr) << CLK_ADDR_SHIFT) |\ + (MCO_ON << MCO_STATUS_SHIFT) |\ + (sel)) + +#define MCO_OFF_CFG(addr) (CMD_ADDR_BIT |\ + ((addr) << CLK_ADDR_SHIFT) |\ + (MCO_OFF << MCO_STATUS_SHIFT)) + +#define CLK_MCO1_HSI MCO_ON_CFG(MCO1, 0) +#define CLK_MCO1_HSE MCO_ON_CFG(MCO1, 1) +#define CLK_MCO1_CSI MCO_ON_CFG(MCO1, 2) +#define CLK_MCO1_LSI MCO_ON_CFG(MCO1, 3) +#define CLK_MCO1_LSE MCO_ON_CFG(MCO1, 4) +#define CLK_MCO1_DISABLED MCO_OFF_CFG(MCO1) + +#define CLK_MCO2_MPU MCO_ON_CFG(MCO2, 0) +#define CLK_MCO2_AXI MCO_ON_CFG(MCO2, 1) +#define CLK_MCO2_MCU MCO_ON_CFG(MCO2, 2) +#define CLK_MCO2_PLL4 MCO_ON_CFG(MCO2, 3) +#define CLK_MCO2_HSE MCO_ON_CFG(MCO2, 4) +#define CLK_MCO2_HSI MCO_ON_CFG(MCO2, 5) +#define CLK_MCO2_DISABLED MCO_OFF_CFG(MCO2) + +#define CLK_I2C12_PCLK1 CLKSRC(MUX_I2C12, 0) +#define CLK_I2C12_PLL4R CLKSRC(MUX_I2C12, 1) +#define CLK_I2C12_HSI CLKSRC(MUX_I2C12, 2) +#define CLK_I2C12_CSI CLKSRC(MUX_I2C12, 3) +#define CLK_I2C12_DISABLED CLKSRC(MUX_I2C12, 7) + +#define CLK_I2C35_PCLK1 CLKSRC(MUX_I2C35, 0) +#define CLK_I2C35_PLL4R CLKSRC(MUX_I2C35, 1) +#define CLK_I2C35_HSI CLKSRC(MUX_I2C35, 2) +#define CLK_I2C35_CSI CLKSRC(MUX_I2C35, 3) +#define CLK_I2C35_DISABLED CLKSRC(MUX_I2C35, 7) + +#define CLK_I2C46_PCLK5 CLKSRC(MUX_I2C46, 0) +#define CLK_I2C46_PLL3Q CLKSRC(MUX_I2C46, 1) +#define CLK_I2C46_HSI CLKSRC(MUX_I2C46, 2) +#define CLK_I2C46_CSI CLKSRC(MUX_I2C46, 3) +#define CLK_I2C46_DISABLED CLKSRC(MUX_I2C46, 7) + +#define CLK_SAI1_PLL4Q CLKSRC(MUX_SAI1, 0) +#define CLK_SAI1_PLL3Q CLKSRC(MUX_SAI1, 1) +#define CLK_SAI1_I2SCKIN CLKSRC(MUX_SAI1, 2) +#define CLK_SAI1_CKPER CLKSRC(MUX_SAI1, 3) +#define CLK_SAI1_PLL3R CLKSRC(MUX_SAI1, 4) +#define CLK_SAI1_DISABLED CLKSRC(MUX_SAI1, 7) + +#define CLK_SAI2_PLL4Q CLKSRC(MUX_SAI2, 0) +#define CLK_SAI2_PLL3Q CLKSRC(MUX_SAI2, 1) +#define CLK_SAI2_I2SCKIN CLKSRC(MUX_SAI2, 2) +#define CLK_SAI2_CKPER CLKSRC(MUX_SAI2, 3) +#define CLK_SAI2_SPDIF CLKSRC(MUX_SAI2, 4) +#define CLK_SAI2_PLL3R CLKSRC(MUX_SAI2, 5) +#define CLK_SAI2_DISABLED CLKSRC(MUX_SAI2, 7) + +#define CLK_SAI3_PLL4Q CLKSRC(MUX_SAI3, 0) +#define CLK_SAI3_PLL3Q CLKSRC(MUX_SAI3, 1) +#define CLK_SAI3_I2SCKIN CLKSRC(MUX_SAI3, 2) +#define CLK_SAI3_CKPER CLKSRC(MUX_SAI3, 3) +#define CLK_SAI3_PLL3R CLKSRC(MUX_SAI3, 4) +#define CLK_SAI3_DISABLED CLKSRC(MUX_SAI3, 7) + +#define CLK_SAI4_PLL4Q CLKSRC(MUX_SAI4, 0) +#define CLK_SAI4_PLL3Q CLKSRC(MUX_SAI4, 1) +#define CLK_SAI4_I2SCKIN CLKSRC(MUX_SAI4, 2) +#define CLK_SAI4_CKPER CLKSRC(MUX_SAI4, 3) +#define CLK_SAI4_PLL3R CLKSRC(MUX_SAI4, 4) +#define CLK_SAI4_DISABLED CLKSRC(MUX_SAI4, 7) + +#define CLK_SPI2S1_PLL4P CLKSRC(MUX_SPI2S1, 0) +#define CLK_SPI2S1_PLL3Q CLKSRC(MUX_SPI2S1, 1) +#define CLK_SPI2S1_I2SCKIN CLKSRC(MUX_SPI2S1, 2) +#define CLK_SPI2S1_CKPER CLKSRC(MUX_SPI2S1, 3) +#define CLK_SPI2S1_PLL3R CLKSRC(MUX_SPI2S1, 4) +#define CLK_SPI2S1_DISABLED CLKSRC(MUX_SPI2S1, 7) + +#define CLK_SPI2S23_PLL4P CLKSRC(MUX_SPI2S23, 0) +#define CLK_SPI2S23_PLL3Q CLKSRC(MUX_SPI2S23, 1) +#define CLK_SPI2S23_I2SCKIN CLKSRC(MUX_SPI2S23, 2) +#define CLK_SPI2S23_CKPER CLKSRC(MUX_SPI2S23, 3) +#define CLK_SPI2S23_PLL3R CLKSRC(MUX_SPI2S23, 4) +#define CLK_SPI2S23_DISABLED CLKSRC(MUX_SPI2S23, 7) + +#define CLK_SPI45_PCLK2 CLKSRC(MUX_SPI45, 0) +#define CLK_SPI45_PLL4Q CLKSRC(MUX_SPI45, 1) +#define CLK_SPI45_HSI CLKSRC(MUX_SPI45, 2) +#define CLK_SPI45_CSI CLKSRC(MUX_SPI45, 3) +#define CLK_SPI45_HSE CLKSRC(MUX_SPI45, 4) +#define CLK_SPI45_DISABLED CLKSRC(MUX_SPI45, 7) + +#define CLK_SPI6_PCLK5 CLKSRC(MUX_SPI6, 0) +#define CLK_SPI6_PLL4Q CLKSRC(MUX_SPI6, 1) +#define CLK_SPI6_HSI CLKSRC(MUX_SPI6, 2) +#define CLK_SPI6_CSI CLKSRC(MUX_SPI6, 3) +#define CLK_SPI6_HSE CLKSRC(MUX_SPI6, 4) +#define CLK_SPI6_PLL3Q CLKSRC(MUX_SPI6, 5) +#define CLK_SPI6_DISABLED CLKSRC(MUX_SPI6, 7) + +#define CLK_UART6_PCLK2 CLKSRC(MUX_UART6, 0) +#define CLK_UART6_PLL4Q CLKSRC(MUX_UART6, 1) +#define CLK_UART6_HSI CLKSRC(MUX_UART6, 2) +#define CLK_UART6_CSI CLKSRC(MUX_UART6, 3) +#define CLK_UART6_HSE CLKSRC(MUX_UART6, 4) +#define CLK_UART6_DISABLED CLKSRC(MUX_UART6, 7) + +#define CLK_UART24_PCLK1 CLKSRC(MUX_UART24, 0) +#define CLK_UART24_PLL4Q CLKSRC(MUX_UART24, 1) +#define CLK_UART24_HSI CLKSRC(MUX_UART24, 2) +#define CLK_UART24_CSI CLKSRC(MUX_UART24, 3) +#define CLK_UART24_HSE CLKSRC(MUX_UART24, 4) +#define CLK_UART24_DISABLED CLKSRC(MUX_UART24, 7) + +#define CLK_UART35_PCLK1 CLKSRC(MUX_UART35, 0) +#define CLK_UART35_PLL4Q CLKSRC(MUX_UART35, 1) +#define CLK_UART35_HSI CLKSRC(MUX_UART35, 2) +#define CLK_UART35_CSI CLKSRC(MUX_UART35, 3) +#define CLK_UART35_HSE CLKSRC(MUX_UART35, 4) +#define CLK_UART35_DISABLED CLKSRC(MUX_UART35, 7) + +#define CLK_UART78_PCLK1 CLKSRC(MUX_UART78, 0) +#define CLK_UART78_PLL4Q CLKSRC(MUX_UART78, 1) +#define CLK_UART78_HSI CLKSRC(MUX_UART78, 2) +#define CLK_UART78_CSI CLKSRC(MUX_UART78, 3) +#define CLK_UART78_HSE CLKSRC(MUX_UART78, 4) +#define CLK_UART78_DISABLED CLKSRC(MUX_UART78, 7) + +#define CLK_UART1_PCLK5 CLKSRC(MUX_UART1, 0) +#define CLK_UART1_PLL3Q CLKSRC(MUX_UART1, 1) +#define CLK_UART1_HSI CLKSRC(MUX_UART1, 2) +#define CLK_UART1_CSI CLKSRC(MUX_UART1, 3) +#define CLK_UART1_PLL4Q CLKSRC(MUX_UART1, 4) +#define CLK_UART1_HSE CLKSRC(MUX_UART1, 5) +#define CLK_UART1_DISABLED CLKSRC(MUX_UART1, 7) + +#define CLK_SDMMC12_HCLK6 CLKSRC(MUX_SDMMC12, 0) +#define CLK_SDMMC12_PLL3R CLKSRC(MUX_SDMMC12, 1) +#define CLK_SDMMC12_PLL4P CLKSRC(MUX_SDMMC12, 2) +#define CLK_SDMMC12_HSI CLKSRC(MUX_SDMMC12, 3) +#define CLK_SDMMC12_DISABLED CLKSRC(MUX_SDMMC12, 7) + +#define CLK_SDMMC3_HCLK2 CLKSRC(MUX_SDMMC3, 0) +#define CLK_SDMMC3_PLL3R CLKSRC(MUX_SDMMC3, 1) +#define CLK_SDMMC3_PLL4P CLKSRC(MUX_SDMMC3, 2) +#define CLK_SDMMC3_HSI CLKSRC(MUX_SDMMC3, 3) +#define CLK_SDMMC3_DISABLED CLKSRC(MUX_SDMMC3, 7) + +#define CLK_ETH_PLL4P CLKSRC(MUX_ETH, 0) +#define CLK_ETH_PLL3Q CLKSRC(MUX_ETH, 1) +#define CLK_ETH_DISABLED CLKSRC(MUX_ETH, 3) + +#define CLK_QSPI_ACLK CLKSRC(MUX_QSPI, 0) +#define CLK_QSPI_PLL3R CLKSRC(MUX_QSPI, 1) +#define CLK_QSPI_PLL4P CLKSRC(MUX_QSPI, 2) +#define CLK_QSPI_CKPER CLKSRC(MUX_QSPI, 3) + +#define CLK_FMC_ACLK CLKSRC(MUX_FMC, 0) +#define CLK_FMC_PLL3R CLKSRC(MUX_FMC, 1) +#define CLK_FMC_PLL4P CLKSRC(MUX_FMC, 2) +#define CLK_FMC_CKPER CLKSRC(MUX_FMC, 3) + +#define CLK_FDCAN_HSE CLKSRC(MUX_FDCAN, 0) +#define CLK_FDCAN_PLL3Q CLKSRC(MUX_FDCAN, 1) +#define CLK_FDCAN_PLL4Q CLKSRC(MUX_FDCAN, 2) +#define CLK_FDCAN_PLL4R CLKSRC(MUX_FDCAN, 3) + +#define CLK_SPDIF_PLL4P CLKSRC(MUX_SPDIF, 0) +#define CLK_SPDIF_PLL3Q CLKSRC(MUX_SPDIF, 1) +#define CLK_SPDIF_HSI CLKSRC(MUX_SPDIF, 2) +#define CLK_SPDIF_DISABLED CLKSRC(MUX_SPDIF, 3) + +#define CLK_CEC_LSE CLKSRC(MUX_CEC, 0) +#define CLK_CEC_LSI CLKSRC(MUX_CEC, 1) +#define CLK_CEC_CSI_DIV122 CLKSRC(MUX_CEC, 2) +#define CLK_CEC_DISABLED CLKSRC(MUX_CEC, 3) + +#define CLK_USBPHY_HSE CLKSRC(MUX_USBPHY, 0) +#define CLK_USBPHY_PLL4R CLKSRC(MUX_USBPHY, 1) +#define CLK_USBPHY_HSE_DIV2 CLKSRC(MUX_USBPHY, 2) +#define CLK_USBPHY_DISABLED CLKSRC(MUX_USBPHY, 3) + +#define CLK_USBO_PLL4R CLKSRC(MUX_USBO, 0) +#define CLK_USBO_USBPHY CLKSRC(MUX_USBO, 1) + +#define CLK_RNG1_CSI CLKSRC(MUX_RNG1, 0) +#define CLK_RNG1_PLL4R CLKSRC(MUX_RNG1, 1) +#define CLK_RNG1_LSE CLKSRC(MUX_RNG1, 2) +#define CLK_RNG1_LSI CLKSRC(MUX_RNG1, 3) + +#define CLK_RNG2_CSI CLKSRC(MUX_RNG2, 0) +#define CLK_RNG2_PLL4R CLKSRC(MUX_RNG2, 1) +#define CLK_RNG2_LSE CLKSRC(MUX_RNG2, 2) +#define CLK_RNG2_LSI CLKSRC(MUX_RNG2, 3) + +#define CLK_CKPER_HSI CLKSRC(MUX_CKPER, 0) +#define CLK_CKPER_CSI CLKSRC(MUX_CKPER, 1) +#define CLK_CKPER_HSE CLKSRC(MUX_CKPER, 2) +#define CLK_CKPER_DISABLED CLKSRC(MUX_CKPER, 3) + +#define CLK_STGEN_HSI CLKSRC(MUX_STGEN, 0) +#define CLK_STGEN_HSE CLKSRC(MUX_STGEN, 1) +#define CLK_STGEN_DISABLED CLKSRC(MUX_STGEN, 3) + +#define CLK_DSI_DSIPLL CLKSRC(MUX_DSI, 0) +#define CLK_DSI_PLL4P CLKSRC(MUX_DSI, 1) + +#define CLK_ADC_PLL4R CLKSRC(MUX_ADC, 0) +#define CLK_ADC_CKPER CLKSRC(MUX_ADC, 1) +#define CLK_ADC_PLL3Q CLKSRC(MUX_ADC, 2) +#define CLK_ADC_DISABLED CLKSRC(MUX_ADC, 3) + +#define CLK_LPTIM45_PCLK3 CLKSRC(MUX_LPTIM45, 0) +#define CLK_LPTIM45_PLL4P CLKSRC(MUX_LPTIM45, 1) +#define CLK_LPTIM45_PLL3Q CLKSRC(MUX_LPTIM45, 2) +#define CLK_LPTIM45_LSE CLKSRC(MUX_LPTIM45, 3) +#define CLK_LPTIM45_LSI CLKSRC(MUX_LPTIM45, 4) +#define CLK_LPTIM45_CKPER CLKSRC(MUX_LPTIM45, 5) +#define CLK_LPTIM45_DISABLED CLKSRC(MUX_LPTIM45, 7) + +#define CLK_LPTIM23_PCLK3 CLKSRC(MUX_LPTIM23, 0) +#define CLK_LPTIM23_PLL4Q CLKSRC(MUX_LPTIM23, 1) +#define CLK_LPTIM23_CKPER CLKSRC(MUX_LPTIM23, 2) +#define CLK_LPTIM23_LSE CLKSRC(MUX_LPTIM23, 3) +#define CLK_LPTIM23_LSI CLKSRC(MUX_LPTIM23, 4) +#define CLK_LPTIM23_DISABLED CLKSRC(MUX_LPTIM23, 7) + +#define CLK_LPTIM1_PCLK1 CLKSRC(MUX_LPTIM1, 0) +#define CLK_LPTIM1_PLL4P CLKSRC(MUX_LPTIM1, 1) +#define CLK_LPTIM1_PLL3Q CLKSRC(MUX_LPTIM1, 2) +#define CLK_LPTIM1_LSE CLKSRC(MUX_LPTIM1, 3) +#define CLK_LPTIM1_LSI CLKSRC(MUX_LPTIM1, 4) +#define CLK_LPTIM1_CKPER CLKSRC(MUX_LPTIM1, 5) +#define CLK_LPTIM1_DISABLED CLKSRC(MUX_LPTIM1, 7) /* define for st,pll /csg */ #define SSCG_MODE_CENTER_SPREAD 0 diff --git a/include/dt-bindings/clock/stm32mp25-clks.h b/include/dt-bindings/clock/stm32mp25-clks.h index c4ff9cfc..e7ab5b0c 100644 --- a/include/dt-bindings/clock/stm32mp25-clks.h +++ b/include/dt-bindings/clock/stm32mp25-clks.h @@ -1,7 +1,7 @@ /* SPDX-License-Identifier: GPL-2.0-only OR BSD-3-Clause */ /* - * Copyright (C) 2023, STMicroelectronics - All Rights Reserved - * Author: Gabriel Fernandez <gabriel.fernandez@foss.st.com> for STMicroelectronics. + * Copyright (C) 2023-2024, STMicroelectronics - All Rights Reserved + * Author: Gabriel Fernandez <gabriel.fernandez@foss.st.com> */ #ifndef _DT_BINDINGS_STM32MP25_CLKS_H_ @@ -109,7 +109,7 @@ /* LOW SPEED MCU CLOCK */ #define CK_ICN_LS_MCU 88 -#define CK_BUS_STM500 89 +#define CK_BUS_STM 89 #define CK_BUS_FMC 90 #define CK_BUS_GPU 91 #define CK_BUS_ETH1 92 @@ -233,7 +233,6 @@ #define CK_BUS_DDRCFG 210 #define CK_BUS_GICV2M 211 #define CK_BUS_USBTC 212 -#define CK_BUS_BUSPERFM 213 #define CK_BUS_USB3PCIEPHY 214 #define CK_BUS_STGEN 215 #define CK_BUS_VDEC 216 @@ -272,7 +271,7 @@ #define CK_BUS_RISAF4 249 #define CK_BUS_USB2OHCI 250 #define CK_BUS_USB2EHCI 251 -#define CK_BUS_USB3DRD 252 +#define CK_BUS_USB3DR 252 #define CK_KER_LPTIM1 253 #define CK_KER_LPTIM2 254 #define CK_KER_USART2 255 @@ -364,8 +363,10 @@ #define CK_BUS_ETHSWACMCFG 341 #define CK_BUS_ETHSWACMMSG 342 #define HSE_DIV2_CK 343 +#define CK_KER_ETR 344 +#define CK_KER_STM 345 -#define STM32MP25_LAST_CLK 344 +#define STM32MP25_LAST_CLK 346 #define CK_SCMI_ICN_HS_MCU 0 #define CK_SCMI_ICN_SDMMC 1 @@ -453,8 +454,7 @@ #define CK_SCMI_TIMG2 83 #define CK_SCMI_BKPSRAM 84 #define CK_SCMI_BSEC 85 -#define CK_SCMI_BUSPERFM 86 -#define CK_SCMI_ETR 87 +#define CK_SCMI_BUS_ETR 87 #define CK_SCMI_FMC 88 #define CK_SCMI_GPIOA 89 #define CK_SCMI_GPIOB 90 @@ -489,6 +489,8 @@ #define CK_SCMI_SYSDBG 119 #define CK_SCMI_SYSATB 120 #define CK_SCMI_TSDBG 121 -#define CK_SCMI_STM500 122 +#define CK_SCMI_BUS_STM 122 +#define CK_SCMI_KER_STM 123 +#define CK_SCMI_KER_ETR 124 #endif /* _DT_BINDINGS_STM32MP25_CLKS_H_ */ diff --git a/include/dt-bindings/clock/stm32mp25-clksrc.h b/include/dt-bindings/clock/stm32mp25-clksrc.h index e6f7154b..319fd824 100644 --- a/include/dt-bindings/clock/stm32mp25-clksrc.h +++ b/include/dt-bindings/clock/stm32mp25-clksrc.h @@ -1,6 +1,6 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later OR BSD-3-Clause */ +/* SPDX-License-Identifier: GPL-2.0-only OR BSD-3-Clause */ /* - * Copyright (C) 2023, STMicroelectronics - All Rights Reserved + * Copyright (C) 2023-2024, STMicroelectronics - All Rights Reserved */ #ifndef _DT_BINDINGS_CLOCK_STM32MP25_CLKSRC_H_ @@ -108,9 +108,8 @@ #define MUX_DSIPHY 18 #define MUX_LVDSPHY 19 #define MUX_DTS 20 -#define MUX_CPU1 21 -#define MUX_D3PER 22 -#define MUX_NB 23 +#define MUX_D3PER 21 +#define MUX_NB 22 #define MUXSEL_HSI 0 #define MUXSEL_HSE 1 @@ -144,7 +143,7 @@ #define MUX_USB3PCIEPHY_FLEX34 0x0 #define MUX_USB3PCIEPHY_HSE 0x1 -#define MUX_DSIBLANE_FLEX28 0x0 +#define MUX_DSIBLANE_DSIPHY 0x0 #define MUX_DSIBLANE_FLEX27 0x1 #define MUX_DSIPHY_FLEX28 0x0 @@ -219,8 +218,8 @@ /* define for st,drive */ #define LSEDRV_LOWEST 0 -#define LSEDRV_MEDIUM_LOW 1 -#define LSEDRV_MEDIUM_HIGH 2 +#define LSEDRV_MEDIUM_LOW 2 +#define LSEDRV_MEDIUM_HIGH 1 #define LSEDRV_HIGHEST 3 #endif /* _DT_BINDINGS_CLOCK_STM32MP25_CLKSRC_H_ */ diff --git a/include/dt-bindings/gpio/stm32-gpio.h b/include/dt-bindings/gpio/stm32-gpio.h new file mode 100644 index 00000000..2c7a0f15 --- /dev/null +++ b/include/dt-bindings/gpio/stm32-gpio.h @@ -0,0 +1,41 @@ +/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) */ +/* + * Copyright (C) 2024 STMicroelectronics - All Rights Reserved + * Author: Paillet Pascal <p.paillet@foss.st.com> for STMicroelectronics. + */ + +#ifndef DT_BINDINGS_STM32_GPIO_H +#define DT_BINDINGS_STM32_GPIO_H + +/* Bank IDs used in GPIO driver API */ +#define GPIO_BANK_A 0U +#define GPIO_BANK_B 1U +#define GPIO_BANK_C 2U +#define GPIO_BANK_D 3U +#define GPIO_BANK_E 4U +#define GPIO_BANK_F 5U +#define GPIO_BANK_G 6U +#define GPIO_BANK_H 7U +#define GPIO_BANK_I 8U +#define GPIO_BANK_J 9U +#define GPIO_BANK_K 10U +#define GPIO_BANK_Z 25U + +/* Bit 0 is used to set GPIO in input mode */ +#define GPIOF_DIR_OUT 0x0 +#define GPIOF_DIR_IN 0x1 + +/* Bit 1 is used to set GPIO high level during init */ +#define GPIOF_INIT_LOW 0x0 +#define GPIOF_INIT_HIGH 0x2 + +#define GPIOF_IN (GPIOF_DIR_IN) +#define GPIOF_OUT_INIT_LOW (GPIOF_DIR_OUT | GPIOF_INIT_LOW) +#define GPIOF_OUT_INIT_HIGH (GPIOF_DIR_OUT | GPIOF_INIT_HIGH) + +/* Bit 2 is used to set GPIO pull up */ +#define GPIOF_PULL_UP 0x4 +/* Bit 3 is used to set GPIO pull down */ +#define GPIOF_PULL_DOWN 0x8 + +#endif /* DT_BINDINGS_STM32_GPIO_H */ diff --git a/include/dt-bindings/reset/stm32mp25-resets.h b/include/dt-bindings/reset/stm32mp25-resets.h index c34fe2ae..99b8058e 100644 --- a/include/dt-bindings/reset/stm32mp25-resets.h +++ b/include/dt-bindings/reset/stm32mp25-resets.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0-or-later or BSD-3-Clause */ /* - * Copyright (C) 2023, STMicroelectronics - All Rights Reserved + * Copyright (C) 2023-2024, STMicroelectronics - All Rights Reserved * Author(s): Gabriel Fernandez <gabriel.fernandez@foss.st.com> for STMicroelectronics. */ @@ -14,16 +14,16 @@ #define C2_R 8288 #define C2_HOLDBOOT_R 8608 #define C1_HOLDBOOT_R 8609 -#define VSW_R 8703 -#define C1MS_R 8808 -#define IWDG2_KER_R 9074 -#define IWDG4_KER_R 9202 -#define C3_R 9312 -#define DDRCP_R 9856 -#define DDRCAPB_R 9888 -#define DDRPHYCAPB_R 9920 -#define DDRCFG_R 9984 -#define DDR_R 10016 +#define VSW_R 8735 +#define C1MS_R 8840 +#define IWDG2_KER_R 9106 +#define IWDG4_KER_R 9234 +#define C3_R 9344 +#define DDRCP_R 9888 +#define DDRCAPB_R 9920 +#define DDRPHYCAPB_R 9952 +#define DDRCFG_R 10016 +#define DDR_R 10048 #define OSPI1_R 10400 #define OSPI1DLL_R 10416 #define OSPI2_R 10432 @@ -115,7 +115,7 @@ #define USB2_R 16352 #define USB2PHY1_R 16384 #define USB2PHY2_R 16416 -#define USB3DRD_R 16448 +#define USB3DR_R 16448 #define USB3PCIEPHY_R 16480 #define PCIE_R 16512 #define USBTC_R 16544 @@ -143,7 +143,6 @@ #define CRYP2_R 17440 #define WWDG1_R 17632 #define WWDG2_R 17664 -#define BUSPERFM_R 17696 #define VREF_R 17728 #define DTS_R 17760 #define CRC_R 17824 @@ -159,6 +158,9 @@ #define RST_SCMI_C1_HOLDBOOT_R 2 #define RST_SCMI_C2_HOLDBOOT_R 3 #define RST_SCMI_FMC 4 -#define RST_SCMI_PCIE 5 +#define RST_SCMI_OSPI1 5 +#define RST_SCMI_OSPI1DLL 6 +#define RST_SCMI_OSPI2 7 +#define RST_SCMI_OSPI2DLL 8 #endif /* _DT_BINDINGS_STM32MP25_RESET_H_ */ diff --git a/include/lib/cpus/aarch32/cpu_macros.S b/include/lib/cpus/aarch32/cpu_macros.S index 096e0b14..cfa5831f 100644 --- a/include/lib/cpus/aarch32/cpu_macros.S +++ b/include/lib/cpus/aarch32/cpu_macros.S @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016-2023, Arm Limited and Contributors. All rights reserved. + * Copyright (c) 2016-2024, Arm Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -115,11 +115,6 @@ .popsection .endif - /* - * Mandatory errata status printing function for CPUs of - * this class. - */ - .word \_name\()_errata_report .word \_name\()_cpu_str #ifdef IMAGE_BL32 @@ -130,45 +125,6 @@ #endif .endm -#if REPORT_ERRATA - /* - * Print status of a CPU errata - * - * _chosen: - * Identifier indicating whether or not a CPU errata has been - * compiled in. - * _cpu: - * Name of the CPU - * _id: - * Errata identifier - * _rev_var: - * Register containing the combined value CPU revision and variant - * - typically the return value of cpu_get_rev_var - */ - .macro report_errata _chosen, _cpu, _id, _rev_var=r4 - /* Stash a string with errata ID */ - .pushsection .rodata - \_cpu\()_errata_\_id\()_str: - .asciz "\_id" - .popsection - - /* Check whether errata applies */ - mov r0, \_rev_var - bl check_errata_\_id - - .ifeq \_chosen - /* - * Errata workaround has not been compiled in. If the errata would have - * applied had it been compiled in, print its status as missing. - */ - cmp r0, #0 - movne r0, #ERRATA_MISSING - .endif - ldr r1, =\_cpu\()_cpu_str - ldr r2, =\_cpu\()_errata_\_id\()_str - bl errata_print_msg - .endm -#endif /* * Helper macro that reads the part number of the current CPU and jumps * to the given label if it matches the CPU MIDR provided. @@ -239,21 +195,4 @@ .popsection .endm -/* - * Maintain compatibility with the old scheme of "each cpu has its own reporter". - * TODO remove entirely once all cpus have been converted. This includes the - * cpu_ops entry, as print_errata_status can call this directly for all cpus - */ -.macro errata_report_shim _cpu:req - #if REPORT_ERRATA - func \_cpu\()_errata_report - push {r12, lr} - - bl generic_errata_report - - pop {r12, lr} - bx lr - endfunc \_cpu\()_errata_report - #endif -.endm #endif /* CPU_MACROS_S */ diff --git a/include/lib/cpus/aarch64/cortex_a35.h b/include/lib/cpus/aarch64/cortex_a35.h index cef2960d..c82b4eb0 100644 --- a/include/lib/cpus/aarch64/cortex_a35.h +++ b/include/lib/cpus/aarch64/cortex_a35.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016-2019, Arm Limited and Contributors. All rights reserved. + * Copyright (c) 2016-2024, Arm Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -12,6 +12,9 @@ /* Cortex-A35 Main ID register for revision 0 */ #define CORTEX_A35_MIDR U(0x410FD040) +/* L2 Extended Control Register */ +#define CORTEX_A35_L2ECTLR_EL1 S3_1_C11_C0_3 + /******************************************************************************* * CPU Extended Control register specific definitions. * CPUECTLR_EL1 is an implementation-specific register. diff --git a/include/lib/cpus/aarch64/cortex_a520.h b/include/lib/cpus/aarch64/cortex_a520.h index 41769815..11ddea91 100644 --- a/include/lib/cpus/aarch64/cortex_a520.h +++ b/include/lib/cpus/aarch64/cortex_a520.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021-2023, Arm Limited. All rights reserved. + * Copyright (c) 2021-2024, Arm Limited. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -12,7 +12,15 @@ /******************************************************************************* * CPU Extended Control register specific definitions ******************************************************************************/ +#define CORTEX_A520_CPUACTLR_EL1 S3_0_C15_C1_0 + #define CORTEX_A520_CPUECTLR_EL1 S3_0_C15_C1_4 +#define CORTEX_A520_CPUECTLR_EL1_EXTLLC_BIT U(0) + +/******************************************************************************* + * CPU Auxiliary Control register 1 specific definitions. + ******************************************************************************/ +#define CORTEX_A520_CPUACTLR_EL1 S3_0_C15_C1_0 /******************************************************************************* * CPU Power Control register specific definitions @@ -20,4 +28,15 @@ #define CORTEX_A520_CPUPWRCTLR_EL1 S3_0_C15_C2_7 #define CORTEX_A520_CPUPWRCTLR_EL1_CORE_PWRDN_BIT U(1) +#ifndef __ASSEMBLER__ +#if ERRATA_A520_2938996 +long check_erratum_cortex_a520_2938996(long cpu_rev); +#else +static inline long check_erratum_cortex_a520_2938996(long cpu_rev) +{ + return 0; +} +#endif /* ERRATA_A520_2938996 */ +#endif /* __ASSEMBLER__ */ + #endif /* CORTEX_A520_H */ diff --git a/include/lib/cpus/aarch64/cortex_a710.h b/include/lib/cpus/aarch64/cortex_a710.h index 432e17ab..9df8d471 100644 --- a/include/lib/cpus/aarch64/cortex_a710.h +++ b/include/lib/cpus/aarch64/cortex_a710.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021-2022, Arm Limited. All rights reserved. + * Copyright (c) 2021-2023, Arm Limited. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -38,6 +38,11 @@ #define CORTEX_A710_CPUACTLR2_EL1_BIT_40 (ULL(1) << 40) #define CORTEX_A710_CPUACTLR2_EL1_BIT_36 (ULL(1) << 36) +/******************************************************************************* + * CPU Auxiliary Control register 3 specific definitions. + ******************************************************************************/ +#define CORTEX_A710_CPUACTLR3_EL1 S3_0_C15_C1_2 + /******************************************************************************* * CPU Auxiliary Control register 5 specific definitions. ******************************************************************************/ diff --git a/include/lib/cpus/aarch64/cortex_a715.h b/include/lib/cpus/aarch64/cortex_a715.h index 950d02f3..c7f50db3 100644 --- a/include/lib/cpus/aarch64/cortex_a715.h +++ b/include/lib/cpus/aarch64/cortex_a715.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021-2023, Arm Limited. All rights reserved. + * Copyright (c) 2021-2024, Arm Limited. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -12,11 +12,26 @@ /* Cortex-A715 loop count for CVE-2022-23960 mitigation */ #define CORTEX_A715_BHB_LOOP_COUNT U(38) +/******************************************************************************* + * CPU Auxiliary Control register 1 specific definitions. + ******************************************************************************/ +#define CORTEX_A715_CPUACTLR_EL1 S3_0_C15_C1_0 + +/******************************************************************************* + * CPU Auxiliary Control register 2 specific definitions. + ******************************************************************************/ +#define CORTEX_A715_CPUACTLR2_EL1 S3_0_C15_C1_1 + /******************************************************************************* * CPU Extended Control register specific definitions ******************************************************************************/ #define CORTEX_A715_CPUECTLR_EL1 S3_0_C15_C1_4 +#define CORTEX_A715_CPUPSELR_EL3 S3_6_C15_C8_0 +#define CORTEX_A715_CPUPCR_EL3 S3_6_C15_C8_1 +#define CORTEX_A715_CPUPOR_EL3 S3_6_C15_C8_2 +#define CORTEX_A715_CPUPMR_EL3 S3_6_C15_C8_3 + /******************************************************************************* * CPU Power Control register specific definitions ******************************************************************************/ diff --git a/include/lib/cpus/aarch64/cortex_a720.h b/include/lib/cpus/aarch64/cortex_a720.h index 47bbbc07..129c1ee3 100644 --- a/include/lib/cpus/aarch64/cortex_a720.h +++ b/include/lib/cpus/aarch64/cortex_a720.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021-2023, Arm Limited. All rights reserved. + * Copyright (c) 2021-2024, Arm Limited. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -12,6 +12,21 @@ /* Cortex A720 loop count for CVE-2022-23960 mitigation */ #define CORTEX_A720_BHB_LOOP_COUNT U(132) +/******************************************************************************* + * CPU Auxiliary Control register 1 specific definitions. + ******************************************************************************/ +#define CORTEX_A720_CPUACTLR_EL1 S3_0_C15_C1_0 + +/******************************************************************************* + * CPU Auxiliary Control register 2 specific definitions. + ******************************************************************************/ +#define CORTEX_A720_CPUACTLR2_EL1 S3_0_C15_C1_1 + +/******************************************************************************* + * CPU Auxiliary Control register 4 specific definitions. + ******************************************************************************/ +#define CORTEX_A720_CPUACTLR4_EL1 S3_0_C15_C1_3 + /******************************************************************************* * CPU Extended Control register specific definitions ******************************************************************************/ diff --git a/include/lib/cpus/aarch64/neoverse_hermes.h b/include/lib/cpus/aarch64/cortex_a720_ae.h similarity index 57% rename from include/lib/cpus/aarch64/neoverse_hermes.h rename to include/lib/cpus/aarch64/cortex_a720_ae.h index 22492c3d..c88b1f9c 100644 --- a/include/lib/cpus/aarch64/neoverse_hermes.h +++ b/include/lib/cpus/aarch64/cortex_a720_ae.h @@ -1,23 +1,23 @@ /* - * Copyright (c) 2023, Arm Limited. All rights reserved. + * Copyright (c) 2024, Arm Limited. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ -#ifndef NEOVERSE_HERMES_H -#define NEOVERSE_HERMES_H +#ifndef CORTEX_A720_AE_H +#define CORTEX_A720_AE_H -#define NEOVERSE_HERMES_MIDR U(0x410FD8E0) +#define CORTEX_A720_AE_MIDR U(0x410FD890) /******************************************************************************* * CPU Extended Control register specific definitions ******************************************************************************/ -#define NEOVERSE_HERMES_CPUECTLR_EL1 S3_0_C15_C1_4 +#define CORTEX_A720_AE_CPUECTLR_EL1 S3_0_C15_C1_4 /******************************************************************************* * CPU Power Control register specific definitions ******************************************************************************/ -#define NEOVERSE_HERMES_CPUPWRCTLR_EL1 S3_0_C15_C2_7 -#define NEOVERSE_HERMES_CPUPWRCTLR_EL1_CORE_PWRDN_BIT U(1) +#define CORTEX_A720_AE_CPUPWRCTLR_EL1 S3_0_C15_C2_7 +#define CORTEX_A720_AE_CPUPWRCTLR_EL1_CORE_PWRDN_BIT U(1) -#endif /* NEOVERSE_HERMES_H */ +#endif /* CORTEX_A720_AE_H */ diff --git a/include/lib/cpus/aarch64/cortex_blackhawk.h b/include/lib/cpus/aarch64/cortex_a725.h similarity index 55% rename from include/lib/cpus/aarch64/cortex_blackhawk.h rename to include/lib/cpus/aarch64/cortex_a725.h index bfb30395..cb1c099c 100644 --- a/include/lib/cpus/aarch64/cortex_blackhawk.h +++ b/include/lib/cpus/aarch64/cortex_a725.h @@ -1,23 +1,24 @@ /* - * Copyright (c) 2023, Arm Limited. All rights reserved. + * Copyright (c) 2023-2024, Arm Limited. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ -#ifndef CORTEX_BLACKHAWK_H -#define CORTEX_BLACKHAWK_H +#ifndef CORTEX_A725_H +#define CORTEX_A725_H -#define CORTEX_BLACKHAWK_MIDR U(0x410FD850) +#define CORTEX_A725_MIDR U(0x410FD870) /******************************************************************************* * CPU Extended Control register specific definitions ******************************************************************************/ -#define CORTEX_BLACKHAWK_CPUECTLR_EL1 S3_0_C15_C1_4 +#define CORTEX_A725_CPUECTLR_EL1 S3_0_C15_C1_4 +#define CORTEX_A725_CPUECTLR_EL1_EXTLLC_BIT U(0) /******************************************************************************* * CPU Power Control register specific definitions ******************************************************************************/ -#define CORTEX_BLACKHAWK_CPUPWRCTLR_EL1 S3_0_C15_C2_7 -#define CORTEX_BLACKHAWK_CPUPWRCTLR_EL1_CORE_PWRDN_BIT U(1) +#define CORTEX_A725_CPUPWRCTLR_EL1 S3_0_C15_C2_7 +#define CORTEX_A725_CPUPWRCTLR_EL1_CORE_PWRDN_BIT U(1) -#endif /* CORTEX_BLACKHAWK_H */ +#endif /* CORTEX_A725_H */ diff --git a/include/lib/cpus/aarch64/cortex_a75.h b/include/lib/cpus/aarch64/cortex_a75.h index ca79991e..7a97ed11 100644 --- a/include/lib/cpus/aarch64/cortex_a75.h +++ b/include/lib/cpus/aarch64/cortex_a75.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2019, Arm Limited and Contributors. All rights reserved. + * Copyright (c) 2017-2024, Arm Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -50,6 +50,11 @@ unsigned int cortex_a75_amu_read_cpuamcntenset_el0(void); unsigned int cortex_a75_amu_read_cpuamcntenclr_el0(void); void cortex_a75_amu_write_cpuamcntenset_el0(unsigned int mask); void cortex_a75_amu_write_cpuamcntenclr_el0(unsigned int mask); + +#if ERRATA_A75_764081 +long check_erratum_cortex_a75_764081(long cpu_rev); +#endif /* ERRATA_A75_764081 */ + #endif /* __ASSEMBLER__ */ #endif /* CORTEX_A75_H */ diff --git a/include/lib/cpus/aarch64/cortex_a78c.h b/include/lib/cpus/aarch64/cortex_a78c.h index 301be69a..d600ecab 100644 --- a/include/lib/cpus/aarch64/cortex_a78c.h +++ b/include/lib/cpus/aarch64/cortex_a78c.h @@ -47,4 +47,9 @@ #define CORTEX_A78C_IMP_CPUPOR_EL3 S3_6_C15_C8_2 #define CORTEX_A78C_IMP_CPUPMR_EL3 S3_6_C15_C8_3 +/******************************************************************************* + * CPU Auxiliary Control register 5 specific definitions. + ******************************************************************************/ +#define CORTEX_A78C_ACTLR5_EL1 S3_0_C15_C9_0 + #endif /* CORTEX_A78C_H */ diff --git a/include/lib/cpus/aarch64/cortex_arcadia.h b/include/lib/cpus/aarch64/cortex_arcadia.h new file mode 100644 index 00000000..8b74de29 --- /dev/null +++ b/include/lib/cpus/aarch64/cortex_arcadia.h @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2024, Arm Limited. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef CORTEX_ARCADIA_H +#define CORTEX_ARCADIA_H + +#define CORTEX_ARCADIA_MIDR U(0x410FD8F0) + +/******************************************************************************* + * CPU Extended Control register specific definitions + ******************************************************************************/ +#define CORTEX_ARCADIA_CPUECTLR_EL1 S3_0_C15_C1_4 +#define CORTEX_ARCADIA_CPUECTLR_EL1_EXTLLC_BIT U(0) + +/******************************************************************************* + * CPU Power Control register specific definitions + ******************************************************************************/ +#define CORTEX_ARCADIA_CPUPWRCTLR_EL1 S3_0_C15_C2_7 +#define CORTEX_ARCADIA_CPUPWRCTLR_EL1_CORE_PWRDN_BIT U(1) + +#endif /* CORTEX_ARCADIA_H */ diff --git a/include/lib/cpus/aarch64/cortex_x2.h b/include/lib/cpus/aarch64/cortex_x2.h index 863b8c8d..0f97b1e1 100644 --- a/include/lib/cpus/aarch64/cortex_x2.h +++ b/include/lib/cpus/aarch64/cortex_x2.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021-2022, Arm Limited. All rights reserved. + * Copyright (c) 2021-2023, Arm Limited. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -27,6 +27,11 @@ #define CORTEX_X2_CPUECTLR2_EL1_PF_MODE_WIDTH U(4) #define CORTEX_X2_CPUECTLR2_EL1_PF_MODE_CNSRV ULL(0x9) +/******************************************************************************* + * CPU Auxiliary Control register 3 specific definitions. + ******************************************************************************/ +#define CORTEX_X2_CPUACTLR3_EL1 S3_0_C15_C1_2 + /******************************************************************************* * CPU Power Control register specific definitions ******************************************************************************/ diff --git a/include/lib/cpus/aarch64/cortex_x3.h b/include/lib/cpus/aarch64/cortex_x3.h index 04548eae..c5f820cf 100644 --- a/include/lib/cpus/aarch64/cortex_x3.h +++ b/include/lib/cpus/aarch64/cortex_x3.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021-2023, Arm Limited. All rights reserved. + * Copyright (c) 2021-2024, Arm Limited. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -25,6 +25,11 @@ #define CORTEX_X3_CPUPWRCTLR_EL1_WFI_RET_CTRL_BITS_SHIFT U(4) #define CORTEX_X3_CPUPWRCTLR_EL1_WFE_RET_CTRL_BITS_SHIFT U(7) +/******************************************************************************* + * CPU Auxiliary Control register specific definitions. + ******************************************************************************/ +#define CORTEX_X3_CPUACTLR_EL1 S3_0_C15_C1_0 + /******************************************************************************* * CPU Auxiliary Control register 2 specific definitions. ******************************************************************************/ @@ -38,6 +43,11 @@ #define CORTEX_X3_CPUACTLR5_EL1_BIT_55 (ULL(1) << 55) #define CORTEX_X3_CPUACTLR5_EL1_BIT_56 (ULL(1) << 56) +/******************************************************************************* + * CPU Auxiliary Control register 6 specific definitions. + ******************************************************************************/ +#define CORTEX_X3_CPUACTLR6_EL1 S3_0_C15_C8_1 + /******************************************************************************* * CPU Extended Control register 2 specific definitions. ******************************************************************************/ @@ -47,4 +57,10 @@ #define CORTEX_X3_CPUECTLR2_EL1_PF_MODE_WIDTH U(4) #define CORTEX_X3_CPUECTLR2_EL1_PF_MODE_CNSRV ULL(0x9) +/******************************************************************************* + * CPU Auxiliary Control register 3 specific definitions. + ******************************************************************************/ +#define CORTEX_X3_CPUACTLR3_EL1 S3_0_C15_C1_2 +#define CORTEX_X3_CPUACTLR3_EL1_BIT_47 (ULL(1) << 47) + #endif /* CORTEX_X3_H */ diff --git a/include/lib/cpus/aarch64/cortex_x4.h b/include/lib/cpus/aarch64/cortex_x4.h index 17d07c8b..f701216b 100644 --- a/include/lib/cpus/aarch64/cortex_x4.h +++ b/include/lib/cpus/aarch64/cortex_x4.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022-2023, Arm Limited. All rights reserved. + * Copyright (c) 2022-2024, Arm Limited. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -23,4 +23,28 @@ #define CORTEX_X4_CPUPWRCTLR_EL1 S3_0_C15_C2_7 #define CORTEX_X4_CPUPWRCTLR_EL1_CORE_PWRDN_BIT U(1) +/******************************************************************************* + * CPU Auxiliary control register specific definitions + ******************************************************************************/ +#define CORTEX_X4_CPUACTLR_EL1 S3_0_C15_C1_0 +#define CORTEX_X4_CPUACTLR3_EL1 S3_0_C15_C1_2 +#define CORTEX_X4_CPUACTLR4_EL1 S3_0_C15_C1_3 + +/******************************************************************************* + * CPU Auxiliary control register 5 specific definitions + ******************************************************************************/ +#define CORTEX_X4_CPUACTLR5_EL1 S3_0_C15_C8_0 +#define CORTEX_X4_CPUACTLR5_EL1_BIT_14 (ULL(1) << 14) + +#ifndef __ASSEMBLER__ +#if ERRATA_X4_2726228 +long check_erratum_cortex_x4_2726228(long cpu_rev); +#else +static inline long check_erratum_cortex_x4_2726228(long cpu_rev) +{ + return 0; +} +#endif /* ERRATA_X4_2726228 */ +#endif /* __ASSEMBLER__ */ + #endif /* CORTEX_X4_H */ diff --git a/include/lib/cpus/aarch64/cortex_chaberton.h b/include/lib/cpus/aarch64/cortex_x925.h similarity index 55% rename from include/lib/cpus/aarch64/cortex_chaberton.h rename to include/lib/cpus/aarch64/cortex_x925.h index 8f10b687..b0d0ca4d 100644 --- a/include/lib/cpus/aarch64/cortex_chaberton.h +++ b/include/lib/cpus/aarch64/cortex_x925.h @@ -1,23 +1,24 @@ /* - * Copyright (c) 2023, Arm Limited. All rights reserved. + * Copyright (c) 2023-2024, Arm Limited. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ -#ifndef CORTEX_CHABERTON_H -#define CORTEX_CHABERTON_H +#ifndef CORTEX_X925_H +#define CORTEX_X925_H -#define CORTEX_CHABERTON_MIDR U(0x410FD870) +#define CORTEX_X925_MIDR U(0x410FD850) /******************************************************************************* * CPU Extended Control register specific definitions ******************************************************************************/ -#define CORTEX_CHABERTON_CPUECTLR_EL1 S3_0_C15_C1_4 +#define CORTEX_X925_CPUECTLR_EL1 S3_0_C15_C1_4 +#define CORTEX_X925_CPUECTLR_EL1_EXTLLC_BIT U(0) /******************************************************************************* * CPU Power Control register specific definitions ******************************************************************************/ -#define CORTEX_CHABERTON_CPUPWRCTLR_EL1 S3_0_C15_C2_7 -#define CORTEX_CHABERTON_CPUPWRCTLR_EL1_CORE_PWRDN_BIT U(1) +#define CORTEX_X925_CPUPWRCTLR_EL1 S3_0_C15_C2_7 +#define CORTEX_X925_CPUPWRCTLR_EL1_CORE_PWRDN_BIT U(1) -#endif /* CORTEX_CHABERTON_H */ +#endif /* CORTEX_X925_H */ diff --git a/include/lib/cpus/aarch64/cpu_macros.S b/include/lib/cpus/aarch64/cpu_macros.S index 6faef5db..5e929344 100644 --- a/include/lib/cpus/aarch64/cpu_macros.S +++ b/include/lib/cpus/aarch64/cpu_macros.S @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2023, Arm Limited and Contributors. All rights reserved. + * Copyright (c) 2014-2024, Arm Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -132,12 +132,6 @@ .popsection .endif - - /* - * Mandatory errata status printing function for CPUs of - * this class. - */ - .quad \_name\()_errata_report .quad \_name\()_cpu_str #ifdef IMAGE_BL31 @@ -171,49 +165,6 @@ \_extra1, \_extra2, \_extra3, 0, \_power_down_ops .endm -/* TODO can be deleted once all CPUs have been converted */ -#if REPORT_ERRATA - /* - * Print status of a CPU errata - * - * _chosen: - * Identifier indicating whether or not a CPU errata has been - * compiled in. - * _cpu: - * Name of the CPU - * _id: - * Errata identifier - * _rev_var: - * Register containing the combined value CPU revision and variant - * - typically the return value of cpu_get_rev_var - */ - .macro report_errata _chosen, _cpu, _id, _rev_var=x8 - /* Stash a string with errata ID */ - .pushsection .rodata - \_cpu\()_errata_\_id\()_str: - .asciz "\_id" - .popsection - - /* Check whether errata applies */ - mov x0, \_rev_var - /* Shall clobber: x0-x7 */ - bl check_errata_\_id - - .ifeq \_chosen - /* - * Errata workaround has not been compiled in. If the errata would have - * applied had it been compiled in, print its status as missing. - */ - cbz x0, 900f - mov x0, #ERRATA_MISSING - .endif -900: - adr x1, \_cpu\()_cpu_str - adr x2, \_cpu\()_errata_\_id\()_str - bl errata_print_msg - .endm -#endif - /* * This macro is used on some CPUs to detect if they are vulnerable * to CVE-2017-5715. @@ -456,6 +407,14 @@ msr \_reg, x0 .endm +.macro sysreg_bitfield_insert_from_gpr _reg:req, _gpr:req, _lsb:req, _width:req + /* Source value in register for BFI */ + mov x1, \_gpr + mrs x0, \_reg + bfi x0, x1, #\_lsb, #\_width + msr \_reg, x0 +.endm + /* * Apply erratum * @@ -474,7 +433,7 @@ * * _get_rev: * Optional parameter that determines whether to insert a call to the CPU revision fetching - * procedure. Stores the result of this in the temporary register x10. + * procedure. Stores the result of this in the temporary register x10 to allow for chaining * * clobbers: x0-x10 (PCS compliant) */ @@ -614,23 +573,4 @@ endfunc \_cpu\()_reset_func .endm -/* - * Maintain compatibility with the old scheme of each cpu has its own reporting. - * TODO remove entirely once all cpus have been converted. This includes the - * cpu_ops entry, as print_errata_status can call this directly for all cpus - */ -.macro errata_report_shim _cpu:req - #if REPORT_ERRATA - func \_cpu\()_errata_report - /* normal stack frame for pretty debugging */ - stp x29, x30, [sp, #-16]! - mov x29, sp - - bl generic_errata_report - - ldp x29, x30, [sp], #16 - ret - endfunc \_cpu\()_errata_report - #endif -.endm #endif /* CPU_MACROS_S */ diff --git a/include/lib/cpus/aarch64/dsu_def.h b/include/lib/cpus/aarch64/dsu_def.h index 577de619..51fbfd1d 100644 --- a/include/lib/cpus/aarch64/dsu_def.h +++ b/include/lib/cpus/aarch64/dsu_def.h @@ -30,6 +30,7 @@ * DSU Cluster Auxiliary Control registers definitions ********************************************************************/ #define CLUSTERACTLR_EL1 S3_0_C15_C3_3 +#define CLUSTERPWRCTLR_EL1 S3_0_C15_C3_5 #define CLUSTERACTLR_EL1_DISABLE_CLOCK_GATING (ULL(1) << 15) #define CLUSTERACTLR_EL1_DISABLE_SCLK_GATING (ULL(3) << 15) @@ -39,4 +40,7 @@ ********************************************************************/ #define DSU_ERRATA_936184_MASK (U(0x3) << 15) +#ifndef __ASSEMBLER__ +void dsu_pwr_dwn(void); +#endif #endif /* DSU_DEF_H */ diff --git a/include/lib/cpus/aarch64/neoverse_n3.h b/include/lib/cpus/aarch64/neoverse_n3.h new file mode 100644 index 00000000..91963305 --- /dev/null +++ b/include/lib/cpus/aarch64/neoverse_n3.h @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2023-2024, Arm Limited. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef NEOVERSE_N3_H +#define NEOVERSE_N3_H + +#define NEOVERSE_N3_MIDR U(0x410FD8E0) + +/******************************************************************************* + * CPU Extended Control register specific definitions + ******************************************************************************/ +#define NEOVERSE_N3_CPUECTLR_EL1 S3_0_C15_C1_4 +#define NEOVERSE_N3_CPUECTLR_EL1_EXTLLC_BIT (ULL(1) << 0) + +/******************************************************************************* + * CPU Power Control register specific definitions + ******************************************************************************/ +#define NEOVERSE_N3_CPUPWRCTLR_EL1 S3_0_C15_C2_7 +#define NEOVERSE_N3_CPUPWRCTLR_EL1_CORE_PWRDN_BIT U(1) + +#endif /* NEOVERSE_N3_H */ diff --git a/include/lib/cpus/aarch64/neoverse_poseidon.h b/include/lib/cpus/aarch64/neoverse_poseidon.h deleted file mode 100644 index 202ef5cb..00000000 --- a/include/lib/cpus/aarch64/neoverse_poseidon.h +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright (c) 2022, Arm Limited. All rights reserved. - * - * SPDX-License-Identifier: BSD-3-Clause - */ - -#ifndef NEOVERSE_POSEIDON_H -#define NEOVERSE_POSEIDON_H - - -#define NEOVERSE_POSEIDON_MIDR U(0x410FD830) - -/* Neoverse Poseidon loop count for CVE-2022-23960 mitigation */ -#define NEOVERSE_POSEIDON_BHB_LOOP_COUNT U(132) - -/******************************************************************************* - * CPU Extended Control register specific definitions. - ******************************************************************************/ -#define NEOVERSE_POSEIDON_CPUECTLR_EL1 S3_0_C15_C1_4 - -/******************************************************************************* - * CPU Power Control register specific definitions - ******************************************************************************/ -#define NEOVERSE_POSEIDON_CPUPWRCTLR_EL1 S3_0_C15_C2_7 -#define NEOVERSE_POSEIDON_CPUPWRCTLR_EL1_CORE_PWRDN_BIT U(1) - -#endif /* NEOVERSE_POSEIDON_H */ diff --git a/include/lib/cpus/aarch64/neoverse_v1.h b/include/lib/cpus/aarch64/neoverse_v1.h index d6189942..1e2d7eaf 100644 --- a/include/lib/cpus/aarch64/neoverse_v1.h +++ b/include/lib/cpus/aarch64/neoverse_v1.h @@ -47,5 +47,6 @@ #define NEOVERSE_V1_ACTLR5_EL1 S3_0_C15_C9_0 #define NEOVERSE_V1_ACTLR5_EL1_BIT_55 (ULL(1) << 55) #define NEOVERSE_V1_ACTLR5_EL1_BIT_56 (ULL(1) << 56) +#define NEOVERSE_V1_ACTLR5_EL1_BIT_61 (ULL(1) << 61) #endif /* NEOVERSE_V1_H */ diff --git a/include/lib/cpus/aarch64/neoverse_v2.h b/include/lib/cpus/aarch64/neoverse_v2.h index 68c15587..1171e952 100644 --- a/include/lib/cpus/aarch64/neoverse_v2.h +++ b/include/lib/cpus/aarch64/neoverse_v2.h @@ -16,12 +16,17 @@ * CPU Extended Control register specific definitions ******************************************************************************/ #define NEOVERSE_V2_CPUECTLR_EL1 S3_0_C15_C1_4 +#define NEOVERSE_V2_CPUECTLR_EL1_EXTLLC_BIT (ULL(1) << 0) /******************************************************************************* * CPU Power Control register specific definitions ******************************************************************************/ #define NEOVERSE_V2_CPUPWRCTLR_EL1 S3_0_C15_C2_7 #define NEOVERSE_V2_CPUPWRCTLR_EL1_CORE_PWRDN_BIT U(1) +#define NEOVERSE_V2_CPUPWRCTLR_EL1_WFI_RET_CTRL_SHIFT U(4) +#define NEOVERSE_V2_CPUPWRCTLR_EL1_WFI_RET_CTRL_WIDTH U(3) +#define NEOVERSE_V2_CPUPWRCTLR_EL1_WFE_RET_CTRL_SHIFT U(7) +#define NEOVERSE_V2_CPUPWRCTLR_EL1_WFE_RET_CTRL_WIDTH U(3) /******************************************************************************* * CPU Extended Control register 2 specific definitions. @@ -30,6 +35,9 @@ #define NEOVERSE_V2_CPUECTLR2_EL1_PF_MODE_CNSRV ULL(9) #define NEOVERSE_V2_CPUECTLR2_EL1_PF_MODE_LSB U(11) #define NEOVERSE_V2_CPUECTLR2_EL1_PF_MODE_WIDTH U(4) +#define NEOVERSE_V2_CPUECTLR2_EL1_TXREQ_STATIC_FULL ULL(0) +#define NEOVERSE_V2_CPUECTLR2_EL1_TXREQ_LSB U(0) +#define NEOVERSE_V2_CPUECTLR2_EL1_TXREQ_WIDTH U(3) /******************************************************************************* * CPU Auxiliary Control register 2 specific definitions. diff --git a/include/lib/cpus/aarch64/neoverse_v3.h b/include/lib/cpus/aarch64/neoverse_v3.h new file mode 100644 index 00000000..e5f75ba9 --- /dev/null +++ b/include/lib/cpus/aarch64/neoverse_v3.h @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2022-2024, Arm Limited. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef NEOVERSE_V3_H +#define NEOVERSE_V3_H + + +#define NEOVERSE_V3_VNAE_MIDR U(0x410FD830) +#define NEOVERSE_V3_MIDR U(0x410FD840) + +/* Neoverse V3 loop count for CVE-2022-23960 mitigation */ +#define NEOVERSE_V3_BHB_LOOP_COUNT U(132) + +/******************************************************************************* + * CPU Extended Control register specific definitions. + ******************************************************************************/ +#define NEOVERSE_V3_CPUECTLR_EL1 S3_0_C15_C1_4 + +/******************************************************************************* + * CPU Power Control register specific definitions + ******************************************************************************/ +#define NEOVERSE_V3_CPUPWRCTLR_EL1 S3_0_C15_C2_7 +#define NEOVERSE_V3_CPUPWRCTLR_EL1_CORE_PWRDN_BIT U(1) + +#endif /* NEOVERSE_V3_H */ diff --git a/include/lib/cpus/cpu_ops.h b/include/lib/cpus/cpu_ops.h index 8b36ff12..0084189b 100644 --- a/include/lib/cpus/cpu_ops.h +++ b/include/lib/cpus/cpu_ops.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023, Arm Limited and Contributors. All rights reserved. + * Copyright (c) 2023-2024, Arm Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -57,7 +57,6 @@ #define CPU_ERRATA_LIST_END_SIZE CPU_WORD_SIZE /* Fields required to print errata status */ #if REPORT_ERRATA -#define CPU_ERRATA_FUNC_SIZE CPU_WORD_SIZE #define CPU_CPU_STR_SIZE CPU_WORD_SIZE /* BL1 doesn't require mutual exclusion and printed flag. */ #if defined(IMAGE_BL31) || defined(IMAGE_BL32) @@ -68,7 +67,6 @@ #define CPU_ERRATA_PRINTED_SIZE 0 #endif /* defined(IMAGE_BL31) || defined(IMAGE_BL32) */ #else -#define CPU_ERRATA_FUNC_SIZE 0 #define CPU_CPU_STR_SIZE 0 #define CPU_ERRATA_LOCK_SIZE 0 #define CPU_ERRATA_PRINTED_SIZE 0 @@ -98,8 +96,7 @@ #endif /* __aarch64__ */ #define CPU_ERRATA_LIST_START CPU_PWR_DWN_OPS + CPU_PWR_DWN_OPS_SIZE #define CPU_ERRATA_LIST_END CPU_ERRATA_LIST_START + CPU_ERRATA_LIST_START_SIZE -#define CPU_ERRATA_FUNC CPU_ERRATA_LIST_END + CPU_ERRATA_LIST_END_SIZE -#define CPU_CPU_STR CPU_ERRATA_FUNC + CPU_ERRATA_FUNC_SIZE +#define CPU_CPU_STR CPU_ERRATA_LIST_END + CPU_ERRATA_LIST_END_SIZE #define CPU_ERRATA_LOCK CPU_CPU_STR + CPU_CPU_STR_SIZE #define CPU_ERRATA_PRINTED CPU_ERRATA_LOCK + CPU_ERRATA_LOCK_SIZE #if __aarch64__ @@ -130,7 +127,6 @@ struct cpu_ops { void *errata_list_start; void *errata_list_end; #if REPORT_ERRATA - void (*errata_func)(void); char *cpu_str; #if defined(IMAGE_BL31) || defined(IMAGE_BL32) spinlock_t *errata_lock; diff --git a/include/lib/cpus/errata.h b/include/lib/cpus/errata.h index 20808989..2c31515e 100644 --- a/include/lib/cpus/errata.h +++ b/include/lib/cpus/errata.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2023, Arm Limited and Contributors. All rights reserved. + * Copyright (c) 2017-2024, Arm Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -25,11 +25,28 @@ #define ERRATUM_MITIGATED ERRATUM_CHOSEN + ERRATUM_CHOSEN_SIZE #define ERRATUM_ENTRY_SIZE ERRATUM_MITIGATED + ERRATUM_MITIGATED_SIZE +/* Errata status */ +#define ERRATA_NOT_APPLIES 0 +#define ERRATA_APPLIES 1 +#define ERRATA_MISSING 2 + #ifndef __ASSEMBLER__ #include <lib/cassert.h> void print_errata_status(void); -void errata_print_msg(unsigned int status, const char *cpu, const char *id); + +#if ERRATA_A75_764081 +bool errata_a75_764081_applies(void); +#else +static inline bool errata_a75_764081_applies(void) +{ + return false; +} +#endif + +#if ERRATA_A520_2938996 || ERRATA_X4_2726228 +unsigned int check_if_affected_core(void); +#endif /* * NOTE that this structure will be different on AArch32 and AArch64. The @@ -74,11 +91,6 @@ CASSERT(sizeof(struct erratum_entry) == ERRATUM_ENTRY_SIZE, #endif /* __ASSEMBLER__ */ -/* Errata status */ -#define ERRATA_NOT_APPLIES 0 -#define ERRATA_APPLIES 1 -#define ERRATA_MISSING 2 - /* Macro to get CPU revision code for checking errata version compatibility. */ #define CPU_REV(r, p) ((r << 4) | p) diff --git a/include/lib/debugfs.h b/include/lib/debugfs.h index 8ed237ae..1fdccb6a 100644 --- a/include/lib/debugfs.h +++ b/include/lib/debugfs.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, Arm Limited. All rights reserved. + * Copyright (c) 2019-2024, Arm Limited. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -61,11 +61,23 @@ int debugfs_smc_setup(void); /* Debugfs version returned through SMC interface */ #define DEBUGFS_VERSION (0x000000001U) -/* Function ID for accessing the debugfs interface */ -#define DEBUGFS_FID_VALUE (0x30U) +/* Function ID for accessing the debugfs interface from + * Vendor-Specific EL3 Range. + */ +#define DEBUGFS_FID_VALUE (0x10U) + +#define is_debugfs_fid(_fid) \ + (GET_SMC_NUM(_fid) == DEBUGFS_FID_VALUE) + + +/* Function ID for accessing the debugfs interface from arm sip. + * This is now deprecated FID and will be removed after 2.12 release. + */ +#define DEBUGFS_FID_VALUE_DEPRECATED (0x30U) + +#define is_debugfs_fid_deprecated(_fid) \ + (GET_SMC_NUM(_fid) == DEBUGFS_FID_VALUE_DEPRECATED) -#define is_debugfs_fid(_fid) \ - (((_fid) & FUNCID_NUM_MASK) == DEBUGFS_FID_VALUE) /* Error code for debugfs SMC interface failures */ #define DEBUGFS_E_INVALID_PARAMS (-2) diff --git a/include/lib/dice/dice.h b/include/lib/dice/dice.h new file mode 100644 index 00000000..cf549422 --- /dev/null +++ b/include/lib/dice/dice.h @@ -0,0 +1,166 @@ +// Copyright 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); you may not +// use this file except in compliance with the License. You may obtain a copy of +// the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +// License for the specific language governing permissions and limitations under +// the License. + +#ifndef DICE_DICE_H_ +#define DICE_DICE_H_ + +#include <stddef.h> +#include <stdint.h> + +#ifdef __cplusplus +extern "C" { +#endif + +#define DICE_CDI_SIZE 32 +#define DICE_HASH_SIZE 64 +#define DICE_HIDDEN_SIZE 64 +#define DICE_INLINE_CONFIG_SIZE 64 +#define DICE_PRIVATE_KEY_SEED_SIZE 32 +#define DICE_ID_SIZE 20 + +typedef enum { + kDiceResultOk, + kDiceResultInvalidInput, + kDiceResultBufferTooSmall, + kDiceResultPlatformError, +} DiceResult; + +typedef enum { + kDiceModeNotInitialized, + kDiceModeNormal, + kDiceModeDebug, + kDiceModeMaintenance, +} DiceMode; + +typedef enum { + kDiceConfigTypeInline, + kDiceConfigTypeDescriptor, +} DiceConfigType; + +// Contains a full set of input values describing the target program or system. +// See the Open Profile for DICE specification for a detailed explanation of +// these inputs. +// +// Fields: +// code_hash: A hash or similar representation of the target code. +// code_descriptor: An optional descriptor to be included in the certificate. +// This descriptor is opaque to the DICE flow and is included verbatim +// in the certificate with no validation. May be null. +// code_descriptor_size: The size in bytes of |code_descriptor|. +// config_type: Indicates how to interpret the remaining config-related +// fields. If the type is 'inline', then the 64 byte configuration input +// value must be provided in |config_value| and |config_descriptor| is +// ignored. If the type is 'descriptor', then |config_descriptor| is +// hashed to get the configuration input value and |config_value| is +// ignored. +// config_value: A 64-byte configuration input value when |config_type| is +// kDiceConfigTypeInline. Otherwise, this field is ignored. +// config_descriptor: A descriptor to be hashed for the configuration input +// value when |config_type| is kDiceConfigTypeDescriptor. Otherwise, +// this field is ignored and may be null. +// config_descriptor_size: The size in bytes of |config_descriptor|. +// authority_hash: A hash or similar representation of the authority used to +// verify the target code. If the code is not verified or the authority +// is implicit, for example hard coded as part of the code currently +// executing, then this value should be set to all zero bytes. +// authority_descriptor: An optional descriptor to be included in the +// certificate. This descriptor is opaque to the DICE flow and is +// included verbatim in the certificate with no validation. May be null. +// authority_descriptor_size: The size in bytes of |authority_descriptor|. +// mode: The current operating mode. +// hidden: Additional input which will not appear in certificates. If this is +// not used it should be set to all zero bytes. +typedef struct DiceInputValues_ { + uint8_t code_hash[DICE_HASH_SIZE]; + const uint8_t* code_descriptor; + size_t code_descriptor_size; + DiceConfigType config_type; + uint8_t config_value[DICE_INLINE_CONFIG_SIZE]; + const uint8_t* config_descriptor; + size_t config_descriptor_size; + uint8_t authority_hash[DICE_HASH_SIZE]; + const uint8_t* authority_descriptor; + size_t authority_descriptor_size; + DiceMode mode; + uint8_t hidden[DICE_HIDDEN_SIZE]; +} DiceInputValues; + +// Derives a |cdi_private_key_seed| from a |cdi_attest| value. On success +// populates |cdi_private_key_seed| and returns kDiceResultOk. +DiceResult DiceDeriveCdiPrivateKeySeed( + void* context, const uint8_t cdi_attest[DICE_CDI_SIZE], + uint8_t cdi_private_key_seed[DICE_PRIVATE_KEY_SEED_SIZE]); + +// Derives an |id| from a |cdi_public_key| value. Because public keys can vary +// in length depending on the algorithm, the |cdi_public_key_size| in bytes must +// be provided. When interpreted as an integer, |id| is big-endian. On success +// populates |id| and returns kDiceResultOk. +DiceResult DiceDeriveCdiCertificateId(void* context, + const uint8_t* cdi_public_key, + size_t cdi_public_key_size, + uint8_t id[DICE_ID_SIZE]); + +// Executes the main DICE flow. +// +// Given a full set of input values and the current CDI values, computes the +// next CDI values and a matching certificate. See the Open Profile for DICE +// specification for a detailed explanation of this flow. +// In certain cases, the caller may not need to generate the CDI certificate. +// The caller should signal this by setting the certificate parameters to +// null/zero values appropriately. +// +// Parameters: +// context: Context provided by the caller that is opaque to this library +// but is passed through to the integration-provided operations in +// dice/ops.h. The value is, therefore, integration-specific and may be +// null. +// current_cdi_attest, current_cdi_seal: The current CDI values as produced +// by a previous DICE flow. If this is the first DICE flow in a system, +// the Unique Device Secret (UDS) should be used for both of these +// arguments. +// input_values: A set of input values describing the target program or +// system. +// next_cdi_certificate_buffer_size: The size in bytes of the buffer pointed +// to by the |next_cdi_certificate| argument. This should be set to zero +// if next CDI certificate should not be computed. +// next_cdi_certificate: On success, will be populated with the generated +// certificate, up to |next_cdi_certificate_buffer_size| in size. If the +// certificate cannot fit in the buffer, |next_cdi_certificate_size| is +// populated with the required size and kDiceResultBufferTooSmall is +// returned. This should be set to NULL if next CDI certificate should +// not be computed. +// next_cdi_certificate_actual_size: On success, will be populated with the +// size, in bytes, of the certificate data written to +// |next_cdi_certificate|. If kDiceResultBufferTooSmall is returned, will +// be populated with the required buffer size. This should be set to NULL +// if next CDI certificate should not be computed. +// next_cdi_attest: On success, will be populated with the next CDI value for +// attestation. +// next_cdi_seal: On success, will be populated with the next CDI value for +// sealing. +DiceResult DiceMainFlow(void* context, + const uint8_t current_cdi_attest[DICE_CDI_SIZE], + const uint8_t current_cdi_seal[DICE_CDI_SIZE], + const DiceInputValues* input_values, + size_t next_cdi_certificate_buffer_size, + uint8_t* next_cdi_certificate, + size_t* next_cdi_certificate_actual_size, + uint8_t next_cdi_attest[DICE_CDI_SIZE], + uint8_t next_cdi_seal[DICE_CDI_SIZE]); + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // DICE_DICE_H_ diff --git a/include/lib/el3_runtime/aarch64/context.h b/include/lib/el3_runtime/aarch64/context.h index 47d91de0..87f15413 100644 --- a/include/lib/el3_runtime/aarch64/context.h +++ b/include/lib/el3_runtime/aarch64/context.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013-2023, Arm Limited and Contributors. All rights reserved. + * Copyright (c) 2013-2024, Arm Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -7,7 +7,18 @@ #ifndef CONTEXT_H #define CONTEXT_H +#if (CTX_INCLUDE_EL2_REGS && IMAGE_BL31) +#include <lib/el3_runtime/context_el2.h> +#else +/** + * El1 context is required either when: + * IMAGE_BL1 || ((!CTX_INCLUDE_EL2_REGS) && IMAGE_BL31) + */ +#include <lib/el3_runtime/context_el1.h> +#endif /* (CTX_INCLUDE_EL2_REGS && IMAGE_BL31) */ + #include <lib/el3_runtime/cpu_data.h> +#include <lib/el3_runtime/simd_ctx.h> #include <lib/utils_def.h> /******************************************************************************* @@ -62,7 +73,7 @@ #define CTX_ELR_EL3 U(0x20) #define CTX_PMCR_EL0 U(0x28) #define CTX_IS_IN_EL3 U(0x30) -#define CTX_MPAM3_EL3 U(0x38) +#define CTX_MDCR_EL3 U(0x38) /* Constants required in supporting nested exception in EL3 */ #define CTX_SAVED_ELR_EL3 U(0x40) /* @@ -78,250 +89,64 @@ #define CTX_EL3STATE_END U(0x70) /* Align to the next 16 byte boundary */ #else #define CTX_EL3STATE_END U(0x50) /* Align to the next 16 byte boundary */ -#endif - -/******************************************************************************* - * Constants that allow assembler code to access members of and the - * 'el1_sys_regs' structure at their correct offsets. Note that some of the - * registers are only 32-bits wide but are stored as 64-bit values for - * convenience - ******************************************************************************/ -#define CTX_EL1_SYSREGS_OFFSET (CTX_EL3STATE_OFFSET + CTX_EL3STATE_END) -#define CTX_SPSR_EL1 U(0x0) -#define CTX_ELR_EL1 U(0x8) -#define CTX_SCTLR_EL1 U(0x10) -#define CTX_TCR_EL1 U(0x18) -#define CTX_CPACR_EL1 U(0x20) -#define CTX_CSSELR_EL1 U(0x28) -#define CTX_SP_EL1 U(0x30) -#define CTX_ESR_EL1 U(0x38) -#define CTX_TTBR0_EL1 U(0x40) -#define CTX_TTBR1_EL1 U(0x48) -#define CTX_MAIR_EL1 U(0x50) -#define CTX_AMAIR_EL1 U(0x58) -#define CTX_ACTLR_EL1 U(0x60) -#define CTX_TPIDR_EL1 U(0x68) -#define CTX_TPIDR_EL0 U(0x70) -#define CTX_TPIDRRO_EL0 U(0x78) -#define CTX_PAR_EL1 U(0x80) -#define CTX_FAR_EL1 U(0x88) -#define CTX_AFSR0_EL1 U(0x90) -#define CTX_AFSR1_EL1 U(0x98) -#define CTX_CONTEXTIDR_EL1 U(0xa0) -#define CTX_VBAR_EL1 U(0xa8) - -/* - * If the platform is AArch64-only, there is no need to save and restore these - * AArch32 registers. - */ -#if CTX_INCLUDE_AARCH32_REGS -#define CTX_SPSR_ABT U(0xb0) /* Align to the next 16 byte boundary */ -#define CTX_SPSR_UND U(0xb8) -#define CTX_SPSR_IRQ U(0xc0) -#define CTX_SPSR_FIQ U(0xc8) -#define CTX_DACR32_EL2 U(0xd0) -#define CTX_IFSR32_EL2 U(0xd8) -#define CTX_AARCH32_END U(0xe0) /* Align to the next 16 byte boundary */ -#else -#define CTX_AARCH32_END U(0xb0) /* Align to the next 16 byte boundary */ -#endif /* CTX_INCLUDE_AARCH32_REGS */ - -/* - * If the timer registers aren't saved and restored, we don't have to reserve - * space for them in the context - */ -#if NS_TIMER_SWITCH -#define CTX_CNTP_CTL_EL0 (CTX_AARCH32_END + U(0x0)) -#define CTX_CNTP_CVAL_EL0 (CTX_AARCH32_END + U(0x8)) -#define CTX_CNTV_CTL_EL0 (CTX_AARCH32_END + U(0x10)) -#define CTX_CNTV_CVAL_EL0 (CTX_AARCH32_END + U(0x18)) -#define CTX_CNTKCTL_EL1 (CTX_AARCH32_END + U(0x20)) -#define CTX_TIMER_SYSREGS_END (CTX_AARCH32_END + U(0x30)) /* Align to the next 16 byte boundary */ -#else -#define CTX_TIMER_SYSREGS_END CTX_AARCH32_END -#endif /* NS_TIMER_SWITCH */ - -#if CTX_INCLUDE_MTE_REGS -#define CTX_TFSRE0_EL1 (CTX_TIMER_SYSREGS_END + U(0x0)) -#define CTX_TFSR_EL1 (CTX_TIMER_SYSREGS_END + U(0x8)) -#define CTX_RGSR_EL1 (CTX_TIMER_SYSREGS_END + U(0x10)) -#define CTX_GCR_EL1 (CTX_TIMER_SYSREGS_END + U(0x18)) - -/* Align to the next 16 byte boundary */ -#define CTX_MTE_REGS_END (CTX_TIMER_SYSREGS_END + U(0x20)) -#else -#define CTX_MTE_REGS_END CTX_TIMER_SYSREGS_END -#endif /* CTX_INCLUDE_MTE_REGS */ - -/* - * End of system registers. - */ -#define CTX_EL1_SYSREGS_END CTX_MTE_REGS_END +#endif /* FFH_SUPPORT */ -/* - * EL2 register set - */ - -#if CTX_INCLUDE_EL2_REGS -/* For later discussion - * ICH_AP0R<n>_EL2 - * ICH_AP1R<n>_EL2 - * AMEVCNTVOFF0<n>_EL2 - * AMEVCNTVOFF1<n>_EL2 - * ICH_LR<n>_EL2 - */ -#define CTX_EL2_SYSREGS_OFFSET (CTX_EL1_SYSREGS_OFFSET + CTX_EL1_SYSREGS_END) - -#define CTX_ACTLR_EL2 U(0x0) -#define CTX_AFSR0_EL2 U(0x8) -#define CTX_AFSR1_EL2 U(0x10) -#define CTX_AMAIR_EL2 U(0x18) -#define CTX_CNTHCTL_EL2 U(0x20) -#define CTX_CNTVOFF_EL2 U(0x28) -#define CTX_CPTR_EL2 U(0x30) -#define CTX_DBGVCR32_EL2 U(0x38) -#define CTX_ELR_EL2 U(0x40) -#define CTX_ESR_EL2 U(0x48) -#define CTX_FAR_EL2 U(0x50) -#define CTX_HACR_EL2 U(0x58) -#define CTX_HCR_EL2 U(0x60) -#define CTX_HPFAR_EL2 U(0x68) -#define CTX_HSTR_EL2 U(0x70) -#define CTX_ICC_SRE_EL2 U(0x78) -#define CTX_ICH_HCR_EL2 U(0x80) -#define CTX_ICH_VMCR_EL2 U(0x88) -#define CTX_MAIR_EL2 U(0x90) -#define CTX_MDCR_EL2 U(0x98) -#define CTX_PMSCR_EL2 U(0xa0) -#define CTX_SCTLR_EL2 U(0xa8) -#define CTX_SPSR_EL2 U(0xb0) -#define CTX_SP_EL2 U(0xb8) -#define CTX_TCR_EL2 U(0xc0) -#define CTX_TPIDR_EL2 U(0xc8) -#define CTX_TTBR0_EL2 U(0xd0) -#define CTX_VBAR_EL2 U(0xd8) -#define CTX_VMPIDR_EL2 U(0xe0) -#define CTX_VPIDR_EL2 U(0xe8) -#define CTX_VTCR_EL2 U(0xf0) -#define CTX_VTTBR_EL2 U(0xf8) - -// Only if MTE registers in use -#define CTX_TFSR_EL2 U(0x100) - -#define CTX_MPAM2_EL2 U(0x108) -#define CTX_MPAMHCR_EL2 U(0x110) -#define CTX_MPAMVPM0_EL2 U(0x118) -#define CTX_MPAMVPM1_EL2 U(0x120) -#define CTX_MPAMVPM2_EL2 U(0x128) -#define CTX_MPAMVPM3_EL2 U(0x130) -#define CTX_MPAMVPM4_EL2 U(0x138) -#define CTX_MPAMVPM5_EL2 U(0x140) -#define CTX_MPAMVPM6_EL2 U(0x148) -#define CTX_MPAMVPM7_EL2 U(0x150) -#define CTX_MPAMVPMV_EL2 U(0x158) - -// Starting with Armv8.6 -#define CTX_HDFGRTR_EL2 U(0x160) -#define CTX_HAFGRTR_EL2 U(0x168) -#define CTX_HDFGWTR_EL2 U(0x170) -#define CTX_HFGITR_EL2 U(0x178) -#define CTX_HFGRTR_EL2 U(0x180) -#define CTX_HFGWTR_EL2 U(0x188) -#define CTX_CNTPOFF_EL2 U(0x190) - -// Starting with Armv8.4 -#define CTX_CONTEXTIDR_EL2 U(0x198) -#define CTX_TTBR1_EL2 U(0x1a0) -#define CTX_VDISR_EL2 U(0x1a8) -#define CTX_VSESR_EL2 U(0x1b0) -#define CTX_VNCR_EL2 U(0x1b8) -#define CTX_TRFCR_EL2 U(0x1c0) - -// Starting with Armv8.5 -#define CTX_SCXTNUM_EL2 U(0x1c8) - -// Register for FEAT_HCX -#define CTX_HCRX_EL2 U(0x1d0) - -// Starting with Armv8.9 -#define CTX_TCR2_EL2 U(0x1d8) -#define CTX_POR_EL2 U(0x1e0) -#define CTX_PIRE0_EL2 U(0x1e8) -#define CTX_PIR_EL2 U(0x1f0) -#define CTX_S2PIR_EL2 U(0x1f8) -#define CTX_GCSCR_EL2 U(0x200) -#define CTX_GCSPR_EL2 U(0x208) - -/* Align to the next 16 byte boundary */ -#define CTX_EL2_SYSREGS_END U(0x210) - -#endif /* CTX_INCLUDE_EL2_REGS */ - -/******************************************************************************* - * Constants that allow assembler code to access members of and the 'fp_regs' - * structure at their correct offsets. - ******************************************************************************/ -#if CTX_INCLUDE_EL2_REGS -# define CTX_FPREGS_OFFSET (CTX_EL2_SYSREGS_OFFSET + CTX_EL2_SYSREGS_END) -#else -# define CTX_FPREGS_OFFSET (CTX_EL1_SYSREGS_OFFSET + CTX_EL1_SYSREGS_END) -#endif -#if CTX_INCLUDE_FPREGS -#define CTX_FP_Q0 U(0x0) -#define CTX_FP_Q1 U(0x10) -#define CTX_FP_Q2 U(0x20) -#define CTX_FP_Q3 U(0x30) -#define CTX_FP_Q4 U(0x40) -#define CTX_FP_Q5 U(0x50) -#define CTX_FP_Q6 U(0x60) -#define CTX_FP_Q7 U(0x70) -#define CTX_FP_Q8 U(0x80) -#define CTX_FP_Q9 U(0x90) -#define CTX_FP_Q10 U(0xa0) -#define CTX_FP_Q11 U(0xb0) -#define CTX_FP_Q12 U(0xc0) -#define CTX_FP_Q13 U(0xd0) -#define CTX_FP_Q14 U(0xe0) -#define CTX_FP_Q15 U(0xf0) -#define CTX_FP_Q16 U(0x100) -#define CTX_FP_Q17 U(0x110) -#define CTX_FP_Q18 U(0x120) -#define CTX_FP_Q19 U(0x130) -#define CTX_FP_Q20 U(0x140) -#define CTX_FP_Q21 U(0x150) -#define CTX_FP_Q22 U(0x160) -#define CTX_FP_Q23 U(0x170) -#define CTX_FP_Q24 U(0x180) -#define CTX_FP_Q25 U(0x190) -#define CTX_FP_Q26 U(0x1a0) -#define CTX_FP_Q27 U(0x1b0) -#define CTX_FP_Q28 U(0x1c0) -#define CTX_FP_Q29 U(0x1d0) -#define CTX_FP_Q30 U(0x1e0) -#define CTX_FP_Q31 U(0x1f0) -#define CTX_FP_FPSR U(0x200) -#define CTX_FP_FPCR U(0x208) -#if CTX_INCLUDE_AARCH32_REGS -#define CTX_FP_FPEXC32_EL2 U(0x210) -#define CTX_FPREGS_END U(0x220) /* Align to the next 16 byte boundary */ -#else -#define CTX_FPREGS_END U(0x210) /* Align to the next 16 byte boundary */ -#endif -#else -#define CTX_FPREGS_END U(0) -#endif /******************************************************************************* * Registers related to CVE-2018-3639 ******************************************************************************/ -#define CTX_CVE_2018_3639_OFFSET (CTX_FPREGS_OFFSET + CTX_FPREGS_END) +#define CTX_CVE_2018_3639_OFFSET (CTX_EL3STATE_OFFSET + CTX_EL3STATE_END) #define CTX_CVE_2018_3639_DISABLE U(0) #define CTX_CVE_2018_3639_END U(0x10) /* Align to the next 16 byte boundary */ +/******************************************************************************* + * Registers related to ERRATA_SPECULATIVE_AT + * + * This is essential as with EL1 and EL2 context registers being decoupled, + * both will not be present for a given build configuration. + * As ERRATA_SPECULATIVE_AT errata requires SCTLR_EL1 and TCR_EL1 registers + * independent of the above logic, we need explicit context entries to be + * reserved for these registers. + * + * NOTE: Based on this we end up with following different configurations depending + * on the presence of errata and inclusion of EL1 or EL2 context. + * + * ============================================================================ + * | ERRATA_SPECULATIVE_AT | EL1 context| Memory allocation(Sctlr_el1,Tcr_el1)| + * ============================================================================ + * | 0 | 0 | None | + * | 0 | 1 | EL1 C-Context structure | + * | 1 | 0 | Errata Context Offset Entries | + * | 1 | 1 | Errata Context Offset Entries | + * ============================================================================ + * + * In the above table, when ERRATA_SPECULATIVE_AT=1, EL1_Context=0, it implies + * there is only EL2 context and memory for SCTLR_EL1 and TCR_EL1 registers is + * reserved explicitly under ERRATA_SPECULATIVE_AT build flag here. + * + * In situations when EL1_Context=1 and ERRATA_SPECULATIVE_AT=1, since SCTLR_EL1 + * and TCR_EL1 registers will be modified under errata and it happens at the + * early in the codeflow prior to el1 context (save and restore operations), + * context memory still will be reserved under the errata logic here explicitly. + * These registers will not be part of EL1 context save & restore routines. + * + * Only when ERRATA_SPECULATIVE_AT=0, EL1_Context=1, for this combination, + * SCTLR_EL1 and TCR_EL1 will be part of EL1 context structure (context_el1.h) + * ----------------------------------------------------------------------------- + ******************************************************************************/ +#define CTX_ERRATA_SPEC_AT_OFFSET (CTX_CVE_2018_3639_OFFSET + CTX_CVE_2018_3639_END) +#if ERRATA_SPECULATIVE_AT +#define CTX_ERRATA_SPEC_AT_SCTLR_EL1 U(0x0) +#define CTX_ERRATA_SPEC_AT_TCR_EL1 U(0x8) +#define CTX_ERRATA_SPEC_AT_END U(0x10) /* Align to the next 16 byte boundary */ +#else +#define CTX_ERRATA_SPEC_AT_END U(0x0) +#endif /* ERRATA_SPECULATIVE_AT */ + /******************************************************************************* * Registers related to ARMv8.3-PAuth. ******************************************************************************/ -#define CTX_PAUTH_REGS_OFFSET (CTX_CVE_2018_3639_OFFSET + CTX_CVE_2018_3639_END) +#define CTX_PAUTH_REGS_OFFSET (CTX_ERRATA_SPEC_AT_OFFSET + CTX_ERRATA_SPEC_AT_END) #if CTX_INCLUDE_PAUTH_REGS #define CTX_PACIAKEY_LO U(0x0) #define CTX_PACIAKEY_HI U(0x8) @@ -341,9 +166,10 @@ /******************************************************************************* * Registers initialised in a per-world context. ******************************************************************************/ -#define CTX_CPTR_EL3 U(0x0) -#define CTX_ZCR_EL3 U(0x8) -#define CTX_GLOBAL_EL3STATE_END U(0x10) +#define CTX_CPTR_EL3 U(0x0) +#define CTX_ZCR_EL3 U(0x8) +#define CTX_MPAM3_EL3 U(0x10) +#define CTX_PERWORLD_EL3STATE_END U(0x18) #ifndef __ASSEMBLER__ @@ -363,15 +189,13 @@ /* Constants to determine the size of individual context structures */ #define CTX_GPREG_ALL (CTX_GPREGS_END >> DWORD_SHIFT) -#define CTX_EL1_SYSREGS_ALL (CTX_EL1_SYSREGS_END >> DWORD_SHIFT) -#if CTX_INCLUDE_EL2_REGS -# define CTX_EL2_SYSREGS_ALL (CTX_EL2_SYSREGS_END >> DWORD_SHIFT) -#endif -#if CTX_INCLUDE_FPREGS -# define CTX_FPREG_ALL (CTX_FPREGS_END >> DWORD_SHIFT) -#endif + #define CTX_EL3STATE_ALL (CTX_EL3STATE_END >> DWORD_SHIFT) #define CTX_CVE_2018_3639_ALL (CTX_CVE_2018_3639_END >> DWORD_SHIFT) + +#if ERRATA_SPECULATIVE_AT +#define CTX_ERRATA_SPEC_AT_ALL (CTX_ERRATA_SPEC_AT_END >> DWORD_SHIFT) +#endif #if CTX_INCLUDE_PAUTH_REGS # define CTX_PAUTH_REGS_ALL (CTX_PAUTH_REGS_END >> DWORD_SHIFT) #endif @@ -385,30 +209,6 @@ */ DEFINE_REG_STRUCT(gp_regs, CTX_GPREG_ALL); -/* - * AArch64 EL1 system register context structure for preserving the - * architectural state during world switches. - */ -DEFINE_REG_STRUCT(el1_sysregs, CTX_EL1_SYSREGS_ALL); - - -/* - * AArch64 EL2 system register context structure for preserving the - * architectural state during world switches. - */ -#if CTX_INCLUDE_EL2_REGS -DEFINE_REG_STRUCT(el2_sysregs, CTX_EL2_SYSREGS_ALL); -#endif - -/* - * AArch64 floating point register context structure for preserving - * the floating point state during switches from one security state to - * another. - */ -#if CTX_INCLUDE_FPREGS -DEFINE_REG_STRUCT(fp_regs, CTX_FPREG_ALL); -#endif - /* * Miscellaneous registers used by EL3 firmware to maintain its state * across exception entries and exits @@ -418,6 +218,11 @@ DEFINE_REG_STRUCT(el3_state, CTX_EL3STATE_ALL); /* Function pointer used by CVE-2018-3639 dynamic mitigation */ DEFINE_REG_STRUCT(cve_2018_3639, CTX_CVE_2018_3639_ALL); +/* Registers associated to Errata_Speculative */ +#if ERRATA_SPECULATIVE_AT +DEFINE_REG_STRUCT(errata_speculative_at, CTX_ERRATA_SPEC_AT_ALL); +#endif + /* Registers associated to ARMv8.3-PAuth */ #if CTX_INCLUDE_PAUTH_REGS DEFINE_REG_STRUCT(pauth, CTX_PAUTH_REGS_ALL); @@ -442,17 +247,29 @@ DEFINE_REG_STRUCT(pauth, CTX_PAUTH_REGS_ALL); typedef struct cpu_context { gp_regs_t gpregs_ctx; el3_state_t el3state_ctx; - el1_sysregs_t el1_sysregs_ctx; -#if CTX_INCLUDE_EL2_REGS - el2_sysregs_t el2_sysregs_ctx; -#endif -#if CTX_INCLUDE_FPREGS - fp_regs_t fpregs_ctx; -#endif + cve_2018_3639_t cve_2018_3639_ctx; + +#if ERRATA_SPECULATIVE_AT + errata_speculative_at_t errata_speculative_at_ctx; +#endif + #if CTX_INCLUDE_PAUTH_REGS pauth_t pauth_ctx; #endif + +#if (CTX_INCLUDE_EL2_REGS && IMAGE_BL31) + el2_sysregs_t el2_sysregs_ctx; +#else + /* El1 context should be included only either for IMAGE_BL1, + * or for IMAGE_BL31 when CTX_INCLUDE_EL2_REGS=0: + * When SPMD_SPM_AT_SEL2=1, SPMC at S-EL2 takes care of saving + * and restoring EL1 registers. In this case, BL31 at EL3 can + * exclude save and restore of EL1 context registers. + */ + el1_sysregs_t el1_sysregs_ctx; +#endif + } cpu_context_t; /* @@ -462,21 +279,27 @@ typedef struct cpu_context { typedef struct per_world_context { uint64_t ctx_cptr_el3; uint64_t ctx_zcr_el3; + uint64_t ctx_mpam3_el3; } per_world_context_t; extern per_world_context_t per_world_context[CPU_DATA_CONTEXT_NUM]; /* Macros to access members of the 'cpu_context_t' structure */ #define get_el3state_ctx(h) (&((cpu_context_t *) h)->el3state_ctx) -#if CTX_INCLUDE_FPREGS -# define get_fpregs_ctx(h) (&((cpu_context_t *) h)->fpregs_ctx) -#endif + +#if (CTX_INCLUDE_EL2_REGS && IMAGE_BL31) +#define get_el2_sysregs_ctx(h) (&((cpu_context_t *) h)->el2_sysregs_ctx) +#else #define get_el1_sysregs_ctx(h) (&((cpu_context_t *) h)->el1_sysregs_ctx) -#if CTX_INCLUDE_EL2_REGS -# define get_el2_sysregs_ctx(h) (&((cpu_context_t *) h)->el2_sysregs_ctx) #endif + #define get_gpregs_ctx(h) (&((cpu_context_t *) h)->gpregs_ctx) #define get_cve_2018_3639_ctx(h) (&((cpu_context_t *) h)->cve_2018_3639_ctx) + +#if ERRATA_SPECULATIVE_AT +#define get_errata_speculative_at_ctx(h) (&((cpu_context_t *) h)->errata_speculative_at_ctx) +#endif + #if CTX_INCLUDE_PAUTH_REGS # define get_pauth_ctx(h) (&((cpu_context_t *) h)->pauth_ctx) #endif @@ -488,24 +311,23 @@ extern per_world_context_t per_world_context[CPU_DATA_CONTEXT_NUM]; */ CASSERT(CTX_GPREGS_OFFSET == __builtin_offsetof(cpu_context_t, gpregs_ctx), assert_core_context_gp_offset_mismatch); -CASSERT(CTX_EL1_SYSREGS_OFFSET == __builtin_offsetof(cpu_context_t, el1_sysregs_ctx), - assert_core_context_el1_sys_offset_mismatch); -#if CTX_INCLUDE_EL2_REGS -CASSERT(CTX_EL2_SYSREGS_OFFSET == __builtin_offsetof(cpu_context_t, el2_sysregs_ctx), - assert_core_context_el2_sys_offset_mismatch); -#endif -#if CTX_INCLUDE_FPREGS -CASSERT(CTX_FPREGS_OFFSET == __builtin_offsetof(cpu_context_t, fpregs_ctx), - assert_core_context_fp_offset_mismatch); -#endif + CASSERT(CTX_EL3STATE_OFFSET == __builtin_offsetof(cpu_context_t, el3state_ctx), assert_core_context_el3state_offset_mismatch); + + CASSERT(CTX_CVE_2018_3639_OFFSET == __builtin_offsetof(cpu_context_t, cve_2018_3639_ctx), assert_core_context_cve_2018_3639_offset_mismatch); + +#if ERRATA_SPECULATIVE_AT +CASSERT(CTX_ERRATA_SPEC_AT_OFFSET == __builtin_offsetof(cpu_context_t, errata_speculative_at_ctx), + assert_core_context_errata_speculative_at_offset_mismatch); +#endif + #if CTX_INCLUDE_PAUTH_REGS CASSERT(CTX_PAUTH_REGS_OFFSET == __builtin_offsetof(cpu_context_t, pauth_ctx), assert_core_context_pauth_offset_mismatch); -#endif +#endif /* CTX_INCLUDE_PAUTH_REGS */ /* * Helper macro to set the general purpose registers that correspond to @@ -546,14 +368,74 @@ CASSERT(CTX_PAUTH_REGS_OFFSET == __builtin_offsetof(cpu_context_t, pauth_ctx), /******************************************************************************* * Function prototypes ******************************************************************************/ -void el1_sysregs_context_save(el1_sysregs_t *regs); -void el1_sysregs_context_restore(el1_sysregs_t *regs); - #if CTX_INCLUDE_FPREGS -void fpregs_context_save(fp_regs_t *regs); -void fpregs_context_restore(fp_regs_t *regs); +void fpregs_context_save(simd_regs_t *regs); +void fpregs_context_restore(simd_regs_t *regs); #endif +/******************************************************************************* + * The next four inline functions are required for IMAGE_BL1, as well as for + * IMAGE_BL31 for the below combinations. + * ============================================================================ + * | ERRATA_SPECULATIVE_AT| CTX_INCLUDE_EL2_REGS | Combination | + * ============================================================================ + * | 0 | 0 | Valid (EL1 ctx) | + * |______________________|______________________|____________________________| + * | | | Invalid (No Errata/EL1 Ctx)| + * | 0 | 1 | Hence commented out. | + * |______________________|______________________|____________________________| + * | | | | + * | 1 | 0 | Valid (Errata ctx) | + * |______________________|______________________|____________________________| + * | | | | + * | 1 | 1 | Valid (Errata ctx) | + * |______________________|______________________|____________________________| + * ============================================================================ + ******************************************************************************/ +#if (IMAGE_BL1 || ((ERRATA_SPECULATIVE_AT) || (!CTX_INCLUDE_EL2_REGS))) + +static inline void write_ctx_sctlr_el1_reg_errata(cpu_context_t *ctx, u_register_t val) +{ +#if (ERRATA_SPECULATIVE_AT) + write_ctx_reg(get_errata_speculative_at_ctx(ctx), + CTX_ERRATA_SPEC_AT_SCTLR_EL1, val); +#else + write_el1_ctx_common(get_el1_sysregs_ctx(ctx), sctlr_el1, val); +#endif /* ERRATA_SPECULATIVE_AT */ +} + +static inline void write_ctx_tcr_el1_reg_errata(cpu_context_t *ctx, u_register_t val) +{ +#if (ERRATA_SPECULATIVE_AT) + write_ctx_reg(get_errata_speculative_at_ctx(ctx), + CTX_ERRATA_SPEC_AT_TCR_EL1, val); +#else + write_el1_ctx_common(get_el1_sysregs_ctx(ctx), tcr_el1, val); +#endif /* ERRATA_SPECULATIVE_AT */ +} + +static inline u_register_t read_ctx_sctlr_el1_reg_errata(cpu_context_t *ctx) +{ +#if (ERRATA_SPECULATIVE_AT) + return read_ctx_reg(get_errata_speculative_at_ctx(ctx), + CTX_ERRATA_SPEC_AT_SCTLR_EL1); +#else + return read_el1_ctx_common(get_el1_sysregs_ctx(ctx), sctlr_el1); +#endif /* ERRATA_SPECULATIVE_AT */ +} + +static inline u_register_t read_ctx_tcr_el1_reg_errata(cpu_context_t *ctx) +{ +#if (ERRATA_SPECULATIVE_AT) + return read_ctx_reg(get_errata_speculative_at_ctx(ctx), + CTX_ERRATA_SPEC_AT_TCR_EL1); +#else + return read_el1_ctx_common(get_el1_sysregs_ctx(ctx), tcr_el1); +#endif /* ERRATA_SPECULATIVE_AT */ +} + +#endif /* (IMAGE_BL1 || ((ERRATA_SPECULATIVE_AT) || (!CTX_INCLUDE_EL2_REGS))) */ + #endif /* __ASSEMBLER__ */ #endif /* CONTEXT_H */ diff --git a/include/lib/el3_runtime/context_debug.h b/include/lib/el3_runtime/context_debug.h new file mode 100644 index 00000000..51e77482 --- /dev/null +++ b/include/lib/el3_runtime/context_debug.h @@ -0,0 +1,19 @@ +/* + * Copyright (c) 2023, Arm Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ +#ifndef CONTEXT_DEBUG_H +#define CONTEXT_DEBUG_H + +#if PLATFORM_REPORT_CTX_MEM_USE && defined(__aarch64__) +/******************************************************************************** + * Reports the allocated memory for every security state and then reports the + * total system-wide allocated memory. + *******************************************************************************/ +void report_ctx_memory_usage(void); +#else +static inline void report_ctx_memory_usage(void) {} +#endif /* PLATFORM_REPORT_CTX_MEM_USE */ + +#endif /* CONTEXT_DEBUG_H */ diff --git a/include/lib/el3_runtime/context_el1.h b/include/lib/el3_runtime/context_el1.h new file mode 100644 index 00000000..7bc02356 --- /dev/null +++ b/include/lib/el3_runtime/context_el1.h @@ -0,0 +1,325 @@ +/* + * Copyright (c) 2024, Arm Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef CONTEXT_EL1_H +#define CONTEXT_EL1_H + +#include <lib/extensions/sysreg128.h> + +#ifndef __ASSEMBLER__ + +/******************************************************************************* + * EL1 Registers: + * AArch64 EL1 system register context structure for preserving the + * architectural state during world switches. + ******************************************************************************/ + +typedef struct el1_common_regs { + uint64_t spsr_el1; + uint64_t elr_el1; + +#if (!ERRATA_SPECULATIVE_AT) + uint64_t sctlr_el1; + uint64_t tcr_el1; +#endif /* ERRATA_SPECULATIVE_AT=0 */ + + uint64_t cpacr_el1; + uint64_t csselr_el1; + uint64_t sp_el1; + uint64_t esr_el1; + uint64_t mair_el1; + uint64_t amair_el1; + uint64_t actlr_el1; + uint64_t tpidr_el1; + uint64_t tpidr_el0; + uint64_t tpidrro_el0; + uint64_t far_el1; + uint64_t afsr0_el1; + uint64_t afsr1_el1; + uint64_t contextidr_el1; + uint64_t vbar_el1; + uint64_t mdccint_el1; + uint64_t mdscr_el1; + sysreg_t par_el1; + sysreg_t ttbr0_el1; + sysreg_t ttbr1_el1; +} el1_common_regs_t; + +typedef struct el1_aarch32_regs { + uint64_t spsr_abt; + uint64_t spsr_und; + uint64_t spsr_irq; + uint64_t spsr_fiq; + uint64_t dacr32_el2; + uint64_t ifsr32_el2; +} el1_aarch32_regs_t; + +typedef struct el1_arch_timer_regs { + uint64_t cntp_ctl_el0; + uint64_t cntp_cval_el0; + uint64_t cntv_ctl_el0; + uint64_t cntv_cval_el0; + uint64_t cntkctl_el1; +} el1_arch_timer_regs_t; + +typedef struct el1_mte2_regs { + uint64_t tfsre0_el1; + uint64_t tfsr_el1; + uint64_t rgsr_el1; + uint64_t gcr_el1; +} el1_mte2_regs_t; + +typedef struct el1_ras_regs { + uint64_t disr_el1; +} el1_ras_regs_t; + +typedef struct el1_s1pie_regs { + uint64_t pire0_el1; + uint64_t pir_el1; +} el1_s1pie_regs_t; + +typedef struct el1_s1poe_regs { + uint64_t por_el1; +} el1_s1poe_regs_t; + +typedef struct el1_s2poe_regs { + uint64_t s2por_el1; +} el1_s2poe_regs_t; + +typedef struct el1_tcr2_regs { + uint64_t tcr2_el1; +} el1_tcr2_regs_t; + +typedef struct el1_trf_regs { + uint64_t trfcr_el1; +} el1_trf_regs_t; + +typedef struct el1_csv2_2_regs { + uint64_t scxtnum_el0; + uint64_t scxtnum_el1; +} el1_csv2_2_regs_t; + +typedef struct el1_gcs_regs { + uint64_t gcscr_el1; + uint64_t gcscre0_el1; + uint64_t gcspr_el1; + uint64_t gcspr_el0; +} el1_gcs_regs_t; + +typedef struct el1_the_regs { + sysreg_t rcwmask_el1; + sysreg_t rcwsmask_el1; +} el1_the_regs_t; + +typedef struct el1_sctlr2_regs { + uint64_t sctlr2_el1; +} el1_sctlr2_regs_t; + +typedef struct el1_ls64_regs { + uint64_t accdata_el1; +} el1_ls64_regs_t; + +typedef struct el1_sysregs { + + el1_common_regs_t common; + +#if CTX_INCLUDE_AARCH32_REGS + el1_aarch32_regs_t el1_aarch32; +#endif + +#if NS_TIMER_SWITCH + el1_arch_timer_regs_t arch_timer; +#endif + +#if ENABLE_FEAT_MTE2 + el1_mte2_regs_t mte2; +#endif + +#if ENABLE_FEAT_RAS + el1_ras_regs_t ras; +#endif + +#if ENABLE_FEAT_S1PIE + el1_s1pie_regs_t s1pie; +#endif + +#if ENABLE_FEAT_S1POE + el1_s1poe_regs_t s1poe; +#endif + +#if ENABLE_FEAT_S2POE + el1_s2poe_regs_t s2poe; +#endif + +#if ENABLE_FEAT_TCR2 + el1_tcr2_regs_t tcr2; +#endif + +#if ENABLE_TRF_FOR_NS + el1_trf_regs_t trf; +#endif + +#if ENABLE_FEAT_CSV2_2 + el1_csv2_2_regs_t csv2_2; +#endif + +#if ENABLE_FEAT_GCS + el1_gcs_regs_t gcs; +#endif + +#if ENABLE_FEAT_THE + el1_the_regs_t the; +#endif + +#if ENABLE_FEAT_SCTLR2 + el1_sctlr2_regs_t sctlr2; +#endif + +#if ENABLE_FEAT_LS64_ACCDATA + el1_ls64_regs_t ls64; +#endif +} el1_sysregs_t; + + +/* + * Macros to access members related to individual features of the el1_sysregs_t + * structures. + */ + +#define read_el1_ctx_common(ctx, reg) (((ctx)->common).reg) + +#define write_el1_ctx_common(ctx, reg, val) ((((ctx)->common).reg) \ + = (uint64_t) (val)) + +#if NS_TIMER_SWITCH +#define read_el1_ctx_arch_timer(ctx, reg) (((ctx)->arch_timer).reg) +#define write_el1_ctx_arch_timer(ctx, reg, val) ((((ctx)->arch_timer).reg) \ + = (uint64_t) (val)) +#else +#define read_el1_ctx_arch_timer(ctx, reg) ULL(0) +#define write_el1_ctx_arch_timer(ctx, reg, val) +#endif /* NS_TIMER_SWITCH */ + +#if CTX_INCLUDE_AARCH32_REGS +#define read_el1_ctx_aarch32(ctx, reg) (((ctx)->el1_aarch32).reg) +#define write_el1_ctx_aarch32(ctx, reg, val) ((((ctx)->el1_aarch32).reg) \ + = (uint64_t) (val)) +#else +#define read_el1_ctx_aarch32(ctx, reg) ULL(0) +#define write_el1_ctx_aarch32(ctx, reg, val) +#endif /* CTX_INCLUDE_AARCH32_REGS */ + +#if ENABLE_FEAT_MTE2 +#define read_el1_ctx_mte2(ctx, reg) (((ctx)->mte2).reg) +#define write_el1_ctx_mte2(ctx, reg, val) ((((ctx)->mte2).reg) \ + = (uint64_t) (val)) +#else +#define read_el1_ctx_mte2(ctx, reg) ULL(0) +#define write_el1_ctx_mte2(ctx, reg, val) +#endif /* ENABLE_FEAT_MTE2 */ + +#if ENABLE_FEAT_RAS +#define read_el1_ctx_ras(ctx, reg) (((ctx)->ras).reg) +#define write_el1_ctx_ras(ctx, reg, val) ((((ctx)->ras).reg) \ + = (uint64_t) (val)) +#else +#define read_el1_ctx_ras(ctx, reg) ULL(0) +#define write_el1_ctx_ras(ctx, reg, val) +#endif /* ENABLE_FEAT_RAS */ + +#if ENABLE_FEAT_S1PIE +#define read_el1_ctx_s1pie(ctx, reg) (((ctx)->s1pie).reg) +#define write_el1_ctx_s1pie(ctx, reg, val) ((((ctx)->s1pie).reg) \ + = (uint64_t) (val)) +#else +#define read_el1_ctx_s1pie(ctx, reg) ULL(0) +#define write_el1_ctx_s1pie(ctx, reg, val) +#endif /* ENABLE_FEAT_S1PIE */ + +#if ENABLE_FEAT_S1POE +#define read_el1_ctx_s1poe(ctx, reg) (((ctx)->s1poe).reg) +#define write_el1_ctx_s1poe(ctx, reg, val) ((((ctx)->s1poe).reg) \ + = (uint64_t) (val)) +#else +#define read_el1_ctx_s1poe(ctx, reg) ULL(0) +#define write_el1_ctx_s1poe(ctx, reg, val) +#endif /* ENABLE_FEAT_S1POE */ + +#if ENABLE_FEAT_S2POE +#define read_el1_ctx_s2poe(ctx, reg) (((ctx)->s2poe).reg) +#define write_el1_ctx_s2poe(ctx, reg, val) ((((ctx)->s2poe).reg) \ + = (uint64_t) (val)) +#else +#define read_el1_ctx_s2poe(ctx, reg) ULL(0) +#define write_el1_ctx_s2poe(ctx, reg, val) +#endif /* ENABLE_FEAT_S2POE */ + +#if ENABLE_FEAT_TCR2 +#define read_el1_ctx_tcr2(ctx, reg) (((ctx)->tcr2).reg) +#define write_el1_ctx_tcr2(ctx, reg, val) ((((ctx)->tcr2).reg) \ + = (uint64_t) (val)) +#else +#define read_el1_ctx_tcr2(ctx, reg) ULL(0) +#define write_el1_ctx_tcr2(ctx, reg, val) +#endif /* ENABLE_FEAT_TCR2 */ + +#if ENABLE_TRF_FOR_NS +#define read_el1_ctx_trf(ctx, reg) (((ctx)->trf).reg) +#define write_el1_ctx_trf(ctx, reg, val) ((((ctx)->trf).reg) \ + = (uint64_t) (val)) +#else +#define read_el1_ctx_trf(ctx, reg) ULL(0) +#define write_el1_ctx_trf(ctx, reg, val) +#endif /* ENABLE_TRF_FOR_NS */ + +#if ENABLE_FEAT_CSV2_2 +#define read_el1_ctx_csv2_2(ctx, reg) (((ctx)->csv2_2).reg) +#define write_el1_ctx_csv2_2(ctx, reg, val) ((((ctx)->csv2_2).reg) \ + = (uint64_t) (val)) +#else +#define read_el1_ctx_csv2_2(ctx, reg) ULL(0) +#define write_el1_ctx_csv2_2(ctx, reg, val) +#endif /* ENABLE_FEAT_CSV2_2 */ + +#if ENABLE_FEAT_GCS +#define read_el1_ctx_gcs(ctx, reg) (((ctx)->gcs).reg) +#define write_el1_ctx_gcs(ctx, reg, val) ((((ctx)->gcs).reg) \ + = (uint64_t) (val)) +#else +#define read_el1_ctx_gcs(ctx, reg) ULL(0) +#define write_el1_ctx_gcs(ctx, reg, val) +#endif /* ENABLE_FEAT_GCS */ + +#if ENABLE_FEAT_THE +#define read_el1_ctx_the(ctx, reg) (((ctx)->the).reg) +#define write_el1_ctx_the(ctx, reg, val) ((((ctx)->the).reg) \ + = (uint64_t) (val)) +#else +#define read_el1_ctx_the(ctx, reg) ULL(0) +#define write_el1_ctx_the(ctx, reg, val) +#endif /* ENABLE_FEAT_THE */ + +#if ENABLE_FEAT_SCTLR2 +#define read_el1_ctx_sctlr2(ctx, reg) (((ctx)->sctlr2).reg) +#define write_el1_ctx_sctlr2(ctx, reg, val) ((((ctx)->sctlr2).reg) \ + = (uint64_t) (val)) +#else +#define read_el1_ctx_sctlr2(ctx, reg) ULL(0) +#define write_el1_ctx_sctlr2(ctx, reg, val) +#endif /* ENABLE_FEAT_SCTLR2 */ + +#if ENABLE_FEAT_LS64_ACCDATA +#define read_el1_ctx_ls64(ctx, reg) (((ctx)->ls64).reg) +#define write_el1_ctx_ls64(ctx, reg, val) ((((ctx)->ls64).reg) \ + = (uint64_t) (val)) +#else +#define read_el1_ctx_ls64(ctx, reg) ULL(0) +#define write_el1_ctx_ls64(ctx, reg, val) +#endif /* ENABLE_FEAT_LS64_ACCDATA */ +/******************************************************************************/ +#endif /* __ASSEMBLER__ */ + +#endif /* CONTEXT_EL1_H */ diff --git a/include/lib/el3_runtime/context_el2.h b/include/lib/el3_runtime/context_el2.h new file mode 100644 index 00000000..7374e39d --- /dev/null +++ b/include/lib/el3_runtime/context_el2.h @@ -0,0 +1,391 @@ +/* + * Copyright (c) 2024, Arm Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef CONTEXT_EL2_H +#define CONTEXT_EL2_H + +#include <lib/extensions/sysreg128.h> + +#ifndef __ASSEMBLER__ + +/******************************************************************************* + * EL2 Registers: + * AArch64 EL2 system register context structure for preserving the + * architectural state during world switches. + ******************************************************************************/ +typedef struct el2_common_regs { + uint64_t actlr_el2; + uint64_t afsr0_el2; + uint64_t afsr1_el2; + uint64_t amair_el2; + uint64_t cnthctl_el2; + uint64_t cntvoff_el2; + uint64_t cptr_el2; + uint64_t dbgvcr32_el2; + uint64_t elr_el2; + uint64_t esr_el2; + uint64_t far_el2; + uint64_t hacr_el2; + uint64_t hcr_el2; + uint64_t hpfar_el2; + uint64_t hstr_el2; + uint64_t icc_sre_el2; + uint64_t ich_hcr_el2; + uint64_t ich_vmcr_el2; + uint64_t mair_el2; + uint64_t mdcr_el2; + uint64_t pmscr_el2; + uint64_t sctlr_el2; + uint64_t spsr_el2; + uint64_t sp_el2; + uint64_t tcr_el2; + uint64_t tpidr_el2; + uint64_t vbar_el2; + uint64_t vmpidr_el2; + uint64_t vpidr_el2; + uint64_t vtcr_el2; + sysreg_t vttbr_el2; + sysreg_t ttbr0_el2; +} el2_common_regs_t; + +typedef struct el2_mte2_regs { + uint64_t tfsr_el2; +} el2_mte2_regs_t; + +typedef struct el2_fgt_regs { + uint64_t hdfgrtr_el2; + uint64_t hafgrtr_el2; + uint64_t hdfgwtr_el2; + uint64_t hfgitr_el2; + uint64_t hfgrtr_el2; + uint64_t hfgwtr_el2; +} el2_fgt_regs_t; + +typedef struct el2_fgt2_regs { + uint64_t hdfgrtr2_el2; + uint64_t hdfgwtr2_el2; + uint64_t hfgitr2_el2; + uint64_t hfgrtr2_el2; + uint64_t hfgwtr2_el2; +} el2_fgt2_regs_t; + +typedef struct el2_ecv_regs { + uint64_t cntpoff_el2; +} el2_ecv_regs_t; + +typedef struct el2_vhe_regs { + uint64_t contextidr_el2; + sysreg_t ttbr1_el2; +} el2_vhe_regs_t; + +typedef struct el2_ras_regs { + uint64_t vdisr_el2; + uint64_t vsesr_el2; +} el2_ras_regs_t; + +typedef struct el2_neve_regs { + uint64_t vncr_el2; +} el2_neve_regs_t; + +typedef struct el2_trf_regs { + uint64_t trfcr_el2; +} el2_trf_regs_t; + +typedef struct el2_csv2_regs { + uint64_t scxtnum_el2; +} el2_csv2_regs_t; + +typedef struct el2_hcx_regs { + uint64_t hcrx_el2; +} el2_hcx_regs_t; + +typedef struct el2_tcr2_regs { + uint64_t tcr2_el2; +} el2_tcr2_regs_t; + +typedef struct el2_sxpoe_regs { + uint64_t por_el2; +} el2_sxpoe_regs_t; + +typedef struct el2_sxpie_regs { + uint64_t pire0_el2; + uint64_t pir_el2; +} el2_sxpie_regs_t; + +typedef struct el2_s2pie_regs { + uint64_t s2pir_el2; +} el2_s2pie_regs_t; + +typedef struct el2_gcs_regs { + uint64_t gcscr_el2; + uint64_t gcspr_el2; +} el2_gcs_regs_t; + +typedef struct el2_mpam_regs { + uint64_t mpam2_el2; + uint64_t mpamhcr_el2; + uint64_t mpamvpm0_el2; + uint64_t mpamvpm1_el2; + uint64_t mpamvpm2_el2; + uint64_t mpamvpm3_el2; + uint64_t mpamvpm4_el2; + uint64_t mpamvpm5_el2; + uint64_t mpamvpm6_el2; + uint64_t mpamvpm7_el2; + uint64_t mpamvpmv_el2; +} el2_mpam_regs_t; + +typedef struct el2_sctlr2_regs { + uint64_t sctlr2_el2; +} el2_sctlr2_regs_t; + +typedef struct el2_sysregs { + + el2_common_regs_t common; + +#if ENABLE_FEAT_MTE2 + el2_mte2_regs_t mte2; +#endif + +#if ENABLE_FEAT_FGT + el2_fgt_regs_t fgt; +#endif + +#if ENABLE_FEAT_FGT2 + el2_fgt2_regs_t fgt2; +#endif + +#if ENABLE_FEAT_ECV + el2_ecv_regs_t ecv; +#endif + +#if ENABLE_FEAT_VHE + el2_vhe_regs_t vhe; +#endif + +#if ENABLE_FEAT_RAS + el2_ras_regs_t ras; +#endif + +#if CTX_INCLUDE_NEVE_REGS + el2_neve_regs_t neve; +#endif + +#if ENABLE_TRF_FOR_NS + el2_trf_regs_t trf; +#endif + +#if ENABLE_FEAT_CSV2_2 + el2_csv2_regs_t csv2; +#endif + +#if ENABLE_FEAT_HCX + el2_hcx_regs_t hcx; +#endif + +#if ENABLE_FEAT_TCR2 + el2_tcr2_regs_t tcr2; +#endif + +#if (ENABLE_FEAT_S1POE || ENABLE_FEAT_S2POE) + el2_sxpoe_regs_t sxpoe; +#endif + +#if (ENABLE_FEAT_S1PIE || ENABLE_FEAT_S2PIE) + el2_sxpie_regs_t sxpie; +#endif + +#if ENABLE_FEAT_S2PIE + el2_s2pie_regs_t s2pie; +#endif + +#if ENABLE_FEAT_GCS + el2_gcs_regs_t gcs; +#endif + +#if CTX_INCLUDE_MPAM_REGS + el2_mpam_regs_t mpam; +#endif + +#if ENABLE_FEAT_SCTLR2 + el2_sctlr2_regs_t sctlr2; +#endif + +} el2_sysregs_t; + +/* + * Macros to access members related to individual features of the el2_sysregs_t + * structures. + */ +#define read_el2_ctx_common(ctx, reg) (((ctx)->common).reg) + +#define write_el2_ctx_common(ctx, reg, val) ((((ctx)->common).reg) \ + = (uint64_t) (val)) + +#define write_el2_ctx_sysreg128(ctx, reg, val) ((((ctx)->common).reg) \ + = (sysreg_t) (val)) + +#if ENABLE_FEAT_MTE2 +#define read_el2_ctx_mte2(ctx, reg) (((ctx)->mte2).reg) +#define write_el2_ctx_mte2(ctx, reg, val) ((((ctx)->mte2).reg) \ + = (uint64_t) (val)) +#else +#define read_el2_ctx_mte2(ctx, reg) ULL(0) +#define write_el2_ctx_mte2(ctx, reg, val) +#endif /* ENABLE_FEAT_MTE2 */ + +#if ENABLE_FEAT_FGT +#define read_el2_ctx_fgt(ctx, reg) (((ctx)->fgt).reg) +#define write_el2_ctx_fgt(ctx, reg, val) ((((ctx)->fgt).reg) \ + = (uint64_t) (val)) +#else +#define read_el2_ctx_fgt(ctx, reg) ULL(0) +#define write_el2_ctx_fgt(ctx, reg, val) +#endif /* ENABLE_FEAT_FGT */ + +#if ENABLE_FEAT_FGT2 +#define read_el2_ctx_fgt2(ctx, reg) (((ctx)->fgt2).reg) +#define write_el2_ctx_fgt2(ctx, reg, val) ((((ctx)->fgt2).reg) \ + = (uint64_t) (val)) +#else +#define read_el2_ctx_fgt2(ctx, reg) ULL(0) +#define write_el2_ctx_fgt2(ctx, reg, val) +#endif /* ENABLE_FEAT_FGT */ + +#if ENABLE_FEAT_ECV +#define read_el2_ctx_ecv(ctx, reg) (((ctx)->ecv).reg) +#define write_el2_ctx_ecv(ctx, reg, val) ((((ctx)->ecv).reg) \ + = (uint64_t) (val)) +#else +#define read_el2_ctx_ecv(ctx, reg) ULL(0) +#define write_el2_ctx_ecv(ctx, reg, val) +#endif /* ENABLE_FEAT_ECV */ + +#if ENABLE_FEAT_VHE +#define read_el2_ctx_vhe(ctx, reg) (((ctx)->vhe).reg) +#define write_el2_ctx_vhe(ctx, reg, val) ((((ctx)->vhe).reg) \ + = (uint64_t) (val)) + +#define write_el2_ctx_vhe_sysreg128(ctx, reg, val) ((((ctx)->vhe).reg) \ + = (sysreg_t) (val)) +#else +#define read_el2_ctx_vhe(ctx, reg) ULL(0) +#define write_el2_ctx_vhe(ctx, reg, val) +#endif /* ENABLE_FEAT_VHE */ + +#if ENABLE_FEAT_RAS +#define read_el2_ctx_ras(ctx, reg) (((ctx)->ras).reg) +#define write_el2_ctx_ras(ctx, reg, val) ((((ctx)->ras).reg) \ + = (uint64_t) (val)) +#else +#define read_el2_ctx_ras(ctx, reg) ULL(0) +#define write_el2_ctx_ras(ctx, reg, val) +#endif /* ENABLE_FEAT_RAS */ + +#if CTX_INCLUDE_NEVE_REGS +#define read_el2_ctx_neve(ctx, reg) (((ctx)->neve).reg) +#define write_el2_ctx_neve(ctx, reg, val) ((((ctx)->neve).reg) \ + = (uint64_t) (val)) +#else +#define read_el2_ctx_neve(ctx, reg) ULL(0) +#define write_el2_ctx_neve(ctx, reg, val) +#endif /* CTX_INCLUDE_NEVE_REGS */ + +#if ENABLE_TRF_FOR_NS +#define read_el2_ctx_trf(ctx, reg) (((ctx)->trf).reg) +#define write_el2_ctx_trf(ctx, reg, val) ((((ctx)->trf).reg) \ + = (uint64_t) (val)) +#else +#define read_el2_ctx_trf(ctx, reg) ULL(0) +#define write_el2_ctx_trf(ctx, reg, val) +#endif /* ENABLE_TRF_FOR_NS */ + +#if ENABLE_FEAT_CSV2_2 +#define read_el2_ctx_csv2_2(ctx, reg) (((ctx)->csv2).reg) +#define write_el2_ctx_csv2_2(ctx, reg, val) ((((ctx)->csv2).reg) \ + = (uint64_t) (val)) +#else +#define read_el2_ctx_csv2_2(ctx, reg) ULL(0) +#define write_el2_ctx_csv2_2(ctx, reg, val) +#endif /* ENABLE_FEAT_CSV2_2 */ + +#if ENABLE_FEAT_HCX +#define read_el2_ctx_hcx(ctx, reg) (((ctx)->hcx).reg) +#define write_el2_ctx_hcx(ctx, reg, val) ((((ctx)->hcx).reg) \ + = (uint64_t) (val)) +#else +#define read_el2_ctx_hcx(ctx, reg) ULL(0) +#define write_el2_ctx_hcx(ctx, reg, val) +#endif /* ENABLE_FEAT_HCX */ + +#if ENABLE_FEAT_TCR2 +#define read_el2_ctx_tcr2(ctx, reg) (((ctx)->tcr2).reg) +#define write_el2_ctx_tcr2(ctx, reg, val) ((((ctx)->tcr2).reg) \ + = (uint64_t) (val)) +#else +#define read_el2_ctx_tcr2(ctx, reg) ULL(0) +#define write_el2_ctx_tcr2(ctx, reg, val) +#endif /* ENABLE_FEAT_TCR2 */ + +#if (ENABLE_FEAT_S1POE || ENABLE_FEAT_S2POE) +#define read_el2_ctx_sxpoe(ctx, reg) (((ctx)->sxpoe).reg) +#define write_el2_ctx_sxpoe(ctx, reg, val) ((((ctx)->sxpoe).reg) \ + = (uint64_t) (val)) +#else +#define read_el2_ctx_sxpoe(ctx, reg) ULL(0) +#define write_el2_ctx_sxpoe(ctx, reg, val) +#endif /*(ENABLE_FEAT_S1POE || ENABLE_FEAT_S2POE) */ + +#if (ENABLE_FEAT_S1PIE || ENABLE_FEAT_S2PIE) +#define read_el2_ctx_sxpie(ctx, reg) (((ctx)->sxpie).reg) +#define write_el2_ctx_sxpie(ctx, reg, val) ((((ctx)->sxpie).reg) \ + = (uint64_t) (val)) +#else +#define read_el2_ctx_sxpie(ctx, reg) ULL(0) +#define write_el2_ctx_sxpie(ctx, reg, val) +#endif /*(ENABLE_FEAT_S1PIE || ENABLE_FEAT_S2PIE) */ + +#if ENABLE_FEAT_S2PIE +#define read_el2_ctx_s2pie(ctx, reg) (((ctx)->s2pie).reg) +#define write_el2_ctx_s2pie(ctx, reg, val) ((((ctx)->s2pie).reg) \ + = (uint64_t) (val)) +#else +#define read_el2_ctx_s2pie(ctx, reg) ULL(0) +#define write_el2_ctx_s2pie(ctx, reg, val) +#endif /* ENABLE_FEAT_S2PIE */ + +#if ENABLE_FEAT_GCS +#define read_el2_ctx_gcs(ctx, reg) (((ctx)->gcs).reg) +#define write_el2_ctx_gcs(ctx, reg, val) ((((ctx)->gcs).reg) \ + = (uint64_t) (val)) +#else +#define read_el2_ctx_gcs(ctx, reg) ULL(0) +#define write_el2_ctx_gcs(ctx, reg, val) +#endif /* ENABLE_FEAT_GCS */ + +#if CTX_INCLUDE_MPAM_REGS +#define read_el2_ctx_mpam(ctx, reg) (((ctx)->mpam).reg) +#define write_el2_ctx_mpam(ctx, reg, val) ((((ctx)->mpam).reg) \ + = (uint64_t) (val)) +#else +#define read_el2_ctx_mpam(ctx, reg) ULL(0) +#define write_el2_ctx_mpam(ctx, reg, val) +#endif /* CTX_INCLUDE_MPAM_REGS */ + +#if ENABLE_FEAT_SCTLR2 +#define read_el2_ctx_sctlr2(ctx, reg) (((ctx)->sctlr2).reg) +#define write_el2_ctx_sctlr2(ctx, reg, val) ((((ctx)->sctlr2).reg) \ + = (uint64_t) (val)) +#else +#define read_el2_ctx_sctlr2(ctx, reg) ULL(0) +#define write_el2_ctx_sctlr2(ctx, reg, val) +#endif /* ENABLE_FEAT_SCTLR2 */ + +/******************************************************************************/ + +#endif /* __ASSEMBLER__ */ + +#endif /* CONTEXT_EL2_H */ diff --git a/include/lib/el3_runtime/context_mgmt.h b/include/lib/el3_runtime/context_mgmt.h index b2bdaf5a..70dbd463 100644 --- a/include/lib/el3_runtime/context_mgmt.h +++ b/include/lib/el3_runtime/context_mgmt.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013-2023, Arm Limited and Contributors. All rights reserved. + * Copyright (c) 2013-2024, Arm Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -30,24 +30,31 @@ void cm_set_context_by_index(unsigned int cpu_idx, void *cm_get_context(uint32_t security_state); void cm_set_context(void *context, uint32_t security_state); void cm_init_my_context(const struct entry_point_info *ep); -void cm_init_context_by_index(unsigned int cpu_idx, - const struct entry_point_info *ep); void cm_setup_context(cpu_context_t *ctx, const struct entry_point_info *ep); void cm_prepare_el3_exit(uint32_t security_state); void cm_prepare_el3_exit_ns(void); +#if !IMAGE_BL1 +void cm_init_context_by_index(unsigned int cpu_idx, + const struct entry_point_info *ep); +#endif /* !IMAGE_BL1 */ + #ifdef __aarch64__ #if IMAGE_BL31 void cm_manage_extensions_el3(void); void manage_extensions_nonsecure_per_world(void); +void cm_el3_arch_init_per_world(per_world_context_t *per_world_ctx); +void cm_handle_asymmetric_features(void); #endif -#if CTX_INCLUDE_EL2_REGS + +#if (CTX_INCLUDE_EL2_REGS && IMAGE_BL31) void cm_el2_sysregs_context_save(uint32_t security_state); void cm_el2_sysregs_context_restore(uint32_t security_state); -#endif - +#else void cm_el1_sysregs_context_save(uint32_t security_state); void cm_el1_sysregs_context_restore(uint32_t security_state); +#endif /* (CTX_INCLUDE_EL2_REGS && IMAGE_BL31) */ + void cm_set_elr_el3(uint32_t security_state, uintptr_t entrypoint); void cm_set_elr_spsr_el3(uint32_t security_state, uintptr_t entrypoint, uint32_t spsr); @@ -90,6 +97,7 @@ void *cm_get_next_context(void); void cm_set_next_context(void *context); static inline void cm_manage_extensions_el3(void) {} static inline void manage_extensions_nonsecure_per_world(void) {} +static inline void cm_handle_asymmetric_features(void) {} #endif /* __aarch64__ */ #endif /* CONTEXT_MGMT_H */ diff --git a/include/lib/el3_runtime/cpu_data.h b/include/lib/el3_runtime/cpu_data.h index 2c7b6196..8b548067 100644 --- a/include/lib/el3_runtime/cpu_data.h +++ b/include/lib/el3_runtime/cpu_data.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2021, Arm Limited and Contributors. All rights reserved. + * Copyright (c) 2014-2024, Arm Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -59,8 +59,19 @@ #define CPU_DATA_CRASH_BUF_END CPU_DATA_CRASH_BUF_OFFSET #endif +/* buffer space for EHF data is sizeof(pe_exc_data_t) */ +#define CPU_DATA_EHF_DATA_SIZE 8 +#define CPU_DATA_EHF_DATA_BUF_OFFSET CPU_DATA_CRASH_BUF_END + +#if defined(IMAGE_BL31) && EL3_EXCEPTION_HANDLING +#define CPU_DATA_EHF_DATA_BUF_END (CPU_DATA_EHF_DATA_BUF_OFFSET + \ + CPU_DATA_EHF_DATA_SIZE) +#else +#define CPU_DATA_EHF_DATA_BUF_END CPU_DATA_EHF_DATA_BUF_OFFSET +#endif /* EL3_EXCEPTION_HANDLING */ + /* cpu_data size is the data size rounded up to the platform cache line size */ -#define CPU_DATA_SIZE (((CPU_DATA_CRASH_BUF_END + \ +#define CPU_DATA_SIZE (((CPU_DATA_EHF_DATA_BUF_END + \ CACHE_WRITEBACK_GRANULE - 1) / \ CACHE_WRITEBACK_GRANULE) * \ CACHE_WRITEBACK_GRANULE) @@ -68,7 +79,7 @@ #if ENABLE_RUNTIME_INSTRUMENTATION /* Temporary space to store PMF timestamps from assembly code */ #define CPU_DATA_PMF_TS_COUNT 1 -#define CPU_DATA_PMF_TS0_OFFSET CPU_DATA_CRASH_BUF_END +#define CPU_DATA_PMF_TS0_OFFSET CPU_DATA_EHF_DATA_BUF_END #define CPU_DATA_PMF_TS0_IDX 0 #endif @@ -159,6 +170,12 @@ CASSERT(CPU_DATA_CRASH_BUF_OFFSET == __builtin_offsetof assert_cpu_data_crash_stack_offset_mismatch); #endif +#if defined(IMAGE_BL31) && EL3_EXCEPTION_HANDLING +CASSERT(CPU_DATA_EHF_DATA_BUF_OFFSET == __builtin_offsetof + (cpu_data_t, ehf_data), + assert_cpu_data_ehf_stack_offset_mismatch); +#endif + CASSERT(CPU_DATA_SIZE == sizeof(cpu_data_t), assert_cpu_data_size_mismatch); diff --git a/include/lib/el3_runtime/simd_ctx.h b/include/lib/el3_runtime/simd_ctx.h new file mode 100644 index 00000000..fdbe24f3 --- /dev/null +++ b/include/lib/el3_runtime/simd_ctx.h @@ -0,0 +1,97 @@ +/* + * Copyright (c) 2024, Arm Limited and Contributors. All rights reserved. + * Copyright (c) 2022, Google LLC. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef SIMD_CTX_H +#define SIMD_CTX_H + +/******************************************************************************* + * Constants that allow assembler code to access members of and the 'simd_context' + * structure at their correct offsets. + ******************************************************************************/ + +#if CTX_INCLUDE_FPREGS || CTX_INCLUDE_SVE_REGS +#if CTX_INCLUDE_SVE_REGS +#define SIMD_VECTOR_LEN_BYTES (SVE_VECTOR_LEN / 8) /* Length of vector in bytes */ +#elif CTX_INCLUDE_FPREGS +#define SIMD_VECTOR_LEN_BYTES U(16) /* 128 bits fixed vector length for FPU */ +#endif /* CTX_INCLUDE_SVE_REGS */ + +#define CTX_SIMD_VECTORS U(0) +/* there are 32 vector registers, each of size SIMD_VECTOR_LEN_BYTES */ +#define CTX_SIMD_FPSR (CTX_SIMD_VECTORS + (32 * SIMD_VECTOR_LEN_BYTES)) +#define CTX_SIMD_FPCR (CTX_SIMD_FPSR + 8) + +#if CTX_INCLUDE_FPREGS && CTX_INCLUDE_AARCH32_REGS +#define CTX_SIMD_FPEXC32 (CTX_SIMD_FPCR + 8) +#define CTX_SIMD_PREDICATES (CTX_SIMD_FPEXC32 + 16) +#else +#define CTX_SIMD_PREDICATES (CTX_SIMD_FPCR + 8) +#endif /* CTX_INCLUDE_FPREGS && CTX_INCLUDE_AARCH32_REGS */ + +/* + * Each predicate register is 1/8th the size of a vector register and there are 16 + * predicate registers + */ +#define CTX_SIMD_FFR (CTX_SIMD_PREDICATES + (16 * (SIMD_VECTOR_LEN_BYTES / 8))) + +#ifndef __ASSEMBLER__ + +#include <stdint.h> +#include <lib/cassert.h> + +/* + * Please don't change order of fields in this struct as that may violate + * alignment requirements and affect how assembly code accesses members of this + * struct. + */ +typedef struct { + uint8_t vectors[32][SIMD_VECTOR_LEN_BYTES]; + uint8_t fpsr[8]; + uint8_t fpcr[8]; +#if CTX_INCLUDE_FPREGS && CTX_INCLUDE_AARCH32_REGS + /* 16 bytes to align to next 16 byte boundary when CTX_INCLUDE_SVE_REGS is 0 */ + uint8_t fpexc32_el2[16]; +#endif +#if CTX_INCLUDE_SVE_REGS + /* FFR and each of predicates is one-eigth of the SVE vector length */ + uint8_t predicates[16][SIMD_VECTOR_LEN_BYTES / 8]; + uint8_t ffr[SIMD_VECTOR_LEN_BYTES / 8]; + /* SMCCCv1.3 FID[16] hint bit state recorded on EL3 entry */ + bool hint; +#endif /* CTX_INCLUDE_SVE_REGS */ +} __aligned(16) simd_regs_t; + +CASSERT(CTX_SIMD_VECTORS == __builtin_offsetof(simd_regs_t, vectors), + assert_vectors_mismatch); + +CASSERT(CTX_SIMD_FPSR == __builtin_offsetof(simd_regs_t, fpsr), + assert_fpsr_mismatch); + +CASSERT(CTX_SIMD_FPCR == __builtin_offsetof(simd_regs_t, fpcr), + assert_fpcr_mismatch); + +#if CTX_INCLUDE_FPREGS && CTX_INCLUDE_AARCH32_REGS +CASSERT(CTX_SIMD_FPEXC32 == __builtin_offsetof(simd_regs_t, fpexc32_el2), + assert_fpex32_mismtatch); +#endif + +#if CTX_INCLUDE_SVE_REGS +CASSERT(CTX_SIMD_PREDICATES == __builtin_offsetof(simd_regs_t, predicates), + assert_predicates_mismatch); + +CASSERT(CTX_SIMD_FFR == __builtin_offsetof(simd_regs_t, ffr), + assert_ffr_mismatch); +#endif + +void simd_ctx_save(uint32_t security_state, bool hint_sve); +void simd_ctx_restore(uint32_t security_state); + +#endif /* __ASSEMBLER__ */ + +#endif /* CTX_INCLUDE_FPREGS || CTX_INCLUDE_SVE_REGS */ + +#endif /* SIMD_CTX_H */ diff --git a/include/lib/extensions/brbe.h b/include/lib/extensions/brbe.h index 194efba7..425a0377 100644 --- a/include/lib/extensions/brbe.h +++ b/include/lib/extensions/brbe.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022-2023, Arm Limited. All rights reserved. + * Copyright (c) 2022-2024, Arm Limited. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -7,10 +7,12 @@ #ifndef BRBE_H #define BRBE_H +#include <context.h> + #if ENABLE_BRBE_FOR_NS -void brbe_init_el3(void); +void brbe_enable(cpu_context_t *ctx); #else -static inline void brbe_init_el3(void) +static inline void brbe_enable(cpu_context_t *ctx) { } #endif /* ENABLE_BRBE_FOR_NS */ diff --git a/include/lib/extensions/debug_v8p9.h b/include/lib/extensions/debug_v8p9.h new file mode 100644 index 00000000..d72c9ea7 --- /dev/null +++ b/include/lib/extensions/debug_v8p9.h @@ -0,0 +1,20 @@ +/* + * Copyright (c) 2024, Arm Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef DEBUG_V8P9_H +#define DEBUG_V8P9_H + +#include <context.h> + +#if ENABLE_FEAT_DEBUGV8P9 +void debugv8p9_extended_bp_wp_enable(cpu_context_t *ctx); +#else +static inline void debugv8p9_extended_bp_wp_enable(cpu_context_t *ctx) +{ +} +#endif /* ENABLE_FEAT_DEBUGV8P9 */ + +#endif /* DEBUG_V8P9_H */ diff --git a/include/lib/extensions/fgt2.h b/include/lib/extensions/fgt2.h new file mode 100644 index 00000000..0388d184 --- /dev/null +++ b/include/lib/extensions/fgt2.h @@ -0,0 +1,20 @@ +/* + * Copyright (c) 2024, Arm Limited. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef FGT2_H +#define FGT2_H + +#include <context.h> + +#if ENABLE_FEAT_FGT2 +void fgt2_enable(cpu_context_t *ctx); +#else +static inline void fgt2_enable(cpu_context_t *ctx) +{ +} +#endif /* ENABLE_FEAT_FGT2 */ + +#endif /* FGT2_H */ diff --git a/include/lib/extensions/mpam.h b/include/lib/extensions/mpam.h index 170f919b..3dd56526 100644 --- a/include/lib/extensions/mpam.h +++ b/include/lib/extensions/mpam.h @@ -12,10 +12,10 @@ #include <context.h> #if ENABLE_FEAT_MPAM -void mpam_enable(cpu_context_t *context); +void mpam_enable_per_world(per_world_context_t *per_world_ctx); void mpam_init_el2_unused(void); #else -static inline void mpam_enable(cpu_context_t *context) +static inline void mpam_enable_per_world(per_world_context_t *per_world_ctx) { } static inline void mpam_init_el2_unused(void) diff --git a/include/lib/extensions/spe.h b/include/lib/extensions/spe.h index 7b390378..4801a220 100644 --- a/include/lib/extensions/spe.h +++ b/include/lib/extensions/spe.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2023, Arm Limited and Contributors. All rights reserved. + * Copyright (c) 2017-2024, Arm Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -8,19 +8,24 @@ #define SPE_H #include <stdbool.h> +#include <context.h> #if ENABLE_SPE_FOR_NS -void spe_init_el3(void); +void spe_enable(cpu_context_t *ctx); +void spe_disable(cpu_context_t *ctx); void spe_init_el2_unused(void); -void spe_disable(void); +void spe_stop(void); #else -static inline void spe_init_el3(void) +static inline void spe_enable(cpu_context_t *ctx) +{ +} +static inline void spe_disable(cpu_context_t *ctx) { } static inline void spe_init_el2_unused(void) { } -static inline void spe_disable(void) +static inline void spe_stop(void) { } #endif /* ENABLE_SPE_FOR_NS */ diff --git a/include/lib/extensions/sve.h b/include/lib/extensions/sve.h index 947c905b..2979efb1 100644 --- a/include/lib/extensions/sve.h +++ b/include/lib/extensions/sve.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2023, Arm Limited and Contributors. All rights reserved. + * Copyright (c) 2017-2024, Arm Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -10,6 +10,7 @@ #include <context.h> #if (ENABLE_SME_FOR_NS || ENABLE_SVE_FOR_NS) + void sve_init_el2_unused(void); void sve_enable_per_world(per_world_context_t *per_world_ctx); void sve_disable_per_world(per_world_context_t *per_world_ctx); @@ -25,4 +26,9 @@ static inline void sve_disable_per_world(per_world_context_t *per_world_ctx) } #endif /* ( ENABLE_SME_FOR_NS | ENABLE_SVE_FOR_NS ) */ +#if CTX_INCLUDE_SVE_REGS +void sve_context_save(simd_regs_t *regs); +void sve_context_restore(simd_regs_t *regs); +#endif + #endif /* SVE_H */ diff --git a/include/lib/extensions/sysreg128.h b/include/lib/extensions/sysreg128.h new file mode 100644 index 00000000..8854856b --- /dev/null +++ b/include/lib/extensions/sysreg128.h @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2024, Arm Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef SYSREG128_H +#define SYSREG128_H + +#ifndef __ASSEMBLER__ + +#if ENABLE_FEAT_D128 +#include <stdint.h> + +typedef uint128_t sysreg_t; + +#define PAR_EL1_D128 (((sysreg_t)(1ULL)) << (64)) + +#define _DECLARE_SYSREG128_READ_FUNC(_name) \ +uint128_t read_ ## _name(void); + +#define _DECLARE_SYSREG128_WRITE_FUNC(_name) \ +void write_ ## _name(uint128_t v); + +#define DECLARE_SYSREG128_RW_FUNCS(_name) \ + _DECLARE_SYSREG128_READ_FUNC(_name) \ + _DECLARE_SYSREG128_WRITE_FUNC(_name) +#else + +typedef uint64_t sysreg_t; + +#endif /* ENABLE_FEAT_D128 */ + +#endif /* __ASSEMBLER__ */ + +#endif /* SYSREG128_H */ diff --git a/include/lib/extensions/tcr2.h b/include/lib/extensions/tcr2.h new file mode 100644 index 00000000..08a2b08c --- /dev/null +++ b/include/lib/extensions/tcr2.h @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2024, Arm Limited. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef TCR2_H +#define TCR2_H + +#include <context.h> + +#if ENABLE_FEAT_TCR2 +void tcr2_enable(cpu_context_t *ctx); +void tcr2_disable(cpu_context_t *ctx); +#else +static inline void tcr2_enable(cpu_context_t *ctx) +{ +} +static inline void tcr2_disable(cpu_context_t *ctx) +{ +} +#endif /* ENABLE_FEAT_TCR2 */ + +#endif /* TCR2_H */ diff --git a/include/lib/extensions/trbe.h b/include/lib/extensions/trbe.h index 0bed4337..2c488e09 100644 --- a/include/lib/extensions/trbe.h +++ b/include/lib/extensions/trbe.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021-2023, Arm Limited. All rights reserved. + * Copyright (c) 2021-2024, Arm Limited. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -7,11 +7,17 @@ #ifndef TRBE_H #define TRBE_H +#include <context.h> + #if ENABLE_TRBE_FOR_NS -void trbe_init_el3(void); +void trbe_disable(cpu_context_t *ctx); +void trbe_enable(cpu_context_t *ctx); void trbe_init_el2_unused(void); #else -static inline void trbe_init_el3(void) +static inline void trbe_disable(cpu_context_t *ctx) +{ +} +static inline void trbe_enable(cpu_context_t *ctx) { } static inline void trbe_init_el2_unused(void) diff --git a/include/lib/extensions/trf.h b/include/lib/extensions/trf.h index 1ac7cda4..f0a946be 100644 --- a/include/lib/extensions/trf.h +++ b/include/lib/extensions/trf.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021-2023, Arm Limited. All rights reserved. + * Copyright (c) 2021-2024, Arm Limited. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -7,16 +7,32 @@ #ifndef TRF_H #define TRF_H +#include <context.h> + #if ENABLE_TRF_FOR_NS -void trf_init_el3(void); + +#if __aarch64__ +void trf_enable(cpu_context_t *ctx); void trf_init_el2_unused(void); -#else -static inline void trf_init_el3(void) +#else /* !__aarch64 */ +void trf_init_el3(void); +#endif /* __aarch64__ */ + +#else /* ENABLE_TRF_FOR_NS=0 */ + +#if __aarch64__ +static inline void trf_enable(cpu_context_t *ctx) { } static inline void trf_init_el2_unused(void) { } +#else /* !__aarch64 */ +static inline void trf_init_el3(void) +{ +} +#endif /* __aarch64__*/ + #endif /* ENABLE_TRF_FOR_NS */ #endif /* TRF_H */ diff --git a/include/lib/pmf/pmf.h b/include/lib/pmf/pmf.h index 9d901e20..41bf7fc7 100644 --- a/include/lib/pmf/pmf.h +++ b/include/lib/pmf/pmf.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016-2020, Arm Limited and Contributors. All rights reserved. + * Copyright (c) 2016-2024, Arm Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -36,19 +36,36 @@ #define PMF_NO_CACHE_MAINT U(0) /* - * Defines for PMF SMC function ids. + * Defines for PMF SMC function ids used with arm-sip + * range, this is now deprecated and will be removed. */ -#define PMF_SMC_GET_TIMESTAMP_32 U(0x82000010) -#define PMF_SMC_GET_TIMESTAMP_64 U(0xC2000010) +#define PMF_SMC_GET_TIMESTAMP_32_DEP U(0x82000010) +#define PMF_SMC_GET_TIMESTAMP_64_DEP U(0xC2000010) + +#define PMF_FID_VALUE_DEPRECATED U(0x10) +#define is_pmf_fid_deprecated(_fid) \ + (GET_SMC_NUM(_fid) == PMF_FID_VALUE_DEPRECATED) + +/* + * Defines for PMF SMC function ids used with Vendor-Specific + * EL3 range. + */ +#define PMF_SMC_GET_TIMESTAMP_32 U(0x87000020) +#define PMF_SMC_GET_TIMESTAMP_64 U(0xC7000020) #define PMF_NUM_SMC_CALLS 2 +#define PMF_SMC_GET_VERSION_32 U(0x87000021) +#define PMF_SMC_GET_VERSION_64 U(0xC7000021) + +#define PMF_SMC_VERSION U(0x00000001) + /* * The macros below are used to identify * PMF calls from the SMC function ID. */ -#define PMF_FID_MASK U(0xffe0) -#define PMF_FID_VALUE U(0) -#define is_pmf_fid(_fid) (((_fid) & PMF_FID_MASK) == PMF_FID_VALUE) +#define PMF_FID_VALUE U(0x20) +#define PMF_ID_MASK (FUNCID_NUM_MASK & ~(0xf)) +#define is_pmf_fid(_fid) ((GET_SMC_NUM(_fid) & PMF_ID_MASK) == PMF_FID_VALUE) /* Following are the supported PMF service IDs */ #define PMF_PSCI_STAT_SVC_ID 0 diff --git a/include/lib/psa/cca_attestation.h b/include/lib/psa/cca_attestation.h new file mode 100644 index 00000000..4062ddef --- /dev/null +++ b/include/lib/psa/cca_attestation.h @@ -0,0 +1,20 @@ +/* + * Copyright (c) 2024, Arm Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef CCA_ATTESTATION_H +#define CCA_ATTESTATION_H + +#include <stdint.h> +#include <psa/crypto_types.h> + +psa_status_t +cca_attestation_get_realm_key(uintptr_t buf, size_t *len, unsigned int type); + +psa_status_t +cca_attestation_get_plat_token(uintptr_t buf, size_t *len, + uintptr_t hash, size_t hash_size); + +#endif /* CCA_ATTESTATION_H */ diff --git a/include/lib/psa/delegated_attestation.h b/include/lib/psa/delegated_attestation.h index 7aaceb3e..ec49f5d3 100644 --- a/include/lib/psa/delegated_attestation.h +++ b/include/lib/psa/delegated_attestation.h @@ -15,9 +15,9 @@ #include "psa/error.h" -/* RSS Delegated Attestation message types that distinguish its services. */ -#define RSS_DELEGATED_ATTEST_GET_DELEGATED_KEY 1001U -#define RSS_DELEGATED_ATTEST_GET_PLATFORM_TOKEN 1002U +/* RSE Delegated Attestation message types that distinguish its services. */ +#define RSE_DELEGATED_ATTEST_GET_DELEGATED_KEY 1001U +#define RSE_DELEGATED_ATTEST_GET_PLATFORM_TOKEN 1002U /** * The aim of these APIs to get a derived signing key (private only) for the @@ -28,13 +28,13 @@ * key is bind to the platform token (details below). * * Expected usage model: - * - First rss_delegated_attest_get_delegated_key() API need to be called to + * - First rse_delegated_attest_get_delegated_key() API need to be called to * obtain the private part of the delegated attestation key. The public part * of key is computed by the cryptographic library when the key is * registered. - * - Secondly the rss_delegated_attest_get_token() must be called to obtain + * - Secondly the rse_delegated_attest_get_token() must be called to obtain * platform attestation token. The hash of the public key (computed by - * the hash_algo indicated in the rss_delegated_attest_get_delegated_key() + * the hash_algo indicated in the rse_delegated_attest_get_delegated_key() * call) must be the input of this call. This ensures that nothing but the * previously derived delegated key is bindable to the platform token. */ @@ -74,7 +74,7 @@ * platform attestation token as they are cryptographically linked together. */ psa_status_t -rss_delegated_attest_get_delegated_key(uint8_t ecc_curve, +rse_delegated_attest_get_delegated_key(uint8_t ecc_curve, uint32_t key_bits, uint8_t *key_buf, size_t key_buf_size, @@ -100,7 +100,7 @@ rss_delegated_attest_get_delegated_key(uint8_t ecc_curve, * code will be returned. */ psa_status_t -rss_delegated_attest_get_token(const uint8_t *dak_pub_hash, +rse_delegated_attest_get_token(const uint8_t *dak_pub_hash, size_t dak_pub_hash_size, uint8_t *token_buf, size_t token_buf_size, diff --git a/include/lib/psa/dice_protection_environment.h b/include/lib/psa/dice_protection_environment.h new file mode 100644 index 00000000..53514516 --- /dev/null +++ b/include/lib/psa/dice_protection_environment.h @@ -0,0 +1,100 @@ +/* + * Copyright (c) 2024, Arm Limited. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + * + */ + +#ifndef DICE_PROTECTION_ENVIRONMENT_H +#define DICE_PROTECTION_ENVIRONMENT_H + +#include <stdbool.h> +#include <stddef.h> +#include <stdint.h> + +#include <dice.h> + +/* Additional defines for max size limit. These limits are set by DPE in RSE. */ +#define DICE_AUTHORITY_DESCRIPTOR_MAX_SIZE 64 +#define DICE_CONFIG_DESCRIPTOR_MAX_SIZE 64 +#define DICE_CODE_DESCRIPTOR_MAX_SIZE 32 + +typedef int32_t dpe_error_t; + +#define DPE_NO_ERROR ((dpe_error_t)0) +#define DPE_INTERNAL_ERROR ((dpe_error_t)1) +#define DPE_INVALID_COMMAND ((dpe_error_t)2) +#define DPE_INVALID_ARGUMENT ((dpe_error_t)3) +#define DPE_ARGUMENT_NOT_SUPPORTED ((dpe_error_t)4) +#define DPE_SESSION_EXHAUSTED ((dpe_error_t)5) + +/* Custom values in RSE based DPE implementation */ +#define DPE_INSUFFICIENT_MEMORY ((dpe_error_t)128) +#define DPE_ERR_CBOR_FORMATTING ((dpe_error_t)129) + +/** + * Client facing API. Parameters are according to the DPE spec version r0.9 + * + * \brief Performs the DICE computation to derive a new context and optionally + * creates an intermediate certificate. Software component measurement + * must be provided in dice_inputs. + * + * \param[in] context_handle Input context handle for the DPE + * context. + * \param[in] cert_id Logical certificate id to which derived + * context belongs to. + * \param[in] retain_parent_context Flag to indicate whether to retain the + * parent context. True only if a client + * will call further DPE commands on the + * same context. + * \param[in] allow_new_context_to_derive Flag to indicate whether derived context + * can derive further. True only if the + * new context will load further components. + * \param[in] create_certificate Flag to indicate whether to create an + * intermediate certificate. True only if + * it is the last component in the layer. + * \param[in] dice_inputs DICE input values. + * \param[in] target_locality Identifies the locality to which the + * derived context will be bound. Could be + * MHU id. + * \param[in] return_certificate Indicates whether to return the generated + * certificate when create_certificate is true. + * \param[in] allow_new_context_to_export Indicates whether the DPE permits export of + * the CDI from the newly derived context. + * \param[in] export_cdi Indicates whether to export derived CDI. + * \param[out] new_context_handle New handle for the derived context. + * \param[out] new_parent_context_handle New handle for the parent context. + * \param[out] new_certificate_buf If create_certificate and return_certificate + * are both true, this argument holds the new + * certificate generated for the new context + * \param[in] new_certificate_buf_size Size of the allocated buffer for + * new certificate. + * \param[out] new_certificate_actual_size Actual size of the new certificate. + * \param[out] exported_cdi_buf If export_cdi is true, this is the + * exported CDI value. + * \param[in] exported_cdi_buf_size Size of the allocated buffer for + * exported cdi. + * \param[out] exported_cdi_actual_size Actual size of the exported cdi. + * + * \return Returns error code of type dpe_error_t + */ +dpe_error_t dpe_derive_context(int context_handle, + uint32_t cert_id, + bool retain_parent_context, + bool allow_new_context_to_derive, + bool create_certificate, + const DiceInputValues *dice_inputs, + int32_t target_locality, + bool return_certificate, + bool allow_new_context_to_export, + bool export_cdi, + int *new_context_handle, + int *new_parent_context_handle, + uint8_t *new_certificate_buf, + size_t new_certificate_buf_size, + size_t *new_certificate_actual_size, + uint8_t *exported_cdi_buf, + size_t exported_cdi_buf_size, + size_t *exported_cdi_actual_size); + +#endif /* DICE_PROTECTION_ENVIRONMENT_H */ diff --git a/include/lib/psa/measured_boot.h b/include/lib/psa/measured_boot.h index af624a6f..3cc6c95d 100644 --- a/include/lib/psa/measured_boot.h +++ b/include/lib/psa/measured_boot.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, Arm Limited. All rights reserved. + * Copyright (c) 2022-2024, Arm Limited. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause * @@ -14,21 +14,6 @@ #include "psa/error.h" -/* Minimum measurement value size that can be requested to store */ -#define MEASUREMENT_VALUE_MIN_SIZE 32U -/* Maximum measurement value size that can be requested to store */ -#define MEASUREMENT_VALUE_MAX_SIZE 64U -/* Minimum signer id size that can be requested to store */ -#define SIGNER_ID_MIN_SIZE MEASUREMENT_VALUE_MIN_SIZE -/* Maximum signer id size that can be requested to store */ -#define SIGNER_ID_MAX_SIZE MEASUREMENT_VALUE_MAX_SIZE -/* The theoretical maximum image version is: "255.255.65535\0" */ -#define VERSION_MAX_SIZE 14U -/* Example sw_type: "BL_2, BL_33, etc." */ -#define SW_TYPE_MAX_SIZE 20U -#define NUM_OF_MEASUREMENT_SLOTS 32U - - /** * Extends and stores a measurement to the requested slot. * @@ -58,11 +43,11 @@ * - When the requested slot is not accessible to the caller. */ -/* Not a standard PSA API, just an extension therefore use the 'rss_' prefix +/* Not a standard PSA API, just an extension therefore use the 'rse_' prefix * rather than the usual 'psa_'. */ psa_status_t -rss_measured_boot_extend_measurement(uint8_t index, +rse_measured_boot_extend_measurement(uint8_t index, const uint8_t *signer_id, size_t signer_id_size, const uint8_t *version, @@ -107,7 +92,7 @@ rss_measured_boot_extend_measurement(uint8_t index, * PSA_ERROR_DOES_NOT_EXIST * - The requested slot is empty, does not contain a measurement. */ -psa_status_t rss_measured_boot_read_measurement(uint8_t index, +psa_status_t rse_measured_boot_read_measurement(uint8_t index, uint8_t *signer_id, size_t signer_id_size, size_t *signer_id_len, diff --git a/include/lib/psa/psa/client.h b/include/lib/psa/psa/client.h index 56fe0288..46fac4a8 100644 --- a/include/lib/psa/psa/client.h +++ b/include/lib/psa/psa/client.h @@ -1,6 +1,5 @@ - /* - * Copyright (c) 2018-2021, Arm Limited. All rights reserved. + * Copyright (c) 2018-2024, Arm Limited. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause * @@ -17,41 +16,57 @@ #ifndef IOVEC_LEN #define IOVEC_LEN(arr) ((uint32_t)(sizeof(arr)/sizeof(arr[0]))) #endif + /*********************** PSA Client Macros and Types *************************/ + /** * The version of the PSA Framework API that is being used to build the calling * firmware. Only part of features of FF-M v1.1 have been implemented. FF-M v1.1 * is compatible with v1.0. */ #define PSA_FRAMEWORK_VERSION (0x0101u) + /** * Return value from psa_version() if the requested RoT Service is not present * in the system. */ #define PSA_VERSION_NONE (0u) + /** * The zero-value null handle can be assigned to variables used in clients and * RoT Services, indicating that there is no current connection or message. */ #define PSA_NULL_HANDLE ((psa_handle_t)0) + /** * Tests whether a handle value returned by psa_connect() is valid. */ #define PSA_HANDLE_IS_VALID(handle) ((psa_handle_t)(handle) > 0) + /** * Converts the handle value returned from a failed call psa_connect() into * an error code. */ #define PSA_HANDLE_TO_ERROR(handle) ((psa_status_t)(handle)) + /** * Maximum number of input and output vectors for a request to psa_call(). */ #define PSA_MAX_IOVEC (4u) + +/** + * The minimum and maximum value that can be passed + * as the type parameter in a call to psa_call(). + */ +#define PSA_CALL_TYPE_MIN (0) +#define PSA_CALL_TYPE_MAX (INT16_MAX) + /** * An IPC message type that indicates a generic client request. */ #define PSA_IPC_CALL (0) typedef int32_t psa_handle_t; + /** * A read-only input memory region provided to an RoT Service. */ @@ -59,6 +74,7 @@ typedef struct psa_invec { const void *base; /*!< the start address of the memory buffer */ size_t len; /*!< the size in bytes */ } psa_invec; + /** * A writable output memory region provided to an RoT Service. */ diff --git a/include/lib/psa/psa_manifest/sid.h b/include/lib/psa/psa_manifest/sid.h index 71831124..bb8abe46 100644 --- a/include/lib/psa/psa_manifest/sid.h +++ b/include/lib/psa/psa_manifest/sid.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019-2023, Arm Limited. All rights reserved. + * Copyright (c) 2019-2024, Arm Limited. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause * @@ -8,16 +8,19 @@ #ifndef PSA_MANIFEST_SID_H #define PSA_MANIFEST_SID_H -/******** RSS_SP_CRYPTO ********/ -#define RSS_CRYPTO_HANDLE (0x40000100U) +/******** RSE_SP_CRYPTO ********/ +#define RSE_CRYPTO_HANDLE (0x40000100U) -/******** RSS_SP_PLATFORM ********/ -#define RSS_PLATFORM_SERVICE_HANDLE (0x40000105U) +/******** RSE_SP_PLATFORM ********/ +#define RSE_PLATFORM_SERVICE_HANDLE (0x40000105U) /******** PSA_SP_MEASURED_BOOT ********/ -#define RSS_MEASURED_BOOT_HANDLE (0x40000110U) +#define RSE_MEASURED_BOOT_HANDLE (0x40000110U) -/******** PSA_SP_DELAGATED_ATTESTATION ********/ -#define RSS_DELEGATED_SERVICE_HANDLE (0x40000111U) +/******** PSA_SP_DELEGATED_ATTESTATION ********/ +#define RSE_DELEGATED_SERVICE_HANDLE (0x40000111U) + +/******** PSA_SP_DICE_PROTECTION_ENVIRONMENT ********/ +#define RSE_DPE_SERVICE_HANDLE (0x40000112U) #endif /* PSA_MANIFEST_SID_H */ diff --git a/include/lib/psa/rse_crypto_defs.h b/include/lib/psa/rse_crypto_defs.h new file mode 100644 index 00000000..b94664fb --- /dev/null +++ b/include/lib/psa/rse_crypto_defs.h @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2023-2024, Arm Limited. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + * + */ + +#ifndef RSE_CRYPTO_DEFS_H +#define RSE_CRYPTO_DEFS_H + +/* Declares types that encode errors, algorithms, key types, policies, etc. */ +#include "psa/crypto_types.h" + +/* + * Value identifying export public key function API, used to dispatch the request + * to the corresponding API implementation in the Crypto service backend. + * + */ +#define RSE_CRYPTO_EXPORT_PUBLIC_KEY_SID (uint16_t)(0x206) + +/* + * The persistent key identifiers for RSE builtin keys. + */ +enum rse_key_id_builtin_t { + RSE_BUILTIN_KEY_ID_HOST_S_ROTPK = 0x7FFF816Cu, + RSE_BUILTIN_KEY_ID_HOST_NS_ROTPK, + RSE_BUILTIN_KEY_ID_HOST_CCA_ROTPK, +}; + +/* + * This type is used to overcome a limitation within RSE firmware in the number of maximum + * IOVECs it can use especially in psa_aead_encrypt and psa_aead_decrypt. + */ +#define RSE_CRYPTO_MAX_NONCE_LENGTH (16u) +struct rse_crypto_aead_pack_input { + uint8_t nonce[RSE_CRYPTO_MAX_NONCE_LENGTH]; + uint32_t nonce_length; +}; + +/* + * Structure used to pack non-pointer types in a call to PSA Crypto APIs + */ +struct rse_crypto_pack_iovec { + psa_key_id_t key_id; /* !< Key id */ + psa_algorithm_t alg; /* !< Algorithm */ + uint32_t op_handle; /* + * !< Frontend context handle + * associated to a multipart operation + */ + uint32_t ad_length; /* + * !< Additional Data length for + * multipart AEAD + */ + uint32_t plaintext_length; /* + * !< Plaintext length for multipart + * AEAD + */ + + struct rse_crypto_aead_pack_input aead_in; /* + * !< Packs AEAD-related + * inputs + */ + + uint16_t function_id; /* + * !< Used to identify the function in the + * API dispatcher to the service backend + * See rse_crypto_func_sid for detail + */ + uint16_t step; /* !< Key derivation step */ + union { + size_t capacity; /* !< Key derivation capacity */ + uint64_t value; /* + * !< Key derivation integer for + * update + */ + }; +}; + +#endif /* RSE_CRYPTO_DEFS_H */ diff --git a/include/lib/psa/rss_platform_api.h b/include/lib/psa/rse_platform_api.h similarity index 74% rename from include/lib/psa/rss_platform_api.h rename to include/lib/psa/rse_platform_api.h index 8f74a51f..535001bd 100644 --- a/include/lib/psa/rss_platform_api.h +++ b/include/lib/psa/rse_platform_api.h @@ -5,16 +5,16 @@ * */ -#ifndef RSS_PLATFORM_API_H -#define RSS_PLATFORM_API_H +#ifndef RSE_PLATFORM_API_H +#define RSE_PLATFORM_API_H #include <stdint.h> #include "psa/error.h" -#include <rss_crypto_defs.h> +#include <rse_crypto_defs.h> -#define RSS_PLATFORM_API_ID_NV_READ (1010) -#define RSS_PLATFORM_API_ID_NV_INCREMENT (1011) +#define RSE_PLATFORM_API_ID_NV_READ (1010) +#define RSE_PLATFORM_API_ID_NV_INCREMENT (1011) /* * Increments the given non-volatile (NV) counter by one @@ -25,7 +25,7 @@ * it returns a PSA_ERROR. */ psa_status_t -rss_platform_nv_counter_increment(uint32_t counter_id); +rse_platform_nv_counter_increment(uint32_t counter_id); /* * Reads the given non-volatile (NV) counter @@ -39,7 +39,7 @@ rss_platform_nv_counter_increment(uint32_t counter_id); * it returns a PSA_ERROR. */ psa_status_t -rss_platform_nv_counter_read(uint32_t counter_id, +rse_platform_nv_counter_read(uint32_t counter_id, uint32_t size, uint8_t *val); /* @@ -54,7 +54,7 @@ rss_platform_nv_counter_read(uint32_t counter_id, * it returns a PSA_ERROR. */ psa_status_t -rss_platform_key_read(enum rss_key_id_builtin_t key, uint8_t *data, +rse_platform_key_read(enum rse_key_id_builtin_t key, uint8_t *data, size_t data_size, size_t *data_length); -#endif /* RSS_PLATFORM_API_H */ +#endif /* RSE_PLATFORM_API_H */ diff --git a/include/lib/psa/rss_crypto_defs.h b/include/lib/psa/rss_crypto_defs.h deleted file mode 100644 index b8c74268..00000000 --- a/include/lib/psa/rss_crypto_defs.h +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright (c) 2023, Arm Limited. All rights reserved. - * - * SPDX-License-Identifier: BSD-3-Clause - * - */ - -#ifndef RSS_CRYPTO_DEFS_H -#define RSS_CRYPTO_DEFS_H - -/* Declares types that encode errors, algorithms, key types, policies, etc. */ -#include "psa/crypto_types.h" - -/* - * Value identifying export public key function API, used to dispatch the request - * to the corresponding API implementation in the Crypto service backend. - * - */ -#define RSS_CRYPTO_EXPORT_PUBLIC_KEY_SID (uint16_t)(0x701) - -/* - * The persistent key identifiers for RSS builtin keys. - */ -enum rss_key_id_builtin_t { - RSS_BUILTIN_KEY_ID_HOST_S_ROTPK = 0x7FFF816Cu, - RSS_BUILTIN_KEY_ID_HOST_NS_ROTPK, - RSS_BUILTIN_KEY_ID_HOST_CCA_ROTPK, -}; - -/* - * This type is used to overcome a limitation within RSS firmware in the number of maximum - * IOVECs it can use especially in psa_aead_encrypt and psa_aead_decrypt. - */ -#define RSS_CRYPTO_MAX_NONCE_LENGTH (16u) -struct rss_crypto_aead_pack_input { - uint8_t nonce[RSS_CRYPTO_MAX_NONCE_LENGTH]; - uint32_t nonce_length; -}; - -/* - * Structure used to pack non-pointer types in a call - */ -struct rss_crypto_pack_iovec { - psa_key_id_t key_id; /* Key id */ - psa_algorithm_t alg; /* Algorithm */ - uint32_t op_handle; /* Frontend context handle associated - to a multipart operation */ - uint32_t capacity; /* Key derivation capacity */ - uint32_t ad_length; /* Additional Data length for multipart AEAD */ - uint32_t plaintext_length; /* Plaintext length for multipart AEAD */ - struct rss_crypto_aead_pack_input aead_in; /* Packs AEAD-related inputs */ - uint16_t function_id; /* Used to identify the function in the API dispatcher - to the service backend. See rss_crypto_func_sid for - detail */ - uint16_t step; /* Key derivation step */ -}; - -#endif /* RSS_CRYPTO_DEFS_H */ diff --git a/include/lib/psci/psci_lib.h b/include/lib/psci/psci_lib.h index 4b244ec3..c50f8cbb 100644 --- a/include/lib/psci/psci_lib.h +++ b/include/lib/psci/psci_lib.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2022, Arm Limited and Contributors. All rights reserved. + * Copyright (c) 2017-2023, Arm Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -94,6 +94,7 @@ int psci_stop_other_cores(unsigned int wait_ms, bool psci_is_last_on_cpu_safe(void); bool psci_are_all_cpus_on_safe(void); void psci_pwrdown_cpu(unsigned int power_level); +void psci_do_manage_extensions(void); #endif /* __ASSEMBLER__ */ diff --git a/include/lib/smccc.h b/include/lib/smccc.h index 8fd60931..775c2b21 100644 --- a/include/lib/smccc.h +++ b/include/lib/smccc.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016-2023, Arm Limited and Contributors. All rights reserved. + * Copyright (c) 2016-2024, Arm Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -20,7 +20,7 @@ SMCCC_VERSION_MINOR_SHIFT)) #define SMCCC_MAJOR_VERSION U(1) -#define SMCCC_MINOR_VERSION U(4) +#define SMCCC_MINOR_VERSION U(5) /******************************************************************************* * Bit definitions inside the function id as per the SMC calling convention @@ -95,6 +95,8 @@ #define OEN_STD_HYP_END U(5) #define OEN_VEN_HYP_START U(6) /* Vendor Hypervisor Service calls */ #define OEN_VEN_HYP_END U(6) +#define OEN_VEN_EL3_START U(7) /* Vendor Specific EL3 Monitor Calls */ +#define OEN_VEN_EL3_END U(7) #define OEN_TAP_START U(48) /* Trusted Applications */ #define OEN_TAP_END U(49) #define OEN_TOS_START U(50) /* Trusted OS */ @@ -111,6 +113,8 @@ #define SMC_OK ULL(0) #define SMC_UNK -1 #define SMC_PREEMPTED -2 /* Not defined by the SMCCC */ +#define SMC_DENIED -3 /* Not defined by the SMCCC */ +#define SMC_INVALID_PARAM -4 /* Not defined by the SMCCC */ /* Return codes for Arm Architecture Service SMC calls */ #define SMC_ARCH_CALL_SUCCESS 0 diff --git a/include/lib/spinlock.h b/include/lib/spinlock.h index 9fd3fc65..055a911d 100644 --- a/include/lib/spinlock.h +++ b/include/lib/spinlock.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013-2018, Arm Limited and Contributors. All rights reserved. + * Copyright (c) 2013-2024, Arm Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -15,15 +15,21 @@ typedef struct spinlock { volatile uint32_t lock; } spinlock_t; +typedef struct bitlock { + volatile uint8_t lock; +} bitlock_t; + void spin_lock(spinlock_t *lock); void spin_unlock(spinlock_t *lock); +void bit_lock(bitlock_t *lock, uint8_t mask); +void bit_unlock(bitlock_t *lock, uint8_t mask); + #else /* Spin lock definitions for use in assembly */ #define SPINLOCK_ASM_ALIGN 2 #define SPINLOCK_ASM_SIZE 4 -#endif - +#endif /* __ASSEMBLER__ */ #endif /* SPINLOCK_H */ diff --git a/include/lib/transfer_list.h b/include/lib/transfer_list.h index 54c86434..1b5ec2d6 100644 --- a/include/lib/transfer_list.h +++ b/include/lib/transfer_list.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023, Linaro Limited and Contributors. All rights reserved. + * Copyright (c) 2023-2024, Linaro Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -10,47 +10,76 @@ #include <stdbool.h> #include <stdint.h> +#include <common/ep_info.h> #include <lib/utils_def.h> -#define TRANSFER_LIST_SIGNATURE U(0x006ed0ff) -#define TRANSFER_LIST_VERSION U(0x0001) +#define TRANSFER_LIST_SIGNATURE U(0x4a0fb10b) +#define TRANSFER_LIST_VERSION U(0x0001) -// Init value of maximum alignment required by any TE data in the TL -// specified as a power of two -#define TRANSFER_LIST_INIT_MAX_ALIGN U(3) +/* + * Init value of maximum alignment required by any TE data in the TL + * specified as a power of two + */ +#define TRANSFER_LIST_INIT_MAX_ALIGN U(3) -// alignment required by TE header start address, in bytes -#define TRANSFER_LIST_GRANULE U(8) +/* Alignment required by TE header start address, in bytes */ +#define TRANSFER_LIST_GRANULE U(8) -// version of the register convention used. -// Set to 1 for both AArch64 and AArch32 according to fw handoff spec v0.9 -#define REGISTER_CONVENTION_VERSION_MASK (1 << 24) +/* + * Version of the register convention used. + * Set to 1 for both AArch64 and AArch32 according to fw handoff spec v0.9 + */ +#define REGISTER_CONVENTION_VERSION_SHIFT_64 UL(32) +#define REGISTER_CONVENTION_VERSION_SHIFT_32 UL(24) +#define REGISTER_CONVENTION_VERSION_MASK UL(0xff) +#define REGISTER_CONVENTION_VERSION UL(1) + +#define TRANSFER_LIST_HANDOFF_X1_VALUE(__version) \ + ((TRANSFER_LIST_SIGNATURE & \ + ((1UL << REGISTER_CONVENTION_VERSION_SHIFT_64) - 1)) | \ + (((__version) & REGISTER_CONVENTION_VERSION_MASK) << \ + REGISTER_CONVENTION_VERSION_SHIFT_64)) + +#define TRANSFER_LIST_HANDOFF_R1_VALUE(__version) \ + ((TRANSFER_LIST_SIGNATURE & \ + ((1UL << REGISTER_CONVENTION_VERSION_SHIFT_32) - 1)) | \ + (((__version) & REGISTER_CONVENTION_VERSION_MASK) << \ + REGISTER_CONVENTION_VERSION_SHIFT_32)) #ifndef __ASSEMBLER__ +#define TL_FLAGS_HAS_CHECKSUM BIT(0) + enum transfer_list_tag_id { TL_TAG_EMPTY = 0, TL_TAG_FDT = 1, TL_TAG_HOB_BLOCK = 2, TL_TAG_HOB_LIST = 3, TL_TAG_ACPI_TABLE_AGGREGATE = 4, + TL_TAG_OPTEE_PAGABLE_PART = 0x100, + TL_TAG_DT_SPMC_MANIFEST = 0x101, + TL_TAG_EXEC_EP_INFO64 = 0x102, + TL_TAG_TB_FW_CONFIG = 0x103, + TL_TAG_SRAM_LAYOUT64 = 0x104, }; enum transfer_list_ops { - TL_OPS_NON, // invalid for any operation - TL_OPS_ALL, // valid for all operations - TL_OPS_RO, // valid for read only - TL_OPS_CUS, // either abort or switch to special code to interpret + TL_OPS_NON, /* invalid for any operation */ + TL_OPS_ALL, /* valid for all operations */ + TL_OPS_RO, /* valid for read only */ + TL_OPS_CUS, /* abort or switch to special code to interpret */ }; struct transfer_list_header { - uint32_t signature; - uint8_t checksum; - uint8_t version; - uint8_t hdr_size; - uint8_t alignment; // max alignment of TE data - uint32_t size; // TL header + all TEs - uint32_t max_size; + uint32_t signature; + uint8_t checksum; + uint8_t version; + uint8_t hdr_size; + uint8_t alignment; /* max alignment of TE data */ + uint32_t size; /* TL header + all TEs */ + uint32_t max_size; + uint32_t flags; + uint32_t reserved; /* spare bytes */ /* * Commented out element used to visualize dynamic part of the * data structure. @@ -63,11 +92,10 @@ struct transfer_list_header { */ }; -struct transfer_list_entry { - uint16_t tag_id; - uint8_t reserved0; // place holder - uint8_t hdr_size; - uint32_t data_size; +struct __attribute__((packed)) transfer_list_entry { + uint32_t tag_id : 24; + uint8_t hdr_size; + uint32_t data_size; /* * Commented out element used to visualize dynamic part of the * data structure. @@ -79,12 +107,19 @@ struct transfer_list_entry { */ }; +CASSERT(sizeof(struct transfer_list_entry) == U(0x8), assert_transfer_list_entry_size); + void transfer_list_dump(struct transfer_list_header *tl); +entry_point_info_t * +transfer_list_set_handoff_args(struct transfer_list_header *tl, + entry_point_info_t *ep_info); struct transfer_list_header *transfer_list_init(void *addr, size_t max_size); -struct transfer_list_header *transfer_list_relocate(struct transfer_list_header *tl, - void *addr, size_t max_size); -enum transfer_list_ops transfer_list_check_header(const struct transfer_list_header *tl); +struct transfer_list_header * +transfer_list_relocate(struct transfer_list_header *tl, void *addr, + size_t max_size); +enum transfer_list_ops +transfer_list_check_header(const struct transfer_list_header *tl); void transfer_list_update_checksum(struct transfer_list_header *tl); bool transfer_list_verify_checksum(const struct transfer_list_header *tl); @@ -94,21 +129,25 @@ bool transfer_list_set_data_size(struct transfer_list_header *tl, uint32_t new_data_size); void *transfer_list_entry_data(struct transfer_list_entry *entry); -bool transfer_list_rem(struct transfer_list_header *tl, struct transfer_list_entry *entry); +bool transfer_list_rem(struct transfer_list_header *tl, + struct transfer_list_entry *entry); struct transfer_list_entry *transfer_list_add(struct transfer_list_header *tl, - uint16_t tag_id, uint32_t data_size, + uint32_t tag_id, + uint32_t data_size, const void *data); -struct transfer_list_entry *transfer_list_add_with_align(struct transfer_list_header *tl, - uint16_t tag_id, uint32_t data_size, - const void *data, uint8_t alignment); +struct transfer_list_entry * +transfer_list_add_with_align(struct transfer_list_header *tl, uint32_t tag_id, + uint32_t data_size, const void *data, + uint8_t alignment); -struct transfer_list_entry *transfer_list_next(struct transfer_list_header *tl, - struct transfer_list_entry *last); +struct transfer_list_entry * +transfer_list_next(struct transfer_list_header *tl, + struct transfer_list_entry *last); struct transfer_list_entry *transfer_list_find(struct transfer_list_header *tl, - uint16_t tag_id); + uint32_t tag_id); #endif /*__ASSEMBLER__*/ #endif /*__TRANSFER_LIST_H*/ diff --git a/include/lib/utils_def.h b/include/lib/utils_def.h index a170a09d..c3f767ee 100644 --- a/include/lib/utils_def.h +++ b/include/lib/utils_def.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016-2023, Arm Limited and Contributors. All rights reserved. + * Copyright (c) 2016-2024, Arm Limited and Contributors. All rights reserved. * Copyright (c) 2020, NVIDIA Corporation. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause @@ -19,8 +19,13 @@ #define SIZE_FROM_LOG2_WORDS(n) (U(4) << (n)) +#if defined(__LINKER__) || defined(__ASSEMBLER__) #define BIT_32(nr) (U(1) << (nr)) #define BIT_64(nr) (ULL(1) << (nr)) +#else +#define BIT_32(nr) (((uint32_t)(1U)) << (nr)) +#define BIT_64(nr) (((uint64_t)(1ULL)) << (nr)) +#endif #ifdef __aarch64__ #define BIT BIT_64 @@ -29,22 +34,22 @@ #endif /* - * Create a contiguous bitmask starting at bit position @l and ending at - * position @h. For example + * Create a contiguous bitmask starting at bit position @low and ending at + * position @high. For example * GENMASK_64(39, 21) gives us the 64bit vector 0x000000ffffe00000. */ #if defined(__LINKER__) || defined(__ASSEMBLER__) -#define GENMASK_32(h, l) \ - (((0xFFFFFFFF) << (l)) & (0xFFFFFFFF >> (32 - 1 - (h)))) +#define GENMASK_32(high, low) \ + (((0xFFFFFFFF) << (low)) & (0xFFFFFFFF >> (32 - 1 - (high)))) -#define GENMASK_64(h, l) \ - ((~0 << (l)) & (~0 >> (64 - 1 - (h)))) +#define GENMASK_64(high, low) \ + ((~0 << (low)) & (~0 >> (64 - 1 - (high)))) #else -#define GENMASK_32(h, l) \ - (((~UINT32_C(0)) << (l)) & (~UINT32_C(0) >> (32 - 1 - (h)))) +#define GENMASK_32(high, low) \ + ((~UINT32_C(0) >> (32U - 1U - (high))) ^ ((BIT_32(low) - 1U))) -#define GENMASK_64(h, l) \ - (((~UINT64_C(0)) << (l)) & (~UINT64_C(0) >> (64 - 1 - (h)))) +#define GENMASK_64(high, low) \ + ((~UINT64_C(0) >> (64U - 1U - (high))) ^ ((BIT_64(low) - 1U))) #endif #ifdef __aarch64__ @@ -53,6 +58,9 @@ #define GENMASK GENMASK_32 #endif +#define HI(addr) (addr >> 32) +#define LO(addr) (addr & 0xffffffff) + /* * This variant of div_round_up can be used in macro definition but should not * be used in C code as the `div` parameter is evaluated twice. diff --git a/include/lib/xlat_tables/xlat_tables_defs.h b/include/lib/xlat_tables/xlat_tables_defs.h index 2d0949b5..5434a9a2 100644 --- a/include/lib/xlat_tables/xlat_tables_defs.h +++ b/include/lib/xlat_tables/xlat_tables_defs.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2021, Arm Limited and Contributors. All rights reserved. + * Copyright (c) 2017-2024, Arm Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -171,8 +171,10 @@ #define SHAREABILITY_SHIFT 8 /* The Access Flag, AF. */ #define ACCESS_FLAG_SHIFT 10 -/* The not global bit, nG. */ +/* The not global bit, nG */ #define NOT_GLOBAL_SHIFT 11 +/* The Non-secure Extension bit, NSE */ +#define NSE_SHIFT 11 /* Contiguous hint bit. */ #define CONT_HINT_SHIFT 52 /* Execute-never bits, XN. */ diff --git a/include/plat/arm/board/common/board_css_def.h b/include/plat/arm/board/common/board_css_def.h index 1963bf0c..3bb68ee3 100644 --- a/include/plat/arm/board/common/board_css_def.h +++ b/include/plat/arm/board/common/board_css_def.h @@ -67,9 +67,6 @@ #define PLAT_ARM_RUN_UART_BASE SOC_CSS_UART1_BASE #define PLAT_ARM_RUN_UART_CLK_IN_HZ SOC_CSS_UART1_CLK_IN_HZ -#define PLAT_ARM_SP_MIN_RUN_UART_BASE SOC_CSS_UART1_BASE -#define PLAT_ARM_SP_MIN_RUN_UART_CLK_IN_HZ SOC_CSS_UART1_CLK_IN_HZ - #define PLAT_ARM_CRASH_UART_BASE PLAT_ARM_RUN_UART_BASE #define PLAT_ARM_CRASH_UART_CLK_IN_HZ PLAT_ARM_RUN_UART_CLK_IN_HZ diff --git a/include/plat/arm/board/common/rotpk/rotpk_def.h b/include/plat/arm/board/common/rotpk/rotpk_def.h new file mode 100644 index 00000000..685c21a6 --- /dev/null +++ b/include/plat/arm/board/common/rotpk/rotpk_def.h @@ -0,0 +1,24 @@ + +/* + * Copyright (c) 2024, ARM Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef ROTPK_DEF_H +#define ROTPK_DEF_H + +/* + * Definitions related to ROTPK + */ + +/* + * Root of trust key lengths + */ +#ifndef ARM_ROTPK_HEADER_LEN +#define ARM_ROTPK_HEADER_LEN 19 +#endif +#ifndef ARM_ROTPK_HASH_LEN +#define ARM_ROTPK_HASH_LEN 32 +#endif +#endif /* ROTPK_DEF_H */ diff --git a/include/plat/arm/board/common/v2m_def.h b/include/plat/arm/board/common/v2m_def.h index cb11dac4..43a77e3c 100644 --- a/include/plat/arm/board/common/v2m_def.h +++ b/include/plat/arm/board/common/v2m_def.h @@ -17,6 +17,7 @@ /* V2M motherboard system registers & offsets */ #define V2M_SYSREGS_BASE UL(0x1c010000) +#define V2M_SYSREGS_SIZE UL(0x00010000) #define V2M_SYS_ID UL(0x0) #define V2M_SYS_SWITCH UL(0x4) #define V2M_SYS_LED UL(0x8) @@ -78,6 +79,8 @@ /* NOR Flash */ #define V2M_FLASH0_BASE (V2M_OFFSET + UL(0x08000000)) #define V2M_FLASH0_SIZE UL(0x04000000) +#define V2M_FLASH1_BASE (V2M_OFFSET + UL(0x0c000000)) +#define V2M_FLASH1_SIZE UL(0x04000000) #define V2M_FLASH_BLOCK_SIZE UL(0x00040000) /* 256 KB */ #define V2M_IOFPGA_BASE (V2M_OFFSET + UL(0x1c000000)) @@ -126,6 +129,14 @@ V2M_FLASH0_SIZE, \ MT_RO_DATA | MT_SECURE) +#define V2M_MAP_FLASH1_RW MAP_REGION_FLAT(V2M_FLASH1_BASE,\ + V2M_FLASH1_SIZE, \ + MT_DEVICE | MT_RW | MT_SECURE) + +#define V2M_MAP_FLASH1_RO MAP_REGION_FLAT(V2M_FLASH1_BASE,\ + V2M_FLASH1_SIZE, \ + MT_RO_DATA | MT_SECURE) + #define V2M_MAP_IOFPGA MAP_REGION_FLAT(V2M_IOFPGA_BASE,\ V2M_IOFPGA_SIZE, \ MT_DEVICE | MT_RW | MT_SECURE) @@ -136,5 +147,19 @@ V2M_IOFPGA_SIZE, \ MT_DEVICE | MT_RW | MT_SECURE | MT_USER) +#define V2M_MAP_SECURE_SYSTEMREG_EL0 MAP_REGION_FLAT( \ + V2M_SYSREGS_BASE, \ + V2M_SYSREGS_SIZE, \ + MT_DEVICE | MT_RW | MT_SECURE | MT_USER) + +#define V2M_MAP_FLASH0_RW_EL0 MAP_REGION_FLAT( \ + V2M_FLASH0_BASE, \ + V2M_FLASH0_SIZE, \ + MT_DEVICE | MT_RW | MT_SECURE | MT_USER) + +#define V2M_MAP_FLASH1_RW_EL0 MAP_REGION_FLAT( \ + V2M_FLASH1_BASE, \ + V2M_FLASH1_SIZE, \ + MT_DEVICE | MT_RW | MT_SECURE | MT_USER) #endif /* V2M_DEF_H */ diff --git a/include/plat/arm/common/arm_def.h b/include/plat/arm/common/arm_def.h index e098c10b..ec5f90bf 100644 --- a/include/plat/arm/common/arm_def.h +++ b/include/plat/arm/common/arm_def.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015-2023, Arm Limited and Contributors. All rights reserved. + * Copyright (c) 2015-2024, Arm Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -12,6 +12,7 @@ #include <drivers/arm/gic_common.h> #include <lib/utils_def.h> #include <lib/xlat_tables/xlat_tables_defs.h> +#include <plat/arm/board/common/rotpk/rotpk_def.h> #include <plat/arm/common/smccc_def.h> #include <plat/common/common_def.h> @@ -19,11 +20,6 @@ * Definitions common to all ARM standard platforms *****************************************************************************/ -/* - * Root of trust key lengths - */ -#define ARM_ROTPK_HEADER_LEN 19 -#define ARM_ROTPK_HASH_LEN 32 /* Special value used to verify platform parameters from BL2 to BL31 */ #define ARM_BL31_PLAT_PARAM_VAL ULL(0x0f1e2d3c4b5a6978) @@ -154,10 +150,10 @@ MEASURED_BOOT #endif /* (SPD_tspd || SPD_opteed || SPD_spmd) && MEASURED_BOOT */ #if ENABLE_RME -#define ARM_L1_GPT_ADDR_BASE (ARM_DRAM1_BASE + \ +#define ARM_L1_GPT_BASE (ARM_DRAM1_BASE + \ ARM_DRAM1_SIZE - \ ARM_L1_GPT_SIZE) -#define ARM_L1_GPT_END (ARM_L1_GPT_ADDR_BASE + \ +#define ARM_L1_GPT_END (ARM_L1_GPT_BASE + \ ARM_L1_GPT_SIZE - 1U) #define ARM_REALM_BASE (ARM_EL3_RMM_SHARED_BASE - \ @@ -347,7 +343,7 @@ MEASURED_BOOT #define ARM_MAP_GPT_L1_DRAM MAP_REGION_FLAT( \ - ARM_L1_GPT_ADDR_BASE, \ + ARM_L1_GPT_BASE, \ ARM_L1_GPT_SIZE, \ MT_MEMORY | MT_RW | EL3_PAS) @@ -415,6 +411,8 @@ MEASURED_BOOT #define ARM_V2M_MAP_MEM_PROTECT MAP_REGION_FLAT(PLAT_ARM_MEM_PROT_ADDR, \ V2M_FLASH_BLOCK_SIZE, \ MT_DEVICE | MT_RW | MT_SECURE) + +#if !TRANSFER_LIST /* * Map the region for device tree configuration with read and write permissions */ @@ -422,11 +420,13 @@ MEASURED_BOOT (ARM_FW_CONFIGS_LIMIT \ - ARM_BL_RAM_BASE), \ MT_MEMORY | MT_RW | EL3_PAS) +#endif + /* * Map L0_GPT with read and write permissions */ #if ENABLE_RME -#define ARM_MAP_L0_GPT_REGION MAP_REGION_FLAT(ARM_L0_GPT_ADDR_BASE, \ +#define ARM_MAP_L0_GPT_REGION MAP_REGION_FLAT(ARM_L0_GPT_BASE, \ ARM_L0_GPT_SIZE, \ MT_MEMORY | MT_RW | MT_ROOT) #endif @@ -509,6 +509,14 @@ MEASURED_BOOT */ #define CACHE_WRITEBACK_GRANULE (U(1) << ARM_CACHE_WRITEBACK_SHIFT) +/* Define memory configuration for trusted boot device tree files. */ +#ifdef PLAT_ARM_TB_FW_CONFIG_SIZE +#define ARM_TB_FW_CONFIG_MAX_SIZE PLAT_ARM_TB_FW_CONFIG_SIZE +#else +#define ARM_TB_FW_CONFIG_MAX_SIZE U(0x400) +#endif + +#if !TRANSFER_LIST /* * To enable FW_CONFIG to be loaded by BL1, define the corresponding base * and limit. Leave enough space of BL2 meminfo. @@ -530,6 +538,7 @@ MEASURED_BOOT */ #define ARM_FW_CONFIGS_SIZE (PAGE_SIZE * 2) #define ARM_FW_CONFIGS_LIMIT (ARM_BL_RAM_BASE + ARM_FW_CONFIGS_SIZE) +#endif #if ENABLE_RME /* @@ -537,8 +546,8 @@ MEASURED_BOOT * configuration memory, 4KB aligned. */ #define ARM_L0_GPT_SIZE (PAGE_SIZE) -#define ARM_L0_GPT_ADDR_BASE (ARM_FW_CONFIGS_LIMIT) -#define ARM_L0_GPT_LIMIT (ARM_L0_GPT_ADDR_BASE + ARM_L0_GPT_SIZE) +#define ARM_L0_GPT_BASE (ARM_FW_CONFIGS_LIMIT) +#define ARM_L0_GPT_LIMIT (ARM_L0_GPT_BASE + ARM_L0_GPT_SIZE) #else #define ARM_L0_GPT_SIZE U(0) #endif diff --git a/include/plat/arm/common/arm_sip_svc.h b/include/plat/arm/common/arm_sip_svc.h index 266092e3..bca224d5 100644 --- a/include/plat/arm/common/arm_sip_svc.h +++ b/include/plat/arm/common/arm_sip_svc.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016-2019,2021-2023, Arm Limited and Contributors. All rights reserved. + * Copyright (c) 2016-2019,2021-2024, Arm Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -16,12 +16,14 @@ /* U(0x8200ff02) is reserved */ #define ARM_SIP_SVC_VERSION U(0x8200ff03) +/* Deprecated FID's Range and will be removed */ /* PMF_SMC_GET_TIMESTAMP_32 0x82000010 */ /* PMF_SMC_GET_TIMESTAMP_64 0xC2000010 */ /* Function ID for requesting state switch of lower EL */ #define ARM_SIP_SVC_EXE_STATE_SWITCH U(0x82000020) +/* Deprecated FID's Range and will be removed */ /* DEBUGFS_SMC_32 0x82000030U */ /* DEBUGFS_SMC_64 0xC2000030U */ @@ -32,8 +34,8 @@ */ /* ARM SiP Service Calls version numbers */ -#define ARM_SIP_SVC_VERSION_MAJOR U(0x0) -#define ARM_SIP_SVC_VERSION_MINOR U(0x2) +#define ARM_SIP_SVC_VERSION_MAJOR U(0x1) +#define ARM_SIP_SVC_VERSION_MINOR U(0x0) /* * Arm SiP SMC calls that are primarily used for testing purposes. @@ -42,6 +44,16 @@ #define ARM_SIP_SET_INTERRUPT_PENDING U(0x82000100) #endif +/** + * Arm SiP Service Call for the SPM to leverage RME to protect a give memory range. + * Protected memory range is one whose PAS was made secure. + * Unprotect relates to reverting a protect operation. + */ +#if SPMD_SPM_AT_SEL2 && ENABLE_RME +#define PLAT_PROTECT_MEM_SMC64 0xC2000101 +#define PLAT_UNPROTECT_MEM_SMC64 0xC2000102 +#endif + /* SiP handler specific to each Arm platform. */ uintptr_t plat_arm_sip_handler(uint32_t smc_fid, u_register_t x1, diff --git a/include/plat/arm/common/arm_tzc_dram.ld.S b/include/plat/arm/common/arm_tzc_dram.ld.S index c790bb92..08990f61 100644 --- a/include/plat/arm/common/arm_tzc_dram.ld.S +++ b/include/plat/arm/common/arm_tzc_dram.ld.S @@ -18,6 +18,9 @@ SECTIONS ASSERT(. == ALIGN(PAGE_SIZE), "ARM_EL3_TZC_DRAM_BASE address is not aligned on a page boundary.") .el3_tzc_dram (NOLOAD) : ALIGN(PAGE_SIZE) { + __PLAT_SPMC_SHMEM_DATASTORE_START__ = .; + *(.arm_spmc_shmem_datastore) + __PLAT_SPMC_SHMEM_DATASTORE_END__ = .; __EL3_SEC_DRAM_START__ = .; *(.arm_el3_tzc_dram) __EL3_SEC_DRAM_UNALIGNED_END__ = .; diff --git a/include/plat/arm/common/fconf_arm_sp_getter.h b/include/plat/arm/common/fconf_arm_sp_getter.h index 96ed9638..d8a332ec 100644 --- a/include/plat/arm/common/fconf_arm_sp_getter.h +++ b/include/plat/arm/common/fconf_arm_sp_getter.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, Arm Limited and Contributors. All rights reserved. + * Copyright (c) 2020-2024, Arm Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -9,12 +9,17 @@ #include <common/tbbr/tbbr_img_def.h> #include <lib/fconf/fconf.h> +#include <platform_def.h> #include <tools_share/uuid.h> /* arm_sp getter */ #define arm__sp_getter(prop) arm_sp.prop +#ifdef PLAT_ARM_SP_MAX_SIZE +#define ARM_SP_MAX_SIZE PLAT_ARM_SP_MAX_SIZE +#else #define ARM_SP_MAX_SIZE U(0xb0000) +#endif /* PLAT_ARM_SP_MAX_SIZE */ #define ARM_SP_OWNER_NAME_LEN U(8) struct arm_sp_t { diff --git a/include/plat/arm/common/plat_arm.h b/include/plat/arm/common/plat_arm.h index 0fb06a66..c3756bf5 100644 --- a/include/plat/arm/common/plat_arm.h +++ b/include/plat/arm/common/plat_arm.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015-2023, Arm Limited and Contributors. All rights reserved. + * Copyright (c) 2015-2024, Arm Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -9,11 +9,14 @@ #include <stdbool.h> #include <stdint.h> +#include <common/desc_image_load.h> #include <drivers/arm/tzc_common.h> #include <lib/bakery_lock.h> #include <lib/cassert.h> #include <lib/el3_runtime/cpu_data.h> +#include <lib/gpt_rme/gpt_rme.h> #include <lib/spinlock.h> +#include <lib/transfer_list.h> #include <lib/utils_def.h> #include <lib/xlat_tables/xlat_tables_compat.h> @@ -31,6 +34,17 @@ typedef struct arm_tzc_regions_info { unsigned int nsaid_permissions; } arm_tzc_regions_info_t; +typedef struct arm_gpt_info { + pas_region_t *pas_region_base; + unsigned int pas_region_count; + uintptr_t l0_base; + uintptr_t l1_base; + size_t l0_size; + size_t l1_size; + gpccr_pps_e pps; + gpccr_pgs_e pgs; +} arm_gpt_info_t; + /******************************************************************************* * Default mapping definition of the TrustZone Controller for ARM standard * platforms. @@ -55,14 +69,16 @@ typedef struct arm_tzc_regions_info { #if SPM_MM || (SPMC_AT_EL3 && SPMC_AT_EL3_SEL0_SP) #define ARM_TZC_REGIONS_DEF \ - {ARM_AP_TZC_DRAM1_BASE, ARM_EL3_TZC_DRAM1_END + ARM_L1_GPT_SIZE,\ - TZC_REGION_S_RDWR, 0}, \ {ARM_NS_DRAM1_BASE, ARM_NS_DRAM1_END, ARM_TZC_NS_DRAM_S_ACCESS, \ PLAT_ARM_TZC_NS_DEV_ACCESS}, \ - {ARM_DRAM2_BASE, ARM_DRAM2_END, ARM_TZC_NS_DRAM_S_ACCESS, \ - PLAT_ARM_TZC_NS_DEV_ACCESS}, \ + {ARM_AP_TZC_DRAM1_BASE, (PLAT_SP_IMAGE_NS_BUF_BASE - 1), \ + TZC_REGION_S_RDWR, 0}, \ {PLAT_SP_IMAGE_NS_BUF_BASE, (PLAT_SP_IMAGE_NS_BUF_BASE + \ - PLAT_SP_IMAGE_NS_BUF_SIZE) - 1, TZC_REGION_S_NONE, \ + PLAT_SP_IMAGE_NS_BUF_SIZE - 1), TZC_REGION_S_NONE, \ + PLAT_ARM_TZC_NS_DEV_ACCESS}, \ + {PLAT_SP_IMAGE_STACK_BASE, ARM_EL3_TZC_DRAM1_END, \ + TZC_REGION_S_RDWR, 0}, \ + {ARM_DRAM2_BASE, ARM_DRAM2_END, ARM_TZC_NS_DRAM_S_ACCESS, \ PLAT_ARM_TZC_NS_DEV_ACCESS} #elif ENABLE_RME @@ -139,11 +155,10 @@ void arm_setup_romlib(void); #define ARM_LOCAL_PSTATE_WIDTH 4 #define ARM_LOCAL_PSTATE_MASK ((1 << ARM_LOCAL_PSTATE_WIDTH) - 1) -#if PSCI_OS_INIT_MODE +/* Last in Level for the OS-initiated */ #define ARM_LAST_AT_PLVL_MASK (ARM_LOCAL_PSTATE_MASK << \ (ARM_LOCAL_PSTATE_WIDTH * \ (PLAT_MAX_PWR_LVL + 1))) -#endif /* __PSCI_OS_INIT_MODE__ */ /* Macros to construct the composite power state */ @@ -242,10 +257,14 @@ uint32_t arm_get_spsr_for_bl33_entry(void); int arm_bl2_plat_handle_post_image_load(unsigned int image_id); int arm_bl2_handle_post_image_load(unsigned int image_id); struct bl_params *arm_get_next_bl_params(void); +void arm_bl2_setup_next_ep_info(bl_mem_params_node_t *next_param_node); /* BL2 at EL3 functions */ void arm_bl2_el3_early_platform_setup(void); void arm_bl2_el3_plat_arch_setup(void); +#if ARM_FW_CONFIG_LOAD_ENABLE +void arm_bl2_el3_plat_config_load(void); +#endif /* ARM_FW_CONFIG_LOAD_ENABLE */ /* BL2U utility functions */ void arm_bl2u_early_platform_setup(struct meminfo *mem_layout, @@ -254,12 +273,22 @@ void arm_bl2u_platform_setup(void); void arm_bl2u_plat_arch_setup(void); /* BL31 utility functions */ +#if TRANSFER_LIST +void arm_bl31_early_platform_setup(u_register_t arg0, u_register_t arg1, + u_register_t arg2, u_register_t arg3); +#else void arm_bl31_early_platform_setup(void *from_bl2, uintptr_t soc_fw_config, uintptr_t hw_config, void *plat_params_from_bl2); +#endif void arm_bl31_platform_setup(void); void arm_bl31_plat_runtime_setup(void); void arm_bl31_plat_arch_setup(void); +/* Firmware Handoff utility functions */ +void arm_transfer_list_dyn_cfg_init(struct transfer_list_header *secure_tl); +void arm_transfer_list_populate_ep_info(bl_mem_params_node_t *next_param_node, + struct transfer_list_header *secure_tl); + /* TSP utility functions */ void arm_tsp_early_platform_setup(void); @@ -273,11 +302,21 @@ void arm_sp_min_plat_arch_setup(void); bool arm_io_is_toc_valid(void); /* Utility functions for Dynamic Config */ -void arm_bl2_dyn_cfg_init(void); + void arm_bl1_set_mbedtls_heap(void); int arm_get_mbedtls_heap(void **heap_addr, size_t *heap_size); +#if IMAGE_BL2 +void arm_bl2_dyn_cfg_init(void); +#endif /* IMAGE_BL2 */ + #if MEASURED_BOOT +#if DICE_PROTECTION_ENVIRONMENT +int arm_set_nt_fw_info(int *ctx_handle); +int arm_set_tb_fw_info(int *ctx_handle); +int arm_get_tb_fw_info(int *ctx_handle); +#else +/* Specific to event log backend */ int arm_set_tos_fw_info(uintptr_t log_addr, size_t log_size); int arm_set_nt_fw_info( /* @@ -292,6 +331,7 @@ int arm_set_tb_fw_info(uintptr_t log_addr, size_t log_size, size_t log_max_size); int arm_get_tb_fw_info(uint64_t *log_addr, size_t *log_size, size_t *log_max_size); +#endif /* DICE_PROTECTION_ENVIRONMENT */ #endif /* MEASURED_BOOT */ /* @@ -325,6 +365,7 @@ void plat_arm_interconnect_enter_coherency(void); void plat_arm_interconnect_exit_coherency(void); void plat_arm_program_trusted_mailbox(uintptr_t address); bool plat_arm_bl1_fwu_needed(void); +int plat_arm_ni_setup(uintptr_t global_cfg); __dead2 void plat_arm_error_handler(int err); __dead2 void plat_arm_system_reset(void); @@ -345,6 +386,8 @@ int arm_get_rotpk_info_dev(void **key_ptr, unsigned int *key_len, unsigned int plat_arm_get_cpu_pe_count(u_register_t mpidr); #endif +unsigned int plat_cluster_id_by_mpidr(u_register_t mpidr); + /* * This function is called after loading SCP_BL2 image and it is used to perform * any platform-specific actions required to handle the SCP firmware. @@ -362,6 +405,9 @@ int plat_arm_get_alt_image_source( unsigned int plat_arm_calc_core_pos(u_register_t mpidr); const mmap_region_t *plat_arm_get_mmap(void); +const arm_gpt_info_t *plat_arm_get_gpt_info(void); +void arm_gpt_setup(void); + /* Allow platform to override psci_pm_ops during runtime */ const plat_psci_ops_t *plat_arm_psci_override_pm_ops(plat_psci_ops_t *ops); diff --git a/include/plat/arm/css/common/css_def.h b/include/plat/arm/css/common/css_def.h index f87f857c..6203937e 100644 --- a/include/plat/arm/css/common/css_def.h +++ b/include/plat/arm/css/common/css_def.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015-2023, Arm Limited and Contributors. All rights reserved. + * Copyright (c) 2015-2024, Arm Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -58,12 +58,12 @@ INTR_PROP_DESC(CSS_IRQ_GPU_SMMU_0, GIC_HIGHEST_SEC_PRIORITY, grp, \ GIC_INTR_CFG_LEVEL), \ INTR_PROP_DESC(CSS_IRQ_TZC, GIC_HIGHEST_SEC_PRIORITY, grp, \ - GIC_INTR_CFG_LEVEL), \ - INTR_PROP_DESC(CSS_IRQ_SEC_SYS_TIMER, GIC_HIGHEST_SEC_PRIORITY, grp, \ GIC_INTR_CFG_LEVEL) #define CSS_G1S_IRQ_PROPS(grp) \ CSS_G1S_INT_PROPS(grp), \ + INTR_PROP_DESC(CSS_IRQ_SEC_SYS_TIMER, GIC_HIGHEST_SEC_PRIORITY, grp, \ + GIC_INTR_CFG_LEVEL), \ INTR_PROP_DESC(CSS_IRQ_TZ_WDOG, GIC_HIGHEST_SEC_PRIORITY, grp, \ GIC_INTR_CFG_LEVEL) @@ -75,6 +75,7 @@ * The SCMI Channel is placed right after the SDS region */ #define CSS_SCMI_PAYLOAD_BASE (PLAT_ARM_SDS_MEM_BASE + PLAT_ARM_SDS_MEM_SIZE_MAX) +#define CSS_SCMI_PAYLOAD_SIZE_MAX 0x100 /* 2x128 bytes for bidirectional communication */ #define CSS_SCMI_MHU_DB_REG_OFF MHU_CPU_INTR_S_SET_OFFSET /* Trusted mailbox base address common to all CSS */ diff --git a/include/plat/common/common_def.h b/include/plat/common/common_def.h index 1d3ac15f..ecec5bc9 100644 --- a/include/plat/common/common_def.h +++ b/include/plat/common/common_def.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015-2022, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2015-2024, Arm Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -12,6 +12,38 @@ #include <platform_def.h> +#ifdef __aarch64__ +#define SZ_32 UL(0x00000020) +#define SZ_64 UL(0x00000040) +#define SZ_128 UL(0x00000080) +#define SZ_256 UL(0x00000100) +#define SZ_512 UL(0x00000200) + +#define SZ_1K UL(0x00000400) +#define SZ_2K UL(0x00000800) +#define SZ_4K UL(0x00001000) +#define SZ_8K UL(0x00002000) +#define SZ_16K UL(0x00004000) +#define SZ_32K UL(0x00008000) +#define SZ_64K UL(0x00010000) +#define SZ_128K UL(0x00020000) +#define SZ_256K UL(0x00040000) +#define SZ_512K UL(0x00080000) + +#define SZ_1M UL(0x00100000) +#define SZ_2M UL(0x00200000) +#define SZ_4M UL(0x00400000) +#define SZ_8M UL(0x00800000) +#define SZ_16M UL(0x01000000) +#define SZ_32M UL(0x02000000) +#define SZ_64M UL(0x04000000) +#define SZ_128M UL(0x08000000) +#define SZ_256M UL(0x10000000) +#define SZ_512M UL(0x20000000) + +#define SZ_1G UL(0x40000000) +#define SZ_2G UL(0x80000000) +#else /* !__aarch64__ */ #define SZ_32 U(0x00000020) #define SZ_64 U(0x00000040) #define SZ_128 U(0x00000080) @@ -42,6 +74,7 @@ #define SZ_1G U(0x40000000) #define SZ_2G U(0x80000000) +#endif /* __aarch64__ */ /****************************************************************************** * Required platform porting definitions that are expected to be common to diff --git a/include/plat/common/plat_drtm.h b/include/plat/common/plat_drtm.h index e96e7195..07545a68 100644 --- a/include/plat/common/plat_drtm.h +++ b/include/plat/common/plat_drtm.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, Arm Limited. All rights reserved. + * Copyright (c) 2022-2024, Arm Limited. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -17,7 +17,7 @@ typedef struct { typedef struct { bool tpm_based_hash_support; - uint32_t firmware_hash_algorithm; + uint16_t firmware_hash_algorithm; } plat_drtm_tpm_features_t; typedef struct { @@ -26,7 +26,7 @@ typedef struct { } __attribute__((packed)) drtm_mem_region_t; /* - * Memory region descriptor table structure as per DRTM beta0 section 3.13 + * Memory region descriptor table structure as per DRTM 1.0 section 3.13 * Table 11 MEMORY_REGION_DESCRIPTOR_TABLE */ typedef struct { diff --git a/include/plat/common/platform.h b/include/plat/common/platform.h index 4d1b1c17..118b537e 100644 --- a/include/plat/common/platform.h +++ b/include/plat/common/platform.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013-2023, Arm Limited and Contributors. All rights reserved. + * Copyright (c) 2013-2024, Arm Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -15,6 +15,7 @@ #endif #if ENABLE_RME #include <services/rmm_core_manifest.h> +#include <services/rmm_el3_token_sign.h> #endif #include <drivers/fwu/fwu_metadata.h> #if TRNG_SUPPORT @@ -40,6 +41,16 @@ struct sp_res_desc; struct rmm_manifest; enum fw_enc_status_t; +/******************************************************************************* + * Structure populated by platform specific code to export routines which + * perform load images functions, and associated pointer to platform ops + ******************************************************************************/ +struct plat_try_images_ops { + int (*next_instance)(unsigned int image_id); +}; + +extern const struct plat_try_images_ops *plat_try_img_ops; + /******************************************************************************* * plat_get_rotpk_info() flags ******************************************************************************/ @@ -136,6 +147,7 @@ void plat_ic_set_spi_routing(unsigned int id, unsigned int routing_mode, void plat_ic_set_interrupt_pending(unsigned int id); void plat_ic_clear_interrupt_pending(unsigned int id); unsigned int plat_ic_set_priority_mask(unsigned int mask); +unsigned int plat_ic_deactivate_priority(unsigned int mask); unsigned int plat_ic_get_interrupt_id(unsigned int raw); /******************************************************************************* @@ -153,7 +165,7 @@ void plat_panic_handler(void) __dead2; void plat_system_reset(void) __dead2; const char *plat_log_get_prefix(unsigned int log_level); void bl2_plat_preload_setup(void); -int plat_try_next_boot_source(void); +void plat_setup_try_img_ops(const struct plat_try_images_ops *plat_try_ops); #if MEASURED_BOOT int plat_mboot_measure_image(unsigned int image_id, image_info_t *image_data); @@ -183,6 +195,14 @@ static inline int plat_mboot_measure_key(const void *pk_oid __unused, } #endif /* MEASURED_BOOT */ +#if EARLY_CONSOLE +void plat_setup_early_console(void); +#else +static inline void plat_setup_early_console(void) +{ +} +#endif /* EARLY_CONSOLE */ + /******************************************************************************* * Mandatory BL1 functions ******************************************************************************/ @@ -242,6 +262,10 @@ __dead2 void bl1_plat_fwu_done(void *client_cookie, void *reserved); int bl1_plat_handle_pre_image_load(unsigned int image_id); int bl1_plat_handle_post_image_load(unsigned int image_id); +/* Utility functions */ +void bl1_plat_calc_bl2_layout(const meminfo_t *bl1_mem_layout, + meminfo_t *bl2_mem_layout); + #if MEASURED_BOOT void bl1_plat_mboot_init(void); void bl1_plat_mboot_finish(void); @@ -252,7 +276,7 @@ static inline void bl1_plat_mboot_init(void) static inline void bl1_plat_mboot_finish(void) { } -#endif /* MEASURED_BOOT */ +#endif /* MEASURED_BOOT || DICE_PROTECTION_ENVIRONMENT */ /******************************************************************************* * Mandatory BL2 functions @@ -272,7 +296,7 @@ int bl2_plat_handle_post_image_load(unsigned int image_id); /******************************************************************************* * Optional BL2 functions (may be overridden) ******************************************************************************/ -#if MEASURED_BOOT +#if (MEASURED_BOOT || DICE_PROTECTION_ENVIRONMENT) void bl2_plat_mboot_init(void); void bl2_plat_mboot_finish(void); #else @@ -282,7 +306,7 @@ static inline void bl2_plat_mboot_init(void) static inline void bl2_plat_mboot_finish(void) { } -#endif /* MEASURED_BOOT */ +#endif /* MEASURED_BOOT || DICE_PROTECTION_ENVIRONMENTs */ /******************************************************************************* * Mandatory BL2 at EL3 functions: Must be implemented @@ -347,10 +371,21 @@ plat_local_state_t plat_get_target_pwr_state(unsigned int lvl, * Mandatory BL31 functions when ENABLE_RME=1 ******************************************************************************/ #if ENABLE_RME + int plat_rmmd_get_cca_attest_token(uintptr_t buf, size_t *len, - uintptr_t hash, size_t hash_size); + uintptr_t hash, size_t hash_size, + uint64_t *remaining_len); int plat_rmmd_get_cca_realm_attest_key(uintptr_t buf, size_t *len, unsigned int type); +/* The following 3 functions are to be implement if + * RMMD_ENABLE_EL3_TOKEN_SIGN=1. + * The following three functions are expected to return E_RMM_* error codes. + */ +int plat_rmmd_el3_token_sign_get_rak_pub(uintptr_t buf, size_t *len, + unsigned int type); +int plat_rmmd_el3_token_sign_push_req( + const struct el3_token_sign_request *req); +int plat_rmmd_el3_token_sign_pull_resp(struct el3_token_sign_response *resp); size_t plat_rmmd_get_el3_rmm_shared_mem(uintptr_t *shared); int plat_rmmd_load_manifest(struct rmm_manifest *manifest); #endif diff --git a/include/plat/nuvoton/common/npcm845x_arm_def.h b/include/plat/nuvoton/common/npcm845x_arm_def.h index 5a44907f..df3ad243 100644 --- a/include/plat/nuvoton/common/npcm845x_arm_def.h +++ b/include/plat/nuvoton/common/npcm845x_arm_def.h @@ -149,7 +149,16 @@ ARM_AP_TZC_DRAM1_SIZE - 1U) /* Define the Access permissions for Secure peripherals to NS_DRAM */ +#if ARM_CRYPTOCELL_INTEG +/* + * Allow Secure peripheral to read NS DRAM when integrated with CryptoCell. + * This is required by CryptoCell to authenticate BL33 which is loaded + * into the Non Secure DDR. + */ +#define ARM_TZC_NS_DRAM_S_ACCESS TZC_REGION_S_RD +#else #define ARM_TZC_NS_DRAM_S_ACCESS TZC_REGION_S_NONE +#endif /* ARM_CRYPTOCELL_INTEG */ #ifdef SPD_opteed /* @@ -310,7 +319,7 @@ BL_RO_DATA_END - BL_RO_DATA_BASE, \ MT_RO_DATA | EL3_PAS) #else -#define ARM_MAP_BL_RO MAP_REGION_FLAT( \ +#define ARM_MAP_BL_RO_NOT_USED MAP_REGION_FLAT( \ BL_CODE_BASE, BL_CODE_END - BL_CODE_BASE, \ MT_CODE | EL3_PAS) #endif /* SEPARATE_CODE_AND_RODATA */ @@ -474,9 +483,9 @@ #define NEW_SRAM_ALLOCATION #ifdef NEW_SRAM_ALLOCATION - #define BL31_BASE 0x20001000 + #define BL31_BASE 0x02000000 #else - #define BL31_BASE 0x20001000 + #define BL31_BASE 0x02001000 #endif /* NEW_SRAM_ALLOCATION */ #define BL31_LIMIT BL2_BASE /* PLAT_ARM_MAX_BL31_SIZE */ @@ -502,6 +511,7 @@ * no SPD and no SPM-MM, as they are the only ones that can be used as BL32. */ #if defined(SPD_none) && !SPM_MM +#error BL32_BASE is not defined #undef BL32_BASE #endif /* SPD_none && !SPM_MM */ diff --git a/include/plat/nuvoton/common/plat_macros.S b/include/plat/nuvoton/common/plat_macros.S index 08f9feb7..549db395 100644 --- a/include/plat/nuvoton/common/plat_macros.S +++ b/include/plat/nuvoton/common/plat_macros.S @@ -41,7 +41,8 @@ arm_print_gic_regs * BL31. */ .macro plat_crash_print_regs - /* TODO */ +plat_print_gic_regs +/*print_cci_regs*/ .endm #endif /* PLAT_MACROS_S */ diff --git a/include/plat/nuvoton/npcm845x/platform_def.h b/include/plat/nuvoton/npcm845x/platform_def.h index 09da36ba..c70ef22f 100644 --- a/include/plat/nuvoton/npcm845x/platform_def.h +++ b/include/plat/nuvoton/npcm845x/platform_def.h @@ -132,11 +132,6 @@ */ #define PLAT_ARM_NS_IMAGE_BASE (ARM_DRAM1_BASE + UL(0x6208000)) -#ifdef NPCM845X_DEBUG -#define COUNTER_FREQUENCY 0x07735940 /* f/4 = 125MHz */ -#endif /* NPCM845X_DEBUG */ - -#define COUNTER_FREQUENCY 0x0EE6B280 /* f/2 = 250MHz */ #define PLAT_ARM_NSTIMER_FRAME_ID U(1) /* GIC parameters */ @@ -162,7 +157,8 @@ /* MMU entry for internal (register) space access */ #define MAP_DEVICE0 \ - MAP_REGION_FLAT(PLAT_REG_BASE, PLAT_REG_SIZE, MT_DEVICE | MT_RW | MT_NS) + MAP_REGION_FLAT(PLAT_REG_BASE, PLAT_REG_SIZE, \ + MT_DEVICE | MT_RW | MT_SECURE) #define MAP_DEVICE1 \ MAP_REGION_FLAT(DEVICE1_BASE, DEVICE1_SIZE, \ diff --git a/include/services/drtm_svc.h b/include/services/drtm_svc.h index 69b314f0..f0d3c63b 100644 --- a/include/services/drtm_svc.h +++ b/include/services/drtm_svc.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022 Arm Limited. All rights reserved. + * Copyright (c) 2022-2024 Arm Limited. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause * @@ -54,10 +54,10 @@ (((_fid) >= ARM_DRTM_SVC_VERSION) && ((_fid) <= ARM_DRTM_SVC_LOCK_TCB_HASH)) /* ARM DRTM Service Calls version numbers */ -#define ARM_DRTM_VERSION_MAJOR U(0) +#define ARM_DRTM_VERSION_MAJOR U(1) #define ARM_DRTM_VERSION_MAJOR_SHIFT 16 #define ARM_DRTM_VERSION_MAJOR_MASK U(0x7FFF) -#define ARM_DRTM_VERSION_MINOR U(1) +#define ARM_DRTM_VERSION_MINOR U(0) #define ARM_DRTM_VERSION_MINOR_SHIFT 0 #define ARM_DRTM_VERSION_MINOR_MASK U(0xFFFF) @@ -74,7 +74,7 @@ #define ARM_DRTM_FEAT_ID_MASK ULL(0xff) /* - * Definitions for DRTM features as per DRTM beta0 section 3.3, + * Definitions for DRTM features as per DRTM 1.0 section 3.3, * Table 6 DRTM_FEATURES */ #define ARM_DRTM_TPM_FEATURES_PCR_SCHEMA_SHIFT U(33) @@ -87,7 +87,7 @@ #define ARM_DRTM_TPM_FEATURES_TPM_HASH_SUPPORTED ULL(0x1) #define ARM_DRTM_TPM_FEATURES_FW_HASH_SHIFT U(0) -#define ARM_DRTM_TPM_FEATURES_FW_HASH_MASK ULL(0xFFFFFFFF) +#define ARM_DRTM_TPM_FEATURES_FW_HASH_MASK ULL(0xFFFF) #define ARM_DRTM_TPM_FEATURES_FW_HASH_SHA256 ULL(0xB) #define ARM_DRTM_TPM_FEATURES_FW_HASH_SHA384 ULL(0xC) #define ARM_DRTM_TPM_FEATURES_FW_HASH_SHA512 ULL(0xD) diff --git a/include/services/ffa_svc.h b/include/services/ffa_svc.h index de56638c..01dbea97 100644 --- a/include/services/ffa_svc.h +++ b/include/services/ffa_svc.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020-2023, Arm Limited. All rights reserved. + * Copyright (c) 2020-2024, Arm Limited. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -24,7 +24,7 @@ /* The macros below are used to identify FFA calls from the SMC function ID */ #define FFA_FNUM_MIN_VALUE U(0x60) -#define FFA_FNUM_MAX_VALUE U(0x8C) +#define FFA_FNUM_MAX_VALUE U(0x8E) #define is_ffa_fid(fid) __extension__ ({ \ __typeof__(fid) _fid = (fid); \ ((GET_SMC_NUM(_fid) >= FFA_FNUM_MIN_VALUE) && \ @@ -34,7 +34,7 @@ #define FFA_VERSION_MAJOR U(1) #define FFA_VERSION_MAJOR_SHIFT 16 #define FFA_VERSION_MAJOR_MASK U(0x7FFF) -#define FFA_VERSION_MINOR U(1) +#define FFA_VERSION_MINOR U(2) #define FFA_VERSION_MINOR_SHIFT 0 #define FFA_VERSION_MINOR_MASK U(0xFFFF) #define FFA_VERSION_BIT31_MASK U(0x1u << 31) @@ -117,8 +117,16 @@ #define FFA_FNUM_SPM_ID_GET U(0x85) #define FFA_FNUM_MSG_SEND2 U(0x86) #define FFA_FNUM_SECONDARY_EP_REGISTER U(0x87) +#define FFA_FNUM_MEM_PERM_GET U(0x88) +#define FFA_FNUM_MEM_PERM_SET U(0x89) + +/* FF-A v1.2 */ #define FFA_FNUM_PARTITION_INFO_GET_REGS U(0x8B) #define FFA_FNUM_EL3_INTR_HANDLE U(0x8C) +#define FFA_FNUM_MSG_SEND_DIRECT_REQ2 U(0x8D) +#define FFA_FNUM_MSG_SEND_DIRECT_RESP2 U(0x8E) + +#define FFA_FNUM_CONSOLE_LOG U(0x8A) /* FFA SMC32 FIDs */ #define FFA_ERROR FFA_FID(SMC_32, FFA_FNUM_ERROR) @@ -165,6 +173,9 @@ #define FFA_SPM_ID_GET FFA_FID(SMC_32, FFA_FNUM_SPM_ID_GET) #define FFA_NORMAL_WORLD_RESUME FFA_FID(SMC_32, FFA_FNUM_NORMAL_WORLD_RESUME) #define FFA_EL3_INTR_HANDLE FFA_FID(SMC_32, FFA_FNUM_EL3_INTR_HANDLE) +#define FFA_MEM_PERM_GET FFA_FID(SMC_32, FFA_FNUM_MEM_PERM_GET) +#define FFA_MEM_PERM_SET FFA_FID(SMC_32, FFA_FNUM_MEM_PERM_SET) +#define FFA_CONSOLE_LOG_SMC32 FFA_FID(SMC_32, FFA_FNUM_CONSOLE_LOG) /* FFA SMC64 FIDs */ #define FFA_ERROR_SMC64 FFA_FID(SMC_64, FFA_FNUM_ERROR) @@ -185,6 +196,11 @@ FFA_FID(SMC_64, FFA_FNUM_NOTIFICATION_INFO_GET) #define FFA_PARTITION_INFO_GET_REGS_SMC64 \ FFA_FID(SMC_64, FFA_FNUM_PARTITION_INFO_GET_REGS) +#define FFA_CONSOLE_LOG_SMC64 FFA_FID(SMC_64, FFA_FNUM_CONSOLE_LOG) +#define FFA_MSG_SEND_DIRECT_REQ2_SMC64 \ + FFA_FID(SMC_64, FFA_FNUM_MSG_SEND_DIRECT_REQ2) +#define FFA_MSG_SEND_DIRECT_RESP2_SMC64 \ + FFA_FID(SMC_64, FFA_FNUM_MSG_SEND_DIRECT_RESP2) /* * FF-A partition properties values. diff --git a/include/services/oem/chromeos/widevine_smc_handlers.h b/include/services/oem/chromeos/widevine_smc_handlers.h new file mode 100644 index 00000000..a5251d76 --- /dev/null +++ b/include/services/oem/chromeos/widevine_smc_handlers.h @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2024, The ChromiumOS Authors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef CROS_WIDEVINE_SMC_HANDLERS_H +#define CROS_WIDEVINE_SMC_HANDLERS_H + +#include <lib/smccc.h> + +/******************************************************************************* + * Defines for CrOS OEM Service queries + ******************************************************************************/ + +/* 0xC300C050 - 0xC300C05F are CrOS OEM service calls */ +#define CROS_OEM_SMC_ID 0xC050 +#define CROS_OEM_SMC_CALL_ID(func_num) \ + ((SMC_TYPE_FAST << FUNCID_TYPE_SHIFT) | \ + ((SMC_64) << FUNCID_CC_SHIFT) | (OEN_OEM_START << FUNCID_OEN_SHIFT) | \ + (CROS_OEM_SMC_ID) | ((func_num) & FUNCID_NUM_MASK)) + +enum cros_drm_set { + CROS_DRM_SET_TPM_AUTH_PUB = 0U, + CROS_DRM_SET_HARDWARE_UNIQUE_KEY = 1U, + CROS_DRM_SET_ROOT_OF_TRUST = 2U, +}; + +/******************************************************************************* + * Defines for runtime services func ids + ******************************************************************************/ + +/* Sets the TPM auth public key. The maximum size is 128 bytes. + * |x1| is the length of the data, |x2| is the physical address of the data. + */ +#define CROS_OEM_SMC_DRM_SET_TPM_AUTH_PUB_FUNC_ID \ + CROS_OEM_SMC_CALL_ID(CROS_DRM_SET_TPM_AUTH_PUB) + +/* Sets the hardware unique key. The maximum size is 32 bytes. + * |x1| is the length of the data, |x2| is the physical address of the data. + */ +#define CROS_OEM_SMC_DRM_SET_HARDWARE_UNIQUE_KEY_FUNC_ID \ + CROS_OEM_SMC_CALL_ID(CROS_DRM_SET_HARDWARE_UNIQUE_KEY) + +/* Sets the widevine root of trust. The maximum size is 32 bytes. + * |x1| is the length of the data, |x2| is the physical address of the data. + */ +#define CROS_OEM_SMC_DRM_SET_ROOT_OF_TRUST_FUNC_ID \ + CROS_OEM_SMC_CALL_ID(CROS_DRM_SET_ROOT_OF_TRUST) + +#define is_cros_oem_smc(_call_id) (((_call_id) & 0xFFF0U) == CROS_OEM_SMC_ID) + +struct cros_oem_data { + uint8_t *buffer; + const uint32_t max_length; + uint32_t length; +}; + +extern struct cros_oem_data cros_oem_tpm_auth_pk; + +extern struct cros_oem_data cros_oem_huk; + +extern struct cros_oem_data cros_oem_rot; + +#endif /* CROS_WIDEVINE_SMC_HANDLERS_H */ diff --git a/include/services/rmm_core_manifest.h b/include/services/rmm_core_manifest.h index b89de9f2..6b57267d 100644 --- a/include/services/rmm_core_manifest.h +++ b/include/services/rmm_core_manifest.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022-2023, Arm Limited. All rights reserved. + * Copyright (c) 2022-2024, Arm Limited. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -14,7 +14,9 @@ #include <lib/cassert.h> #define RMMD_MANIFEST_VERSION_MAJOR U(0) -#define RMMD_MANIFEST_VERSION_MINOR U(2) +#define RMMD_MANIFEST_VERSION_MINOR U(3) + +#define RMM_CONSOLE_MAX_NAME_LEN U(8) /* * Manifest version encoding: @@ -60,12 +62,49 @@ CASSERT(offsetof(struct ns_dram_info, banks) == 8UL, CASSERT(offsetof(struct ns_dram_info, checksum) == 16UL, rmm_manifest_checksum_unaligned); -/* Boot manifest core structure as per v0.2 */ +/* Console info structure */ +struct console_info { + uintptr_t base; /* Console base address */ + uint64_t map_pages; /* Num of pages to be mapped in RMM for the console MMIO */ + char name[RMM_CONSOLE_MAX_NAME_LEN]; /* Name of console */ + uint64_t clk_in_hz; /* UART clock (in Hz) for the console */ + uint64_t baud_rate; /* Baud rate */ + uint64_t flags; /* Additional flags RES0 */ +}; + +CASSERT(offsetof(struct console_info, base) == 0UL, + rmm_manifest_console_base_unaligned); +CASSERT(offsetof(struct console_info, map_pages) == 8UL, + rmm_manifest_console_map_pages_unaligned); +CASSERT(offsetof(struct console_info, name) == 16UL, + rmm_manifest_console_name_unaligned); +CASSERT(offsetof(struct console_info, clk_in_hz) == 24UL, + rmm_manifest_console_clk_in_hz_unaligned); +CASSERT(offsetof(struct console_info, baud_rate) == 32UL, + rmm_manifest_console_baud_rate_unaligned); +CASSERT(offsetof(struct console_info, flags) == 40UL, + rmm_manifest_console_flags_unaligned); + +struct console_list { + uint64_t num_consoles; /* Number of consoles */ + struct console_info *consoles; /* Pointer to console_info[] */ + uint64_t checksum; /* Checksum of console_list data */ +}; + +CASSERT(offsetof(struct console_list, num_consoles) == 0UL, + rmm_manifest_num_consoles); +CASSERT(offsetof(struct console_list, consoles) == 8UL, + rmm_manifest_consoles); +CASSERT(offsetof(struct console_list, checksum) == 16UL, + rmm_manifest_console_list_checksum); + +/* Boot manifest core structure as per v0.3 */ struct rmm_manifest { - uint32_t version; /* Manifest version */ - uint32_t padding; /* RES0 */ - uintptr_t plat_data; /* Manifest platform data */ - struct ns_dram_info plat_dram; /* Platform NS DRAM data */ + uint32_t version; /* Manifest version */ + uint32_t padding; /* RES0 */ + uintptr_t plat_data; /* Manifest platform data */ + struct ns_dram_info plat_dram; /* Platform NS DRAM data (v0.2) */ + struct console_list plat_console; /* Platform console list (v0.3) */ }; CASSERT(offsetof(struct rmm_manifest, version) == 0UL, @@ -74,5 +113,7 @@ CASSERT(offsetof(struct rmm_manifest, plat_data) == 8UL, rmm_manifest_plat_data_unaligned); CASSERT(offsetof(struct rmm_manifest, plat_dram) == 16UL, rmm_manifest_plat_dram_unaligned); +CASSERT(offsetof(struct rmm_manifest, plat_console) == 40UL, + rmm_manifest_plat_console_unaligned); #endif /* RMM_CORE_MANIFEST_H */ diff --git a/include/services/rmm_el3_token_sign.h b/include/services/rmm_el3_token_sign.h new file mode 100644 index 00000000..154940c4 --- /dev/null +++ b/include/services/rmm_el3_token_sign.h @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2024, NVIDIA Corporation. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef RMM_EL3_TOKEN_SIGN_H +#define RMM_EL3_TOKEN_SIGN_H + +#include <stdint.h> +#include <lib/cassert.h> +#include <services/rmmd_svc.h> + +/* + * Defines member of structure and reserves space + * for the next member with specified offset. + */ +/* cppcheck-suppress [misra-c2012-20.7] */ +#define SET_MEMBER(member, start, end) \ + union { \ + member; \ + unsigned char reserved##end[((end) - (start))]; \ + } + +#define EL3_TOKEN_RESPONSE_MAX_SIG_LEN U(512) + +struct el3_token_sign_request { + SET_MEMBER(uint32_t sig_alg_id, 0x0, 0x8); + SET_MEMBER(uint64_t rec_granule, 0x8, 0x10); + SET_MEMBER(uint64_t req_ticket, 0x10, 0x18); + SET_MEMBER(uint32_t hash_alg_id, 0x18, 0x20); + SET_MEMBER(uint8_t hash_buf[SHA512_DIGEST_SIZE], 0x20, 0x60); +}; + +CASSERT(__builtin_offsetof(struct el3_token_sign_request, sig_alg_id) == 0x0U, + assert_el3_token_sign_request_sig_alg_mismatch); +CASSERT(__builtin_offsetof(struct el3_token_sign_request, rec_granule) == 0x8U, + assert_el3_token_sign_request_rec_granule_mismatch); +CASSERT(__builtin_offsetof(struct el3_token_sign_request, req_ticket) == 0x10U, + assert_el3_token_sign_request_req_ticket_mismatch); +CASSERT(__builtin_offsetof(struct el3_token_sign_request, hash_alg_id) == 0x18U, + assert_el3_token_sign_request_hash_alg_id_mismatch); +CASSERT(__builtin_offsetof(struct el3_token_sign_request, hash_buf) == 0x20U, + assert_el3_token_sign_request_hash_buf_mismatch); + + +struct el3_token_sign_response { + SET_MEMBER(uint64_t rec_granule, 0x0, 0x8); + SET_MEMBER(uint64_t req_ticket, 0x8, 0x10); + SET_MEMBER(uint16_t sig_len, 0x10, 0x12); + SET_MEMBER(uint8_t signature_buf[EL3_TOKEN_RESPONSE_MAX_SIG_LEN], 0x12, 0x212); +}; + +CASSERT(__builtin_offsetof(struct el3_token_sign_response, rec_granule) == 0x0U, + assert_el3_token_sign_resp_rec_granule_mismatch); +CASSERT(__builtin_offsetof(struct el3_token_sign_response, req_ticket) == 0x8U, + assert_el3_token_sign_resp_req_ticket_mismatch); +CASSERT(__builtin_offsetof(struct el3_token_sign_response, sig_len) == 0x10U, + assert_el3_token_sign_resp_sig_len_mismatch); +CASSERT(__builtin_offsetof(struct el3_token_sign_response, signature_buf) == 0x12U, + assert_el3_token_sign_resp_sig_buf_mismatch); + +#endif /* RMM_EL3_TOKEN_SIGN_H */ diff --git a/include/services/rmmd_svc.h b/include/services/rmmd_svc.h index a567d285..0cc8628a 100644 --- a/include/services/rmmd_svc.h +++ b/include/services/rmmd_svc.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021-2022, Arm Limited and Contributors. All rights reserved. + * Copyright (c) 2021-2024, Arm Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -7,6 +7,7 @@ #ifndef RMMD_SVC_H #define RMMD_SVC_H +#include <common/sha_common_macros.h> #include <lib/smccc.h> #include <lib/utils_def.h> @@ -90,16 +91,12 @@ #define E_RMM_BAD_PAS -3 #define E_RMM_NOMEM -4 #define E_RMM_INVAL -5 +#define E_RMM_AGAIN -6 /* Return error codes from RMI SMCs */ #define RMI_SUCCESS 0 #define RMI_ERROR_INPUT 1 -/* Acceptable SHA sizes for Challenge object */ -#define SHA256_DIGEST_SIZE 32U -#define SHA384_DIGEST_SIZE 48U -#define SHA512_DIGEST_SIZE 64U - /* * Retrieve Realm attestation key from EL3. Only P-384 ECC curve key is * supported. The arguments to this SMC are : @@ -132,8 +129,43 @@ /* 0x1B3 */ #define RMM_ATTEST_GET_PLAT_TOKEN SMC64_RMMD_EL3_FID(U(3)) +/* Starting RMM-EL3 interface version 0.4 */ +#define RMM_EL3_FEATURES SMC64_RMMD_EL3_FID(U(4)) +#define RMM_EL3_FEAT_REG_0_IDX U(0) +/* Bit 0 of FEAT_REG_0 */ +/* 1 - the feature is present in EL3 , 0 - the feature is absent */ +#define RMM_EL3_FEAT_REG_0_EL3_TOKEN_SIGN_MASK U(0x1) + +/* + * Function codes to support attestation where EL3 is used to sign + * realm attestation tokens. In this model, the private key is not + * exposed to the RMM. + * The arguments to this SMC are: + * arg0 - Function ID. + * arg1 - Opcode, one of: + * RMM_EL3_TOKEN_SIGN_PUSH_REQ_OP, + * RMM_EL3_TOKEN_SIGN_PULL_RESP_OP, + * RMM_EL3_TOKEN_SIGN_GET_RAK_PUB_OP + * arg2 - Pointer to buffer with request/response structures, + * which is in the RMM<->EL3 shared buffer. + * arg3 - Buffer size of memory pointed by arg2. + * arg4 - ECC Curve, when opcode is RMM_EL3_TOKEN_SIGN_GET_RAK_PUB_OP + * The return arguments are: + * ret0 - Status/Error + * ret1 - Size of public key if opcode is RMM_EL3_TOKEN_SIGN_GET_RAK_PUB_OP + */ +#define RMM_EL3_TOKEN_SIGN SMC64_RMMD_EL3_FID(U(5)) + +/* Opcodes for RMM_EL3_TOKEN_SIGN */ +#define RMM_EL3_TOKEN_SIGN_PUSH_REQ_OP U(1) +#define RMM_EL3_TOKEN_SIGN_PULL_RESP_OP U(2) +#define RMM_EL3_TOKEN_SIGN_GET_RAK_PUB_OP U(3) + /* ECC Curve types for attest key generation */ -#define ATTEST_KEY_CURVE_ECC_SECP384R1 0 +#define ATTEST_KEY_CURVE_ECC_SECP384R1 U(0) + +/* Identifier for the hash algorithm used for attestation signing */ +#define EL3_TOKEN_SIGN_HASH_ALG_SHA384 U(1) /* * RMM_BOOT_COMPLETE originates on RMM when the boot finishes (either cold @@ -156,7 +188,7 @@ * Increase this when a bug is fixed, or a feature is added without * breaking compatibility. */ -#define RMM_EL3_IFC_VERSION_MINOR (U(2)) +#define RMM_EL3_IFC_VERSION_MINOR (U(4)) #define RMM_EL3_INTERFACE_VERSION \ (((RMM_EL3_IFC_VERSION_MAJOR << 16) & 0x7FFFF) | \ diff --git a/include/services/spmd_svc.h b/include/services/spmd_svc.h index 29dfdad3..95f07075 100644 --- a/include/services/spmd_svc.h +++ b/include/services/spmd_svc.h @@ -34,7 +34,8 @@ uint64_t spmd_smc_switch_state(uint32_t smc_fid, uint64_t x2, uint64_t x3, uint64_t x4, - void *handle); + void *handle, + uint64_t flags); #endif /* __ASSEMBLER__ */ #endif /* SPMD_SVC_H */ diff --git a/include/services/ven_el3_svc.h b/include/services/ven_el3_svc.h new file mode 100644 index 00000000..e030b68c --- /dev/null +++ b/include/services/ven_el3_svc.h @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2024, Arm Limited. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef VEN_EL3_SVC_H +#define VEN_EL3_SVC_H + +/* + * Function Identifier value ranges for Vendor-Specific + * EL3 Monitor Service Calls. + */ +/* VEN_EL3_SMC_32 0x87000000U */ +/* VEN_EL3_SMC_64 0xC7000000U */ + + +/* Function Identifier values of general queries */ +#define VEN_EL3_SVC_UID 0x8700ff01 +/* 0x8700ff02 is reserved */ +#define VEN_EL3_SVC_VERSION 0x8700ff03 + +#define VEN_EL3_SVC_VERSION_MAJOR 1 +#define VEN_EL3_SVC_VERSION_MINOR 0 + +/* DEBUGFS_SMC_32 0x87000010U */ +/* DEBUGFS_SMC_64 0xC7000010U */ + +/* PMF_SMC_GET_TIMESTAMP_32 0x87000020U */ +/* PMF_SMC_GET_TIMESTAMP_64 0xC7000020U */ + +#endif /* VEN_EL3_SVC_H */ diff --git a/include/tools_share/cca_oid.h b/include/tools_share/cca_oid.h index 8c53ef95..6f89c169 100644 --- a/include/tools_share/cca_oid.h +++ b/include/tools_share/cca_oid.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022-2023, Arm Limited. All rights reserved. + * Copyright (c) 2022-2024, Arm Limited. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -30,15 +30,17 @@ /* * First undef previous definitions from tbbr_oid.h. - * CCA ROTPK authenticates BL31 and its configuration image in + * CCA ROTPK authenticates BL31, SCP_BL2 and its configuration image in * CCA CoT. **/ #undef BL31_IMAGE_KEY_OID #undef SOC_FW_CONFIG_KEY_OID #undef HW_CONFIG_KEY_OID +#undef SCP_BL2_IMAGE_KEY_OID #define BL31_IMAGE_KEY_OID ZERO_OID #define SOC_FW_CONFIG_KEY_OID ZERO_OID #define HW_CONFIG_KEY_OID ZERO_OID +#define SCP_BL2_IMAGE_KEY_OID ZERO_OID #define RMM_IMAGE_KEY_OID ZERO_OID #endif /* CCA_OID_H */ diff --git a/include/tools_share/tbbr_oid.h b/include/tools_share/tbbr_oid.h index 9881d1a1..1a2e3553 100644 --- a/include/tools_share/tbbr_oid.h +++ b/include/tools_share/tbbr_oid.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015-2023, Arm Limited and Contributors. All rights reserved. + * Copyright (c) 2015-2024, Arm Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -170,6 +170,12 @@ #define SOC_FW_CONFIG_KEY_OID SOC_FW_CONTENT_CERT_PK_OID #define HW_CONFIG_KEY_OID ZERO_OID +#define SCP_BL2_IMAGE_KEY_OID SCP_FW_CONTENT_CERT_PK_OID +#define BL32_IMAGE_KEY_OID TRUSTED_OS_FW_CONTENT_CERT_PK_OID +#define TOS_FW_CONFIG_KEY_OID TRUSTED_OS_FW_CONTENT_CERT_PK_OID +#define BL33_IMAGE_KEY_OID NON_TRUSTED_FW_CONTENT_CERT_PK_OID +#define NT_FW_CONFIG_KEY_OID NON_TRUSTED_FW_CONTENT_CERT_PK_OID + #ifdef PLAT_DEF_OID #include <platform_oid.h> #endif diff --git a/lib/aarch64/cache_helpers.S b/lib/aarch64/cache_helpers.S index 314ed6ef..ff9a4e6b 100644 --- a/lib/aarch64/cache_helpers.S +++ b/lib/aarch64/cache_helpers.S @@ -9,6 +9,7 @@ .globl flush_dcache_range .globl flush_dcache_to_popa_range + .globl flush_dcache_to_popa_range_mte2 .globl clean_dcache_range .globl inv_dcache_range .globl dcsw_op_louis @@ -17,6 +18,20 @@ .globl dcsw_op_level2 .globl dcsw_op_level3 +/* Opcodes for data cache maintenance by PA instructions. */ + +/* + * sys #6, c7, c14, #1, x0 + * DC CIPAPA, X0 + */ +#define dc_cipapa_x0 0xd50e7e20 + +/* + * sys #6, c7, c14, #3, x0 + * DC CIDGPAPA, X0 + */ +#define dc_cigdpapa_x0 0xd50e7ea0 + /* * This macro can be used for implementing various data cache operations `op` */ @@ -37,6 +52,24 @@ exit_loop_\op: ret .endm +/* op: the hexadecimal instruction opcode for the cache operation */ +.macro do_dcache_maintenance_instr op + /* Exit early if size is zero */ + cbz x1, exit_loop_\op + dcache_line_size x2, x3 + sub x3, x2, #1 + bic x0, x0, x3 + add x1, x1, x0 +loop_\op: + .inst \op + add x0, x0, x2 + cmp x0, x1 + b.lo loop_\op + dsb osh +exit_loop_\op: + ret +.endm + .macro check_plat_can_cmo #if CONDITIONAL_CMO mov x3, x30 @@ -49,10 +82,11 @@ exit_loop_\op: mov x0, x2 #endif .endm - /* ------------------------------------------ - * Clean+Invalidate from base address till - * size. 'x0' = addr, 'x1' = size - * ------------------------------------------ + + /* ------------------------------------------- + * DCache Clean+Invalidate by MVA from base + * address till size. 'x0' = addr, 'x1' = size + * ------------------------------------------- */ func flush_dcache_range check_plat_can_cmo @@ -60,8 +94,8 @@ func flush_dcache_range endfunc flush_dcache_range /* ------------------------------------------ - * Clean from base address till size. - * 'x0' = addr, 'x1' = size + * DCache Clean by MVA from base address till + * size. 'x0' = addr, 'x1' = size * ------------------------------------------ */ func clean_dcache_range @@ -70,8 +104,8 @@ func clean_dcache_range endfunc clean_dcache_range /* ------------------------------------------ - * Invalidate from base address till - * size. 'x0' = addr, 'x1' = size + * DCache Invalidate by MVA from base address + * till size. 'x0' = addr, 'x1' = size * ------------------------------------------ */ func inv_dcache_range @@ -79,37 +113,36 @@ func inv_dcache_range do_dcache_maintenance_by_mva ivac endfunc inv_dcache_range - /* - * On implementations with FEAT_MTE2, - * Root firmware must issue DC_CIGDPAPA instead of DC_CIPAPA , - * in order to additionally clean and invalidate Allocation Tags - * associated with the affected locations. - * * ------------------------------------------ - * Clean+Invalidate by PA to POPA - * from base address till size. + * DCache Clean+Invalidate by PA to POPA from + * base address till size. * 'x0' = addr, 'x1' = size * ------------------------------------------ */ func flush_dcache_to_popa_range - /* Exit early if size is zero */ - cbz x1, exit_loop_dc_cipapa check_plat_can_cmo - dcache_line_size x2, x3 - sub x3, x2, #1 - bic x0, x0, x3 - add x1, x1, x0 -loop_dc_cipapa: - sys #6, c7, c14, #1, x0 /* DC CIPAPA,<Xt> */ - add x0, x0, x2 - cmp x0, x1 - b.lo loop_dc_cipapa - dsb osh -exit_loop_dc_cipapa: - ret + /* dc cipapa, x0 */ + do_dcache_maintenance_instr dc_cipapa_x0 endfunc flush_dcache_to_popa_range + /* + * ------------------------------------------ + * Clean+Invalidate by PA to POPA (MTE2) + * from base address till size. + * 'x0' = addr, 'x1' = size + * ------------------------------------------ + * On implementations with FEAT_MTE2, Root firmware must issue + * DC_CIGDPAPA instead of DC_CIPAPA, in order to additionally + * clean and invalidate Allocation Tags associated with the + * affected locations. + */ +func flush_dcache_to_popa_range_mte2 + check_plat_can_cmo + /* dc cigdpapa, x0 */ + do_dcache_maintenance_instr dc_cigdpapa_x0 +endfunc flush_dcache_to_popa_range_mte2 + /* --------------------------------------------------------------- * Data cache operations by set/way to the level specified * diff --git a/lib/aarch64/misc_helpers.S b/lib/aarch64/misc_helpers.S index f9c4bafd..93771df3 100644 --- a/lib/aarch64/misc_helpers.S +++ b/lib/aarch64/misc_helpers.S @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013-2023, Arm Limited and Contributors. All rights reserved. + * Copyright (c) 2013-2024, Arm Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -15,7 +15,6 @@ .globl zero_normalmem .globl zeromem .globl memcpy16 - .globl gpt_tlbi_by_pa_ll .globl disable_mmu_el1 .globl disable_mmu_el3 @@ -594,20 +593,3 @@ func fixup_gdt_reloc b.lo 1b ret endfunc fixup_gdt_reloc - -/* - * TODO: Currently only supports size of 4KB, - * support other sizes as well. - */ -func gpt_tlbi_by_pa_ll -#if ENABLE_ASSERTIONS - cmp x1, #PAGE_SIZE_4KB - ASM_ASSERT(eq) - tst x0, #(PAGE_SIZE_MASK) - ASM_ASSERT(eq) -#endif - lsr x0, x0, #FOUR_KB_SHIFT /* 4KB size encoding is zero */ - sys #6, c8, c4, #7, x0 /* TLBI RPALOS, <Xt> */ - dsb sy - ret -endfunc gpt_tlbi_by_pa_ll diff --git a/lib/compiler-rt/builtins/assembly.h b/lib/compiler-rt/builtins/assembly.h index 169d4968..8c42fc77 100644 --- a/lib/compiler-rt/builtins/assembly.h +++ b/lib/compiler-rt/builtins/assembly.h @@ -260,9 +260,10 @@ .globl name SEPARATOR \ SYMBOL_IS_FUNC(name) SEPARATOR \ DECLARE_SYMBOL_VISIBILITY_UNMANGLED(name) SEPARATOR \ - CFI_START SEPARATOR \ DECLARE_FUNC_ENCODING \ - name: SEPARATOR BTI_C + name: \ + SEPARATOR CFI_START \ + SEPARATOR BTI_C #define DEFINE_COMPILERRT_FUNCTION_ALIAS(name, target) \ .globl SYMBOL_NAME(name) SEPARATOR \ diff --git a/lib/compiler-rt/builtins/int_lib.h b/lib/compiler-rt/builtins/int_lib.h index 04ea2d91..f6c1b7cf 100644 --- a/lib/compiler-rt/builtins/int_lib.h +++ b/lib/compiler-rt/builtins/int_lib.h @@ -119,14 +119,14 @@ COMPILER_RT_ABI tu_int __udivmodti4(tu_int a, tu_int b, tu_int *rem); #if defined(_MSC_VER) && !defined(__clang__) #include <intrin.h> -int __inline __builtin_ctz(uint32_t value) { +static int __inline __builtin_ctz(uint32_t value) { unsigned long trailing_zero = 0; if (_BitScanForward(&trailing_zero, value)) return trailing_zero; return 32; } -int __inline __builtin_clz(uint32_t value) { +static int __inline __builtin_clz(uint32_t value) { unsigned long leading_zero = 0; if (_BitScanReverse(&leading_zero, value)) return 31 - leading_zero; @@ -134,14 +134,14 @@ int __inline __builtin_clz(uint32_t value) { } #if defined(_M_ARM) || defined(_M_X64) -int __inline __builtin_clzll(uint64_t value) { +static int __inline __builtin_clzll(uint64_t value) { unsigned long leading_zero = 0; if (_BitScanReverse64(&leading_zero, value)) return 63 - leading_zero; return 64; } #else -int __inline __builtin_clzll(uint64_t value) { +static int __inline __builtin_clzll(uint64_t value) { if (value == 0) return 64; uint32_t msh = (uint32_t)(value >> 32); @@ -154,7 +154,7 @@ int __inline __builtin_clzll(uint64_t value) { #define __builtin_clzl __builtin_clzll -bool __inline __builtin_sadd_overflow(int x, int y, int *result) { +static bool __inline __builtin_sadd_overflow(int x, int y, int *result) { if ((x < 0) != (y < 0)) { *result = x + y; return false; diff --git a/lib/compiler-rt/builtins/int_types.h b/lib/compiler-rt/builtins/int_types.h index 18bf0a7f..48862f36 100644 --- a/lib/compiler-rt/builtins/int_types.h +++ b/lib/compiler-rt/builtins/int_types.h @@ -107,8 +107,8 @@ typedef union { static __inline ti_int make_ti(di_int h, di_int l) { twords r; - r.s.high = h; - r.s.low = l; + r.s.high = (du_int)h; + r.s.low = (du_int)l; return r.all; } @@ -139,7 +139,6 @@ typedef union { udwords u; double f; } double_bits; -#endif typedef struct { #if _YUGA_LITTLE_ENDIAN @@ -190,12 +189,16 @@ typedef long double tf_float; #define CRT_LDBL_IEEE_F128 #endif #define TF_C(x) x##L -#elif __LDBL_MANT_DIG__ == 113 -// Use long double instead of __float128 if it matches the IEEE 128-bit format. +#elif __LDBL_MANT_DIG__ == 113 || \ + (__FLT_RADIX__ == 16 && __LDBL_MANT_DIG__ == 28) +// Use long double instead of __float128 if it matches the IEEE 128-bit format +// or the IBM hexadecimal format. #define CRT_LDBL_128BIT #define CRT_HAS_F128 +#if __LDBL_MANT_DIG__ == 113 #define CRT_HAS_IEEE_TF #define CRT_LDBL_IEEE_F128 +#endif typedef long double tf_float; #define TF_C(x) x##L #elif defined(__FLOAT128__) || defined(__SIZEOF_FLOAT128__) @@ -220,7 +223,6 @@ typedef union { #define CRT_HAS_TF_MODE #endif -#if CRT_HAS_FLOATING_POINT #if __STDC_VERSION__ >= 199901L typedef float _Complex Fcomplex; typedef double _Complex Dcomplex; @@ -270,5 +272,5 @@ typedef struct { #define COMPLEXTF_IMAGINARY(x) (x).imaginary #endif -#endif +#endif // CRT_HAS_FLOATING_POINT #endif // INT_TYPES_H diff --git a/lib/cpus/aarch32/aem_generic.S b/lib/cpus/aarch32/aem_generic.S index 9f45e387..f4dc0d17 100644 --- a/lib/cpus/aarch32/aem_generic.S +++ b/lib/cpus/aarch32/aem_generic.S @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016-2017, Arm Limited and Contributors. All rights reserved. + * Copyright (c) 2016-2024, Arm Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -40,14 +40,6 @@ func aem_generic_cluster_pwr_dwn b dcsw_op_all endfunc aem_generic_cluster_pwr_dwn -#if REPORT_ERRATA -/* - * Errata printing function for AEM. Must follow AAPCS. - */ -func aem_generic_errata_report - bx lr -endfunc aem_generic_errata_report -#endif /* cpu_ops for Base AEM FVP */ declare_cpu_ops aem_generic, BASE_AEM_MIDR, CPU_NO_RESET_FUNC, \ diff --git a/lib/cpus/aarch32/cortex_a12.S b/lib/cpus/aarch32/cortex_a12.S index 8eec27cb..b95020e6 100644 --- a/lib/cpus/aarch32/cortex_a12.S +++ b/lib/cpus/aarch32/cortex_a12.S @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2023, Arm Limited and Contributors. All rights reserved. + * Copyright (c) 2017-2024, Arm Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -73,8 +73,6 @@ func cortex_a12_cluster_pwr_dwn b cortex_a12_disable_smp endfunc cortex_a12_cluster_pwr_dwn -errata_report_shim cortex_a12 - declare_cpu_ops cortex_a12, CORTEX_A12_MIDR, \ cortex_a12_reset_func, \ cortex_a12_core_pwr_dwn, \ diff --git a/lib/cpus/aarch32/cortex_a15.S b/lib/cpus/aarch32/cortex_a15.S index b41676d9..53489ad1 100644 --- a/lib/cpus/aarch32/cortex_a15.S +++ b/lib/cpus/aarch32/cortex_a15.S @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016-2023, Arm Limited and Contributors. All rights reserved. + * Copyright (c) 2016-2024, Arm Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -172,8 +172,6 @@ func cortex_a15_cluster_pwr_dwn b cortex_a15_disable_smp endfunc cortex_a15_cluster_pwr_dwn -errata_report_shim cortex_a15 - declare_cpu_ops cortex_a15, CORTEX_A15_MIDR, \ cortex_a15_reset_func, \ cortex_a15_core_pwr_dwn, \ diff --git a/lib/cpus/aarch32/cortex_a17.S b/lib/cpus/aarch32/cortex_a17.S index 18775708..05e96169 100644 --- a/lib/cpus/aarch32/cortex_a17.S +++ b/lib/cpus/aarch32/cortex_a17.S @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2023, Arm Limited and Contributors. All rights reserved. + * Copyright (c) 2017-2024, Arm Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -106,8 +106,6 @@ endfunc check_errata_cve_2017_5715 add_erratum_entry cortex_a17, CVE(2017, 5715), WORKAROUND_CVE_2017_5715 -errata_report_shim cortex_a17 - func cortex_a17_reset_func mov r5, lr bl cpu_get_rev_var diff --git a/lib/cpus/aarch32/cortex_a32.S b/lib/cpus/aarch32/cortex_a32.S index d08b4ff5..c92a8c1b 100644 --- a/lib/cpus/aarch32/cortex_a32.S +++ b/lib/cpus/aarch32/cortex_a32.S @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016-2023, Arm Limited and Contributors. All rights reserved. + * Copyright (c) 2016-2024, Arm Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -117,8 +117,6 @@ func cortex_a32_cluster_pwr_dwn b cortex_a32_disable_smp endfunc cortex_a32_cluster_pwr_dwn -errata_report_shim cortex_a32 - declare_cpu_ops cortex_a32, CORTEX_A32_MIDR, \ cortex_a32_reset_func, \ cortex_a32_core_pwr_dwn, \ diff --git a/lib/cpus/aarch32/cortex_a5.S b/lib/cpus/aarch32/cortex_a5.S index 625ea7ba..146eb9c5 100644 --- a/lib/cpus/aarch32/cortex_a5.S +++ b/lib/cpus/aarch32/cortex_a5.S @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016-2023, Arm Limited and Contributors. All rights reserved. + * Copyright (c) 2016-2024, Arm Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -69,8 +69,6 @@ func cortex_a5_cluster_pwr_dwn b cortex_a5_disable_smp endfunc cortex_a5_cluster_pwr_dwn -errata_report_shim cortex_a5 - declare_cpu_ops cortex_a5, CORTEX_A5_MIDR, \ cortex_a5_reset_func, \ cortex_a5_core_pwr_dwn, \ diff --git a/lib/cpus/aarch32/cortex_a53.S b/lib/cpus/aarch32/cortex_a53.S index 89b238a6..60be2b33 100644 --- a/lib/cpus/aarch32/cortex_a53.S +++ b/lib/cpus/aarch32/cortex_a53.S @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2023, Arm Limited and Contributors. All rights reserved. + * Copyright (c) 2017-2024, Arm Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -297,8 +297,6 @@ func cortex_a53_cluster_pwr_dwn b cortex_a53_disable_smp endfunc cortex_a53_cluster_pwr_dwn -errata_report_shim cortex_a53 - declare_cpu_ops cortex_a53, CORTEX_A53_MIDR, \ cortex_a53_reset_func, \ cortex_a53_core_pwr_dwn, \ diff --git a/lib/cpus/aarch32/cortex_a57.S b/lib/cpus/aarch32/cortex_a57.S index 1e5377b2..d563482d 100644 --- a/lib/cpus/aarch32/cortex_a57.S +++ b/lib/cpus/aarch32/cortex_a57.S @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2023, Arm Limited and Contributors. All rights reserved. + * Copyright (c) 2017-2024, Arm Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -606,8 +606,6 @@ func cortex_a57_cluster_pwr_dwn b cortex_a57_disable_ext_debug endfunc cortex_a57_cluster_pwr_dwn -errata_report_shim cortex_a57 - declare_cpu_ops cortex_a57, CORTEX_A57_MIDR, \ cortex_a57_reset_func, \ cortex_a57_core_pwr_dwn, \ diff --git a/lib/cpus/aarch32/cortex_a7.S b/lib/cpus/aarch32/cortex_a7.S index 4842ca63..f99ae791 100644 --- a/lib/cpus/aarch32/cortex_a7.S +++ b/lib/cpus/aarch32/cortex_a7.S @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2023, Arm Limited and Contributors. All rights reserved. + * Copyright (c) 2017-2024, Arm Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -73,8 +73,6 @@ func cortex_a7_cluster_pwr_dwn b cortex_a7_disable_smp endfunc cortex_a7_cluster_pwr_dwn -errata_report_shim cortex_a7 - declare_cpu_ops cortex_a7, CORTEX_A7_MIDR, \ cortex_a7_reset_func, \ cortex_a7_core_pwr_dwn, \ diff --git a/lib/cpus/aarch32/cortex_a72.S b/lib/cpus/aarch32/cortex_a72.S index 77cf84dd..8d399fd6 100644 --- a/lib/cpus/aarch32/cortex_a72.S +++ b/lib/cpus/aarch32/cortex_a72.S @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2023, Arm Limited and Contributors. All rights reserved. + * Copyright (c) 2017-2024, Arm Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -256,8 +256,6 @@ func cortex_a72_cluster_pwr_dwn b cortex_a72_disable_ext_debug endfunc cortex_a72_cluster_pwr_dwn -errata_report_shim cortex_a72 - declare_cpu_ops cortex_a72, CORTEX_A72_MIDR, \ cortex_a72_reset_func, \ cortex_a72_core_pwr_dwn, \ diff --git a/lib/cpus/aarch32/cortex_a9.S b/lib/cpus/aarch32/cortex_a9.S index 1e9757a4..dc5ff270 100644 --- a/lib/cpus/aarch32/cortex_a9.S +++ b/lib/cpus/aarch32/cortex_a9.S @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016-2023, Arm Limited and Contributors. All rights reserved. + * Copyright (c) 2016-2024, Arm Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -57,8 +57,6 @@ endfunc check_errata_cve_2017_5715 add_erratum_entry cortex_a9, CVE(2017, 5715), WORKAROUND_CVE_2017_5715 -errata_report_shim cortex_a9 - func cortex_a9_reset_func #if IMAGE_BL32 && WORKAROUND_CVE_2017_5715 ldr r0, =wa_cve_2017_5715_bpiall_vbar diff --git a/lib/cpus/aarch64/a64fx.S b/lib/cpus/aarch64/a64fx.S index 54c20c32..4893a44d 100644 --- a/lib/cpus/aarch64/a64fx.S +++ b/lib/cpus/aarch64/a64fx.S @@ -16,15 +16,6 @@ endfunc a64fx_core_pwr_dwn func a64fx_cluster_pwr_dwn endfunc a64fx_cluster_pwr_dwn -#if REPORT_ERRATA -/* - * Errata printing function for A64FX. Must follow AAPCS. - */ -func a64fx_errata_report - ret -endfunc a64fx_errata_report -#endif - /* --------------------------------------------- * This function provides cpu specific * register information for crash reporting. diff --git a/lib/cpus/aarch64/aem_generic.S b/lib/cpus/aarch64/aem_generic.S index d47279a7..d5634cff 100644 --- a/lib/cpus/aarch64/aem_generic.S +++ b/lib/cpus/aarch64/aem_generic.S @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2019, Arm Limited and Contributors. All rights reserved. + * Copyright (c) 2014-2024, Arm Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -74,15 +74,6 @@ func aem_generic_cluster_pwr_dwn b dcsw_op_all endfunc aem_generic_cluster_pwr_dwn -#if REPORT_ERRATA -/* - * Errata printing function for AEM. Must follow AAPCS. - */ -func aem_generic_errata_report - ret -endfunc aem_generic_errata_report -#endif - /* --------------------------------------------- * This function provides cpu specific * register information for crash reporting. diff --git a/lib/cpus/aarch64/cortex_a35.S b/lib/cpus/aarch64/cortex_a35.S index 6ffb9440..c3d8c8dd 100644 --- a/lib/cpus/aarch64/cortex_a35.S +++ b/lib/cpus/aarch64/cortex_a35.S @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016-2023, Arm Limited and Contributors. All rights reserved. + * Copyright (c) 2016-2024, Arm Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -111,8 +111,6 @@ func cortex_a35_cluster_pwr_dwn b cortex_a35_disable_smp endfunc cortex_a35_cluster_pwr_dwn -errata_report_shim cortex_a35 - /* --------------------------------------------- * This function provides cortex_a35 specific * register information for crash reporting. diff --git a/lib/cpus/aarch64/cortex_a510.S b/lib/cpus/aarch64/cortex_a510.S index a59b92c1..b49d45a2 100644 --- a/lib/cpus/aarch64/cortex_a510.S +++ b/lib/cpus/aarch64/cortex_a510.S @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023, Arm Limited. All rights reserved. + * Copyright (c) 2023-2024, Arm Limited. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -204,8 +204,6 @@ func cortex_a510_core_pwr_dwn ret endfunc cortex_a510_core_pwr_dwn -errata_report_shim cortex_a510 - cpu_reset_func_start cortex_a510 /* Disable speculative loads */ msr SSBS, xzr diff --git a/lib/cpus/aarch64/cortex_a520.S b/lib/cpus/aarch64/cortex_a520.S index 6c2f33e8..811c836a 100644 --- a/lib/cpus/aarch64/cortex_a520.S +++ b/lib/cpus/aarch64/cortex_a520.S @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021-2023, Arm Limited. All rights reserved. + * Copyright (c) 2021-2024, Arm Limited. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -11,6 +11,9 @@ #include <cpu_macros.S> #include <plat_macros.S> +/* .global erratum_cortex_a520_2938996_wa */ +.global check_erratum_cortex_a520_2938996 + /* Hardware handled coherency */ #if HW_ASSISTED_COHERENCY == 0 #error "Cortex A520 must be compiled with HW_ASSISTED_COHERENCY enabled" @@ -21,6 +24,36 @@ #error "Cortex A520 supports only AArch64. Compile with CTX_INCLUDE_AARCH32_REGS=0" #endif +workaround_reset_start cortex_a520, ERRATUM(2630792), ERRATA_A520_2630792 + sysreg_bit_set CORTEX_A520_CPUACTLR_EL1, BIT(38) +workaround_reset_end cortex_a520, ERRATUM(2630792) + +check_erratum_ls cortex_a520, ERRATUM(2630792), CPU_REV(0, 1) + +workaround_reset_start cortex_a520, ERRATUM(2858100), ERRATA_A520_2858100 + sysreg_bit_set CORTEX_A520_CPUACTLR_EL1, BIT(29) +workaround_reset_end cortex_a520, ERRATUM(2858100) + +check_erratum_ls cortex_a520, ERRATUM(2858100), CPU_REV(0, 1) + +workaround_runtime_start cortex_a520, ERRATUM(2938996), ERRATA_A520_2938996, CORTEX_A520_MIDR +workaround_runtime_end cortex_a520, ERRATUM(2938996) + +check_erratum_custom_start cortex_a520, ERRATUM(2938996) + + /* This erratum needs to be enabled for r0p0 and r0p1. + * Check if revision is less than or equal to r0p1. + */ + +#if ERRATA_A520_2938996 + mov x1, #1 + b cpu_rev_var_ls +#else + mov x0, #ERRATA_MISSING +#endif + ret +check_erratum_custom_end cortex_a520, ERRATUM(2938996) + /* ---------------------------------------------------- * HW will do the cache maintenance while powering down * ---------------------------------------------------- @@ -35,8 +68,6 @@ func cortex_a520_core_pwr_dwn ret endfunc cortex_a520_core_pwr_dwn -errata_report_shim cortex_a520 - cpu_reset_func_start cortex_a520 /* Disable speculative loads */ msr SSBS, xzr diff --git a/lib/cpus/aarch64/cortex_a53.S b/lib/cpus/aarch64/cortex_a53.S index e6fb08a4..4a5b3181 100644 --- a/lib/cpus/aarch64/cortex_a53.S +++ b/lib/cpus/aarch64/cortex_a53.S @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2023, Arm Limited and Contributors. All rights reserved. + * Copyright (c) 2014-2024, Arm Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -199,8 +199,6 @@ func cortex_a53_cluster_pwr_dwn b cortex_a53_disable_smp endfunc cortex_a53_cluster_pwr_dwn -errata_report_shim cortex_a53 - /* --------------------------------------------- * This function provides cortex_a53 specific * register information for crash reporting. diff --git a/lib/cpus/aarch64/cortex_a55.S b/lib/cpus/aarch64/cortex_a55.S index 712b6e0a..d5a74e96 100644 --- a/lib/cpus/aarch64/cortex_a55.S +++ b/lib/cpus/aarch64/cortex_a55.S @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2023, Arm Limited and Contributors. All rights reserved. + * Copyright (c) 2017-2024, Arm Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -116,8 +116,6 @@ add_erratum_entry cortex_a55, ERRATUM(1530923), ERRATA_A55_1530923, NO_APPLY_AT_ cpu_reset_func_start cortex_a55 cpu_reset_func_end cortex_a55 -errata_report_shim cortex_a55 - /* --------------------------------------------- * HW will do the cache maintenance while powering down * --------------------------------------------- diff --git a/lib/cpus/aarch64/cortex_a57.S b/lib/cpus/aarch64/cortex_a57.S index 8fafacab..fecb56f4 100644 --- a/lib/cpus/aarch64/cortex_a57.S +++ b/lib/cpus/aarch64/cortex_a57.S @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2023, Arm Limited and Contributors. All rights reserved. + * Copyright (c) 2014-2024, Arm Limited and Contributors. All rights reserved. * Copyright (c) 2020, NVIDIA Corporation. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause @@ -57,7 +57,7 @@ func cortex_a57_disable_ext_debug msr osdlr_el1, x0 isb - apply_erratum cortex_a57, ERRATUM(817169), ERRATA_A57_817169 + apply_erratum cortex_a57, ERRATUM(817169), ERRATA_A57_817169, NO_GET_CPU_REV dsb sy ret @@ -284,8 +284,6 @@ func cortex_a57_cluster_pwr_dwn b cortex_a57_disable_ext_debug endfunc cortex_a57_cluster_pwr_dwn -errata_report_shim cortex_a57 - /* --------------------------------------------- * This function provides cortex_a57 specific * register information for crash reporting. diff --git a/lib/cpus/aarch64/cortex_a65.S b/lib/cpus/aarch64/cortex_a65.S index 666324c1..3023ecbe 100644 --- a/lib/cpus/aarch64/cortex_a65.S +++ b/lib/cpus/aarch64/cortex_a65.S @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, Arm Limited. All rights reserved. + * Copyright (c) 2019-2024, Arm Limited. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -45,26 +45,6 @@ func cortex_a65_cpu_pwr_dwn ret endfunc cortex_a65_cpu_pwr_dwn -#if REPORT_ERRATA -/* - * Errata printing function for Cortex-A65. Must follow AAPCS. - */ -func cortex_a65_errata_report - stp x8, x30, [sp, #-16]! - - bl cpu_get_rev_var - mov x8, x0 - - /* - * Report all errata. The revision-variant information is passed to - * checking functions of each errata. - */ - report_errata ERRATA_DSU_936184, cortex_a65, dsu_936184 - - ldp x8, x30, [sp], #16 - ret -endfunc cortex_a65_errata_report -#endif .section .rodata.cortex_a65_regs, "aS" cortex_a65_regs: /* The ascii list of register names to be reported */ diff --git a/lib/cpus/aarch64/cortex_a65ae.S b/lib/cpus/aarch64/cortex_a65ae.S index 85d1894e..1cbb06af 100644 --- a/lib/cpus/aarch64/cortex_a65ae.S +++ b/lib/cpus/aarch64/cortex_a65ae.S @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019-2023, Arm Limited. All rights reserved. + * Copyright (c) 2019-2024, Arm Limited. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -41,8 +41,6 @@ func cortex_a65ae_cpu_pwr_dwn ret endfunc cortex_a65ae_cpu_pwr_dwn -errata_report_shim cortex_a65ae - .section .rodata.cortex_a65ae_regs, "aS" cortex_a65ae_regs: /* The ascii list of register names to be reported */ .asciz "cpuectlr_el1", "" diff --git a/lib/cpus/aarch64/cortex_a710.S b/lib/cpus/aarch64/cortex_a710.S index f3931d74..b9f6081e 100644 --- a/lib/cpus/aarch64/cortex_a710.S +++ b/lib/cpus/aarch64/cortex_a710.S @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021-2023, Arm Limited. All rights reserved. + * Copyright (c) 2021-2024, Arm Limited. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -193,6 +193,12 @@ workaround_runtime_end cortex_a710, ERRATUM(2768515), NO_ISB check_erratum_ls cortex_a710, ERRATUM(2768515), CPU_REV(2, 1) +workaround_reset_start cortex_a710, ERRATUM(2778471), ERRATA_A710_2778471 + sysreg_bit_set CORTEX_A710_CPUACTLR3_EL1, BIT(47) +workaround_reset_end cortex_a710, ERRATUM(2778471) + +check_erratum_ls cortex_a710, ERRATUM(2778471), CPU_REV(2, 1) + workaround_reset_start cortex_a710, CVE(2022, 23960), WORKAROUND_CVE_2022_23960 #if IMAGE_BL31 /* @@ -210,7 +216,7 @@ check_erratum_chosen cortex_a710, CVE(2022, 23960), WORKAROUND_CVE_2022_23960 * ---------------------------------------------------- */ func cortex_a710_core_pwr_dwn - apply_erratum cortex_a710, ERRATUM(2008768), ERRATA_A710_2008768 + apply_erratum cortex_a710, ERRATUM(2008768), ERRATA_A710_2008768, NO_GET_CPU_REV apply_erratum cortex_a710, ERRATUM(2291219), ERRATA_A710_2291219, NO_GET_CPU_REV /* --------------------------------------------------- @@ -223,8 +229,6 @@ func cortex_a710_core_pwr_dwn ret endfunc cortex_a710_core_pwr_dwn -errata_report_shim cortex_a710 - cpu_reset_func_start cortex_a710 /* Disable speculative loads */ msr SSBS, xzr diff --git a/lib/cpus/aarch64/cortex_a715.S b/lib/cpus/aarch64/cortex_a715.S index dd4c307f..8c9988da 100644 --- a/lib/cpus/aarch64/cortex_a715.S +++ b/lib/cpus/aarch64/cortex_a715.S @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021-2023, Arm Limited. All rights reserved. + * Copyright (c) 2021-2024, Arm Limited. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -26,6 +26,95 @@ wa_cve_2022_23960_bhb_vector_table CORTEX_A715_BHB_LOOP_COUNT, cortex_a715 #endif /* WORKAROUND_CVE_2022_23960 */ +workaround_reset_start cortex_a715, ERRATUM(2331818), ERRATA_A715_2331818 + sysreg_bit_set CORTEX_A715_CPUACTLR2_EL1, BIT(20) +workaround_reset_end cortex_a715, ERRATUM(2331818) + +check_erratum_ls cortex_a715, ERRATUM(2331818), CPU_REV(1, 0) + +workaround_reset_start cortex_a715, ERRATUM(2344187), ERRATA_A715_2344187 + /* GCR_EL1 is only present with FEAT_MTE2. */ + mrs x1, ID_AA64PFR1_EL1 + ubfx x0, x1, ID_AA64PFR1_EL1_MTE_SHIFT, #4 + cmp x0, #MTE_IMPLEMENTED_ELX + bne #1f + sysreg_bit_set GCR_EL1, GCR_EL1_RRND_BIT + +1: + /* Mitigation upon ERETAA and ERETAB. */ + mov x0, #2 + msr CORTEX_A715_CPUPSELR_EL3, x0 + isb + ldr x0, =0xd69f0bff + msr CORTEX_A715_CPUPOR_EL3, x0 + ldr x0, =0xfffffbff + msr CORTEX_A715_CPUPMR_EL3, x0 + mov x1, #0 + orr x1, x1, #(1<<0) + orr x1, x1, #(3<<4) + orr x1, x1, #(0xf<<6) + orr x1, x1, #(1<<13) + orr x1, x1, #(1<<53) + msr CORTEX_A715_CPUPCR_EL3, x1 +workaround_reset_end cortex_a715, ERRATUM(2344187) + +check_erratum_ls cortex_a715, ERRATUM(2344187), CPU_REV(1, 0) + +workaround_reset_start cortex_a715, ERRATUM(2413290), ERRATA_A715_2413290 +/* Erratum 2413290 workaround is required only if SPE is enabled */ +#if ENABLE_SPE_FOR_NS != 0 + /* Check if Static profiling extension is implemented or present. */ + mrs x1, id_aa64dfr0_el1 + ubfx x0, x1, ID_AA64DFR0_PMS_SHIFT, #4 + cbz x0, 1f + /* Apply the workaround by setting CPUACTLR_EL1[58:57] = 0b11. */ + sysreg_bit_set CORTEX_A715_CPUACTLR_EL1, BIT(57) + sysreg_bit_set CORTEX_A715_CPUACTLR_EL1, BIT(58) +1: +#endif +workaround_reset_end cortex_a715, ERRATUM(2413290) + +check_erratum_range cortex_a715, ERRATUM(2413290), CPU_REV(1,0), CPU_REV(1, 0) + +workaround_reset_start cortex_a715, ERRATUM(2420947), ERRATA_A715_2420947 + sysreg_bit_set CORTEX_A715_CPUACTLR2_EL1, BIT(33) +workaround_reset_end cortex_a715, ERRATUM(2420947) + +check_erratum_range cortex_a715, ERRATUM(2420947), CPU_REV(1, 0), CPU_REV(1, 0) + +workaround_reset_start cortex_a715, ERRATUM(2429384), ERRATA_A715_2429384 + sysreg_bit_set CORTEX_A715_CPUACTLR2_EL1, BIT(27) +workaround_reset_end cortex_a715, ERRATUM(2429384) + +check_erratum_range cortex_a715, ERRATUM(2429384), CPU_REV(1, 0), CPU_REV(1, 0) + +workaround_reset_start cortex_a715, ERRATUM(2561034), ERRATA_A715_2561034 + sysreg_bit_set CORTEX_A715_CPUACTLR2_EL1, BIT(26) +workaround_reset_end cortex_a715, ERRATUM(2561034) + +check_erratum_range cortex_a715, ERRATUM(2561034), CPU_REV(1, 0), CPU_REV(1, 0) + +workaround_reset_start cortex_a715, ERRATUM(2728106), ERRATA_A715_2728106 + mov x0, #3 + msr CORTEX_A715_CPUPSELR_EL3, x0 + isb + ldr x0, =0xd503339f + msr CORTEX_A715_CPUPOR_EL3, x0 + ldr x0, =0xfffff3ff + msr CORTEX_A715_CPUPMR_EL3, x0 + mov x0, #1 + orr x0, x0, #(3<<4) + orr x0, x0, #(0xf<<6) + orr x0, x0, #(1<<13) + orr x0, x0, #(1<<20) + orr x0, x0, #(1<<22) + orr x0, x0, #(1<<31) + orr x0, x0, #(1<<50) + msr CORTEX_A715_CPUPCR_EL3, x0 +workaround_reset_end cortex_a715, ERRATUM(2728106) + +check_erratum_ls cortex_a715, ERRATUM(2728106), CPU_REV(1, 1) + workaround_reset_start cortex_a715, CVE(2022, 23960), WORKAROUND_CVE_2022_23960 #if IMAGE_BL31 /* @@ -59,8 +148,6 @@ func cortex_a715_core_pwr_dwn ret endfunc cortex_a715_core_pwr_dwn -errata_report_shim cortex_a715 - /* --------------------------------------------- * This function provides Cortex-A715 specific * register information for crash reporting. diff --git a/lib/cpus/aarch64/cortex_a72.S b/lib/cpus/aarch64/cortex_a72.S index 997f261b..c300ea7c 100644 --- a/lib/cpus/aarch64/cortex_a72.S +++ b/lib/cpus/aarch64/cortex_a72.S @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015-2023, Arm Limited and Contributors. All rights reserved. + * Copyright (c) 2015-2024, Arm Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -271,8 +271,6 @@ func cortex_a72_cluster_pwr_dwn b cortex_a72_disable_ext_debug endfunc cortex_a72_cluster_pwr_dwn -errata_report_shim cortex_a72 - /* --------------------------------------------- * This function provides cortex_a72 specific * register information for crash reporting. diff --git a/lib/cpus/aarch64/cortex_a720.S b/lib/cpus/aarch64/cortex_a720.S index 4b28fdb0..9befb36e 100644 --- a/lib/cpus/aarch64/cortex_a720.S +++ b/lib/cpus/aarch64/cortex_a720.S @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021-2023, Arm Limited. All rights reserved. + * Copyright (c) 2021-2024, Arm Limited. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -26,6 +26,40 @@ wa_cve_2022_23960_bhb_vector_table CORTEX_A720_BHB_LOOP_COUNT, cortex_a720 #endif /* WORKAROUND_CVE_2022_23960 */ +workaround_reset_start cortex_a720, ERRATUM(2792132), ERRATA_A720_2792132 + sysreg_bit_set CORTEX_A720_CPUACTLR2_EL1, BIT(26) +workaround_reset_end cortex_a720, ERRATUM(2792132) + +check_erratum_ls cortex_a720, ERRATUM(2792132), CPU_REV(0, 1) + +workaround_reset_start cortex_a720, ERRATUM(2844092), ERRATA_A720_2844092 + sysreg_bit_set CORTEX_A720_CPUACTLR4_EL1, BIT(11) +workaround_reset_end cortex_a720, ERRATUM(2844092) + +check_erratum_ls cortex_a720, ERRATUM(2844092), CPU_REV(0, 1) + +workaround_reset_start cortex_a720, ERRATUM(2926083), ERRATA_A720_2926083 +/* Erratum 2926083 workaround is required only if SPE is enabled */ +#if ENABLE_SPE_FOR_NS != 0 + /* Check if Static profiling extension is implemented or present. */ + mrs x1, id_aa64dfr0_el1 + ubfx x0, x1, ID_AA64DFR0_PMS_SHIFT, #4 + cbz x0, 1f + /* Apply the workaround by setting CPUACTLR_EL1[58:57] = 0b11. */ + sysreg_bit_set CORTEX_A720_CPUACTLR_EL1, BIT(57) + sysreg_bit_set CORTEX_A720_CPUACTLR_EL1, BIT(58) +1: +#endif +workaround_reset_end cortex_a720, ERRATUM(2926083) + +check_erratum_ls cortex_a720, ERRATUM(2926083), CPU_REV(0, 1) + +workaround_reset_start cortex_a720, ERRATUM(2940794), ERRATA_A720_2940794 + sysreg_bit_set CORTEX_A720_CPUACTLR2_EL1, BIT(37) +workaround_reset_end cortex_a720, ERRATUM(2940794) + +check_erratum_ls cortex_a720, ERRATUM(2940794), CPU_REV(0, 1) + workaround_reset_start cortex_a720, CVE(2022, 23960), WORKAROUND_CVE_2022_23960 #if IMAGE_BL31 /* @@ -58,8 +92,6 @@ func cortex_a720_core_pwr_dwn ret endfunc cortex_a720_core_pwr_dwn -errata_report_shim cortex_a720 - /* --------------------------------------------- * This function provides Cortex A720-specific * register information for crash reporting. diff --git a/lib/cpus/aarch64/neoverse_hermes.S b/lib/cpus/aarch64/cortex_a720_ae.S similarity index 52% rename from lib/cpus/aarch64/neoverse_hermes.S rename to lib/cpus/aarch64/cortex_a720_ae.S index cb90b715..42d49c33 100644 --- a/lib/cpus/aarch64/neoverse_hermes.S +++ b/lib/cpus/aarch64/cortex_a720_ae.S @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023, Arm Limited. All rights reserved. + * Copyright (c) 2024, Arm Limited. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -7,43 +7,42 @@ #include <arch.h> #include <asm_macros.S> #include <common/bl_common.h> -#include <neoverse_hermes.h> +#include <cortex_a720_ae.h> #include <cpu_macros.S> #include <plat_macros.S> /* Hardware handled coherency */ #if HW_ASSISTED_COHERENCY == 0 -#error "Neoverse Hermes must be compiled with HW_ASSISTED_COHERENCY enabled" +#error "Cortex-A720AE must be compiled with HW_ASSISTED_COHERENCY enabled" #endif /* 64-bit only core */ #if CTX_INCLUDE_AARCH32_REGS == 1 -#error "Neoverse Hermes supports only AArch64. Compile with CTX_INCLUDE_AARCH32_REGS=0" +#error "Cortex-A720AE supports only AArch64. Compile with CTX_INCLUDE_AARCH32_REGS=0" #endif -cpu_reset_func_start neoverse_hermes +cpu_reset_func_start cortex_a720_ae /* Disable speculative loads */ msr SSBS, xzr -cpu_reset_func_end neoverse_hermes +cpu_reset_func_end cortex_a720_ae /* ---------------------------------------------------- * HW will do the cache maintenance while powering down * ---------------------------------------------------- */ -func neoverse_hermes_core_pwr_dwn +func cortex_a720_ae_core_pwr_dwn /* --------------------------------------------------- * Enable CPU power down bit in power control register * --------------------------------------------------- */ - sysreg_bit_set NEOVERSE_HERMES_CPUPWRCTLR_EL1, NEOVERSE_HERMES_CPUPWRCTLR_EL1_CORE_PWRDN_BIT + sysreg_bit_set CORTEX_A720_AE_CPUPWRCTLR_EL1, CORTEX_A720_AE_CPUPWRCTLR_EL1_CORE_PWRDN_BIT + isb ret -endfunc neoverse_hermes_core_pwr_dwn - -errata_report_shim neoverse_hermes +endfunc cortex_a720_ae_core_pwr_dwn /* --------------------------------------------- - * This function provides Neoverse Hermes specific + * This function provides Cortex-A720AE specific * register information for crash reporting. * It needs to return with x6 pointing to * a list of register names in ascii and @@ -51,16 +50,16 @@ errata_report_shim neoverse_hermes * reported. * --------------------------------------------- */ -.section .rodata.neoverse_hermes_regs, "aS" -neoverse_hermes_regs: /* The ascii list of register names to be reported */ +.section .rodata.cortex_a720_ae_regs, "aS" +cortex_a720_ae_regs: /* The ascii list of register names to be reported */ .asciz "cpuectlr_el1", "" -func neoverse_hermes_cpu_reg_dump - adr x6, neoverse_hermes_regs - mrs x8, NEOVERSE_HERMES_CPUECTLR_EL1 +func cortex_a720_ae_cpu_reg_dump + adr x6, cortex_a720_ae_regs + mrs x8, CORTEX_A720_AE_CPUECTLR_EL1 ret -endfunc neoverse_hermes_cpu_reg_dump +endfunc cortex_a720_ae_cpu_reg_dump -declare_cpu_ops neoverse_hermes, NEOVERSE_HERMES_MIDR, \ - neoverse_hermes_reset_func, \ - neoverse_hermes_core_pwr_dwn +declare_cpu_ops cortex_a720_ae, CORTEX_A720_AE_MIDR, \ + cortex_a720_ae_reset_func, \ + cortex_a720_ae_core_pwr_dwn diff --git a/lib/cpus/aarch64/cortex_blackhawk.S b/lib/cpus/aarch64/cortex_a725.S similarity index 51% rename from lib/cpus/aarch64/cortex_blackhawk.S rename to lib/cpus/aarch64/cortex_a725.S index b7b7a2dd..af98d145 100644 --- a/lib/cpus/aarch64/cortex_blackhawk.S +++ b/lib/cpus/aarch64/cortex_a725.S @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023, Arm Limited. All rights reserved. + * Copyright (c) 2023-2024, Arm Limited. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -7,43 +7,41 @@ #include <arch.h> #include <asm_macros.S> #include <common/bl_common.h> -#include <cortex_blackhawk.h> +#include <cortex_a725.h> #include <cpu_macros.S> #include <plat_macros.S> /* Hardware handled coherency */ #if HW_ASSISTED_COHERENCY == 0 -#error "Cortex blackhawk must be compiled with HW_ASSISTED_COHERENCY enabled" +#error "Cortex-A725 must be compiled with HW_ASSISTED_COHERENCY enabled" #endif /* 64-bit only core */ #if CTX_INCLUDE_AARCH32_REGS == 1 -#error "Cortex blackhawk supports only AArch64. Compile with CTX_INCLUDE_AARCH32_REGS=0" +#error "Cortex-A725 supports only AArch64. Compile with CTX_INCLUDE_AARCH32_REGS=0" #endif -cpu_reset_func_start cortex_blackhawk +cpu_reset_func_start cortex_a725 /* Disable speculative loads */ msr SSBS, xzr -cpu_reset_func_end cortex_blackhawk +cpu_reset_func_end cortex_a725 /* ---------------------------------------------------- * HW will do the cache maintenance while powering down * ---------------------------------------------------- */ -func cortex_blackhawk_core_pwr_dwn +func cortex_a725_core_pwr_dwn /* --------------------------------------------------- * Enable CPU power down bit in power control register * --------------------------------------------------- */ - sysreg_bit_set CORTEX_BLACKHAWK_CPUPWRCTLR_EL1, CORTEX_BLACKHAWK_CPUPWRCTLR_EL1_CORE_PWRDN_BIT + sysreg_bit_set CORTEX_A725_CPUPWRCTLR_EL1, CORTEX_A725_CPUPWRCTLR_EL1_CORE_PWRDN_BIT isb ret -endfunc cortex_blackhawk_core_pwr_dwn - -errata_report_shim cortex_blackhawk +endfunc cortex_a725_core_pwr_dwn /* --------------------------------------------- - * This function provides Cortex Blackhawk specific + * This function provides Cortex-A725 specific * register information for crash reporting. * It needs to return with x6 pointing to * a list of register names in ascii and @@ -51,16 +49,16 @@ errata_report_shim cortex_blackhawk * reported. * --------------------------------------------- */ -.section .rodata.cortex_blackhawk_regs, "aS" -cortex_blackhawk_regs: /* The ascii list of register names to be reported */ +.section .rodata.cortex_a725_regs, "aS" +cortex_a725_regs: /* The ascii list of register names to be reported */ .asciz "cpuectlr_el1", "" -func cortex_blackhawk_cpu_reg_dump - adr x6, cortex_blackhawk_regs - mrs x8, CORTEX_BLACKHAWK_CPUECTLR_EL1 +func cortex_a725_cpu_reg_dump + adr x6, cortex_a725_regs + mrs x8, CORTEX_A725_CPUECTLR_EL1 ret -endfunc cortex_blackhawk_cpu_reg_dump +endfunc cortex_a725_cpu_reg_dump -declare_cpu_ops cortex_blackhawk, CORTEX_BLACKHAWK_MIDR, \ - cortex_blackhawk_reset_func, \ - cortex_blackhawk_core_pwr_dwn +declare_cpu_ops cortex_a725, CORTEX_A725_MIDR, \ + cortex_a725_reset_func, \ + cortex_a725_core_pwr_dwn diff --git a/lib/cpus/aarch64/cortex_a73.S b/lib/cpus/aarch64/cortex_a73.S index 3a6b9226..2130ceb1 100644 --- a/lib/cpus/aarch64/cortex_a73.S +++ b/lib/cpus/aarch64/cortex_a73.S @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016-2023, Arm Limited and Contributors. All rights reserved. + * Copyright (c) 2016-2024, Arm Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -178,9 +178,6 @@ func cortex_a73_cluster_pwr_dwn b cortex_a73_disable_smp endfunc cortex_a73_cluster_pwr_dwn - -errata_report_shim cortex_a73 - /* --------------------------------------------- * This function provides cortex_a73 specific * register information for crash reporting. diff --git a/lib/cpus/aarch64/cortex_a75.S b/lib/cpus/aarch64/cortex_a75.S index c90be679..152c81f6 100644 --- a/lib/cpus/aarch64/cortex_a75.S +++ b/lib/cpus/aarch64/cortex_a75.S @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2023, Arm Limited and Contributors. All rights reserved. + * Copyright (c) 2017-2024, Arm Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -10,6 +10,8 @@ #include <cpuamu.h> #include <cpu_macros.S> +.global check_erratum_cortex_a75_764081 + /* Hardware handled coherency */ #if HW_ASSISTED_COHERENCY == 0 #error "Cortex-A75 must be compiled with HW_ASSISTED_COHERENCY enabled" @@ -146,8 +148,6 @@ func cortex_a75_core_pwr_dwn ret endfunc cortex_a75_core_pwr_dwn -errata_report_shim cortex_a75 - /* --------------------------------------------- * This function provides cortex_a75 specific * register information for crash reporting. diff --git a/lib/cpus/aarch64/cortex_a76.S b/lib/cpus/aarch64/cortex_a76.S index 8b3d7300..017086aa 100644 --- a/lib/cpus/aarch64/cortex_a76.S +++ b/lib/cpus/aarch64/cortex_a76.S @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2023, Arm Limited and Contributors. All rights reserved. + * Copyright (c) 2017-2024, Arm Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -505,14 +505,12 @@ func cortex_a76_core_pwr_dwn */ sysreg_bit_set CORTEX_A76_CPUPWRCTLR_EL1, CORTEX_A76_CORE_PWRDN_EN_MASK - apply_erratum cortex_a76, ERRATUM(2743102), ERRATA_A76_2743102 + apply_erratum cortex_a76, ERRATUM(2743102), ERRATA_A76_2743102, NO_GET_CPU_REV isb ret endfunc cortex_a76_core_pwr_dwn -errata_report_shim cortex_a76 - /* --------------------------------------------- * This function provides cortex_a76 specific * register information for crash reporting. diff --git a/lib/cpus/aarch64/cortex_a76ae.S b/lib/cpus/aarch64/cortex_a76ae.S index 08a6ef91..2fe3dbcf 100644 --- a/lib/cpus/aarch64/cortex_a76ae.S +++ b/lib/cpus/aarch64/cortex_a76ae.S @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019-2023, Arm Limited. All rights reserved. + * Copyright (c) 2019-2024, Arm Limited. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -41,8 +41,6 @@ workaround_reset_end cortex_a76ae, CVE(2022, 23960) cpu_reset_func_start cortex_a76ae cpu_reset_func_end cortex_a76ae -errata_report_shim cortex_a76ae - /* ---------------------------------------------------- * HW will do the cache maintenance while powering down * ---------------------------------------------------- diff --git a/lib/cpus/aarch64/cortex_a77.S b/lib/cpus/aarch64/cortex_a77.S index 86c25615..f53b6467 100644 --- a/lib/cpus/aarch64/cortex_a77.S +++ b/lib/cpus/aarch64/cortex_a77.S @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018-2023, Arm Limited and Contributors. All rights reserved. + * Copyright (c) 2018-2024, Arm Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -161,13 +161,12 @@ func cortex_a77_core_pwr_dwn sysreg_bit_set CORTEX_A77_CPUPWRCTLR_EL1, \ CORTEX_A77_CPUPWRCTLR_EL1_CORE_PWRDN_BIT - apply_erratum cortex_a77, ERRATUM(2743100), ERRATA_A77_2743100 + apply_erratum cortex_a77, ERRATUM(2743100), ERRATA_A77_2743100, NO_GET_CPU_REV isb ret endfunc cortex_a77_core_pwr_dwn -errata_report_shim cortex_a77 /* --------------------------------------------- * This function provides Cortex-A77 specific * register information for crash reporting. diff --git a/lib/cpus/aarch64/cortex_a78.S b/lib/cpus/aarch64/cortex_a78.S index b5c24e14..1de570a0 100644 --- a/lib/cpus/aarch64/cortex_a78.S +++ b/lib/cpus/aarch64/cortex_a78.S @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019-2023, Arm Limited. All rights reserved. + * Copyright (c) 2019-2024, Arm Limited. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -192,14 +192,12 @@ cpu_reset_func_end cortex_a78 func cortex_a78_core_pwr_dwn sysreg_bit_set CORTEX_A78_CPUPWRCTLR_EL1, CORTEX_A78_CPUPWRCTLR_EL1_CORE_PWRDN_EN_BIT - apply_erratum cortex_a78, ERRATUM(2772019), ERRATA_A78_2772019 + apply_erratum cortex_a78, ERRATUM(2772019), ERRATA_A78_2772019, NO_GET_CPU_REV isb ret endfunc cortex_a78_core_pwr_dwn -errata_report_shim cortex_a78 - /* --------------------------------------------- * This function provides cortex_a78 specific * register information for crash reporting. diff --git a/lib/cpus/aarch64/cortex_a78_ae.S b/lib/cpus/aarch64/cortex_a78_ae.S index d3a3e5d8..bc10186f 100644 --- a/lib/cpus/aarch64/cortex_a78_ae.S +++ b/lib/cpus/aarch64/cortex_a78_ae.S @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019-2023, Arm Limited. All rights reserved. + * Copyright (c) 2019-2024, Arm Limited. All rights reserved. * Copyright (c) 2021-2023, NVIDIA Corporation. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause @@ -128,8 +128,6 @@ func cortex_a78_ae_core_pwr_dwn ret endfunc cortex_a78_ae_core_pwr_dwn -errata_report_shim cortex_a78_ae - /* ------------------------------------------------------- * This function provides cortex_a78_ae specific * register information for crash reporting. diff --git a/lib/cpus/aarch64/cortex_a78c.S b/lib/cpus/aarch64/cortex_a78c.S index d19c6938..260cc73a 100644 --- a/lib/cpus/aarch64/cortex_a78c.S +++ b/lib/cpus/aarch64/cortex_a78c.S @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021-2023, Arm Limited. All rights reserved. + * Copyright (c) 2021-2024, Arm Limited. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -72,6 +72,27 @@ workaround_reset_end cortex_a78c, ERRATUM(2395411) check_erratum_range cortex_a78c, ERRATUM(2395411), CPU_REV(0, 1), CPU_REV(0, 2) +workaround_reset_start cortex_a78c, ERRATUM(2683027), ERRATA_A78C_2683027 + ldr x0, =0x3 + msr CORTEX_A78C_IMP_CPUPSELR_EL3, x0 + ldr x0, =0xEE010F10 + msr CORTEX_A78C_IMP_CPUPOR_EL3, x0 + ldr x0, =0xFF1F0FFE + msr CORTEX_A78C_IMP_CPUPMR_EL3, x0 + ldr x0, =0x100000004003FF + msr CORTEX_A78C_IMP_CPUPCR_EL3, x0 +workaround_reset_end cortex_a78c, ERRATUM(2683027) + +check_erratum_range cortex_a78c, ERRATUM(2683027), CPU_REV(0, 1), CPU_REV(0, 2) + +workaround_reset_start cortex_a78c, ERRATUM(2743232), ERRATA_A78C_2743232 + /* Set CPUACTLR5_EL1[56:55] to 2'b01 */ + sysreg_bit_set CORTEX_A78C_ACTLR5_EL1, BIT(55) + sysreg_bit_clear CORTEX_A78C_ACTLR5_EL1, BIT(56) +workaround_reset_end cortex_a78c, ERRATUM(2743232) + +check_erratum_range cortex_a78c, ERRATUM(2743232), CPU_REV(0, 1), CPU_REV(0, 2) + workaround_runtime_start cortex_a78c, ERRATUM(2772121), ERRATA_A78C_2772121 /* dsb before isb of power down sequence */ dsb sy @@ -100,8 +121,6 @@ workaround_reset_end cortex_a78c, CVE(2022, 23960) cpu_reset_func_start cortex_a78c cpu_reset_func_end cortex_a78c -errata_report_shim cortex_a78c - /* ---------------------------------------------------- * HW will do the cache maintenance while powering down * ---------------------------------------------------- @@ -113,7 +132,7 @@ func cortex_a78c_core_pwr_dwn */ sysreg_bit_set CORTEX_A78C_CPUPWRCTLR_EL1, CORTEX_A78C_CPUPWRCTLR_EL1_CORE_PWRDN_EN_BIT - apply_erratum cortex_a78c, ERRATUM(2772121), ERRATA_A78C_2772121 + apply_erratum cortex_a78c, ERRATUM(2772121), ERRATA_A78C_2772121, NO_GET_CPU_REV isb ret diff --git a/lib/cpus/aarch64/cortex_chaberton.S b/lib/cpus/aarch64/cortex_arcadia.S similarity index 51% rename from lib/cpus/aarch64/cortex_chaberton.S rename to lib/cpus/aarch64/cortex_arcadia.S index 596fe4a6..c97d87db 100644 --- a/lib/cpus/aarch64/cortex_chaberton.S +++ b/lib/cpus/aarch64/cortex_arcadia.S @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023, Arm Limited. All rights reserved. + * Copyright (c) 2024, Arm Limited. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -7,43 +7,41 @@ #include <arch.h> #include <asm_macros.S> #include <common/bl_common.h> -#include <cortex_chaberton.h> +#include <cortex_arcadia.h> #include <cpu_macros.S> #include <plat_macros.S> /* Hardware handled coherency */ #if HW_ASSISTED_COHERENCY == 0 -#error "Cortex Chaberton must be compiled with HW_ASSISTED_COHERENCY enabled" +#error "Cortex-ARCADIA must be compiled with HW_ASSISTED_COHERENCY enabled" #endif /* 64-bit only core */ #if CTX_INCLUDE_AARCH32_REGS == 1 -#error "Cortex Chaberton supports only AArch64. Compile with CTX_INCLUDE_AARCH32_REGS=0" +#error "Cortex-ARCADIA supports only AArch64. Compile with CTX_INCLUDE_AARCH32_REGS=0" #endif -cpu_reset_func_start cortex_chaberton +cpu_reset_func_start cortex_arcadia /* Disable speculative loads */ msr SSBS, xzr -cpu_reset_func_end cortex_chaberton +cpu_reset_func_end cortex_arcadia /* ---------------------------------------------------- * HW will do the cache maintenance while powering down * ---------------------------------------------------- */ -func cortex_chaberton_core_pwr_dwn +func cortex_arcadia_core_pwr_dwn /* --------------------------------------------------- * Enable CPU power down bit in power control register * --------------------------------------------------- */ - sysreg_bit_set CORTEX_CHABERTON_CPUPWRCTLR_EL1, CORTEX_CHABERTON_CPUPWRCTLR_EL1_CORE_PWRDN_BIT + sysreg_bit_set CORTEX_ARCADIA_CPUPWRCTLR_EL1, CORTEX_ARCADIA_CPUPWRCTLR_EL1_CORE_PWRDN_BIT isb ret -endfunc cortex_chaberton_core_pwr_dwn - -errata_report_shim cortex_chaberton +endfunc cortex_arcadia_core_pwr_dwn /* --------------------------------------------- - * This function provides Cortex Chaberton specific + * This function provides Cortex-Arcadia specific * register information for crash reporting. * It needs to return with x6 pointing to * a list of register names in ascii and @@ -51,16 +49,16 @@ errata_report_shim cortex_chaberton * reported. * --------------------------------------------- */ -.section .rodata.cortex_chaberton_regs, "aS" -cortex_chaberton_regs: /* The ascii list of register names to be reported */ +.section .rodata.cortex_arcadia_regs, "aS" +cortex_arcadia_regs: /* The ascii list of register names to be reported */ .asciz "cpuectlr_el1", "" -func cortex_chaberton_cpu_reg_dump - adr x6, cortex_chaberton_regs - mrs x8, CORTEX_CHABERTON_CPUECTLR_EL1 +func cortex_arcadia_cpu_reg_dump + adr x6, cortex_arcadia_regs + mrs x8, CORTEX_ARCADIA_CPUECTLR_EL1 ret -endfunc cortex_chaberton_cpu_reg_dump +endfunc cortex_arcadia_cpu_reg_dump -declare_cpu_ops cortex_chaberton, CORTEX_CHABERTON_MIDR, \ - cortex_chaberton_reset_func, \ - cortex_chaberton_core_pwr_dwn +declare_cpu_ops cortex_arcadia, CORTEX_ARCADIA_MIDR, \ + cortex_arcadia_reset_func, \ + cortex_arcadia_core_pwr_dwn diff --git a/lib/cpus/aarch64/cortex_gelas.S b/lib/cpus/aarch64/cortex_gelas.S index dc704f20..891e9a65 100644 --- a/lib/cpus/aarch64/cortex_gelas.S +++ b/lib/cpus/aarch64/cortex_gelas.S @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023, Arm Limited. All rights reserved. + * Copyright (c) 2023-2024, Arm Limited. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -42,7 +42,7 @@ func cortex_gelas_core_pwr_dwn mrs x0, ID_AA64PFR1_EL1 ubfx x0, x0, #ID_AA64PFR1_EL1_SME_SHIFT, \ #ID_AA64PFR1_EL1_SME_WIDTH - cmp x0, #ID_AA64PFR1_EL1_SME_NOT_SUPPORTED + cmp x0, #SME_NOT_IMPLEMENTED b.eq 1f msr CORTEX_GELAS_SVCRSM, xzr msr CORTEX_GELAS_SVCRZA, xzr @@ -58,8 +58,6 @@ func cortex_gelas_core_pwr_dwn ret endfunc cortex_gelas_core_pwr_dwn -errata_report_shim cortex_gelas - /* --------------------------------------------- * This function provides Gelas specific * register information for crash reporting. diff --git a/lib/cpus/aarch64/cortex_x1.S b/lib/cpus/aarch64/cortex_x1.S index 42634f1a..ca6cac99 100644 --- a/lib/cpus/aarch64/cortex_x1.S +++ b/lib/cpus/aarch64/cortex_x1.S @@ -66,8 +66,6 @@ func cortex_x1_core_pwr_dwn ret endfunc cortex_x1_core_pwr_dwn -errata_report_shim cortex_x1 - /* --------------------------------------------- * This function provides Cortex X1 specific * register information for crash reporting. diff --git a/lib/cpus/aarch64/cortex_x2.S b/lib/cpus/aarch64/cortex_x2.S index 258288c6..ac60903a 100644 --- a/lib/cpus/aarch64/cortex_x2.S +++ b/lib/cpus/aarch64/cortex_x2.S @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021-2023, Arm Limited. All rights reserved. + * Copyright (c) 2021-2024, Arm Limited. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -126,13 +126,19 @@ workaround_reset_end cortex_x2, ERRATUM(2742423) check_erratum_ls cortex_x2, ERRATUM(2742423), CPU_REV(2, 1) -workaround_reset_start cortex_x2, ERRATUM(2768515), ERRATA_X2_2768515 +workaround_runtime_start cortex_x2, ERRATUM(2768515), ERRATA_X2_2768515 /* dsb before isb of power down sequence */ dsb sy -workaround_reset_end cortex_x2, ERRATUM(2768515) +workaround_runtime_end cortex_x2, ERRATUM(2768515) check_erratum_ls cortex_x2, ERRATUM(2768515), CPU_REV(2, 1) +workaround_reset_start cortex_x2, ERRATUM(2778471), ERRATA_X2_2778471 + sysreg_bit_set CORTEX_X2_CPUACTLR3_EL1, BIT(47) +workaround_reset_end cortex_x2, ERRATUM(2778471) + +check_erratum_ls cortex_x2, ERRATUM(2778471), CPU_REV(2, 1) + workaround_reset_start cortex_x2, CVE(2022, 23960), WORKAROUND_CVE_2022_23960 #if IMAGE_BL31 /* @@ -166,18 +172,11 @@ func cortex_x2_core_pwr_dwn */ sysreg_bit_set CORTEX_X2_CPUPWRCTLR_EL1, CORTEX_X2_CPUPWRCTLR_EL1_CORE_PWRDN_BIT -#if ERRATA_X2_2768515 - mov x15, x30 - bl cpu_get_rev_var - bl erratum_cortex_x2_2768515_wa - mov x30, x15 -#endif /* ERRATA_X2_2768515 */ + apply_erratum cortex_x2, ERRATUM(2768515), ERRATA_X2_2768515, NO_GET_CPU_REV isb ret endfunc cortex_x2_core_pwr_dwn -errata_report_shim cortex_x2 - cpu_reset_func_start cortex_x2 /* Disable speculative loads */ msr SSBS, xzr diff --git a/lib/cpus/aarch64/cortex_x3.S b/lib/cpus/aarch64/cortex_x3.S index 0cb3b976..a81c4cf9 100644 --- a/lib/cpus/aarch64/cortex_x3.S +++ b/lib/cpus/aarch64/cortex_x3.S @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021-2023, Arm Limited. All rights reserved. + * Copyright (c) 2021-2024, Arm Limited. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -33,12 +33,31 @@ workaround_reset_end cortex_x3, ERRATUM(2070301) check_erratum_ls cortex_x3, ERRATUM(2070301), CPU_REV(1, 2) +workaround_reset_start cortex_x3, ERRATUM(2266875), ERRATA_X3_2266875 + sysreg_bit_set CORTEX_X3_CPUACTLR_EL1, BIT(22) +workaround_reset_end cortex_x3, ERRATUM(2266875) + +check_erratum_ls cortex_x3, ERRATUM(2266875), CPU_REV(1, 0) + +workaround_runtime_start cortex_x3, ERRATUM(2302506), ERRATA_X3_2302506 + sysreg_bit_set CORTEX_X3_CPUACTLR2_EL1, BIT(0) +workaround_runtime_end cortex_x3, ERRATUM(2302506), NO_ISB + +check_erratum_ls cortex_x3, ERRATUM(2302506), CPU_REV(1, 1) + workaround_runtime_start cortex_x3, ERRATUM(2313909), ERRATA_X3_2313909 sysreg_bit_set CORTEX_X3_CPUACTLR2_EL1, CORTEX_X3_CPUACTLR2_EL1_BIT_36 workaround_runtime_end cortex_x3, ERRATUM(2313909), NO_ISB check_erratum_ls cortex_x3, ERRATUM(2313909), CPU_REV(1, 0) +workaround_reset_start cortex_x3, ERRATUM(2372204), ERRATA_X3_2372204 + /* Set bit 40 in CPUACTLR2_EL1 */ + sysreg_bit_set CORTEX_X3_CPUACTLR2_EL1, BIT(40) +workaround_reset_end cortex_x3, ERRATUM(2372204) + +check_erratum_ls cortex_x3, ERRATUM(2372204), CPU_REV(1, 0) + workaround_reset_start cortex_x3, ERRATUM(2615812), ERRATA_X3_2615812 /* Disable retention control for WFI and WFE. */ mrs x0, CORTEX_X3_CPUPWRCTLR_EL1 @@ -49,6 +68,12 @@ workaround_reset_end cortex_x3, ERRATUM(2615812) check_erratum_ls cortex_x3, ERRATUM(2615812), CPU_REV(1, 1) +workaround_runtime_start cortex_x3, ERRATUM(2641945), ERRATA_X3_2641945 + sysreg_bit_set CORTEX_X3_CPUACTLR6_EL1, BIT(41) +workaround_runtime_end cortex_x3, ERRATUM(2641945), NO_ISB + +check_erratum_ls cortex_x3, ERRATUM(2641945), CPU_REV(1, 0) + workaround_reset_start cortex_x3, ERRATUM(2742421), ERRATA_X3_2742421 /* Set CPUACTLR5_EL1[56:55] to 2'b01 */ sysreg_bit_set CORTEX_X3_CPUACTLR5_EL1, CORTEX_X3_CPUACTLR5_EL1_BIT_55 @@ -57,6 +82,20 @@ workaround_reset_end cortex_x3, ERRATUM(2742421) check_erratum_ls cortex_x3, ERRATUM(2742421), CPU_REV(1, 1) +workaround_runtime_start cortex_x3, ERRATUM(2743088), ERRATA_X3_2743088 + /* dsb before isb of power down sequence */ + dsb sy +workaround_runtime_end cortex_x3, ERRATUM(2743088), NO_ISB + +check_erratum_ls cortex_x3, ERRATUM(2743088), CPU_REV(1, 1) + +workaround_reset_start cortex_x3, ERRATUM(2779509), ERRATA_X3_2779509 + /* Set CPUACTLR3_EL1 bit 47 */ + sysreg_bit_set CORTEX_X3_CPUACTLR3_EL1, CORTEX_X3_CPUACTLR3_EL1_BIT_47 +workaround_reset_end cortex_x3, ERRATUM(2779509) + +check_erratum_ls cortex_x3, ERRATUM(2779509), CPU_REV(1, 1) + workaround_reset_start cortex_x3, CVE(2022, 23960), WORKAROUND_CVE_2022_23960 #if IMAGE_BL31 override_vector_table wa_cve_vbar_cortex_x3 @@ -75,18 +114,17 @@ cpu_reset_func_end cortex_x3 * ---------------------------------------------------- */ func cortex_x3_core_pwr_dwn -apply_erratum cortex_x3, ERRATUM(2313909), ERRATA_X3_2313909 + apply_erratum cortex_x3, ERRATUM(2313909), ERRATA_X3_2313909, NO_GET_CPU_REV /* --------------------------------------------------- * Enable CPU power down bit in power control register * --------------------------------------------------- */ sysreg_bit_set CORTEX_X3_CPUPWRCTLR_EL1, CORTEX_X3_CPUPWRCTLR_EL1_CORE_PWRDN_BIT + apply_erratum cortex_x3, ERRATUM(2743088), ERRATA_X3_2743088, NO_GET_CPU_REV isb ret endfunc cortex_x3_core_pwr_dwn -errata_report_shim cortex_x3 - /* --------------------------------------------- * This function provides Cortex-X3- * specific register information for crash diff --git a/lib/cpus/aarch64/cortex_x4.S b/lib/cpus/aarch64/cortex_x4.S index 7619f9cf..320fd90b 100644 --- a/lib/cpus/aarch64/cortex_x4.S +++ b/lib/cpus/aarch64/cortex_x4.S @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022-2023, Arm Limited. All rights reserved. + * Copyright (c) 2022-2024, Arm Limited. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -22,10 +22,67 @@ #error "Cortex X4 supports only AArch64. Compile with CTX_INCLUDE_AARCH32_REGS=0" #endif +.global check_erratum_cortex_x4_2726228 + #if WORKAROUND_CVE_2022_23960 wa_cve_2022_23960_bhb_vector_table CORTEX_X4_BHB_LOOP_COUNT, cortex_x4 #endif /* WORKAROUND_CVE_2022_23960 */ +workaround_runtime_start cortex_x4, ERRATUM(2726228), ERRATA_X4_2726228, CORTEX_X4_MIDR +workaround_runtime_end cortex_x4, ERRATUM(2726228) + +check_erratum_custom_start cortex_x4, ERRATUM(2726228) + + /* This erratum needs to be enabled for r0p0 and r0p1. + * Check if revision is less than or equal to r0p1. + */ + +#if ERRATA_X4_2726228 + mov x1, #1 + b cpu_rev_var_ls +#else + mov x0, #ERRATA_MISSING +#endif + ret +check_erratum_custom_end cortex_x4, ERRATUM(2726228) + +workaround_runtime_start cortex_x4, ERRATUM(2740089), ERRATA_X4_2740089 + /* dsb before isb of power down sequence */ + dsb sy +workaround_runtime_end cortex_x4, ERRATUM(2740089) + +check_erratum_ls cortex_x4, ERRATUM(2740089), CPU_REV(0, 1) + +workaround_reset_start cortex_x4, ERRATUM(2763018), ERRATA_X4_2763018 + sysreg_bit_set CORTEX_X4_CPUACTLR3_EL1, BIT(47) +workaround_reset_end cortex_x4, ERRATUM(2763018) + +check_erratum_ls cortex_x4, ERRATUM(2763018), CPU_REV(0, 1) + +workaround_reset_start cortex_x4, ERRATUM(2816013), ERRATA_X4_2816013 + mrs x1, id_aa64pfr1_el1 + ubfx x2, x1, ID_AA64PFR1_EL1_MTE_SHIFT, #4 + cbz x2, #1f + sysreg_bit_set CORTEX_X4_CPUACTLR5_EL1, BIT(14) +1: +workaround_reset_end cortex_x4, ERRATUM(2816013) + +check_erratum_ls cortex_x4, ERRATUM(2816013), CPU_REV(0, 1) + +workaround_reset_start cortex_x4, ERRATUM(2897503), ERRATA_X4_2897503 + sysreg_bit_set CORTEX_X4_CPUACTLR4_EL1, BIT(8) +workaround_reset_end cortex_x4, ERRATUM(2897503) + +check_erratum_ls cortex_x4, ERRATUM(2897503), CPU_REV(0, 1) + +workaround_reset_start cortex_x4, ERRATUM(3076789), ERRATA_X4_3076789 + sysreg_bit_set CORTEX_X4_CPUACTLR3_EL1, BIT(14) + sysreg_bit_set CORTEX_X4_CPUACTLR3_EL1, BIT(13) + sysreg_bit_set CORTEX_X4_CPUACTLR_EL1, BIT(52) +workaround_reset_end cortex_x4, ERRATUM(3076789) + +check_erratum_ls cortex_x4, ERRATUM(3076789), CPU_REV(0, 1) + workaround_reset_start cortex_x4, CVE(2022, 23960), WORKAROUND_CVE_2022_23960 #if IMAGE_BL31 /* @@ -53,12 +110,13 @@ func cortex_x4_core_pwr_dwn * --------------------------------------------------- */ sysreg_bit_set CORTEX_X4_CPUPWRCTLR_EL1, CORTEX_X4_CPUPWRCTLR_EL1_CORE_PWRDN_BIT + + apply_erratum cortex_x4, ERRATUM(2740089), ERRATA_X4_2740089, NO_GET_CPU_REV + isb ret endfunc cortex_x4_core_pwr_dwn -errata_report_shim cortex_x4 - /* --------------------------------------------- * This function provides Cortex X4-specific * register information for crash reporting. diff --git a/lib/cpus/aarch64/cortex_x925.S b/lib/cpus/aarch64/cortex_x925.S new file mode 100644 index 00000000..8109ffba --- /dev/null +++ b/lib/cpus/aarch64/cortex_x925.S @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2023-2024, Arm Limited. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <arch.h> +#include <asm_macros.S> +#include <common/bl_common.h> +#include <cortex_x925.h> +#include <cpu_macros.S> +#include <plat_macros.S> + +/* Hardware handled coherency */ +#if HW_ASSISTED_COHERENCY == 0 +#error "Cortex-X925 must be compiled with HW_ASSISTED_COHERENCY enabled" +#endif + +/* 64-bit only core */ +#if CTX_INCLUDE_AARCH32_REGS == 1 +#error "Cortex-X925 supports only AArch64. Compile with CTX_INCLUDE_AARCH32_REGS=0" +#endif + +cpu_reset_func_start cortex_x925 + /* Disable speculative loads */ + msr SSBS, xzr +cpu_reset_func_end cortex_x925 + + /* ---------------------------------------------------- + * HW will do the cache maintenance while powering down + * ---------------------------------------------------- + */ +func cortex_x925_core_pwr_dwn + /* --------------------------------------------------- + * Enable CPU power down bit in power control register + * --------------------------------------------------- + */ + sysreg_bit_set CORTEX_X925_CPUPWRCTLR_EL1, CORTEX_X925_CPUPWRCTLR_EL1_CORE_PWRDN_BIT + isb + ret +endfunc cortex_x925_core_pwr_dwn + + /* --------------------------------------------- + * This function provides Cortex-X925 specific + * register information for crash reporting. + * It needs to return with x6 pointing to + * a list of register names in ascii and + * x8 - x15 having values of registers to be + * reported. + * --------------------------------------------- + */ +.section .rodata.cortex_x925_regs, "aS" +cortex_x925_regs: /* The ascii list of register names to be reported */ + .asciz "cpuectlr_el1", "" + +func cortex_x925_cpu_reg_dump + adr x6, cortex_x925_regs + mrs x8, CORTEX_X925_CPUECTLR_EL1 + ret +endfunc cortex_x925_cpu_reg_dump + +declare_cpu_ops cortex_x925, CORTEX_X925_MIDR, \ + cortex_x925_reset_func, \ + cortex_x925_core_pwr_dwn diff --git a/lib/cpus/aarch64/cpu_helpers.S b/lib/cpus/aarch64/cpu_helpers.S index 1ae31803..3aa4f155 100644 --- a/lib/cpus/aarch64/cpu_helpers.S +++ b/lib/cpus/aarch64/cpu_helpers.S @@ -165,13 +165,13 @@ func get_cpu_ops_ptr and w2, w2, w3 /* Get the cpu_ops end location */ - adr x5, (__CPU_OPS_END__ + CPU_MIDR) + adr_l x5, (__CPU_OPS_END__ + CPU_MIDR) /* Initialize the return parameter */ mov x0, #0 1: /* Get the cpu_ops start location */ - adr x4, (__CPU_OPS_START__ + CPU_MIDR) + adr_l x4, (__CPU_OPS_START__ + CPU_MIDR) 2: /* Check if we have reached end of list */ diff --git a/lib/cpus/aarch64/denver.S b/lib/cpus/aarch64/denver.S index 884281d3..ca250d37 100644 --- a/lib/cpus/aarch64/denver.S +++ b/lib/cpus/aarch64/denver.S @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015-2023, Arm Limited and Contributors. All rights reserved. + * Copyright (c) 2015-2024, Arm Limited and Contributors. All rights reserved. * Copyright (c) 2020-2022, NVIDIA Corporation. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause @@ -296,8 +296,6 @@ func denver_cluster_pwr_dwn ret endfunc denver_cluster_pwr_dwn -errata_report_shim denver - /* --------------------------------------------- * This function provides Denver specific * register information for crash reporting. diff --git a/lib/cpus/aarch64/dsu_helpers.S b/lib/cpus/aarch64/dsu_helpers.S index a34b9a67..3c5bf2ea 100644 --- a/lib/cpus/aarch64/dsu_helpers.S +++ b/lib/cpus/aarch64/dsu_helpers.S @@ -24,6 +24,7 @@ */ .globl check_errata_dsu_798953 .globl errata_dsu_798953_wa + .globl dsu_pwr_dwn func check_errata_dsu_798953 mov x2, #ERRATA_APPLIES @@ -151,13 +152,22 @@ endfunc errata_dsu_936184_wa * This function is called from both assembly and C environment. So it * follows AAPCS. * - * Clobbers: x0-x3 + * Clobbers: x0-x4 * ----------------------------------------------------------------------- */ .globl check_errata_dsu_2313941 .globl errata_dsu_2313941_wa func check_errata_dsu_2313941 + mov x4, x30 + bl is_scu_present_in_dsu + cmp x0, xzr + /* Default error status */ + mov x0, #ERRATA_NOT_APPLIES + + /* If SCU is not present, return without applying patch */ + b.eq 1f + mov x2, #ERRATA_APPLIES mov x3, #ERRATA_NOT_APPLIES @@ -170,7 +180,8 @@ func check_errata_dsu_2313941 mov x1, #(0x31 << CLUSTERIDR_REV_SHIFT) cmp x0, x1 csel x0, x2, x3, LS - ret +1: + ret x4 endfunc check_errata_dsu_2313941 /* -------------------------------------------------- @@ -192,3 +203,15 @@ func errata_dsu_2313941_wa 1: ret x8 endfunc errata_dsu_2313941_wa + + /* --------------------------------------------- + * controls power features of the cluster + * 1. Cache portion power not request + * 2. Disable the retention circuit + * --------------------------------------------- + */ +func dsu_pwr_dwn + msr CLUSTERPWRCTLR_EL1, xzr + isb + ret +endfunc dsu_pwr_dwn diff --git a/lib/cpus/aarch64/generic.S b/lib/cpus/aarch64/generic.S index ef1f048a..5d7a857e 100644 --- a/lib/cpus/aarch64/generic.S +++ b/lib/cpus/aarch64/generic.S @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, Arm Limited. All rights reserved. + * Copyright (c) 2020-2024, Arm Limited. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -79,7 +79,6 @@ endfunc generic_cluster_pwr_dwn * Unimplemented functions. * --------------------------------------------- */ -.equ generic_errata_report, 0 .equ generic_cpu_reg_dump, 0 .equ generic_reset_func, 0 diff --git a/lib/cpus/aarch64/neoverse_e1.S b/lib/cpus/aarch64/neoverse_e1.S index 45bd8d31..4bc95d05 100644 --- a/lib/cpus/aarch64/neoverse_e1.S +++ b/lib/cpus/aarch64/neoverse_e1.S @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018-2023, Arm Limited and Contributors. All rights reserved. + * Copyright (c) 2018-2024, Arm Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -42,8 +42,6 @@ func neoverse_e1_cpu_pwr_dwn ret endfunc neoverse_e1_cpu_pwr_dwn -errata_report_shim neoverse_e1 - .section .rodata.neoverse_e1_regs, "aS" neoverse_e1_regs: /* The ascii list of register names to be reported */ .asciz "cpuectlr_el1", "" diff --git a/lib/cpus/aarch64/neoverse_n1.S b/lib/cpus/aarch64/neoverse_n1.S index 36a7ee75..f727226b 100644 --- a/lib/cpus/aarch64/neoverse_n1.S +++ b/lib/cpus/aarch64/neoverse_n1.S @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2023, Arm Limited and Contributors. All rights reserved. + * Copyright (c) 2017-2024, Arm Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -236,14 +236,12 @@ func neoverse_n1_core_pwr_dwn */ sysreg_bit_set NEOVERSE_N1_CPUPWRCTLR_EL1, NEOVERSE_N1_CORE_PWRDN_EN_MASK - apply_erratum neoverse_n1, ERRATUM(2743102), ERRATA_N1_2743102 + apply_erratum neoverse_n1, ERRATUM(2743102), ERRATA_N1_2743102, NO_GET_CPU_REV isb ret endfunc neoverse_n1_core_pwr_dwn -errata_report_shim neoverse_n1 - /* * Handle trap of EL0 IC IVAU instructions to EL3 by executing a TLB * inner-shareable invalidation to an arbitrary address followed by a DSB. diff --git a/lib/cpus/aarch64/neoverse_n2.S b/lib/cpus/aarch64/neoverse_n2.S index 477522fe..d2237f1c 100644 --- a/lib/cpus/aarch64/neoverse_n2.S +++ b/lib/cpus/aarch64/neoverse_n2.S @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020-2023, Arm Limited. All rights reserved. + * Copyright (c) 2020-2024, Arm Limited. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -252,9 +252,9 @@ cpu_reset_func_start neoverse_n2 #if ENABLE_FEAT_AMU /* Make sure accesses from EL0/EL1 and EL2 are not trapped to EL3 */ - sysreg_bit_set cptr_el3, TAM_BIT + sysreg_bit_clear cptr_el3, TAM_BIT /* Make sure accesses from EL0/EL1 are not trapped to EL2 */ - sysreg_bit_set cptr_el2, TAM_BIT + sysreg_bit_clear cptr_el2, TAM_BIT /* No need to enable the counters as this would be done at el3 exit */ #endif @@ -265,8 +265,7 @@ cpu_reset_func_start neoverse_n2 cpu_reset_func_end neoverse_n2 func neoverse_n2_core_pwr_dwn - - apply_erratum neoverse_n2, ERRATUM(2009478), ERRATA_N2_2009478 + apply_erratum neoverse_n2, ERRATUM(2009478), ERRATA_N2_2009478, NO_GET_CPU_REV apply_erratum neoverse_n2, ERRATUM(2326639), ERRATA_N2_2326639, NO_GET_CPU_REV /* --------------------------------------------------- @@ -276,14 +275,12 @@ func neoverse_n2_core_pwr_dwn */ sysreg_bit_set NEOVERSE_N2_CPUPWRCTLR_EL1, NEOVERSE_N2_CORE_PWRDN_EN_BIT - apply_erratum neoverse_n2, ERRATUM(2743089), ERRATA_N2_2743089 + apply_erratum neoverse_n2, ERRATUM(2743089), ERRATA_N2_2743089, NO_GET_CPU_REV isb ret endfunc neoverse_n2_core_pwr_dwn -errata_report_shim neoverse_n2 - /* --------------------------------------------- * This function provides Neoverse N2 specific * register information for crash reporting. diff --git a/lib/cpus/aarch64/neoverse_n3.S b/lib/cpus/aarch64/neoverse_n3.S new file mode 100644 index 00000000..d96c9d46 --- /dev/null +++ b/lib/cpus/aarch64/neoverse_n3.S @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2023-2024, Arm Limited. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <arch.h> +#include <asm_macros.S> +#include <common/bl_common.h> +#include <neoverse_n3.h> +#include <cpu_macros.S> +#include <plat_macros.S> + +/* Hardware handled coherency */ +#if HW_ASSISTED_COHERENCY == 0 +#error "Neoverse-N3 must be compiled with HW_ASSISTED_COHERENCY enabled" +#endif + +/* 64-bit only core */ +#if CTX_INCLUDE_AARCH32_REGS == 1 +#error "Neoverse-N3 supports only AArch64. Compile with CTX_INCLUDE_AARCH32_REGS=0" +#endif + +cpu_reset_func_start neoverse_n3 + /* Disable speculative loads */ + msr SSBS, xzr + +#if NEOVERSE_Nx_EXTERNAL_LLC + /* Some systems may have External LLC, core needs to be made aware */ + sysreg_bit_set NEOVERSE_N3_CPUECTLR_EL1, NEOVERSE_N3_CPUECTLR_EL1_EXTLLC_BIT +#endif +cpu_reset_func_end neoverse_n3 + + /* ---------------------------------------------------- + * HW will do the cache maintenance while powering down + * ---------------------------------------------------- + */ +func neoverse_n3_core_pwr_dwn + /* --------------------------------------------------- + * Enable CPU power down bit in power control register + * --------------------------------------------------- + */ + sysreg_bit_set NEOVERSE_N3_CPUPWRCTLR_EL1, NEOVERSE_N3_CPUPWRCTLR_EL1_CORE_PWRDN_BIT + isb + ret +endfunc neoverse_n3_core_pwr_dwn + + /* --------------------------------------------- + * This function provides Neoverse-N3 specific + * register information for crash reporting. + * It needs to return with x6 pointing to + * a list of register names in ascii and + * x8 - x15 having values of registers to be + * reported. + * --------------------------------------------- + */ +.section .rodata.neoverse_n3_regs, "aS" +neoverse_n3_regs: /* The ascii list of register names to be reported */ + .asciz "cpuectlr_el1", "" + +func neoverse_n3_cpu_reg_dump + adr x6, neoverse_n3_regs + mrs x8, NEOVERSE_N3_CPUECTLR_EL1 + ret +endfunc neoverse_n3_cpu_reg_dump + +declare_cpu_ops neoverse_n3, NEOVERSE_N3_MIDR, \ + neoverse_n3_reset_func, \ + neoverse_n3_core_pwr_dwn diff --git a/lib/cpus/aarch64/neoverse_poseidon.S b/lib/cpus/aarch64/neoverse_poseidon.S deleted file mode 100644 index 3b3245d8..00000000 --- a/lib/cpus/aarch64/neoverse_poseidon.S +++ /dev/null @@ -1,86 +0,0 @@ -/* - * Copyright (c) 2022-2023, Arm Limited. All rights reserved. - * - * SPDX-License-Identifier: BSD-3-Clause - */ - -#include <arch.h> -#include <asm_macros.S> -#include <common/bl_common.h> -#include <neoverse_poseidon.h> -#include <cpu_macros.S> -#include <plat_macros.S> -#include "wa_cve_2022_23960_bhb_vector.S" - -/* Hardware handled coherency */ -#if HW_ASSISTED_COHERENCY == 0 -#error "Neoverse Poseidon must be compiled with HW_ASSISTED_COHERENCY enabled" -#endif - -/* 64-bit only core */ -#if CTX_INCLUDE_AARCH32_REGS == 1 -#error "Neoverse Poseidon supports only AArch64. Compile with CTX_INCLUDE_AARCH32_REGS=0" -#endif - -#if WORKAROUND_CVE_2022_23960 - wa_cve_2022_23960_bhb_vector_table NEOVERSE_POSEIDON_BHB_LOOP_COUNT, neoverse_poseidon -#endif /* WORKAROUND_CVE_2022_23960 */ - -workaround_reset_start neoverse_poseidon, CVE(2022,23960), WORKAROUND_CVE_2022_23960 -#if IMAGE_BL31 - /* - * The Neoverse-poseidon generic vectors are overridden to apply errata - * mitigation on exception entry from lower ELs. - */ - override_vector_table wa_cve_vbar_neoverse_poseidon - -#endif /* IMAGE_BL31 */ -workaround_reset_end neoverse_poseidon, CVE(2022,23960) - -check_erratum_chosen neoverse_poseidon, CVE(2022, 23960), WORKAROUND_CVE_2022_23960 - - /* --------------------------------------------- - * HW will do the cache maintenance while powering down - * --------------------------------------------- - */ -func neoverse_poseidon_core_pwr_dwn - /* --------------------------------------------- - * Enable CPU power down bit in power control register - * --------------------------------------------- - */ - sysreg_bit_set NEOVERSE_POSEIDON_CPUPWRCTLR_EL1, \ - NEOVERSE_POSEIDON_CPUPWRCTLR_EL1_CORE_PWRDN_BIT - - isb - ret -endfunc neoverse_poseidon_core_pwr_dwn - -cpu_reset_func_start neoverse_poseidon - /* Disable speculative loads */ - msr SSBS, xzr -cpu_reset_func_end neoverse_poseidon - -errata_report_shim neoverse_poseidon - - /* --------------------------------------------- - * This function provides Neoverse-Poseidon specific - * register information for crash reporting. - * It needs to return with x6 pointing to - * a list of register names in ascii and - * x8 - x15 having values of registers to be - * reported. - * --------------------------------------------- - */ -.section .rodata.neoverse_poseidon_regs, "aS" -neoverse_poseidon_regs: /* The ascii list of register names to be reported */ - .asciz "cpuectlr_el1", "" - -func neoverse_poseidon_cpu_reg_dump - adr x6, neoverse_poseidon_regs - mrs x8, NEOVERSE_POSEIDON_CPUECTLR_EL1 - ret -endfunc neoverse_poseidon_cpu_reg_dump - -declare_cpu_ops neoverse_poseidon, NEOVERSE_POSEIDON_MIDR, \ - neoverse_poseidon_reset_func, \ - neoverse_poseidon_core_pwr_dwn diff --git a/lib/cpus/aarch64/neoverse_v1.S b/lib/cpus/aarch64/neoverse_v1.S index 2a49134f..1ec3e944 100644 --- a/lib/cpus/aarch64/neoverse_v1.S +++ b/lib/cpus/aarch64/neoverse_v1.S @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019-2023, Arm Limited. All rights reserved. + * Copyright (c) 2019-2024, Arm Limited. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -196,6 +196,13 @@ workaround_reset_end neoverse_v1, ERRATUM(2294912) check_erratum_ls neoverse_v1, ERRATUM(2294912), CPU_REV(1, 2) +workaround_runtime_start neoverse_v1, ERRATUM(2348377), ERRATA_V1_2348377 + /* Set bit 61 in CPUACTLR5_EL1 */ + sysreg_bit_set NEOVERSE_V1_ACTLR5_EL1, NEOVERSE_V1_ACTLR5_EL1_BIT_61 +workaround_runtime_end neoverse_v1, ERRATUM(2348377) + +check_erratum_ls neoverse_v1, ERRATUM(2348377), CPU_REV(1, 1) + workaround_reset_start neoverse_v1, ERRATUM(2372203), ERRATA_V1_2372203 /* Set bit 40 in ACTLR2_EL1 */ sysreg_bit_set NEOVERSE_V1_ACTLR2_EL1, NEOVERSE_V1_ACTLR2_EL1_BIT_40 @@ -246,14 +253,12 @@ func neoverse_v1_core_pwr_dwn * --------------------------------------------- */ sysreg_bit_set NEOVERSE_V1_CPUPWRCTLR_EL1, NEOVERSE_V1_CPUPWRCTLR_EL1_CORE_PWRDN_BIT - apply_erratum neoverse_v1, ERRATUM(2743093), ERRATA_V1_2743093 + apply_erratum neoverse_v1, ERRATUM(2743093), ERRATA_V1_2743093, NO_GET_CPU_REV isb ret endfunc neoverse_v1_core_pwr_dwn -errata_report_shim neoverse_v1 - cpu_reset_func_start neoverse_v1 /* Disable speculative loads */ msr SSBS, xzr diff --git a/lib/cpus/aarch64/neoverse_v2.S b/lib/cpus/aarch64/neoverse_v2.S index bfd088d5..ca66f8dd 100644 --- a/lib/cpus/aarch64/neoverse_v2.S +++ b/lib/cpus/aarch64/neoverse_v2.S @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021-2023, Arm Limited. All rights reserved. + * Copyright (c) 2021-2024, Arm Limited. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -29,6 +29,25 @@ workaround_reset_end neoverse_v2, ERRATUM(2331132) check_erratum_ls neoverse_v2, ERRATUM(2331132), CPU_REV(0, 2) +workaround_reset_start neoverse_v2, ERRATUM(2618597), ERRATA_V2_2618597 + /* Disable retention control for WFI and WFE. */ + mrs x0, NEOVERSE_V2_CPUPWRCTLR_EL1 + bfi x0, xzr, #NEOVERSE_V2_CPUPWRCTLR_EL1_WFI_RET_CTRL_SHIFT, \ + #NEOVERSE_V2_CPUPWRCTLR_EL1_WFI_RET_CTRL_WIDTH + bfi x0, xzr, #NEOVERSE_V2_CPUPWRCTLR_EL1_WFE_RET_CTRL_SHIFT, \ + #NEOVERSE_V2_CPUPWRCTLR_EL1_WFE_RET_CTRL_WIDTH + msr NEOVERSE_V2_CPUPWRCTLR_EL1, x0 +workaround_reset_end neoverse_v2, ERRATUM(2618597) + +check_erratum_ls neoverse_v2, ERRATUM(2618597), CPU_REV(0, 1) + +workaround_reset_start neoverse_v2, ERRATUM(2662553), ERRATA_V2_2662553 + sysreg_bitfield_insert NEOVERSE_V2_CPUECTLR2_EL1, NEOVERSE_V2_CPUECTLR2_EL1_TXREQ_STATIC_FULL, \ + NEOVERSE_V2_CPUECTLR2_EL1_TXREQ_LSB, NEOVERSE_V2_CPUECTLR2_EL1_TXREQ_WIDTH +workaround_reset_end neoverse_v2, ERRATUM(2662553) + +check_erratum_ls neoverse_v2, ERRATUM(2662553), CPU_REV(0, 1) + workaround_reset_start neoverse_v2, ERRATUM(2719105), ERRATA_V2_2719105 sysreg_bit_set NEOVERSE_V2_CPUACTLR2_EL1, NEOVERSE_V2_CPUACTLR2_EL1_BIT_0 workaround_reset_end neoverse_v2, ERRATUM(2719105) @@ -81,7 +100,7 @@ func neoverse_v2_core_pwr_dwn * --------------------------------------------------- */ sysreg_bit_set NEOVERSE_V2_CPUPWRCTLR_EL1, NEOVERSE_V2_CPUPWRCTLR_EL1_CORE_PWRDN_BIT - apply_erratum neoverse_v2, ERRATUM(2801372), ERRATA_V2_2801372 + apply_erratum neoverse_v2, ERRATUM(2801372), ERRATA_V2_2801372, NO_GET_CPU_REV isb ret @@ -90,9 +109,13 @@ endfunc neoverse_v2_core_pwr_dwn cpu_reset_func_start neoverse_v2 /* Disable speculative loads */ msr SSBS, xzr + +#if NEOVERSE_Vx_EXTERNAL_LLC + /* Some systems may have External LLC, core needs to be made aware */ + sysreg_bit_set NEOVERSE_V2_CPUECTLR_EL1, NEOVERSE_V2_CPUECTLR_EL1_EXTLLC_BIT +#endif cpu_reset_func_end neoverse_v2 -errata_report_shim neoverse_v2 /* --------------------------------------------- * This function provides Neoverse V2- * specific register information for crash diff --git a/lib/cpus/aarch64/neoverse_v3.S b/lib/cpus/aarch64/neoverse_v3.S new file mode 100644 index 00000000..01ac38f1 --- /dev/null +++ b/lib/cpus/aarch64/neoverse_v3.S @@ -0,0 +1,88 @@ +/* + * Copyright (c) 2022-2024, Arm Limited. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <arch.h> +#include <asm_macros.S> +#include <common/bl_common.h> +#include <neoverse_v3.h> +#include <cpu_macros.S> +#include <plat_macros.S> +#include "wa_cve_2022_23960_bhb_vector.S" + +/* Hardware handled coherency */ +#if HW_ASSISTED_COHERENCY == 0 +#error "Neoverse V3 must be compiled with HW_ASSISTED_COHERENCY enabled" +#endif + +/* 64-bit only core */ +#if CTX_INCLUDE_AARCH32_REGS == 1 +#error "Neoverse V3 supports only AArch64. Compile with CTX_INCLUDE_AARCH32_REGS=0" +#endif + +#if WORKAROUND_CVE_2022_23960 + wa_cve_2022_23960_bhb_vector_table NEOVERSE_V3_BHB_LOOP_COUNT, neoverse_v3 +#endif /* WORKAROUND_CVE_2022_23960 */ + +workaround_reset_start neoverse_v3, CVE(2022,23960), WORKAROUND_CVE_2022_23960 +#if IMAGE_BL31 + /* + * The Neoverse V3 generic vectors are overridden to apply errata + * mitigation on exception entry from lower ELs. + */ + override_vector_table wa_cve_vbar_neoverse_v3 + +#endif /* IMAGE_BL31 */ +workaround_reset_end neoverse_v3, CVE(2022,23960) + +check_erratum_chosen neoverse_v3, CVE(2022, 23960), WORKAROUND_CVE_2022_23960 + + /* --------------------------------------------- + * HW will do the cache maintenance while powering down + * --------------------------------------------- + */ +func neoverse_v3_core_pwr_dwn + /* --------------------------------------------- + * Enable CPU power down bit in power control register + * --------------------------------------------- + */ + sysreg_bit_set NEOVERSE_V3_CPUPWRCTLR_EL1, \ + NEOVERSE_V3_CPUPWRCTLR_EL1_CORE_PWRDN_BIT + + isb + ret +endfunc neoverse_v3_core_pwr_dwn + +cpu_reset_func_start neoverse_v3 + /* Disable speculative loads */ + msr SSBS, xzr +cpu_reset_func_end neoverse_v3 + + /* --------------------------------------------- + * This function provides Neoverse V3 specific + * register information for crash reporting. + * It needs to return with x6 pointing to + * a list of register names in ascii and + * x8 - x15 having values of registers to be + * reported. + * --------------------------------------------- + */ +.section .rodata.neoverse_v3_regs, "aS" +neoverse_v3_regs: /* The ascii list of register names to be reported */ + .asciz "cpuectlr_el1", "" + +func neoverse_v3_cpu_reg_dump + adr x6, neoverse_v3_regs + mrs x8, NEOVERSE_V3_CPUECTLR_EL1 + ret +endfunc neoverse_v3_cpu_reg_dump + +declare_cpu_ops neoverse_v3, NEOVERSE_V3_VNAE_MIDR, \ + neoverse_v3_reset_func, \ + neoverse_v3_core_pwr_dwn + +declare_cpu_ops neoverse_v3, NEOVERSE_V3_MIDR, \ + neoverse_v3_reset_func, \ + neoverse_v3_core_pwr_dwn diff --git a/lib/cpus/aarch64/nevis.S b/lib/cpus/aarch64/nevis.S index 36830a9b..0180ab7d 100644 --- a/lib/cpus/aarch64/nevis.S +++ b/lib/cpus/aarch64/nevis.S @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023, Arm Limited. All rights reserved. + * Copyright (c) 2023-2024, Arm Limited. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -40,8 +40,6 @@ func nevis_core_pwr_dwn ret endfunc nevis_core_pwr_dwn -errata_report_shim nevis - .section .rodata.nevis_regs, "aS" nevis_regs: /* The ASCII list of register names to be reported */ .asciz "cpuectlr_el1", "" diff --git a/lib/cpus/aarch64/qemu_max.S b/lib/cpus/aarch64/qemu_max.S index 00963bc3..fb03cf15 100644 --- a/lib/cpus/aarch64/qemu_max.S +++ b/lib/cpus/aarch64/qemu_max.S @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2023, Arm Limited and Contributors. All rights reserved. + * Copyright (c) 2014-2024, Arm Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -47,8 +47,6 @@ func qemu_max_cluster_pwr_dwn b dcsw_op_all endfunc qemu_max_cluster_pwr_dwn -errata_report_shim qemu_max - /* --------------------------------------------- * This function provides cpu specific * register information for crash reporting. diff --git a/lib/cpus/aarch64/rainier.S b/lib/cpus/aarch64/rainier.S index c770f54f..ea687be6 100644 --- a/lib/cpus/aarch64/rainier.S +++ b/lib/cpus/aarch64/rainier.S @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020-2023, Arm Limited. All rights reserved. + * Copyright (c) 2020-2024, Arm Limited. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -80,8 +80,6 @@ func rainier_core_pwr_dwn ret endfunc rainier_core_pwr_dwn -errata_report_shim rainier - /* --------------------------------------------- * This function provides Rainier specific * register information for crash reporting. diff --git a/lib/cpus/aarch64/travis.S b/lib/cpus/aarch64/travis.S index 2abefe94..e8b3860b 100644 --- a/lib/cpus/aarch64/travis.S +++ b/lib/cpus/aarch64/travis.S @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023, Arm Limited. All rights reserved. + * Copyright (c) 2023-2024, Arm Limited. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -38,7 +38,7 @@ func travis_core_pwr_dwn mrs x0, ID_AA64PFR1_EL1 ubfx x0, x0, #ID_AA64PFR1_EL1_SME_SHIFT, \ #ID_AA64PFR1_EL1_SME_WIDTH - cmp x0, #ID_AA64PFR1_EL1_SME_NOT_SUPPORTED + cmp x0, #SME_NOT_IMPLEMENTED b.eq 1f msr TRAVIS_SVCRSM, xzr msr TRAVIS_SVCRZA, xzr @@ -54,8 +54,6 @@ func travis_core_pwr_dwn ret endfunc travis_core_pwr_dwn -errata_report_shim travis - .section .rodata.travis_regs, "aS" travis_regs: /* The ASCII list of register names to be reported */ .asciz "cpuectlr_el1", "" diff --git a/lib/cpus/cpu-ops.mk b/lib/cpus/cpu-ops.mk index 434ee081..4c207850 100644 --- a/lib/cpus/cpu-ops.mk +++ b/lib/cpus/cpu-ops.mk @@ -1,5 +1,5 @@ # -# Copyright (c) 2014-2023, Arm Limited and Contributors. All rights reserved. +# Copyright (c) 2014-2024, Arm Limited and Contributors. All rights reserved. # Copyright (c) 2020-2022, NVIDIA Corporation. All rights reserved. # # SPDX-License-Identifier: BSD-3-Clause @@ -388,11 +388,19 @@ CPU_FLAG_LIST += ERRATA_A78C_2376749 # to revisions r0p1 and r0p2 of the A78C cpu. It is still open. CPU_FLAG_LIST += ERRATA_A78C_2395411 +# Flag to apply erratum 2683027 workaround during reset. This erratum applies +# to revisions r0p1 and r0p2 of the A78C cpu. It is still open. +CPU_FLAG_LIST += ERRATA_A78C_2683027 + # Flag to apply erratum 2712575 workaround for non-arm interconnect ip. This # erratum applies to revisions r0p1 and r0p2 of the A78C cpu. # It is still open. CPU_FLAG_LIST += ERRATA_A78C_2712575 +# Flag to apply erratum 2743232 workaround during reset. This erratum applies +# to revisions r0p1 and r0p2 of the A78C cpu. It is still open. +CPU_FLAG_LIST += ERRATA_A78C_2743232 + # Flag to apply erratum 2772121 workaround during powerdown. This erratum # applies to revisions r0p0, r0p1 and r0p2 of the A78C cpu. It is still open. CPU_FLAG_LIST += ERRATA_A78C_2772121 @@ -520,6 +528,10 @@ CPU_FLAG_LIST += ERRATA_V1_2216392 # to revisions r0p0, r1p0, and r1p1 and r1p2 of the Neoverse V1 cpu and is still open. CPU_FLAG_LIST += ERRATA_V1_2294912 +# Flag to apply erratum 2348377 workaround during reset. This erratum applies +# to revisions r0p0, r1p0 and r1p1 of the Neoverse V1 cpu and is fixed in r1p2. +CPU_FLAG_LIST += ERRATA_V1_2348377 + # Flag to apply erratum 2372203 workaround during reset. This erratum applies # to revisions r0p0, r1p0 and r1p1 of the Neoverse V1 cpu and is still open. CPU_FLAG_LIST += ERRATA_V1_2372203 @@ -617,6 +629,11 @@ CPU_FLAG_LIST += ERRATA_A710_2742423 # still open. CPU_FLAG_LIST += ERRATA_A710_2768515 +# Flag to apply erratum 2778471 workaround during reset. This erratum applies +# to revisions r0p0, r1p0, r2p0, r2p1 of the Cortex-A710 cpu and is still +# open. +CPU_FLAG_LIST += ERRATA_A710_2778471 + # Flag to apply erratum 2002655 workaround during reset. This erratum applies # to revisions r0p0 of the Neoverse-N2 cpu and is fixed in r0p1. CPU_FLAG_LIST += ERRATA_N2_2002655 @@ -752,23 +769,84 @@ CPU_FLAG_LIST += ERRATA_X2_2742423 # still open. CPU_FLAG_LIST += ERRATA_X2_2768515 +# Flag to apply erratum 2778471 workaround during reset. This erratum applies +# to revisions r0p0, r1p0, r2p0, r2p1 of the Cortex-X2 cpu and it is still open. +CPU_FLAG_LIST += ERRATA_X2_2778471 + # Flag to apply erratum 2070301 workaround on reset. This erratum applies # to revisions r0p0, r1p0, r1p1 and r1p2 of the Cortex-X3 cpu and is # still open. CPU_FLAG_LIST += ERRATA_X3_2070301 +# Flag to apply erratum 2266875 workaround during reset. This erratum applies +# to revisions r0p0 and r1p0 of the Cortex-X3 cpu, it is fixed in r1p1. +CPU_FLAG_LIST += ERRATA_X3_2266875 + +# Flag to apply erratum 2302506 workaround during reset. This erratum applies +# to revisions r0p0, r1p0 and r1p1 of the Cortex-X3 cpu, it is fixed in r1p2. +CPU_FLAG_LIST += ERRATA_X3_2302506 + # Flag to apply erratum 2313909 workaround on powerdown. This erratum applies # to revisions r0p0 and r1p0 of the Cortex-X3 cpu, it is fixed in r1p1. CPU_FLAG_LIST += ERRATA_X3_2313909 +# Flag to apply erratum 2372204 workaround during reset. This erratum applies +# to revisions r0p0 and r1p0 of the Cortex-X3 cpu, it is fixed in r1p1. +CPU_FLAG_LIST += ERRATA_X3_2372204 + # Flag to apply erratum 2615812 workaround on powerdown. This erratum applies -# to revisions r0p0, r1p0, r1p1 of the Cortex-X3 cpu, it is still open. +# to revisions r0p0, r1p0, r1p1 of the Cortex-X3 cpu, it is fixed in r1p2. CPU_FLAG_LIST += ERRATA_X3_2615812 +# Flag to apply erratum 2641945 workaround on reset. This erratum applies +# to revisions r0p0 and r1p0 of the Cortex-X3 cpu, it is fixed in r1p1. +CPU_FLAG_LIST += ERRATA_X3_2641945 + +# Flag to apply erratum 2701951 workaround for non-arm interconnect ip. +# This erratum applies to revisions r0p0, r1p0, and r1p1. Its is fixed in r1p2. +CPU_FLAG_LIST += ERRATA_X3_2701951 + # Flag to apply erratum 2742421 workaround on reset. This erratum applies # to revisions r0p0, r1p0 and r1p1 of the Cortex-X3 cpu, it is fixed in r1p2. CPU_FLAG_LIST += ERRATA_X3_2742421 +# Flag to apply erratum 2743088 workaround on powerdown. This erratum applies +# to revisions r0p0, r1p0 and r1p1 of the Cortex-X3 cpu, it is fixed in r1p2. +CPU_FLAG_LIST += ERRATA_X3_2743088 + +# Flag to apply erratum 2779509 workaround on reset. This erratum applies +# to revisions r0p0, r1p0, r1p1 of the Cortex-X3 cpu, it is fixed in r1p2. +CPU_FLAG_LIST += ERRATA_X3_2779509 + +# Flag to apply erratum 2701112 workaround for platforms that do not use an +# Arm interconnect IP. This erratum applies to revisions r0p0 of the Cortex-X4 +# cpu and is fixed in r0p1. +CPU_FLAG_LIST += ERRATA_X4_2701112 + +# Flag to apply erratum 2726228 workaround during warmboot. This erratum +# applies to all revisions <= r0p1 of the Cortex-X4 cpu, it is fixed in r0p2. +CPU_FLAG_LIST += ERRATA_X4_2726228 + +# Flag to apply erratum 2740089 workaround during powerdown. This erratum +# applies to all revisions <= r0p1 of the Cortex-X4 cpu, it is fixed in r0p2. +CPU_FLAG_LIST += ERRATA_X4_2740089 + +# Flag to apply erratum 2763018 workaround on reset. This erratum applies +# to revisions r0p0 and r0p1 of the Cortex-X4 cpu. It is fixed in r0p2. +CPU_FLAG_LIST += ERRATA_X4_2763018 + +# Flag to apply erratum 2816013 workaround on reset. This erratum applies +# to revisions r0p0 and r0p1 of the Cortex-X4 cpu. It is fixed in r0p2. +CPU_FLAG_LIST += ERRATA_X4_2816013 + +# Flag to apply erratum 2897503 workaround on reset. This erratum applies +# to revisions r0p0 and r0p1 of the Cortex-X4 cpu. It is fixed in r0p2. +CPU_FLAG_LIST += ERRATA_X4_2897503 + +# Flag to apply erratum 3076789 workaround on reset. This erratum applies +# to revisions r0p0 and r0p1 of the Cortex-X4 cpu. It is fixed in r0p2. +CPU_FLAG_LIST += ERRATA_X4_3076789 + # Flag to apply erratum 1922240 workaround during reset. This erratum applies # to revision r0p0 of the Cortex-A510 cpu and is fixed in r0p1. CPU_FLAG_LIST += ERRATA_A510_1922240 @@ -822,10 +900,30 @@ CPU_FLAG_LIST += ERRATA_A510_2666669 # Cortex-A510 cpu and is fixed in r1p3. CPU_FLAG_LIST += ERRATA_A510_2684597 +# Flag to apply erratum 2630792 workaround during reset. This erratum applies +# to revisions r0p0, r0p1 of the Cortex-A520 cpu and is still open. +CPU_FLAG_LIST += ERRATA_A520_2630792 + +# Flag to apply erratum 2858100 workaround during reset. This erratum +# applies to revision r0p0 and r0p1 of the Cortex-A520 cpu and is still open. +CPU_FLAG_LIST += ERRATA_A520_2858100 + +# Flag to apply erratum 2938996 workaround during reset. This erratum +# applies to revision r0p0 and r0p1 of the Cortex-A520 cpu and is fixed in r0p2. +CPU_FLAG_LIST += ERRATA_A520_2938996 + # Flag to apply erratum 2331132 workaround during reset. This erratum applies # to revisions r0p0, r0p1 and r0p2. It is still open. CPU_FLAG_LIST += ERRATA_V2_2331132 +# Flag to apply erratum 2618597 workaround during reset. This erratum applies +# to revisions r0p0 and r0p1. It is fixed in r0p2. +CPU_FLAG_LIST += ERRATA_V2_2618597 + +# Flag to apply erratum 2662553 workaround during reset. This erratum applies +# to revisions r0p0 and r0p1. It is fixed in r0p2. +CPU_FLAG_LIST += ERRATA_V2_2662553 + # Flag to apply erratum 2719103 workaround for non-arm interconnect ip. This # erratum applies to revisions r0p0, rop1. Fixed in r0p2. CPU_FLAG_LIST += ERRATA_V2_2719103 @@ -846,9 +944,49 @@ CPU_FLAG_LIST += ERRATA_V2_2779510 # This erratum applies to revisions r0p0, r0p1. Fixed in r0p2. CPU_FLAG_LIST += ERRATA_V2_2801372 -# Flag to apply erratum 2701951 workaround for non-arm interconnect ip. -# This erratum applies to revisions r0p0, r1p0, and r1p1. Its is fixed in r1p2. -CPU_FLAG_LIST += ERRATA_A715_2701951 +# Flag to apply erratum 2331818 workaround during reset. This erratum applies +# to revisions r0p0 and r1p0. It is fixed in r1p1. +CPU_FLAG_LIST += ERRATA_A715_2331818 + +# Flag to apply erratum 2344187 workaround during reset. This erratum applies +# to revisions r0p0, and r1p0. It is fixed in r1p1. +CPU_FLAG_LIST += ERRATA_A715_2344187 + +# Flag to apply erratum 2413290 workaround during reset. This erratum applies +# only to revision r1p0. It is fixed in r1p1. +CPU_FLAG_LIST += ERRATA_A715_2413290 + +# Flag to apply erratum 2420947 workaround during reset. This erratum applies +# only to revision r1p0. It is fixed in r1p1. +CPU_FLAG_LIST += ERRATA_A715_2420947 + +# Flag to apply erratum 2429384 workaround during reset. This erratum applies +# to revision r1p0. There is no workaround for r0p0. It is fixed in r1p1. +CPU_FLAG_LIST += ERRATA_A715_2429384 + +# Flag to apply erratum 2561034 workaround during reset. This erratum applies +# only to revision r1p0. It is fixed in r1p1. +CPU_FLAG_LIST += ERRATA_A715_2561034 + +# Flag to apply erratum 2728106 workaround during reset. This erratum applies +# only to revision r0p0, r1p0 and r1p1. It is fixed in r1p2. +CPU_FLAG_LIST += ERRATA_A715_2728106 + +# Flag to apply erratum 2792132 workaround during reset. This erratum applies +# to revisions r0p0 and r0p1. It is fixed in r0p2. +CPU_FLAG_LIST += ERRATA_A720_2792132 + +# Flag to apply erratum 2844092 workaround during reset. This erratum applies +# to revisions r0p0 and r0p1. It is fixed in r0p2. +CPU_FLAG_LIST += ERRATA_A720_2844092 + +# Flag to apply erratum 2926083 workaround during reset. This erratum applies +# to revisions r0p0 and r0p1. It is fixed in r0p2. +CPU_FLAG_LIST += ERRATA_A720_2926083 + +# Flag to apply erratum 2940794 workaround during reset. This erratum applies +# to revisions r0p0 and r0p1. It is fixed in r0p2. +CPU_FLAG_LIST += ERRATA_A720_2940794 # Flag to apply DSU erratum 798953. This erratum applies to DSUs revision r0p0. # Applying the workaround results in higher DSU power consumption on idle. diff --git a/lib/cpus/errata_common.c b/lib/cpus/errata_common.c new file mode 100644 index 00000000..a4515a9e --- /dev/null +++ b/lib/cpus/errata_common.c @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2024, Arm Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +/* Runtime C routines for errata workarounds and common routines */ + +#include <arch.h> +#include <arch_helpers.h> +#include <cortex_a520.h> +#include <cortex_x4.h> +#include <cortex_a75.h> +#include <lib/cpus/cpu_ops.h> +#include <lib/cpus/errata.h> + +#if ERRATA_A520_2938996 || ERRATA_X4_2726228 +unsigned int check_if_affected_core(void) +{ + uint32_t midr_val = read_midr(); + long rev_var = cpu_get_rev_var(); + + if (EXTRACT_PARTNUM(midr_val) == EXTRACT_PARTNUM(CORTEX_A520_MIDR)) { + return check_erratum_cortex_a520_2938996(rev_var); + } else if (EXTRACT_PARTNUM(midr_val) == EXTRACT_PARTNUM(CORTEX_X4_MIDR)) { + return check_erratum_cortex_x4_2726228(rev_var); + } + + return ERRATA_NOT_APPLIES; +} +#endif + +#if ERRATA_A75_764081 +bool errata_a75_764081_applies(void) +{ + long rev_var = cpu_get_rev_var(); + if (check_erratum_cortex_a75_764081(rev_var) == ERRATA_APPLIES) { + return true; + } + return false; +} +#endif /* ERRATA_A75_764081 */ diff --git a/lib/cpus/errata_report.c b/lib/cpus/errata_report.c index 4e9bdfc5..e0a9076e 100644 --- a/lib/cpus/errata_report.c +++ b/lib/cpus/errata_report.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2023, Arm Limited and Contributors. All rights reserved. + * Copyright (c) 2017-2024, Arm Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -51,9 +51,9 @@ static __unused void print_status(int status, char *cpu_str, uint16_t cve, uint3 } } else { if (cve) { - VERBOSE(CVE_FORMAT, BL_STRING, cpu_str, cve, id, "not applied"); + VERBOSE(CVE_FORMAT, BL_STRING, cpu_str, cve, id, "not applicable"); } else { - VERBOSE(ERRATUM_FORMAT, BL_STRING, cpu_str, id, "not applied"); + VERBOSE(ERRATUM_FORMAT, BL_STRING, cpu_str, id, "not applicable"); } } } @@ -67,7 +67,7 @@ void print_errata_status(void) {} * save space. This functionality is only useful on development and platform * bringup builds, when FEATURE_DETECTION should be used anyway */ -void __unused generic_errata_report(void) +void generic_errata_report(void) { struct cpu_ops *cpu_ops = get_cpu_ops_ptr(); struct erratum_entry *entry = cpu_ops->errata_list_start; @@ -159,70 +159,16 @@ static __unused int errata_needs_reporting(spinlock_t *lock, uint32_t *reported) */ void print_errata_status(void) { - struct cpu_ops *cpu_ops; #ifdef IMAGE_BL1 - /* - * BL1 doesn't have per-CPU data. So retrieve the CPU operations - * directly. - */ - cpu_ops = get_cpu_ops_ptr(); - - if (cpu_ops->errata_func != NULL) { - cpu_ops->errata_func(); - } + generic_errata_report(); #else /* IMAGE_BL1 */ - cpu_ops = (void *) get_cpu_data(cpu_ops_ptr); + struct cpu_ops *cpu_ops = (void *) get_cpu_data(cpu_ops_ptr); assert(cpu_ops != NULL); - if (cpu_ops->errata_func == NULL) { - return; - } - if (errata_needs_reporting(cpu_ops->errata_lock, cpu_ops->errata_reported)) { - cpu_ops->errata_func(); + generic_errata_report(); } #endif /* IMAGE_BL1 */ } - -/* - * Old errata status message printer - * TODO: remove once all cpus have been converted to the new printing method - */ -void __unused errata_print_msg(unsigned int status, const char *cpu, const char *id) -{ - /* Errata status strings */ - static const char *const errata_status_str[] = { - [ERRATA_NOT_APPLIES] = "not applied", - [ERRATA_APPLIES] = "applied", - [ERRATA_MISSING] = "missing!" - }; - static const char *const __unused bl_str = BL_STRING; - const char *msg __unused; - - - assert(status < ARRAY_SIZE(errata_status_str)); - assert(cpu != NULL); - assert(id != NULL); - - msg = errata_status_str[status]; - - switch (status) { - case ERRATA_NOT_APPLIES: - VERBOSE(ERRATA_FORMAT, bl_str, cpu, id, msg); - break; - - case ERRATA_APPLIES: - INFO(ERRATA_FORMAT, bl_str, cpu, id, msg); - break; - - case ERRATA_MISSING: - WARN(ERRATA_FORMAT, bl_str, cpu, id, msg); - break; - - default: - WARN(ERRATA_FORMAT, bl_str, cpu, id, "unknown"); - break; - } -} #endif /* !REPORT_ERRATA */ diff --git a/lib/el3_runtime/aarch32/context_mgmt.c b/lib/el3_runtime/aarch32/context_mgmt.c index b60b8e0f..132888cf 100644 --- a/lib/el3_runtime/aarch32/context_mgmt.c +++ b/lib/el3_runtime/aarch32/context_mgmt.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016-2023, Arm Limited and Contributors. All rights reserved. + * Copyright (c) 2016-2024, Arm Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -149,14 +149,13 @@ static void enable_extensions_nonsecure(bool el2_unused) trf_init_el3(); } - /* - * Also applies to PMU < v3. The PMU is only disabled for EL3 and Secure - * state execution. This does not affect lower NS ELs. - */ - pmuv3_init_el3(); + if (is_feat_pmuv3_present()) { + pmuv3_init_el3(); + } #endif /* IMAGE_BL32 */ } +#if !IMAGE_BL1 /******************************************************************************* * The following function initializes the cpu_context for a CPU specified by * its `cpu_idx` for first use, and sets the initial entrypoint state as @@ -169,6 +168,7 @@ void cm_init_context_by_index(unsigned int cpu_idx, ctx = cm_get_context_by_index(cpu_idx, GET_SECURITY_STATE(ep->h.attr)); cm_setup_context(ctx, ep); } +#endif /* !IMAGE_BL1 */ /******************************************************************************* * The following function initializes the cpu_context for the current CPU diff --git a/lib/el3_runtime/aarch64/context.S b/lib/el3_runtime/aarch64/context.S index 631094f7..a353a87d 100644 --- a/lib/el3_runtime/aarch64/context.S +++ b/lib/el3_runtime/aarch64/context.S @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013-2023, Arm Limited and Contributors. All rights reserved. + * Copyright (c) 2013-2024, Arm Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -9,216 +9,55 @@ #include <assert_macros.S> #include <context.h> #include <el3_common_macros.S> +#include <platform_def.h> - .global el1_sysregs_context_save - .global el1_sysregs_context_restore #if CTX_INCLUDE_FPREGS .global fpregs_context_save .global fpregs_context_restore #endif /* CTX_INCLUDE_FPREGS */ - .global prepare_el3_entry - .global restore_gp_pmcr_pauth_regs - .global save_and_update_ptw_el1_sys_regs - .global el3_exit +#if CTX_INCLUDE_SVE_REGS + .global sve_context_save + .global sve_context_restore +#endif /* CTX_INCLUDE_SVE_REGS */ -/* ------------------------------------------------------------------ - * The following function strictly follows the AArch64 PCS to use - * x9-x17 (temporary caller-saved registers) to save EL1 system - * register context. It assumes that 'x0' is pointing to a - * 'el1_sys_regs' structure where the register context will be saved. - * ------------------------------------------------------------------ - */ -func el1_sysregs_context_save +#if ERRATA_SPECULATIVE_AT + .global save_and_update_ptw_el1_sys_regs +#endif /* ERRATA_SPECULATIVE_AT */ - mrs x9, spsr_el1 - mrs x10, elr_el1 - stp x9, x10, [x0, #CTX_SPSR_EL1] + .global prepare_el3_entry + .global restore_gp_pmcr_pauth_regs + .global el3_exit -#if !ERRATA_SPECULATIVE_AT - mrs x15, sctlr_el1 - mrs x16, tcr_el1 - stp x15, x16, [x0, #CTX_SCTLR_EL1] -#endif /* ERRATA_SPECULATIVE_AT */ +/* Following macros will be used if any of CTX_INCLUDE_FPREGS or CTX_INCLUDE_SVE_REGS is enabled */ +#if CTX_INCLUDE_FPREGS || CTX_INCLUDE_SVE_REGS +.macro fpregs_state_save base:req hold:req + mrs \hold, fpsr + str \hold, [\base, #CTX_SIMD_FPSR] - mrs x17, cpacr_el1 - mrs x9, csselr_el1 - stp x17, x9, [x0, #CTX_CPACR_EL1] - - mrs x10, sp_el1 - mrs x11, esr_el1 - stp x10, x11, [x0, #CTX_SP_EL1] - - mrs x12, ttbr0_el1 - mrs x13, ttbr1_el1 - stp x12, x13, [x0, #CTX_TTBR0_EL1] - - mrs x14, mair_el1 - mrs x15, amair_el1 - stp x14, x15, [x0, #CTX_MAIR_EL1] - - mrs x16, actlr_el1 - mrs x17, tpidr_el1 - stp x16, x17, [x0, #CTX_ACTLR_EL1] - - mrs x9, tpidr_el0 - mrs x10, tpidrro_el0 - stp x9, x10, [x0, #CTX_TPIDR_EL0] - - mrs x13, par_el1 - mrs x14, far_el1 - stp x13, x14, [x0, #CTX_PAR_EL1] - - mrs x15, afsr0_el1 - mrs x16, afsr1_el1 - stp x15, x16, [x0, #CTX_AFSR0_EL1] - - mrs x17, contextidr_el1 - mrs x9, vbar_el1 - stp x17, x9, [x0, #CTX_CONTEXTIDR_EL1] - - /* Save AArch32 system registers if the build has instructed so */ -#if CTX_INCLUDE_AARCH32_REGS - mrs x11, spsr_abt - mrs x12, spsr_und - stp x11, x12, [x0, #CTX_SPSR_ABT] - - mrs x13, spsr_irq - mrs x14, spsr_fiq - stp x13, x14, [x0, #CTX_SPSR_IRQ] - - mrs x15, dacr32_el2 - mrs x16, ifsr32_el2 - stp x15, x16, [x0, #CTX_DACR32_EL2] -#endif /* CTX_INCLUDE_AARCH32_REGS */ - - /* Save NS timer registers if the build has instructed so */ -#if NS_TIMER_SWITCH - mrs x10, cntp_ctl_el0 - mrs x11, cntp_cval_el0 - stp x10, x11, [x0, #CTX_CNTP_CTL_EL0] - - mrs x12, cntv_ctl_el0 - mrs x13, cntv_cval_el0 - stp x12, x13, [x0, #CTX_CNTV_CTL_EL0] - - mrs x14, cntkctl_el1 - str x14, [x0, #CTX_CNTKCTL_EL1] -#endif /* NS_TIMER_SWITCH */ - - /* Save MTE system registers if the build has instructed so */ -#if CTX_INCLUDE_MTE_REGS - mrs x15, TFSRE0_EL1 - mrs x16, TFSR_EL1 - stp x15, x16, [x0, #CTX_TFSRE0_EL1] - - mrs x9, RGSR_EL1 - mrs x10, GCR_EL1 - stp x9, x10, [x0, #CTX_RGSR_EL1] -#endif /* CTX_INCLUDE_MTE_REGS */ + mrs \hold, fpcr + str \hold, [\base, #CTX_SIMD_FPCR] - ret -endfunc el1_sysregs_context_save +#if CTX_INCLUDE_AARCH32_REGS && CTX_INCLUDE_FPREGS + mrs \hold, fpexc32_el2 + str \hold, [\base, #CTX_SIMD_FPEXC32] +#endif +.endm -/* ------------------------------------------------------------------ - * The following function strictly follows the AArch64 PCS to use - * x9-x17 (temporary caller-saved registers) to restore EL1 system - * register context. It assumes that 'x0' is pointing to a - * 'el1_sys_regs' structure from where the register context will be - * restored - * ------------------------------------------------------------------ - */ -func el1_sysregs_context_restore +.macro fpregs_state_restore base:req hold:req + ldr \hold, [\base, #CTX_SIMD_FPSR] + msr fpsr, \hold - ldp x9, x10, [x0, #CTX_SPSR_EL1] - msr spsr_el1, x9 - msr elr_el1, x10 + ldr \hold, [\base, #CTX_SIMD_FPCR] + msr fpcr, \hold -#if !ERRATA_SPECULATIVE_AT - ldp x15, x16, [x0, #CTX_SCTLR_EL1] - msr sctlr_el1, x15 - msr tcr_el1, x16 -#endif /* ERRATA_SPECULATIVE_AT */ +#if CTX_INCLUDE_AARCH32_REGS && CTX_INCLUDE_FPREGS + ldr \hold, [\base, #CTX_SIMD_FPEXC32] + msr fpexc32_el2, \hold +#endif +.endm - ldp x17, x9, [x0, #CTX_CPACR_EL1] - msr cpacr_el1, x17 - msr csselr_el1, x9 - - ldp x10, x11, [x0, #CTX_SP_EL1] - msr sp_el1, x10 - msr esr_el1, x11 - - ldp x12, x13, [x0, #CTX_TTBR0_EL1] - msr ttbr0_el1, x12 - msr ttbr1_el1, x13 - - ldp x14, x15, [x0, #CTX_MAIR_EL1] - msr mair_el1, x14 - msr amair_el1, x15 - - ldp x16, x17, [x0, #CTX_ACTLR_EL1] - msr actlr_el1, x16 - msr tpidr_el1, x17 - - ldp x9, x10, [x0, #CTX_TPIDR_EL0] - msr tpidr_el0, x9 - msr tpidrro_el0, x10 - - ldp x13, x14, [x0, #CTX_PAR_EL1] - msr par_el1, x13 - msr far_el1, x14 - - ldp x15, x16, [x0, #CTX_AFSR0_EL1] - msr afsr0_el1, x15 - msr afsr1_el1, x16 - - ldp x17, x9, [x0, #CTX_CONTEXTIDR_EL1] - msr contextidr_el1, x17 - msr vbar_el1, x9 - - /* Restore AArch32 system registers if the build has instructed so */ -#if CTX_INCLUDE_AARCH32_REGS - ldp x11, x12, [x0, #CTX_SPSR_ABT] - msr spsr_abt, x11 - msr spsr_und, x12 - - ldp x13, x14, [x0, #CTX_SPSR_IRQ] - msr spsr_irq, x13 - msr spsr_fiq, x14 - - ldp x15, x16, [x0, #CTX_DACR32_EL2] - msr dacr32_el2, x15 - msr ifsr32_el2, x16 -#endif /* CTX_INCLUDE_AARCH32_REGS */ - - /* Restore NS timer registers if the build has instructed so */ -#if NS_TIMER_SWITCH - ldp x10, x11, [x0, #CTX_CNTP_CTL_EL0] - msr cntp_ctl_el0, x10 - msr cntp_cval_el0, x11 - - ldp x12, x13, [x0, #CTX_CNTV_CTL_EL0] - msr cntv_ctl_el0, x12 - msr cntv_cval_el0, x13 - - ldr x14, [x0, #CTX_CNTKCTL_EL1] - msr cntkctl_el1, x14 -#endif /* NS_TIMER_SWITCH */ - - /* Restore MTE system registers if the build has instructed so */ -#if CTX_INCLUDE_MTE_REGS - ldp x11, x12, [x0, #CTX_TFSRE0_EL1] - msr TFSRE0_EL1, x11 - msr TFSR_EL1, x12 - - ldp x13, x14, [x0, #CTX_RGSR_EL1] - msr RGSR_EL1, x13 - msr GCR_EL1, x14 -#endif /* CTX_INCLUDE_MTE_REGS */ - - /* No explict ISB required here as ERET covers it */ - ret -endfunc el1_sysregs_context_restore +#endif /* CTX_INCLUDE_FPREGS || CTX_INCLUDE_SVE_REGS */ /* ------------------------------------------------------------------ * The following function follows the aapcs_64 strictly to use @@ -236,33 +75,25 @@ endfunc el1_sysregs_context_restore */ #if CTX_INCLUDE_FPREGS func fpregs_context_save - stp q0, q1, [x0, #CTX_FP_Q0] - stp q2, q3, [x0, #CTX_FP_Q2] - stp q4, q5, [x0, #CTX_FP_Q4] - stp q6, q7, [x0, #CTX_FP_Q6] - stp q8, q9, [x0, #CTX_FP_Q8] - stp q10, q11, [x0, #CTX_FP_Q10] - stp q12, q13, [x0, #CTX_FP_Q12] - stp q14, q15, [x0, #CTX_FP_Q14] - stp q16, q17, [x0, #CTX_FP_Q16] - stp q18, q19, [x0, #CTX_FP_Q18] - stp q20, q21, [x0, #CTX_FP_Q20] - stp q22, q23, [x0, #CTX_FP_Q22] - stp q24, q25, [x0, #CTX_FP_Q24] - stp q26, q27, [x0, #CTX_FP_Q26] - stp q28, q29, [x0, #CTX_FP_Q28] - stp q30, q31, [x0, #CTX_FP_Q30] - - mrs x9, fpsr - str x9, [x0, #CTX_FP_FPSR] - - mrs x10, fpcr - str x10, [x0, #CTX_FP_FPCR] - -#if CTX_INCLUDE_AARCH32_REGS - mrs x11, fpexc32_el2 - str x11, [x0, #CTX_FP_FPEXC32_EL2] -#endif /* CTX_INCLUDE_AARCH32_REGS */ + stp q0, q1, [x0], #32 + stp q2, q3, [x0], #32 + stp q4, q5, [x0], #32 + stp q6, q7, [x0], #32 + stp q8, q9, [x0], #32 + stp q10, q11, [x0], #32 + stp q12, q13, [x0], #32 + stp q14, q15, [x0], #32 + stp q16, q17, [x0], #32 + stp q18, q19, [x0], #32 + stp q20, q21, [x0], #32 + stp q22, q23, [x0], #32 + stp q24, q25, [x0], #32 + stp q26, q27, [x0], #32 + stp q28, q29, [x0], #32 + stp q30, q31, [x0], #32 + + fpregs_state_save x0, x9 + ret endfunc fpregs_context_save @@ -281,51 +112,196 @@ endfunc fpregs_context_save * ------------------------------------------------------------------ */ func fpregs_context_restore - ldp q0, q1, [x0, #CTX_FP_Q0] - ldp q2, q3, [x0, #CTX_FP_Q2] - ldp q4, q5, [x0, #CTX_FP_Q4] - ldp q6, q7, [x0, #CTX_FP_Q6] - ldp q8, q9, [x0, #CTX_FP_Q8] - ldp q10, q11, [x0, #CTX_FP_Q10] - ldp q12, q13, [x0, #CTX_FP_Q12] - ldp q14, q15, [x0, #CTX_FP_Q14] - ldp q16, q17, [x0, #CTX_FP_Q16] - ldp q18, q19, [x0, #CTX_FP_Q18] - ldp q20, q21, [x0, #CTX_FP_Q20] - ldp q22, q23, [x0, #CTX_FP_Q22] - ldp q24, q25, [x0, #CTX_FP_Q24] - ldp q26, q27, [x0, #CTX_FP_Q26] - ldp q28, q29, [x0, #CTX_FP_Q28] - ldp q30, q31, [x0, #CTX_FP_Q30] - - ldr x9, [x0, #CTX_FP_FPSR] - msr fpsr, x9 - - ldr x10, [x0, #CTX_FP_FPCR] - msr fpcr, x10 - -#if CTX_INCLUDE_AARCH32_REGS - ldr x11, [x0, #CTX_FP_FPEXC32_EL2] - msr fpexc32_el2, x11 -#endif /* CTX_INCLUDE_AARCH32_REGS */ - - /* - * No explict ISB required here as ERET to - * switch to secure EL1 or non-secure world - * covers it - */ + ldp q0, q1, [x0], #32 + ldp q2, q3, [x0], #32 + ldp q4, q5, [x0], #32 + ldp q6, q7, [x0], #32 + ldp q8, q9, [x0], #32 + ldp q10, q11, [x0], #32 + ldp q12, q13, [x0], #32 + ldp q14, q15, [x0], #32 + ldp q16, q17, [x0], #32 + ldp q18, q19, [x0], #32 + ldp q20, q21, [x0], #32 + ldp q22, q23, [x0], #32 + ldp q24, q25, [x0], #32 + ldp q26, q27, [x0], #32 + ldp q28, q29, [x0], #32 + ldp q30, q31, [x0], #32 + + fpregs_state_restore x0, x9 ret endfunc fpregs_context_restore #endif /* CTX_INCLUDE_FPREGS */ +#if CTX_INCLUDE_SVE_REGS +/* + * Helper macros for SVE predicates save/restore operations. + */ +.macro sve_predicate_op op:req reg:req + \op p0, [\reg, #0, MUL VL] + \op p1, [\reg, #1, MUL VL] + \op p2, [\reg, #2, MUL VL] + \op p3, [\reg, #3, MUL VL] + \op p4, [\reg, #4, MUL VL] + \op p5, [\reg, #5, MUL VL] + \op p6, [\reg, #6, MUL VL] + \op p7, [\reg, #7, MUL VL] + \op p8, [\reg, #8, MUL VL] + \op p9, [\reg, #9, MUL VL] + \op p10, [\reg, #10, MUL VL] + \op p11, [\reg, #11, MUL VL] + \op p12, [\reg, #12, MUL VL] + \op p13, [\reg, #13, MUL VL] + \op p14, [\reg, #14, MUL VL] + \op p15, [\reg, #15, MUL VL] +.endm + +.macro sve_vectors_op op:req reg:req + \op z0, [\reg, #0, MUL VL] + \op z1, [\reg, #1, MUL VL] + \op z2, [\reg, #2, MUL VL] + \op z3, [\reg, #3, MUL VL] + \op z4, [\reg, #4, MUL VL] + \op z5, [\reg, #5, MUL VL] + \op z6, [\reg, #6, MUL VL] + \op z7, [\reg, #7, MUL VL] + \op z8, [\reg, #8, MUL VL] + \op z9, [\reg, #9, MUL VL] + \op z10, [\reg, #10, MUL VL] + \op z11, [\reg, #11, MUL VL] + \op z12, [\reg, #12, MUL VL] + \op z13, [\reg, #13, MUL VL] + \op z14, [\reg, #14, MUL VL] + \op z15, [\reg, #15, MUL VL] + \op z16, [\reg, #16, MUL VL] + \op z17, [\reg, #17, MUL VL] + \op z18, [\reg, #18, MUL VL] + \op z19, [\reg, #19, MUL VL] + \op z20, [\reg, #20, MUL VL] + \op z21, [\reg, #21, MUL VL] + \op z22, [\reg, #22, MUL VL] + \op z23, [\reg, #23, MUL VL] + \op z24, [\reg, #24, MUL VL] + \op z25, [\reg, #25, MUL VL] + \op z26, [\reg, #26, MUL VL] + \op z27, [\reg, #27, MUL VL] + \op z28, [\reg, #28, MUL VL] + \op z29, [\reg, #29, MUL VL] + \op z30, [\reg, #30, MUL VL] + \op z31, [\reg, #31, MUL VL] +.endm + +/* ------------------------------------------------------------------ + * The following function follows the aapcs_64 strictly to use x9-x17 + * (temporary caller-saved registers according to AArch64 PCS) to + * restore SVE register context. It assumes that 'x0' is + * pointing to a 'sve_regs_t' structure to which the register context + * will be saved. + * ------------------------------------------------------------------ + */ +func sve_context_save +.arch_extension sve + /* Temporarily enable SVE */ + mrs x10, cptr_el3 + orr x11, x10, #CPTR_EZ_BIT + bic x11, x11, #TFP_BIT + msr cptr_el3, x11 + isb + + /* zcr_el3 */ + mrs x12, S3_6_C1_C2_0 + mov x13, #((SVE_VECTOR_LEN >> 7) - 1) + msr S3_6_C1_C2_0, x13 + isb + + /* Predicate registers */ + mov x13, #CTX_SIMD_PREDICATES + add x9, x0, x13 + sve_predicate_op str, x9 + + /* Save FFR after predicates */ + mov x13, #CTX_SIMD_FFR + add x9, x0, x13 + rdffr p0.b + str p0, [x9] + + /* Save vector registers */ + mov x13, #CTX_SIMD_VECTORS + add x9, x0, x13 + sve_vectors_op str, x9 + + /* Restore SVE enablement */ + msr S3_6_C1_C2_0, x12 /* zcr_el3 */ + msr cptr_el3, x10 + isb +.arch_extension nosve + + /* Save FPSR, FPCR and FPEXC32 */ + fpregs_state_save x0, x9 + + ret +endfunc sve_context_save + +/* ------------------------------------------------------------------ + * The following function follows the aapcs_64 strictly to use x9-x17 + * (temporary caller-saved registers according to AArch64 PCS) to + * restore SVE register context. It assumes that 'x0' is pointing to + * a 'sve_regs_t' structure from where the register context will be + * restored. + * ------------------------------------------------------------------ + */ +func sve_context_restore +.arch_extension sve + /* Temporarily enable SVE for EL3 */ + mrs x10, cptr_el3 + orr x11, x10, #CPTR_EZ_BIT + bic x11, x11, #TFP_BIT + msr cptr_el3, x11 + isb + + /* zcr_el3 */ + mrs x12, S3_6_C1_C2_0 + mov x13, #((SVE_VECTOR_LEN >> 7) - 1) + msr S3_6_C1_C2_0, x13 + isb + + /* Restore FFR register before predicates */ + mov x13, #CTX_SIMD_FFR + add x9, x0, x13 + ldr p0, [x9] + wrffr p0.b + + /* Restore predicate registers */ + mov x13, #CTX_SIMD_PREDICATES + add x9, x0, x13 + sve_predicate_op ldr, x9 + + /* Restore vector registers */ + mov x13, #CTX_SIMD_VECTORS + add x9, x0, x13 + sve_vectors_op ldr, x9 + + /* Restore SVE enablement */ + msr S3_6_C1_C2_0, x12 /* zcr_el3 */ + msr cptr_el3, x10 + isb +.arch_extension nosve + + /* Restore FPSR, FPCR and FPEXC32 */ + fpregs_state_restore x0, x9 + ret +endfunc sve_context_restore +#endif /* CTX_INCLUDE_SVE_REGS */ + /* * Set SCR_EL3.EA bit to enable SErrors at EL3 */ .macro enable_serror_at_el3 - mrs x8, scr_el3 - orr x8, x8, #SCR_EA_BIT - msr scr_el3, x8 + mrs x8, scr_el3 + orr x8, x8, #SCR_EA_BIT + msr scr_el3, x8 .endm /* @@ -339,13 +315,13 @@ endfunc fpregs_context_restore * always enable DIT in EL3 */ #if ENABLE_FEAT_DIT -#if ENABLE_FEAT_DIT == 2 +#if ENABLE_FEAT_DIT >= 2 mrs x8, id_aa64pfr0_el1 and x8, x8, #(ID_AA64PFR0_DIT_MASK << ID_AA64PFR0_DIT_SHIFT) cbz x8, 1f #endif - mov x8, #DIT_BIT - msr DIT, x8 + mov x8, #DIT_BIT + msr DIT, x8 1: #endif /* ENABLE_FEAT_DIT */ .endm /* set_unset_pstate_bits */ @@ -363,8 +339,7 @@ endfunc fpregs_context_restore .macro restore_mpam3_el3 #if ENABLE_FEAT_MPAM -#if ENABLE_FEAT_MPAM == 2 - +#if ENABLE_FEAT_MPAM >= 2 mrs x8, id_aa64pfr0_el1 lsr x8, x8, #(ID_AA64PFR0_MPAM_SHIFT) and x8, x8, #(ID_AA64PFR0_MPAM_MASK) @@ -378,9 +353,11 @@ endfunc fpregs_context_restore * Restore MPAM3_EL3 register as per context state * Currently we only enable MPAM for NS world and trap to EL3 * for MPAM access in lower ELs of Secure and Realm world + * x9 holds address of the per_world context * ----------------------------------------------------------- */ - ldr x17, [sp, #CTX_EL3STATE_OFFSET + CTX_MPAM3_EL3] + + ldr x17, [x9, #CTX_MPAM3_EL3] msr S3_6_C10_C5_0, x17 /* mpam3_el3 */ no_mpam: @@ -423,9 +400,6 @@ no_mpam: /* PMUv3 is presumed to be always present */ mrs x9, pmcr_el0 str x9, [sp, #CTX_EL3STATE_OFFSET + CTX_PMCR_EL0] - /* Disable cycle counter when event counting is prohibited */ - orr x9, x9, #PMCR_EL0_DP_BIT - msr pmcr_el0, x9 isb #if CTX_INCLUDE_PAUTH_REGS /* ---------------------------------------------------------- @@ -467,12 +441,7 @@ no_mpam: */ func prepare_el3_entry save_gp_pmcr_pauth_regs - enable_serror_at_el3 - /* - * Set the PSTATE bits not described in the Aarch64.TakeException - * pseudocode to their default values. - */ - set_unset_pstate_bits + setup_el3_execution_context ret endfunc prepare_el3_entry @@ -528,10 +497,12 @@ func restore_gp_pmcr_pauth_regs ret endfunc restore_gp_pmcr_pauth_regs -/* +#if ERRATA_SPECULATIVE_AT +/* -------------------------------------------------------------------- * In case of ERRATA_SPECULATIVE_AT, save SCTLR_EL1 and TCR_EL1 * registers and update EL1 registers to disable stage1 and stage2 - * page table walk + * page table walk. + * -------------------------------------------------------------------- */ func save_and_update_ptw_el1_sys_regs /* ---------------------------------------------------------- @@ -539,9 +510,9 @@ func save_and_update_ptw_el1_sys_regs * ---------------------------------------------------------- */ mrs x29, sctlr_el1 - str x29, [sp, #(CTX_EL1_SYSREGS_OFFSET + CTX_SCTLR_EL1)] + str x29, [sp, #(CTX_ERRATA_SPEC_AT_OFFSET + CTX_ERRATA_SPEC_AT_SCTLR_EL1)] mrs x29, tcr_el1 - str x29, [sp, #(CTX_EL1_SYSREGS_OFFSET + CTX_TCR_EL1)] + str x29, [sp, #(CTX_ERRATA_SPEC_AT_OFFSET + CTX_ERRATA_SPEC_AT_TCR_EL1)] /* ------------------------------------------------------------ * Must follow below order in order to disable page table @@ -566,10 +537,11 @@ func save_and_update_ptw_el1_sys_regs orr x29, x29, #SCTLR_M_BIT msr sctlr_el1, x29 isb - ret endfunc save_and_update_ptw_el1_sys_regs +#endif /* ERRATA_SPECULATIVE_AT */ + /* ----------------------------------------------------------------- * The below macro returns the address of the per_world context for * the security state, retrieved through "get_security_state" macro. @@ -581,7 +553,7 @@ endfunc save_and_update_ptw_el1_sys_regs .macro get_per_world_context _reg:req ldr x10, [sp, #CTX_EL3STATE_OFFSET + CTX_SCR_EL3] get_security_state x9, x10 - mov_imm x10, (CTX_GLOBAL_EL3STATE_END - CTX_CPTR_EL3) + mov_imm x10, (CTX_PERWORLD_EL3STATE_END - CTX_CPTR_EL3) mul x9, x9, x10 adrp x10, per_world_context add x10, x10, :lo12:per_world_context @@ -653,15 +625,17 @@ sve_not_enabled: synchronize_errors #endif /* IMAGE_BL31 */ - /* ---------------------------------------------------------- - * Restore SPSR_EL3, ELR_EL3 and SCR_EL3 prior to ERET - * ---------------------------------------------------------- + /* -------------------------------------------------------------- + * Restore MDCR_EL3, SPSR_EL3, ELR_EL3 and SCR_EL3 prior to ERET + * -------------------------------------------------------------- */ - ldr x18, [sp, #CTX_EL3STATE_OFFSET + CTX_SCR_EL3] ldp x16, x17, [sp, #CTX_EL3STATE_OFFSET + CTX_SPSR_EL3] - msr scr_el3, x18 + ldr x18, [sp, #CTX_EL3STATE_OFFSET + CTX_SCR_EL3] + ldr x19, [sp, #CTX_EL3STATE_OFFSET + CTX_MDCR_EL3] msr spsr_el3, x16 msr elr_el3, x17 + msr scr_el3, x18 + msr mdcr_el3, x19 restore_ptw_el1_sys_regs diff --git a/lib/el3_runtime/aarch64/context_debug.c b/lib/el3_runtime/aarch64/context_debug.c new file mode 100644 index 00000000..b37bcb75 --- /dev/null +++ b/lib/el3_runtime/aarch64/context_debug.c @@ -0,0 +1,208 @@ +/* + * Copyright (c) 2023-2024, Arm Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <string.h> + +#include <common/debug.h> +#include <context.h> +#include <lib/el3_runtime/context_mgmt.h> +#include <lib/el3_runtime/cpu_data.h> + +/******************************************************************************** + * Function that returns the corresponding string constant for a security state + * index. + *******************************************************************************/ +static const char *get_context_name_by_idx(unsigned int security_state_idx) +{ + assert(security_state_idx < CPU_CONTEXT_NUM); + static const char * const state_names[] = { + "Secure", + "Non Secure" +#if ENABLE_RME + , "Realm" +#endif /* ENABLE_RME */ + }; + return state_names[security_state_idx]; +} + +#define PRINT_MEM_USAGE_SEPARATOR() \ + do { \ + printf("+-----------+-----------+-----------" \ + "+-----------+-----------+-----------+\n"); \ + } while (false) + +#define NAME_PLACEHOLDER_LEN 14 + +#define PRINT_DASH(n) \ + for (; n > 0; n--) { \ + putchar('-'); \ + } + +#define PRINT_SINGLE_MEM_USAGE_SEP_BLOCK() \ + do { \ + printf("+-----------"); \ + } while (false) + +/******************************************************************************** + * This function prints the allocated memory for a specific security state. + * Values are grouped by exception level and core. The memory usage for the + * global context and the total memory for the security state are also computed. + *******************************************************************************/ +static size_t report_allocated_memory(unsigned int security_state_idx) +{ + size_t core_total = 0U; + size_t gp_total = 0U; + size_t el3_total = 0U; + size_t other_total = 0U; + size_t total = 0U; + size_t per_world_ctx_size = 0U; + +#if CTX_INCLUDE_EL2_REGS + size_t el2_total = 0U; +#else + size_t el1_total = 0U; +#endif /* CTX_INCLUDE_EL2_REGS */ + +#if CTX_INCLUDE_PAUTH_REGS + size_t pauth_total = 0U; + PRINT_SINGLE_MEM_USAGE_SEP_BLOCK(); +#endif + + PRINT_MEM_USAGE_SEPARATOR(); + + printf("| Core | GP | EL3 "); +#if CTX_INCLUDE_EL2_REGS + printf("| EL2 "); +#else + printf("| EL1 "); +#endif /* CTX_INCLUDE_EL2_REGS */ + +#if CTX_INCLUDE_PAUTH_REGS + printf("| PAUTH "); +#endif + + printf("| Other | Total |\n"); + + /* Compute memory usage for each core's context */ + for (unsigned int i = 0U; i < PLATFORM_CORE_COUNT; i++) { + size_t size_other = 0U; + size_t el3_size = 0U; + size_t gp_size = 0U; +#if CTX_INCLUDE_EL2_REGS + size_t el2_size = 0U; +#else + size_t el1_size = 0U; +#endif /* CTX_INCLUDE_EL2_REGS */ + +#if CTX_INCLUDE_PAUTH_REGS + size_t pauth_size = 0U; + PRINT_SINGLE_MEM_USAGE_SEP_BLOCK(); +#endif + + PRINT_MEM_USAGE_SEPARATOR(); + + cpu_context_t *ctx = (cpu_context_t *)cm_get_context_by_index(i, + security_state_idx); + core_total = sizeof(*ctx); + el3_size = sizeof(ctx->el3state_ctx); + gp_size = sizeof(ctx->gpregs_ctx); + size_other = core_total - (el3_size + gp_size); + printf("| %9u | %8luB | %8luB ", i, gp_size, el3_size); + +#if CTX_INCLUDE_EL2_REGS + el2_size = sizeof(ctx->el2_sysregs_ctx); + size_other -= el2_size; + el2_total += el2_size; + printf("| %8luB ", el2_size); +#else + el1_size = sizeof(ctx->el1_sysregs_ctx); + size_other -= el1_size; + el1_total += el1_size; + printf("| %8luB ", el1_size); +#endif /* CTX_INCLUDE_EL2_REGS */ + +#if CTX_INCLUDE_PAUTH_REGS + pauth_size = sizeof(ctx->pauth_ctx); + size_other -= pauth_size; + pauth_total += pauth_size; + printf("| %8luB ", pauth_size); +#endif + printf("| %8luB | %8luB |\n", size_other, core_total); + + gp_total += gp_size; + el3_total += el3_size; + other_total += size_other; + total += core_total; + } + +#if CTX_INCLUDE_PAUTH_REGS + PRINT_SINGLE_MEM_USAGE_SEP_BLOCK(); +#endif + + PRINT_MEM_USAGE_SEPARATOR(); + +#if CTX_INCLUDE_PAUTH_REGS + PRINT_SINGLE_MEM_USAGE_SEP_BLOCK(); +#endif + + PRINT_MEM_USAGE_SEPARATOR(); + + printf("| All | %8luB | %8luB ", gp_total, el3_total); + +#if CTX_INCLUDE_EL2_REGS + printf("| %8luB ", el2_total); +#else + printf("| %8luB ", el1_total); +#endif /* CTX_INCLUDE_EL2_REGS */ + +#if CTX_INCLUDE_PAUTH_REGS + printf("| %8luB ", pauth_total); +#endif + + printf("| %8luB | %8luB |\n", other_total, total); + +#if CTX_INCLUDE_PAUTH_REGS + PRINT_SINGLE_MEM_USAGE_SEP_BLOCK(); +#endif + PRINT_MEM_USAGE_SEPARATOR(); + printf("\n"); + + /* Compute memory usage for the global context */ + per_world_ctx_size = sizeof(per_world_context[security_state_idx]); + + total += per_world_ctx_size; + + printf("Per-world context: %luB\n\n", per_world_ctx_size); + + printf("TOTAL: %luB\n", total); + + return total; +} + +/******************************************************************************** + * Reports the allocated memory for every security state and then reports the + * total system-wide allocated memory. + *******************************************************************************/ +void report_ctx_memory_usage(void) +{ + INFO("Context memory allocation:\n"); + + size_t total = 0U; + + for (unsigned int i = 0U; i < CPU_CONTEXT_NUM; i++) { + const char *context_name = get_context_name_by_idx(i); + size_t len = 0U; + + printf("Memory usage for %s:\n", context_name); + total += report_allocated_memory(i); + printf("------------------------"); + len = NAME_PLACEHOLDER_LEN - printf("End %s", context_name); + PRINT_DASH(len); + printf("-----------------------\n\n"); + } + + printf("Total context memory allocated: %luB\n\n", total); +} diff --git a/lib/el3_runtime/aarch64/context_mgmt.c b/lib/el3_runtime/aarch64/context_mgmt.c index fdd1388c..4ae13061 100644 --- a/lib/el3_runtime/aarch64/context_mgmt.c +++ b/lib/el3_runtime/aarch64/context_mgmt.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013-2023, Arm Limited and Contributors. All rights reserved. + * Copyright (c) 2013-2024, Arm Limited and Contributors. All rights reserved. * Copyright (c) 2022, NVIDIA Corporation. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause @@ -19,17 +19,23 @@ #include <common/debug.h> #include <context.h> #include <drivers/arm/gicv3.h> +#include <lib/cpus/cpu_ops.h> +#include <lib/cpus/errata.h> #include <lib/el3_runtime/context_mgmt.h> #include <lib/el3_runtime/cpu_data.h> #include <lib/el3_runtime/pubsub_events.h> #include <lib/extensions/amu.h> #include <lib/extensions/brbe.h> +#include <lib/extensions/debug_v8p9.h> +#include <lib/extensions/fgt2.h> #include <lib/extensions/mpam.h> #include <lib/extensions/pmuv3.h> #include <lib/extensions/sme.h> #include <lib/extensions/spe.h> #include <lib/extensions/sve.h> +#include <lib/extensions/sysreg128.h> #include <lib/extensions/sys_reg_trace.h> +#include <lib/extensions/tcr2.h> #include <lib/extensions/trbe.h> #include <lib/extensions/trf.h> #include <lib/utils.h> @@ -42,10 +48,12 @@ CASSERT(((TWED_DELAY & ~SCR_TWEDEL_MASK) == 0U), assert_twed_delay_value_check); per_world_context_t per_world_context[CPU_DATA_CONTEXT_NUM]; static bool has_secure_perworld_init; +static void manage_extensions_common(cpu_context_t *ctx); static void manage_extensions_nonsecure(cpu_context_t *ctx); static void manage_extensions_secure(cpu_context_t *ctx); static void manage_extensions_secure_per_world(void); +#if ((IMAGE_BL1) || (IMAGE_BL31 && (!CTX_INCLUDE_EL2_REGS))) static void setup_el1_context(cpu_context_t *ctx, const struct entry_point_info *ep) { u_register_t sctlr_elx, actlr_elx; @@ -82,15 +90,16 @@ static void setup_el1_context(cpu_context_t *ctx, const struct entry_point_info | SCTLR_NTWI_BIT | SCTLR_NTWE_BIT; } -#if ERRATA_A75_764081 /* * If workaround of errata 764081 for Cortex-A75 is used then set * SCTLR_EL1.IESB to enable Implicit Error Synchronization Barrier. */ - sctlr_elx |= SCTLR_IESB_BIT; -#endif + if (errata_a75_764081_applies()) { + sctlr_elx |= SCTLR_IESB_BIT; + } + /* Store the initialised SCTLR_EL1 value in the cpu_context */ - write_ctx_reg(get_el1_sysregs_ctx(ctx), CTX_SCTLR_EL1, sctlr_elx); + write_ctx_sctlr_el1_reg_errata(ctx, sctlr_elx); /* * Base the context ACTLR_EL1 on the current value, as it is @@ -100,8 +109,9 @@ static void setup_el1_context(cpu_context_t *ctx, const struct entry_point_info * be zero. */ actlr_elx = read_actlr_el1(); - write_ctx_reg((get_el1_sysregs_ctx(ctx)), (CTX_ACTLR_EL1), (actlr_elx)); + write_el1_ctx_common(get_el1_sysregs_ctx(ctx), actlr_el1, actlr_elx); } +#endif /* (IMAGE_BL1) || (IMAGE_BL31 && (!CTX_INCLUDE_EL2_REGS)) */ /****************************************************************************** * This function performs initializations that are specific to SECURE state @@ -123,22 +133,10 @@ static void setup_secure_context(cpu_context_t *ctx, const struct entry_point_in scr_el3 |= get_scr_el3_from_routing_model(SECURE); #endif -#if !CTX_INCLUDE_MTE_REGS || ENABLE_ASSERTIONS - /* Get Memory Tagging Extension support level */ - unsigned int mte = get_armv8_5_mte_support(); -#endif - /* - * Allow access to Allocation Tags when CTX_INCLUDE_MTE_REGS - * is set, or when MTE is only implemented at EL0. - */ -#if CTX_INCLUDE_MTE_REGS - assert((mte == MTE_IMPLEMENTED_ELX) || (mte == MTE_IMPLEMENTED_ASY)); - scr_el3 |= SCR_ATA_BIT; -#else - if (mte == MTE_IMPLEMENTED_EL0) { + /* Allow access to Allocation Tags when FEAT_MTE2 is implemented and enabled. */ + if (is_feat_mte2_supported()) { scr_el3 |= SCR_ATA_BIT; } -#endif /* CTX_INCLUDE_MTE_REGS */ write_ctx_reg(state, CTX_SCR_EL3, scr_el3); @@ -146,7 +144,7 @@ static void setup_secure_context(cpu_context_t *ctx, const struct entry_point_in * Initialize EL1 context registers unless SPMC is running * at S-EL2. */ -#if !SPMD_SPM_AT_SEL2 +#if (!SPMD_SPM_AT_SEL2) setup_el1_context(ctx, ep); #endif @@ -162,7 +160,6 @@ static void setup_secure_context(cpu_context_t *ctx, const struct entry_point_in if (!has_secure_perworld_init) { manage_extensions_secure_per_world(); } - } #if ENABLE_RME @@ -180,11 +177,19 @@ static void setup_realm_context(cpu_context_t *ctx, const struct entry_point_inf scr_el3 |= SCR_NS_BIT | SCR_NSE_BIT; + /* CSV2 version 2 and above */ if (is_feat_csv2_2_supported()) { /* Enable access to the SCXTNUM_ELx registers. */ scr_el3 |= SCR_EnSCXT_BIT; } + if (is_feat_sctlr2_supported()) { + /* Set the SCTLR2En bit in SCR_EL3 to enable access to + * SCTLR2_ELx registers. + */ + scr_el3 |= SCR_SCTLR2En_BIT; + } + write_ctx_reg(state, CTX_SCR_EL3, scr_el3); } #endif /* ENABLE_RME */ @@ -204,8 +209,10 @@ static void setup_ns_context(cpu_context_t *ctx, const struct entry_point_info * /* SCR_NS: Set the NS bit */ scr_el3 |= SCR_NS_BIT; - /* Allow access to Allocation Tags when MTE is implemented. */ - scr_el3 |= SCR_ATA_BIT; + /* Allow access to Allocation Tags when FEAT_MTE2 is implemented and enabled. */ + if (is_feat_mte2_supported()) { + scr_el3 |= SCR_ATA_BIT; + } #if !CTX_INCLUDE_PAUTH_REGS /* @@ -248,6 +255,7 @@ static void setup_ns_context(cpu_context_t *ctx, const struct entry_point_info * scr_el3 |= SCR_TERR_BIT; #endif + /* CSV2 version 2 and above */ if (is_feat_csv2_2_supported()) { /* Enable access to the SCXTNUM_ELx registers. */ scr_el3 |= SCR_EnSCXT_BIT; @@ -260,22 +268,38 @@ static void setup_ns_context(cpu_context_t *ctx, const struct entry_point_info * */ scr_el3 |= get_scr_el3_from_routing_model(NON_SECURE); #endif - write_ctx_reg(state, CTX_SCR_EL3, scr_el3); - /* Initialize EL1 context registers */ - setup_el1_context(ctx, ep); + if (is_feat_the_supported()) { + /* Set the RCWMASKEn bit in SCR_EL3 to enable access to + * RCWMASK_EL1 and RCWSMASK_EL1 registers. + */ + scr_el3 |= SCR_RCWMASKEn_BIT; + } + + if (is_feat_sctlr2_supported()) { + /* Set the SCTLR2En bit in SCR_EL3 to enable access to + * SCTLR2_ELx registers. + */ + scr_el3 |= SCR_SCTLR2En_BIT; + } + + if (is_feat_d128_supported()) { + /* Set the D128En bit in SCR_EL3 to enable access to 128-bit + * versions of TTBR0_EL1, TTBR1_EL1, RCWMASK_EL1, RCWSMASK_EL1, + * PAR_EL1 and TTBR1_EL2, TTBR0_EL2 and VTTBR_EL2 registers. + */ + scr_el3 |= SCR_D128En_BIT; + } + + write_ctx_reg(state, CTX_SCR_EL3, scr_el3); /* Initialize EL2 context registers */ -#if CTX_INCLUDE_EL2_REGS +#if (CTX_INCLUDE_EL2_REGS && IMAGE_BL31) /* - * Initialize SCTLR_EL2 context register using Endianness value - * taken from the entrypoint attribute. + * Initialize SCTLR_EL2 context register with reset value. */ - u_register_t sctlr_el2 = (EP_GET_EE(ep->h.attr) != 0U) ? SCTLR_EE_BIT : 0UL; - sctlr_el2 |= SCTLR_EL2_RES1; - write_ctx_reg(get_el2_sysregs_ctx(ctx), CTX_SCTLR_EL2, - sctlr_el2); + write_el2_ctx_common(get_el2_sysregs_ctx(ctx), sctlr_el2, SCTLR_EL2_RES1); if (is_feat_hcx_supported()) { /* @@ -286,7 +310,7 @@ static void setup_ns_context(cpu_context_t *ctx, const struct entry_point_info * * this feature if not properly initialized, especially when * it comes to those bits that enable/disable traps. */ - write_ctx_reg(get_el2_sysregs_ctx(ctx), CTX_HCRX_EL2, + write_el2_ctx_hcx(get_el2_sysregs_ctx(ctx), hcrx_el2, HCRX_EL2_INIT_VAL); } @@ -296,14 +320,17 @@ static void setup_ns_context(cpu_context_t *ctx, const struct entry_point_info * * systems unaware of FEAT_FGT do not get trapped due to their lack * of initialization for this feature. */ - write_ctx_reg(get_el2_sysregs_ctx(ctx), CTX_HFGITR_EL2, + write_el2_ctx_fgt(get_el2_sysregs_ctx(ctx), hfgitr_el2, HFGITR_EL2_INIT_VAL); - write_ctx_reg(get_el2_sysregs_ctx(ctx), CTX_HFGRTR_EL2, + write_el2_ctx_fgt(get_el2_sysregs_ctx(ctx), hfgrtr_el2, HFGRTR_EL2_INIT_VAL); - write_ctx_reg(get_el2_sysregs_ctx(ctx), CTX_HFGWTR_EL2, + write_el2_ctx_fgt(get_el2_sysregs_ctx(ctx), hfgwtr_el2, HFGWTR_EL2_INIT_VAL); } -#endif /* CTX_INCLUDE_EL2_REGS */ +#else + /* Initialize EL1 context registers */ + setup_el1_context(ctx, ep); +#endif /* (CTX_INCLUDE_EL2_REGS && IMAGE_BL31) */ manage_extensions_nonsecure(ctx); } @@ -319,6 +346,7 @@ static void setup_ns_context(cpu_context_t *ctx, const struct entry_point_info * static void setup_context_common(cpu_context_t *ctx, const entry_point_info_t *ep) { u_register_t scr_el3; + u_register_t mdcr_el3; el3_state_t *state; gp_regs_t *gp_regs; @@ -333,17 +361,23 @@ static void setup_context_common(cpu_context_t *ctx, const entry_point_info_t *e * to boot correctly. However, there are very few registers where this * is not true and some values need to be recreated. */ -#if CTX_INCLUDE_EL2_REGS +#if (CTX_INCLUDE_EL2_REGS && IMAGE_BL31) el2_sysregs_t *el2_ctx = get_el2_sysregs_ctx(ctx); /* * These bits are set in the gicv3 driver. Losing them (especially the * SRE bit) is problematic for all worlds. Henceforth recreate them. */ - u_register_t icc_sre_el2 = ICC_SRE_DIB_BIT | ICC_SRE_DFB_BIT | + u_register_t icc_sre_el2_val = ICC_SRE_DIB_BIT | ICC_SRE_DFB_BIT | ICC_SRE_EN_BIT | ICC_SRE_SRE_BIT; - write_ctx_reg(el2_ctx, CTX_ICC_SRE_EL2, icc_sre_el2); -#endif /* CTX_INCLUDE_EL2_REGS */ + write_el2_ctx_common(el2_ctx, icc_sre_el2, icc_sre_el2_val); + + /* + * The actlr_el2 register can be initialized in platform's reset handler + * and it may contain access control bits (e.g. CLUSTERPMUEN bit). + */ + write_el2_ctx_common(el2_ctx, actlr_el2, read_actlr_el2()); +#endif /* (CTX_INCLUDE_EL2_REGS && IMAGE_BL31) */ /* Start with a clean SCR_EL3 copy as all relevant values are set */ scr_el3 = SCR_RESET_VAL; @@ -392,6 +426,15 @@ static void setup_context_common(cpu_context_t *ctx, const entry_point_info_t *e scr_el3 |= SCR_HXEn_BIT; } + /* + * If FEAT_LS64_ACCDATA is enabled, enable access to ACCDATA_EL1 by + * setting SCR_EL3.ADEn and allow the ST64BV0 instruction by setting + * SCR_EL3.EnAS0. + */ + if (is_feat_ls64_accdata_supported()) { + scr_el3 |= SCR_ADEn_BIT | SCR_EnAS0_BIT; + } + /* * If FEAT_RNG_TRAP is enabled, all reads of the RNDR and RNDRRS * registers are trapped to EL3. @@ -483,11 +526,6 @@ static void setup_context_common(cpu_context_t *ctx, const entry_point_info_t *e } #endif /* (IMAGE_BL31 && defined(SPD_spmd) && SPMD_SPM_AT_SEL2) */ - if (is_feat_mpam_supported()) { - write_ctx_reg(get_el3state_ctx(ctx), CTX_MPAM3_EL3, \ - MPAM3_EL3_RESET_VAL); - } - /* * Populate EL3 state so that we've the right context * before doing ERET @@ -496,6 +534,37 @@ static void setup_context_common(cpu_context_t *ctx, const entry_point_info_t *e write_ctx_reg(state, CTX_ELR_EL3, ep->pc); write_ctx_reg(state, CTX_SPSR_EL3, ep->spsr); + /* Start with a clean MDCR_EL3 copy as all relevant values are set */ + mdcr_el3 = MDCR_EL3_RESET_VAL; + + /* --------------------------------------------------------------------- + * Initialise MDCR_EL3, setting all fields rather than relying on hw. + * Some fields are architecturally UNKNOWN on reset. + * + * MDCR_EL3.SDD: Set to one to disable AArch64 Secure self-hosted debug. + * Debug exceptions, other than Breakpoint Instruction exceptions, are + * disabled from all ELs in Secure state. + * + * MDCR_EL3.SPD32: Set to 0b10 to disable AArch32 Secure self-hosted + * privileged debug from S-EL1. + * + * MDCR_EL3.TDOSA: Set to zero so that EL2 and EL2 System register + * access to the powerdown debug registers do not trap to EL3. + * + * MDCR_EL3.TDA: Set to zero to allow EL0, EL1 and EL2 access to the + * debug registers, other than those registers that are controlled by + * MDCR_EL3.TDOSA. + */ + mdcr_el3 |= ((MDCR_SDD_BIT | MDCR_SPD32(MDCR_SPD32_DISABLE)) + & ~(MDCR_TDA_BIT | MDCR_TDOSA_BIT)) ; + write_ctx_reg(state, CTX_MDCR_EL3, mdcr_el3); + + /* + * Configure MDCR_EL3 register as applicable for each world + * (NS/Secure/Realm) context. + */ + manage_extensions_common(ctx); + /* * Store the X0-X7 value from the entrypoint into the context * Use memcpy as we are in control of the layout of the structures @@ -572,10 +641,6 @@ void cm_setup_context(cpu_context_t *ctx, const entry_point_info_t *ep) #if IMAGE_BL31 void cm_manage_extensions_el3(void) { - if (is_feat_spe_supported()) { - spe_init_el3(); - } - if (is_feat_amu_supported()) { amu_init_el3(); } @@ -584,19 +649,39 @@ void cm_manage_extensions_el3(void) sme_init_el3(); } - if (is_feat_trbe_supported()) { - trbe_init_el3(); - } + pmuv3_init_el3(); +} +#endif /* IMAGE_BL31 */ - if (is_feat_brbe_supported()) { - brbe_init_el3(); - } +/****************************************************************************** + * Function to initialise the registers with the RESET values in the context + * memory, which are maintained per world. + ******************************************************************************/ +#if IMAGE_BL31 +void cm_el3_arch_init_per_world(per_world_context_t *per_world_ctx) +{ + /* + * Initialise CPTR_EL3, setting all fields rather than relying on hw. + * + * CPTR_EL3.TFP: Set to zero so that accesses to the V- or Z- registers + * by Advanced SIMD, floating-point or SVE instructions (if + * implemented) do not trap to EL3. + * + * CPTR_EL3.TCPAC: Set to zero so that accesses to CPACR_EL1, + * CPTR_EL2,CPACR, or HCPTR do not trap to EL3. + */ + uint64_t cptr_el3 = CPTR_EL3_RESET_VAL & ~(TCPAC_BIT | TFP_BIT); - if (is_feat_trf_supported()) { - trf_init_el3(); - } + per_world_ctx->ctx_cptr_el3 = cptr_el3; - pmuv3_init_el3(); + /* + * Initialize MPAM3_EL3 to its default reset value + * + * MPAM3_EL3_RESET_VAL sets the MPAM3_EL3.TRAPLOWER bit that forces + * all lower ELn MPAM3_EL3 register access to, trap to EL3 + */ + + per_world_ctx->ctx_mpam3_el3 = MPAM3_EL3_RESET_VAL; } #endif /* IMAGE_BL31 */ @@ -608,6 +693,8 @@ void cm_manage_extensions_el3(void) #if IMAGE_BL31 void manage_extensions_nonsecure_per_world(void) { + cm_el3_arch_init_per_world(&per_world_context[CPU_CONTEXT_NS]); + if (is_feat_sme_supported()) { sme_enable_per_world(&per_world_context[CPU_CONTEXT_NS]); } @@ -623,6 +710,10 @@ void manage_extensions_nonsecure_per_world(void) if (is_feat_sys_reg_trace_supported()) { sys_reg_trace_enable_per_world(&per_world_context[CPU_CONTEXT_NS]); } + + if (is_feat_mpam_supported()) { + mpam_enable_per_world(&per_world_context[CPU_CONTEXT_NS]); + } } #endif /* IMAGE_BL31 */ @@ -631,10 +722,11 @@ void manage_extensions_nonsecure_per_world(void) * This function enables the architecture extensions, which have same value * across the cores for the secure world. ******************************************************************************/ - static void manage_extensions_secure_per_world(void) { #if IMAGE_BL31 + cm_el3_arch_init_per_world(&per_world_context[CPU_CONTEXT_SECURE]); + if (is_feat_sme_supported()) { if (ENABLE_SME_FOR_SWD) { @@ -676,6 +768,41 @@ static void manage_extensions_secure_per_world(void) #endif /* IMAGE_BL31 */ } +/******************************************************************************* + * Enable architecture extensions on first entry to Non-secure world only + * and disable for secure world. + * + * NOTE: Arch features which have been provided with the capability of getting + * enabled only for non-secure world and being disabled for secure world are + * grouped here, as the MDCR_EL3 context value remains same across the worlds. + ******************************************************************************/ +static void manage_extensions_common(cpu_context_t *ctx) +{ +#if IMAGE_BL31 + if (is_feat_spe_supported()) { + /* + * Enable FEAT_SPE for Non-Secure and prohibit for Secure state. + */ + spe_enable(ctx); + } + + if (is_feat_trbe_supported()) { + /* + * Enable FEAT_TRBE for Non-Secure and prohibit for Secure and + * Realm state. + */ + trbe_enable(ctx); + } + + if (is_feat_trf_supported()) { + /* + * Enable FEAT_TRF for Non-Secure and prohibit for Secure state. + */ + trf_enable(ctx); + } +#endif /* IMAGE_BL31 */ +} + /******************************************************************************* * Enable architecture extensions on first entry to Non-secure world. ******************************************************************************/ @@ -690,9 +817,18 @@ static void manage_extensions_nonsecure(cpu_context_t *ctx) sme_enable(ctx); } - if (is_feat_mpam_supported()) { - mpam_enable(ctx); + if (is_feat_fgt2_supported()) { + fgt2_enable(ctx); + } + + if (is_feat_debugv8p9_supported()) { + debugv8p9_extended_bp_wp_enable(ctx); } + + if (is_feat_brbe_supported()) { + brbe_enable(ctx); + } + pmuv3_enable(ctx); #endif /* IMAGE_BL31 */ } @@ -785,6 +921,7 @@ static void manage_extensions_secure(cpu_context_t *ctx) #endif /* IMAGE_BL31 */ } +#if !IMAGE_BL1 /******************************************************************************* * The following function initializes the cpu_context for a CPU specified by * its `cpu_idx` for first use, and sets the initial entrypoint state as @@ -797,6 +934,7 @@ void cm_init_context_by_index(unsigned int cpu_idx, ctx = cm_get_context_by_index(cpu_idx, GET_SECURITY_STATE(ep->h.attr)); cm_setup_context(ctx, ep); } +#endif /* !IMAGE_BL1 */ /******************************************************************************* * The following function initializes the cpu_context for the current CPU @@ -926,7 +1064,7 @@ static void init_nonsecure_el2_unused(cpu_context_t *ctx) ******************************************************************************/ void cm_prepare_el3_exit(uint32_t security_state) { - u_register_t sctlr_elx, scr_el3; + u_register_t sctlr_el2, scr_el3; cpu_context_t *ctx = cm_get_context(security_state); assert(ctx != NULL); @@ -937,8 +1075,8 @@ void cm_prepare_el3_exit(uint32_t security_state) scr_el3 = read_ctx_reg(get_el3state_ctx(ctx), CTX_SCR_EL3); - if (((scr_el3 & SCR_HCE_BIT) != 0U) - || (el2_implemented != EL_IMPL_NONE)) { + if (el2_implemented != EL_IMPL_NONE) { + /* * If context is not being used for EL2, initialize * HCRX_EL2 with its init value here. @@ -964,64 +1102,87 @@ void cm_prepare_el3_exit(uint32_t security_state) write_hfgrtr_el2(HFGRTR_EL2_INIT_VAL); write_hfgwtr_el2(HFGWTR_EL2_INIT_VAL); } - } + /* Condition to ensure EL2 is being used. */ + if ((scr_el3 & SCR_HCE_BIT) != 0U) { + /* Initialize SCTLR_EL2 register with reset value. */ + sctlr_el2 = SCTLR_EL2_RES1; - if ((scr_el3 & SCR_HCE_BIT) != 0U) { - /* Use SCTLR_EL1.EE value to initialise sctlr_el2 */ - sctlr_elx = read_ctx_reg(get_el1_sysregs_ctx(ctx), - CTX_SCTLR_EL1); - sctlr_elx &= SCTLR_EE_BIT; - sctlr_elx |= SCTLR_EL2_RES1; -#if ERRATA_A75_764081 - /* - * If workaround of errata 764081 for Cortex-A75 is used - * then set SCTLR_EL2.IESB to enable Implicit Error - * Synchronization Barrier. - */ - sctlr_elx |= SCTLR_IESB_BIT; -#endif - write_sctlr_el2(sctlr_elx); - } else if (el2_implemented != EL_IMPL_NONE) { - init_nonsecure_el2_unused(ctx); + /* + * If workaround of errata 764081 for Cortex-A75 + * is used then set SCTLR_EL2.IESB to enable + * Implicit Error Synchronization Barrier. + */ + if (errata_a75_764081_applies()) { + sctlr_el2 |= SCTLR_IESB_BIT; + } + + write_sctlr_el2(sctlr_el2); + } else { + /* + * (scr_el3 & SCR_HCE_BIT==0) + * EL2 implemented but unused. + */ + init_nonsecure_el2_unused(ctx); + } } } - +#if (!CTX_INCLUDE_EL2_REGS) + /* Restore EL1 system registers, only when CTX_INCLUDE_EL2_REGS=0 */ cm_el1_sysregs_context_restore(security_state); +#endif cm_set_next_eret_context(security_state); } -#if CTX_INCLUDE_EL2_REGS +#if (CTX_INCLUDE_EL2_REGS && IMAGE_BL31) static void el2_sysregs_context_save_fgt(el2_sysregs_t *ctx) { - write_ctx_reg(ctx, CTX_HDFGRTR_EL2, read_hdfgrtr_el2()); + write_el2_ctx_fgt(ctx, hdfgrtr_el2, read_hdfgrtr_el2()); if (is_feat_amu_supported()) { - write_ctx_reg(ctx, CTX_HAFGRTR_EL2, read_hafgrtr_el2()); + write_el2_ctx_fgt(ctx, hafgrtr_el2, read_hafgrtr_el2()); } - write_ctx_reg(ctx, CTX_HDFGWTR_EL2, read_hdfgwtr_el2()); - write_ctx_reg(ctx, CTX_HFGITR_EL2, read_hfgitr_el2()); - write_ctx_reg(ctx, CTX_HFGRTR_EL2, read_hfgrtr_el2()); - write_ctx_reg(ctx, CTX_HFGWTR_EL2, read_hfgwtr_el2()); + write_el2_ctx_fgt(ctx, hdfgwtr_el2, read_hdfgwtr_el2()); + write_el2_ctx_fgt(ctx, hfgitr_el2, read_hfgitr_el2()); + write_el2_ctx_fgt(ctx, hfgrtr_el2, read_hfgrtr_el2()); + write_el2_ctx_fgt(ctx, hfgwtr_el2, read_hfgwtr_el2()); } static void el2_sysregs_context_restore_fgt(el2_sysregs_t *ctx) { - write_hdfgrtr_el2(read_ctx_reg(ctx, CTX_HDFGRTR_EL2)); + write_hdfgrtr_el2(read_el2_ctx_fgt(ctx, hdfgrtr_el2)); if (is_feat_amu_supported()) { - write_hafgrtr_el2(read_ctx_reg(ctx, CTX_HAFGRTR_EL2)); + write_hafgrtr_el2(read_el2_ctx_fgt(ctx, hafgrtr_el2)); } - write_hdfgwtr_el2(read_ctx_reg(ctx, CTX_HDFGWTR_EL2)); - write_hfgitr_el2(read_ctx_reg(ctx, CTX_HFGITR_EL2)); - write_hfgrtr_el2(read_ctx_reg(ctx, CTX_HFGRTR_EL2)); - write_hfgwtr_el2(read_ctx_reg(ctx, CTX_HFGWTR_EL2)); + write_hdfgwtr_el2(read_el2_ctx_fgt(ctx, hdfgwtr_el2)); + write_hfgitr_el2(read_el2_ctx_fgt(ctx, hfgitr_el2)); + write_hfgrtr_el2(read_el2_ctx_fgt(ctx, hfgrtr_el2)); + write_hfgwtr_el2(read_el2_ctx_fgt(ctx, hfgwtr_el2)); +} + +static void el2_sysregs_context_save_fgt2(el2_sysregs_t *ctx) +{ + write_el2_ctx_fgt2(ctx, hdfgrtr2_el2, read_hdfgrtr2_el2()); + write_el2_ctx_fgt2(ctx, hdfgwtr2_el2, read_hdfgwtr2_el2()); + write_el2_ctx_fgt2(ctx, hfgitr2_el2, read_hfgitr2_el2()); + write_el2_ctx_fgt2(ctx, hfgrtr2_el2, read_hfgrtr2_el2()); + write_el2_ctx_fgt2(ctx, hfgwtr2_el2, read_hfgwtr2_el2()); +} + +static void el2_sysregs_context_restore_fgt2(el2_sysregs_t *ctx) +{ + write_hdfgrtr2_el2(read_el2_ctx_fgt2(ctx, hdfgrtr2_el2)); + write_hdfgwtr2_el2(read_el2_ctx_fgt2(ctx, hdfgwtr2_el2)); + write_hfgitr2_el2(read_el2_ctx_fgt2(ctx, hfgitr2_el2)); + write_hfgrtr2_el2(read_el2_ctx_fgt2(ctx, hfgrtr2_el2)); + write_hfgwtr2_el2(read_el2_ctx_fgt2(ctx, hfgwtr2_el2)); } static void el2_sysregs_context_save_mpam(el2_sysregs_t *ctx) { u_register_t mpam_idr = read_mpamidr_el1(); - write_ctx_reg(ctx, CTX_MPAM2_EL2, read_mpam2_el2()); + write_el2_ctx_mpam(ctx, mpam2_el2, read_mpam2_el2()); /* * The context registers that we intend to save would be part of the @@ -1035,9 +1196,9 @@ static void el2_sysregs_context_save_mpam(el2_sysregs_t *ctx) * MPAMHCR_EL2, MPAMVPMV_EL2 and MPAMVPM0_EL2 are always present if * MPAMIDR_HAS_HCR_BIT == 1. */ - write_ctx_reg(ctx, CTX_MPAMHCR_EL2, read_mpamhcr_el2()); - write_ctx_reg(ctx, CTX_MPAMVPM0_EL2, read_mpamvpm0_el2()); - write_ctx_reg(ctx, CTX_MPAMVPMV_EL2, read_mpamvpmv_el2()); + write_el2_ctx_mpam(ctx, mpamhcr_el2, read_mpamhcr_el2()); + write_el2_ctx_mpam(ctx, mpamvpm0_el2, read_mpamvpm0_el2()); + write_el2_ctx_mpam(ctx, mpamvpmv_el2, read_mpamvpmv_el2()); /* * The number of MPAMVPM registers is implementation defined, their @@ -1045,25 +1206,25 @@ static void el2_sysregs_context_save_mpam(el2_sysregs_t *ctx) */ switch ((mpam_idr >> MPAMIDR_EL1_VPMR_MAX_SHIFT) & MPAMIDR_EL1_VPMR_MAX_MASK) { case 7: - write_ctx_reg(ctx, CTX_MPAMVPM7_EL2, read_mpamvpm7_el2()); + write_el2_ctx_mpam(ctx, mpamvpm7_el2, read_mpamvpm7_el2()); __fallthrough; case 6: - write_ctx_reg(ctx, CTX_MPAMVPM6_EL2, read_mpamvpm6_el2()); + write_el2_ctx_mpam(ctx, mpamvpm6_el2, read_mpamvpm6_el2()); __fallthrough; case 5: - write_ctx_reg(ctx, CTX_MPAMVPM5_EL2, read_mpamvpm5_el2()); + write_el2_ctx_mpam(ctx, mpamvpm5_el2, read_mpamvpm5_el2()); __fallthrough; case 4: - write_ctx_reg(ctx, CTX_MPAMVPM4_EL2, read_mpamvpm4_el2()); + write_el2_ctx_mpam(ctx, mpamvpm4_el2, read_mpamvpm4_el2()); __fallthrough; case 3: - write_ctx_reg(ctx, CTX_MPAMVPM3_EL2, read_mpamvpm3_el2()); + write_el2_ctx_mpam(ctx, mpamvpm3_el2, read_mpamvpm3_el2()); __fallthrough; case 2: - write_ctx_reg(ctx, CTX_MPAMVPM2_EL2, read_mpamvpm2_el2()); + write_el2_ctx_mpam(ctx, mpamvpm2_el2, read_mpamvpm2_el2()); __fallthrough; case 1: - write_ctx_reg(ctx, CTX_MPAMVPM1_EL2, read_mpamvpm1_el2()); + write_el2_ctx_mpam(ctx, mpamvpm1_el2, read_mpamvpm1_el2()); break; } } @@ -1072,148 +1233,161 @@ static void el2_sysregs_context_restore_mpam(el2_sysregs_t *ctx) { u_register_t mpam_idr = read_mpamidr_el1(); - write_mpam2_el2(read_ctx_reg(ctx, CTX_MPAM2_EL2)); + write_mpam2_el2(read_el2_ctx_mpam(ctx, mpam2_el2)); if ((mpam_idr & MPAMIDR_HAS_HCR_BIT) == 0U) { return; } - write_mpamhcr_el2(read_ctx_reg(ctx, CTX_MPAMHCR_EL2)); - write_mpamvpm0_el2(read_ctx_reg(ctx, CTX_MPAMVPM0_EL2)); - write_mpamvpmv_el2(read_ctx_reg(ctx, CTX_MPAMVPMV_EL2)); + write_mpamhcr_el2(read_el2_ctx_mpam(ctx, mpamhcr_el2)); + write_mpamvpm0_el2(read_el2_ctx_mpam(ctx, mpamvpm0_el2)); + write_mpamvpmv_el2(read_el2_ctx_mpam(ctx, mpamvpmv_el2)); switch ((mpam_idr >> MPAMIDR_EL1_VPMR_MAX_SHIFT) & MPAMIDR_EL1_VPMR_MAX_MASK) { case 7: - write_mpamvpm7_el2(read_ctx_reg(ctx, CTX_MPAMVPM7_EL2)); + write_mpamvpm7_el2(read_el2_ctx_mpam(ctx, mpamvpm7_el2)); __fallthrough; case 6: - write_mpamvpm6_el2(read_ctx_reg(ctx, CTX_MPAMVPM6_EL2)); + write_mpamvpm6_el2(read_el2_ctx_mpam(ctx, mpamvpm6_el2)); __fallthrough; case 5: - write_mpamvpm5_el2(read_ctx_reg(ctx, CTX_MPAMVPM5_EL2)); + write_mpamvpm5_el2(read_el2_ctx_mpam(ctx, mpamvpm5_el2)); __fallthrough; case 4: - write_mpamvpm4_el2(read_ctx_reg(ctx, CTX_MPAMVPM4_EL2)); + write_mpamvpm4_el2(read_el2_ctx_mpam(ctx, mpamvpm4_el2)); __fallthrough; case 3: - write_mpamvpm3_el2(read_ctx_reg(ctx, CTX_MPAMVPM3_EL2)); + write_mpamvpm3_el2(read_el2_ctx_mpam(ctx, mpamvpm3_el2)); __fallthrough; case 2: - write_mpamvpm2_el2(read_ctx_reg(ctx, CTX_MPAMVPM2_EL2)); + write_mpamvpm2_el2(read_el2_ctx_mpam(ctx, mpamvpm2_el2)); __fallthrough; case 1: - write_mpamvpm1_el2(read_ctx_reg(ctx, CTX_MPAMVPM1_EL2)); + write_mpamvpm1_el2(read_el2_ctx_mpam(ctx, mpamvpm1_el2)); break; } } -/* ----------------------------------------------------- +/* --------------------------------------------------------------------------- * The following registers are not added: - * AMEVCNTVOFF0<n>_EL2 - * AMEVCNTVOFF1<n>_EL2 * ICH_AP0R<n>_EL2 * ICH_AP1R<n>_EL2 * ICH_LR<n>_EL2 - * ----------------------------------------------------- + * + * NOTE: For a system with S-EL2 present but not enabled, accessing + * ICC_SRE_EL2 is undefined from EL3. To workaround this change the + * SCR_EL3.NS = 1 before accessing this register. + * --------------------------------------------------------------------------- */ -static void el2_sysregs_context_save_common(el2_sysregs_t *ctx) +static void el2_sysregs_context_save_gic(el2_sysregs_t *ctx) { - write_ctx_reg(ctx, CTX_ACTLR_EL2, read_actlr_el2()); - write_ctx_reg(ctx, CTX_AFSR0_EL2, read_afsr0_el2()); - write_ctx_reg(ctx, CTX_AFSR1_EL2, read_afsr1_el2()); - write_ctx_reg(ctx, CTX_AMAIR_EL2, read_amair_el2()); - write_ctx_reg(ctx, CTX_CNTHCTL_EL2, read_cnthctl_el2()); - write_ctx_reg(ctx, CTX_CNTVOFF_EL2, read_cntvoff_el2()); - write_ctx_reg(ctx, CTX_CPTR_EL2, read_cptr_el2()); - if (CTX_INCLUDE_AARCH32_REGS) { - write_ctx_reg(ctx, CTX_DBGVCR32_EL2, read_dbgvcr32_el2()); - } - write_ctx_reg(ctx, CTX_ELR_EL2, read_elr_el2()); - write_ctx_reg(ctx, CTX_ESR_EL2, read_esr_el2()); - write_ctx_reg(ctx, CTX_FAR_EL2, read_far_el2()); - write_ctx_reg(ctx, CTX_HACR_EL2, read_hacr_el2()); - write_ctx_reg(ctx, CTX_HCR_EL2, read_hcr_el2()); - write_ctx_reg(ctx, CTX_HPFAR_EL2, read_hpfar_el2()); - write_ctx_reg(ctx, CTX_HSTR_EL2, read_hstr_el2()); - - /* - * Set the NS bit to be able to access the ICC_SRE_EL2 register - * TODO: remove with root context - */ +#if defined(SPD_spmd) && SPMD_SPM_AT_SEL2 + write_el2_ctx_common(ctx, icc_sre_el2, read_icc_sre_el2()); +#else u_register_t scr_el3 = read_scr_el3(); - write_scr_el3(scr_el3 | SCR_NS_BIT); isb(); - write_ctx_reg(ctx, CTX_ICC_SRE_EL2, read_icc_sre_el2()); + + write_el2_ctx_common(ctx, icc_sre_el2, read_icc_sre_el2()); write_scr_el3(scr_el3); isb(); - - write_ctx_reg(ctx, CTX_ICH_HCR_EL2, read_ich_hcr_el2()); - write_ctx_reg(ctx, CTX_ICH_VMCR_EL2, read_ich_vmcr_el2()); - write_ctx_reg(ctx, CTX_MAIR_EL2, read_mair_el2()); - write_ctx_reg(ctx, CTX_MDCR_EL2, read_mdcr_el2()); - write_ctx_reg(ctx, CTX_SCTLR_EL2, read_sctlr_el2()); - write_ctx_reg(ctx, CTX_SPSR_EL2, read_spsr_el2()); - write_ctx_reg(ctx, CTX_SP_EL2, read_sp_el2()); - write_ctx_reg(ctx, CTX_TCR_EL2, read_tcr_el2()); - write_ctx_reg(ctx, CTX_TPIDR_EL2, read_tpidr_el2()); - write_ctx_reg(ctx, CTX_TTBR0_EL2, read_ttbr0_el2()); - write_ctx_reg(ctx, CTX_VBAR_EL2, read_vbar_el2()); - write_ctx_reg(ctx, CTX_VMPIDR_EL2, read_vmpidr_el2()); - write_ctx_reg(ctx, CTX_VPIDR_EL2, read_vpidr_el2()); - write_ctx_reg(ctx, CTX_VTCR_EL2, read_vtcr_el2()); - write_ctx_reg(ctx, CTX_VTTBR_EL2, read_vttbr_el2()); +#endif + write_el2_ctx_common(ctx, ich_hcr_el2, read_ich_hcr_el2()); + write_el2_ctx_common(ctx, ich_vmcr_el2, read_ich_vmcr_el2()); } -static void el2_sysregs_context_restore_common(el2_sysregs_t *ctx) +static void el2_sysregs_context_restore_gic(el2_sysregs_t *ctx) { - write_actlr_el2(read_ctx_reg(ctx, CTX_ACTLR_EL2)); - write_afsr0_el2(read_ctx_reg(ctx, CTX_AFSR0_EL2)); - write_afsr1_el2(read_ctx_reg(ctx, CTX_AFSR1_EL2)); - write_amair_el2(read_ctx_reg(ctx, CTX_AMAIR_EL2)); - write_cnthctl_el2(read_ctx_reg(ctx, CTX_CNTHCTL_EL2)); - write_cntvoff_el2(read_ctx_reg(ctx, CTX_CNTVOFF_EL2)); - write_cptr_el2(read_ctx_reg(ctx, CTX_CPTR_EL2)); - if (CTX_INCLUDE_AARCH32_REGS) { - write_dbgvcr32_el2(read_ctx_reg(ctx, CTX_DBGVCR32_EL2)); - } - write_elr_el2(read_ctx_reg(ctx, CTX_ELR_EL2)); - write_esr_el2(read_ctx_reg(ctx, CTX_ESR_EL2)); - write_far_el2(read_ctx_reg(ctx, CTX_FAR_EL2)); - write_hacr_el2(read_ctx_reg(ctx, CTX_HACR_EL2)); - write_hcr_el2(read_ctx_reg(ctx, CTX_HCR_EL2)); - write_hpfar_el2(read_ctx_reg(ctx, CTX_HPFAR_EL2)); - write_hstr_el2(read_ctx_reg(ctx, CTX_HSTR_EL2)); - - /* - * Set the NS bit to be able to access the ICC_SRE_EL2 register - * TODO: remove with root context - */ +#if defined(SPD_spmd) && SPMD_SPM_AT_SEL2 + write_icc_sre_el2(read_el2_ctx_common(ctx, icc_sre_el2)); +#else u_register_t scr_el3 = read_scr_el3(); - write_scr_el3(scr_el3 | SCR_NS_BIT); isb(); - write_icc_sre_el2(read_ctx_reg(ctx, CTX_ICC_SRE_EL2)); + + write_icc_sre_el2(read_el2_ctx_common(ctx, icc_sre_el2)); write_scr_el3(scr_el3); isb(); +#endif + write_ich_hcr_el2(read_el2_ctx_common(ctx, ich_hcr_el2)); + write_ich_vmcr_el2(read_el2_ctx_common(ctx, ich_vmcr_el2)); +} + +/* ----------------------------------------------------- + * The following registers are not added: + * AMEVCNTVOFF0<n>_EL2 + * AMEVCNTVOFF1<n>_EL2 + * ----------------------------------------------------- + */ +static void el2_sysregs_context_save_common(el2_sysregs_t *ctx) +{ + write_el2_ctx_common(ctx, actlr_el2, read_actlr_el2()); + write_el2_ctx_common(ctx, afsr0_el2, read_afsr0_el2()); + write_el2_ctx_common(ctx, afsr1_el2, read_afsr1_el2()); + write_el2_ctx_common(ctx, amair_el2, read_amair_el2()); + write_el2_ctx_common(ctx, cnthctl_el2, read_cnthctl_el2()); + write_el2_ctx_common(ctx, cntvoff_el2, read_cntvoff_el2()); + write_el2_ctx_common(ctx, cptr_el2, read_cptr_el2()); + if (CTX_INCLUDE_AARCH32_REGS) { + write_el2_ctx_common(ctx, dbgvcr32_el2, read_dbgvcr32_el2()); + } + write_el2_ctx_common(ctx, elr_el2, read_elr_el2()); + write_el2_ctx_common(ctx, esr_el2, read_esr_el2()); + write_el2_ctx_common(ctx, far_el2, read_far_el2()); + write_el2_ctx_common(ctx, hacr_el2, read_hacr_el2()); + write_el2_ctx_common(ctx, hcr_el2, read_hcr_el2()); + write_el2_ctx_common(ctx, hpfar_el2, read_hpfar_el2()); + write_el2_ctx_common(ctx, hstr_el2, read_hstr_el2()); + write_el2_ctx_common(ctx, mair_el2, read_mair_el2()); + write_el2_ctx_common(ctx, mdcr_el2, read_mdcr_el2()); + write_el2_ctx_common(ctx, sctlr_el2, read_sctlr_el2()); + write_el2_ctx_common(ctx, spsr_el2, read_spsr_el2()); + write_el2_ctx_common(ctx, sp_el2, read_sp_el2()); + write_el2_ctx_common(ctx, tcr_el2, read_tcr_el2()); + write_el2_ctx_common(ctx, tpidr_el2, read_tpidr_el2()); + write_el2_ctx_common(ctx, vbar_el2, read_vbar_el2()); + write_el2_ctx_common(ctx, vmpidr_el2, read_vmpidr_el2()); + write_el2_ctx_common(ctx, vpidr_el2, read_vpidr_el2()); + write_el2_ctx_common(ctx, vtcr_el2, read_vtcr_el2()); + + write_el2_ctx_sysreg128(ctx, ttbr0_el2, read_ttbr0_el2()); + write_el2_ctx_sysreg128(ctx, vttbr_el2, read_vttbr_el2()); +} - write_ich_hcr_el2(read_ctx_reg(ctx, CTX_ICH_HCR_EL2)); - write_ich_vmcr_el2(read_ctx_reg(ctx, CTX_ICH_VMCR_EL2)); - write_mair_el2(read_ctx_reg(ctx, CTX_MAIR_EL2)); - write_mdcr_el2(read_ctx_reg(ctx, CTX_MDCR_EL2)); - write_sctlr_el2(read_ctx_reg(ctx, CTX_SCTLR_EL2)); - write_spsr_el2(read_ctx_reg(ctx, CTX_SPSR_EL2)); - write_sp_el2(read_ctx_reg(ctx, CTX_SP_EL2)); - write_tcr_el2(read_ctx_reg(ctx, CTX_TCR_EL2)); - write_tpidr_el2(read_ctx_reg(ctx, CTX_TPIDR_EL2)); - write_ttbr0_el2(read_ctx_reg(ctx, CTX_TTBR0_EL2)); - write_vbar_el2(read_ctx_reg(ctx, CTX_VBAR_EL2)); - write_vmpidr_el2(read_ctx_reg(ctx, CTX_VMPIDR_EL2)); - write_vpidr_el2(read_ctx_reg(ctx, CTX_VPIDR_EL2)); - write_vtcr_el2(read_ctx_reg(ctx, CTX_VTCR_EL2)); - write_vttbr_el2(read_ctx_reg(ctx, CTX_VTTBR_EL2)); +static void el2_sysregs_context_restore_common(el2_sysregs_t *ctx) +{ + write_actlr_el2(read_el2_ctx_common(ctx, actlr_el2)); + write_afsr0_el2(read_el2_ctx_common(ctx, afsr0_el2)); + write_afsr1_el2(read_el2_ctx_common(ctx, afsr1_el2)); + write_amair_el2(read_el2_ctx_common(ctx, amair_el2)); + write_cnthctl_el2(read_el2_ctx_common(ctx, cnthctl_el2)); + write_cntvoff_el2(read_el2_ctx_common(ctx, cntvoff_el2)); + write_cptr_el2(read_el2_ctx_common(ctx, cptr_el2)); + if (CTX_INCLUDE_AARCH32_REGS) { + write_dbgvcr32_el2(read_el2_ctx_common(ctx, dbgvcr32_el2)); + } + write_elr_el2(read_el2_ctx_common(ctx, elr_el2)); + write_esr_el2(read_el2_ctx_common(ctx, esr_el2)); + write_far_el2(read_el2_ctx_common(ctx, far_el2)); + write_hacr_el2(read_el2_ctx_common(ctx, hacr_el2)); + write_hcr_el2(read_el2_ctx_common(ctx, hcr_el2)); + write_hpfar_el2(read_el2_ctx_common(ctx, hpfar_el2)); + write_hstr_el2(read_el2_ctx_common(ctx, hstr_el2)); + write_mair_el2(read_el2_ctx_common(ctx, mair_el2)); + write_mdcr_el2(read_el2_ctx_common(ctx, mdcr_el2)); + write_sctlr_el2(read_el2_ctx_common(ctx, sctlr_el2)); + write_spsr_el2(read_el2_ctx_common(ctx, spsr_el2)); + write_sp_el2(read_el2_ctx_common(ctx, sp_el2)); + write_tcr_el2(read_el2_ctx_common(ctx, tcr_el2)); + write_tpidr_el2(read_el2_ctx_common(ctx, tpidr_el2)); + write_ttbr0_el2(read_el2_ctx_common(ctx, ttbr0_el2)); + write_vbar_el2(read_el2_ctx_common(ctx, vbar_el2)); + write_vmpidr_el2(read_el2_ctx_common(ctx, vmpidr_el2)); + write_vpidr_el2(read_el2_ctx_common(ctx, vpidr_el2)); + write_vtcr_el2(read_el2_ctx_common(ctx, vtcr_el2)); + write_vttbr_el2(read_el2_ctx_common(ctx, vttbr_el2)); } /******************************************************************************* @@ -1230,9 +1404,12 @@ void cm_el2_sysregs_context_save(uint32_t security_state) el2_sysregs_ctx = get_el2_sysregs_ctx(ctx); el2_sysregs_context_save_common(el2_sysregs_ctx); -#if CTX_INCLUDE_MTE_REGS - write_ctx_reg(el2_sysregs_ctx, CTX_TFSR_EL2, read_tfsr_el2()); -#endif + el2_sysregs_context_save_gic(el2_sysregs_ctx); + + if (is_feat_mte2_supported()) { + write_el2_ctx_mte2(el2_sysregs_ctx, tfsr_el2, read_tfsr_el2()); + } + if (is_feat_mpam_supported()) { el2_sysregs_context_save_mpam(el2_sysregs_ctx); } @@ -1241,51 +1418,66 @@ void cm_el2_sysregs_context_save(uint32_t security_state) el2_sysregs_context_save_fgt(el2_sysregs_ctx); } + if (is_feat_fgt2_supported()) { + el2_sysregs_context_save_fgt2(el2_sysregs_ctx); + } + if (is_feat_ecv_v2_supported()) { - write_ctx_reg(el2_sysregs_ctx, CTX_CNTPOFF_EL2, read_cntpoff_el2()); + write_el2_ctx_ecv(el2_sysregs_ctx, cntpoff_el2, read_cntpoff_el2()); } if (is_feat_vhe_supported()) { - write_ctx_reg(el2_sysregs_ctx, CTX_CONTEXTIDR_EL2, read_contextidr_el2()); - write_ctx_reg(el2_sysregs_ctx, CTX_TTBR1_EL2, read_ttbr1_el2()); + write_el2_ctx_vhe(el2_sysregs_ctx, contextidr_el2, + read_contextidr_el2()); + write_el2_ctx_vhe_sysreg128(el2_sysregs_ctx, ttbr1_el2, read_ttbr1_el2()); } if (is_feat_ras_supported()) { - write_ctx_reg(el2_sysregs_ctx, CTX_VDISR_EL2, read_vdisr_el2()); - write_ctx_reg(el2_sysregs_ctx, CTX_VSESR_EL2, read_vsesr_el2()); + write_el2_ctx_ras(el2_sysregs_ctx, vdisr_el2, read_vdisr_el2()); + write_el2_ctx_ras(el2_sysregs_ctx, vsesr_el2, read_vsesr_el2()); } if (is_feat_nv2_supported()) { - write_ctx_reg(el2_sysregs_ctx, CTX_VNCR_EL2, read_vncr_el2()); + write_el2_ctx_neve(el2_sysregs_ctx, vncr_el2, read_vncr_el2()); } if (is_feat_trf_supported()) { - write_ctx_reg(el2_sysregs_ctx, CTX_TRFCR_EL2, read_trfcr_el2()); + write_el2_ctx_trf(el2_sysregs_ctx, trfcr_el2, read_trfcr_el2()); } if (is_feat_csv2_2_supported()) { - write_ctx_reg(el2_sysregs_ctx, CTX_SCXTNUM_EL2, read_scxtnum_el2()); + write_el2_ctx_csv2_2(el2_sysregs_ctx, scxtnum_el2, + read_scxtnum_el2()); } if (is_feat_hcx_supported()) { - write_ctx_reg(el2_sysregs_ctx, CTX_HCRX_EL2, read_hcrx_el2()); + write_el2_ctx_hcx(el2_sysregs_ctx, hcrx_el2, read_hcrx_el2()); } + if (is_feat_tcr2_supported()) { - write_ctx_reg(el2_sysregs_ctx, CTX_TCR2_EL2, read_tcr2_el2()); + write_el2_ctx_tcr2(el2_sysregs_ctx, tcr2_el2, read_tcr2_el2()); } + if (is_feat_sxpie_supported()) { - write_ctx_reg(el2_sysregs_ctx, CTX_PIRE0_EL2, read_pire0_el2()); - write_ctx_reg(el2_sysregs_ctx, CTX_PIR_EL2, read_pir_el2()); - } - if (is_feat_s2pie_supported()) { - write_ctx_reg(el2_sysregs_ctx, CTX_S2PIR_EL2, read_s2pir_el2()); + write_el2_ctx_sxpie(el2_sysregs_ctx, pire0_el2, read_pire0_el2()); + write_el2_ctx_sxpie(el2_sysregs_ctx, pir_el2, read_pir_el2()); } + if (is_feat_sxpoe_supported()) { - write_ctx_reg(el2_sysregs_ctx, CTX_POR_EL2, read_por_el2()); + write_el2_ctx_sxpoe(el2_sysregs_ctx, por_el2, read_por_el2()); } + + if (is_feat_s2pie_supported()) { + write_el2_ctx_s2pie(el2_sysregs_ctx, s2pir_el2, read_s2pir_el2()); + } + if (is_feat_gcs_supported()) { - write_ctx_reg(el2_sysregs_ctx, CTX_GCSPR_EL2, read_gcspr_el2()); - write_ctx_reg(el2_sysregs_ctx, CTX_GCSCR_EL2, read_gcscr_el2()); + write_el2_ctx_gcs(el2_sysregs_ctx, gcscr_el2, read_gcscr_el2()); + write_el2_ctx_gcs(el2_sysregs_ctx, gcspr_el2, read_gcspr_el2()); + } + + if (is_feat_sctlr2_supported()) { + write_el2_ctx_sctlr2(el2_sysregs_ctx, sctlr2_el2, read_sctlr2_el2()); } } @@ -1303,9 +1495,12 @@ void cm_el2_sysregs_context_restore(uint32_t security_state) el2_sysregs_ctx = get_el2_sysregs_ctx(ctx); el2_sysregs_context_restore_common(el2_sysregs_ctx); -#if CTX_INCLUDE_MTE_REGS - write_tfsr_el2(read_ctx_reg(el2_sysregs_ctx, CTX_TFSR_EL2)); -#endif + el2_sysregs_context_restore_gic(el2_sysregs_ctx); + + if (is_feat_mte2_supported()) { + write_tfsr_el2(read_el2_ctx_mte2(el2_sysregs_ctx, tfsr_el2)); + } + if (is_feat_mpam_supported()) { el2_sysregs_context_restore_mpam(el2_sysregs_ctx); } @@ -1314,53 +1509,117 @@ void cm_el2_sysregs_context_restore(uint32_t security_state) el2_sysregs_context_restore_fgt(el2_sysregs_ctx); } + if (is_feat_fgt2_supported()) { + el2_sysregs_context_restore_fgt2(el2_sysregs_ctx); + } + if (is_feat_ecv_v2_supported()) { - write_cntpoff_el2(read_ctx_reg(el2_sysregs_ctx, CTX_CNTPOFF_EL2)); + write_cntpoff_el2(read_el2_ctx_ecv(el2_sysregs_ctx, cntpoff_el2)); } if (is_feat_vhe_supported()) { - write_contextidr_el2(read_ctx_reg(el2_sysregs_ctx, CTX_CONTEXTIDR_EL2)); - write_ttbr1_el2(read_ctx_reg(el2_sysregs_ctx, CTX_TTBR1_EL2)); + write_contextidr_el2(read_el2_ctx_vhe(el2_sysregs_ctx, + contextidr_el2)); + write_ttbr1_el2(read_el2_ctx_vhe(el2_sysregs_ctx, ttbr1_el2)); } if (is_feat_ras_supported()) { - write_vdisr_el2(read_ctx_reg(el2_sysregs_ctx, CTX_VDISR_EL2)); - write_vsesr_el2(read_ctx_reg(el2_sysregs_ctx, CTX_VSESR_EL2)); + write_vdisr_el2(read_el2_ctx_ras(el2_sysregs_ctx, vdisr_el2)); + write_vsesr_el2(read_el2_ctx_ras(el2_sysregs_ctx, vsesr_el2)); } if (is_feat_nv2_supported()) { - write_vncr_el2(read_ctx_reg(el2_sysregs_ctx, CTX_VNCR_EL2)); + write_vncr_el2(read_el2_ctx_neve(el2_sysregs_ctx, vncr_el2)); } + if (is_feat_trf_supported()) { - write_trfcr_el2(read_ctx_reg(el2_sysregs_ctx, CTX_TRFCR_EL2)); + write_trfcr_el2(read_el2_ctx_trf(el2_sysregs_ctx, trfcr_el2)); } if (is_feat_csv2_2_supported()) { - write_scxtnum_el2(read_ctx_reg(el2_sysregs_ctx, CTX_SCXTNUM_EL2)); + write_scxtnum_el2(read_el2_ctx_csv2_2(el2_sysregs_ctx, + scxtnum_el2)); } if (is_feat_hcx_supported()) { - write_hcrx_el2(read_ctx_reg(el2_sysregs_ctx, CTX_HCRX_EL2)); + write_hcrx_el2(read_el2_ctx_hcx(el2_sysregs_ctx, hcrx_el2)); } + if (is_feat_tcr2_supported()) { - write_tcr2_el2(read_ctx_reg(el2_sysregs_ctx, CTX_TCR2_EL2)); + write_tcr2_el2(read_el2_ctx_tcr2(el2_sysregs_ctx, tcr2_el2)); } + if (is_feat_sxpie_supported()) { - write_pire0_el2(read_ctx_reg(el2_sysregs_ctx, CTX_PIRE0_EL2)); - write_pir_el2(read_ctx_reg(el2_sysregs_ctx, CTX_PIR_EL2)); - } - if (is_feat_s2pie_supported()) { - write_s2pir_el2(read_ctx_reg(el2_sysregs_ctx, CTX_S2PIR_EL2)); + write_pire0_el2(read_el2_ctx_sxpie(el2_sysregs_ctx, pire0_el2)); + write_pir_el2(read_el2_ctx_sxpie(el2_sysregs_ctx, pir_el2)); } + if (is_feat_sxpoe_supported()) { - write_por_el2(read_ctx_reg(el2_sysregs_ctx, CTX_POR_EL2)); + write_por_el2(read_el2_ctx_sxpoe(el2_sysregs_ctx, por_el2)); } + + if (is_feat_s2pie_supported()) { + write_s2pir_el2(read_el2_ctx_s2pie(el2_sysregs_ctx, s2pir_el2)); + } + if (is_feat_gcs_supported()) { - write_gcscr_el2(read_ctx_reg(el2_sysregs_ctx, CTX_GCSCR_EL2)); - write_gcspr_el2(read_ctx_reg(el2_sysregs_ctx, CTX_GCSPR_EL2)); + write_gcscr_el2(read_el2_ctx_gcs(el2_sysregs_ctx, gcscr_el2)); + write_gcspr_el2(read_el2_ctx_gcs(el2_sysregs_ctx, gcspr_el2)); + } + + if (is_feat_sctlr2_supported()) { + write_sctlr2_el2(read_el2_ctx_sctlr2(el2_sysregs_ctx, sctlr2_el2)); } } -#endif /* CTX_INCLUDE_EL2_REGS */ +#endif /* (CTX_INCLUDE_EL2_REGS && IMAGE_BL31) */ + +#if IMAGE_BL31 +/********************************************************************************* +* This function allows Architecture features asymmetry among cores. +* TF-A assumes that all the cores in the platform has architecture feature parity +* and hence the context is setup on different core (e.g. primary sets up the +* context for secondary cores).This assumption may not be true for systems where +* cores are not conforming to same Arch version or there is CPU Erratum which +* requires certain feature to be be disabled only on a given core. +* +* This function is called on secondary cores to override any disparity in context +* setup by primary, this would be called during warmboot path. +*********************************************************************************/ +void cm_handle_asymmetric_features(void) +{ + cpu_context_t *ctx __maybe_unused = cm_get_context(NON_SECURE); + + assert(ctx != NULL); + +#if ENABLE_SPE_FOR_NS == FEAT_STATE_CHECK_ASYMMETRIC + if (is_feat_spe_supported()) { + spe_enable(ctx); + } else { + spe_disable(ctx); + } +#endif + +#if ERRATA_A520_2938996 || ERRATA_X4_2726228 + if (check_if_affected_core() == ERRATA_APPLIES) { + if (is_feat_trbe_supported()) { + trbe_disable(ctx); + } + } +#endif + +#if ENABLE_FEAT_TCR2 == FEAT_STATE_CHECK_ASYMMETRIC + el3_state_t *el3_state = get_el3state_ctx(ctx); + u_register_t spsr = read_ctx_reg(el3_state, CTX_SPSR_EL3); + + if (is_feat_tcr2_supported() && (GET_RW(spsr) == MODE_RW_64)) { + tcr2_enable(ctx); + } else { + tcr2_disable(ctx); + } +#endif + +} +#endif /******************************************************************************* * This function is used to exit to Non-secure world. If CTX_INCLUDE_EL2_REGS @@ -1370,7 +1629,19 @@ void cm_el2_sysregs_context_restore(uint32_t security_state) ******************************************************************************/ void cm_prepare_el3_exit_ns(void) { -#if CTX_INCLUDE_EL2_REGS +#if IMAGE_BL31 + /* + * Check and handle Architecture feature asymmetry among cores. + * + * In warmboot path secondary cores context is initialized on core which + * did CPU_ON SMC call, if there is feature asymmetry in these cores handle + * it in this function call. + * For Symmetric cores this is an empty function. + */ + cm_handle_asymmetric_features(); +#endif + +#if (CTX_INCLUDE_EL2_REGS && IMAGE_BL31) #if ENABLE_ASSERTIONS cpu_context_t *ctx = cm_get_context(NON_SECURE); assert(ctx != NULL); @@ -1381,19 +1652,238 @@ void cm_prepare_el3_exit_ns(void) (el_implemented(2U) != EL_IMPL_NONE)); #endif /* ENABLE_ASSERTIONS */ - /* Restore EL2 and EL1 sysreg contexts */ + /* Restore EL2 sysreg contexts */ cm_el2_sysregs_context_restore(NON_SECURE); - cm_el1_sysregs_context_restore(NON_SECURE); cm_set_next_eret_context(NON_SECURE); #else cm_prepare_el3_exit(NON_SECURE); -#endif /* CTX_INCLUDE_EL2_REGS */ +#endif /* (CTX_INCLUDE_EL2_REGS && IMAGE_BL31) */ } +#if ((IMAGE_BL1) || (IMAGE_BL31 && (!CTX_INCLUDE_EL2_REGS))) /******************************************************************************* - * The next four functions are used by runtime services to save and restore - * EL1 context on the 'cpu_context' structure for the specified security - * state. + * The next set of six functions are used by runtime services to save and restore + * EL1 context on the 'cpu_context' structure for the specified security state. + ******************************************************************************/ +static void el1_sysregs_context_save(el1_sysregs_t *ctx) +{ + write_el1_ctx_common(ctx, spsr_el1, read_spsr_el1()); + write_el1_ctx_common(ctx, elr_el1, read_elr_el1()); + +#if (!ERRATA_SPECULATIVE_AT) + write_el1_ctx_common(ctx, sctlr_el1, read_sctlr_el1()); + write_el1_ctx_common(ctx, tcr_el1, read_tcr_el1()); +#endif /* (!ERRATA_SPECULATIVE_AT) */ + + write_el1_ctx_common(ctx, cpacr_el1, read_cpacr_el1()); + write_el1_ctx_common(ctx, csselr_el1, read_csselr_el1()); + write_el1_ctx_common(ctx, sp_el1, read_sp_el1()); + write_el1_ctx_common(ctx, esr_el1, read_esr_el1()); + write_el1_ctx_common(ctx, ttbr0_el1, read_ttbr0_el1()); + write_el1_ctx_common(ctx, ttbr1_el1, read_ttbr1_el1()); + write_el1_ctx_common(ctx, mair_el1, read_mair_el1()); + write_el1_ctx_common(ctx, amair_el1, read_amair_el1()); + write_el1_ctx_common(ctx, actlr_el1, read_actlr_el1()); + write_el1_ctx_common(ctx, tpidr_el1, read_tpidr_el1()); + write_el1_ctx_common(ctx, tpidr_el0, read_tpidr_el0()); + write_el1_ctx_common(ctx, tpidrro_el0, read_tpidrro_el0()); + write_el1_ctx_common(ctx, par_el1, read_par_el1()); + write_el1_ctx_common(ctx, far_el1, read_far_el1()); + write_el1_ctx_common(ctx, afsr0_el1, read_afsr0_el1()); + write_el1_ctx_common(ctx, afsr1_el1, read_afsr1_el1()); + write_el1_ctx_common(ctx, contextidr_el1, read_contextidr_el1()); + write_el1_ctx_common(ctx, vbar_el1, read_vbar_el1()); + write_el1_ctx_common(ctx, mdccint_el1, read_mdccint_el1()); + write_el1_ctx_common(ctx, mdscr_el1, read_mdscr_el1()); + + if (CTX_INCLUDE_AARCH32_REGS) { + /* Save Aarch32 registers */ + write_el1_ctx_aarch32(ctx, spsr_abt, read_spsr_abt()); + write_el1_ctx_aarch32(ctx, spsr_und, read_spsr_und()); + write_el1_ctx_aarch32(ctx, spsr_irq, read_spsr_irq()); + write_el1_ctx_aarch32(ctx, spsr_fiq, read_spsr_fiq()); + write_el1_ctx_aarch32(ctx, dacr32_el2, read_dacr32_el2()); + write_el1_ctx_aarch32(ctx, ifsr32_el2, read_ifsr32_el2()); + } + + if (NS_TIMER_SWITCH) { + /* Save NS Timer registers */ + write_el1_ctx_arch_timer(ctx, cntp_ctl_el0, read_cntp_ctl_el0()); + write_el1_ctx_arch_timer(ctx, cntp_cval_el0, read_cntp_cval_el0()); + write_el1_ctx_arch_timer(ctx, cntv_ctl_el0, read_cntv_ctl_el0()); + write_el1_ctx_arch_timer(ctx, cntv_cval_el0, read_cntv_cval_el0()); + write_el1_ctx_arch_timer(ctx, cntkctl_el1, read_cntkctl_el1()); + } + + if (is_feat_mte2_supported()) { + write_el1_ctx_mte2(ctx, tfsre0_el1, read_tfsre0_el1()); + write_el1_ctx_mte2(ctx, tfsr_el1, read_tfsr_el1()); + write_el1_ctx_mte2(ctx, rgsr_el1, read_rgsr_el1()); + write_el1_ctx_mte2(ctx, gcr_el1, read_gcr_el1()); + } + + if (is_feat_ras_supported()) { + write_el1_ctx_ras(ctx, disr_el1, read_disr_el1()); + } + + if (is_feat_s1pie_supported()) { + write_el1_ctx_s1pie(ctx, pire0_el1, read_pire0_el1()); + write_el1_ctx_s1pie(ctx, pir_el1, read_pir_el1()); + } + + if (is_feat_s1poe_supported()) { + write_el1_ctx_s1poe(ctx, por_el1, read_por_el1()); + } + + if (is_feat_s2poe_supported()) { + write_el1_ctx_s2poe(ctx, s2por_el1, read_s2por_el1()); + } + + if (is_feat_tcr2_supported()) { + write_el1_ctx_tcr2(ctx, tcr2_el1, read_tcr2_el1()); + } + + if (is_feat_trf_supported()) { + write_el1_ctx_trf(ctx, trfcr_el1, read_trfcr_el1()); + } + + if (is_feat_csv2_2_supported()) { + write_el1_ctx_csv2_2(ctx, scxtnum_el0, read_scxtnum_el0()); + write_el1_ctx_csv2_2(ctx, scxtnum_el1, read_scxtnum_el1()); + } + + if (is_feat_gcs_supported()) { + write_el1_ctx_gcs(ctx, gcscr_el1, read_gcscr_el1()); + write_el1_ctx_gcs(ctx, gcscre0_el1, read_gcscre0_el1()); + write_el1_ctx_gcs(ctx, gcspr_el1, read_gcspr_el1()); + write_el1_ctx_gcs(ctx, gcspr_el0, read_gcspr_el0()); + } + + if (is_feat_the_supported()) { + write_el1_ctx_the(ctx, rcwmask_el1, read_rcwmask_el1()); + write_el1_ctx_the(ctx, rcwsmask_el1, read_rcwsmask_el1()); + } + + if (is_feat_sctlr2_supported()) { + write_el1_ctx_sctlr2(ctx, sctlr2_el1, read_sctlr2_el1()); + } + + if (is_feat_ls64_accdata_supported()) { + write_el1_ctx_ls64(ctx, accdata_el1, read_accdata_el1()); + } +} + +static void el1_sysregs_context_restore(el1_sysregs_t *ctx) +{ + write_spsr_el1(read_el1_ctx_common(ctx, spsr_el1)); + write_elr_el1(read_el1_ctx_common(ctx, elr_el1)); + +#if (!ERRATA_SPECULATIVE_AT) + write_sctlr_el1(read_el1_ctx_common(ctx, sctlr_el1)); + write_tcr_el1(read_el1_ctx_common(ctx, tcr_el1)); +#endif /* (!ERRATA_SPECULATIVE_AT) */ + + write_cpacr_el1(read_el1_ctx_common(ctx, cpacr_el1)); + write_csselr_el1(read_el1_ctx_common(ctx, csselr_el1)); + write_sp_el1(read_el1_ctx_common(ctx, sp_el1)); + write_esr_el1(read_el1_ctx_common(ctx, esr_el1)); + write_ttbr0_el1(read_el1_ctx_common(ctx, ttbr0_el1)); + write_ttbr1_el1(read_el1_ctx_common(ctx, ttbr1_el1)); + write_mair_el1(read_el1_ctx_common(ctx, mair_el1)); + write_amair_el1(read_el1_ctx_common(ctx, amair_el1)); + write_actlr_el1(read_el1_ctx_common(ctx, actlr_el1)); + write_tpidr_el1(read_el1_ctx_common(ctx, tpidr_el1)); + write_tpidr_el0(read_el1_ctx_common(ctx, tpidr_el0)); + write_tpidrro_el0(read_el1_ctx_common(ctx, tpidrro_el0)); + write_par_el1(read_el1_ctx_common(ctx, par_el1)); + write_far_el1(read_el1_ctx_common(ctx, far_el1)); + write_afsr0_el1(read_el1_ctx_common(ctx, afsr0_el1)); + write_afsr1_el1(read_el1_ctx_common(ctx, afsr1_el1)); + write_contextidr_el1(read_el1_ctx_common(ctx, contextidr_el1)); + write_vbar_el1(read_el1_ctx_common(ctx, vbar_el1)); + write_mdccint_el1(read_el1_ctx_common(ctx, mdccint_el1)); + write_mdscr_el1(read_el1_ctx_common(ctx, mdscr_el1)); + + if (CTX_INCLUDE_AARCH32_REGS) { + /* Restore Aarch32 registers */ + write_spsr_abt(read_el1_ctx_aarch32(ctx, spsr_abt)); + write_spsr_und(read_el1_ctx_aarch32(ctx, spsr_und)); + write_spsr_irq(read_el1_ctx_aarch32(ctx, spsr_irq)); + write_spsr_fiq(read_el1_ctx_aarch32(ctx, spsr_fiq)); + write_dacr32_el2(read_el1_ctx_aarch32(ctx, dacr32_el2)); + write_ifsr32_el2(read_el1_ctx_aarch32(ctx, ifsr32_el2)); + } + + if (NS_TIMER_SWITCH) { + /* Restore NS Timer registers */ + write_cntp_ctl_el0(read_el1_ctx_arch_timer(ctx, cntp_ctl_el0)); + write_cntp_cval_el0(read_el1_ctx_arch_timer(ctx, cntp_cval_el0)); + write_cntv_ctl_el0(read_el1_ctx_arch_timer(ctx, cntv_ctl_el0)); + write_cntv_cval_el0(read_el1_ctx_arch_timer(ctx, cntv_cval_el0)); + write_cntkctl_el1(read_el1_ctx_arch_timer(ctx, cntkctl_el1)); + } + + if (is_feat_mte2_supported()) { + write_tfsre0_el1(read_el1_ctx_mte2(ctx, tfsre0_el1)); + write_tfsr_el1(read_el1_ctx_mte2(ctx, tfsr_el1)); + write_rgsr_el1(read_el1_ctx_mte2(ctx, rgsr_el1)); + write_gcr_el1(read_el1_ctx_mte2(ctx, gcr_el1)); + } + + if (is_feat_ras_supported()) { + write_disr_el1(read_el1_ctx_ras(ctx, disr_el1)); + } + + if (is_feat_s1pie_supported()) { + write_pire0_el1(read_el1_ctx_s1pie(ctx, pire0_el1)); + write_pir_el1(read_el1_ctx_s1pie(ctx, pir_el1)); + } + + if (is_feat_s1poe_supported()) { + write_por_el1(read_el1_ctx_s1poe(ctx, por_el1)); + } + + if (is_feat_s2poe_supported()) { + write_s2por_el1(read_el1_ctx_s2poe(ctx, s2por_el1)); + } + + if (is_feat_tcr2_supported()) { + write_tcr2_el1(read_el1_ctx_tcr2(ctx, tcr2_el1)); + } + + if (is_feat_trf_supported()) { + write_trfcr_el1(read_el1_ctx_trf(ctx, trfcr_el1)); + } + + if (is_feat_csv2_2_supported()) { + write_scxtnum_el0(read_el1_ctx_csv2_2(ctx, scxtnum_el0)); + write_scxtnum_el1(read_el1_ctx_csv2_2(ctx, scxtnum_el1)); + } + + if (is_feat_gcs_supported()) { + write_gcscr_el1(read_el1_ctx_gcs(ctx, gcscr_el1)); + write_gcscre0_el1(read_el1_ctx_gcs(ctx, gcscre0_el1)); + write_gcspr_el1(read_el1_ctx_gcs(ctx, gcspr_el1)); + write_gcspr_el0(read_el1_ctx_gcs(ctx, gcspr_el0)); + } + + if (is_feat_the_supported()) { + write_rcwmask_el1(read_el1_ctx_the(ctx, rcwmask_el1)); + write_rcwsmask_el1(read_el1_ctx_the(ctx, rcwsmask_el1)); + } + + if (is_feat_sctlr2_supported()) { + write_sctlr2_el1(read_el1_ctx_sctlr2(ctx, sctlr2_el1)); + } + + if (is_feat_ls64_accdata_supported()) { + write_accdata_el1(read_el1_ctx_ls64(ctx, accdata_el1)); + } +} + +/******************************************************************************* + * The next couple of functions are used by runtime services to save and restore + * EL1 context on the 'cpu_context' structure for the specified security state. ******************************************************************************/ void cm_el1_sysregs_context_save(uint32_t security_state) { @@ -1429,6 +1919,8 @@ void cm_el1_sysregs_context_restore(uint32_t security_state) #endif } +#endif /* ((IMAGE_BL1) || (IMAGE_BL31 && (!CTX_INCLUDE_EL2_REGS))) */ + /******************************************************************************* * This function populates ELR_EL3 member of 'cpu_context' pertaining to the * given security state with the given entrypoint diff --git a/lib/el3_runtime/simd_ctx.c b/lib/el3_runtime/simd_ctx.c new file mode 100644 index 00000000..f7a87dfe --- /dev/null +++ b/lib/el3_runtime/simd_ctx.c @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2024, Arm Limited and Contributors. All rights reserved. + * Copyright (c) 2022, Google LLC. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <stdint.h> + +#include <common/debug.h> +#include <lib/el3_runtime/aarch64/context.h> +#include <lib/el3_runtime/context_mgmt.h> +#include <lib/el3_runtime/cpu_data.h> +#include <lib/el3_runtime/simd_ctx.h> +#include <lib/extensions/sve.h> +#include <plat/common/platform.h> + +#if CTX_INCLUDE_FPREGS || CTX_INCLUDE_SVE_REGS + +/* SIMD context managed for Secure and Normal Worlds. */ +#define SIMD_CTXT_COUNT 2 + +#if SEPARATE_SIMD_SECTION +__section(".simd_context") +#else +__section(".bss.simd_context") +#endif +static simd_regs_t simd_context[SIMD_CTXT_COUNT][PLATFORM_CORE_COUNT]; + +void simd_ctx_save(uint32_t security_state, bool hint_sve) +{ + simd_regs_t *regs; + + if (security_state != NON_SECURE && security_state != SECURE) { + ERROR("Unsupported security state specified for SIMD context: %u\n", + security_state); + panic(); + } + + regs = &simd_context[security_state][plat_my_core_pos()]; + +#if CTX_INCLUDE_SVE_REGS + regs->hint = hint_sve; + + if (hint_sve) { + /* + * Hint bit denoting absence of SVE live state. Hence, only + * save FP context. + */ + fpregs_context_save(regs); + } else { + sve_context_save(regs); + } +#elif CTX_INCLUDE_FPREGS + fpregs_context_save(regs); +#endif +} + +void simd_ctx_restore(uint32_t security_state) +{ + simd_regs_t *regs; + + if (security_state != NON_SECURE && security_state != SECURE) { + ERROR("Unsupported security state specified for SIMD context: %u\n", + security_state); + panic(); + } + + regs = &simd_context[security_state][plat_my_core_pos()]; + +#if CTX_INCLUDE_SVE_REGS + if (regs->hint) { + fpregs_context_restore(regs); + } else { + sve_context_restore(regs); + } +#elif CTX_INCLUDE_FPREGS + fpregs_context_restore(regs); +#endif +} +#endif /* CTX_INCLUDE_FPREGS || CTX_INCLUDE_SVE_REGS */ diff --git a/lib/extensions/brbe/brbe.c b/lib/extensions/brbe/brbe.c index 37bd8349..fef66478 100644 --- a/lib/extensions/brbe/brbe.c +++ b/lib/extensions/brbe/brbe.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022-2023, Arm Limited. All rights reserved. + * Copyright (c) 2022-2024, Arm Limited. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -9,18 +9,20 @@ #include <arch_helpers.h> #include <lib/extensions/brbe.h> -void brbe_init_el3(void) +void brbe_enable(cpu_context_t *ctx) { - uint64_t val; + el3_state_t *state = get_el3state_ctx(ctx); + u_register_t mdcr_el3_val = read_ctx_reg(state, CTX_MDCR_EL3); /* * MDCR_EL3.SBRBE = 0b01 - * * Allows BRBE usage in non-secure world and prohibited in * secure world. + * + * MDCR_EL3.{E3BREW, E3BREC} = 0b00 + * Branch recording at EL3 is disabled */ - val = read_mdcr_el3(); - val &= ~(MDCR_SBRBE_MASK << MDCR_SBRBE_SHIFT); - val |= (0x1UL << MDCR_SBRBE_SHIFT); - write_mdcr_el3(val); + mdcr_el3_val &= ~((MDCR_SBRBE_MASK << MDCR_SBRBE_SHIFT) | MDCR_E3BREW | MDCR_E3BREC); + mdcr_el3_val |= (0x1UL << MDCR_SBRBE_SHIFT); + write_ctx_reg(state, CTX_MDCR_EL3, mdcr_el3_val); } diff --git a/lib/extensions/debug/debugv8p9.c b/lib/extensions/debug/debugv8p9.c new file mode 100644 index 00000000..f3cffa73 --- /dev/null +++ b/lib/extensions/debug/debugv8p9.c @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2024, Arm Limited. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <arch.h> +#include <arch_features.h> +#include <arch_helpers.h> +#include <lib/extensions/debug_v8p9.h> + +void debugv8p9_extended_bp_wp_enable(cpu_context_t *ctx) +{ + el3_state_t *state = get_el3state_ctx(ctx); + u_register_t mdcr_el3_val = read_ctx_reg(state, CTX_MDCR_EL3); + + /* When FEAT_Debugv8p9 is implemented: + * + * MDCR_EL3.EBWE: Set to 0b1 + * Enables use of additional breakpoints or watchpoints, + * and disables trap to EL3 on accesses to debug register. + */ + + mdcr_el3_val |= MDCR_EBWE_BIT; + write_ctx_reg(state, CTX_MDCR_EL3, mdcr_el3_val); +} diff --git a/lib/extensions/fgt/fgt2.c b/lib/extensions/fgt/fgt2.c new file mode 100644 index 00000000..78f1a822 --- /dev/null +++ b/lib/extensions/fgt/fgt2.c @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2024, Arm Limited. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <arch.h> +#include <arch_features.h> +#include <arch_helpers.h> +#include <lib/extensions/fgt2.h> + +void fgt2_enable(cpu_context_t *context) +{ + u_register_t reg; + el3_state_t *state; + + state = get_el3state_ctx(context); + + /* Set the FGTEN2 bit in SCR_EL3 to enable access to HFGITR2_EL2, + * HFGRTR2_EL2, HFGWTR_EL2, HDFGRTR2_EL2, and HDFGWTR2_EL2. + */ + + reg = read_ctx_reg(state, CTX_SCR_EL3); + reg |= SCR_FGTEN2_BIT; + write_ctx_reg(state, CTX_SCR_EL3, reg); +} + diff --git a/lib/extensions/mpam/mpam.c b/lib/extensions/mpam/mpam.c index 875ad9c2..5285b96d 100644 --- a/lib/extensions/mpam/mpam.c +++ b/lib/extensions/mpam/mpam.c @@ -11,19 +11,19 @@ #include <arch_helpers.h> #include <lib/extensions/mpam.h> -void mpam_enable(cpu_context_t *context) +void mpam_enable_per_world(per_world_context_t *per_world_ctx) { u_register_t mpam3_el3; - mpam3_el3 = read_ctx_reg(get_el3state_ctx(context), CTX_MPAM3_EL3); - /* * Enable MPAM, and disable trapping to EL3 when lower ELs access their * own MPAM registers */ + mpam3_el3 = per_world_ctx->ctx_mpam3_el3; mpam3_el3 = (mpam3_el3 | MPAM3_EL3_MPAMEN_BIT) & ~(MPAM3_EL3_TRAPLOWER_BIT); - write_ctx_reg(get_el3state_ctx(context), CTX_MPAM3_EL3, mpam3_el3); + + per_world_ctx->ctx_mpam3_el3 = mpam3_el3; } /* diff --git a/lib/extensions/pmuv3/aarch32/pmuv3.c b/lib/extensions/pmuv3/aarch32/pmuv3.c index effb7e02..456a48ef 100644 --- a/lib/extensions/pmuv3/aarch32/pmuv3.c +++ b/lib/extensions/pmuv3/aarch32/pmuv3.c @@ -25,10 +25,6 @@ static u_register_t mtpmu_disable_el3(u_register_t sdcr) return sdcr; } -/* - * Applies to all PMU versions. Name is PMUv3 for compatibility with aarch64 and - * to not clash with platforms which reuse the PMU name - */ void pmuv3_init_el3(void) { u_register_t sdcr = read_sdcr(); diff --git a/lib/extensions/pmuv3/aarch64/pmuv3.c b/lib/extensions/pmuv3/aarch64/pmuv3.c index 61fc47dc..f9e32cad 100644 --- a/lib/extensions/pmuv3/aarch64/pmuv3.c +++ b/lib/extensions/pmuv3/aarch64/pmuv3.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023, Arm Limited. All rights reserved. + * Copyright (c) 2023-2024, Arm Limited. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -23,13 +23,13 @@ static u_register_t init_mdcr_el2_hpmn(u_register_t mdcr_el2) void pmuv3_enable(cpu_context_t *ctx) { -#if CTX_INCLUDE_EL2_REGS - u_register_t mdcr_el2; +#if (CTX_INCLUDE_EL2_REGS && IMAGE_BL31) + u_register_t mdcr_el2_val; - mdcr_el2 = read_ctx_reg(get_el2_sysregs_ctx(ctx), CTX_MDCR_EL2); - mdcr_el2 = init_mdcr_el2_hpmn(mdcr_el2); - write_ctx_reg(get_el2_sysregs_ctx(ctx), CTX_MDCR_EL2, mdcr_el2); -#endif /* CTX_INCLUDE_EL2_REGS */ + mdcr_el2_val = read_el2_ctx_common(get_el2_sysregs_ctx(ctx), mdcr_el2); + mdcr_el2_val = init_mdcr_el2_hpmn(mdcr_el2_val); + write_el2_ctx_common(get_el2_sysregs_ctx(ctx), mdcr_el2, mdcr_el2_val); +#endif /* (CTX_INCLUDE_EL2_REGS && IMAGE_BL31) */ } static u_register_t mtpmu_disable_el3(u_register_t mdcr_el3) diff --git a/lib/extensions/sme/sme.c b/lib/extensions/sme/sme.c index b1409b95..98d57e92 100644 --- a/lib/extensions/sme/sme.c +++ b/lib/extensions/sme/sme.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021-2023, Arm Limited and Contributors. All rights reserved. + * Copyright (c) 2021-2024, Arm Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -53,7 +53,7 @@ void sme_init_el3(void) * using SMCR_EL2 and SMCR_EL1. */ smcr_el3 = SMCR_ELX_LEN_MAX; - if (read_feat_sme_fa64_id_field() != 0U) { + if (is_feat_sme_fa64_present()) { VERBOSE("[SME] FA64 enabled\n"); smcr_el3 |= SMCR_ELX_FA64_BIT; } diff --git a/lib/extensions/spe/spe.c b/lib/extensions/spe/spe.c index 2c25a9db..d6532224 100644 --- a/lib/extensions/spe/spe.c +++ b/lib/extensions/spe/spe.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2023, Arm Limited and Contributors. All rights reserved. + * Copyright (c) 2017-2024, Arm Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -12,6 +12,14 @@ #include <lib/el3_runtime/pubsub.h> #include <lib/extensions/spe.h> +#include <plat/common/platform.h> + +typedef struct spe_ctx { + u_register_t pmblimitr_el1; +} spe_ctx_t; + +static struct spe_ctx spe_ctxs[PLATFORM_CORE_COUNT]; + static inline void psb_csync(void) { /* @@ -21,9 +29,10 @@ static inline void psb_csync(void) __asm__ volatile("hint #17"); } -void spe_init_el3(void) +void spe_enable(cpu_context_t *ctx) { - uint64_t v; + el3_state_t *state = get_el3state_ctx(ctx); + u_register_t mdcr_el3_val = read_ctx_reg(state, CTX_MDCR_EL3); /* * MDCR_EL3.NSPB (ARM v8.2): SPE enabled in Non-secure state @@ -38,10 +47,30 @@ void spe_init_el3(void) * Setting this bit to 1 doesn't have any effect on it when * FEAT_SPEv1p2 not implemented. */ - v = read_mdcr_el3(); - v |= MDCR_NSPB(MDCR_NSPB_EL1) | MDCR_EnPMSN_BIT; - v &= ~(MDCR_NSPBE_BIT); - write_mdcr_el3(v); + mdcr_el3_val |= MDCR_NSPB(MDCR_NSPB_EL1) | MDCR_EnPMSN_BIT; + mdcr_el3_val &= ~(MDCR_NSPBE_BIT); + write_ctx_reg(state, CTX_MDCR_EL3, mdcr_el3_val); +} + +void spe_disable(cpu_context_t *ctx) +{ + el3_state_t *state = get_el3state_ctx(ctx); + u_register_t mdcr_el3_val = read_ctx_reg(state, CTX_MDCR_EL3); + + /* + * MDCR_EL3.NSPB: Clear these bits to disable SPE feature, as it was enabled + * for Non-secure state only. After clearing these bits Secure state owns + * the Profiling Buffer and accesses to Statistical Profiling and Profiling + * Buffer control registers at EL2 and EL1 generate Trap exceptions to EL3 + * + * MDCR_EL3.NSPBE: Don't care as it was cleared during spe_enable and setting + * this to 1 does not make sense as NSPBE{1} and NSPB{0b0x} is RESERVED. + * + * MDCR_EL3.EnPMSN (ARM v8.7): Clear the bit to trap access of PMSNEVFR_EL1 + * from EL2/EL1 to EL3. + */ + mdcr_el3_val &= ~(MDCR_NSPB(MDCR_NSPB_EL1) | MDCR_EnPMSN_BIT); + write_ctx_reg(state, CTX_MDCR_EL3, mdcr_el3_val); } void spe_init_el2_unused(void) @@ -62,7 +91,7 @@ void spe_init_el2_unused(void) write_mdcr_el2(v); } -void spe_disable(void) +void spe_stop(void) { uint64_t v; @@ -89,4 +118,35 @@ static void *spe_drain_buffers_hook(const void *arg) return (void *)0; } +static void *spe_context_save(const void *arg) +{ + unsigned int core_pos; + struct spe_ctx *ctx; + + if (is_feat_spe_supported()) { + core_pos = plat_my_core_pos(); + ctx = &spe_ctxs[core_pos]; + ctx->pmblimitr_el1 = read_pmblimitr_el1(); + } + + return NULL; +} + +static void *spe_context_restore(const void *arg) +{ + unsigned int core_pos; + struct spe_ctx *ctx; + + if (is_feat_spe_supported()) { + core_pos = plat_my_core_pos(); + ctx = &spe_ctxs[core_pos]; + write_pmblimitr_el1(ctx->pmblimitr_el1); + } + + return NULL; +} + SUBSCRIBE_TO_EVENT(cm_entering_secure_world, spe_drain_buffers_hook); + +SUBSCRIBE_TO_EVENT(psci_suspend_pwrdown_start, spe_context_save); +SUBSCRIBE_TO_EVENT(psci_suspend_pwrdown_finish, spe_context_restore); diff --git a/lib/extensions/sysreg128/sysreg128.S b/lib/extensions/sysreg128/sysreg128.S new file mode 100644 index 00000000..08cff2fb --- /dev/null +++ b/lib/extensions/sysreg128/sysreg128.S @@ -0,0 +1,139 @@ +/* + * Copyright (c) 2024, Arm Limited. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <arch.h> +#include <asm_macros.S> +#include <lib/extensions/sysreg128.h> + + .global read_par_el1 + .global write_par_el1 + .global read_ttbr0_el1 + .global write_ttbr0_el1 + .global read_ttbr1_el1 + .global write_ttbr1_el1 + .global read_ttbr0_el2 + .global write_ttbr0_el2 + .global read_ttbr1_el2 + .global write_ttbr1_el2 + .global read_vttbr_el2 + .global write_vttbr_el2 + .global read_rcwmask_el1 + .global write_rcwmask_el1 + .global read_rcwsmask_el1 + .global write_rcwsmask_el1 + +/* + * _mrrs - Move System register to two adjacent general-purpose + * registers. + * Instruction: MRRS <Xt>, <Xt+1>, (<systemreg>|S<op0>_<op1>_<Cn>_<Cm>_<op2>) + * + * Arguments/Opcode bit field: + * regins: System register opcode. + * + * Clobbers: x0,x1,x2 + */ +.macro _mrrs regins:req +#if ENABLE_FEAT_D128 == 2 + mrs x0, ID_AA64MMFR3_EL1 + tst x0, #(ID_AA64MMFR3_EL1_D128_MASK << ID_AA64MMFR3_EL1_D128_SHIFT) + bne 1f + /* If FEAT_D128 is not implemented then use mrs */ + .inst 0xD5300000 | (\regins) + ret +#endif +1: + .inst 0xD5700000 | (\regins) + ret +.endm + +/* + * _msrr - Move two adjacent general-purpose registers to System register. + * Instruction: MSRR (<systemreg>|S<op0>_<op1>_<Cn>_<Cm>_<op2>), <Xt>, <Xt+1> + * + * Arguments/Opcode bit field: + * regins: System register opcode. + * + * Clobbers: x0,x1,x2 + */ +.macro _msrr regins:req + /* If FEAT_D128 is not implemented use msr, dont tamper + * x0, x1 as they maybe used for mrrs */ +#if ENABLE_FEAT_D128 == 2 + mrs x2, ID_AA64MMFR3_EL1 + tst x2, #(ID_AA64MMFR3_EL1_D128_MASK << ID_AA64MMFR3_EL1_D128_SHIFT) + bne 1f + /* If FEAT_D128 is not implemented then use msr */ + .inst 0xD5100000 | (\regins) + ret +#endif +1: + .inst 0xD5500000 | (\regins) + ret +.endm + +func read_par_el1 + _mrrs 0x87400 /* S3_0_C7_C4_0 */ +endfunc read_par_el1 + +func write_par_el1 + _msrr 0x87400 +endfunc write_par_el1 + +func read_ttbr0_el1 + _mrrs 0x82000 /* S3_0_C2_C0_0 */ +endfunc read_ttbr0_el1 + +func write_ttbr0_el1 + _msrr 0x82000 +endfunc write_ttbr0_el1 + +func read_ttbr1_el1 + _mrrs 0x82020 /* S3_0_C2_C0_1 */ +endfunc read_ttbr1_el1 + +func write_ttbr1_el1 + _msrr 0x82020 +endfunc write_ttbr1_el1 + +func read_ttbr0_el2 + _mrrs 0xC2000 /* S3_4_C2_C0_0 */ +endfunc read_ttbr0_el2 + +func write_ttbr0_el2 + _msrr 0xC2000 +endfunc write_ttbr0_el2 + +func read_ttbr1_el2 + _mrrs 0xC2020 /* S3_4_C2_C0_1 */ +endfunc read_ttbr1_el2 + +func write_ttbr1_el2 + _msrr 0xC2020 +endfunc write_ttbr1_el2 + +func read_vttbr_el2 + _mrrs 0xC2100 /* S3_4_C2_C1_0 */ +endfunc read_vttbr_el2 + +func write_vttbr_el2 + _msrr 0xC2100 +endfunc write_vttbr_el2 + +func read_rcwmask_el1 + _mrrs 0x8D0C0 /* S3_0_C13_C0_6 */ +endfunc read_rcwmask_el1 + +func write_rcwmask_el1 + _msrr 0x8D0C0 +endfunc write_rcwmask_el1 + +func read_rcwsmask_el1 + _mrrs 0x8D060 /* S3_0_C13_C0_3 */ +endfunc read_rcwsmask_el1 + +func write_rcwsmask_el1 + _msrr 0x8D060 +endfunc write_rcwsmask_el1 diff --git a/lib/extensions/tcr/tcr2.c b/lib/extensions/tcr/tcr2.c new file mode 100644 index 00000000..70bc5f8a --- /dev/null +++ b/lib/extensions/tcr/tcr2.c @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2024, Arm Limited. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <arch.h> +#include <arch_features.h> +#include <arch_helpers.h> +#include <lib/extensions/tcr2.h> + +void tcr2_enable(cpu_context_t *ctx) +{ + u_register_t reg; + el3_state_t *state; + + state = get_el3state_ctx(ctx); + + /* Set the TCR2EN bit in SCR_EL3 to enable access to TCR2_EL1, + * and TCR2_EL2 registers . + */ + + reg = read_ctx_reg(state, CTX_SCR_EL3); + reg |= SCR_TCR2EN_BIT; + write_ctx_reg(state, CTX_SCR_EL3, reg); +} + +void tcr2_disable(cpu_context_t *ctx) +{ + u_register_t reg; + el3_state_t *state; + + state = get_el3state_ctx(ctx); + + /* Clear the TCR2EN bit in SCR_EL3 to disable access to TCR2_EL1, + * and TCR2_EL2 registers . + */ + + reg = read_ctx_reg(state, CTX_SCR_EL3); + reg &= ~SCR_TCR2EN_BIT; + write_ctx_reg(state, CTX_SCR_EL3, reg); +} diff --git a/lib/extensions/trbe/trbe.c b/lib/extensions/trbe/trbe.c index d4fbdfbc..8c1c4218 100644 --- a/lib/extensions/trbe/trbe.c +++ b/lib/extensions/trbe/trbe.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021-2023, Arm Limited. All rights reserved. + * Copyright (c) 2021-2024, Arm Limited. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -19,9 +19,10 @@ static void tsb_csync(void) __asm__ volatile("hint #18"); } -void trbe_init_el3(void) +void trbe_enable(cpu_context_t *ctx) { - u_register_t val; + el3_state_t *state = get_el3state_ctx(ctx); + u_register_t mdcr_el3_val = read_ctx_reg(state, CTX_MDCR_EL3); /* * MDCR_EL3.NSTBE = 0b0 @@ -33,10 +34,28 @@ void trbe_init_el3(void) * NS-EL2, tracing is prohibited in Secure and Realm state (if * implemented). */ - val = read_mdcr_el3(); - val |= MDCR_NSTB(MDCR_NSTB_EL1); - val &= ~(MDCR_NSTBE_BIT); - write_mdcr_el3(val); + mdcr_el3_val |= MDCR_NSTB(MDCR_NSTB_EL1); + mdcr_el3_val &= ~(MDCR_NSTBE_BIT); + write_ctx_reg(state, CTX_MDCR_EL3, mdcr_el3_val); +} + +void trbe_disable(cpu_context_t *ctx) +{ + el3_state_t *state = get_el3state_ctx(ctx); + u_register_t mdcr_el3_val = read_ctx_reg(state, CTX_MDCR_EL3); + + /* + * MDCR_EL3.NSTBE = 0b0 + * Trace Buffer owning Security state is secure state. If FEAT_RME + * is not implemented, this field is RES0. + * + * MDCR_EL3.NSTB = 0b00 + * Clear these bits to disable access of trace buffer control registers + * from lower ELs in any security state. + */ + mdcr_el3_val &= ~(MDCR_NSTB(MDCR_NSTB_EL1)); + mdcr_el3_val &= ~(MDCR_NSTBE_BIT); + write_ctx_reg(state, CTX_MDCR_EL3, mdcr_el3_val); } void trbe_init_el2_unused(void) diff --git a/lib/extensions/trf/aarch64/trf.c b/lib/extensions/trf/aarch64/trf.c index 83fbf859..d36853a5 100644 --- a/lib/extensions/trf/aarch64/trf.c +++ b/lib/extensions/trf/aarch64/trf.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021-2023, Arm Limited. All rights reserved. + * Copyright (c) 2021-2024, Arm Limited. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -9,9 +9,10 @@ #include <arch_helpers.h> #include <lib/extensions/trf.h> -void trf_init_el3(void) +void trf_enable(cpu_context_t *ctx) { - u_register_t val; + el3_state_t *state = get_el3state_ctx(ctx); + u_register_t mdcr_el3_val = read_ctx_reg(state, CTX_MDCR_EL3); /* * MDCR_EL3.STE = b0 @@ -22,9 +23,8 @@ void trf_init_el3(void) * Allow access of trace filter control registers from NS-EL2 * and NS-EL1 when NS-EL2 is implemented but not used */ - val = read_mdcr_el3(); - val &= ~(MDCR_STE_BIT | MDCR_TTRF_BIT); - write_mdcr_el3(val); + mdcr_el3_val &= ~(MDCR_STE_BIT | MDCR_TTRF_BIT); + write_ctx_reg(state, CTX_MDCR_EL3, mdcr_el3_val); } void trf_init_el2_unused(void) diff --git a/lib/fconf/fconf_cot_getter.c b/lib/fconf/fconf_cot_getter.c index 1033018d..b9bc9de0 100644 --- a/lib/fconf/fconf_cot_getter.c +++ b/lib/fconf/fconf_cot_getter.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020-2023, Arm Limited. All rights reserved. + * Copyright (c) 2020-2024, Arm Limited. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -237,13 +237,17 @@ static int populate_and_set_auth_methods(const void *dtb, int node, * verified by signature and images are verified by hash. */ if (type == IMG_CERT) { - if (root_certificate) { - oid = NULL; - } else { - rc = get_oid(dtb, node, "signing-key", &oid); - if (rc < 0) { + rc = get_oid(dtb, node, "signing-key", &oid); + if (rc < 0) { + /* + * The signing-key property is optional in root + * certificates, mandatory otherwise. + */ + if (root_certificate) { + oid = NULL; + } else { ERROR("FCONF: Can't read %s property\n", - "signing-key"); + "signing-key"); return rc; } } diff --git a/lib/gpt_rme/gpt_rme.c b/lib/gpt_rme/gpt_rme.c index f5353cb1..79c4ea5d 100644 --- a/lib/gpt_rme/gpt_rme.c +++ b/lib/gpt_rme/gpt_rme.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, Arm Limited. All rights reserved. + * Copyright (c) 2022-2024, Arm Limited. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -11,6 +11,7 @@ #include <stdint.h> #include <arch.h> +#include <arch_features.h> #include <arch_helpers.h> #include <common/debug.h> #include "gpt_rme_private.h" @@ -20,7 +21,7 @@ #include <lib/xlat_tables/xlat_tables_v2.h> #if !ENABLE_RME -#error "ENABLE_RME must be enabled to use the GPT library." +#error "ENABLE_RME must be enabled to use the GPT library" #endif /* @@ -56,8 +57,15 @@ static const gpt_t_val_e gpt_t_lookup[] = {PPS_4GB_T, PPS_64GB_T, */ static const gpt_p_val_e gpt_p_lookup[] = {PGS_4KB_P, PGS_64KB_P, PGS_16KB_P}; +static void shatter_2mb(uintptr_t base, const gpi_info_t *gpi_info, + uint64_t l1_desc); +static void shatter_32mb(uintptr_t base, const gpi_info_t *gpi_info, + uint64_t l1_desc); +static void shatter_512mb(uintptr_t base, const gpi_info_t *gpi_info, + uint64_t l1_desc); + /* - * This structure contains GPT configuration data. + * This structure contains GPT configuration data */ typedef struct { uintptr_t plat_gpt_l0_base; @@ -69,10 +77,187 @@ typedef struct { static gpt_config_t gpt_config; -/* These variables are used during initialization of the L1 tables. */ -static unsigned int gpt_next_l1_tbl_idx; +/* + * Number of L1 entries in 2MB, depending on GPCCR_EL3.PGS: + * +-------+------------+ + * | PGS | L1 entries | + * +-------+------------+ + * | 4KB | 32 | + * +-------+------------+ + * | 16KB | 8 | + * +-------+------------+ + * | 64KB | 2 | + * +-------+------------+ + */ +static unsigned int gpt_l1_cnt_2mb; + +/* + * Mask for the L1 index field, depending on + * GPCCR_EL3.L0GPTSZ and GPCCR_EL3.PGS: + * +---------+-------------------------------+ + * | | PGS | + * +---------+----------+----------+---------+ + * | L0GPTSZ | 4KB | 16KB | 64KB | + * +---------+----------+----------+---------+ + * | 1GB | 0x3FFF | 0xFFF | 0x3FF | + * +---------+----------+----------+---------+ + * | 16GB | 0x3FFFF | 0xFFFF | 0x3FFF | + * +---------+----------+----------+---------+ + * | 64GB | 0xFFFFF | 0x3FFFF | 0xFFFF | + * +---------+----------+----------+---------+ + * | 512GB | 0x7FFFFF | 0x1FFFFF | 0x7FFFF | + * +---------+----------+----------+---------+ + */ +static uint64_t gpt_l1_index_mask; + +/* Number of 128-bit L1 entries in 2MB, 32MB and 512MB */ +#define L1_QWORDS_2MB (gpt_l1_cnt_2mb / 2U) +#define L1_QWORDS_32MB (L1_QWORDS_2MB * 16U) +#define L1_QWORDS_512MB (L1_QWORDS_32MB * 16U) + +/* Size in bytes of L1 entries in 2MB, 32MB */ +#define L1_BYTES_2MB (gpt_l1_cnt_2mb * sizeof(uint64_t)) +#define L1_BYTES_32MB (L1_BYTES_2MB * 16U) + +/* Get the index into the L1 table from a physical address */ +#define GPT_L1_INDEX(_pa) \ + (((_pa) >> (unsigned int)GPT_L1_IDX_SHIFT(gpt_config.p)) & gpt_l1_index_mask) + +/* These variables are used during initialization of the L1 tables */ static uintptr_t gpt_l1_tbl; +/* These variable is used during runtime */ +#if (RME_GPT_BITLOCK_BLOCK == 0) +/* + * The GPTs are protected by a global spinlock to ensure + * that multiple CPUs do not attempt to change the descriptors at once. + */ +static spinlock_t gpt_lock; +#else + +/* Bitlocks base address */ +static bitlock_t *gpt_bitlock_base; +#endif + +/* Lock/unlock macros for GPT entries */ +#if (RME_GPT_BITLOCK_BLOCK == 0) +/* + * Access to GPT is controlled by a global lock to ensure + * that no more than one CPU is allowed to make changes at any + * given time. + */ +#define GPT_LOCK spin_lock(&gpt_lock) +#define GPT_UNLOCK spin_unlock(&gpt_lock) +#else +/* + * Access to a block of memory is controlled by a bitlock. + * Size of block = RME_GPT_BITLOCK_BLOCK * 512MB. + */ +#define GPT_LOCK bit_lock(gpi_info.lock, gpi_info.mask) +#define GPT_UNLOCK bit_unlock(gpi_info.lock, gpi_info.mask) +#endif + +static void tlbi_page_dsbosh(uintptr_t base) +{ + /* Look-up table for invalidation TLBs for 4KB, 16KB and 64KB pages */ + static const gpt_tlbi_lookup_t tlbi_page_lookup[] = { + { tlbirpalos_4k, ~(SZ_4K - 1UL) }, + { tlbirpalos_64k, ~(SZ_64K - 1UL) }, + { tlbirpalos_16k, ~(SZ_16K - 1UL) } + }; + + tlbi_page_lookup[gpt_config.pgs].function( + base & tlbi_page_lookup[gpt_config.pgs].mask); + dsbosh(); +} + +/* + * Helper function to fill out GPI entries in a single L1 table + * with Granules or Contiguous descriptor. + * + * Parameters + * l1 Pointer to 2MB, 32MB or 512MB aligned L1 table entry to fill out + * l1_desc GPT Granules or Contiguous descriptor set this range to + * cnt Number of double 128-bit L1 entries to fill + * + */ +static void fill_desc(uint64_t *l1, uint64_t l1_desc, unsigned int cnt) +{ + uint128_t *l1_quad = (uint128_t *)l1; + uint128_t l1_quad_desc = (uint128_t)l1_desc | ((uint128_t)l1_desc << 64); + + VERBOSE("GPT: %s(%p 0x%"PRIx64" %u)\n", __func__, l1, l1_desc, cnt); + + for (unsigned int i = 0U; i < cnt; i++) { + *l1_quad++ = l1_quad_desc; + } +} + +static void shatter_2mb(uintptr_t base, const gpi_info_t *gpi_info, + uint64_t l1_desc) +{ + unsigned long idx = GPT_L1_INDEX(ALIGN_2MB(base)); + + VERBOSE("GPT: %s(0x%"PRIxPTR" 0x%"PRIx64")\n", + __func__, base, l1_desc); + + /* Convert 2MB Contiguous block to Granules */ + fill_desc(&gpi_info->gpt_l1_addr[idx], l1_desc, L1_QWORDS_2MB); +} + +static void shatter_32mb(uintptr_t base, const gpi_info_t *gpi_info, + uint64_t l1_desc) +{ + unsigned long idx = GPT_L1_INDEX(ALIGN_2MB(base)); + const uint64_t *l1_gran = &gpi_info->gpt_l1_addr[idx]; + uint64_t l1_cont_desc = GPT_L1_CONT_DESC(l1_desc, 2MB); + uint64_t *l1; + + VERBOSE("GPT: %s(0x%"PRIxPTR" 0x%"PRIx64")\n", + __func__, base, l1_desc); + + /* Get index corresponding to 32MB aligned address */ + idx = GPT_L1_INDEX(ALIGN_32MB(base)); + l1 = &gpi_info->gpt_l1_addr[idx]; + + /* 16 x 2MB blocks in 32MB */ + for (unsigned int i = 0U; i < 16U; i++) { + /* Fill with Granules or Contiguous descriptors */ + fill_desc(l1, (l1 == l1_gran) ? l1_desc : l1_cont_desc, + L1_QWORDS_2MB); + l1 = (uint64_t *)((uintptr_t)l1 + L1_BYTES_2MB); + } +} + +static void shatter_512mb(uintptr_t base, const gpi_info_t *gpi_info, + uint64_t l1_desc) +{ + unsigned long idx = GPT_L1_INDEX(ALIGN_32MB(base)); + const uint64_t *l1_32mb = &gpi_info->gpt_l1_addr[idx]; + uint64_t l1_cont_desc = GPT_L1_CONT_DESC(l1_desc, 32MB); + uint64_t *l1; + + VERBOSE("GPT: %s(0x%"PRIxPTR" 0x%"PRIx64")\n", + __func__, base, l1_desc); + + /* Get index corresponding to 512MB aligned address */ + idx = GPT_L1_INDEX(ALIGN_512MB(base)); + l1 = &gpi_info->gpt_l1_addr[idx]; + + /* 16 x 32MB blocks in 512MB */ + for (unsigned int i = 0U; i < 16U; i++) { + if (l1 == l1_32mb) { + /* Shatter this 32MB block */ + shatter_32mb(base, gpi_info, l1_desc); + } else { + /* Fill 32MB with Contiguous descriptors */ + fill_desc(l1, l1_cont_desc, L1_QWORDS_32MB); + } + + l1 = (uint64_t *)((uintptr_t)l1 + L1_BYTES_32MB); + } +} + /* * This function checks to see if a GPI value is valid. * @@ -90,7 +275,7 @@ static uintptr_t gpt_l1_tbl; * Return * true for a valid GPI, false for an invalid one. */ -static bool gpt_is_gpi_valid(unsigned int gpi) +static bool is_gpi_valid(unsigned int gpi) { if ((gpi == GPT_GPI_NO_ACCESS) || (gpi == GPT_GPI_ANY) || ((gpi >= GPT_GPI_SECURE) && (gpi <= GPT_GPI_REALM))) { @@ -111,8 +296,8 @@ static bool gpt_is_gpi_valid(unsigned int gpi) * Return * True if PAS regions overlap, false if they do not. */ -static bool gpt_check_pas_overlap(uintptr_t base_1, size_t size_1, - uintptr_t base_2, size_t size_2) +static bool check_pas_overlap(uintptr_t base_1, size_t size_1, + uintptr_t base_2, size_t size_2) { if (((base_1 + size_1) > base_2) && ((base_2 + size_2) > base_1)) { return true; @@ -132,13 +317,13 @@ static bool gpt_check_pas_overlap(uintptr_t base_1, size_t size_1, * Return * True if a PAS region occupies the L0 region in question, false if not. */ -static bool gpt_does_previous_pas_exist_here(unsigned int l0_idx, - pas_region_t *pas_regions, - unsigned int pas_idx) +static bool does_previous_pas_exist_here(unsigned int l0_idx, + pas_region_t *pas_regions, + unsigned int pas_idx) { - /* Iterate over PAS regions up to pas_idx. */ + /* Iterate over PAS regions up to pas_idx */ for (unsigned int i = 0U; i < pas_idx; i++) { - if (gpt_check_pas_overlap((GPT_L0GPTSZ_ACTUAL_SIZE * l0_idx), + if (check_pas_overlap((GPT_L0GPTSZ_ACTUAL_SIZE * l0_idx), GPT_L0GPTSZ_ACTUAL_SIZE, pas_regions[i].base_pa, pas_regions[i].size)) { return true; @@ -163,8 +348,8 @@ static bool gpt_does_previous_pas_exist_here(unsigned int l0_idx, * Negative Linux error code in the event of a failure, number of L1 regions * required when successful. */ -static int gpt_validate_pas_mappings(pas_region_t *pas_regions, - unsigned int pas_region_cnt) +static int validate_pas_mappings(pas_region_t *pas_regions, + unsigned int pas_region_cnt) { unsigned int idx; unsigned int l1_cnt = 0U; @@ -175,18 +360,18 @@ static int gpt_validate_pas_mappings(pas_region_t *pas_regions, assert(pas_region_cnt != 0U); for (idx = 0U; idx < pas_region_cnt; idx++) { - /* Check for arithmetic overflow in region. */ + /* Check for arithmetic overflow in region */ if ((ULONG_MAX - pas_regions[idx].base_pa) < pas_regions[idx].size) { - ERROR("[GPT] Address overflow in PAS[%u]!\n", idx); + ERROR("GPT: Address overflow in PAS[%u]!\n", idx); return -EOVERFLOW; } - /* Initial checks for PAS validity. */ + /* Initial checks for PAS validity */ if (((pas_regions[idx].base_pa + pas_regions[idx].size) > GPT_PPS_ACTUAL_SIZE(gpt_config.t)) || - !gpt_is_gpi_valid(GPT_PAS_ATTR_GPI(pas_regions[idx].attrs))) { - ERROR("[GPT] PAS[%u] is invalid!\n", idx); + !is_gpi_valid(GPT_PAS_ATTR_GPI(pas_regions[idx].attrs))) { + ERROR("GPT: PAS[%u] is invalid!\n", idx); return -EFAULT; } @@ -195,12 +380,12 @@ static int gpt_validate_pas_mappings(pas_region_t *pas_regions, * start from idx + 1 instead of 0 since prior PAS mappings will * have already checked themselves against this one. */ - for (unsigned int i = idx + 1; i < pas_region_cnt; i++) { - if (gpt_check_pas_overlap(pas_regions[idx].base_pa, + for (unsigned int i = idx + 1U; i < pas_region_cnt; i++) { + if (check_pas_overlap(pas_regions[idx].base_pa, pas_regions[idx].size, pas_regions[i].base_pa, pas_regions[i].size)) { - ERROR("[GPT] PAS[%u] overlaps with PAS[%u]\n", + ERROR("GPT: PAS[%u] overlaps with PAS[%u]\n", i, idx); return -EFAULT; } @@ -212,12 +397,14 @@ static int gpt_validate_pas_mappings(pas_region_t *pas_regions, * to see if this PAS would fall into one that has already been * initialized. */ - for (unsigned int i = GPT_L0_IDX(pas_regions[idx].base_pa); - i <= GPT_L0_IDX(pas_regions[idx].base_pa + pas_regions[idx].size - 1); - i++) { + for (unsigned int i = + (unsigned int)GPT_L0_IDX(pas_regions[idx].base_pa); + i <= GPT_L0_IDX(pas_regions[idx].base_pa + + pas_regions[idx].size - 1UL); + i++) { if ((GPT_L0_TYPE(l0_desc[i]) == GPT_L0_TYPE_BLK_DESC) && (GPT_L0_BLKD_GPI(l0_desc[i]) == GPT_GPI_ANY)) { - /* This descriptor is unused so continue. */ + /* This descriptor is unused so continue */ continue; } @@ -225,18 +412,18 @@ static int gpt_validate_pas_mappings(pas_region_t *pas_regions, * This descriptor has been initialized in a previous * call to this function so cannot be initialized again. */ - ERROR("[GPT] PAS[%u] overlaps with previous L0[%d]!\n", + ERROR("GPT: PAS[%u] overlaps with previous L0[%u]!\n", idx, i); return -EFAULT; } - /* Check for block mapping (L0) type. */ + /* Check for block mapping (L0) type */ if (GPT_PAS_ATTR_MAP_TYPE(pas_regions[idx].attrs) == GPT_PAS_ATTR_MAP_TYPE_BLOCK) { - /* Make sure base and size are block-aligned. */ + /* Make sure base and size are block-aligned */ if (!GPT_IS_L0_ALIGNED(pas_regions[idx].base_pa) || !GPT_IS_L0_ALIGNED(pas_regions[idx].size)) { - ERROR("[GPT] PAS[%u] is not block-aligned!\n", + ERROR("GPT: PAS[%u] is not block-aligned!\n", idx); return -EFAULT; } @@ -244,21 +431,21 @@ static int gpt_validate_pas_mappings(pas_region_t *pas_regions, continue; } - /* Check for granule mapping (L1) type. */ + /* Check for granule mapping (L1) type */ if (GPT_PAS_ATTR_MAP_TYPE(pas_regions[idx].attrs) == GPT_PAS_ATTR_MAP_TYPE_GRANULE) { - /* Make sure base and size are granule-aligned. */ + /* Make sure base and size are granule-aligned */ if (!GPT_IS_L1_ALIGNED(gpt_config.p, pas_regions[idx].base_pa) || !GPT_IS_L1_ALIGNED(gpt_config.p, pas_regions[idx].size)) { - ERROR("[GPT] PAS[%u] is not granule-aligned!\n", + ERROR("GPT: PAS[%u] is not granule-aligned!\n", idx); return -EFAULT; } - /* Find how many L1 tables this PAS occupies. */ + /* Find how many L1 tables this PAS occupies */ pas_l1_cnt = (GPT_L0_IDX(pas_regions[idx].base_pa + - pas_regions[idx].size - 1) - - GPT_L0_IDX(pas_regions[idx].base_pa) + 1); + pas_regions[idx].size - 1UL) - + GPT_L0_IDX(pas_regions[idx].base_pa) + 1U); /* * This creates a situation where, if multiple PAS @@ -276,26 +463,26 @@ static int gpt_validate_pas_mappings(pas_region_t *pas_regions, * both for overlap against other PAS. */ if (pas_l1_cnt > 1) { - if (gpt_does_previous_pas_exist_here( + if (does_previous_pas_exist_here( GPT_L0_IDX(pas_regions[idx].base_pa + - pas_regions[idx].size - 1), + pas_regions[idx].size - 1UL), pas_regions, idx)) { - pas_l1_cnt = pas_l1_cnt - 1; + pas_l1_cnt--; } } - if (gpt_does_previous_pas_exist_here( + if (does_previous_pas_exist_here( GPT_L0_IDX(pas_regions[idx].base_pa), pas_regions, idx)) { - pas_l1_cnt = pas_l1_cnt - 1; + pas_l1_cnt--; } l1_cnt += pas_l1_cnt; continue; } - /* If execution reaches this point, mapping type is invalid. */ - ERROR("[GPT] PAS[%u] has invalid mapping type 0x%x.\n", idx, + /* If execution reaches this point, mapping type is invalid */ + ERROR("GPT: PAS[%u] has invalid mapping type 0x%x.\n", idx, GPT_PAS_ATTR_MAP_TYPE(pas_regions[idx].attrs)); return -EINVAL; } @@ -308,44 +495,62 @@ static int gpt_validate_pas_mappings(pas_region_t *pas_regions, * * Parameters * l0_mem_base Base address of memory used for L0 tables. - * l1_mem_size Size of memory available for L0 tables. + * l0_mem_size Size of memory available for L0 tables. * * Return * Negative Linux error code in the event of a failure, 0 for success. */ -static int gpt_validate_l0_params(gpccr_pps_e pps, uintptr_t l0_mem_base, - size_t l0_mem_size) +static int validate_l0_params(gpccr_pps_e pps, uintptr_t l0_mem_base, + size_t l0_mem_size) { - size_t l0_alignment; + size_t l0_alignment, locks_size = 0; /* * Make sure PPS is valid and then store it since macros need this value * to work. */ if (pps > GPT_PPS_MAX) { - ERROR("[GPT] Invalid PPS: 0x%x\n", pps); + ERROR("GPT: Invalid PPS: 0x%x\n", pps); return -EINVAL; } gpt_config.pps = pps; gpt_config.t = gpt_t_lookup[pps]; - /* Alignment must be the greater of 4k or l0 table size. */ + /* Alignment must be the greater of 4KB or l0 table size */ l0_alignment = PAGE_SIZE_4KB; if (l0_alignment < GPT_L0_TABLE_SIZE(gpt_config.t)) { l0_alignment = GPT_L0_TABLE_SIZE(gpt_config.t); } - /* Check base address. */ - if ((l0_mem_base == 0U) || ((l0_mem_base & (l0_alignment - 1)) != 0U)) { - ERROR("[GPT] Invalid L0 base address: 0x%lx\n", l0_mem_base); + /* Check base address */ + if ((l0_mem_base == 0UL) || + ((l0_mem_base & (l0_alignment - 1UL)) != 0UL)) { + ERROR("GPT: Invalid L0 base address: 0x%lx\n", l0_mem_base); return -EFAULT; } - /* Check size. */ - if (l0_mem_size < GPT_L0_TABLE_SIZE(gpt_config.t)) { - ERROR("[GPT] Inadequate L0 memory: need 0x%lx, have 0x%lx)\n", - GPT_L0_TABLE_SIZE(gpt_config.t), - l0_mem_size); +#if (RME_GPT_BITLOCK_BLOCK != 0) + /* + * Size of bitlocks in bytes for the protected address space + * with RME_GPT_BITLOCK_BLOCK * 512MB per bitlock. + */ + locks_size = GPT_PPS_ACTUAL_SIZE(gpt_config.t) / + (RME_GPT_BITLOCK_BLOCK * SZ_512M * 8U); + + /* + * If protected space size is less than the size covered + * by 'bitlock' structure, check for a single bitlock. + */ + if (locks_size < LOCK_SIZE) { + locks_size = LOCK_SIZE; + } +#endif + /* Check size for L0 tables and bitlocks */ + if (l0_mem_size < (GPT_L0_TABLE_SIZE(gpt_config.t) + locks_size)) { + ERROR("GPT: Inadequate L0 memory\n"); + ERROR(" Expected 0x%lx bytes, got 0x%lx bytes\n", + GPT_L0_TABLE_SIZE(gpt_config.t) + locks_size, + l0_mem_size); return -ENOMEM; } @@ -364,8 +569,8 @@ static int gpt_validate_l0_params(gpccr_pps_e pps, uintptr_t l0_mem_base, * Return * Negative Linux error code in the event of a failure, 0 for success. */ -static int gpt_validate_l1_params(uintptr_t l1_mem_base, size_t l1_mem_size, - unsigned int l1_gpt_cnt) +static int validate_l1_params(uintptr_t l1_mem_base, size_t l1_mem_size, + unsigned int l1_gpt_cnt) { size_t l1_gpt_mem_sz; @@ -375,31 +580,32 @@ static int gpt_validate_l1_params(uintptr_t l1_mem_base, size_t l1_mem_size, return -EPERM; } - /* Make sure L1 tables are aligned to their size. */ - if ((l1_mem_base & (GPT_L1_TABLE_SIZE(gpt_config.p) - 1)) != 0U) { - ERROR("[GPT] Unaligned L1 GPT base address: 0x%lx\n", + /* Make sure L1 tables are aligned to their size */ + if ((l1_mem_base & (GPT_L1_TABLE_SIZE(gpt_config.p) - 1UL)) != 0UL) { + ERROR("GPT: Unaligned L1 GPT base address: 0x%"PRIxPTR"\n", l1_mem_base); return -EFAULT; } - /* Get total memory needed for L1 tables. */ + /* Get total memory needed for L1 tables */ l1_gpt_mem_sz = l1_gpt_cnt * GPT_L1_TABLE_SIZE(gpt_config.p); - /* Check for overflow. */ + /* Check for overflow */ if ((l1_gpt_mem_sz / GPT_L1_TABLE_SIZE(gpt_config.p)) != l1_gpt_cnt) { - ERROR("[GPT] Overflow calculating L1 memory size.\n"); + ERROR("GPT: Overflow calculating L1 memory size\n"); return -ENOMEM; } - /* Make sure enough space was supplied. */ + /* Make sure enough space was supplied */ if (l1_mem_size < l1_gpt_mem_sz) { - ERROR("[GPT] Inadequate memory for L1 GPTs. "); - ERROR(" Expected 0x%lx bytes. Got 0x%lx bytes\n", - l1_gpt_mem_sz, l1_mem_size); + ERROR("%sL1 GPTs%s", (const char *)"GPT: Inadequate ", + (const char *)" memory\n"); + ERROR(" Expected 0x%lx bytes, got 0x%lx bytes\n", + l1_gpt_mem_sz, l1_mem_size); return -ENOMEM; } - VERBOSE("[GPT] Requested 0x%lx bytes for L1 GPTs.\n", l1_gpt_mem_sz); + VERBOSE("GPT: Requested 0x%lx bytes for L1 GPTs\n", l1_gpt_mem_sz); return 0; } @@ -411,11 +617,10 @@ static int gpt_validate_l1_params(uintptr_t l1_mem_base, size_t l1_mem_size, * *pas Pointer to the structure defining the PAS region to * initialize. */ -static void gpt_generate_l0_blk_desc(pas_region_t *pas) +static void generate_l0_blk_desc(pas_region_t *pas) { uint64_t gpt_desc; - unsigned int end_idx; - unsigned int idx; + unsigned long idx, end_idx; uint64_t *l0_gpt_arr; assert(gpt_config.plat_gpt_l0_base != 0U); @@ -423,7 +628,7 @@ static void gpt_generate_l0_blk_desc(pas_region_t *pas) /* * Checking of PAS parameters has already been done in - * gpt_validate_pas_mappings so no need to check the same things again. + * validate_pas_mappings so no need to check the same things again. */ l0_gpt_arr = (uint64_t *)gpt_config.plat_gpt_l0_base; @@ -441,10 +646,10 @@ static void gpt_generate_l0_blk_desc(pas_region_t *pas) */ end_idx = GPT_L0_IDX(pas->base_pa + pas->size); - /* Generate the needed block descriptors. */ + /* Generate the needed block descriptors */ for (; idx < end_idx; idx++) { l0_gpt_arr[idx] = gpt_desc; - VERBOSE("[GPT] L0 entry (BLOCK) index %u [%p]: GPI = 0x%" PRIx64 " (0x%" PRIx64 ")\n", + VERBOSE("GPT: L0 entry (BLOCK) index %lu [%p]: GPI = 0x%"PRIx64" (0x%"PRIx64")\n", idx, &l0_gpt_arr[idx], (gpt_desc >> GPT_L0_BLK_DESC_GPI_SHIFT) & GPT_L0_BLK_DESC_GPI_MASK, l0_gpt_arr[idx]); @@ -464,7 +669,7 @@ static void gpt_generate_l0_blk_desc(pas_region_t *pas) * Return * The PA of the end of the current range. */ -static uintptr_t gpt_get_l1_end_pa(uintptr_t cur_pa, uintptr_t end_pa) +static uintptr_t get_l1_end_pa(uintptr_t cur_pa, uintptr_t end_pa) { uintptr_t cur_idx; uintptr_t end_idx; @@ -478,51 +683,205 @@ static uintptr_t gpt_get_l1_end_pa(uintptr_t cur_pa, uintptr_t end_pa) return end_pa; } - return (cur_idx + 1U) << GPT_L0_IDX_SHIFT; + return (cur_idx + 1UL) << GPT_L0_IDX_SHIFT; } /* - * Helper function to fill out GPI entries in a single L1 table. This function - * fills out entire L1 descriptors at a time to save memory writes. + * Helper function to fill out GPI entries from 'first' granule address of + * the specified 'length' in a single L1 table with 'l1_desc' Contiguous + * descriptor. * * Parameters - * gpi GPI to set this range to * l1 Pointer to L1 table to fill out - * first Address of first granule in range. - * last Address of last granule in range (inclusive). + * first Address of first granule in range + * length Length of the range in bytes + * gpi GPI set this range to + * + * Return + * Address of next granule in range. */ -static void gpt_fill_l1_tbl(uint64_t gpi, uint64_t *l1, uintptr_t first, - uintptr_t last) +__unused static uintptr_t fill_l1_cont_desc(uint64_t *l1, uintptr_t first, + size_t length, unsigned int gpi) { - uint64_t gpi_field = GPT_BUILD_L1_DESC(gpi); - uint64_t gpi_mask = 0xFFFFFFFFFFFFFFFF; + /* + * Look up table for contiguous blocks and descriptors. + * Entries should be defined in descending block sizes: + * 512MB, 32MB and 2MB. + */ + static const gpt_fill_lookup_t gpt_fill_lookup[] = { +#if (RME_GPT_MAX_BLOCK == 512) + { SZ_512M, GPT_L1_CONT_DESC_512MB }, +#endif +#if (RME_GPT_MAX_BLOCK >= 32) + { SZ_32M, GPT_L1_CONT_DESC_32MB }, +#endif +#if (RME_GPT_MAX_BLOCK != 0) + { SZ_2M, GPT_L1_CONT_DESC_2MB } +#endif + }; - assert(first <= last); - assert((first & (GPT_PGS_ACTUAL_SIZE(gpt_config.p) - 1)) == 0U); - assert((last & (GPT_PGS_ACTUAL_SIZE(gpt_config.p) - 1)) == 0U); - assert(GPT_L0_IDX(first) == GPT_L0_IDX(last)); - assert(l1 != NULL); + /* + * Iterate through all block sizes (512MB, 32MB and 2MB) + * starting with maximum supported. + */ + for (unsigned long i = 0UL; i < ARRAY_SIZE(gpt_fill_lookup); i++) { + /* Calculate index */ + unsigned long idx = GPT_L1_INDEX(first); + + /* Contiguous block size */ + size_t cont_size = gpt_fill_lookup[i].size; + + if (GPT_REGION_IS_CONT(length, first, cont_size)) { + + /* Generate Contiguous descriptor */ + uint64_t l1_desc = GPT_L1_GPI_CONT_DESC(gpi, + gpt_fill_lookup[i].desc); + + /* Number of 128-bit L1 entries in block */ + unsigned int cnt; + + switch (cont_size) { + case SZ_512M: + cnt = L1_QWORDS_512MB; + break; + case SZ_32M: + cnt = L1_QWORDS_32MB; + break; + default: /* SZ_2MB */ + cnt = L1_QWORDS_2MB; + } + + VERBOSE("GPT: Contiguous descriptor 0x%"PRIxPTR" %luMB\n", + first, cont_size / SZ_1M); + + /* Fill Contiguous descriptors */ + fill_desc(&l1[idx], l1_desc, cnt); + first += cont_size; + length -= cont_size; - /* Shift the mask if we're starting in the middle of an L1 entry. */ - gpi_mask = gpi_mask << (GPT_L1_GPI_IDX(gpt_config.p, first) << 2); + if (length == 0UL) { + break; + } + } + } + + return first; +} - /* Fill out each L1 entry for this region. */ - for (unsigned int i = GPT_L1_IDX(gpt_config.p, first); - i <= GPT_L1_IDX(gpt_config.p, last); i++) { - /* Account for stopping in the middle of an L1 entry. */ - if (i == GPT_L1_IDX(gpt_config.p, last)) { - gpi_mask &= (gpi_mask >> ((15 - +/* Build Granules descriptor with the same 'gpi' for every GPI entry */ +static uint64_t build_l1_desc(unsigned int gpi) +{ + uint64_t l1_desc = (uint64_t)gpi | ((uint64_t)gpi << 4); + + l1_desc |= (l1_desc << 8); + l1_desc |= (l1_desc << 16); + return (l1_desc | (l1_desc << 32)); +} + +/* + * Helper function to fill out GPI entries from 'first' to 'last' granule + * address in a single L1 table with 'l1_desc' Granules descriptor. + * + * Parameters + * l1 Pointer to L1 table to fill out + * first Address of first granule in range + * last Address of last granule in range (inclusive) + * gpi GPI set this range to + * + * Return + * Address of next granule in range. + */ +static uintptr_t fill_l1_gran_desc(uint64_t *l1, uintptr_t first, + uintptr_t last, unsigned int gpi) +{ + uint64_t gpi_mask; + unsigned long i; + + /* Generate Granules descriptor */ + uint64_t l1_desc = build_l1_desc(gpi); + + /* Shift the mask if we're starting in the middle of an L1 entry */ + gpi_mask = ULONG_MAX << (GPT_L1_GPI_IDX(gpt_config.p, first) << 2); + + /* Fill out each L1 entry for this region */ + for (i = GPT_L1_INDEX(first); i <= GPT_L1_INDEX(last); i++) { + + /* Account for stopping in the middle of an L1 entry */ + if (i == GPT_L1_INDEX(last)) { + gpi_mask &= (gpi_mask >> ((15U - GPT_L1_GPI_IDX(gpt_config.p, last)) << 2)); } - /* Write GPI values. */ - assert((l1[i] & gpi_mask) == - (GPT_BUILD_L1_DESC(GPT_GPI_ANY) & gpi_mask)); - l1[i] = (l1[i] & ~gpi_mask) | (gpi_mask & gpi_field); + assert((l1[i] & gpi_mask) == (GPT_L1_ANY_DESC & gpi_mask)); + + /* Write GPI values */ + l1[i] = (l1[i] & ~gpi_mask) | (l1_desc & gpi_mask); + + /* Reset mask */ + gpi_mask = ULONG_MAX; + } + + return last + GPT_PGS_ACTUAL_SIZE(gpt_config.p); +} + +/* + * Helper function to fill out GPI entries in a single L1 table. + * This function fills out an entire L1 table with either Granules or Contiguous + * (RME_GPT_MAX_BLOCK != 0) descriptors depending on region length and alignment. + * Note. If RME_GPT_MAX_BLOCK == 0, then the L1 tables are filled with regular + * Granules descriptors. + * + * Parameters + * l1 Pointer to L1 table to fill out + * first Address of first granule in range + * last Address of last granule in range (inclusive) + * gpi GPI set this range to + */ +static void fill_l1_tbl(uint64_t *l1, uintptr_t first, uintptr_t last, + unsigned int gpi) +{ + assert(l1 != NULL); + assert(first <= last); + assert((first & (GPT_PGS_ACTUAL_SIZE(gpt_config.p) - 1UL)) == 0UL); + assert((last & (GPT_PGS_ACTUAL_SIZE(gpt_config.p) - 1UL)) == 0UL); + assert(GPT_L0_IDX(first) == GPT_L0_IDX(last)); + +#if (RME_GPT_MAX_BLOCK != 0) + while (first <= last) { + /* Region length */ + size_t length = last - first + GPT_PGS_ACTUAL_SIZE(gpt_config.p); + + if (length < SZ_2M) { + /* + * Fill with Granule descriptors in case of + * region length < 2MB. + */ + first = fill_l1_gran_desc(l1, first, last, gpi); + + } else if ((first & (SZ_2M - UL(1))) == UL(0)) { + /* + * For region length >= 2MB and at least 2MB aligned + * call to fill_l1_cont_desc will iterate through + * all block sizes (512MB, 32MB and 2MB) supported and + * fill corresponding Contiguous descriptors. + */ + first = fill_l1_cont_desc(l1, first, length, gpi); + } else { + /* + * For not aligned region >= 2MB fill with Granules + * descriptors up to the next 2MB aligned address. + */ + uintptr_t new_last = ALIGN_2MB(first + SZ_2M) - + GPT_PGS_ACTUAL_SIZE(gpt_config.p); - /* Reset mask. */ - gpi_mask = 0xFFFFFFFFFFFFFFFF; + first = fill_l1_gran_desc(l1, first, new_last, gpi); + } } +#else + /* Fill with Granule descriptors */ + first = fill_l1_gran_desc(l1, first, last, gpi); +#endif + assert(first == (last + GPT_PGS_ACTUAL_SIZE(gpt_config.p))); } /* @@ -536,19 +895,17 @@ static void gpt_fill_l1_tbl(uint64_t gpi, uint64_t *l1, uintptr_t first, * Return * Pointer to the next available L1 table. */ -static uint64_t *gpt_get_new_l1_tbl(void) +static uint64_t *get_new_l1_tbl(void) { - /* Retrieve the next L1 table. */ - uint64_t *l1 = (uint64_t *)((uint64_t)(gpt_l1_tbl) + - (GPT_L1_TABLE_SIZE(gpt_config.p) * - gpt_next_l1_tbl_idx)); + /* Retrieve the next L1 table */ + uint64_t *l1 = (uint64_t *)gpt_l1_tbl; - /* Increment L1 counter. */ - gpt_next_l1_tbl_idx++; + /* Increment L1 GPT address */ + gpt_l1_tbl += GPT_L1_TABLE_SIZE(gpt_config.p); /* Initialize all GPIs to GPT_GPI_ANY */ for (unsigned int i = 0U; i < GPT_L1_ENTRY_COUNT(gpt_config.p); i++) { - l1[i] = GPT_BUILD_L1_DESC(GPT_GPI_ANY); + l1[i] = GPT_L1_ANY_DESC; } return l1; @@ -562,58 +919,57 @@ static uint64_t *gpt_get_new_l1_tbl(void) * Parameters * *pas Pointer to the structure defining the PAS region. */ -static void gpt_generate_l0_tbl_desc(pas_region_t *pas) +static void generate_l0_tbl_desc(pas_region_t *pas) { uintptr_t end_pa; uintptr_t cur_pa; uintptr_t last_gran_pa; uint64_t *l0_gpt_base; uint64_t *l1_gpt_arr; - unsigned int l0_idx; + unsigned int l0_idx, gpi; assert(gpt_config.plat_gpt_l0_base != 0U); assert(pas != NULL); /* * Checking of PAS parameters has already been done in - * gpt_validate_pas_mappings so no need to check the same things again. + * validate_pas_mappings so no need to check the same things again. */ - end_pa = pas->base_pa + pas->size; l0_gpt_base = (uint64_t *)gpt_config.plat_gpt_l0_base; /* We start working from the granule at base PA */ cur_pa = pas->base_pa; - /* Iterate over each L0 region in this memory range. */ - for (l0_idx = GPT_L0_IDX(pas->base_pa); - l0_idx <= GPT_L0_IDX(end_pa - 1U); - l0_idx++) { + /* Get GPI */ + gpi = GPT_PAS_ATTR_GPI(pas->attrs); + /* Iterate over each L0 region in this memory range */ + for (l0_idx = (unsigned int)GPT_L0_IDX(pas->base_pa); + l0_idx <= (unsigned int)GPT_L0_IDX(end_pa - 1UL); + l0_idx++) { /* * See if the L0 entry is already a table descriptor or if we * need to create one. */ if (GPT_L0_TYPE(l0_gpt_base[l0_idx]) == GPT_L0_TYPE_TBL_DESC) { - /* Get the L1 array from the L0 entry. */ + /* Get the L1 array from the L0 entry */ l1_gpt_arr = GPT_L0_TBLD_ADDR(l0_gpt_base[l0_idx]); } else { - /* Get a new L1 table from the L1 memory space. */ - l1_gpt_arr = gpt_get_new_l1_tbl(); + /* Get a new L1 table from the L1 memory space */ + l1_gpt_arr = get_new_l1_tbl(); - /* Fill out the L0 descriptor and flush it. */ + /* Fill out the L0 descriptor and flush it */ l0_gpt_base[l0_idx] = GPT_L0_TBL_DESC(l1_gpt_arr); } - VERBOSE("[GPT] L0 entry (TABLE) index %u [%p] ==> L1 Addr 0x%llx (0x%" PRIx64 ")\n", - l0_idx, &l0_gpt_base[l0_idx], - (unsigned long long)(l1_gpt_arr), - l0_gpt_base[l0_idx]); + VERBOSE("GPT: L0 entry (TABLE) index %u [%p] ==> L1 Addr %p (0x%"PRIx64")\n", + l0_idx, &l0_gpt_base[l0_idx], l1_gpt_arr, l0_gpt_base[l0_idx]); /* * Determine the PA of the last granule in this L0 descriptor. */ - last_gran_pa = gpt_get_l1_end_pa(cur_pa, end_pa) - + last_gran_pa = get_l1_end_pa(cur_pa, end_pa) - GPT_PGS_ACTUAL_SIZE(gpt_config.p); /* @@ -621,11 +977,10 @@ static void gpt_generate_l0_tbl_desc(pas_region_t *pas) * function needs the addresses of the first granule and last * granule in the range. */ - gpt_fill_l1_tbl(GPT_PAS_ATTR_GPI(pas->attrs), l1_gpt_arr, - cur_pa, last_gran_pa); + fill_l1_tbl(l1_gpt_arr, cur_pa, last_gran_pa, gpi); - /* Advance cur_pa to first granule in next L0 region. */ - cur_pa = gpt_get_l1_end_pa(cur_pa, end_pa); + /* Advance cur_pa to first granule in next L0 region */ + cur_pa = get_l1_end_pa(cur_pa, end_pa); } } @@ -642,25 +997,25 @@ static void gpt_generate_l0_tbl_desc(pas_region_t *pas) */ static void flush_l0_for_pas_array(pas_region_t *pas, unsigned int pas_count) { - unsigned int idx; - unsigned int start_idx; - unsigned int end_idx; + unsigned long idx; + unsigned long start_idx; + unsigned long end_idx; uint64_t *l0 = (uint64_t *)gpt_config.plat_gpt_l0_base; assert(pas != NULL); - assert(pas_count > 0); + assert(pas_count != 0U); - /* Initial start and end values. */ + /* Initial start and end values */ start_idx = GPT_L0_IDX(pas[0].base_pa); - end_idx = GPT_L0_IDX(pas[0].base_pa + pas[0].size - 1); + end_idx = GPT_L0_IDX(pas[0].base_pa + pas[0].size - 1UL); - /* Find lowest and highest L0 indices used in this PAS array. */ - for (idx = 1; idx < pas_count; idx++) { + /* Find lowest and highest L0 indices used in this PAS array */ + for (idx = 1UL; idx < pas_count; idx++) { if (GPT_L0_IDX(pas[idx].base_pa) < start_idx) { start_idx = GPT_L0_IDX(pas[idx].base_pa); } - if (GPT_L0_IDX(pas[idx].base_pa + pas[idx].size - 1) > end_idx) { - end_idx = GPT_L0_IDX(pas[idx].base_pa + pas[idx].size - 1); + if (GPT_L0_IDX(pas[idx].base_pa + pas[idx].size - 1UL) > end_idx) { + end_idx = GPT_L0_IDX(pas[idx].base_pa + pas[idx].size - 1UL); } } @@ -669,7 +1024,7 @@ static void flush_l0_for_pas_array(pas_region_t *pas, unsigned int pas_count) * the end index value. */ flush_dcache_range((uintptr_t)&l0[start_idx], - ((end_idx + 1) - start_idx) * sizeof(uint64_t)); + ((end_idx + 1UL) - start_idx) * sizeof(uint64_t)); } /* @@ -688,8 +1043,8 @@ int gpt_enable(void) * Granule tables must be initialised before enabling * granule protection. */ - if (gpt_config.plat_gpt_l0_base == 0U) { - ERROR("[GPT] Tables have not been initialized!\n"); + if (gpt_config.plat_gpt_l0_base == 0UL) { + ERROR("GPT: Tables have not been initialized!\n"); return -EPERM; } @@ -710,7 +1065,7 @@ int gpt_enable(void) */ gpccr_el3 |= SET_GPCCR_SH(GPCCR_SH_IS); - /* Outer and Inner cacheability set to Normal memory, WB, RA, WA. */ + /* Outer and Inner cacheability set to Normal memory, WB, RA, WA */ gpccr_el3 |= SET_GPCCR_ORGN(GPCCR_ORGN_WB_RA_WA); gpccr_el3 |= SET_GPCCR_IRGN(GPCCR_IRGN_WB_RA_WA); @@ -726,7 +1081,7 @@ int gpt_enable(void) /* Enable GPT */ gpccr_el3 |= GPCCR_GPC_BIT; - /* TODO: Configure GPCCR_EL3_GPCP for Fault control. */ + /* TODO: Configure GPCCR_EL3_GPCP for Fault control */ write_gpccr_el3(gpccr_el3); isb(); tlbipaallos(); @@ -765,19 +1120,21 @@ void gpt_disable(void) int gpt_init_l0_tables(gpccr_pps_e pps, uintptr_t l0_mem_base, size_t l0_mem_size) { - int ret; uint64_t gpt_desc; + size_t locks_size = 0; + __unused bitlock_t *bit_locks; + int ret; - /* Ensure that MMU and Data caches are enabled. */ + /* Ensure that MMU and Data caches are enabled */ assert((read_sctlr_el3() & SCTLR_C_BIT) != 0U); - /* Validate other parameters. */ - ret = gpt_validate_l0_params(pps, l0_mem_base, l0_mem_size); + /* Validate other parameters */ + ret = validate_l0_params(pps, l0_mem_base, l0_mem_size); if (ret != 0) { return ret; } - /* Create the descriptor to initialize L0 entries with. */ + /* Create the descriptor to initialize L0 entries with */ gpt_desc = GPT_L0_BLK_DESC(GPT_GPI_ANY); /* Iterate through all L0 entries */ @@ -785,11 +1142,33 @@ int gpt_init_l0_tables(gpccr_pps_e pps, uintptr_t l0_mem_base, ((uint64_t *)l0_mem_base)[i] = gpt_desc; } - /* Flush updated L0 tables to memory. */ +#if (RME_GPT_BITLOCK_BLOCK != 0) + /* Initialise bitlocks at the end of L0 table */ + bit_locks = (bitlock_t *)(l0_mem_base + + GPT_L0_TABLE_SIZE(gpt_config.t)); + + /* Size of bitlocks in bytes */ + locks_size = GPT_PPS_ACTUAL_SIZE(gpt_config.t) / + (RME_GPT_BITLOCK_BLOCK * SZ_512M * 8U); + + /* + * If protected space size is less than the size covered + * by 'bitlock' structure, initialise a single bitlock. + */ + if (locks_size < LOCK_SIZE) { + locks_size = LOCK_SIZE; + } + + for (size_t i = 0UL; i < (locks_size/LOCK_SIZE); i++) { + bit_locks[i].lock = 0U; + } +#endif + + /* Flush updated L0 tables and bitlocks to memory */ flush_dcache_range((uintptr_t)l0_mem_base, - (size_t)GPT_L0_TABLE_SIZE(gpt_config.t)); + GPT_L0_TABLE_SIZE(gpt_config.t) + locks_size); - /* Stash the L0 base address once initial setup is complete. */ + /* Stash the L0 base address once initial setup is complete */ gpt_config.plat_gpt_l0_base = l0_mem_base; return 0; @@ -804,7 +1183,7 @@ int gpt_init_l0_tables(gpccr_pps_e pps, uintptr_t l0_mem_base, * This function can be called multiple times with different L1 memory ranges * and PAS regions if it is desirable to place L1 tables in different locations * in memory. (ex: you have multiple DDR banks and want to place the L1 tables - * in the DDR bank that they control) + * in the DDR bank that they control). * * Parameters * pgs PGS value to use for table generation. @@ -820,82 +1199,86 @@ int gpt_init_pas_l1_tables(gpccr_pgs_e pgs, uintptr_t l1_mem_base, size_t l1_mem_size, pas_region_t *pas_regions, unsigned int pas_count) { - int ret; - int l1_gpt_cnt; + int l1_gpt_cnt, ret; - /* Ensure that MMU and Data caches are enabled. */ + /* Ensure that MMU and Data caches are enabled */ assert((read_sctlr_el3() & SCTLR_C_BIT) != 0U); - /* PGS is needed for gpt_validate_pas_mappings so check it now. */ + /* PGS is needed for validate_pas_mappings so check it now */ if (pgs > GPT_PGS_MAX) { - ERROR("[GPT] Invalid PGS: 0x%x\n", pgs); + ERROR("GPT: Invalid PGS: 0x%x\n", pgs); return -EINVAL; } gpt_config.pgs = pgs; gpt_config.p = gpt_p_lookup[pgs]; - /* Make sure L0 tables have been initialized. */ + /* Make sure L0 tables have been initialized */ if (gpt_config.plat_gpt_l0_base == 0U) { - ERROR("[GPT] L0 tables must be initialized first!\n"); + ERROR("GPT: L0 tables must be initialized first!\n"); return -EPERM; } - /* Check if L1 GPTs are required and how many. */ - l1_gpt_cnt = gpt_validate_pas_mappings(pas_regions, pas_count); + /* Check if L1 GPTs are required and how many */ + l1_gpt_cnt = validate_pas_mappings(pas_regions, pas_count); if (l1_gpt_cnt < 0) { return l1_gpt_cnt; } - VERBOSE("[GPT] %u L1 GPTs requested.\n", l1_gpt_cnt); + VERBOSE("GPT: %i L1 GPTs requested\n", l1_gpt_cnt); - /* If L1 tables are needed then validate the L1 parameters. */ + /* If L1 tables are needed then validate the L1 parameters */ if (l1_gpt_cnt > 0) { - ret = gpt_validate_l1_params(l1_mem_base, l1_mem_size, - l1_gpt_cnt); + ret = validate_l1_params(l1_mem_base, l1_mem_size, + (unsigned int)l1_gpt_cnt); if (ret != 0) { return ret; } - /* Set up parameters for L1 table generation. */ + /* Set up parameters for L1 table generation */ gpt_l1_tbl = l1_mem_base; - gpt_next_l1_tbl_idx = 0U; } - INFO("[GPT] Boot Configuration\n"); + /* Number of L1 entries in 2MB depends on GPCCR_EL3.PGS value */ + gpt_l1_cnt_2mb = (unsigned int)GPT_L1_ENTRY_COUNT_2MB(gpt_config.p); + + /* Mask for the L1 index field */ + gpt_l1_index_mask = GPT_L1_IDX_MASK(gpt_config.p); + + INFO("GPT: Boot Configuration\n"); INFO(" PPS/T: 0x%x/%u\n", gpt_config.pps, gpt_config.t); INFO(" PGS/P: 0x%x/%u\n", gpt_config.pgs, gpt_config.p); INFO(" L0GPTSZ/S: 0x%x/%u\n", GPT_L0GPTSZ, GPT_S_VAL); - INFO(" PAS count: 0x%x\n", pas_count); - INFO(" L0 base: 0x%lx\n", gpt_config.plat_gpt_l0_base); + INFO(" PAS count: %u\n", pas_count); + INFO(" L0 base: 0x%"PRIxPTR"\n", gpt_config.plat_gpt_l0_base); - /* Generate the tables in memory. */ + /* Generate the tables in memory */ for (unsigned int idx = 0U; idx < pas_count; idx++) { - INFO("[GPT] PAS[%u]: base 0x%lx, size 0x%lx, GPI 0x%x, type 0x%x\n", - idx, pas_regions[idx].base_pa, pas_regions[idx].size, - GPT_PAS_ATTR_GPI(pas_regions[idx].attrs), - GPT_PAS_ATTR_MAP_TYPE(pas_regions[idx].attrs)); + VERBOSE("GPT: PAS[%u]: base 0x%"PRIxPTR"\tsize 0x%lx\tGPI 0x%x\ttype 0x%x\n", + idx, pas_regions[idx].base_pa, pas_regions[idx].size, + GPT_PAS_ATTR_GPI(pas_regions[idx].attrs), + GPT_PAS_ATTR_MAP_TYPE(pas_regions[idx].attrs)); /* Check if a block or table descriptor is required */ if (GPT_PAS_ATTR_MAP_TYPE(pas_regions[idx].attrs) == GPT_PAS_ATTR_MAP_TYPE_BLOCK) { - gpt_generate_l0_blk_desc(&pas_regions[idx]); + generate_l0_blk_desc(&pas_regions[idx]); } else { - gpt_generate_l0_tbl_desc(&pas_regions[idx]); + generate_l0_tbl_desc(&pas_regions[idx]); } } - /* Flush modified L0 tables. */ + /* Flush modified L0 tables */ flush_l0_for_pas_array(pas_regions, pas_count); - /* Flush L1 tables if needed. */ + /* Flush L1 tables if needed */ if (l1_gpt_cnt > 0) { flush_dcache_range(l1_mem_base, GPT_L1_TABLE_SIZE(gpt_config.p) * - l1_gpt_cnt); + (size_t)l1_gpt_cnt); } - /* Make sure that all the entries are written to the memory. */ + /* Make sure that all the entries are written to the memory */ dsbishst(); tlbipaallos(); dsb(); @@ -919,12 +1302,12 @@ int gpt_runtime_init(void) { u_register_t reg; - /* Ensure that MMU and Data caches are enabled. */ + /* Ensure that MMU and Data caches are enabled */ assert((read_sctlr_el3() & SCTLR_C_BIT) != 0U); - /* Ensure GPC are already enabled. */ + /* Ensure GPC are already enabled */ if ((read_gpccr_el3() & GPCCR_GPC_BIT) == 0U) { - ERROR("[GPT] Granule protection checks are not enabled!\n"); + ERROR("GPT: Granule protection checks are not enabled!\n"); return -EPERM; } @@ -937,32 +1320,38 @@ int gpt_runtime_init(void) GPTBR_BADDR_MASK) << GPTBR_BADDR_VAL_SHIFT; - /* Read GPCCR to get PGS and PPS values. */ + /* Read GPCCR to get PGS and PPS values */ reg = read_gpccr_el3(); gpt_config.pps = (reg >> GPCCR_PPS_SHIFT) & GPCCR_PPS_MASK; gpt_config.t = gpt_t_lookup[gpt_config.pps]; gpt_config.pgs = (reg >> GPCCR_PGS_SHIFT) & GPCCR_PGS_MASK; gpt_config.p = gpt_p_lookup[gpt_config.pgs]; - VERBOSE("[GPT] Runtime Configuration\n"); + /* Number of L1 entries in 2MB depends on GPCCR_EL3.PGS value */ + gpt_l1_cnt_2mb = (unsigned int)GPT_L1_ENTRY_COUNT_2MB(gpt_config.p); + + /* Mask for the L1 index field */ + gpt_l1_index_mask = GPT_L1_IDX_MASK(gpt_config.p); + +#if (RME_GPT_BITLOCK_BLOCK != 0) + /* Bitlocks at the end of L0 table */ + gpt_bitlock_base = (bitlock_t *)(gpt_config.plat_gpt_l0_base + + GPT_L0_TABLE_SIZE(gpt_config.t)); +#endif + VERBOSE("GPT: Runtime Configuration\n"); VERBOSE(" PPS/T: 0x%x/%u\n", gpt_config.pps, gpt_config.t); VERBOSE(" PGS/P: 0x%x/%u\n", gpt_config.pgs, gpt_config.p); VERBOSE(" L0GPTSZ/S: 0x%x/%u\n", GPT_L0GPTSZ, GPT_S_VAL); - VERBOSE(" L0 base: 0x%lx\n", gpt_config.plat_gpt_l0_base); - + VERBOSE(" L0 base: 0x%"PRIxPTR"\n", gpt_config.plat_gpt_l0_base); +#if (RME_GPT_BITLOCK_BLOCK != 0) + VERBOSE(" Bitlocks: 0x%"PRIxPTR"\n", (uintptr_t)gpt_bitlock_base); +#endif return 0; } -/* - * The L1 descriptors are protected by a spinlock to ensure that multiple - * CPUs do not attempt to change the descriptors at once. In the future it - * would be better to have separate spinlocks for each L1 descriptor. - */ -static spinlock_t gpt_lock; - /* * A helper to write the value (target_pas << gpi_shift) to the index of - * the gpt_l1_addr + * the gpt_l1_addr. */ static inline void write_gpt(uint64_t *gpt_l1_desc, uint64_t *gpt_l1_addr, unsigned int gpi_shift, unsigned int idx, @@ -971,39 +1360,334 @@ static inline void write_gpt(uint64_t *gpt_l1_desc, uint64_t *gpt_l1_addr, *gpt_l1_desc &= ~(GPT_L1_GRAN_DESC_GPI_MASK << gpi_shift); *gpt_l1_desc |= ((uint64_t)target_pas << gpi_shift); gpt_l1_addr[idx] = *gpt_l1_desc; + + dsboshst(); } /* * Helper to retrieve the gpt_l1_* information from the base address - * returned in gpi_info + * returned in gpi_info. */ static int get_gpi_params(uint64_t base, gpi_info_t *gpi_info) { uint64_t gpt_l0_desc, *gpt_l0_base; + __unused unsigned int block_idx; gpt_l0_base = (uint64_t *)gpt_config.plat_gpt_l0_base; gpt_l0_desc = gpt_l0_base[GPT_L0_IDX(base)]; if (GPT_L0_TYPE(gpt_l0_desc) != GPT_L0_TYPE_TBL_DESC) { - VERBOSE("[GPT] Granule is not covered by a table descriptor!\n"); - VERBOSE(" Base=0x%" PRIx64 "\n", base); + VERBOSE("GPT: Granule is not covered by a table descriptor!\n"); + VERBOSE(" Base=0x%"PRIx64"\n", base); return -EINVAL; } - /* Get the table index and GPI shift from PA. */ + /* Get the table index and GPI shift from PA */ gpi_info->gpt_l1_addr = GPT_L0_TBLD_ADDR(gpt_l0_desc); - gpi_info->idx = GPT_L1_IDX(gpt_config.p, base); + gpi_info->idx = (unsigned int)GPT_L1_INDEX(base); gpi_info->gpi_shift = GPT_L1_GPI_IDX(gpt_config.p, base) << 2; - gpi_info->gpt_l1_desc = (gpi_info->gpt_l1_addr)[gpi_info->idx]; - gpi_info->gpi = (gpi_info->gpt_l1_desc >> gpi_info->gpi_shift) & - GPT_L1_GRAN_DESC_GPI_MASK; +#if (RME_GPT_BITLOCK_BLOCK != 0) + /* Block index */ + block_idx = (unsigned int)(base / (RME_GPT_BITLOCK_BLOCK * SZ_512M)); + + /* Bitlock address and mask */ + gpi_info->lock = &gpt_bitlock_base[block_idx / LOCK_BITS]; + gpi_info->mask = 1U << (block_idx & (LOCK_BITS - 1U)); +#endif return 0; } +/* + * Helper to retrieve the gpt_l1_desc and GPI information from gpi_info. + * This function is called with bitlock or spinlock acquired. + */ +static void read_gpi(gpi_info_t *gpi_info) +{ + gpi_info->gpt_l1_desc = (gpi_info->gpt_l1_addr)[gpi_info->idx]; + + if ((gpi_info->gpt_l1_desc & GPT_L1_TYPE_CONT_DESC_MASK) == + GPT_L1_TYPE_CONT_DESC) { + /* Read GPI from Contiguous descriptor */ + gpi_info->gpi = (unsigned int)GPT_L1_CONT_GPI(gpi_info->gpt_l1_desc); + } else { + /* Read GPI from Granules descriptor */ + gpi_info->gpi = (unsigned int)((gpi_info->gpt_l1_desc >> gpi_info->gpi_shift) & + GPT_L1_GRAN_DESC_GPI_MASK); + } +} + +static void flush_page_to_popa(uintptr_t addr) +{ + size_t size = GPT_PGS_ACTUAL_SIZE(gpt_config.p); + + if (is_feat_mte2_supported()) { + flush_dcache_to_popa_range_mte2(addr, size); + } else { + flush_dcache_to_popa_range(addr, size); + } +} + +/* + * Helper function to check if all L1 entries in 2MB block have + * the same Granules descriptor value. + * + * Parameters + * base Base address of the region to be checked + * gpi_info Pointer to 'gpt_config_t' structure + * l1_desc GPT Granules descriptor with all entries + * set to the same GPI. + * + * Return + * true if L1 all entries have the same descriptor value, false otherwise. + */ +__unused static bool check_fuse_2mb(uint64_t base, const gpi_info_t *gpi_info, + uint64_t l1_desc) +{ + /* Last L1 entry index in 2MB block */ + unsigned int long idx = GPT_L1_INDEX(ALIGN_2MB(base)) + + gpt_l1_cnt_2mb - 1UL; + + /* Number of L1 entries in 2MB block */ + unsigned int cnt = gpt_l1_cnt_2mb; + + /* + * Start check from the last L1 entry and continue until the first + * non-matching to the passed Granules descriptor value is found. + */ + while (cnt-- != 0U) { + if (gpi_info->gpt_l1_addr[idx--] != l1_desc) { + /* Non-matching L1 entry found */ + return false; + } + } + + return true; +} + +__unused static void fuse_2mb(uint64_t base, const gpi_info_t *gpi_info, + uint64_t l1_desc) +{ + /* L1 entry index of the start of 2MB block */ + unsigned long idx_2 = GPT_L1_INDEX(ALIGN_2MB(base)); + + /* 2MB Contiguous descriptor */ + uint64_t l1_cont_desc = GPT_L1_CONT_DESC(l1_desc, 2MB); + + VERBOSE("GPT: %s(0x%"PRIxPTR" 0x%"PRIx64")\n", __func__, base, l1_desc); + + fill_desc(&gpi_info->gpt_l1_addr[idx_2], l1_cont_desc, L1_QWORDS_2MB); +} + +/* + * Helper function to check if all 1st L1 entries of 2MB blocks + * in 32MB have the same 2MB Contiguous descriptor value. + * + * Parameters + * base Base address of the region to be checked + * gpi_info Pointer to 'gpt_config_t' structure + * l1_desc GPT Granules descriptor. + * + * Return + * true if all L1 entries have the same descriptor value, false otherwise. + */ +__unused static bool check_fuse_32mb(uint64_t base, const gpi_info_t *gpi_info, + uint64_t l1_desc) +{ + /* The 1st L1 entry index of the last 2MB block in 32MB */ + unsigned long idx = GPT_L1_INDEX(ALIGN_32MB(base)) + + (15UL * gpt_l1_cnt_2mb); + + /* 2MB Contiguous descriptor */ + uint64_t l1_cont_desc = GPT_L1_CONT_DESC(l1_desc, 2MB); + + /* Number of 2MB blocks in 32MB */ + unsigned int cnt = 16U; + + /* Set the first L1 entry to 2MB Contiguous descriptor */ + gpi_info->gpt_l1_addr[GPT_L1_INDEX(ALIGN_2MB(base))] = l1_cont_desc; + + /* + * Start check from the 1st L1 entry of the last 2MB block and + * continue until the first non-matching to 2MB Contiguous descriptor + * value is found. + */ + while (cnt-- != 0U) { + if (gpi_info->gpt_l1_addr[idx] != l1_cont_desc) { + /* Non-matching L1 entry found */ + return false; + } + idx -= gpt_l1_cnt_2mb; + } + + return true; +} + +__unused static void fuse_32mb(uint64_t base, const gpi_info_t *gpi_info, + uint64_t l1_desc) +{ + /* L1 entry index of the start of 32MB block */ + unsigned long idx_32 = GPT_L1_INDEX(ALIGN_32MB(base)); + + /* 32MB Contiguous descriptor */ + uint64_t l1_cont_desc = GPT_L1_CONT_DESC(l1_desc, 32MB); + + VERBOSE("GPT: %s(0x%"PRIxPTR" 0x%"PRIx64")\n", __func__, base, l1_desc); + + fill_desc(&gpi_info->gpt_l1_addr[idx_32], l1_cont_desc, L1_QWORDS_32MB); +} + +/* + * Helper function to check if all 1st L1 entries of 32MB blocks + * in 512MB have the same 32MB Contiguous descriptor value. + * + * Parameters + * base Base address of the region to be checked + * gpi_info Pointer to 'gpt_config_t' structure + * l1_desc GPT Granules descriptor. + * + * Return + * true if all L1 entries have the same descriptor value, false otherwise. + */ +__unused static bool check_fuse_512mb(uint64_t base, const gpi_info_t *gpi_info, + uint64_t l1_desc) +{ + /* The 1st L1 entry index of the last 32MB block in 512MB */ + unsigned long idx = GPT_L1_INDEX(ALIGN_512MB(base)) + + (15UL * 16UL * gpt_l1_cnt_2mb); + + /* 32MB Contiguous descriptor */ + uint64_t l1_cont_desc = GPT_L1_CONT_DESC(l1_desc, 32MB); + + /* Number of 32MB blocks in 512MB */ + unsigned int cnt = 16U; + + /* Set the first L1 entry to 2MB Contiguous descriptor */ + gpi_info->gpt_l1_addr[GPT_L1_INDEX(ALIGN_32MB(base))] = l1_cont_desc; + + /* + * Start check from the 1st L1 entry of the last 32MB block and + * continue until the first non-matching to 32MB Contiguous descriptor + * value is found. + */ + while (cnt-- != 0U) { + if (gpi_info->gpt_l1_addr[idx] != l1_cont_desc) { + /* Non-matching L1 entry found */ + return false; + } + idx -= 16UL * gpt_l1_cnt_2mb; + } + + return true; +} + +__unused static void fuse_512mb(uint64_t base, const gpi_info_t *gpi_info, + uint64_t l1_desc) +{ + /* L1 entry index of the start of 512MB block */ + unsigned long idx_512 = GPT_L1_INDEX(ALIGN_512MB(base)); + + /* 512MB Contiguous descriptor */ + uint64_t l1_cont_desc = GPT_L1_CONT_DESC(l1_desc, 512MB); + + VERBOSE("GPT: %s(0x%"PRIxPTR" 0x%"PRIx64")\n", __func__, base, l1_desc); + + fill_desc(&gpi_info->gpt_l1_addr[idx_512], l1_cont_desc, L1_QWORDS_512MB); +} + +/* + * Helper function to convert GPI entries in a single L1 table + * from Granules to Contiguous descriptor. + * + * Parameters + * base Base address of the region to be written + * gpi_info Pointer to 'gpt_config_t' structure + * l1_desc GPT Granules descriptor with all entries + * set to the same GPI. + */ +__unused static void fuse_block(uint64_t base, const gpi_info_t *gpi_info, + uint64_t l1_desc) +{ + /* Start with check for 2MB block */ + if (!check_fuse_2mb(base, gpi_info, l1_desc)) { + /* Check for 2MB fusing failed */ + return; + } + +#if (RME_GPT_MAX_BLOCK == 2) + fuse_2mb(base, gpi_info, l1_desc); +#else + /* Check for 32MB block */ + if (!check_fuse_32mb(base, gpi_info, l1_desc)) { + /* Check for 32MB fusing failed, fuse to 2MB */ + fuse_2mb(base, gpi_info, l1_desc); + return; + } + +#if (RME_GPT_MAX_BLOCK == 32) + fuse_32mb(base, gpi_info, l1_desc); +#else + /* Check for 512MB block */ + if (!check_fuse_512mb(base, gpi_info, l1_desc)) { + /* Check for 512MB fusing failed, fuse to 32MB */ + fuse_32mb(base, gpi_info, l1_desc); + return; + } + + /* Fuse to 512MB */ + fuse_512mb(base, gpi_info, l1_desc); + +#endif /* RME_GPT_MAX_BLOCK == 32 */ +#endif /* RME_GPT_MAX_BLOCK == 2 */ +} + +/* + * Helper function to convert GPI entries in a single L1 table + * from Contiguous to Granules descriptor. This function updates + * descriptor to Granules in passed 'gpt_config_t' structure as + * the result of shuttering. + * + * Parameters + * base Base address of the region to be written + * gpi_info Pointer to 'gpt_config_t' structure + * l1_desc GPT Granules descriptor set this range to. + */ +__unused static void shatter_block(uint64_t base, gpi_info_t *gpi_info, + uint64_t l1_desc) +{ + /* Look-up table for 2MB, 32MB and 512MB locks shattering */ + static const gpt_shatter_func gpt_shatter_lookup[] = { + shatter_2mb, + shatter_32mb, + shatter_512mb + }; + + /* Look-up table for invalidation TLBs for 2MB, 32MB and 512MB blocks */ + static const gpt_tlbi_lookup_t tlbi_lookup[] = { + { tlbirpalos_2m, ~(SZ_2M - 1UL) }, + { tlbirpalos_32m, ~(SZ_32M - 1UL) }, + { tlbirpalos_512m, ~(SZ_512M - 1UL) } + }; + + /* Get shattering level from Contig field of Contiguous descriptor */ + unsigned long level = GPT_L1_CONT_CONTIG(gpi_info->gpt_l1_desc) - 1UL; + + /* Shatter contiguous block */ + gpt_shatter_lookup[level](base, gpi_info, l1_desc); + + tlbi_lookup[level].function(base & tlbi_lookup[level].mask); + dsbosh(); + + /* + * Update 'gpt_config_t' structure's descriptor to Granules to reflect + * the shattered GPI back to caller. + */ + gpi_info->gpt_l1_desc = l1_desc; +} + /* * This function is the granule transition delegate service. When a granule * transition request occurs it is routed to this function to have the request, - * if valid, fulfilled following A1.1.1 Delegate of RME supplement + * if valid, fulfilled following A1.1.1 Delegate of RME supplement. * * TODO: implement support for transitioning multiple granules at once. * @@ -1020,104 +1704,120 @@ static int get_gpi_params(uint64_t base, gpi_info_t *gpi_info) int gpt_delegate_pas(uint64_t base, size_t size, unsigned int src_sec_state) { gpi_info_t gpi_info; - uint64_t nse; - int res; + uint64_t nse, __unused l1_desc; unsigned int target_pas; + int res; - /* Ensure that the tables have been set up before taking requests. */ + /* Ensure that the tables have been set up before taking requests */ assert(gpt_config.plat_gpt_l0_base != 0UL); - /* Ensure that caches are enabled. */ + /* Ensure that caches are enabled */ assert((read_sctlr_el3() & SCTLR_C_BIT) != 0UL); - /* Delegate request can only come from REALM or SECURE */ - assert(src_sec_state == SMC_FROM_REALM || - src_sec_state == SMC_FROM_SECURE); - - /* See if this is a single or a range of granule transition. */ + /* See if this is a single or a range of granule transition */ if (size != GPT_PGS_ACTUAL_SIZE(gpt_config.p)) { return -EINVAL; } /* Check that base and size are valid */ if ((ULONG_MAX - base) < size) { - VERBOSE("[GPT] Transition request address overflow!\n"); - VERBOSE(" Base=0x%" PRIx64 "\n", base); + VERBOSE("GPT: Transition request address overflow!\n"); + VERBOSE(" Base=0x%"PRIx64"\n", base); VERBOSE(" Size=0x%lx\n", size); return -EINVAL; } - /* Make sure base and size are valid. */ - if (((base & (GPT_PGS_ACTUAL_SIZE(gpt_config.p) - 1)) != 0UL) || - ((size & (GPT_PGS_ACTUAL_SIZE(gpt_config.p) - 1)) != 0UL) || + /* Make sure base and size are valid */ + if (((base & (GPT_PGS_ACTUAL_SIZE(gpt_config.p) - 1UL)) != 0UL) || + ((size & (GPT_PGS_ACTUAL_SIZE(gpt_config.p) - 1UL)) != 0UL) || (size == 0UL) || ((base + size) >= GPT_PPS_ACTUAL_SIZE(gpt_config.t))) { - VERBOSE("[GPT] Invalid granule transition address range!\n"); - VERBOSE(" Base=0x%" PRIx64 "\n", base); + VERBOSE("GPT: Invalid granule transition address range!\n"); + VERBOSE(" Base=0x%"PRIx64"\n", base); VERBOSE(" Size=0x%lx\n", size); return -EINVAL; } - target_pas = GPT_GPI_REALM; - if (src_sec_state == SMC_FROM_SECURE) { + /* Delegate request can only come from REALM or SECURE */ + if ((src_sec_state != SMC_FROM_REALM) && + (src_sec_state != SMC_FROM_SECURE)) { + VERBOSE("GPT: Invalid caller security state 0x%x\n", + src_sec_state); + return -EINVAL; + } + + if (src_sec_state == SMC_FROM_REALM) { + target_pas = GPT_GPI_REALM; + nse = (uint64_t)GPT_NSE_REALM << GPT_NSE_SHIFT; + l1_desc = GPT_L1_REALM_DESC; + } else { target_pas = GPT_GPI_SECURE; + nse = (uint64_t)GPT_NSE_SECURE << GPT_NSE_SHIFT; + l1_desc = GPT_L1_SECURE_DESC; } - /* - * Access to L1 tables is controlled by a global lock to ensure - * that no more than one CPU is allowed to make changes at any - * given time. - */ - spin_lock(&gpt_lock); res = get_gpi_params(base, &gpi_info); if (res != 0) { - spin_unlock(&gpt_lock); return res; } + /* + * Access to GPT is controlled by a lock to ensure that no more + * than one CPU is allowed to make changes at any given time. + */ + GPT_LOCK; + read_gpi(&gpi_info); + /* Check that the current address is in NS state */ if (gpi_info.gpi != GPT_GPI_NS) { - VERBOSE("[GPT] Only Granule in NS state can be delegated.\n"); + VERBOSE("GPT: Only Granule in NS state can be delegated.\n"); VERBOSE(" Caller: %u, Current GPI: %u\n", src_sec_state, gpi_info.gpi); - spin_unlock(&gpt_lock); + GPT_UNLOCK; return -EPERM; } - if (src_sec_state == SMC_FROM_SECURE) { - nse = (uint64_t)GPT_NSE_SECURE << GPT_NSE_SHIFT; - } else { - nse = (uint64_t)GPT_NSE_REALM << GPT_NSE_SHIFT; +#if (RME_GPT_MAX_BLOCK != 0) + /* Check for Contiguous descriptor */ + if ((gpi_info.gpt_l1_desc & GPT_L1_TYPE_CONT_DESC_MASK) == + GPT_L1_TYPE_CONT_DESC) { + shatter_block(base, &gpi_info, GPT_L1_NS_DESC); } - +#endif /* * In order to maintain mutual distrust between Realm and Secure * states, remove any data speculatively fetched into the target - * physical address space. Issue DC CIPAPA over address range + * physical address space. + * Issue DC CIPAPA or DC_CIGDPAPA on implementations with FEAT_MTE2. */ - flush_dcache_to_popa_range(nse | base, - GPT_PGS_ACTUAL_SIZE(gpt_config.p)); + flush_page_to_popa(base | nse); write_gpt(&gpi_info.gpt_l1_desc, gpi_info.gpt_l1_addr, gpi_info.gpi_shift, gpi_info.idx, target_pas); - dsboshst(); - gpt_tlbi_by_pa_ll(base, GPT_PGS_ACTUAL_SIZE(gpt_config.p)); - dsbosh(); + /* Ensure that all agents observe the new configuration */ + tlbi_page_dsbosh(base); nse = (uint64_t)GPT_NSE_NS << GPT_NSE_SHIFT; - flush_dcache_to_popa_range(nse | base, - GPT_PGS_ACTUAL_SIZE(gpt_config.p)); + /* Ensure that the scrubbed data have made it past the PoPA */ + flush_page_to_popa(base | nse); + +#if (RME_GPT_MAX_BLOCK != 0) + if (gpi_info.gpt_l1_desc == l1_desc) { + /* Try to fuse */ + fuse_block(base, &gpi_info, l1_desc); + } +#endif - /* Unlock access to the L1 tables. */ - spin_unlock(&gpt_lock); + /* Unlock the lock to GPT */ + GPT_UNLOCK; /* * The isb() will be done as part of context - * synchronization when returning to lower EL + * synchronization when returning to lower EL. */ - VERBOSE("[GPT] Granule 0x%" PRIx64 ", GPI 0x%x->0x%x\n", + VERBOSE("GPT: Granule 0x%"PRIx64" GPI 0x%x->0x%x\n", base, gpi_info.gpi, target_pas); return 0; @@ -1143,117 +1843,119 @@ int gpt_delegate_pas(uint64_t base, size_t size, unsigned int src_sec_state) int gpt_undelegate_pas(uint64_t base, size_t size, unsigned int src_sec_state) { gpi_info_t gpi_info; - uint64_t nse; + uint64_t nse, __unused l1_desc; int res; - /* Ensure that the tables have been set up before taking requests. */ + /* Ensure that the tables have been set up before taking requests */ assert(gpt_config.plat_gpt_l0_base != 0UL); - /* Ensure that MMU and caches are enabled. */ + /* Ensure that MMU and caches are enabled */ assert((read_sctlr_el3() & SCTLR_C_BIT) != 0UL); - /* Delegate request can only come from REALM or SECURE */ - assert(src_sec_state == SMC_FROM_REALM || - src_sec_state == SMC_FROM_SECURE); - - /* See if this is a single or a range of granule transition. */ + /* See if this is a single or a range of granule transition */ if (size != GPT_PGS_ACTUAL_SIZE(gpt_config.p)) { return -EINVAL; } /* Check that base and size are valid */ if ((ULONG_MAX - base) < size) { - VERBOSE("[GPT] Transition request address overflow!\n"); - VERBOSE(" Base=0x%" PRIx64 "\n", base); + VERBOSE("GPT: Transition request address overflow!\n"); + VERBOSE(" Base=0x%"PRIx64"\n", base); VERBOSE(" Size=0x%lx\n", size); return -EINVAL; } - /* Make sure base and size are valid. */ - if (((base & (GPT_PGS_ACTUAL_SIZE(gpt_config.p) - 1)) != 0UL) || - ((size & (GPT_PGS_ACTUAL_SIZE(gpt_config.p) - 1)) != 0UL) || + /* Make sure base and size are valid */ + if (((base & (GPT_PGS_ACTUAL_SIZE(gpt_config.p) - 1UL)) != 0UL) || + ((size & (GPT_PGS_ACTUAL_SIZE(gpt_config.p) - 1UL)) != 0UL) || (size == 0UL) || ((base + size) >= GPT_PPS_ACTUAL_SIZE(gpt_config.t))) { - VERBOSE("[GPT] Invalid granule transition address range!\n"); - VERBOSE(" Base=0x%" PRIx64 "\n", base); + VERBOSE("GPT: Invalid granule transition address range!\n"); + VERBOSE(" Base=0x%"PRIx64"\n", base); VERBOSE(" Size=0x%lx\n", size); return -EINVAL; } - /* - * Access to L1 tables is controlled by a global lock to ensure - * that no more than one CPU is allowed to make changes at any - * given time. - */ - spin_lock(&gpt_lock); - res = get_gpi_params(base, &gpi_info); if (res != 0) { - spin_unlock(&gpt_lock); return res; } + /* + * Access to GPT is controlled by a lock to ensure that no more + * than one CPU is allowed to make changes at any given time. + */ + GPT_LOCK; + read_gpi(&gpi_info); + /* Check that the current address is in the delegated state */ - if ((src_sec_state == SMC_FROM_REALM && - gpi_info.gpi != GPT_GPI_REALM) || - (src_sec_state == SMC_FROM_SECURE && - gpi_info.gpi != GPT_GPI_SECURE)) { - VERBOSE("[GPT] Only Granule in REALM or SECURE state can be undelegated.\n"); - VERBOSE(" Caller: %u, Current GPI: %u\n", src_sec_state, + if ((src_sec_state == SMC_FROM_REALM) && + (gpi_info.gpi == GPT_GPI_REALM)) { + l1_desc = GPT_L1_REALM_DESC; + nse = (uint64_t)GPT_NSE_REALM << GPT_NSE_SHIFT; + } else if ((src_sec_state == SMC_FROM_SECURE) && + (gpi_info.gpi == GPT_GPI_SECURE)) { + l1_desc = GPT_L1_SECURE_DESC; + nse = (uint64_t)GPT_NSE_SECURE << GPT_NSE_SHIFT; + } else { + VERBOSE("GPT: Only Granule in REALM or SECURE state can be undelegated\n"); + VERBOSE(" Caller: %u Current GPI: %u\n", src_sec_state, gpi_info.gpi); - spin_unlock(&gpt_lock); + GPT_UNLOCK; return -EPERM; } - - /* In order to maintain mutual distrust between Realm and Secure +#if (RME_GPT_MAX_BLOCK != 0) + /* Check for Contiguous descriptor */ + if ((gpi_info.gpt_l1_desc & GPT_L1_TYPE_CONT_DESC_MASK) == + GPT_L1_TYPE_CONT_DESC) { + shatter_block(base, &gpi_info, l1_desc); + } +#endif + /* + * In order to maintain mutual distrust between Realm and Secure * states, remove access now, in order to guarantee that writes * to the currently-accessible physical address space will not * later become observable. */ write_gpt(&gpi_info.gpt_l1_desc, gpi_info.gpt_l1_addr, gpi_info.gpi_shift, gpi_info.idx, GPT_GPI_NO_ACCESS); - dsboshst(); - gpt_tlbi_by_pa_ll(base, GPT_PGS_ACTUAL_SIZE(gpt_config.p)); - dsbosh(); - - if (src_sec_state == SMC_FROM_SECURE) { - nse = (uint64_t)GPT_NSE_SECURE << GPT_NSE_SHIFT; - } else { - nse = (uint64_t)GPT_NSE_REALM << GPT_NSE_SHIFT; - } + /* Ensure that all agents observe the new NO_ACCESS configuration */ + tlbi_page_dsbosh(base); - /* Ensure that the scrubbed data has made it past the PoPA */ - flush_dcache_to_popa_range(nse | base, - GPT_PGS_ACTUAL_SIZE(gpt_config.p)); + /* Ensure that the scrubbed data have made it past the PoPA */ + flush_page_to_popa(base | nse); /* - * Remove any data loaded speculatively - * in NS space from before the scrubbing + * Remove any data loaded speculatively in NS space from before + * the scrubbing. */ nse = (uint64_t)GPT_NSE_NS << GPT_NSE_SHIFT; - flush_dcache_to_popa_range(nse | base, - GPT_PGS_ACTUAL_SIZE(gpt_config.p)); + flush_page_to_popa(base | nse); - /* Clear existing GPI encoding and transition granule. */ + /* Clear existing GPI encoding and transition granule */ write_gpt(&gpi_info.gpt_l1_desc, gpi_info.gpt_l1_addr, gpi_info.gpi_shift, gpi_info.idx, GPT_GPI_NS); - dsboshst(); /* Ensure that all agents observe the new NS configuration */ - gpt_tlbi_by_pa_ll(base, GPT_PGS_ACTUAL_SIZE(gpt_config.p)); - dsbosh(); + tlbi_page_dsbosh(base); - /* Unlock access to the L1 tables. */ - spin_unlock(&gpt_lock); +#if (RME_GPT_MAX_BLOCK != 0) + if (gpi_info.gpt_l1_desc == GPT_L1_NS_DESC) { + /* Try to fuse */ + fuse_block(base, &gpi_info, GPT_L1_NS_DESC); + } +#endif + /* Unlock the lock to GPT */ + GPT_UNLOCK; /* * The isb() will be done as part of context - * synchronization when returning to lower EL + * synchronization when returning to lower EL. */ - VERBOSE("[GPT] Granule 0x%" PRIx64 ", GPI 0x%x->0x%x\n", + VERBOSE("GPT: Granule 0x%"PRIx64" GPI 0x%x->0x%x\n", base, gpi_info.gpi, GPT_GPI_NS); return 0; diff --git a/lib/gpt_rme/gpt_rme.mk b/lib/gpt_rme/gpt_rme.mk index 60176f4e..7d6b61f9 100644 --- a/lib/gpt_rme/gpt_rme.mk +++ b/lib/gpt_rme/gpt_rme.mk @@ -1,8 +1,22 @@ # -# Copyright (c) 2021, Arm Limited. All rights reserved. +# Copyright (c) 2021-2024, Arm Limited. All rights reserved. # # SPDX-License-Identifier: BSD-3-Clause # +# Process RME_GPT_BITLOCK_BLOCK value +ifeq ($(filter 0 1 2 4 8 16 32 64 128 256 512, ${RME_GPT_BITLOCK_BLOCK}),) + $(error "Invalid value for RME_GPT_BITLOCK_BLOCK: ${RME_GPT_BITLOCK_BLOCK}") +endif + +ifeq (${RME_GPT_BITLOCK_BLOCK},0) + $(warning "GPT library uses global spinlock") +endif + +# Process RME_GPT_MAX_BLOCK value +ifeq ($(filter 0 2 32 512, ${RME_GPT_MAX_BLOCK}),) + $(error "Invalid value for RME_GPT_MAX_BLOCK: ${RME_GPT_MAX_BLOCK}") +endif + GPT_LIB_SRCS := $(addprefix lib/gpt_rme/, \ gpt_rme.c) diff --git a/lib/gpt_rme/gpt_rme_private.h b/lib/gpt_rme/gpt_rme_private.h index 3c817f3e..31dad20c 100644 --- a/lib/gpt_rme/gpt_rme_private.h +++ b/lib/gpt_rme/gpt_rme_private.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, Arm Limited. All rights reserved. + * Copyright (c) 2022-2024, Arm Limited. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -9,19 +9,20 @@ #include <arch.h> #include <lib/gpt_rme/gpt_rme.h> +#include <lib/spinlock.h> #include <lib/utils_def.h> /******************************************************************************/ /* GPT descriptor definitions */ /******************************************************************************/ -/* GPT level 0 descriptor bit definitions. */ +/* GPT level 0 descriptor bit definitions */ #define GPT_L0_TYPE_MASK UL(0xF) #define GPT_L0_TYPE_SHIFT U(0) -/* For now, we don't support contiguous descriptors, only table and block. */ -#define GPT_L0_TYPE_TBL_DESC UL(0x3) -#define GPT_L0_TYPE_BLK_DESC UL(0x1) +/* GPT level 0 table and block descriptors */ +#define GPT_L0_TYPE_TBL_DESC UL(3) +#define GPT_L0_TYPE_BLK_DESC UL(1) #define GPT_L0_TBL_DESC_L1ADDR_MASK UL(0xFFFFFFFFFF) #define GPT_L0_TBL_DESC_L1ADDR_SHIFT U(12) @@ -29,35 +30,69 @@ #define GPT_L0_BLK_DESC_GPI_MASK UL(0xF) #define GPT_L0_BLK_DESC_GPI_SHIFT U(4) -/* GPT level 1 descriptor bit definitions */ +/* GPT level 1 Contiguous descriptor */ +#define GPT_L1_TYPE_CONT_DESC_MASK UL(0xF) +#define GPT_L1_TYPE_CONT_DESC UL(1) + +/* GPT level 1 Contiguous descriptor definitions */ +#define GPT_L1_CONTIG_2MB UL(1) +#define GPT_L1_CONTIG_32MB UL(2) +#define GPT_L1_CONTIG_512MB UL(3) + +#define GPT_L1_CONT_DESC_GPI_SHIFT U(4) +#define GPT_L1_CONT_DESC_GPI_MASK UL(0xF) +#define GPT_L1_CONT_DESC_CONTIG_SHIFT U(8) +#define GPT_L1_CONT_DESC_CONTIG_MASK UL(3) + +/* GPT level 1 Granules descriptor bit definitions */ #define GPT_L1_GRAN_DESC_GPI_MASK UL(0xF) +/* L1 Contiguous descriptors templates */ +#define GPT_L1_CONT_DESC_2MB \ + (GPT_L1_TYPE_CONT_DESC | \ + (GPT_L1_CONTIG_2MB << GPT_L1_CONT_DESC_CONTIG_SHIFT)) +#define GPT_L1_CONT_DESC_32MB \ + (GPT_L1_TYPE_CONT_DESC | \ + (GPT_L1_CONTIG_32MB << GPT_L1_CONT_DESC_CONTIG_SHIFT)) +#define GPT_L1_CONT_DESC_512MB \ + (GPT_L1_TYPE_CONT_DESC | \ + (GPT_L1_CONTIG_512MB << GPT_L1_CONT_DESC_CONTIG_SHIFT)) + +/* Create L1 Contiguous descriptor from GPI and template */ +#define GPT_L1_GPI_CONT_DESC(_gpi, _desc) \ + ((_desc) | ((uint64_t)(_gpi) << GPT_L1_CONT_DESC_GPI_SHIFT)) + +/* Create L1 Contiguous descriptor from Granules descriptor and size */ +#define GPT_L1_CONT_DESC(_desc, _size) \ + (GPT_L1_CONT_DESC_##_size | \ + (((_desc) & GPT_L1_GRAN_DESC_GPI_MASK) << \ + GPT_L1_CONT_DESC_GPI_SHIFT)) + +/* Create L1 Contiguous descriptor from GPI and size */ +#define GPT_L1_CONT_DESC_SIZE(_gpi, _size) \ + (GPT_L1_CONT_DESC_##_size | \ + (((uint64_t)(_gpi) << GPT_L1_CONT_DESC_GPI_SHIFT)) + +#define GPT_L1_GPI_BYTE(_gpi) (uint64_t)((_gpi) | ((_gpi) << 4)) +#define GPT_L1_GPI_HALF(_gpi) (GPT_L1_GPI_BYTE(_gpi) | (GPT_L1_GPI_BYTE(_gpi) << 8)) +#define GPT_L1_GPI_WORD(_gpi) (GPT_L1_GPI_HALF(_gpi) | (GPT_L1_GPI_HALF(_gpi) << 16)) + /* - * This macro fills out every GPI entry in a granules descriptor to the same - * value. + * This macro generates a Granules descriptor + * with the same value for every GPI entry. */ -#define GPT_BUILD_L1_DESC(_gpi) (((uint64_t)(_gpi) << 4*0) | \ - ((uint64_t)(_gpi) << 4*1) | \ - ((uint64_t)(_gpi) << 4*2) | \ - ((uint64_t)(_gpi) << 4*3) | \ - ((uint64_t)(_gpi) << 4*4) | \ - ((uint64_t)(_gpi) << 4*5) | \ - ((uint64_t)(_gpi) << 4*6) | \ - ((uint64_t)(_gpi) << 4*7) | \ - ((uint64_t)(_gpi) << 4*8) | \ - ((uint64_t)(_gpi) << 4*9) | \ - ((uint64_t)(_gpi) << 4*10) | \ - ((uint64_t)(_gpi) << 4*11) | \ - ((uint64_t)(_gpi) << 4*12) | \ - ((uint64_t)(_gpi) << 4*13) | \ - ((uint64_t)(_gpi) << 4*14) | \ - ((uint64_t)(_gpi) << 4*15)) +#define GPT_BUILD_L1_DESC(_gpi) (GPT_L1_GPI_WORD(_gpi) | (GPT_L1_GPI_WORD(_gpi) << 32)) + +#define GPT_L1_SECURE_DESC GPT_BUILD_L1_DESC(GPT_GPI_SECURE) +#define GPT_L1_NS_DESC GPT_BUILD_L1_DESC(GPT_GPI_NS) +#define GPT_L1_REALM_DESC GPT_BUILD_L1_DESC(GPT_GPI_REALM) +#define GPT_L1_ANY_DESC GPT_BUILD_L1_DESC(GPT_GPI_ANY) /******************************************************************************/ /* GPT platform configuration */ /******************************************************************************/ -/* This value comes from GPCCR_EL3 so no externally supplied definition. */ +/* This value comes from GPCCR_EL3 so no externally supplied definition */ #define GPT_L0GPTSZ ((unsigned int)((read_gpccr_el3() >> \ GPCCR_L0GPTSZ_SHIFT) & GPCCR_L0GPTSZ_MASK)) @@ -106,21 +141,50 @@ typedef enum { PGS_64KB_P = 16U } gpt_p_val_e; +#define LOCK_SIZE sizeof(((bitlock_t *)NULL)->lock) +#define LOCK_TYPE typeof(((bitlock_t *)NULL)->lock) +#define LOCK_BITS (LOCK_SIZE * 8U) + /* - * Internal structure to retrieve the values from get_gpi_info(); + * Internal structure to retrieve the values from get_gpi_params(); */ -typedef struct gpi_info { +typedef struct { uint64_t gpt_l1_desc; uint64_t *gpt_l1_addr; unsigned int idx; unsigned int gpi_shift; unsigned int gpi; +#if (RME_GPT_BITLOCK_BLOCK != 0) + bitlock_t *lock; + LOCK_TYPE mask; +#endif } gpi_info_t; -/* Max valid value for PGS. */ +/* + * Look up structure for contiguous blocks and descriptors + */ +typedef struct { + size_t size; + unsigned int desc; +} gpt_fill_lookup_t; + +typedef void (*gpt_shatter_func)(uintptr_t base, const gpi_info_t *gpi_info, + uint64_t l1_desc); +typedef void (*gpt_tlbi_func)(uintptr_t base); + +/* + * Look-up structure for + * invalidating TLBs of GPT entries by Physical address, last level. + */ +typedef struct { + gpt_tlbi_func function; + size_t mask; +} gpt_tlbi_lookup_t; + +/* Max valid value for PGS */ #define GPT_PGS_MAX (2U) -/* Max valid value for PPS. */ +/* Max valid value for PPS */ #define GPT_PPS_MAX (6U) /******************************************************************************/ @@ -136,10 +200,10 @@ typedef struct gpi_info { * special case we'll get a negative width value which does not make sense and * would cause problems. */ -#define GPT_L0_IDX_WIDTH(_t) (((_t) > GPT_S_VAL) ? \ - ((_t) - GPT_S_VAL) : (0U)) +#define GPT_L0_IDX_WIDTH(_t) (((unsigned int)(_t) > GPT_S_VAL) ? \ + ((unsigned int)(_t) - GPT_S_VAL) : (0U)) -/* Bit shift for the L0 index field in a PA. */ +/* Bit shift for the L0 index field in a PA */ #define GPT_L0_IDX_SHIFT (GPT_S_VAL) /* @@ -153,13 +217,13 @@ typedef struct gpi_info { #define GPT_L0_IDX_MASK(_t) (0x3FFFFFUL >> (22U - \ (GPT_L0_IDX_WIDTH(_t)))) -/* Total number of L0 regions. */ +/* Total number of L0 regions */ #define GPT_L0_REGION_COUNT(_t) ((GPT_L0_IDX_MASK(_t)) + 1U) -/* Total size of each GPT L0 region in bytes. */ +/* Total size of each GPT L0 region in bytes */ #define GPT_L0_REGION_SIZE (1UL << (GPT_L0_IDX_SHIFT)) -/* Total size in bytes of the whole L0 table. */ +/* Total size in bytes of the whole L0 table */ #define GPT_L0_TABLE_SIZE(_t) ((GPT_L0_REGION_COUNT(_t)) << 3U) /******************************************************************************/ @@ -173,89 +237,124 @@ typedef struct gpi_info { * the L0 index field above since all valid combinations of PGS (p) and L0GPTSZ * (s) will result in a positive width value. */ -#define GPT_L1_IDX_WIDTH(_p) ((GPT_S_VAL - 1U) - ((_p) + 3U)) +#define GPT_L1_IDX_WIDTH(_p) ((GPT_S_VAL - 1U) - \ + ((unsigned int)(_p) + 3U)) -/* Bit shift for the L1 index field. */ -#define GPT_L1_IDX_SHIFT(_p) ((_p) + 4U) +/* Bit shift for the L1 index field */ +#define GPT_L1_IDX_SHIFT(_p) ((unsigned int)(_p) + 4U) /* * Mask for the L1 index field, must be shifted. * * The value 0x7FFFFF is 23 bits wide and is the maximum possible width of the * L1 index within a physical address. It is calculated by - * ((s_max - 1) - (p_min + 4) + 1) where s_max is 39 for 512gb, the largest + * ((s_max - 1) - (p_min + 4) + 1) where s_max is 39 for 512GB, the largest * L0GPTSZ, and p_min is 12 for 4KB granules, the smallest PGS. */ #define GPT_L1_IDX_MASK(_p) (0x7FFFFFUL >> (23U - \ (GPT_L1_IDX_WIDTH(_p)))) -/* Bit shift for the index of the L1 GPI in a PA. */ +/* Bit shift for the index of the L1 GPI in a PA */ #define GPT_L1_GPI_IDX_SHIFT(_p) (_p) -/* Mask for the index of the L1 GPI in a PA. */ +/* Mask for the index of the L1 GPI in a PA */ #define GPT_L1_GPI_IDX_MASK (0xF) -/* Total number of entries in each L1 table. */ -#define GPT_L1_ENTRY_COUNT(_p) ((GPT_L1_IDX_MASK(_p)) + 1U) +/* Total number of entries in each L1 table */ +#define GPT_L1_ENTRY_COUNT(_p) ((GPT_L1_IDX_MASK(_p)) + 1UL) + +/* Number of L1 entries in 2MB block */ +#define GPT_L1_ENTRY_COUNT_2MB(_p) (SZ_2M >> GPT_L1_IDX_SHIFT(_p)) -/* Total size in bytes of each L1 table. */ +/* Total size in bytes of each L1 table */ #define GPT_L1_TABLE_SIZE(_p) ((GPT_L1_ENTRY_COUNT(_p)) << 3U) /******************************************************************************/ /* General helper macros */ /******************************************************************************/ -/* Protected space actual size in bytes. */ -#define GPT_PPS_ACTUAL_SIZE(_t) (1UL << (_t)) +/* Protected space actual size in bytes */ +#define GPT_PPS_ACTUAL_SIZE(_t) (1UL << (unsigned int)(_t)) -/* Granule actual size in bytes. */ -#define GPT_PGS_ACTUAL_SIZE(_p) (1UL << (_p)) +/* Granule actual size in bytes */ +#define GPT_PGS_ACTUAL_SIZE(_p) (1UL << (unsigned int)(_p)) -/* L0 GPT region size in bytes. */ +/* Number of granules in 2MB block */ +#define GPT_PGS_COUNT_2MB(_p) (1UL << (21U - (unsigned int)(_p))) + +/* L0 GPT region size in bytes */ #define GPT_L0GPTSZ_ACTUAL_SIZE (1UL << GPT_S_VAL) -/* Get the index of the L0 entry from a physical address. */ +/* Get the index of the L0 entry from a physical address */ #define GPT_L0_IDX(_pa) ((_pa) >> GPT_L0_IDX_SHIFT) /* * This definition is used to determine if a physical address lies on an L0 * region boundary. */ -#define GPT_IS_L0_ALIGNED(_pa) (((_pa) & (GPT_L0_REGION_SIZE - U(1))) == U(0)) +#define GPT_IS_L0_ALIGNED(_pa) \ + (((_pa) & (GPT_L0_REGION_SIZE - UL(1))) == UL(0)) -/* Get the type field from an L0 descriptor. */ +/* Get the type field from an L0 descriptor */ #define GPT_L0_TYPE(_desc) (((_desc) >> GPT_L0_TYPE_SHIFT) & \ GPT_L0_TYPE_MASK) -/* Create an L0 block descriptor. */ +/* Create an L0 block descriptor */ #define GPT_L0_BLK_DESC(_gpi) (GPT_L0_TYPE_BLK_DESC | \ (((_gpi) & GPT_L0_BLK_DESC_GPI_MASK) << \ GPT_L0_BLK_DESC_GPI_SHIFT)) -/* Create an L0 table descriptor with an L1 table address. */ +/* Create an L0 table descriptor with an L1 table address */ #define GPT_L0_TBL_DESC(_pa) (GPT_L0_TYPE_TBL_DESC | ((uint64_t)(_pa) & \ (GPT_L0_TBL_DESC_L1ADDR_MASK << \ GPT_L0_TBL_DESC_L1ADDR_SHIFT))) -/* Get the GPI from an L0 block descriptor. */ +/* Get the GPI from an L0 block descriptor */ #define GPT_L0_BLKD_GPI(_desc) (((_desc) >> GPT_L0_BLK_DESC_GPI_SHIFT) & \ GPT_L0_BLK_DESC_GPI_MASK) -/* Get the L1 address from an L0 table descriptor. */ +/* Get the L1 address from an L0 table descriptor */ #define GPT_L0_TBLD_ADDR(_desc) ((uint64_t *)(((_desc) & \ (GPT_L0_TBL_DESC_L1ADDR_MASK << \ GPT_L0_TBL_DESC_L1ADDR_SHIFT)))) -/* Get the index into the L1 table from a physical address. */ -#define GPT_L1_IDX(_p, _pa) (((_pa) >> GPT_L1_IDX_SHIFT(_p)) & \ - GPT_L1_IDX_MASK(_p)) +/* Get the GPI from L1 Contiguous descriptor */ +#define GPT_L1_CONT_GPI(_desc) \ + (((_desc) >> GPT_L1_CONT_DESC_GPI_SHIFT) & GPT_L1_CONT_DESC_GPI_MASK) + +/* Get the GPI from L1 Granules descriptor */ +#define GPT_L1_GRAN_GPI(_desc) ((_desc) & GPT_L1_GRAN_DESC_GPI_MASK) + +/* Get the Contig from L1 Contiguous descriptor */ +#define GPT_L1_CONT_CONTIG(_desc) \ + (((_desc) >> GPT_L1_CONT_DESC_CONTIG_SHIFT) & \ + GPT_L1_CONT_DESC_CONTIG_MASK) + +/* Get the index into the L1 table from a physical address */ +#define GPT_L1_IDX(_p, _pa) \ + (((_pa) >> GPT_L1_IDX_SHIFT(_p)) & GPT_L1_IDX_MASK(_p)) + +/* Get the index of the GPI within an L1 table entry from a physical address */ +#define GPT_L1_GPI_IDX(_p, _pa) \ + (((_pa) >> GPT_L1_GPI_IDX_SHIFT(_p)) & GPT_L1_GPI_IDX_MASK) + +/* Determine if an address is granule-aligned */ +#define GPT_IS_L1_ALIGNED(_p, _pa) \ + (((_pa) & (GPT_PGS_ACTUAL_SIZE(_p) - UL(1))) == UL(0)) + +/* Get aligned addresses */ +#define ALIGN_2MB(_addr) ((_addr) & ~(SZ_2M - 1UL)) +#define ALIGN_32MB(_addr) ((_addr) & ~(SZ_32M - 1UL)) +#define ALIGN_512MB(_addr) ((_addr) & ~(SZ_512M - 1UL)) + +/* Determine if region is contiguous */ +#define GPT_REGION_IS_CONT(_len, _addr, _size) \ + (((_len) >= (_size)) && (((_addr) & ((_size) - UL(1))) == UL(0))) -/* Get the index of the GPI within an L1 table entry from a physical address. */ -#define GPT_L1_GPI_IDX(_p, _pa) (((_pa) >> GPT_L1_GPI_IDX_SHIFT(_p)) & \ - GPT_L1_GPI_IDX_MASK) +/* Get 32MB block number in 512MB block: 0-15 */ +#define GET_32MB_NUM(_addr) ((_addr >> 25) & 0xF) -/* Determine if an address is granule-aligned. */ -#define GPT_IS_L1_ALIGNED(_p, _pa) (((_pa) & (GPT_PGS_ACTUAL_SIZE(_p) - U(1))) \ - == U(0)) +/* Get 2MB block number in 32MB block: 0-15 */ +#define GET_2MB_NUM(_addr) ((_addr >> 21) & 0xF) #endif /* GPT_RME_PRIVATE_H */ diff --git a/lib/libc/libc.mk b/lib/libc/libc.mk index 95da68c7..03e1fb31 100644 --- a/lib/libc/libc.mk +++ b/lib/libc/libc.mk @@ -1,42 +1,11 @@ # -# Copyright (c) 2016-2021, Arm Limited and Contributors. All rights reserved. +# Copyright (c) 2016-2024, Arm Limited and Contributors. All rights reserved. # # SPDX-License-Identifier: BSD-3-Clause # +# -LIBC_SRCS := $(addprefix lib/libc/, \ - abort.c \ - assert.c \ - exit.c \ - memchr.c \ - memcmp.c \ - memcpy.c \ - memcpy_s.c \ - memmove.c \ - memrchr.c \ - memset.c \ - printf.c \ - putchar.c \ - puts.c \ - snprintf.c \ - strchr.c \ - strcmp.c \ - strlcat.c \ - strlcpy.c \ - strlen.c \ - strncmp.c \ - strnlen.c \ - strrchr.c \ - strtok.c \ - strtoul.c \ - strtoll.c \ - strtoull.c \ - strtol.c) - -ifeq (${ARCH},aarch64) -LIBC_SRCS += $(addprefix lib/libc/aarch64/, \ - setjmp.S) -endif +include lib/libc/libc_common.mk -INCLUDES += -Iinclude/lib/libc \ - -Iinclude/lib/libc/$(ARCH) \ +LIBC_SRCS += $(addprefix lib/libc/, \ + memset.c) diff --git a/lib/libc/libc_asm.mk b/lib/libc/libc_asm.mk index 2f272651..6d9bb9de 100644 --- a/lib/libc/libc_asm.mk +++ b/lib/libc/libc_asm.mk @@ -1,44 +1,15 @@ # -# Copyright (c) 2020-2021, Arm Limited. All rights reserved. +# Copyright (c) 2020-2024, Arm Limited. All rights reserved. # # SPDX-License-Identifier: BSD-3-Clause # -LIBC_SRCS := $(addprefix lib/libc/, \ - abort.c \ - assert.c \ - exit.c \ - memchr.c \ - memcmp.c \ - memcpy.c \ - memmove.c \ - memrchr.c \ - printf.c \ - putchar.c \ - puts.c \ - snprintf.c \ - strchr.c \ - strcmp.c \ - strlcat.c \ - strlcpy.c \ - strlen.c \ - strncmp.c \ - strnlen.c \ - strrchr.c \ - strtok.c \ - strtoul.c \ - strtoll.c \ - strtoull.c \ - strtol.c) +include lib/libc/libc_common.mk ifeq (${ARCH},aarch64) LIBC_SRCS += $(addprefix lib/libc/aarch64/, \ - memset.S \ - setjmp.S) + memset.S) else LIBC_SRCS += $(addprefix lib/libc/aarch32/, \ memset.S) endif - -INCLUDES += -Iinclude/lib/libc \ - -Iinclude/lib/libc/$(ARCH) \ diff --git a/lib/libc/libc_common.mk b/lib/libc/libc_common.mk new file mode 100644 index 00000000..4879818d --- /dev/null +++ b/lib/libc/libc_common.mk @@ -0,0 +1,42 @@ +# +# Copyright (c) 2024, Arm Limited and Contributors. All rights reserved. +# +# SPDX-License-Identifier: BSD-3-Clause +# + +LIBC_SRCS := $(addprefix lib/libc/, \ + abort.c \ + assert.c \ + exit.c \ + memchr.c \ + memcmp.c \ + memcpy.c \ + memcpy_s.c \ + memmove.c \ + memrchr.c \ + printf.c \ + putchar.c \ + puts.c \ + snprintf.c \ + strchr.c \ + strcmp.c \ + strlcat.c \ + strlcpy.c \ + strlen.c \ + strncmp.c \ + strnlen.c \ + strrchr.c \ + strtok.c \ + strtoul.c \ + strtoll.c \ + strtoull.c \ + strtol.c) + +ifeq (${ARCH},aarch64) +LIBC_SRCS += $(addprefix lib/libc/aarch64/, \ + setjmp.S) +endif + +INCLUDES += -Iinclude/lib/libc \ + -Iinclude/lib/libc/$(ARCH) \ + diff --git a/lib/libc/printf.c b/lib/libc/printf.c index 6931a7ea..a8563456 100644 --- a/lib/libc/printf.c +++ b/lib/libc/printf.c @@ -95,6 +95,7 @@ static int unsigned_num_print(unsigned long long int unum, unsigned int radix, * * The following padding specifiers are supported by this print * %0NN - Left-pad the number with 0s (NN is a decimal number) + * %NN - Left-pad the number with spaces (NN is a decimal number) * * The print exits on all other formats specifiers other than valid * combinations of the above specifiers. @@ -182,6 +183,27 @@ loop: padn = 0; fmt++; + for (;;) { + char ch = *fmt; + if ((ch < '0') || (ch > '9')) { + goto loop; + } + padn = (padn * 10) + (ch - '0'); + fmt++; + } + assert(0); /* Unreachable */ + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + padc = ' '; + padn = 0; + for (;;) { char ch = *fmt; if ((ch < '0') || (ch > '9')) { diff --git a/lib/libfdt/libfdt.mk b/lib/libfdt/libfdt.mk index 812057d3..c7f54049 100644 --- a/lib/libfdt/libfdt.mk +++ b/lib/libfdt/libfdt.mk @@ -1,19 +1,23 @@ # -# Copyright (c) 2016, Arm Limited and Contributors. All rights reserved. +# Copyright (c) 2016-2024, Arm Limited and Contributors. All rights reserved. # # SPDX-License-Identifier: BSD-3-Clause # -LIBFDT_SRCS := $(addprefix lib/libfdt/, \ - fdt.c \ - fdt_addresses.c \ - fdt_empty_tree.c \ - fdt_ro.c \ - fdt_rw.c \ - fdt_strerror.c \ - fdt_sw.c \ - fdt_wip.c) \ +ifndef libfdt-mk + libfdt-mk := 1 -INCLUDES += -Iinclude/lib/libfdt + LIBFDT_SRCS := $(addprefix lib/libfdt/, \ + fdt.c \ + fdt_addresses.c \ + fdt_empty_tree.c \ + fdt_ro.c \ + fdt_rw.c \ + fdt_strerror.c \ + fdt_sw.c \ + fdt_wip.c) -$(eval $(call MAKE_LIB,fdt)) + INCLUDES += -Iinclude/lib/libfdt + + $(eval $(call MAKE_LIB,fdt)) +endif diff --git a/lib/locks/exclusive/aarch64/spinlock.S b/lib/locks/exclusive/aarch64/spinlock.S index 5144bf7a..77bb7fe4 100644 --- a/lib/locks/exclusive/aarch64/spinlock.S +++ b/lib/locks/exclusive/aarch64/spinlock.S @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013-2019, Arm Limited and Contributors. All rights reserved. + * Copyright (c) 2013-2024, Arm Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -8,6 +8,8 @@ .globl spin_lock .globl spin_unlock + .globl bit_lock + .globl bit_unlock #if USE_SPINLOCK_CAS #if !ARM_ARCH_AT_LEAST(8, 1) @@ -73,3 +75,43 @@ func spin_unlock stlr wzr, [x0] ret endfunc spin_unlock + +/* + * Atomic bit clear and set instructions require FEAT_LSE which is + * mandatory from Armv8.1. + */ +#if ARM_ARCH_AT_LEAST(8, 1) + +/* + * Acquire bitlock using atomic bit set on byte. If the original read value + * has the bit set, use load exclusive semantics to monitor the address and + * enter WFE. + * + * void bit_lock(bitlock_t *lock, uint8_t mask); + */ +func bit_lock +1: ldsetab w1, w2, [x0] + tst w2, w1 + b.eq 2f + ldxrb w2, [x0] + tst w2, w1 + b.eq 1b + wfe + b 1b +2: + ret +endfunc bit_lock + +/* + * Use atomic bit clear store-release to unconditionally clear bitlock variable. + * Store operation generates an event to all cores waiting in WFE when address + * is monitored by the global monitor. + * + * void bit_unlock(bitlock_t *lock, uint8_t mask); + */ +func bit_unlock + stclrlb w1, [x0] + ret +endfunc bit_unlock + +#endif /* ARM_ARCH_AT_LEAST(8, 1) */ diff --git a/lib/pmf/pmf_smc.c b/lib/pmf/pmf_smc.c index f3dd1127..ac7f53a8 100644 --- a/lib/pmf/pmf_smc.c +++ b/lib/pmf/pmf_smc.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016-2018, Arm Limited and Contributors. All rights reserved. + * Copyright (c) 2016-2024, Arm Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -36,7 +36,8 @@ uintptr_t pmf_smc_handler(unsigned int smc_fid, x2 = (uint32_t)x2; x3 = (uint32_t)x3; - if (smc_fid == PMF_SMC_GET_TIMESTAMP_32) { + if (smc_fid == PMF_SMC_GET_TIMESTAMP_32 || + smc_fid == PMF_SMC_GET_TIMESTAMP_32_DEP) { /* * Return error code and the captured * time-stamp to the caller. @@ -48,8 +49,13 @@ uintptr_t pmf_smc_handler(unsigned int smc_fid, SMC_RET3(handle, rc, (uint32_t)ts_value, (uint32_t)(ts_value >> 32)); } + + if (smc_fid == PMF_SMC_GET_VERSION_32) { + SMC_RET2(handle, SMC_OK, PMF_SMC_VERSION); + } } else { - if (smc_fid == PMF_SMC_GET_TIMESTAMP_64) { + if (smc_fid == PMF_SMC_GET_TIMESTAMP_64 || + smc_fid == PMF_SMC_GET_TIMESTAMP_64_DEP) { /* * Return error code and the captured * time-stamp to the caller. @@ -60,6 +66,10 @@ uintptr_t pmf_smc_handler(unsigned int smc_fid, (unsigned int)x3, &ts_value); SMC_RET2(handle, rc, ts_value); } + + if (smc_fid == PMF_SMC_GET_VERSION_64) { + SMC_RET2(handle, SMC_OK, PMF_SMC_VERSION); + } } WARN("Unimplemented PMF Call: 0x%x \n", smc_fid); diff --git a/lib/psa/cca_attestation.c b/lib/psa/cca_attestation.c new file mode 100644 index 00000000..9e9e0c11 --- /dev/null +++ b/lib/psa/cca_attestation.c @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2024, Arm Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <assert.h> + +#include <psa/crypto_sizes.h> +#include <psa/crypto_types.h> +#include <psa/crypto_values.h> + +#include <cca_attestation.h> +#include <delegated_attestation.h> +#include <services/rmmd_svc.h> + +psa_status_t +cca_attestation_get_realm_key(uintptr_t buf, size_t *len, unsigned int type) +{ + size_t dak_len; + psa_status_t ret = PSA_SUCCESS; + + /* + * Current RMM implementations only support the public key size for + * ECC-P384, i.e. ATTEST_KEY_CURVE_ECC_SECP384R1 attestation key. + * + * This ECC key has following properties: + * ecc_curve: 0x12 (PSA_ECC_FAMILY_SECP_R1) + * key_bits: 384 + * hash_alg: 0x02000009 (PSA_ALG_SHA_256) + */ + assert(type == ATTEST_KEY_CURVE_ECC_SECP384R1); + + ret = rse_delegated_attest_get_delegated_key(PSA_ECC_FAMILY_SECP_R1, + 384, (uint8_t *)buf, *len, + &dak_len, PSA_ALG_SHA_256); + if (ret != PSA_SUCCESS) { + return ret; + } + + if (dak_len != PSA_BITS_TO_BYTES(384)) { + return PSA_ERROR_INVALID_ARGUMENT; + } + + *len = dak_len; + + return ret; +} + +psa_status_t +cca_attestation_get_plat_token(uintptr_t buf, size_t *len, + uintptr_t hash, size_t hash_size) +{ + size_t token_len = 0; + psa_status_t ret = PSA_SUCCESS; + + ret = rse_delegated_attest_get_token((const uint8_t *)hash, hash_size, + (uint8_t *)buf, *len, &token_len); + if (ret != PSA_SUCCESS) { + return ret; + } + + *len = token_len; + + return ret; +} diff --git a/lib/psa/delegated_attestation.c b/lib/psa/delegated_attestation.c index 81e26215..805a9416 100644 --- a/lib/psa/delegated_attestation.c +++ b/lib/psa/delegated_attestation.c @@ -10,7 +10,7 @@ #include <psa_manifest/sid.h> psa_status_t -rss_delegated_attest_get_delegated_key(uint8_t ecc_curve, +rse_delegated_attest_get_delegated_key(uint8_t ecc_curve, uint32_t key_bits, uint8_t *key_buf, size_t key_buf_size, @@ -31,8 +31,8 @@ rss_delegated_attest_get_delegated_key(uint8_t ecc_curve, return PSA_ERROR_INVALID_ARGUMENT; } - status = psa_call(RSS_DELEGATED_SERVICE_HANDLE, - RSS_DELEGATED_ATTEST_GET_DELEGATED_KEY, + status = psa_call(RSE_DELEGATED_SERVICE_HANDLE, + RSE_DELEGATED_ATTEST_GET_DELEGATED_KEY, in_vec, IOVEC_LEN(in_vec), out_vec, IOVEC_LEN(out_vec)); if (status == PSA_SUCCESS) { @@ -43,7 +43,7 @@ rss_delegated_attest_get_delegated_key(uint8_t ecc_curve, } psa_status_t -rss_delegated_attest_get_token(const uint8_t *dak_pub_hash, +rse_delegated_attest_get_token(const uint8_t *dak_pub_hash, size_t dak_pub_hash_size, uint8_t *token_buf, size_t token_buf_size, @@ -61,8 +61,8 @@ rss_delegated_attest_get_token(const uint8_t *dak_pub_hash, return PSA_ERROR_INVALID_ARGUMENT; } - status = psa_call(RSS_DELEGATED_SERVICE_HANDLE, - RSS_DELEGATED_ATTEST_GET_PLATFORM_TOKEN, + status = psa_call(RSE_DELEGATED_SERVICE_HANDLE, + RSE_DELEGATED_ATTEST_GET_PLATFORM_TOKEN, in_vec, IOVEC_LEN(in_vec), out_vec, IOVEC_LEN(out_vec)); if (status == PSA_SUCCESS) { diff --git a/lib/psa/dice_protection_environment.c b/lib/psa/dice_protection_environment.c new file mode 100644 index 00000000..21456117 --- /dev/null +++ b/lib/psa/dice_protection_environment.c @@ -0,0 +1,370 @@ +/* + * Copyright (c) 2024, Arm Limited. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + * + */ + +#include <qcbor/qcbor_decode.h> +#include <qcbor/qcbor_encode.h> +#include <qcbor/qcbor_spiffy_decode.h> + +#include <common/debug.h> +#include <dice.h> +#include <dice_protection_environment.h> +#include <psa/client.h> +#include <psa_manifest/sid.h> + +enum dpe_command_id_t { + /* Standard commands */ + DPE_GET_PROFILE = 1, + DPE_OPEN_SESSION = 2, + DPE_CLOSE_SESSION = 3, + DPE_SYNC_SESSION = 4, + DPE_EXPORT_SESSION = 5, + DPE_IMPORT_SESSION = 6, + DPE_INITIALIZE_CONTEXT = 7, + DPE_DERIVE_CONTEXT = 8, + DPE_CERTIFY_KEY = 9, + DPE_SIGN = 10, + DPE_SEAL = 11, + DPE_UNSEAL = 12, + DPE_DERIVE_SEALING_PUBLIC_KEY = 13, + DPE_ROTATE_CONTEXT_HANDLE = 14, + DPE_DESTROY_CONTEXT = 15, +}; + +enum dice_input_labels_t { + DICE_CODE_HASH = 1, + DICE_CODE_DESCRIPTOR = 2, + DICE_CONFIG_TYPE = 3, + DICE_CONFIG_VALUE = 4, + DICE_CONFIG_DESCRIPTOR = 5, + DICE_AUTHORITY_HASH = 6, + DICE_AUTHORITY_DESCRIPTOR = 7, + DICE_MODE = 8, + DICE_HIDDEN = 9, +}; + +enum dpe_derive_context_input_labels_t { + DPE_DERIVE_CONTEXT_CONTEXT_HANDLE = 1, + DPE_DERIVE_CONTEXT_RETAIN_PARENT_CONTEXT = 2, + DPE_DERIVE_CONTEXT_ALLOW_NEW_CONTEXT_TO_DERIVE = 3, + DPE_DERIVE_CONTEXT_CREATE_CERTIFICATE = 4, + DPE_DERIVE_CONTEXT_NEW_SESSION_INITIATOR_HANDSHAKE = 5, + DPE_DERIVE_CONTEXT_INPUT_DATA = 6, + DPE_DERIVE_CONTEXT_INTERNAL_INPUTS = 7, + DPE_DERIVE_CONTEXT_TARGET_LOCALITY = 8, + DPE_DERIVE_CONTEXT_RETURN_CERTIFICATE = 9, + DPE_DERIVE_CONTEXT_ALLOW_NEW_CONTEXT_TO_EXPORT = 10, + DPE_DERIVE_CONTEXT_EXPORT_CDI = 11, + /* enum values 256 and onwards are reserved for custom arguments */ + DPE_DERIVE_CONTEXT_CERT_ID = 256, +}; + +enum dpe_derive_context_output_labels_t { + DPE_DERIVE_CONTEXT_NEW_CONTEXT_HANDLE = 1, + DPE_DERIVE_CONTEXT_NEW_SESSION_RESPONDER_HANDSHAKE = 2, + DPE_DERIVE_CONTEXT_PARENT_CONTEXT_HANDLE = 3, + DPE_DERIVE_CONTEXT_NEW_CERTIFICATE = 4, + DPE_DERIVE_CONTEXT_EXPORTED_CDI = 5, +}; + +struct derive_context_input_t { + int context_handle; + uint32_t cert_id; + bool retain_parent_context; + bool allow_new_context_to_derive; + bool create_certificate; + const DiceInputValues *dice_inputs; + int32_t target_locality; + bool return_certificate; + bool allow_new_context_to_export; + bool export_cdi; +}; + +struct derive_context_output_t { + int new_context_handle; + int new_parent_context_handle; + const uint8_t *new_certificate; + size_t new_certificate_size; + const uint8_t *exported_cdi; + size_t exported_cdi_size; +}; + +static void encode_dice_inputs(QCBOREncodeContext *encode_ctx, + const DiceInputValues *input) +{ + /* Wrap the DICE inputs into a byte string */ + QCBOREncode_BstrWrapInMapN(encode_ctx, DPE_DERIVE_CONTEXT_INPUT_DATA); + + /* Inside the byte string the DICE inputs are encoded as a map */ + QCBOREncode_OpenMap(encode_ctx); + + QCBOREncode_AddBytesToMapN(encode_ctx, DICE_CODE_HASH, + (UsefulBufC) { input->code_hash, + sizeof(input->code_hash) }); + + QCBOREncode_AddBytesToMapN(encode_ctx, DICE_CODE_DESCRIPTOR, + (UsefulBufC) { input->code_descriptor, + input->code_descriptor_size }); + + QCBOREncode_AddInt64ToMapN(encode_ctx, DICE_CONFIG_TYPE, + input->config_type); + + if (input->config_type == kDiceConfigTypeInline) { + QCBOREncode_AddBytesToMapN(encode_ctx, DICE_CONFIG_VALUE, + (UsefulBufC) { input->config_value, + sizeof(input->config_value) }); + } else { + QCBOREncode_AddBytesToMapN(encode_ctx, DICE_CONFIG_DESCRIPTOR, + (UsefulBufC) { input->config_descriptor, + input->config_descriptor_size }); + } + + QCBOREncode_AddBytesToMapN(encode_ctx, DICE_AUTHORITY_HASH, + (UsefulBufC) { input->authority_hash, + sizeof(input->authority_hash) }); + + QCBOREncode_AddBytesToMapN(encode_ctx, DICE_AUTHORITY_DESCRIPTOR, + (UsefulBufC) { input->authority_descriptor, + input->authority_descriptor_size }); + + QCBOREncode_AddInt64ToMapN(encode_ctx, DICE_MODE, input->mode); + + QCBOREncode_AddBytesToMapN(encode_ctx, DICE_HIDDEN, + (UsefulBufC) { input->hidden, + sizeof(input->hidden) }); + + QCBOREncode_CloseMap(encode_ctx); + QCBOREncode_CloseBstrWrap2(encode_ctx, true, NULL); +} + +static QCBORError encode_derive_context(const struct derive_context_input_t *args, + UsefulBuf buf, + UsefulBufC *encoded_buf) +{ + QCBOREncodeContext encode_ctx; + + QCBOREncode_Init(&encode_ctx, buf); + + QCBOREncode_OpenArray(&encode_ctx); + QCBOREncode_AddUInt64(&encode_ctx, DPE_DERIVE_CONTEXT); + + /* Encode DeriveContext command */ + QCBOREncode_OpenMap(&encode_ctx); + QCBOREncode_AddBytesToMapN(&encode_ctx, + DPE_DERIVE_CONTEXT_CONTEXT_HANDLE, + (UsefulBufC) { &args->context_handle, + sizeof(args->context_handle) }); + QCBOREncode_AddUInt64ToMapN(&encode_ctx, + DPE_DERIVE_CONTEXT_CERT_ID, + args->cert_id); + QCBOREncode_AddBoolToMapN(&encode_ctx, + DPE_DERIVE_CONTEXT_RETAIN_PARENT_CONTEXT, + args->retain_parent_context); + QCBOREncode_AddBoolToMapN(&encode_ctx, + DPE_DERIVE_CONTEXT_ALLOW_NEW_CONTEXT_TO_DERIVE, + args->allow_new_context_to_derive); + QCBOREncode_AddBoolToMapN(&encode_ctx, + DPE_DERIVE_CONTEXT_CREATE_CERTIFICATE, + args->create_certificate); + encode_dice_inputs(&encode_ctx, args->dice_inputs); + QCBOREncode_AddBytesToMapN(&encode_ctx, + DPE_DERIVE_CONTEXT_TARGET_LOCALITY, + (UsefulBufC) { &args->target_locality, + sizeof(args->target_locality) }); + QCBOREncode_AddBoolToMapN(&encode_ctx, + DPE_DERIVE_CONTEXT_RETURN_CERTIFICATE, + args->return_certificate); + QCBOREncode_AddBoolToMapN(&encode_ctx, + DPE_DERIVE_CONTEXT_ALLOW_NEW_CONTEXT_TO_EXPORT, + args->allow_new_context_to_export); + QCBOREncode_AddBoolToMapN(&encode_ctx, + DPE_DERIVE_CONTEXT_EXPORT_CDI, + args->export_cdi); + QCBOREncode_CloseMap(&encode_ctx); + + QCBOREncode_CloseArray(&encode_ctx); + + return QCBOREncode_Finish(&encode_ctx, encoded_buf); +} + +static QCBORError decode_derive_context_response(UsefulBufC encoded_buf, + struct derive_context_output_t *args, + dpe_error_t *dpe_err) +{ + QCBORDecodeContext decode_ctx; + UsefulBufC out; + int64_t response_dpe_err; + + QCBORDecode_Init(&decode_ctx, encoded_buf, QCBOR_DECODE_MODE_NORMAL); + + QCBORDecode_EnterArray(&decode_ctx, NULL); + + /* Get the error code from the response. DPE returns int32_t */ + QCBORDecode_GetInt64(&decode_ctx, &response_dpe_err); + *dpe_err = (dpe_error_t)response_dpe_err; + + /* Decode DeriveContext response if successful */ + if (*dpe_err == DPE_NO_ERROR) { + QCBORDecode_EnterMap(&decode_ctx, NULL); + + QCBORDecode_GetByteStringInMapN(&decode_ctx, + DPE_DERIVE_CONTEXT_NEW_CONTEXT_HANDLE, + &out); + if (out.len != sizeof(args->new_context_handle)) { + return QCBORDecode_Finish(&decode_ctx); + } + memcpy(&args->new_context_handle, out.ptr, out.len); + + QCBORDecode_GetByteStringInMapN(&decode_ctx, + DPE_DERIVE_CONTEXT_PARENT_CONTEXT_HANDLE, + &out); + if (out.len != sizeof(args->new_parent_context_handle)) { + return QCBORDecode_Finish(&decode_ctx); + } + memcpy(&args->new_parent_context_handle, out.ptr, out.len); + + QCBORDecode_GetByteStringInMapN(&decode_ctx, + DPE_DERIVE_CONTEXT_NEW_CERTIFICATE, + &out); + args->new_certificate = out.ptr; + args->new_certificate_size = out.len; + + QCBORDecode_GetByteStringInMapN(&decode_ctx, + DPE_DERIVE_CONTEXT_EXPORTED_CDI, + &out); + args->exported_cdi = out.ptr; + args->exported_cdi_size = out.len; + + QCBORDecode_ExitMap(&decode_ctx); + } + + QCBORDecode_ExitArray(&decode_ctx); + + return QCBORDecode_Finish(&decode_ctx); +} + +static int32_t dpe_client_call(const char *cmd_input, size_t cmd_input_size, + char *cmd_output, size_t *cmd_output_size) +{ + int32_t err; + + psa_invec in_vec[] = { + { cmd_input, cmd_input_size }, + }; + psa_outvec out_vec[] = { + { cmd_output, *cmd_output_size }, + }; + + err = psa_call(RSE_DPE_SERVICE_HANDLE, 0, + in_vec, IOVEC_LEN(in_vec), out_vec, IOVEC_LEN(out_vec)); + + if (err == PSA_SUCCESS) { + *cmd_output_size = out_vec[0].len; + } + + return err; +} + +dpe_error_t dpe_derive_context(int context_handle, + uint32_t cert_id, + bool retain_parent_context, + bool allow_new_context_to_derive, + bool create_certificate, + const DiceInputValues *dice_inputs, + int32_t target_locality, + bool return_certificate, + bool allow_new_context_to_export, + bool export_cdi, + int *new_context_handle, + int *new_parent_context_handle, + uint8_t *new_certificate_buf, + size_t new_certificate_buf_size, + size_t *new_certificate_actual_size, + uint8_t *exported_cdi_buf, + size_t exported_cdi_buf_size, + size_t *exported_cdi_actual_size) +{ + int32_t service_err; + dpe_error_t dpe_err; + QCBORError qcbor_err; + UsefulBufC encoded_buf; + UsefulBuf_MAKE_STACK_UB(cmd_buf, 612); + + const struct derive_context_input_t in_args = { + context_handle, + cert_id, + retain_parent_context, + allow_new_context_to_derive, + create_certificate, + dice_inputs, + target_locality, + return_certificate, + allow_new_context_to_export, + export_cdi, + }; + struct derive_context_output_t out_args; + + /* + * Validate the output params here because they are not sent to the + * service. Input params are validated by the DPE service. + */ + if ((new_context_handle == NULL) || + (retain_parent_context == true && new_parent_context_handle == NULL) || + (return_certificate == true && + (new_certificate_buf == NULL || new_certificate_actual_size == NULL)) || + (export_cdi == true && + (exported_cdi_buf == NULL || exported_cdi_actual_size == NULL))) { + return DPE_INVALID_ARGUMENT; + } + + qcbor_err = encode_derive_context(&in_args, cmd_buf, &encoded_buf); + if (qcbor_err != QCBOR_SUCCESS) { + return DPE_INTERNAL_ERROR; + } + + service_err = dpe_client_call(encoded_buf.ptr, encoded_buf.len, + cmd_buf.ptr, &cmd_buf.len); + if (service_err != 0) { + return DPE_INTERNAL_ERROR; + } + + qcbor_err = decode_derive_context_response(UsefulBuf_Const(cmd_buf), + &out_args, &dpe_err); + if (qcbor_err != QCBOR_SUCCESS) { + return DPE_INTERNAL_ERROR; + } else if (dpe_err != DPE_NO_ERROR) { + return dpe_err; + } + + /* Copy returned values into caller's memory */ + *new_context_handle = out_args.new_context_handle; + + if (retain_parent_context == true) { + *new_parent_context_handle = out_args.new_parent_context_handle; + } + + if (return_certificate == true) { + if (out_args.new_certificate_size > new_certificate_buf_size) { + return DPE_INVALID_ARGUMENT; + } + + memcpy(new_certificate_buf, out_args.new_certificate, + out_args.new_certificate_size); + *new_certificate_actual_size = out_args.new_certificate_size; + } + + if (export_cdi == true) { + if (out_args.exported_cdi_size > exported_cdi_buf_size) { + return DPE_INVALID_ARGUMENT; + } + + memcpy(exported_cdi_buf, out_args.exported_cdi, + out_args.exported_cdi_size); + *exported_cdi_actual_size = out_args.exported_cdi_size; + } + + return DPE_NO_ERROR; +} diff --git a/lib/psa/measured_boot.c b/lib/psa/measured_boot.c index c359e9f8..c66b8dae 100644 --- a/lib/psa/measured_boot.c +++ b/lib/psa/measured_boot.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, Arm Limited. All rights reserved. + * Copyright (c) 2022-2024, Arm Limited. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause * @@ -8,6 +8,7 @@ #include <string.h> #include <common/debug.h> +#include <drivers/measured_boot/metadata.h> #include <measured_boot.h> #include <psa/client.h> #include <psa_manifest/sid.h> @@ -61,9 +62,8 @@ static void log_measurement(uint8_t index, INFO(" - locking : %s\n", lock_measurement ? "true" : "false"); } -#if !PLAT_RSS_NOT_SUPPORTED psa_status_t -rss_measured_boot_extend_measurement(uint8_t index, +rse_measured_boot_extend_measurement(uint8_t index, const uint8_t *signer_id, size_t signer_id_size, const uint8_t *version, @@ -115,13 +115,13 @@ rss_measured_boot_extend_measurement(uint8_t index, measurement_algo, measurement_value, measurement_value_size, lock_measurement); - return psa_call(RSS_MEASURED_BOOT_HANDLE, - RSS_MEASURED_BOOT_EXTEND, + return psa_call(RSE_MEASURED_BOOT_HANDLE, + RSE_MEASURED_BOOT_EXTEND, in_vec, IOVEC_LEN(in_vec), NULL, 0); } -psa_status_t rss_measured_boot_read_measurement(uint8_t index, +psa_status_t rse_measured_boot_read_measurement(uint8_t index, uint8_t *signer_id, size_t signer_id_size, size_t *signer_id_len, @@ -158,7 +158,7 @@ psa_status_t rss_measured_boot_read_measurement(uint8_t index, {.base = measurement_value, .len = measurement_value_size} }; - status = psa_call(RSS_MEASURED_BOOT_HANDLE, RSS_MEASURED_BOOT_READ, + status = psa_call(RSE_MEASURED_BOOT_HANDLE, RSE_MEASURED_BOOT_READ, in_vec, IOVEC_LEN(in_vec), out_vec, IOVEC_LEN(out_vec)); @@ -175,47 +175,3 @@ psa_status_t rss_measured_boot_read_measurement(uint8_t index, return status; } - -#else /* !PLAT_RSS_NOT_SUPPORTED */ - -psa_status_t -rss_measured_boot_extend_measurement(uint8_t index, - const uint8_t *signer_id, - size_t signer_id_size, - const uint8_t *version, - size_t version_size, - uint32_t measurement_algo, - const uint8_t *sw_type, - size_t sw_type_size, - const uint8_t *measurement_value, - size_t measurement_value_size, - bool lock_measurement) -{ - log_measurement(index, signer_id, signer_id_size, - version, version_size, sw_type, sw_type_size, - measurement_algo, measurement_value, - measurement_value_size, lock_measurement); - - return PSA_SUCCESS; -} - -psa_status_t rss_measured_boot_read_measurement(uint8_t index, - uint8_t *signer_id, - size_t signer_id_size, - size_t *signer_id_len, - uint8_t *version, - size_t version_size, - size_t *version_len, - uint32_t *measurement_algo, - uint8_t *sw_type, - size_t sw_type_size, - size_t *sw_type_len, - uint8_t *measurement_value, - size_t measurement_value_size, - size_t *measurement_value_len, - bool *is_locked) -{ - return PSA_SUCCESS; -} - -#endif /* !PLAT_RSS_NOT_SUPPORTED */ diff --git a/lib/psa/measured_boot_private.h b/lib/psa/measured_boot_private.h index 80d2c19f..bf2ae48a 100644 --- a/lib/psa/measured_boot_private.h +++ b/lib/psa/measured_boot_private.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, Arm Limited. All rights reserved. + * Copyright (c) 2022-2024, Arm Limited. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause * @@ -10,9 +10,11 @@ #include <stdint.h> +#include <drivers/measured_boot/metadata.h> + /* Measured boot message types that distinguish its services */ -#define RSS_MEASURED_BOOT_READ 1001U -#define RSS_MEASURED_BOOT_EXTEND 1002U +#define RSE_MEASURED_BOOT_READ 1001U +#define RSE_MEASURED_BOOT_EXTEND 1002U struct measured_boot_read_iovec_in_t { uint8_t index; diff --git a/lib/psa/rss_platform.c b/lib/psa/rse_platform.c similarity index 59% rename from lib/psa/rss_platform.c rename to lib/psa/rse_platform.c index 7d90bfce..7fc23820 100644 --- a/lib/psa/rss_platform.c +++ b/lib/psa/rse_platform.c @@ -7,24 +7,24 @@ #include <psa/client.h> #include <psa_manifest/sid.h> -#include <rss_crypto_defs.h> -#include <rss_platform_api.h> +#include <rse_crypto_defs.h> +#include <rse_platform_api.h> psa_status_t -rss_platform_nv_counter_increment(uint32_t counter_id) +rse_platform_nv_counter_increment(uint32_t counter_id) { struct psa_invec in_vec[1]; in_vec[0].base = &counter_id; in_vec[0].len = sizeof(counter_id); - return psa_call(RSS_PLATFORM_SERVICE_HANDLE, - RSS_PLATFORM_API_ID_NV_INCREMENT, + return psa_call(RSE_PLATFORM_SERVICE_HANDLE, + RSE_PLATFORM_API_ID_NV_INCREMENT, in_vec, 1, NULL, 0); } psa_status_t -rss_platform_nv_counter_read(uint32_t counter_id, +rse_platform_nv_counter_read(uint32_t counter_id, uint32_t size, uint8_t *val) { struct psa_invec in_vec[1]; @@ -36,30 +36,30 @@ rss_platform_nv_counter_read(uint32_t counter_id, out_vec[0].base = val; out_vec[0].len = size; - return psa_call(RSS_PLATFORM_SERVICE_HANDLE, - RSS_PLATFORM_API_ID_NV_READ, + return psa_call(RSE_PLATFORM_SERVICE_HANDLE, + RSE_PLATFORM_API_ID_NV_READ, in_vec, 1, out_vec, 1); } psa_status_t -rss_platform_key_read(enum rss_key_id_builtin_t key, uint8_t *data, +rse_platform_key_read(enum rse_key_id_builtin_t key, uint8_t *data, size_t data_size, size_t *data_length) { psa_status_t status; - struct rss_crypto_pack_iovec iov = { - .function_id = RSS_CRYPTO_EXPORT_PUBLIC_KEY_SID, + struct rse_crypto_pack_iovec iov = { + .function_id = RSE_CRYPTO_EXPORT_PUBLIC_KEY_SID, .key_id = key, }; psa_invec in_vec[] = { - {.base = &iov, .len = sizeof(struct rss_crypto_pack_iovec)}, + {.base = &iov, .len = sizeof(struct rse_crypto_pack_iovec)}, }; psa_outvec out_vec[] = { {.base = data, .len = data_size} }; - status = psa_call(RSS_CRYPTO_HANDLE, PSA_IPC_CALL, + status = psa_call(RSE_CRYPTO_HANDLE, PSA_IPC_CALL, in_vec, IOVEC_LEN(in_vec), out_vec, IOVEC_LEN(out_vec)); diff --git a/lib/psci/aarch64/psci_helpers.S b/lib/psci/aarch64/psci_helpers.S index 3b77ab23..cca08c13 100644 --- a/lib/psci/aarch64/psci_helpers.S +++ b/lib/psci/aarch64/psci_helpers.S @@ -1,11 +1,12 @@ /* - * Copyright (c) 2014-2023, Arm Limited and Contributors. All rights reserved. + * Copyright (c) 2014-2024, Arm Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ #include <asm_macros.S> #include <assert_macros.S> +#include <cpu_macros.S> #include <lib/psci/psci.h> #include <platform_def.h> @@ -124,9 +125,8 @@ endfunc psci_do_pwrup_cache_maintenance * ----------------------------------------------------------------------- */ func psci_power_down_wfi -#if ERRATA_A510_2684597 - bl apply_cpu_pwr_dwn_errata -#endif + apply_erratum cortex_a510, ERRATUM(2684597), ERRATA_A510_2684597 + dsb sy // ensure write buffer empty 1: wfi diff --git a/lib/psci/aarch64/runtime_errata.S b/lib/psci/aarch64/runtime_errata.S deleted file mode 100644 index 89e3e12b..00000000 --- a/lib/psci/aarch64/runtime_errata.S +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright (c) 2023, Arm Limited and Contributors. All rights reserved. - * - * SPDX-License-Identifier: BSD-3-Clause - */ - -#include <asm_macros.S> -#include <cortex_a510.h> -#include <cpu_macros.S> - -/* - * void apply_cpu_pwr_dwn_errata(void); - * - * This function applies various CPU errata during power down. - */ - .globl apply_cpu_pwr_dwn_errata -func apply_cpu_pwr_dwn_errata - mov x19, x30 - bl cpu_get_rev_var - mov x18, x0 - -#if ERRATA_A510_2684597 - bl erratum_cortex_a510_2684597_wa -#endif - - ret x19 -endfunc apply_cpu_pwr_dwn_errata diff --git a/lib/psci/psci_common.c b/lib/psci/psci_common.c index f9de432b..375cdbab 100644 --- a/lib/psci/psci_common.c +++ b/lib/psci/psci_common.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013-2023, Arm Limited and Contributors. All rights reserved. + * Copyright (c) 2013-2024, Arm Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -8,12 +8,14 @@ #include <string.h> #include <arch.h> +#include <arch_features.h> #include <arch_helpers.h> #include <common/bl_common.h> #include <common/debug.h> #include <context.h> #include <drivers/delay_timer.h> #include <lib/el3_runtime/context_mgmt.h> +#include <lib/extensions/spe.h> #include <lib/utils.h> #include <plat/common/platform.h> @@ -170,7 +172,8 @@ void psci_query_sys_suspend_pwrstate(psci_power_state_t *state_info) ******************************************************************************/ static bool psci_is_last_cpu_to_idle_at_pwrlvl(unsigned int end_pwrlvl) { - unsigned int my_idx, lvl, parent_idx; + unsigned int my_idx, lvl; + unsigned int parent_idx = 0; unsigned int cpu_start_idx, ncpus, cpu_idx; plat_local_state_t local_state; @@ -179,9 +182,9 @@ static bool psci_is_last_cpu_to_idle_at_pwrlvl(unsigned int end_pwrlvl) } my_idx = plat_my_core_pos(); - - for (lvl = PSCI_CPU_PWR_LVL; lvl <= end_pwrlvl; lvl++) { - parent_idx = psci_cpu_pd_nodes[my_idx].parent_node; + parent_idx = psci_cpu_pd_nodes[my_idx].parent_node; + for (lvl = PSCI_CPU_PWR_LVL + U(1); lvl < end_pwrlvl; lvl++) { + parent_idx = psci_non_cpu_pd_nodes[parent_idx].parent_node; } cpu_start_idx = psci_non_cpu_pd_nodes[parent_idx].cpu_start_idx; @@ -662,6 +665,8 @@ int psci_validate_state_coordination(unsigned int end_pwrlvl, } goto exit; } + + parent_idx = psci_non_cpu_pd_nodes[parent_idx].parent_node; } /* @@ -1164,6 +1169,8 @@ int psci_secondaries_brought_up(void) ******************************************************************************/ void psci_pwrdown_cpu(unsigned int power_level) { + psci_do_manage_extensions(); + #if HW_ASSISTED_COHERENCY /* * With hardware-assisted coherency, the CPU drivers only initiate the @@ -1283,3 +1290,20 @@ bool psci_are_all_cpus_on_safe(void) return true; } + +/******************************************************************************* + * This function performs architectural feature specific management. + * It ensures the architectural features are disabled during cpu + * power off/suspend operations. + ******************************************************************************/ +void psci_do_manage_extensions(void) +{ + /* + * On power down we need to disable statistical profiling extensions + * before exiting coherency. + */ + if (is_feat_spe_supported()) { + spe_stop(); + } + +} diff --git a/lib/psci/psci_lib.mk b/lib/psci/psci_lib.mk index c71580f9..527ad3a1 100644 --- a/lib/psci/psci_lib.mk +++ b/lib/psci/psci_lib.mk @@ -1,5 +1,5 @@ # -# Copyright (c) 2016-2023, Arm Limited and Contributors. All rights reserved. +# Copyright (c) 2016-2024, Arm Limited and Contributors. All rights reserved. # # SPDX-License-Identifier: BSD-3-Clause # @@ -21,8 +21,7 @@ PSCI_LIB_SOURCES := lib/el3_runtime/cpu_data_array.c \ lib/psci/${ARCH}/psci_helpers.S ifeq (${ARCH}, aarch64) -PSCI_LIB_SOURCES += lib/el3_runtime/aarch64/context.S \ - lib/psci/aarch64/runtime_errata.S +PSCI_LIB_SOURCES += lib/el3_runtime/aarch64/context.S endif ifeq (${USE_COHERENT_MEM}, 1) diff --git a/lib/romlib/Makefile b/lib/romlib/Makefile index c3ddc5af..367487ad 100644 --- a/lib/romlib/Makefile +++ b/lib/romlib/Makefile @@ -1,14 +1,13 @@ # -# Copyright (c) 2018-2019, Arm Limited and Contributors. All rights reserved. +# Copyright (c) 2018-2024, Arm Limited and Contributors. All rights reserved. # # SPDX-License-Identifier: BSD-3-Clause # -AS = $(CROSS_COMPILE)as -AR = $(CROSS_COMPILE)ar -LD = $(CROSS_COMPILE)ld -OC = $(CROSS_COMPILE)objcopy -CPP = $(CROSS_COMPILE)cpp +include ../../make_helpers/build-rules.mk +include ../../make_helpers/common.mk +include ../../make_helpers/toolchain.mk + ROMLIB_GEN = ./romlib_generator.py BUILD_DIR = $(BUILD_PLAT)/romlib LIB_DIR = $(BUILD_PLAT)/lib @@ -20,20 +19,16 @@ OBJS = $(BUILD_DIR)/jmptbl.o $(BUILD_DIR)/init.o MAPFILE = $(BUILD_PLAT)/romlib/romlib.map ifneq ($(PLAT_DIR),) - WRAPPER_SOURCES = $(shell $(ROMLIB_GEN) genwrappers -b $(WRAPPER_DIR) --list ../../$(PLAT_DIR)/jmptbl.i) - WRAPPER_OBJS = $(WRAPPER_SOURCES:.s=.o) -endif + WRAPPER_SOURCES = $(sort $(shell $(ROMLIB_GEN) genwrappers -b $\ + $(WRAPPER_DIR) --list ../../$(PLAT_DIR)/jmptbl.i)) -V ?= 0 -ifeq ($(V),0) - Q := @ -else - Q := + WRAPPER_OBJS = $(WRAPPER_SOURCES:.s=.o) endif -LDFLAGS := --gc-sections -O1 +LDFLAGS := -Wl,--gc-sections -nostdlib + ifeq ($(DEBUG),1) - LDFLAGS += -Map=$(MAPFILE) + LDFLAGS += -Wl,-Map=$(MAPFILE) endif ifeq (${ARM_ARCH_MINOR},0) @@ -44,55 +39,56 @@ endif .PHONY: all clean distclean -all: $(BUILD_DIR)/romlib.bin $(LIB_DIR)/libwrappers.a +all: $(BUILD_DIR)/romlib.bin $(BUILD_DIR)/romlib.ldflags $(LIB_DIR)/libwrappers.a -%.o: %.s - @echo " AS $@" - $(Q)$(AS) $(ASFLAGS) -o $@ $< +%.o: %.s | $$(@D)/ + $(s)echo " AS $@" + $(q)$(aarch64-as) -c $(ASFLAGS) -o $@ $< -$(BUILD_DIR)/%.o: %.s - @echo " AS $@" - $(Q)$(AS) $(ASFLAGS) -o $@ $< +$(BUILD_DIR)/%.o: %.s | $$(@D)/ + $(s)echo " AS $@" + $(q)$(aarch64-as) -c $(ASFLAGS) -o $@ $< -$(BUILD_DIR)/romlib.ld: romlib.ld.S - @echo " PP $@" - $(Q)$(CPP) $(PPFLAGS) -o $@ romlib.ld.S +$(BUILD_DIR)/romlib.ld: romlib.ld.S | $$(@D)/ + $(s)echo " PP $@" + $(q)$(aarch64-cpp) -E $(PPFLAGS) -o $@ romlib.ld.S -$(BUILD_DIR)/romlib.elf: $(OBJS) $(BUILD_DIR)/romlib.ld - @echo " LD $@" - $(Q)$(LD) -T $(BUILD_DIR)/romlib.ld -L$(LIB_DIR) $(LDFLAGS) -o $@ $(OBJS) $(LIBS) +$(BUILD_DIR)/romlib.elf: $(OBJS) $(BUILD_DIR)/romlib.ld | $$(@D)/ + $(s)echo " LD $@" + $(q)$(aarch64-ld) -T $(BUILD_DIR)/romlib.ld -L$(LIB_DIR) $(LDFLAGS) -o $@ $(OBJS) $(LIBS) -$(BUILD_DIR)/romlib.bin: $(BUILD_DIR)/romlib.elf - @echo " BIN $@" - $(Q)$(OC) -O binary $(BUILD_DIR)/romlib.elf $@ +$(BUILD_DIR)/romlib.bin: $(BUILD_DIR)/romlib.elf | $$(@D)/ + $(s)echo " BIN $@" + $(q)$(aarch64-oc) -O binary $(BUILD_DIR)/romlib.elf $@ -$(WRAPPER_DIR)/jmpvar.s: $(BUILD_DIR)/romlib.elf - @echo " VAR $@" - $(Q)$(ROMLIB_GEN) genvar --output $@ $< +$(WRAPPER_DIR)/jmpvar.s: $(BUILD_DIR)/romlib.elf | $$(@D)/ + $(s)echo " VAR $@" + $(q)$(ROMLIB_GEN) genvar --output $@ $< -$(LIB_DIR)/libwrappers.a: $(WRAPPER_DIR)/jmpvar.o $(WRAPPER_OBJS) - @echo " AR $@" - $(Q)$(AR) -rc $@ $(WRAPPER_DIR)/jmpvar.o $(WRAPPER_OBJS) +$(LIB_DIR)/libwrappers.a: $(WRAPPER_DIR)/jmpvar.o $(WRAPPER_OBJS) | $$(@D)/ + $(s)echo " AR $@" + $(q)$(aarch64-ar) -rc $@ $(WRAPPER_DIR)/jmpvar.o $(WRAPPER_OBJS) -$(BUILD_DIR)/jmptbl.i: ../../$(PLAT_DIR)/jmptbl.i - @echo " PRE $@" - $(Q)$(ROMLIB_GEN) pre --output $@ --deps $(BUILD_DIR)/jmptbl.d $< +$(BUILD_DIR)/jmptbl.i: ../../$(PLAT_DIR)/jmptbl.i | $$(@D)/ + $(s)echo " PRE $@" + $(q)$(ROMLIB_GEN) pre --output $@ --deps $(BUILD_DIR)/jmptbl.d $< -$(BUILD_DIR)/wrappers.stamp: $(BUILD_DIR)/jmptbl.i - @echo " WRP $<" - $(Q)$(ROMLIB_GEN) genwrappers --bti=$(ENABLE_BTI) -b $(WRAPPER_DIR) $< - @touch $@ +$(WRAPPER_SOURCES) $&: $(BUILD_DIR)/jmptbl.i | $$(@D)/ + $(s)echo " WRP $<" + $(q)$(ROMLIB_GEN) genwrappers --bti=$(ENABLE_BTI) -b $(WRAPPER_DIR) $< -$(WRAPPER_SOURCES): $(BUILD_DIR)/wrappers.stamp +$(WRAPPER_OBJS): $(WRAPPER_DIR)/%.o: $(WRAPPER_DIR)/%.s | $$(@D)/ -$(WRAPPER_OBJS): $(WRAPPER_SOURCES) $(BUILD_DIR)/wrappers.stamp +$(BUILD_DIR)/jmptbl.s: $(BUILD_DIR)/jmptbl.i | $$(@D)/ + $(s)echo " TBL $@" + $(q)$(ROMLIB_GEN) gentbl --output $@ --bti=$(ENABLE_BTI) $< -$(BUILD_DIR)/jmptbl.s: $(BUILD_DIR)/jmptbl.i - @echo " TBL $@" - $(Q)$(ROMLIB_GEN) gentbl --output $@ --bti=$(ENABLE_BTI) $< +$(BUILD_DIR)/romlib.ldflags: ../../$(PLAT_DIR)/jmptbl.i | $$(@D)/ + $(s)echo " LDFLAGS $@" + $(q)$(ROMLIB_GEN) link-flags $< > $@ clean: - @rm -f $(BUILD_DIR)/* + $(q)rm -f $(BUILD_DIR)/* -include $(BUILD_DIR)/romlib.d -include $(BUILD_DIR)/jmptbl.d diff --git a/lib/romlib/romlib_generator.py b/lib/romlib/romlib_generator.py index 0682dd49..8d2e88d5 100755 --- a/lib/romlib/romlib_generator.py +++ b/lib/romlib/romlib_generator.py @@ -182,6 +182,22 @@ class TableGenerator(RomlibApplication): template_name = "jmptbl_entry_" + item["type"] + bti + ".S" output_file.write(self.build_template(template_name, item, True)) +class LinkArgs(RomlibApplication): + """ Generates the link arguments to wrap functions. """ + + def __init__(self, prog): + RomlibApplication.__init__(self, prog) + self.args.add_argument("file", help="Input file") + + def main(self): + index_file_parser = IndexFileParser() + index_file_parser.parse(self.config.file) + + fns = [item["function_name"] for item in index_file_parser.items + if not item["patch"] and item["type"] != "reserved"] + + print(" ".join("-Wl,--wrap " + f for f in fns)) + class WrapperGenerator(RomlibApplication): """ Generates a wrapper function for each entry in the index file except for the ones that contain @@ -214,21 +230,19 @@ class WrapperGenerator(RomlibApplication): if item["type"] == "reserved" or item["patch"]: continue - asm = self.config.b + "/" + item["function_name"] + ".s" - if self.config.list: - # Only listing files - files.append(asm) - else: - with open(asm, "w") as asm_file: - # The jump instruction is 4 bytes but BTI requires and extra instruction so - # this makes it 8 bytes per entry. - function_offset = item_index * (8 if self.config.bti else 4) + if not self.config.list: + # The jump instruction is 4 bytes but BTI requires and extra instruction so + # this makes it 8 bytes per entry. + function_offset = item_index * (8 if self.config.bti else 4) - item["function_offset"] = function_offset - asm_file.write(self.build_template("wrapper" + bti + ".S", item)) + item["function_offset"] = function_offset + files.append(self.build_template("wrapper" + bti + ".S", item)) if self.config.list: - print(" ".join(files)) + print(self.config.b + "/wrappers.s") + else: + with open(self.config.b + "/wrappers.s", "w") as asm_file: + asm_file.write("\n".join(files)) class VariableGenerator(RomlibApplication): """ Generates the jump table global variable with the absolute address in ROM. """ @@ -258,7 +272,8 @@ class VariableGenerator(RomlibApplication): if __name__ == "__main__": APPS = {"genvar": VariableGenerator, "pre": IndexPreprocessor, - "gentbl": TableGenerator, "genwrappers": WrapperGenerator} + "gentbl": TableGenerator, "genwrappers": WrapperGenerator, + "link-flags": LinkArgs} if len(sys.argv) < 2 or sys.argv[1] not in APPS: print("usage: romlib_generator.py [%s] [args]" % "|".join(APPS.keys()), file=sys.stderr) diff --git a/lib/romlib/templates/wrapper.S b/lib/romlib/templates/wrapper.S index 734a68a2..576474a6 100644 --- a/lib/romlib/templates/wrapper.S +++ b/lib/romlib/templates/wrapper.S @@ -3,8 +3,9 @@ * * SPDX-License-Identifier: BSD-3-Clause */ - .globl ${function_name} -${function_name}: + .section .text.__wrap_${function_name} + .globl __wrap_${function_name} +__wrap_${function_name}: ldr x17, =jmptbl mov x16, #${function_offset} ldr x17, [x17] diff --git a/lib/romlib/templates/wrapper_bti.S b/lib/romlib/templates/wrapper_bti.S index ba9b11ce..0dc316cb 100644 --- a/lib/romlib/templates/wrapper_bti.S +++ b/lib/romlib/templates/wrapper_bti.S @@ -3,8 +3,9 @@ * * SPDX-License-Identifier: BSD-3-Clause */ - .globl ${function_name} -${function_name}: + .section .text.__wrap_${function_name} + .globl __wrap_${function_name} +__wrap_${function_name}: bti jc ldr x17, =jmptbl mov x16, #${function_offset} diff --git a/lib/transfer_list/transfer_list.c b/lib/transfer_list/transfer_list.c index e38bf742..8d82d259 100644 --- a/lib/transfer_list/transfer_list.c +++ b/lib/transfer_list/transfer_list.c @@ -4,6 +4,7 @@ * SPDX-License-Identifier: BSD-3-Clause */ +#include <arch.h> #include <assert.h> #include <inttypes.h> #include <string.h> @@ -20,28 +21,66 @@ void transfer_list_dump(struct transfer_list_header *tl) if (!tl) { return; } - NOTICE("Dump transfer list:\n"); - NOTICE("signature 0x%x\n", tl->signature); - NOTICE("checksum 0x%x\n", tl->checksum); - NOTICE("version 0x%x\n", tl->version); - NOTICE("hdr_size 0x%x\n", tl->hdr_size); - NOTICE("alignment 0x%x\n", tl->alignment); - NOTICE("size 0x%x\n", tl->size); - NOTICE("max_size 0x%x\n", tl->max_size); + INFO("Dump transfer list:\n"); + INFO("signature 0x%x\n", tl->signature); + INFO("checksum 0x%x\n", tl->checksum); + INFO("version 0x%x\n", tl->version); + INFO("hdr_size 0x%x\n", tl->hdr_size); + INFO("alignment 0x%x\n", tl->alignment); + INFO("size 0x%x\n", tl->size); + INFO("max_size 0x%x\n", tl->max_size); + INFO("flags 0x%x\n", tl->flags); while (true) { te = transfer_list_next(tl, te); if (!te) { break; } - NOTICE("Entry %d:\n", i++); - NOTICE("tag_id 0x%x\n", te->tag_id); - NOTICE("hdr_size 0x%x\n", te->hdr_size); - NOTICE("data_size 0x%x\n", te->data_size); - NOTICE("data_addr 0x%lx\n", - (unsigned long)transfer_list_entry_data(te)); + INFO("Entry %d:\n", i++); + INFO("tag_id 0x%x\n", te->tag_id); + INFO("hdr_size 0x%x\n", te->hdr_size); + INFO("data_size 0x%x\n", te->data_size); + INFO("data_addr 0x%lx\n", + (unsigned long)transfer_list_entry_data(te)); } } +/******************************************************************************* + * Set the handoff arguments according to the transfer list payload + * Return pointer to the entry point info if arguments are set properly + * or NULL if not + ******************************************************************************/ +entry_point_info_t * +transfer_list_set_handoff_args(struct transfer_list_header *tl, + entry_point_info_t *ep_info) +{ + struct transfer_list_entry *te = NULL; + void *dt = NULL; + + if (!ep_info || !tl || transfer_list_check_header(tl) == TL_OPS_NON) { + return NULL; + } + + te = transfer_list_find(tl, TL_TAG_FDT); + dt = transfer_list_entry_data(te); + +#ifdef __aarch64__ + if (GET_RW(ep_info->spsr) == MODE_RW_64) { + ep_info->args.arg0 = (uintptr_t)dt; + ep_info->args.arg1 = TRANSFER_LIST_HANDOFF_X1_VALUE(REGISTER_CONVENTION_VERSION); + ep_info->args.arg2 = 0; + } else +#endif + { + ep_info->args.arg0 = 0; + ep_info->args.arg1 = TRANSFER_LIST_HANDOFF_R1_VALUE(REGISTER_CONVENTION_VERSION); + ep_info->args.arg2 = (uintptr_t)dt; + } + + ep_info->args.arg3 = (uintptr_t)tl; + + return ep_info; +} + /******************************************************************************* * Creating a transfer list in a reserved memory region specified * Compliant to 2.4.5 of Firmware handoff specification (v0.9) @@ -65,9 +104,10 @@ struct transfer_list_header *transfer_list_init(void *addr, size_t max_size) tl->signature = TRANSFER_LIST_SIGNATURE; tl->version = TRANSFER_LIST_VERSION; tl->hdr_size = sizeof(*tl); - tl->alignment = TRANSFER_LIST_INIT_MAX_ALIGN; // initial max align - tl->size = sizeof(*tl); // initial size is the size of header + tl->alignment = TRANSFER_LIST_INIT_MAX_ALIGN; /* initial max align */ + tl->size = sizeof(*tl); /* initial size is the size of header */ tl->max_size = max_size; + tl->flags = TL_FLAGS_HAS_CHECKSUM; transfer_list_update_checksum(tl); @@ -77,11 +117,11 @@ struct transfer_list_header *transfer_list_init(void *addr, size_t max_size) /******************************************************************************* * Relocating a transfer list to a reserved memory region specified * Compliant to 2.4.6 of Firmware handoff specification (v0.9) - * Return true on success or false on error + * Return pointer to the relocated transfer list or NULL on error ******************************************************************************/ -struct transfer_list_header *transfer_list_relocate( - struct transfer_list_header *tl, - void *addr, size_t max_size) +struct transfer_list_header * +transfer_list_relocate(struct transfer_list_header *tl, void *addr, + size_t max_size) { uintptr_t new_addr, align_mask, align_off; struct transfer_list_header *new_tl; @@ -101,7 +141,7 @@ struct transfer_list_header *transfer_list_relocate( new_max_size = max_size - (new_addr - (uintptr_t)addr); - // the new space is not sufficient for the tl + /* the new space is not sufficient for the tl */ if (tl->size > new_max_size) { return NULL; } @@ -120,37 +160,39 @@ struct transfer_list_header *transfer_list_relocate( * Compliant to 2.4.1 of Firmware handoff specification (v0.9) * Return transfer list operation status code ******************************************************************************/ -enum transfer_list_ops transfer_list_check_header( - const struct transfer_list_header *tl) +enum transfer_list_ops +transfer_list_check_header(const struct transfer_list_header *tl) { if (!tl) { return TL_OPS_NON; } if (tl->signature != TRANSFER_LIST_SIGNATURE) { - ERROR("Bad transfer list signature %#"PRIx32"\n", + ERROR("Bad transfer list signature %#" PRIx32 "\n", tl->signature); return TL_OPS_NON; } if (!tl->max_size) { - ERROR("Bad transfer list max size %#"PRIx32"\n", + ERROR("Bad transfer list max size %#" PRIx32 "\n", tl->max_size); return TL_OPS_NON; } if (tl->size > tl->max_size) { - ERROR("Bad transfer list size %#"PRIx32"\n", tl->size); + ERROR("Bad transfer list size %#" PRIx32 "\n", tl->size); return TL_OPS_NON; } if (tl->hdr_size != sizeof(struct transfer_list_header)) { - ERROR("Bad transfer list header size %#"PRIx32"\n", tl->hdr_size); + ERROR("Bad transfer list header size %#" PRIx32 "\n", + tl->hdr_size); return TL_OPS_NON; } if (!transfer_list_verify_checksum(tl)) { - ERROR("Bad transfer list checksum %#"PRIx32"\n", tl->checksum); + ERROR("Bad transfer list checksum %#" PRIx32 "\n", + tl->checksum); return TL_OPS_NON; } @@ -190,14 +232,13 @@ struct transfer_list_entry *transfer_list_next(struct transfer_list_header *tl, if (last) { va = (uintptr_t)last; - // check if the total size overflow - if (add_overflow(last->hdr_size, - last->data_size, &sz)) { + /* check if the total size overflow */ + if (add_overflow(last->hdr_size, last->data_size, &sz)) { return NULL; } - // roundup to the next entry - if (add_with_round_up_overflow(va, sz, - TRANSFER_LIST_GRANULE, &va)) { + /* roundup to the next entry */ + if (add_with_round_up_overflow(va, sz, TRANSFER_LIST_GRANULE, + &va)) { return NULL; } } else { @@ -207,9 +248,8 @@ struct transfer_list_entry *transfer_list_next(struct transfer_list_header *tl, te = (struct transfer_list_entry *)va; if (va + sizeof(*te) > tl_ev || te->hdr_size < sizeof(*te) || - add_overflow(te->hdr_size, te->data_size, &sz) || - add_overflow(va, sz, &ev) || - ev > tl_ev) { + add_overflow(te->hdr_size, te->data_size, &sz) || + add_overflow(va, sz, &ev) || ev > tl_ev) { return NULL; } @@ -226,10 +266,6 @@ static uint8_t calc_byte_sum(const struct transfer_list_header *tl) uint8_t cs = 0; size_t n = 0; - if (!tl) { - return 0; - } - for (n = 0; n < tl->size; n++) { cs += b[n]; } @@ -245,7 +281,7 @@ void transfer_list_update_checksum(struct transfer_list_header *tl) { uint8_t cs; - if (!tl) { + if (!tl || !(tl->flags & TL_FLAGS_HAS_CHECKSUM)) { return; } @@ -262,6 +298,14 @@ void transfer_list_update_checksum(struct transfer_list_header *tl) ******************************************************************************/ bool transfer_list_verify_checksum(const struct transfer_list_header *tl) { + if (!tl) { + return false; + } + + if (!(tl->flags & TL_FLAGS_HAS_CHECKSUM)) { + return true; + } + return !calc_byte_sum(tl); } @@ -284,27 +328,31 @@ bool transfer_list_set_data_size(struct transfer_list_header *tl, } tl_old_ev = (uintptr_t)tl + tl->size; - // calculate the old and new end of TE - // both must be roundup to align with TRANSFER_LIST_GRANULE + /* + * calculate the old and new end of TE + * both must be roundup to align with TRANSFER_LIST_GRANULE + */ if (add_overflow(te->hdr_size, te->data_size, &sz) || - add_with_round_up_overflow((uintptr_t)te, sz, - TRANSFER_LIST_GRANULE, &old_ev)) { + add_with_round_up_overflow((uintptr_t)te, sz, TRANSFER_LIST_GRANULE, + &old_ev)) { return false; } if (add_overflow(te->hdr_size, new_data_size, &sz) || - add_with_round_up_overflow((uintptr_t)te, sz, - TRANSFER_LIST_GRANULE, &new_ev)) { + add_with_round_up_overflow((uintptr_t)te, sz, TRANSFER_LIST_GRANULE, + &new_ev)) { return false; } if (new_ev > old_ev) { - // move distance should be roundup - // to meet the requirement of TE data max alignment - // ensure that the increased size doesn't exceed - // the max size of TL + /* + * move distance should be roundup + * to meet the requirement of TE data max alignment + * ensure that the increased size doesn't exceed + * the max size of TL + */ mov_dis = new_ev - old_ev; - if (round_up_overflow(mov_dis, 1 << tl->alignment, - &mov_dis) || tl->size + mov_dis > tl->max_size) { + if (round_up_overflow(mov_dis, 1 << tl->alignment, &mov_dis) || + tl->size + mov_dis > tl->max_size) { return false; } ru_new_ev = old_ev + mov_dis; @@ -316,10 +364,9 @@ bool transfer_list_set_data_size(struct transfer_list_header *tl, } if (gap >= sizeof(*dummy_te)) { - // create a dummy TE to fill up the gap + /* create a dummy TE to fill up the gap */ dummy_te = (struct transfer_list_entry *)new_ev; dummy_te->tag_id = TL_TAG_EMPTY; - dummy_te->reserved0 = 0; dummy_te->hdr_size = sizeof(*dummy_te); dummy_te->data_size = gap - sizeof(*dummy_te); } @@ -335,13 +382,12 @@ bool transfer_list_set_data_size(struct transfer_list_header *tl, * Return true on success or false on error ******************************************************************************/ bool transfer_list_rem(struct transfer_list_header *tl, - struct transfer_list_entry *te) + struct transfer_list_entry *te) { if (!tl || !te || (uintptr_t)te > (uintptr_t)tl + tl->size) { return false; } te->tag_id = TL_TAG_EMPTY; - te->reserved0 = 0; transfer_list_update_checksum(tl); return true; } @@ -352,7 +398,7 @@ bool transfer_list_rem(struct transfer_list_header *tl, * Return pointer to the added transfer entry or NULL on error ******************************************************************************/ struct transfer_list_entry *transfer_list_add(struct transfer_list_header *tl, - uint16_t tag_id, + uint32_t tag_id, uint32_t data_size, const void *data) { @@ -369,23 +415,24 @@ struct transfer_list_entry *transfer_list_add(struct transfer_list_header *tl, tl_ev = (uintptr_t)tl + tl->size; ev = tl_ev; - // skip the step 1 (optional step) - // new TE will be added into the tail + /* + * skip the step 1 (optional step) + * new TE will be added into the tail + */ if (add_overflow(sizeof(*te), data_size, &sz) || - add_with_round_up_overflow(ev, sz, - TRANSFER_LIST_GRANULE, &ev) || ev > max_tl_ev) { + add_with_round_up_overflow(ev, sz, TRANSFER_LIST_GRANULE, &ev) || + ev > max_tl_ev) { return NULL; } te = (struct transfer_list_entry *)tl_ev; te->tag_id = tag_id; - te->reserved0 = 0; te->hdr_size = sizeof(*te); te->data_size = data_size; tl->size += ev - tl_ev; if (data) { - // get TE data pointer + /* get TE data pointer */ te_data = transfer_list_entry_data(te); if (!te_data) { return NULL; @@ -404,10 +451,10 @@ struct transfer_list_entry *transfer_list_add(struct transfer_list_header *tl, * Compliant to 2.4.4 of Firmware handoff specification (v0.9) * Return pointer to the added transfer entry or NULL on error ******************************************************************************/ -struct transfer_list_entry *transfer_list_add_with_align( - struct transfer_list_header *tl, - uint16_t tag_id, uint32_t data_size, - const void *data, uint8_t alignment) +struct transfer_list_entry * +transfer_list_add_with_align(struct transfer_list_header *tl, uint32_t tag_id, + uint32_t data_size, const void *data, + uint8_t alignment) { struct transfer_list_entry *te = NULL; uintptr_t tl_ev, ev, new_tl_ev; @@ -421,15 +468,17 @@ struct transfer_list_entry *transfer_list_add_with_align( ev = tl_ev + sizeof(struct transfer_list_entry); if (!is_aligned(ev, 1 << alignment)) { - // TE data address is not aligned to the new alignment - // fill the gap with an empty TE as a placeholder before - // adding the desire TE + /* + * TE data address is not aligned to the new alignment + * fill the gap with an empty TE as a placeholder before + * adding the desire TE + */ new_tl_ev = round_up(ev, 1 << alignment) - - sizeof(struct transfer_list_entry); - dummy_te_data_sz = new_tl_ev - tl_ev - - sizeof(struct transfer_list_entry); + sizeof(struct transfer_list_entry); + dummy_te_data_sz = + new_tl_ev - tl_ev - sizeof(struct transfer_list_entry); if (!transfer_list_add(tl, TL_TAG_EMPTY, dummy_te_data_sz, - NULL)) { + NULL)) { return NULL; } } @@ -450,13 +499,13 @@ struct transfer_list_entry *transfer_list_add_with_align( * Return pointer to the found transfer entry or NULL on error ******************************************************************************/ struct transfer_list_entry *transfer_list_find(struct transfer_list_header *tl, - uint16_t tag_id) + uint32_t tag_id) { struct transfer_list_entry *te = NULL; do { te = transfer_list_next(tl, te); - } while (te && (te->tag_id != tag_id || te->reserved0 != 0)); + } while (te && (te->tag_id != tag_id)); return te; } diff --git a/lib/transfer_list/transfer_list.mk b/lib/transfer_list/transfer_list.mk index 42574e85..3ec4df23 100644 --- a/lib/transfer_list/transfer_list.mk +++ b/lib/transfer_list/transfer_list.mk @@ -1,5 +1,5 @@ # -# Copyright (c) 2023, Arm Limited and Contributors. All rights reserved. +# Copyright (c) 2023-2024, Arm Limited and Contributors. All rights reserved. # # SPDX-License-Identifier: BSD-3-Clause # @@ -15,6 +15,7 @@ TRANSFER_LIST_SOURCES += $(addprefix lib/transfer_list/, \ BL31_SOURCES += $(TRANSFER_LIST_SOURCES) BL2_SOURCES += $(TRANSFER_LIST_SOURCES) +BL1_SOURCES += $(TRANSFER_LIST_SOURCES) endif # TRANSFER_LIST diff --git a/lib/xlat_mpu/aarch64/xlat_mpu_arch.c b/lib/xlat_mpu/aarch64/xlat_mpu_arch.c index 5a2120bc..b462de0d 100644 --- a/lib/xlat_mpu/aarch64/xlat_mpu_arch.c +++ b/lib/xlat_mpu/aarch64/xlat_mpu_arch.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, Arm Limited and Contributors. All rights reserved. + * Copyright (c) 2021-2024, Arm Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -27,7 +27,7 @@ uintptr_t xlat_get_min_virt_addr_space_size(void) { uintptr_t ret; - if (is_armv8_4_ttst_present()) { + if (is_feat_ttst_present()) { ret = MIN_VIRT_ADDR_SPACE_SIZE_TTST; } else { ret = MIN_VIRT_ADDR_SPACE_SIZE; diff --git a/lib/xlat_tables/aarch64/xlat_tables.c b/lib/xlat_tables/aarch64/xlat_tables.c index 4dbfc118..f2072662 100644 --- a/lib/xlat_tables/aarch64/xlat_tables.c +++ b/lib/xlat_tables/aarch64/xlat_tables.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2021, Arm Limited and Contributors. All rights reserved. + * Copyright (c) 2014-2024, Arm Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -66,7 +66,7 @@ static unsigned long long calc_physical_addr_size_bits( */ static const unsigned int pa_range_bits_arr[] = { PARANGE_0000, PARANGE_0001, PARANGE_0010, PARANGE_0011, PARANGE_0100, - PARANGE_0101, PARANGE_0110 + PARANGE_0101, PARANGE_0110, PARANGE_0111 }; static unsigned long long get_max_supported_pa(void) @@ -87,7 +87,7 @@ static uintptr_t xlat_get_min_virt_addr_space_size(void) { uintptr_t ret; - if (is_armv8_4_ttst_present()) + if (is_feat_ttst_present()) ret = MIN_VIRT_ADDR_SPACE_SIZE_TTST; else ret = MIN_VIRT_ADDR_SPACE_SIZE; diff --git a/lib/xlat_tables_v2/aarch32/xlat_tables_arch.c b/lib/xlat_tables_v2/aarch32/xlat_tables_arch.c index 920754b4..b63543c9 100644 --- a/lib/xlat_tables_v2/aarch32/xlat_tables_arch.c +++ b/lib/xlat_tables_v2/aarch32/xlat_tables_arch.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2021, Arm Limited and Contributors. All rights reserved. + * Copyright (c) 2017-2024, Arm Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -248,7 +248,7 @@ void setup_mmu_cfg(uint64_t *params, unsigned int flags, /* Set TTBR0 bits as well */ ttbr0 = (uint64_t)(uintptr_t) base_table; - if (is_armv8_2_ttcnp_present()) { + if (is_feat_ttcnp_present()) { /* Enable CnP bit so as to share page tables with all PEs. */ ttbr0 |= TTBR_CNP_BIT; } diff --git a/lib/xlat_tables_v2/aarch64/xlat_tables_arch.c b/lib/xlat_tables_v2/aarch64/xlat_tables_arch.c index bb6a35cf..7321fd72 100644 --- a/lib/xlat_tables_v2/aarch64/xlat_tables_arch.c +++ b/lib/xlat_tables_v2/aarch64/xlat_tables_arch.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2023, Arm Limited and Contributors. All rights reserved. + * Copyright (c) 2017-2024, Arm Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -22,19 +22,14 @@ */ bool xlat_arch_is_granule_size_supported(size_t size) { - unsigned int tgranx; - if (size == PAGE_SIZE_4KB) { - tgranx = read_id_aa64mmfr0_el0_tgran4_field(); /* MSB of TGRAN4 field will be '1' for unsupported feature */ - return (tgranx < 8U); + return is_feat_tgran4K_present(); } else if (size == PAGE_SIZE_16KB) { - tgranx = read_id_aa64mmfr0_el0_tgran16_field(); - return (tgranx >= ID_AA64MMFR0_EL1_TGRAN16_SUPPORTED); + return is_feat_tgran16K_present(); } else if (size == PAGE_SIZE_64KB) { - tgranx = read_id_aa64mmfr0_el0_tgran64_field(); /* MSB of TGRAN64 field will be '1' for unsupported feature */ - return (tgranx < 8U); + return is_feat_tgran64K_present(); } else { return false; } @@ -114,7 +109,7 @@ unsigned long long tcr_physical_addr_size_bits(unsigned long long max_addr) */ static const unsigned int pa_range_bits_arr[] = { PARANGE_0000, PARANGE_0001, PARANGE_0010, PARANGE_0011, PARANGE_0100, - PARANGE_0101, PARANGE_0110 + PARANGE_0101, PARANGE_0110, PARANGE_0111 }; unsigned long long xlat_arch_get_max_supported_pa(void) @@ -135,7 +130,7 @@ uintptr_t xlat_get_min_virt_addr_space_size(void) { uintptr_t ret; - if (is_armv8_4_ttst_present()) + if (is_feat_ttst_present()) ret = MIN_VIRT_ADDR_SPACE_SIZE_TTST; else ret = MIN_VIRT_ADDR_SPACE_SIZE; @@ -312,7 +307,7 @@ void setup_mmu_cfg(uint64_t *params, unsigned int flags, /* Set TTBR bits as well */ ttbr0 = (uint64_t) base_table; - if (is_armv8_2_ttcnp_present()) { + if (is_feat_ttcnp_present()) { /* Enable CnP bit so as to share page tables with all PEs. */ ttbr0 |= TTBR_CNP_BIT; } diff --git a/lib/xlat_tables_v2/xlat_tables_core.c b/lib/xlat_tables_v2/xlat_tables_core.c index 3a9c0588..971dba48 100644 --- a/lib/xlat_tables_v2/xlat_tables_core.c +++ b/lib/xlat_tables_v2/xlat_tables_core.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2021, Arm Limited and Contributors. All rights reserved. + * Copyright (c) 2017-2024, Arm Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -214,7 +214,7 @@ uint64_t xlat_desc(const xlat_ctx_t *ctx, uint32_t attr, /* Set GP bit for block and page code entries * if BTI mechanism is implemented. */ - if (is_armv8_5_bti_present() && + if (is_feat_bti_present() && ((attr & (MT_TYPE_MASK | MT_RW | MT_EXECUTE_NEVER)) == MT_CODE)) { desc |= GP; diff --git a/lib/xlat_tables_v2/xlat_tables_utils.c b/lib/xlat_tables_v2/xlat_tables_utils.c index f3a53ccd..a3b913c2 100644 --- a/lib/xlat_tables_v2/xlat_tables_utils.c +++ b/lib/xlat_tables_v2/xlat_tables_utils.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2021, Arm Limited and Contributors. All rights reserved. + * Copyright (c) 2017-2024, Arm Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -13,6 +13,7 @@ #include <platform_def.h> +#include <arch_features.h> #include <arch_helpers.h> #include <common/debug.h> #include <lib/utils_def.h> @@ -419,10 +420,59 @@ static int xlat_get_mem_attributes_internal(const xlat_ctx_t *ctx, *attributes |= MT_USER; } - uint64_t ns_bit = (desc >> NS_SHIFT) & 1U; + uint64_t ns_bit = (desc >> NS_SHIFT) & 1ULL; - if (ns_bit == 1U) +#if ENABLE_RME + uint64_t nse_bit = (desc >> NSE_SHIFT) & 1ULL; + uint32_t sec_state = (uint32_t)(ns_bit | (nse_bit << 1ULL)); + +/* + * ========================================================= + * NSE NS | Output PA space + * ========================================================= + * 0 0 | Secure (if S-EL2 is present, else invalid) + * 0 1 | Non-secure + * 1 0 | Root + * 1 1 | Realm + *========================================================== + */ + switch (sec_state) { + case 0U: + /* + * We expect to get Secure mapping on an RME system only if + * S-EL2 is enabled. + * Hence panic() if we hit the case without EEL2 being enabled. + */ + if ((read_scr_el3() & SCR_EEL2_BIT) == 0ULL) { + ERROR("A secure descriptor is not supported when" + "FEAT_RME is implemented and FEAT_SEL2 is" + "not enabled\n"); + panic(); + } else { + *attributes |= MT_SECURE; + } + break; + case 1U: *attributes |= MT_NS; + break; + case 2U: + *attributes |= MT_ROOT; + break; + case 3U: + *attributes |= MT_REALM; + break; + default: + /* unreachable code */ + assert(false); + break; + } +#else /* !ENABLE_RME */ + if (ns_bit == 1ULL) { + *attributes |= MT_NS; + } else { + *attributes |= MT_SECURE; + } +#endif /* ENABLE_RME */ uint64_t xn_mask = xlat_arch_regime_get_xn_desc(ctx->xlat_regime); diff --git a/licenses/LICENSE-APACHE-2.0.txt b/licenses/LICENSE-APACHE-2.0.txt new file mode 100644 index 00000000..d6456956 --- /dev/null +++ b/licenses/LICENSE-APACHE-2.0.txt @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/make_helpers/arch_features.mk b/make_helpers/arch_features.mk index a337e767..57609d51 100644 --- a/make_helpers/arch_features.mk +++ b/make_helpers/arch_features.mk @@ -1,5 +1,5 @@ # -# Copyright (c) 2022-2023, Arm Limited. All rights reserved. +# Copyright (c) 2022-2024, Arm Limited. All rights reserved. # # SPDX-License-Identifier: BSD-3-Clause # @@ -8,65 +8,129 @@ # and enables them based on the configured architecture version. # This file follows the following format: -# - Enable mandatory feature if applicable to an Arch Version. +# - Enable mandatory feature if not updated, as applicable to an Arch Version. # - By default disable any mandatory features if they have not been defined yet. # - Disable or enable any optional feature this would be enabled/disabled if needed by platform. # ################################################################################ -# Enable Mandatory features based on Arch versions. +# Enable Mandatory features if not updated yet, based on Arch versions. ################################################################################ # # Enable the features which are mandatory from ARCH version 8.1 and upwards. ifeq "8.1" "$(word 1, $(sort 8.1 $(ARM_ARCH_MAJOR).$(ARM_ARCH_MINOR)))" -ENABLE_FEAT_PAN := 1 -ENABLE_FEAT_VHE := 1 +armv8-1-a-feats := ENABLE_FEAT_PAN ENABLE_FEAT_VHE + +FEAT_LIST := ${armv8-1-a-feats} endif # Enable the features which are mandatory from ARCH version 8.2 and upwards. ifeq "8.2" "$(word 1, $(sort 8.2 $(ARM_ARCH_MAJOR).$(ARM_ARCH_MINOR)))" -ENABLE_FEAT_RAS := 1 +armv8-2-a-feats := ENABLE_FEAT_RAS +# 8.1 Compliant +armv8-2-a-feats += ${armv8-1-a-feats} + +FEAT_LIST := ${armv8-2-a-feats} +endif + +# Enable the features which are mandatory from ARCH version 8.3 and upwards. +ifeq "8.3" "$(word 1, $(sort 8.3 $(ARM_ARCH_MAJOR).$(ARM_ARCH_MINOR)))" +# 8.2 Compliant +armv8-3-a-feats += ${armv8-2-a-feats} + +FEAT_LIST := ${armv8-3-a-feats} endif # Enable the features which are mandatory from ARCH version 8.4 and upwards. ifeq "8.4" "$(word 1, $(sort 8.4 $(ARM_ARCH_MAJOR).$(ARM_ARCH_MINOR)))" -ENABLE_FEAT_SEL2 := 1 -ENABLE_TRF_FOR_NS := 1 -ENABLE_FEAT_DIT := 1 +armv8-4-a-feats := ENABLE_FEAT_SEL2 ENABLE_TRF_FOR_NS ENABLE_FEAT_DIT +# 8.3 Compliant +armv8-4-a-feats += ${armv8-3-a-feats} + +FEAT_LIST := ${armv8-4-a-feats} endif # Enable the features which are mandatory from ARCH version 8.5 and upwards. ifeq "8.5" "$(word 1, $(sort 8.5 $(ARM_ARCH_MAJOR).$(ARM_ARCH_MINOR)))" -ENABLE_FEAT_RNG := 1 -ENABLE_FEAT_SB := 1 +armv8-5-a-feats := ENABLE_FEAT_RNG ENABLE_FEAT_SB +# 8.4 Compliant +armv8-5-a-feats += ${armv8-4-a-feats} +FEAT_LIST := ${armv8-5-a-feats} # Enable Memory tagging, Branch Target Identification for aarch64 only. ifeq ($(ARCH), aarch64) - mem_tag_arch_support := yes + mem_tag_arch_support ?= yes endif #(ARCH=aarch64) endif # Enable the features which are mandatory from ARCH version 8.6 and upwards. ifeq "8.6" "$(word 1, $(sort 8.6 $(ARM_ARCH_MAJOR).$(ARM_ARCH_MINOR)))" -ENABLE_FEAT_ECV := 1 -ENABLE_FEAT_FGT := 1 +armv8-6-a-feats := ENABLE_FEAT_ECV ENABLE_FEAT_FGT +# 8.5 Compliant +armv8-6-a-feats += ${armv8-5-a-feats} +FEAT_LIST := ${armv8-6-a-feats} endif # Enable the features which are mandatory from ARCH version 8.7 and upwards. ifeq "8.7" "$(word 1, $(sort 8.7 $(ARM_ARCH_MAJOR).$(ARM_ARCH_MINOR)))" -ENABLE_FEAT_HCX := 1 +armv8-7-a-feats := ENABLE_FEAT_HCX +# 8.6 Compliant +armv8-7-a-feats += ${armv8-6-a-feats} +FEAT_LIST := ${armv8-7-a-feats} +endif + +# Enable the features which are mandatory from ARCH version 8.8 and upwards. +ifeq "8.8" "$(word 1, $(sort 8.8 $(ARM_ARCH_MAJOR).$(ARM_ARCH_MINOR)))" +# 8.7 Compliant +armv8-8-a-feats += ${armv8-7-a-feats} +FEAT_LIST := ${armv8-8-a-feats} endif # Enable the features which are mandatory from ARCH version 8.9 and upwards. ifeq "8.9" "$(word 1, $(sort 8.9 $(ARM_ARCH_MAJOR).$(ARM_ARCH_MINOR)))" -ENABLE_FEAT_TCR2 := 1 +armv8-9-a-feats := ENABLE_FEAT_TCR2 ENABLE_FEAT_DEBUGV8P9 ENABLE_FEAT_SCTLR2 +# 8.8 Compliant +armv8-9-a-feats += ${armv8-8-a-feats} +FEAT_LIST := ${armv8-9-a-feats} +endif + +# Enable the features which are mandatory from ARCH version 9.0 and upwards. +ifeq "9.0" "$(word 1, $(sort 9.0 $(ARM_ARCH_MAJOR).$(ARM_ARCH_MINOR)))" +# 8.5 Compliant +armv9-0-a-feats += ${armv8-5-a-feats} +FEAT_LIST := ${armv9-0-a-feats} +endif + +# Enable the features which are mandatory from ARCH version 9.1 and upwards. +ifeq "9.1" "$(word 1, $(sort 9.1 $(ARM_ARCH_MAJOR).$(ARM_ARCH_MINOR)))" +# 8.6 and 9.0 Compliant +armv9-1-a-feats += ${armv8-6-a-feats} ${armv9-0-a-feats} +FEAT_LIST := ${armv9-1-a-feats} +endif + +# Enable the features which are mandatory from ARCH version 9.2 and upwards. +ifeq "9.2" "$(word 1, $(sort 9.2 $(ARM_ARCH_MAJOR).$(ARM_ARCH_MINOR)))" +# 8.7 and 9.1 Compliant +armv9-2-a-feats += ${armv8-7-a-feats} ${armv9-1-a-feats} +FEAT_LIST := ${armv9-2-a-feats} endif +# Enable the features which are mandatory from ARCH version 9.3 and upwards. +ifeq "9.3" "$(word 1, $(sort 9.3 $(ARM_ARCH_MAJOR).$(ARM_ARCH_MINOR)))" +# 8.8 and 9.2 Compliant +armv9-3-a-feats += ${armv8-8-a-feats} ${armv9-2-a-feats} +FEAT_LIST := ${armv9-3-a-feats} +endif + +# Set all FEAT_* in FEAT_LIST to '1' if they are not yet defined or set +# from build commandline options or platform makefile. +$(eval $(call default_ones, ${sort ${FEAT_LIST}})) + # ################################################################################ -# Set mandatory features by default to zero. +# Set mandatory features by default to zero, if they are not already updated. ################################################################################ # @@ -155,6 +219,9 @@ ENABLE_FEAT_HCX ?= 0 # Flag to enable access to TCR2 (FEAT_TCR2). ENABLE_FEAT_TCR2 ?= 0 +# Flag to enable access to SCTLR2 (FEAT_SCTLR2). +ENABLE_FEAT_SCTLR2 ?= 0 + # ################################################################################ # Optional Features defaulted to 0 or 2, if they are not enabled from @@ -169,6 +236,10 @@ ENABLE_FEAT_TCR2 ?= 0 # Flag to enable CSV2_2 extension. ENABLE_FEAT_CSV2_2 ?= 0 +# Flag to enable CSV2_3 extension. FEAT_CSV2_3 enables access to the +# SCXTNUM_ELx register. +ENABLE_FEAT_CSV2_3 ?= 0 + # By default, disable access of trace system registers from NS lower # ELs i.e. NS-EL2, or NS-EL1 if NS-EL2 implemented but unused if # system register trace is implemented. This feature is available if @@ -212,7 +283,7 @@ endif ENABLE_FEAT_AMU ?= 0 ENABLE_AMU_AUXILIARY_COUNTERS ?= 0 ENABLE_AMU_FCONF ?= 0 -AMU_RESTRICT_COUNTERS ?= 0 +AMU_RESTRICT_COUNTERS ?= 1 # Build option to enable MPAM for lower ELs. # Enabling it by default @@ -239,10 +310,9 @@ CTX_INCLUDE_NEVE_REGS ?= 0 # registers, by setting SCR_EL3.TRNDR. ENABLE_FEAT_RNG_TRAP ?= 0 -# Include Memory Tagging Extension registers in cpu context. This must be set -# to 1 if the platform wants to use this feature in the Secure world and MTE is -# enabled at ELX. -CTX_INCLUDE_MTE_REGS ?= 0 +# Enable FEAT_MTE2. This must be set to 1 if the platform wants +# to use this feature and is enabled at ELX. +ENABLE_FEAT_MTE2 ?= 0 #---- # 8.6 @@ -263,12 +333,22 @@ TWED_DELAY ?= 0 # Disable MTPMU if FEAT_MTPMU is supported. DISABLE_MTPMU ?= 0 +# Flag to enable FEAT_FGT2 (Fine Granular Traps 2) +ENABLE_FEAT_FGT2 ?= 0 + +# LoadStore64Bytes extension using the ACCDATA_EL1 system register +ENABLE_FEAT_LS64_ACCDATA ?= 0 + #---- -# 8.9 +# 8.8 #---- -# Flag to enable NoTagAccess memory region attribute for stage 2 of translation. -ENABLE_FEAT_MTE_PERM ?= 0 +# Flag to enable FEAT_THE (Translation Hardening Extension) +ENABLE_FEAT_THE ?= 0 + +#---- +# 8.9 +#---- # Flag to enable access to Stage 2 Permission Indirection (FEAT_S2PIE). ENABLE_FEAT_S2PIE ?= 0 @@ -282,13 +362,13 @@ ENABLE_FEAT_S2POE ?= 0 # Flag to enable access to Stage 1 Permission Overlay (FEAT_S1POE). ENABLE_FEAT_S1POE ?= 0 +# Flag to enable access to Arm v8.9 Debug extension +ENABLE_FEAT_DEBUGV8P9 ?= 0 + #---- # 9.0 #---- -# Flag to enable Realm Management Extension (FEAT_RME). -ENABLE_RME ?= 0 - # Scalable Matrix Extension for non-secure world. ENABLE_SME_FOR_NS ?= 0 @@ -314,6 +394,9 @@ endif # 9.2 #---- +# Flag to enable Realm Management Extension (FEAT_RME). +ENABLE_RME ?= 0 + # Scalable Matrix Extension version 2 for non-secure world. ENABLE_SME2_FOR_NS ?= 0 @@ -325,6 +408,12 @@ ENABLE_SME_FOR_SWD ?= 0 # if FEAT_BRBE is implemented. ENABLE_BRBE_FOR_NS ?= 0 +#---- +# 9.3 +#---- +# Flag to enable access to Arm v9.3 FEAT_D128 extension +ENABLE_FEAT_D128 ?= 0 + #---- #9.4 #---- diff --git a/make_helpers/build-rules.mk b/make_helpers/build-rules.mk new file mode 100644 index 00000000..d325b3a8 --- /dev/null +++ b/make_helpers/build-rules.mk @@ -0,0 +1,18 @@ +# +# Copyright (c) 2024, Arm Limited and Contributors. All rights reserved. +# +# SPDX-License-Identifier: BSD-3-Clause +# + +ifndef build-rules-mk + build-rules-mk := $(lastword $(MAKEFILE_LIST)) + + include $(dir $(build-rules-mk))common.mk + include $(dir $(build-rules-mk))utilities.mk + + .SECONDEXPANSION: + + %/: + $(s)echo ' MD '$(call escape-shell,$(abspath $@)) + $(q)mkdir -p $(call escape-shell,$@) +endif diff --git a/make_helpers/build_env.mk b/make_helpers/build_env.mk index 83093bd6..13acaae4 100644 --- a/make_helpers/build_env.mk +++ b/make_helpers/build_env.mk @@ -1,5 +1,5 @@ # -# Copyright (c) 2016, ARM Limited and Contributors. All rights reserved. +# Copyright (c) 2016-2024, Arm Limited and Contributors. All rights reserved. # # SPDX-License-Identifier: BSD-3-Clause # @@ -15,8 +15,6 @@ ifndef BUILD_ENV_MK COPY := $$(error "Replace COPY with call to SHELL_COPY or SHELL_COPY_TREE.") CP := $$(error "Replace CP with call to SHELL_COPY or SHELL_COPY_TREE.") DEL := $$(error "Replace DEL with call to SHELL_DELETE.") - MD := $$(error "Replace MD with call to MAKE_PREREQ_DIR.") - MKDIR := $$(error "Replace MKDIR with call to MAKE_PREREQ_DIR.") RD := $$(error "Replace RD with call to SHELL_REMOVE_DIR.") RM := $$(error "Replace RM with call to SHELL_DELETE.") RMDIR := $$(error "Replace RMDIR with call to SHELL_REMOVE_DIR.") @@ -47,7 +45,7 @@ ifndef BUILD_ENV_MK endif endif endif - include ${MAKE_HELPERS_DIRECTORY}${ENV_FILE_TO_INCLUDE} + include $(dir $(lastword $(MAKEFILE_LIST)))${ENV_FILE_TO_INCLUDE} ENV_FILE_TO_INCLUDE := ifndef SHELL_COPY @@ -62,9 +60,6 @@ ifndef BUILD_ENV_MK ifndef SHELL_DELETE $(error "SHELL_DELETE not defined for build environment.") endif - ifndef MAKE_PREREQ_DIR - $(error "MAKE_PREREQ_DIR not defined for build environment.") - endif ifndef SHELL_REMOVE_DIR $(error "SHELL_REMOVE_DIR not defined for build environment.") endif diff --git a/make_helpers/build_macros.mk b/make_helpers/build_macros.mk index 71cf18b0..d454efd9 100644 --- a/make_helpers/build_macros.mk +++ b/make_helpers/build_macros.mk @@ -1,5 +1,5 @@ # -# Copyright (c) 2015-2023, Arm Limited and Contributors. All rights reserved. +# Copyright (c) 2015-2024, Arm Limited and Contributors. All rights reserved. # # SPDX-License-Identifier: BSD-3-Clause # @@ -10,11 +10,6 @@ ifneq (${eval_available},T) $(error This makefile only works with a Make program that supports $$(eval)) endif -# Some utility macros for manipulating awkward (whitespace) characters. -blank := -space :=${blank} ${blank} -comma := , - # A user defined function to recursively search for a filename below a directory # $1 is the directory root of the recursive search (blank for current directory). # $2 is the file name to search for. @@ -22,22 +17,6 @@ define rwildcard $(strip $(foreach d,$(wildcard ${1}*),$(call rwildcard,${d}/,${2}) $(filter $(subst *,%,%${2}),${d}))) endef -# This table is used in converting lower case to upper case. -uppercase_table:=a,A b,B c,C d,D e,E f,F g,G h,H i,I j,J k,K l,L m,M n,N o,O p,P q,Q r,R s,S t,T u,U v,V w,W x,X y,Y z,Z - -# Internal macro used for converting lower case to upper case. -# $(1) = upper case table -# $(2) = String to convert -define uppercase_internal -$(if $(1),$$(subst $(firstword $(1)),$(call uppercase_internal,$(wordlist 2,$(words $(1)),$(1)),$(2))),$(2)) -endef - -# A macro for converting a string to upper case -# $(1) = String to convert -define uppercase -$(eval uppercase_result:=$(call uppercase_internal,$(uppercase_table),$(1)))$(uppercase_result) -endef - # Convenience function for setting a variable to 0 if not previously set # $(eval $(call default_zero,FOO)) define default_zero @@ -50,6 +29,18 @@ define default_zeros $(foreach var,$1,$(eval $(call default_zero,$(var)))) endef +# Convenience function for setting a variable to 1 if not previously set +# $(eval $(call default_one,FOO)) +define default_one + $(eval $(1) ?= 1) +endef + +# Convenience function for setting a list of variables to 1 if not previously set +# $(eval $(call default_ones,FOO BAR)) +define default_ones + $(foreach var,$1,$(eval $(call default_one,$(var)))) +endef + # Convenience function for adding build definitions # $(eval $(call add_define,FOO)) will have: # -DFOO if $(FOO) is empty; -DFOO=$(FOO) otherwise @@ -101,14 +92,16 @@ endef # Convenience function to check for a given linker option. An call to # $(call ld_option, --no-XYZ) will return --no-XYZ if supported by the linker -define ld_option - $(shell if $(LD) $(1) -v >/dev/null 2>&1; then echo $(1); fi ) -endef +ld_option = $(shell $($(ARCH)-ld) $(1) -Wl,--version >/dev/null 2>&1 || $($(ARCH)-ld) $(1) -v >/dev/null 2>&1 && echo $(1)) # Convenience function to check for a given compiler option. A call to # $(call cc_option, --no-XYZ) will return --no-XYZ if supported by the compiler +# NOTE: consider assigning to an immediately expanded temporary variable before +# assigning. This is because variables like TF_CFLAGS are recursively expanded +# and assigning this directly will cause it to be expanded every time the +# variable is used, potentially thrashing multicore performance. define cc_option - $(shell if $(CC) $(1) -c -x c /dev/null -o /dev/null >/dev/null 2>&1; then echo $(1); fi ) + $(shell if $($(ARCH)-cc) $(1) -c -x c /dev/null -o /dev/null >/dev/null 2>&1; then echo $(1); fi ) endef # CREATE_SEQ is a recursive function to create sequence of numbers from 1 to @@ -159,8 +152,8 @@ endef # $(2) = output encrypted firmware binary define ENCRYPT_FW $(2): $(1) enctool - $$(ECHO) " ENC $$<" - $$(Q)$$(ENCTOOL) $$(ENC_ARGS) -i $$< -o $$@ + $$(s)echo " ENC $$<" + $$(q)$$(ENCTOOL) $$(ENC_ARGS) -i $$< -o $$@ endef # TOOL_ADD_PAYLOAD appends the command line arguments required by fiptool to @@ -194,7 +187,7 @@ endef define TOOL_ADD_IMG_PAYLOAD -$(eval PRE_TOOL_FILTER := $($(call uppercase,$(1))_PRE_TOOL_FILTER)) +$(eval PRE_TOOL_FILTER := $($(1)_PRE_TOOL_FILTER)) ifneq ($(PRE_TOOL_FILTER),) @@ -231,7 +224,8 @@ endef define TOOL_ADD_IMG # Build option to specify the image filename (SCP_BL2, BL33, etc) # This is the uppercase form of the first parameter - $(eval _V := $(call uppercase,$(1))) + $(eval BL := $(call uppercase,$(1))) + $(eval _V := $(BL)) # $(check_$(1)_cmd) variable is executed in the check_$(1) target and also # is put into the ${CHECK_$(3)FIP_CMD} variable which is executed by the @@ -246,10 +240,10 @@ define TOOL_ADD_IMG ifeq ($(4),1) $(eval ENC_BIN := ${BUILD_PLAT}/$(1)_enc.bin) $(call ENCRYPT_FW,$(value $(_V)),$(ENC_BIN)) - $(call TOOL_ADD_IMG_PAYLOAD,$(1),$(value $(_V)),$(2),$(ENC_BIN),$(3), \ + $(call TOOL_ADD_IMG_PAYLOAD,$(BL),$(value $(_V)),$(2),$(ENC_BIN),$(3), \ $(ENC_BIN)) else - $(call TOOL_ADD_IMG_PAYLOAD,$(1),$(value $(_V)),$(2),$(if $(wildcard $(value $(_V))),$(value $(_V)),FORCE),$(3)) + $(call TOOL_ADD_IMG_PAYLOAD,$(BL),$(value $(_V)),$(2),$(if $(wildcard $(value $(_V))),$(value $(_V)),FORCE),$(3)) endif .PHONY: check_$(1) @@ -278,8 +272,8 @@ endef # GZIP define GZIP_RULE $(1): $(2) - $(ECHO) " GZIP $$@" - $(Q)gzip -n -f -9 $$< --stdout > $$@ + $(s)echo " GZIP $$@" + $(q)gzip -n -f -9 $$< --stdout > $$@ endef GZIP_SUFFIX := .gz @@ -295,14 +289,15 @@ MAKE_DEP = -Wp,-MD,$(DEP) -MT $$@ -MP # $(1) = output directory # $(2) = source file (%.c) # $(3) = library name +# $(4) = uppercase name of the library define MAKE_C_LIB $(eval OBJ := $(1)/$(patsubst %.c,%.o,$(notdir $(2)))) $(eval DEP := $(patsubst %.o,%.d,$(OBJ))) -$(eval LIB := $(call uppercase, $(notdir $(1)))) +$(eval LIB := $(notdir $(1))) -$(OBJ): $(2) $(filter-out %.d,$(MAKEFILE_LIST)) | lib$(3)_dirs - $$(ECHO) " CC $$<" - $$(Q)$$(CC) $$($(LIB)_CFLAGS) $$(TF_CFLAGS) $$(CFLAGS) $(MAKE_DEP) -c $$< -o $$@ +$(OBJ): $(2) $(filter-out %.d,$(MAKEFILE_LIST)) | $$$$(@D)/ + $$(s)echo " CC $$<" + $$(q)$($(ARCH)-cc) $$($(LIB)_CFLAGS) $$(TF_CFLAGS) $$(CFLAGS) $(MAKE_DEP) -c $$< -o $$@ -include $(DEP) @@ -312,13 +307,14 @@ endef # $(1) = output directory # $(2) = source file (%.S) # $(3) = library name +# $(4) = uppercase name of the library define MAKE_S_LIB $(eval OBJ := $(1)/$(patsubst %.S,%.o,$(notdir $(2)))) $(eval DEP := $(patsubst %.o,%.d,$(OBJ))) -$(OBJ): $(2) $(filter-out %.d,$(MAKEFILE_LIST)) | lib$(3)_dirs - $$(ECHO) " AS $$<" - $$(Q)$$(AS) $$(ASFLAGS) $(MAKE_DEP) -c $$< -o $$@ +$(OBJ): $(2) $(filter-out %.d,$(MAKEFILE_LIST)) | $$$$(@D)/ + $$(s)echo " AS $$<" + $$(q)$($(ARCH)-as) -x assembler-with-cpp $$(TF_CFLAGS_$(ARCH)) $$(ASFLAGS) $(MAKE_DEP) -c $$< -o $$@ -include $(DEP) @@ -329,19 +325,20 @@ endef # $(1) = output directory # $(2) = source file (%.c) # $(3) = BL stage +# $(4) = uppercase BL stage define MAKE_C $(eval OBJ := $(1)/$(patsubst %.c,%.o,$(notdir $(2)))) $(eval DEP := $(patsubst %.o,%.d,$(OBJ))) -$(eval BL_DEFINES := IMAGE_$(call uppercase,$(3)) $($(call uppercase,$(3))_DEFINES) $(PLAT_BL_COMMON_DEFINES)) -$(eval BL_INCLUDE_DIRS := $($(call uppercase,$(3))_INCLUDE_DIRS) $(PLAT_BL_COMMON_INCLUDE_DIRS)) -$(eval BL_CPPFLAGS := $($(call uppercase,$(3))_CPPFLAGS) $(addprefix -D,$(BL_DEFINES)) $(addprefix -I,$(BL_INCLUDE_DIRS)) $(PLAT_BL_COMMON_CPPFLAGS)) -$(eval BL_CFLAGS := $($(call uppercase,$(3))_CFLAGS) $(PLAT_BL_COMMON_CFLAGS)) +$(eval BL_DEFINES := IMAGE_$(4) $($(4)_DEFINES) $(PLAT_BL_COMMON_DEFINES)) +$(eval BL_INCLUDE_DIRS := $($(4)_INCLUDE_DIRS) $(PLAT_BL_COMMON_INCLUDE_DIRS)) +$(eval BL_CPPFLAGS := $($(4)_CPPFLAGS) $(addprefix -D,$(BL_DEFINES)) $(addprefix -I,$(BL_INCLUDE_DIRS)) $(PLAT_BL_COMMON_CPPFLAGS)) +$(eval BL_CFLAGS := $($(4)_CFLAGS) $(PLAT_BL_COMMON_CFLAGS)) -$(OBJ): $(2) $(filter-out %.d,$(MAKEFILE_LIST)) | $(3)_dirs - $$(ECHO) " CC $$<" - $$(Q)$$(CC) $$(LTO_CFLAGS) $$(TF_CFLAGS) $$(CFLAGS) $(BL_CPPFLAGS) $(BL_CFLAGS) $(MAKE_DEP) -c $$< -o $$@ +$(OBJ): $(2) $(filter-out %.d,$(MAKEFILE_LIST)) | $$$$(@D)/ + $$(s)echo " CC $$<" + $$(q)$($(ARCH)-cc) $$(LTO_CFLAGS) $$(TF_CFLAGS) $$(CFLAGS) $(BL_CPPFLAGS) $(BL_CFLAGS) $(MAKE_DEP) -c $$< -o $$@ -include $(DEP) @@ -352,19 +349,20 @@ endef # $(1) = output directory # $(2) = assembly file (%.S) # $(3) = BL stage +# $(4) = uppercase BL stage define MAKE_S $(eval OBJ := $(1)/$(patsubst %.S,%.o,$(notdir $(2)))) $(eval DEP := $(patsubst %.o,%.d,$(OBJ))) -$(eval BL_DEFINES := IMAGE_$(call uppercase,$(3)) $($(call uppercase,$(3))_DEFINES) $(PLAT_BL_COMMON_DEFINES)) -$(eval BL_INCLUDE_DIRS := $($(call uppercase,$(3))_INCLUDE_DIRS) $(PLAT_BL_COMMON_INCLUDE_DIRS)) -$(eval BL_CPPFLAGS := $($(call uppercase,$(3))_CPPFLAGS) $(addprefix -D,$(BL_DEFINES)) $(addprefix -I,$(BL_INCLUDE_DIRS)) $(PLAT_BL_COMMON_CPPFLAGS)) -$(eval BL_ASFLAGS := $($(call uppercase,$(3))_ASFLAGS) $(PLAT_BL_COMMON_ASFLAGS)) +$(eval BL_DEFINES := IMAGE_$(4) $($(4)_DEFINES) $(PLAT_BL_COMMON_DEFINES)) +$(eval BL_INCLUDE_DIRS := $($(4)_INCLUDE_DIRS) $(PLAT_BL_COMMON_INCLUDE_DIRS)) +$(eval BL_CPPFLAGS := $($(4)_CPPFLAGS) $(addprefix -D,$(BL_DEFINES)) $(addprefix -I,$(BL_INCLUDE_DIRS)) $(PLAT_BL_COMMON_CPPFLAGS)) +$(eval BL_ASFLAGS := $($(4)_ASFLAGS) $(PLAT_BL_COMMON_ASFLAGS)) -$(OBJ): $(2) $(filter-out %.d,$(MAKEFILE_LIST)) | $(3)_dirs - $$(ECHO) " AS $$<" - $$(Q)$$(AS) $$(ASFLAGS) $(BL_CPPFLAGS) $(BL_ASFLAGS) $(MAKE_DEP) -c $$< -o $$@ +$(OBJ): $(2) $(filter-out %.d,$(MAKEFILE_LIST)) | $$$$(@D)/ + $$(s)echo " AS $$<" + $$(q)$($(ARCH)-as) -x assembler-with-cpp $$(TF_CFLAGS_$(ARCH)) $$(ASFLAGS) $(BL_CPPFLAGS) $(BL_ASFLAGS) $(MAKE_DEP) -c $$< -o $$@ -include $(DEP) @@ -375,17 +373,18 @@ endef # $(1) = output linker script # $(2) = input template # $(3) = BL stage +# $(4) = uppercase BL stage define MAKE_LD $(eval DEP := $(1).d) -$(eval BL_DEFINES := IMAGE_$(call uppercase,$(3)) $($(call uppercase,$(3))_DEFINES) $(PLAT_BL_COMMON_DEFINES)) -$(eval BL_INCLUDE_DIRS := $($(call uppercase,$(3))_INCLUDE_DIRS) $(PLAT_BL_COMMON_INCLUDE_DIRS)) -$(eval BL_CPPFLAGS := $($(call uppercase,$(3))_CPPFLAGS) $(addprefix -D,$(BL_DEFINES)) $(addprefix -I,$(BL_INCLUDE_DIRS)) $(PLAT_BL_COMMON_CPPFLAGS)) +$(eval BL_DEFINES := IMAGE_$(4) $($(4)_DEFINES) $(PLAT_BL_COMMON_DEFINES)) +$(eval BL_INCLUDE_DIRS := $($(4)_INCLUDE_DIRS) $(PLAT_BL_COMMON_INCLUDE_DIRS)) +$(eval BL_CPPFLAGS := $($(4)_CPPFLAGS) $(addprefix -D,$(BL_DEFINES)) $(addprefix -I,$(BL_INCLUDE_DIRS)) $(PLAT_BL_COMMON_CPPFLAGS)) -$(1): $(2) $(filter-out %.d,$(MAKEFILE_LIST)) | $(3)_dirs - $$(ECHO) " PP $$<" - $$(Q)$$(CPP) $$(CPPFLAGS) $(BL_CPPFLAGS) $(TF_CFLAGS_$(ARCH)) -P -x assembler-with-cpp -D__LINKER__ $(MAKE_DEP) -o $$@ $$< +$(1): $(2) $(filter-out %.d,$(MAKEFILE_LIST)) | $$$$(@D)/ + $$(s)echo " PP $$<" + $$(q)$($(ARCH)-cpp) -E $$(CPPFLAGS) $(BL_CPPFLAGS) $(TF_CFLAGS_$(ARCH)) -P -x assembler-with-cpp -D__LINKER__ $(MAKE_DEP) -o $$@ $$< -include $(DEP) @@ -395,14 +394,15 @@ endef # $(1) = output directory # $(2) = list of source files # $(3) = name of the library +# $(4) = uppercase name of the library define MAKE_LIB_OBJS $(eval C_OBJS := $(filter %.c,$(2))) $(eval REMAIN := $(filter-out %.c,$(2))) - $(eval $(foreach obj,$(C_OBJS),$(call MAKE_C_LIB,$(1),$(obj),$(3)))) + $(eval $(foreach obj,$(C_OBJS),$(call MAKE_C_LIB,$(1),$(obj),$(3),$(4)))) $(eval S_OBJS := $(filter %.S,$(REMAIN))) $(eval REMAIN := $(filter-out %.S,$(REMAIN))) - $(eval $(foreach obj,$(S_OBJS),$(call MAKE_S_LIB,$(1),$(obj),$(3)))) + $(eval $(foreach obj,$(S_OBJS),$(call MAKE_S_LIB,$(1),$(obj),$(3),$(4)))) $(and $(REMAIN),$(error Unexpected source files present: $(REMAIN))) endef @@ -412,14 +412,15 @@ endef # $(1) = output directory # $(2) = list of source files (both C and assembly) # $(3) = BL stage +# $(4) = uppercase BL stage define MAKE_OBJS $(eval C_OBJS := $(filter %.c,$(2))) $(eval REMAIN := $(filter-out %.c,$(2))) - $(eval $(foreach obj,$(C_OBJS),$(call MAKE_C,$(1),$(obj),$(3)))) + $(eval $(foreach obj,$(C_OBJS),$(call MAKE_C,$(1),$(obj),$(3),$(4)))) $(eval S_OBJS := $(filter %.S,$(REMAIN))) $(eval REMAIN := $(filter-out %.S,$(REMAIN))) - $(eval $(foreach obj,$(S_OBJS),$(call MAKE_S,$(1),$(obj),$(3)))) + $(eval $(foreach obj,$(S_OBJS),$(call MAKE_S,$(1),$(obj),$(3),$(4)))) $(and $(REMAIN),$(error Unexpected source files present: $(REMAIN))) endef @@ -433,41 +434,23 @@ define SOURCES_TO_OBJS $(notdir $(patsubst %.S,%.o,$(filter %.S,$(1)))) endef -# Allow overriding the timestamp, for example for reproducible builds, or to -# synchronize timestamps across multiple projects. -# This must be set to a C string (including quotes where applicable). -BUILD_MESSAGE_TIMESTAMP ?= __TIME__", "__DATE__ - .PHONY: libraries -# MAKE_LIB_DIRS macro defines the target for the directory where -# libraries are created -define MAKE_LIB_DIRS - $(eval LIB_DIR := ${BUILD_PLAT}/lib) - $(eval ROMLIB_DIR := ${BUILD_PLAT}/romlib) - $(eval LIBWRAPPER_DIR := ${BUILD_PLAT}/libwrapper) - $(eval $(call MAKE_PREREQ_DIR,${LIB_DIR},${BUILD_PLAT})) - $(eval $(call MAKE_PREREQ_DIR,${ROMLIB_DIR},${BUILD_PLAT})) - $(eval $(call MAKE_PREREQ_DIR,${LIBWRAPPER_DIR},${BUILD_PLAT})) -endef - # MAKE_LIB macro defines the targets and options to build each BL image. # Arguments: # $(1) = Library name define MAKE_LIB + $(eval BL := $(call uppercase,$(1))) $(eval BUILD_DIR := ${BUILD_PLAT}/lib$(1)) $(eval LIB_DIR := ${BUILD_PLAT}/lib) $(eval ROMLIB_DIR := ${BUILD_PLAT}/romlib) - $(eval SOURCES := $(LIB$(call uppercase,$(1))_SRCS)) + $(eval SOURCES := $(LIB$(BL)_SRCS)) $(eval OBJS := $(addprefix $(BUILD_DIR)/,$(call SOURCES_TO_OBJS,$(SOURCES)))) -$(eval $(call MAKE_PREREQ_DIR,${BUILD_DIR},${BUILD_PLAT})) -$(eval $(call MAKE_LIB_OBJS,$(BUILD_DIR),$(SOURCES),$(1))) +$(eval $(call MAKE_LIB_OBJS,$(BUILD_DIR),$(SOURCES),$(1),$(BL))) -.PHONY : lib${1}_dirs -lib${1}_dirs: | ${BUILD_DIR} ${LIB_DIR} ${ROMLIB_DIR} ${LIBWRAPPER_DIR} libraries: ${LIB_DIR}/lib$(1).a -ifneq ($(findstring armlink,$(notdir $(LD))),) +ifeq ($($(ARCH)-ld-id),arm-link) LDPATHS = --userlibpath=${LIB_DIR} LDLIBS += --library=$(1) else @@ -481,9 +464,9 @@ endif all: ${LIB_DIR}/lib$(1).a -${LIB_DIR}/lib$(1).a: $(OBJS) - $$(ECHO) " AR $$@" - $$(Q)$$(AR) cr $$@ $$? +${LIB_DIR}/lib$(1).a: $(OBJS) | $$$$(@D)/ + $$(s)echo " AR $$@" + $$(q)$($(ARCH)-ar) cr $$@ $$? endef # Generate the path to one or more preprocessed linker scripts given the paths @@ -495,6 +478,10 @@ define linker_script_path $(patsubst %.S,$(BUILD_DIR)/%,$(1)) endef +ifeq ($(USE_ROMLIB),1) +WRAPPER_FLAGS := @${BUILD_PLAT}/romlib/romlib.ldflags +endif + # MAKE_BL macro defines the targets and options to build each BL image. # Arguments: # $(1) = BL stage @@ -502,8 +489,9 @@ endef # $(3) = FIP prefix (optional) (if FWU_, target is fwu_fip instead of fip) # $(4) = BL encryption flag (optional) (0, 1) define MAKE_BL + $(eval BL := $(call uppercase,$(1))) $(eval BUILD_DIR := ${BUILD_PLAT}/$(1)) - $(eval BL_SOURCES := $($(call uppercase,$(1))_SOURCES)) + $(eval BL_SOURCES := $($(BL)_SOURCES)) $(eval SOURCES := $(sort $(BL_SOURCES) $(BL_COMMON_SOURCES) $(PLAT_BL_COMMON_SOURCES))) $(eval OBJS := $(addprefix $(BUILD_DIR)/,$(call SOURCES_TO_OBJS,$(SOURCES)))) $(eval MAPFILE := $(call IMG_MAPFILE,$(1))) @@ -511,94 +499,63 @@ define MAKE_BL $(eval DUMP := $(call IMG_DUMP,$(1))) $(eval BIN := $(call IMG_BIN,$(1))) $(eval ENC_BIN := $(call IMG_ENC_BIN,$(1))) - $(eval BL_LIBS := $($(call uppercase,$(1))_LIBS)) + $(eval BL_LIBS := $($(BL)_LIBS)) - $(eval DEFAULT_LINKER_SCRIPT_SOURCE := $($(call uppercase,$(1))_DEFAULT_LINKER_SCRIPT_SOURCE)) + $(eval DEFAULT_LINKER_SCRIPT_SOURCE := $($(BL)_DEFAULT_LINKER_SCRIPT_SOURCE)) $(eval DEFAULT_LINKER_SCRIPT := $(call linker_script_path,$(DEFAULT_LINKER_SCRIPT_SOURCE))) - $(eval LINKER_SCRIPT_SOURCES := $($(call uppercase,$(1))_LINKER_SCRIPT_SOURCES)) + $(eval LINKER_SCRIPT_SOURCES := $($(BL)_LINKER_SCRIPT_SOURCES)) $(eval LINKER_SCRIPTS := $(call linker_script_path,$(LINKER_SCRIPT_SOURCES))) - # We use sort only to get a list of unique object directory names. - # ordering is not relevant but sort removes duplicates. - $(eval TEMP_OBJ_DIRS := $(sort $(dir ${OBJS} ${DEFAULT_LINKER_SCRIPT} ${LINKER_SCRIPTS}))) - # The $(dir ) function leaves a trailing / on the directory names - # Rip off the / to match directory names with make rule targets. - $(eval OBJ_DIRS := $(patsubst %/,%,$(TEMP_OBJ_DIRS))) - -# Create generators for object directory structure - -$(eval $(call MAKE_PREREQ_DIR,${BUILD_DIR},${BUILD_PLAT})) - -$(eval $(foreach objd,${OBJ_DIRS}, - $(call MAKE_PREREQ_DIR,${objd},${BUILD_DIR}))) - -.PHONY : ${1}_dirs - -# We use order-only prerequisites to ensure that directories are created, -# but do not cause re-builds every time a file is written. -${1}_dirs: | ${OBJ_DIRS} - -$(eval $(call MAKE_OBJS,$(BUILD_DIR),$(SOURCES),$(1))) +$(eval $(call MAKE_OBJS,$(BUILD_DIR),$(SOURCES),$(1),$(BL))) # Generate targets to preprocess each required linker script $(eval $(foreach source,$(DEFAULT_LINKER_SCRIPT_SOURCE) $(LINKER_SCRIPT_SOURCES), \ - $(call MAKE_LD,$(call linker_script_path,$(source)),$(source),$(1)))) + $(call MAKE_LD,$(call linker_script_path,$(source)),$(source),$(1),$(BL)))) -$(eval BL_LDFLAGS := $($(call uppercase,$(1))_LDFLAGS)) +$(eval BL_LDFLAGS := $($(BL)_LDFLAGS)) ifeq ($(USE_ROMLIB),1) -$(ELF): romlib.bin +$(ELF): romlib.bin | $$$$(@D)/ endif # MODULE_OBJS can be assigned by vendors with different compiled # object file path, and prebuilt object file path. $(eval OBJS += $(MODULE_OBJS)) -$(ELF): $(OBJS) $(DEFAULT_LINKER_SCRIPT) $(LINKER_SCRIPTS) | $(1)_dirs libraries $(BL_LIBS) - $$(ECHO) " LD $$@" -ifdef MAKE_BUILD_STRINGS - $(call MAKE_BUILD_STRINGS,$(BUILD_DIR)/build_message.o) -else - @echo 'const char build_message[] = "Built : "$(BUILD_MESSAGE_TIMESTAMP); \ - const char version_string[] = "${VERSION_STRING}"; \ - const char version[] = "${VERSION}";' | \ - $$(CC) $$(TF_CFLAGS) $$(CFLAGS) -xc -c - -o $(BUILD_DIR)/build_message.o -endif -ifneq ($(findstring armlink,$(notdir $(LD))),) - $$(Q)$$(LD) -o $$@ $$(TF_LDFLAGS) $$(LDFLAGS) $(BL_LDFLAGS) --entry=${1}_entrypoint \ - --predefine="-D__LINKER__=$(__LINKER__)" \ - --predefine="-DTF_CFLAGS=$(TF_CFLAGS)" \ +$(ELF): $(OBJS) $(DEFAULT_LINKER_SCRIPT) $(LINKER_SCRIPTS) | $$$$(@D)/ libraries $(BL_LIBS) + $$(s)echo " LD $$@" +ifeq ($($(ARCH)-ld-id),arm-link) + $$(q)$($(ARCH)-ld) -o $$@ $$(TF_LDFLAGS) $$(LDFLAGS) $(BL_LDFLAGS) --entry=${1}_entrypoint \ + --predefine=$(call escape-shell,-D__LINKER__=$(__LINKER__)) \ + --predefine=$(call escape-shell,-DTF_CFLAGS=$(TF_CFLAGS)) \ --map --list="$(MAPFILE)" --scatter=${PLAT_DIR}/scat/${1}.scat \ - $(LDPATHS) $(LIBWRAPPER) $(LDLIBS) $(BL_LIBS) \ - $(BUILD_DIR)/build_message.o $(OBJS) -else ifneq ($(findstring gcc,$(notdir $(LD))),) - $$(Q)$$(LD) -o $$@ $$(TF_LDFLAGS) $$(LDFLAGS) -Wl,-Map=$(MAPFILE) \ + $(LDPATHS) $(LIBWRAPPER) $(LDLIBS) $(BL_LIBS) $(OBJS) +else ifeq ($($(ARCH)-ld-id),gnu-gcc) + $$(q)$($(ARCH)-ld) -o $$@ $$(TF_LDFLAGS) $$(LDFLAGS) $$(WRAPPER_FLAGS) $(BL_LDFLAGS) -Wl,-Map=$(MAPFILE) \ $(addprefix -Wl$(comma)--script$(comma),$(LINKER_SCRIPTS)) -Wl,--script,$(DEFAULT_LINKER_SCRIPT) \ - $(BUILD_DIR)/build_message.o \ $(OBJS) $(LDPATHS) $(LIBWRAPPER) $(LDLIBS) $(BL_LIBS) else - $$(Q)$$(LD) -o $$@ $$(TF_LDFLAGS) $$(LDFLAGS) $(BL_LDFLAGS) -Map=$(MAPFILE) \ + $$(q)$($(ARCH)-ld) -o $$@ $$(TF_LDFLAGS) $$(LDFLAGS) $$(WRAPPER_FLAGS) $(BL_LDFLAGS) -Map=$(MAPFILE) \ $(addprefix -T ,$(LINKER_SCRIPTS)) --script $(DEFAULT_LINKER_SCRIPT) \ - $(BUILD_DIR)/build_message.o \ $(OBJS) $(LDPATHS) $(LIBWRAPPER) $(LDLIBS) $(BL_LIBS) endif ifeq ($(DISABLE_BIN_GENERATION),1) - @${ECHO_BLANK_LINE} - @echo "Built $$@ successfully" - @${ECHO_BLANK_LINE} + $(s)echo + $(s)echo "Built $$@ successfully" + $(s)echo endif -$(DUMP): $(ELF) - $${ECHO} " OD $$@" - $${Q}$${OD} -dx $$< > $$@ +$(DUMP): $(ELF) | $$$$(@D)/ + $$(s)echo " OD $$@" + $$(q)$($(ARCH)-od) -dx $$< > $$@ -$(BIN): $(ELF) - $${ECHO} " BIN $$@" - $$(Q)$$(OC) -O binary $$< $$@ - @${ECHO_BLANK_LINE} - @echo "Built $$@ successfully" - @${ECHO_BLANK_LINE} +$(BIN): $(ELF) | $$$$(@D)/ + $$(s)echo " BIN $$@" + $$(q)$($(ARCH)-oc) -O binary $$< $$@ + $(s)echo + $(s)echo "Built $$@ successfully" + $(s)echo .PHONY: $(1) ifeq ($(DISABLE_BIN_GENERATION),1) @@ -611,10 +568,10 @@ all: $(1) ifeq ($(4),1) $(call ENCRYPT_FW,$(BIN),$(ENC_BIN)) -$(if $(2),$(call TOOL_ADD_IMG_PAYLOAD,$(1),$(BIN),--$(2),$(ENC_BIN),$(3), \ +$(if $(2),$(call TOOL_ADD_IMG_PAYLOAD,$(BL),$(BIN),--$(2),$(ENC_BIN),$(3), \ $(ENC_BIN))) else -$(if $(2),$(call TOOL_ADD_IMG_PAYLOAD,$(1),$(BIN),--$(2),$(BIN),$(3))) +$(if $(2),$(call TOOL_ADD_IMG_PAYLOAD,$(BL),$(BIN),--$(2),$(BIN),$(3))) endif endef @@ -625,22 +582,6 @@ define SOURCES_TO_DTBS $(notdir $(patsubst %.dts,%.dtb,$(filter %.dts,$(1)))) endef -# MAKE_FDT_DIRS macro creates the prerequisite directories that host the -# FDT binaries -# $(1) = output directory -# $(2) = input dts -define MAKE_FDT_DIRS - $(eval DTBS := $(addprefix $(1)/,$(call SOURCES_TO_DTBS,$(2)))) - $(eval TEMP_DTB_DIRS := $(sort $(dir ${DTBS}))) - # The $(dir ) function leaves a trailing / on the directory names - # Rip off the / to match directory names with make rule targets. - $(eval DTB_DIRS := $(patsubst %/,%,$(TEMP_DTB_DIRS))) - -$(eval $(foreach objd,${DTB_DIRS},$(call MAKE_PREREQ_DIR,${objd},${BUILD_DIR}))) - -fdt_dirs: ${DTB_DIRS} -endef - # MAKE_DTB generate the Flattened device tree binary # $(1) = output directory # $(2) = input dts @@ -655,12 +596,14 @@ $(eval DTSDEP := $(patsubst %.dtb,%.o.d,$(DOBJ))) # Dependencies of the DT compilation on its pre-compiled DTS $(eval DTBDEP := $(patsubst %.dtb,%.d,$(DOBJ))) -$(DOBJ): $(2) $(filter-out %.d,$(MAKEFILE_LIST)) | fdt_dirs - $${ECHO} " CPP $$<" +$(DPRE): $(2) | $$$$(@D)/ + $$(s)echo " CPP $$<" $(eval DTBS := $(addprefix $(1)/,$(call SOURCES_TO_DTBS,$(2)))) - $$(Q)$$(PP) $$(DTC_CPPFLAGS) -MT $(DTBS) -MMD -MF $(DTSDEP) -o $(DPRE) $$< - $${ECHO} " DTC $$<" - $$(Q)$$(DTC) $$(DTC_FLAGS) -d $(DTBDEP) -o $$@ $(DPRE) + $$(q)$($(ARCH)-cpp) -E $$(TF_CFLAGS_$(ARCH)) $$(DTC_CPPFLAGS) -MT $(DTBS) -MMD -MF $(DTSDEP) -o $(DPRE) $$< + +$(DOBJ): $(DPRE) $(filter-out %.d,$(MAKEFILE_LIST)) | $$$$(@D)/ + $$(s)echo " DTC $$<" + $$(q)$($(ARCH)-dtc) $$(DTC_FLAGS) -d $(DTBDEP) -o $$@ $$< -include $(DTBDEP) -include $(DTSDEP) @@ -676,8 +619,6 @@ define MAKE_DTBS $(and $(REMAIN),$(error FDT_SOURCES contain non-DTS files: $(REMAIN))) $(eval $(foreach obj,$(DOBJS),$(call MAKE_DTB,$(1),$(obj)))) - $(eval $(call MAKE_FDT_DIRS,$(1),$(2))) - -dtbs: $(DTBS) +dtbs: $(addprefix $(1)/,$(call SOURCES_TO_DTBS,$(2))) all: dtbs endef diff --git a/make_helpers/common.mk b/make_helpers/common.mk new file mode 100644 index 00000000..848e4e94 --- /dev/null +++ b/make_helpers/common.mk @@ -0,0 +1,17 @@ +# +# Copyright (c) 2024, Arm Limited and Contributors. All rights reserved. +# +# SPDX-License-Identifier: BSD-3-Clause +# + +ifndef common-mk + common-mk := $(lastword $(MAKEFILE_LIST)) + + include $(dir $(common-mk))utilities.mk + + silent := $(call bool,$(findstring s,$(firstword ~$(MAKEFLAGS)))) + verbose := $(if $(silent),,$(call bool,$(V))) + + s := @$(if $(or $(verbose),$(silent)),: ) + q := $(if $(verbose),,@) +endif diff --git a/make_helpers/defaults.mk b/make_helpers/defaults.mk index f0f157c1..d6c09de6 100644 --- a/make_helpers/defaults.mk +++ b/make_helpers/defaults.mk @@ -1,5 +1,5 @@ # -# Copyright (c) 2016-2023, Arm Limited. All rights reserved. +# Copyright (c) 2016-2024, Arm Limited. All rights reserved. # # SPDX-License-Identifier: BSD-3-Clause # @@ -63,6 +63,9 @@ CTX_INCLUDE_AARCH32_REGS := 1 # Include FP registers in cpu context CTX_INCLUDE_FPREGS := 0 +# Include SVE registers in cpu context +CTX_INCLUDE_SVE_REGS := 0 + # Debug build DEBUG := 0 @@ -139,6 +142,12 @@ FW_ENC_STATUS := 0 # For Chain of Trust GENERATE_COT := 0 +# Default number of 512 blocks per bitlock +RME_GPT_BITLOCK_BLOCK := 1 + +# Default maximum size of GPT contiguous block +RME_GPT_MAX_BLOCK := 512 + # Hint platform interrupt control layer that Group 0 interrupts are for EL3. By # default, they are for Secure EL1. GICV2_G0_FOR_EL3 := 0 @@ -150,6 +159,10 @@ HANDLE_EA_EL3_FIRST_NS := 0 # Enable Handoff protocol using transfer lists TRANSFER_LIST := 0 +# Enables support for the gcc compiler option "-mharden-sls=all". +# By default, disables all SLS hardening. +HARDEN_SLS := 0 + # Secure hash algorithm flag, accepts 3 values: sha256, sha384 and sha512. # The default value is sha256. HASH_ALG := sha256 @@ -172,6 +185,9 @@ endif # Option to build TF with Measured Boot support MEASURED_BOOT := 0 +# Option to enable the DICE Protection Environmnet as a Measured Boot backend +DICE_PROTECTION_ENVIRONMENT :=0 + # NS timer register save and restore NS_TIMER_SWITCH := 0 @@ -224,6 +240,14 @@ SEPARATE_NOBITS_REGION := 0 # region, platform Makefile is free to override this value. SEPARATE_BL2_NOLOAD_REGION := 0 +# Put RW DATA sections (.rwdata) in a separate memory region, which may be +# discontiguous from the rest of BL31. +SEPARATE_RWDATA_REGION := 0 + +# Put SIMD context data structures in a separate memory region. Platforms +# have the choice to put it outside of default BSS region of EL3 firmware. +SEPARATE_SIMD_SECTION := 0 + # If the BL31 image initialisation code is recalimed after use for the secondary # cores stack RECLAIM_INIT_CODE := 0 @@ -281,9 +305,6 @@ COT := tbbr # Use tbbr_oid.h instead of platform_oid.h USE_TBBR_DEFS := 1 -# Build verbosity -V := 0 - # Whether to enable D-Cache early during warm boot. This is usually # applicable for platforms wherein interconnect programming is not # required to enable cache coherency after warm reset (eg: single cluster @@ -347,8 +368,13 @@ NR_OF_IMAGES_IN_FW_BANK := 1 # Disable Firmware update support by default PSA_FWU_SUPPORT := 0 -# By default, disable the mocking of RSS provided services -PLAT_RSS_NOT_SUPPORTED := 0 +# Enable image description in FWU metadata by default when PSA_FWU_SUPPORT +# is enabled. +ifeq ($(PSA_FWU_SUPPORT),1) +PSA_FWU_METADATA_FW_STORE_DESC := 1 +else +PSA_FWU_METADATA_FW_STORE_DESC := 0 +endif # Dynamic Root of Trust for Measurement support DRTM_SUPPORT := 0 @@ -373,3 +399,20 @@ ENABLE_CONSOLE_GETC := 0 # functions must be enabled by platforms if they require it. # Disabled by default. INIT_UNUSED_NS_EL2 := 0 + +# Disable including MPAM EL2 registers in context by default since currently +# it's only enabled for NS world +CTX_INCLUDE_MPAM_REGS := 0 + +# Enable context memory usage reporting during BL31 setup. +PLATFORM_REPORT_CTX_MEM_USE := 0 + +# Enable early console +EARLY_CONSOLE := 0 + +# Allow platforms to save/restore DSU PMU registers over a power cycle. +# Disabled by default and must be enabled by individual platforms. +PRESERVE_DSU_PMU_REGS := 0 + +# Enable RMMD to forward attestation requests from RMM to EL3. +RMMD_ENABLE_EL3_TOKEN_SIGN := 0 diff --git a/make_helpers/march.mk b/make_helpers/march.mk index 2417709c..8e73116a 100644 --- a/make_helpers/march.mk +++ b/make_helpers/march.mk @@ -1,5 +1,5 @@ # -# Copyright (c) 2023, Arm Limited. All rights reserved. +# Copyright (c) 2023-2024, Arm Limited. All rights reserved. # # SPDX-License-Identifier: BSD-3-Clause # @@ -21,7 +21,7 @@ # armv8.6-a armv8.7-a armv8.8-a armv8-r armv9-a # [...] # -GCC_MARCH_OUTPUT := $(shell $(CC) -march=foo -Q --help=target -v 2>&1) +GCC_MARCH_OUTPUT := $(if $($(ARCH)-cc),$(shell $($(ARCH)-cc) -march=foo -Q --help=target -v 2>&1)) # This function is used to find the best march value supported by the given compiler. # We try to use `GCC_MARCH_OUTPUT` which has verbose message with supported march values we filter that @@ -54,7 +54,7 @@ else provided-march = armv${ARM_ARCH_MAJOR}.${ARM_ARCH_MINOR}-a endif -ifeq ($(findstring clang,$(notdir $(CC))),) +ifeq ($(filter %-clang,$($(ARCH)-cc-id)),) # We expect from Platform to provide a correct Major/Minor value but expecting something # from compiler with unsupported march means we shouldn't fail without trying anything, @@ -82,4 +82,13 @@ endif # not clang march-directive := -march=${provided-march} +# Set the compiler's architecture feature modifiers +ifneq ($(arch-features), none) + # Strip "none+" from arch-features + arch-features := $(subst none+,,$(arch-features)) + march-directive := $(march-directive)+$(arch-features) +# Print features + $(info Arm Architecture Features specified: $(subst +, ,$(arch-features))) +endif #(arch-features) + endif # MARCH_DIRECTIVE diff --git a/make_helpers/plat_helpers.mk b/make_helpers/plat_helpers.mk index a7ae9a27..bc02a207 100644 --- a/make_helpers/plat_helpers.mk +++ b/make_helpers/plat_helpers.mk @@ -11,6 +11,7 @@ ifndef PLAT_HELPERS_MK PLAT_HELPERS_MK := $(lastword $(MAKEFILE_LIST)) + PLAT:= ${DEFAULT_PLAT} ifeq (${PLAT},) $(error "Error: Unknown platform. Please use PLAT=<platform name> to specify the platform") endif @@ -18,15 +19,18 @@ ifndef PLAT_HELPERS_MK # TF_PLATFORM_ROOT can be overridden for when building tools directly TF_PLATFORM_ROOT ?= plat/ PLAT_MAKEFILE := platform.mk + PLAT_DEFAULTS_MAKEFILE := platform_defaults.mk # Generate the platforms list by recursively searching for all directories # under /plat containing a PLAT_MAKEFILE. Append each platform with a `|` # char and strip out the final '|'. ALL_PLATFORM_MK_FILES := $(call rwildcard,${TF_PLATFORM_ROOT},${PLAT_MAKEFILE}) + ALL_PLATFORM_MK_DEF_FILES := $(call rwildcard,${TF_PLATFORM_ROOT},${PLAT_DEFAULTS_MAKEFILE}) ALL_PLATFORM_DIRS := $(patsubst %/,%,$(dir ${ALL_PLATFORM_MK_FILES})) ALL_PLATFORMS := $(sort $(notdir ${ALL_PLATFORM_DIRS})) PLAT_MAKEFILE_FULL := $(filter %/${PLAT}/${PLAT_MAKEFILE},${ALL_PLATFORM_MK_FILES}) + PLAT_DEFAULTS_MAKEFILE_FULL := $(filter %/${PLAT}/${PLAT_DEFAULTS_MAKEFILE},${ALL_PLATFORM_MK_DEF_FILES}) PLATFORM_LIST := $(subst ${space},|,${ALL_PLATFORMS}) ifeq ($(PLAT_MAKEFILE_FULL),) $(error "Error: Invalid platform. The following platforms are available: ${PLATFORM_LIST}") diff --git a/make_helpers/toolchain.mk b/make_helpers/toolchain.mk new file mode 100644 index 00000000..2ab577cd --- /dev/null +++ b/make_helpers/toolchain.mk @@ -0,0 +1,436 @@ +# +# Copyright (c) 2023-2024, Arm Limited and Contributors. All rights reserved. +# +# SPDX-License-Identifier: BSD-3-Clause +# + +# +# TF-A uses three toolchains: +# +# - The host toolchain (`host`) for building native tools +# - The AArch32 toolchain (`aarch32`) for building Arm AArch32 images +# - The AArch64 toolchain (`aarch64`) for building Arm AArch64 images +# +# In the main Makefile only one of the two Arm toolchains is enabled in any +# given build, but individual tools and libraries may need access to both. +# + +ifndef toolchain-mk + toolchain-mk := $(lastword $(MAKEFILE_LIST)) + + include $(dir $(toolchain-mk))build_env.mk + include $(dir $(toolchain-mk))utilities.mk + + # + # Make assigns generic default values to `CC`, `CPP`, `AS`, etc. if they + # are not explicitly assigned values by the user. These are usually okay + # for very simple programs when building for the host system, but we + # need greater control over the toolchain flow. + # + # Therefore, we undefine these built-in variables if they have default + # values, so that we can provide our own default values later instead. + # + + ifeq ($(origin CC),default) + undefine CC + endif + + ifeq ($(origin CPP),default) + undefine CPP + endif + + ifeq ($(origin AS),default) + undefine AS + endif + + ifeq ($(origin AR),default) + undefine AR + endif + + ifeq ($(origin LD),default) + undefine LD + endif + + # + # The full list of toolchains supported by TF-A. + # + # Each of these toolchains defines a file of the same name in the + # `toolchains` directory, which must configure the following variables: + # + # - <toolchain>-name + # + # A human-readable name for the toolchain, + # + # Additionally, for every tool class, it must also define: + # + # - <toolchain>-<tool-class>-parameter + # + # The command line or environment variable used to set the tool for + # for the given tool class. + # + # - <toolchain>-<tool-class>-default-id + # + # The default tool identifier used if the tool for the given tool + # class cannot be identified. + # + # - <toolchain>-<tool-class>-default + # + # The default commands to try, in the order defined, for the given + # tool class if the user does not explicitly provide one, and if the + # command could not be derived from the C compiler. + # + + toolchains := host # Used for host targets + toolchains += aarch32 # Used for AArch32 targets + toolchains += aarch64 # Used for AArch64 targets + toolchains += rk3399-m0 # Used for RK3399 Cortex-M0 targets + + include $(toolchains:%=$(dir $(toolchain-mk))toolchains/%.mk) + + # + # Configure tool classes that we recognize. + # + # In the context of this build system, a tool class identifies a + # specific role or type of tool in the toolchain. + # + + toolchain-tool-classes := cc + toolchain-tool-class-name-cc := C compiler + + toolchain-tool-classes += cpp + toolchain-tool-class-name-cpp := C preprocessor + + toolchain-tool-classes += as + toolchain-tool-class-name-as := assembler + + toolchain-tool-classes += ld + toolchain-tool-class-name-ld := linker + + toolchain-tool-classes += oc + toolchain-tool-class-name-oc := object copier + + toolchain-tool-classes += od + toolchain-tool-class-name-od := object dumper + + toolchain-tool-classes += ar + toolchain-tool-class-name-ar := archiver + + toolchain-tool-classes += dtc + toolchain-tool-class-name-dtc := device tree compiler + + toolchain-tool-classes += poetry + toolchain-tool-class-name-poetry := Python Poetry package manager + + # + # Configure tools that we recognize. + # + # Here we declare the list of specific toolchain tools that we know how + # to interact with. We don't organize these into tool classes yet - that + # happens further down. + # + + # Arm® Compiler for Embedded + toolchain-tools := arm-clang + toolchain-tool-name-arm-clang := Arm® Compiler for Embedded `armclang` + + toolchain-tools += arm-link + toolchain-tool-name-arm-link := Arm® Compiler for Embedded `armlink` + + toolchain-tools += arm-ar + toolchain-tool-name-arm-ar := Arm® Compiler for Embedded `armar` + + toolchain-tools += arm-fromelf + toolchain-tool-name-arm-fromelf := Arm® Compiler for Embedded `fromelf` + + # LLVM Project + toolchain-tools += llvm-clang + toolchain-tool-name-llvm-clang := LLVM Clang (`clang`) + + toolchain-tools += llvm-lld + toolchain-tool-name-llvm-lld := LLVM LLD (`lld`) + + toolchain-tools += llvm-objcopy + toolchain-tool-name-llvm-objcopy := LLVM `llvm-objcopy` + + toolchain-tools += llvm-objdump + toolchain-tool-name-llvm-objdump := LLVM `llvm-objdump` + + toolchain-tools += llvm-ar + toolchain-tool-name-llvm-ar := LLVM `llvm-ar` + + # GNU Compiler Collection & GNU Binary Utilities + toolchain-tools += gnu-gcc + toolchain-tool-name-gnu-gcc := GNU GCC (`gcc`) + + toolchain-tools += gnu-ld + toolchain-tool-name-gnu-ld := GNU LD (`ld.bfd`) + + toolchain-tools += gnu-objcopy + toolchain-tool-name-gnu-objcopy := GNU `objcopy` + + toolchain-tools += gnu-objdump + toolchain-tool-name-gnu-objdump := GNU `objdump` + + toolchain-tools += gnu-ar + toolchain-tool-name-gnu-ar := GNU `ar` + + # Other tools + toolchain-tools += generic-dtc + toolchain-tool-name-generic-dtc := Device Tree Compiler (`dtc`) + + toolchain-tools += generic-poetry + toolchain-tool-name-generic-poetry := Poetry (`poetry`) + + # + # Assign tools to tool classes. + # + # Multifunctional tools, i.e. tools which can perform multiple roles in + # a toolchain, may be specified in multiple tool class lists. For + # example, a C compiler which can also perform the role of a linker may + # be placed in both `toolchain-tools-cc` and `toolchain-tools-ld`. + # + + # C-related tools + toolchain-tools-cc := arm-clang llvm-clang gnu-gcc # C compilers + toolchain-tools-cpp := arm-clang llvm-clang gnu-gcc # C preprocessors + + # Assembly-related tools + toolchain-tools-as := arm-clang llvm-clang gnu-gcc # Assemblers + + # Linking and object-handling tools + toolchain-tools-ld := arm-clang arm-link llvm-clang llvm-lld gnu-gcc gnu-ld # Linkers + toolchain-tools-oc := arm-fromelf llvm-objcopy gnu-objcopy # Object copiers + toolchain-tools-od := arm-fromelf llvm-objdump gnu-objdump # Object dumpers + toolchain-tools-ar := arm-ar llvm-ar gnu-ar # Archivers + + # Other tools + toolchain-tools-dtc := generic-dtc # Device tree compilers + toolchain-tools-poetry := generic-poetry # Python Poetry package manager + + # + # Helper functions to identify toolchain tools. + # + # The functions defined in this section return a tool identifier when + # given a path to a binary. We generally check a help or version string + # to more reliably identify tools than by looking at the path alone + # (e.g. `gcc` on macOS is actually Apple Clang). + # + # Each tool-guessing function (`toolchain-guess-tool-$(tool)`) takes a + # single argument giving the path to the tool to guess, and returns a + # non-empty value if the tool corresponds to the tool identifier + # `$(tool)`: + # + # $(call toolchain-guess-tool-llvm-clang,aarch64-none-elf-gcc) # <empty> + # $(call toolchain-guess-tool-gnu-gcc,aarch64-none-elf-gcc) # <non-empty> + # + # The `toolchain-guess-tool` function tries to find the corresponding tool + # identifier for a tool given its path. It takes two arguments: + # + # - $(1): a list of candidate tool identifiers to check + # - $(2): the path to the tool to identify + # + # If any of the guess functions corresponding to candidate tool + # identifiers return a non-empty value then the tool identifier of the + # first function to do so is returned: + # + # $(call toolchain-guess-tool,gnu-gcc llvm-clang,armclang) # <empty> + # $(call toolchain-guess-tool,gnu-gcc llvm-clang,clang-14) # llvm-clang + # $(call toolchain-guess-tool,gnu-gcc llvm-clang,aarch64-none-elf-gcc-12) # gnu-gcc + # + # Tools are checked in the order that they are provided, and the first + # match is returned. + # + + # Arm Compiler for Embedded + toolchain-guess-tool-arm-clang = $(shell $(1) --version 2>&1 <$(nul) | grep -o "Tool: armclang") + toolchain-guess-tool-arm-link = $(shell $(1) --help 2>&1 <$(nul) | grep -o "Tool: armlink") + toolchain-guess-tool-arm-fromelf = $(shell $(1) --help 2>&1 <$(nul) | grep -o "Tool: fromelf") + toolchain-guess-tool-arm-ar = $(shell $(1) --version 2>&1 <$(nul) | grep -o "Tool: armar") + + # LLVM Project + toolchain-guess-tool-llvm-clang = $(shell $(1) -v 2>&1 <$(nul) | grep -o "clang version") + toolchain-guess-tool-llvm-lld = $(shell $(1) --help 2>&1 <$(nul) | grep -o "OVERVIEW: lld") + toolchain-guess-tool-llvm-objcopy = $(shell $(1) --help 2>&1 <$(nul) | grep -o "llvm-objcopy tool") + toolchain-guess-tool-llvm-objdump = $(shell $(1) --help 2>&1 <$(nul) | grep -o "llvm object file dumper") + toolchain-guess-tool-llvm-ar = $(shell $(1) --help 2>&1 <$(nul) | grep -o "LLVM Archiver") + + # GNU Compiler Collection & GNU Binary Utilities + toolchain-guess-tool-gnu-gcc = $(shell $(1) -v 2>&1 <$(nul) | grep -o "gcc version") + toolchain-guess-tool-gnu-ld = $(shell $(1) -v 2>&1 <$(nul) | grep -o "GNU ld") + toolchain-guess-tool-gnu-objcopy = $(shell $(1) --version 2>&1 <$(nul) | grep -o "GNU objcopy") + toolchain-guess-tool-gnu-objdump = $(shell $(1) --version 2>&1 <$(nul) | grep -o "GNU objdump") + toolchain-guess-tool-gnu-ar = $(shell $(1) --version 2>&1 <$(nul) | grep -o "GNU ar") + + # Other tools + toolchain-guess-tool-generic-dtc = $(shell $(1) --version 2>&1 <$(nul) | grep -o "Version: DTC") + toolchain-guess-tool-generic-poetry = $(shell $(1) --version 2>&1 <$(nul)) + + toolchain-guess-tool = $(if $(2),$(firstword $(foreach candidate,$(1),$\ + $(if $(call toolchain-guess-tool-$(candidate),$(2)),$(candidate))))) + + # + # Warn the user that a tool could not be identified. + # + # Parameters: + # + # - $1: The toolchain that the tool belongs to. + # - $2: The tool class that the tool belongs to. + # + + define toolchain-warn-unrecognized + $(warning ) + $(warning The configured $($(1)-name) $(toolchain-tool-class-name-$(2)) could not be identified:) + $(warning ) + $(warning $(space) $($(1)-$(2))$(if $($(1)-$(2)-parameter), (via `$($(1)-$(2)-parameter)`))) + $(warning ) + $(warning The following tools were tried, but either did not exist or could not be identified:) + $(warning ) + + $(foreach default,$($(1)-$(2)-default), \ + $(warning $(space) - $(default))) + + $(warning ) + $(warning The following tools are supported:) + $(warning ) + + $(foreach tool,$(toolchain-tools-$(2)), \ + $(warning $(space) - $(toolchain-tool-name-$(tool)))) + + $(warning ) + $(warning The build system will treat this $(toolchain-tool-class-name-$(2)) as $(toolchain-tool-name-$($(1)-$(2)-default-id)).) + $(warning ) + endef + + # + # Locate and identify tools belonging to each toolchain. + # + # Each tool class in each toolchain receives a variable of the form + # `$(toolchain)-$(tool)` giving the associated path to the program. For + # example: + # + # - `aarch64-ld` gives the linker for the AArch64 toolchain, + # - `aarch32-oc` gives the object copier for the AArch32 toolchain, and + # - `host-cc` gives the C compiler for the host toolchain. + # + # For each of these variables, if no program path is explicitly provided + # by the parent Makefile then the C compiler is queried (if supported) + # for its location. + # + # If the C compiler cannot provide the location (or the tool class *is* + # the C compiler), then it is assigned a default value specific for that + # toolchain. + # + + toolchain-derive-arm-clang-cpp = $(1) + toolchain-derive-arm-clang-as = $(1) + toolchain-derive-arm-clang-ld = # Fall back to `$(toolchain)-ld-default` + toolchain-derive-arm-clang-oc = # Fall back to `$(toolchain)-oc-default` + toolchain-derive-arm-clang-od = # Fall back to `$(toolchain)-od-default` + toolchain-derive-arm-clang-ar = # Fall back to `$(toolchain)-ar-default` + + toolchain-derive-llvm-clang-cpp = $(1) + toolchain-derive-llvm-clang-as = $(1) + toolchain-derive-llvm-clang-ld = $(shell $(1) --print-prog-name ld.lld 2>$(nul)) + toolchain-derive-llvm-clang-oc = $(shell $(1) --print-prog-name llvm-objcopy 2>$(nul)) + toolchain-derive-llvm-clang-od = $(shell $(1) --print-prog-name llvm-objdump 2>$(nul)) + toolchain-derive-llvm-clang-ar = $(shell $(1) --print-prog-name llvm-ar 2>$(nul)) + + toolchain-derive-gnu-gcc-cpp = $(1) + toolchain-derive-gnu-gcc-as = $(1) + toolchain-derive-gnu-gcc-ld = $(1) + toolchain-derive-gnu-gcc-oc = $(shell $(1) --print-prog-name objcopy 2>$(nul)) + toolchain-derive-gnu-gcc-od = $(shell $(1) --print-prog-name objdump 2>$(nul)) + toolchain-derive-gnu-gcc-ar = $(shell $(1) --print-prog-name ar 2>$(nul)) + + toolchain-derive = $(if $3,$(call toolchain-derive-$1-$2,$3)) + + # + # Configure a toolchain. + # + # Parameters: + # + # - $1: The toolchain to configure. + # + # This function iterates over all tool classes and configures them for + # the provided toolchain. Toolchain tools are initialized lazily and + # on-demand based on the first read of the tool path or identifier + # variables. + # + + define toolchain-configure + $$(foreach tool-class,$$(toolchain-tool-classes), \ + $$(eval $$(call toolchain-configure-tool,$1,$$(tool-class)))) + endef + + # + # Configure a specific tool within a toolchain. + # + # Parameters: + # + # - $1: The toolchain to configure. + # - $2: The tool class to configure. + # + + define toolchain-configure-tool + $1-$2-configure = $\ + $$(eval $$(call toolchain-determine-tool,$1,$2)) + + # + # When either of the following variables are read for the first + # time, the appropriate tool is determined and *both* variables + # are overwritten with their final values. + # + + $1-$2 = $$($1-$2-configure)$$($1-$2) + $1-$2-id = $$($1-$2-configure)$$($1-$2-id) + endef + + # + # Determines and identifies a tool. + # + # Parameters: + # + # - $1: The toolchain identifier. + # - $2: The tool class. + # + # Tool identification happens by reading the designated tool parameter + # to get the user-specified command for the tool (e.g. `CC` or `LD`). If + # no tool parameter is defined then try to derive the tool from the C + # compiler. + # + # If all else fails, fall back to the default command defined by the + # toolchain makefile. + # + + define toolchain-determine-tool + toolchain-$1-$2-derive-from-cc = $$(if $$(filter-out cc,$2),$\ + $$(call toolchain-derive,$$($1-cc-id),$2,$$($1-cc))) + + toolchain-$1-$2-shell = $\ + $$(if $$(call defined,$$($1-$2-parameter)),$\ + $$($$($1-$2-parameter)),$\ + $$(or $$(toolchain-$1-$2-derive-from-cc),$\ + $$(toolchain-$1-$2-default))) + + toolchain-$1-$2-default = $$(firstword $\ + $$(foreach default,$$($1-$2-default),$\ + $$(if $$(call which,$$(default)),$$(default))) $\ + $$($1-$2-default)) + + $1-$2 := $$(if $$(call which,$$(toolchain-$1-$2-shell)),$\ + $$(call escape-shell,$$(toolchain-$1-$2-shell)),$\ + $$(toolchain-$1-$2-shell)) + + $1-$2-id := $$(if $$($1-$2),$$(or $\ + $$(call toolchain-guess-tool,$$\ + $$(toolchain-tools-$2),$$($1-$2)),$\ + $$($1-$2-default-id))) + + ifeq ($$(or $$($1-$2-id),$$(call bool,$$($1-$2-optional))),) + $$(call toolchain-warn-unrecognized,$1,$2) + endif + endef + + $(foreach toolchain,$(toolchains), \ + $(eval $(call toolchain-configure,$(toolchain)))) +endif diff --git a/make_helpers/toolchains/aarch32.mk b/make_helpers/toolchains/aarch32.mk new file mode 100644 index 00000000..4063ed92 --- /dev/null +++ b/make_helpers/toolchains/aarch32.mk @@ -0,0 +1,39 @@ +# +# Copyright (c) 2023-2024, Arm Limited and Contributors. All rights reserved. +# +# SPDX-License-Identifier: BSD-3-Clause +# + +aarch32-name := AArch32 + +aarch32-cc-parameter := CC +aarch32-cc-default-id := gnu-gcc +aarch32-cc-default := $(or $(CROSS_COMPILE),arm-none-eabi-)gcc + +aarch32-cpp-parameter := CPP +aarch32-cpp-default-id := gnu-gcc +aarch32-cpp-default := $(or $(CROSS_COMPILE),arm-none-eabi-)gcc + +aarch32-as-parameter := AS +aarch32-as-default-id := gnu-gcc +aarch32-as-default := $(or $(CROSS_COMPILE),arm-none-eabi-)gcc + +aarch32-ld-parameter := LD +aarch32-ld-default-id := gnu-gcc +aarch32-ld-default := $(or $(CROSS_COMPILE),arm-none-eabi-)gcc + +aarch32-oc-parameter := OC +aarch32-oc-default-id := gnu-objcopy +aarch32-oc-default := $(or $(CROSS_COMPILE),arm-none-eabi-)objcopy + +aarch32-od-parameter := OD +aarch32-od-default-id := gnu-objdump +aarch32-od-default := $(or $(CROSS_COMPILE),arm-none-eabi-)objdump + +aarch32-ar-parameter := AR +aarch32-ar-default-id := gnu-ar +aarch32-ar-default := $(or $(CROSS_COMPILE),arm-none-eabi-)gcc-ar + +aarch32-dtc-parameter := DTC +aarch32-dtc-default-id := generic-dtc +aarch32-dtc-default := dtc diff --git a/make_helpers/toolchains/aarch64.mk b/make_helpers/toolchains/aarch64.mk new file mode 100644 index 00000000..476fbf3c --- /dev/null +++ b/make_helpers/toolchains/aarch64.mk @@ -0,0 +1,46 @@ +# +# Copyright (c) 2023-2024, Arm Limited and Contributors. All rights reserved. +# +# SPDX-License-Identifier: BSD-3-Clause +# + +aarch64-name := AArch64 + +aarch64-cc-parameter := CC +aarch64-cc-default-id := gnu-gcc +aarch64-cc-default := $(or $(CROSS_COMPILE),aarch64-none-elf-)gcc +aarch64-cc-default += $(if $(CROSS_COMPILE),,aarch64-linux-gnu-gcc) + +aarch64-cpp-parameter := CPP +aarch64-cpp-default-id := gnu-gcc +aarch64-cpp-default := $(or $(CROSS_COMPILE),aarch64-none-elf-)gcc +aarch64-cpp-default += $(if $(CROSS_COMPILE),,aarch64-linux-gnu-gcc) + +aarch64-as-parameter := AS +aarch64-as-default-id := gnu-gcc +aarch64-as-default := $(or $(CROSS_COMPILE),aarch64-none-elf-)gcc +aarch64-as-default += $(if $(CROSS_COMPILE),,aarch64-linux-gnu-gcc) + +aarch64-ld-parameter := LD +aarch64-ld-default-id := gnu-gcc +aarch64-ld-default := $(or $(CROSS_COMPILE),aarch64-none-elf-)gcc +aarch64-ld-default += $(if $(CROSS_COMPILE),,aarch64-linux-gnu-gcc) + +aarch64-oc-parameter := OC +aarch64-oc-default-id := gnu-objcopy +aarch64-oc-default := $(or $(CROSS_COMPILE),aarch64-none-elf-)objcopy +aarch64-oc-default += $(if $(CROSS_COMPILE),,aarch64-linux-gnu-objcopy) + +aarch64-od-parameter := OD +aarch64-od-default-id := gnu-objdump +aarch64-od-default := $(or $(CROSS_COMPILE),aarch64-none-elf-)objdump +aarch64-od-default += $(if $(CROSS_COMPILE),,aarch64-linux-gnu-objdump) + +aarch64-ar-parameter := AR +aarch64-ar-default-id := gnu-ar +aarch64-ar-default := $(or $(CROSS_COMPILE),aarch64-none-elf-)gcc-ar +aarch64-ar-default += $(if $(CROSS_COMPILE),,aarch64-linux-gnu-gcc-ar) + +aarch64-dtc-parameter := DTC +aarch64-dtc-default-id := generic-dtc +aarch64-dtc-default := dtc diff --git a/make_helpers/toolchains/host.mk b/make_helpers/toolchains/host.mk new file mode 100644 index 00000000..dc538c64 --- /dev/null +++ b/make_helpers/toolchains/host.mk @@ -0,0 +1,44 @@ +# +# Copyright (c) 2023-2024, Arm Limited and Contributors. All rights reserved. +# +# SPDX-License-Identifier: BSD-3-Clause +# + +host-name := host + +host-cc-parameter := HOSTCC +host-cc-default-id := gnu-gcc +host-cc-default := gcc + +host-cpp-parameter := HOSTCPP +host-cpp-default-id := gnu-gcc +host-cpp-default := gcc + +host-as-parameter := HOSTAS +host-as-default-id := gnu-gcc +host-as-default := gcc + +host-ld-parameter := HOSTLD +host-ld-default-id := gnu-gcc +host-ld-default := gcc + +host-oc-parameter := HOSTOC +host-oc-default-id := gnu-objcopy +host-oc-default := objcopy + +host-od-parameter := HOSTOD +host-od-default-id := gnu-objdump +host-od-default := objdump + +host-ar-parameter := HOSTAR +host-ar-default-id := gnu-ar +host-ar-default := gcc-ar + +host-dtc-parameter := HOSTDTC +host-dtc-default-id := generic-dtc +host-dtc-default := dtc + +host-poetry-parameter := POETRY +host-poetry-optional := yes +host-poetry-default-id := generic-poetry +host-poetry-default := poetry diff --git a/make_helpers/toolchains/rk3399-m0.mk b/make_helpers/toolchains/rk3399-m0.mk new file mode 100644 index 00000000..3a7f173a --- /dev/null +++ b/make_helpers/toolchains/rk3399-m0.mk @@ -0,0 +1,31 @@ +# +# Copyright (c) 2024, Arm Limited and Contributors. All rights reserved. +# +# SPDX-License-Identifier: BSD-3-Clause +# + +rk3399-m0-name := RK3399 M0 + +rk3399-m0-cc-default-id := gnu-gcc +rk3399-m0-cc-default := $(or $(M0_CROSS_COMPILE),arm-none-eabi-)gcc + +rk3399-m0-cpp-default-id := gnu-gcc +rk3399-m0-cpp-default := $(or $(M0_CROSS_COMPILE),arm-none-eabi-)gcc + +rk3399-m0-as-default-id := gnu-gcc +rk3399-m0-as-default := $(or $(M0_CROSS_COMPILE),arm-none-eabi-)gcc + +rk3399-m0-ld-default-id := gnu-gcc +rk3399-m0-ld-default := $(or $(M0_CROSS_COMPILE),arm-none-eabi-)gcc + +rk3399-m0-oc-default-id := gnu-objcopy +rk3399-m0-oc-default := $(or $(M0_CROSS_COMPILE),arm-none-eabi-)objcopy + +rk3399-m0-od-default-id := gnu-objdump +rk3399-m0-od-default := $(or $(M0_CROSS_COMPILE),arm-none-eabi-)objdump + +rk3399-m0-ar-default-id := gnu-ar +rk3399-m0-ar-default := $(or $(M0_CROSS_COMPILE),arm-none-eabi-)gcc-ar + +rk3399-m0-dtc-default-id := generic-dtc +rk3399-m0-dtc-default := dtc diff --git a/make_helpers/unix.mk b/make_helpers/unix.mk index 545ddfde..fa7722a6 100644 --- a/make_helpers/unix.mk +++ b/make_helpers/unix.mk @@ -1,5 +1,5 @@ # -# Copyright (c) 2016-2018, ARM Limited and Contributors. All rights reserved. +# Copyright (c) 2016-2024, Arm Limited and Contributors. All rights reserved. # # SPDX-License-Identifier: BSD-3-Clause # @@ -9,9 +9,6 @@ ifndef UNIX_MK UNIX_MK := $(lastword $(MAKEFILE_LIST)) - ECHO_BLANK_LINE := echo - ECHO_QUIET := @\# - DIR_DELIM := / PATH_SEP := : @@ -21,40 +18,31 @@ ifndef UNIX_MK # ${1} is the file to be copied. # ${2} is the destination file name. define SHELL_COPY - ${Q}cp -f "${1}" "${2}" + $(q)cp -f "${1}" "${2}" endef # ${1} is the directory to be copied. # ${2} is the destination directory path. define SHELL_COPY_TREE - ${Q}cp -rf "${1}" "${2}" + $(q)cp -rf "${1}" "${2}" endef # ${1} is the file to be deleted. define SHELL_DELETE - -${Q}rm -f "${1}" + -$(q)rm -f "${1}" endef # ${1} is a space delimited list of files to be deleted. # Note that we do not quote ${1}, as multiple parameters may be passed. define SHELL_DELETE_ALL - -${Q}rm -rf ${1} - endef - - # ${1} is the directory to be generated. - # ${2} is optional, and allows a prerequisite to be specified. - # Do nothing if $1 == $2, to ignore self dependencies. - define MAKE_PREREQ_DIR - ifneq (${1},${2}) - -${1} : ${2} - ${Q}mkdir -p "${1}" - - endif + -$(q)rm -rf ${1} endef define SHELL_REMOVE_DIR - -${Q}rm -rf "${1}" + -$(q)rm -rf "${1}" endef + nul := /dev/null + + which = $(shell command -v $(call escape-shell,$(1)) 2>$(nul)) endif diff --git a/make_helpers/utilities.mk b/make_helpers/utilities.mk new file mode 100644 index 00000000..fcccd240 --- /dev/null +++ b/make_helpers/utilities.mk @@ -0,0 +1,129 @@ +# +# Copyright (c) 2024, Arm Limited and Contributors. All rights reserved. +# +# SPDX-License-Identifier: BSD-3-Clause +# + +space := +space := $(space) $(space) +comma := , + +null := � + +compat-path = $(subst $(space),$(null),$(1)) +decompat-path = $(subst $(null), ,$(1)) + +absolute-path = $(call decompat-path,$(abspath $(call compat-path,$(1)))) +real-path = $(call decompat-path,$(realpath $(call compat-path,$(1)))) + +file-name = $(call decompat-path,$(notdir $(call compat-path,$(1)))) +directory-name = $(call decompat-path,$(dir $(call compat-path,$(1)))) + +escape-shell = '$(subst ','\'',$(1))' + +# +# The grouped-target symbol. Grouped targets are not supported on versions of +# GNU Make <= 4.2, which was most recently packaged with Ubuntu 20.04. +# + +& := $(if $(filter grouped-target,$(.FEATURES)),&) + +# +# Upper-case a string value. +# +# Parameters: +# +# - $(1): The string to upper-case. +# +# Example usage: +# +# $(call uppercase,HeLlO wOrLd) # "HELLO WORLD" +# + +uppercase = $(shell echo $(call escape-shell,$(1)) | tr '[:lower:]' '[:upper:]') + +# +# Lower-case a string value. +# +# Parameters: +# +# - $(1): The string to lower-case. +# +# Example usage: +# +# $(call lowercase,HeLlO wOrLd) # "hello world" +# + +lowercase = $(shell echo $(call escape-shell,$(1)) | tr '[:upper:]' '[:lower:]') + +# +# Determine the "truthiness" of a value. +# +# Parameters: +# +# - $(1): The value to determine the truthiness of. +# +# A value is considered to be falsy if it is: +# +# - empty, or +# - equal to "0", "N", "NO", "F" or "FALSE" after upper-casing. +# +# If the value is truthy then the value is returned as-is, otherwise no value +# is returned. +# +# Example usage: +# +# truthy := y +# truthy-bool := $(call bool,$(truthy)) # "y" +# +# falsy := n +# falsy-bool := $(call bool,$(falsy)) # <empty> +# + +bool = $(filter-out 0 n no f false,$(call lowercase,$(1))) + +# +# Determine the "truthiness" of a value, returning 0 or 1. +# +# Parameters: +# +# - $(1): The value to determine the truthiness of. +# +# A value is considered to be falsy if it is: +# +# - empty, or +# - equal to "0", "N", "NO", "F" or "FALSE" after upper-casing. +# +# If the value is truthy then the value is returned as-is, otherwise no value +# is returned. +# +# Example usage: +# +# truthy := y +# truthy-bool := $(call bool,$(truthy)) # "1" +# +# falsy := n +# falsy-bool := $(call bool,$(falsy)) # "0" +# + +bool-01 = $(if $(call bool,$(1)),1,0) + +# +# Determine whether a variable is defined or not. +# +# Parameters: +# +# - $(1): The variable to check. +# +# Example usage: +# +# xyz-defined := $(call defined,xyz) # <empty> +# +# xyz := +# xyz-defined := $(call defined,xyz) # <non-empty> +# +# xyz := hello +# xyz-defined := $(call defined,xyz) # <non-empty> +# + +defined = $(call bool,$(filter-out undefined,$(origin $(1)))) diff --git a/make_helpers/windows.mk b/make_helpers/windows.mk index ac0f9404..c24aa084 100644 --- a/make_helpers/windows.mk +++ b/make_helpers/windows.mk @@ -1,5 +1,5 @@ # -# Copyright (c) 2016-2023, Arm Limited and Contributors. All rights reserved. +# Copyright (c) 2016-2024, Arm Limited and Contributors. All rights reserved. # # SPDX-License-Identifier: BSD-3-Clause # @@ -12,8 +12,6 @@ ifndef WINDOWS_MK WINDOWS_MK := $(lastword $(MAKEFILE_LIST)) - ECHO_BLANK_LINE := @cmd /c echo. - ECHO_QUIET := @rem DIR_DELIM := $(strip \) BIN_EXT := .exe PATH_SEP := ; @@ -49,25 +47,15 @@ ifndef WINDOWS_MK $(eval $(foreach filename,$(wildcard ${1}),$(call DELETE_IF_THERE,${filename}))) endef - # ${1} is the directory to be generated. - # ${2} is optional, and allows prerequisites to be specified. - # Do nothing if $1 == $2, to ignore self dependencies. - define MAKE_PREREQ_DIR - ifneq (${1},${2}) - -${1} : ${2} - $(eval tmp_dir:=$(subst /,\,${1})) - -@if not exist "$(tmp_dir)" mkdir "${tmp_dir}" - - endif - endef - # ${1} is the directory to be removed. define SHELL_REMOVE_DIR $(eval tmp_dir:=$(subst /,\,${1})) -@if exist "$(tmp_dir)" rd /Q /S "$(tmp_dir)" endef + nul := nul + + which = $(shell where "$(1)" 2>$(nul)) endif # Because git is not available from CMD.EXE, we need to avoid @@ -76,17 +64,4 @@ endif # This can be overridden from the command line or environment. BUILD_STRING ?= development build -# The DOS echo shell command does not strip ' characters from the command -# parameters before printing. We therefore use an alternative method invoked -# by defining the MAKE_BUILD_STRINGS macro. -BUILT_TIME_DATE_STRING = const char build_message[] = "Built : "${BUILD_MESSAGE_TIMESTAMP}; -VERSION_STRING_MESSAGE = const char version_string[] = "${VERSION_STRING}"; -VERSION_MESSAGE = const char version[] = "${VERSION}"; -define MAKE_BUILD_STRINGS - $$(file >$1.in,$$(TF_CFLAGS) $$(CFLAGS)) - @echo $$(BUILT_TIME_DATE_STRING) $$(VERSION_STRING_MESSAGE) $$(VERSION_MESSAGE) | \ - $$(CC) @$1.in -x c -c - -o $1 -endef - MSVC_NMAKE := nmake.exe - diff --git a/package-lock.json b/package-lock.json index e43fa657..dd115597 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,57 +1,119 @@ { "name": "trusted-firmware-a", - "version": "2.10.0", - "lockfileVersion": 2, + "version": "2.12.0", + "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "trusted-firmware-a", - "version": "2.10.0", - "hasInstallScript": true, + "version": "2.12.0", "license": "BSD-3-Clause", "devDependencies": { - "@commitlint/cli": "^16.1.0", - "@commitlint/config-conventional": "^16.0.0", - "@commitlint/cz-commitlint": "^16.1.0", - "commitizen": "^4.2.4", + "@commitlint/cli": "^19.0.0", + "@commitlint/config-conventional": "^19.0.0", + "@commitlint/cz-commitlint": "^19.0.0", + "commitizen": "^4.3.0", "conventional-changelog-tf-a": "file:tools/conventional-changelog-tf-a", - "husky": "^7.0.4", + "husky": "^9.0.11", "js-yaml": "^4.1.0", - "standard-version": "^9.3.2" + "standard-version": "^9.5.0" }, "engines": { - "node": ">=16.0.0" + "node": ">=20" } }, "node_modules/@babel/code-frame": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.18.6.tgz", - "integrity": "sha512-TDCmlK5eOvH+eH7cdAFlNXeVJqWIQ7gW9tY1GJIpUtFb6CmjVyq2VM3u71bOyR8CRihcCgMUYoDNyLXao3+70Q==", + "version": "7.23.5", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.23.5.tgz", + "integrity": "sha512-CgH3s1a96LipHCmSUmYFPwY7MNx8C3avkq7i4Wl3cfa662ldtUe4VM1TPXX70pfmrlWTb6jLqTYrZyT2ZTJBgA==", "dev": true, "dependencies": { - "@babel/highlight": "^7.18.6" + "@babel/highlight": "^7.23.4", + "chalk": "^2.4.2" }, "engines": { "node": ">=6.9.0" } }, + "node_modules/@babel/code-frame/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/code-frame/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/code-frame/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/@babel/code-frame/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true + }, + "node_modules/@babel/code-frame/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/code-frame/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/@babel/helper-validator-identifier": { - "version": "7.19.1", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz", - "integrity": "sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==", + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz", + "integrity": "sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==", "dev": true, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/highlight": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.18.6.tgz", - "integrity": "sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g==", + "version": "7.23.4", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.23.4.tgz", + "integrity": "sha512-acGdbYSfp2WheJoJm/EBBBLh/ID8KDc64ISZ9DYtBmC8/Q204PZJLHyzeB5qMzJ5trcOkybd78M4x2KWsUq++A==", "dev": true, "dependencies": { - "@babel/helper-validator-identifier": "^7.18.6", - "chalk": "^2.0.0", + "@babel/helper-validator-identifier": "^7.22.20", + "chalk": "^2.4.2", "js-tokens": "^4.0.0" }, "engines": { @@ -121,4525 +183,1219 @@ } }, "node_modules/@commitlint/cli": { - "version": "16.3.0", - "resolved": "https://registry.npmjs.org/@commitlint/cli/-/cli-16.3.0.tgz", - "integrity": "sha512-P+kvONlfsuTMnxSwWE1H+ZcPMY3STFaHb2kAacsqoIkNx66O0T7sTpBxpxkMrFPyhkJiLJnJWMhk4bbvYD3BMA==", - "dev": true, - "dependencies": { - "@commitlint/format": "^16.2.1", - "@commitlint/lint": "^16.2.4", - "@commitlint/load": "^16.3.0", - "@commitlint/read": "^16.2.1", - "@commitlint/types": "^16.2.1", - "lodash": "^4.17.19", - "resolve-from": "5.0.0", - "resolve-global": "1.0.0", + "version": "19.0.0", + "resolved": "https://registry.npmjs.org/@commitlint/cli/-/cli-19.0.0.tgz", + "integrity": "sha512-SVBQG6k+eOOmlejYTtxnqJGmhrzy/m0qH3bVeoHY3gtlJBK3Kb32RjJioteBYk8Vuo58x5ehAjXwsQFX58X+xw==", + "dev": true, + "dependencies": { + "@commitlint/format": "^19.0.0", + "@commitlint/lint": "^19.0.0", + "@commitlint/load": "^19.0.0", + "@commitlint/read": "^19.0.0", + "@commitlint/types": "^19.0.0", + "execa": "^8.0.1", + "resolve-from": "^5.0.0", + "resolve-global": "^2.0.0", "yargs": "^17.0.0" }, "bin": { "commitlint": "cli.js" }, "engines": { - "node": ">=v12" + "node": ">=v18" } }, - "node_modules/@commitlint/config-conventional": { - "version": "16.2.4", - "resolved": "https://registry.npmjs.org/@commitlint/config-conventional/-/config-conventional-16.2.4.tgz", - "integrity": "sha512-av2UQJa3CuE5P0dzxj/o/B9XVALqYzEViHrMXtDrW9iuflrqCStWBAioijppj9URyz6ONpohJKAtSdgAOE0gkA==", + "node_modules/@commitlint/cli/node_modules/execa": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-8.0.1.tgz", + "integrity": "sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==", "dev": true, "dependencies": { - "conventional-changelog-conventionalcommits": "^4.3.1" + "cross-spawn": "^7.0.3", + "get-stream": "^8.0.1", + "human-signals": "^5.0.0", + "is-stream": "^3.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^5.1.0", + "onetime": "^6.0.0", + "signal-exit": "^4.1.0", + "strip-final-newline": "^3.0.0" }, "engines": { - "node": ">=v12" + "node": ">=16.17" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" } }, - "node_modules/@commitlint/config-validator": { - "version": "16.2.1", - "resolved": "https://registry.npmjs.org/@commitlint/config-validator/-/config-validator-16.2.1.tgz", - "integrity": "sha512-hogSe0WGg7CKmp4IfNbdNES3Rq3UEI4XRPB8JL4EPgo/ORq5nrGTVzxJh78omibNuB8Ho4501Czb1Er1MoDWpw==", + "node_modules/@commitlint/cli/node_modules/get-stream": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-8.0.1.tgz", + "integrity": "sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA==", "dev": true, - "dependencies": { - "@commitlint/types": "^16.2.1", - "ajv": "^6.12.6" - }, "engines": { - "node": ">=v12" + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/@commitlint/cz-commitlint": { - "version": "16.3.0", - "resolved": "https://registry.npmjs.org/@commitlint/cz-commitlint/-/cz-commitlint-16.3.0.tgz", - "integrity": "sha512-Q+QLQmSIHEgzI18F3/7mqq3vwL0IN9k+Tjp9Um4adFnRXMtUTnEa0er0CXAXxWvoA/x/6nt3t7faAv2HugDIGg==", + "node_modules/@commitlint/cli/node_modules/human-signals": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-5.0.0.tgz", + "integrity": "sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==", "dev": true, - "dependencies": { - "@commitlint/ensure": "^16.2.1", - "@commitlint/load": "^16.3.0", - "@commitlint/types": "^16.2.1", - "chalk": "^4.1.0", - "lodash": "^4.17.21", - "word-wrap": "^1.2.3" - }, "engines": { - "node": ">=v12" - }, - "peerDependencies": { - "commitizen": "^4.0.3", - "inquirer": "^8.0.0" + "node": ">=16.17.0" } }, - "node_modules/@commitlint/ensure": { - "version": "16.2.1", - "resolved": "https://registry.npmjs.org/@commitlint/ensure/-/ensure-16.2.1.tgz", - "integrity": "sha512-/h+lBTgf1r5fhbDNHOViLuej38i3rZqTQnBTk+xEg+ehOwQDXUuissQ5GsYXXqI5uGy+261ew++sT4EA3uBJ+A==", + "node_modules/@commitlint/cli/node_modules/is-stream": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-3.0.0.tgz", + "integrity": "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==", "dev": true, - "dependencies": { - "@commitlint/types": "^16.2.1", - "lodash": "^4.17.19" - }, "engines": { - "node": ">=v12" + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/@commitlint/execute-rule": { - "version": "16.2.1", - "resolved": "https://registry.npmjs.org/@commitlint/execute-rule/-/execute-rule-16.2.1.tgz", - "integrity": "sha512-oSls82fmUTLM6cl5V3epdVo4gHhbmBFvCvQGHBRdQ50H/690Uq1Dyd7hXMuKITCIdcnr9umyDkr8r5C6HZDF3g==", + "node_modules/@commitlint/cli/node_modules/mimic-fn": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-4.0.0.tgz", + "integrity": "sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==", "dev": true, "engines": { - "node": ">=v12" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/@commitlint/format": { - "version": "16.2.1", - "resolved": "https://registry.npmjs.org/@commitlint/format/-/format-16.2.1.tgz", - "integrity": "sha512-Yyio9bdHWmNDRlEJrxHKglamIk3d6hC0NkEUW6Ti6ipEh2g0BAhy8Od6t4vLhdZRa1I2n+gY13foy+tUgk0i1Q==", + "node_modules/@commitlint/cli/node_modules/npm-run-path": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.3.0.tgz", + "integrity": "sha512-ppwTtiJZq0O/ai0z7yfudtBpWIoxM8yE6nHi1X47eFR2EWORqfbu6CnPlNsjeN683eT0qG6H/Pyf9fCcvjnnnQ==", "dev": true, "dependencies": { - "@commitlint/types": "^16.2.1", - "chalk": "^4.0.0" + "path-key": "^4.0.0" }, "engines": { - "node": ">=v12" + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/@commitlint/is-ignored": { - "version": "16.2.4", - "resolved": "https://registry.npmjs.org/@commitlint/is-ignored/-/is-ignored-16.2.4.tgz", - "integrity": "sha512-Lxdq9aOAYCOOOjKi58ulbwK/oBiiKz+7Sq0+/SpFIEFwhHkIVugvDvWjh2VRBXmRC/x5lNcjDcYEwS/uYUvlYQ==", + "node_modules/@commitlint/cli/node_modules/onetime": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-6.0.0.tgz", + "integrity": "sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==", "dev": true, "dependencies": { - "@commitlint/types": "^16.2.1", - "semver": "7.3.7" + "mimic-fn": "^4.0.0" }, "engines": { - "node": ">=v12" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/@commitlint/lint": { - "version": "16.2.4", - "resolved": "https://registry.npmjs.org/@commitlint/lint/-/lint-16.2.4.tgz", - "integrity": "sha512-AUDuwOxb2eGqsXbTMON3imUGkc1jRdtXrbbohiLSCSk3jFVXgJLTMaEcr39pR00N8nE9uZ+V2sYaiILByZVmxQ==", + "node_modules/@commitlint/cli/node_modules/path-key": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz", + "integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==", "dev": true, - "dependencies": { - "@commitlint/is-ignored": "^16.2.4", - "@commitlint/parse": "^16.2.1", - "@commitlint/rules": "^16.2.4", - "@commitlint/types": "^16.2.1" - }, "engines": { - "node": ">=v12" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/@commitlint/load": { - "version": "16.3.0", - "resolved": "https://registry.npmjs.org/@commitlint/load/-/load-16.3.0.tgz", - "integrity": "sha512-3tykjV/iwbkv2FU9DG+NZ/JqmP0Nm3b7aDwgCNQhhKV5P74JAuByULkafnhn+zsFGypG1qMtI5u+BZoa9APm0A==", - "dev": true, - "dependencies": { - "@commitlint/config-validator": "^16.2.1", - "@commitlint/execute-rule": "^16.2.1", - "@commitlint/resolve-extends": "^16.2.1", - "@commitlint/types": "^16.2.1", - "@types/node": ">=12", - "chalk": "^4.0.0", - "cosmiconfig": "^7.0.0", - "cosmiconfig-typescript-loader": "^2.0.0", - "lodash": "^4.17.19", - "resolve-from": "^5.0.0", - "typescript": "^4.4.3" - }, + "node_modules/@commitlint/cli/node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true, "engines": { - "node": ">=v12" + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/@commitlint/message": { - "version": "16.2.1", - "resolved": "https://registry.npmjs.org/@commitlint/message/-/message-16.2.1.tgz", - "integrity": "sha512-2eWX/47rftViYg7a3axYDdrgwKv32mxbycBJT6OQY/MJM7SUfYNYYvbMFOQFaA4xIVZt7t2Alyqslbl6blVwWw==", + "node_modules/@commitlint/cli/node_modules/strip-final-newline": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-3.0.0.tgz", + "integrity": "sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==", "dev": true, "engines": { - "node": ">=v12" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/@commitlint/parse": { - "version": "16.2.1", - "resolved": "https://registry.npmjs.org/@commitlint/parse/-/parse-16.2.1.tgz", - "integrity": "sha512-2NP2dDQNL378VZYioLrgGVZhWdnJO4nAxQl5LXwYb08nEcN+cgxHN1dJV8OLJ5uxlGJtDeR8UZZ1mnQ1gSAD/g==", + "node_modules/@commitlint/config-conventional": { + "version": "19.0.0", + "resolved": "https://registry.npmjs.org/@commitlint/config-conventional/-/config-conventional-19.0.0.tgz", + "integrity": "sha512-d8lPm+slPUdA8Zof2Y36RqAm/MmAYx/QQIEd2gKbpfLThQK1oYLs+0C3sMPD+4LIq2kh4cnbV9WnPA0P5sN8Ig==", "dev": true, "dependencies": { - "@commitlint/types": "^16.2.1", - "conventional-changelog-angular": "^5.0.11", - "conventional-commits-parser": "^3.2.2" + "@commitlint/types": "^19.0.0", + "conventional-changelog-conventionalcommits": "^7.0.2" }, "engines": { - "node": ">=v12" + "node": ">=v18" } }, - "node_modules/@commitlint/read": { - "version": "16.2.1", - "resolved": "https://registry.npmjs.org/@commitlint/read/-/read-16.2.1.tgz", - "integrity": "sha512-tViXGuaxLTrw2r7PiYMQOFA2fueZxnnt0lkOWqKyxT+n2XdEMGYcI9ID5ndJKXnfPGPppD0w/IItKsIXlZ+alw==", + "node_modules/@commitlint/config-conventional/node_modules/conventional-changelog-conventionalcommits": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/conventional-changelog-conventionalcommits/-/conventional-changelog-conventionalcommits-7.0.2.tgz", + "integrity": "sha512-NKXYmMR/Hr1DevQegFB4MwfM5Vv0m4UIxKZTTYuD98lpTknaZlSRrDOG4X7wIXpGkfsYxZTghUN+Qq+T0YQI7w==", "dev": true, "dependencies": { - "@commitlint/top-level": "^16.2.1", - "@commitlint/types": "^16.2.1", - "fs-extra": "^10.0.0", - "git-raw-commits": "^2.0.0" + "compare-func": "^2.0.0" }, "engines": { - "node": ">=v12" + "node": ">=16" } }, - "node_modules/@commitlint/resolve-extends": { - "version": "16.2.1", - "resolved": "https://registry.npmjs.org/@commitlint/resolve-extends/-/resolve-extends-16.2.1.tgz", - "integrity": "sha512-NbbCMPKTFf2J805kwfP9EO+vV+XvnaHRcBy6ud5dF35dxMsvdJqke54W3XazXF1ZAxC4a3LBy4i/GNVBAthsEg==", + "node_modules/@commitlint/config-validator": { + "version": "19.0.0", + "resolved": "https://registry.npmjs.org/@commitlint/config-validator/-/config-validator-19.0.0.tgz", + "integrity": "sha512-oxJ2k+jBPRyWzv1ixfxwGZO5DJ1S+v3D8u/QESMwuPh3kQmeOYBRxGI+5FDWMwiVSHpztlhvvxDAU9SFXeMqUA==", "dev": true, "dependencies": { - "@commitlint/config-validator": "^16.2.1", - "@commitlint/types": "^16.2.1", - "import-fresh": "^3.0.0", - "lodash": "^4.17.19", - "resolve-from": "^5.0.0", - "resolve-global": "^1.0.0" + "@commitlint/types": "^19.0.0", + "ajv": "^8.11.0" }, "engines": { - "node": ">=v12" + "node": ">=v18" } }, - "node_modules/@commitlint/rules": { - "version": "16.2.4", - "resolved": "https://registry.npmjs.org/@commitlint/rules/-/rules-16.2.4.tgz", - "integrity": "sha512-rK5rNBIN2ZQNQK+I6trRPK3dWa0MtaTN4xnwOma1qxa4d5wQMQJtScwTZjTJeallFxhOgbNOgr48AMHkdounVg==", + "node_modules/@commitlint/cz-commitlint": { + "version": "19.0.0", + "resolved": "https://registry.npmjs.org/@commitlint/cz-commitlint/-/cz-commitlint-19.0.0.tgz", + "integrity": "sha512-hIWExZOycAuq0fW7rBq23AuBMJAmvTuM3GSlAX5kSV8gvASwXSrHRKgxrHQCcozV/ZnLlbFEvfVgBRi+UbH8pA==", "dev": true, "dependencies": { - "@commitlint/ensure": "^16.2.1", - "@commitlint/message": "^16.2.1", - "@commitlint/to-lines": "^16.2.1", - "@commitlint/types": "^16.2.1", - "execa": "^5.0.0" + "@commitlint/ensure": "^19.0.0", + "@commitlint/load": "^19.0.0", + "@commitlint/types": "^19.0.0", + "chalk": "^5.3.0", + "lodash.isplainobject": "^4.0.6", + "word-wrap": "^1.2.5" }, "engines": { - "node": ">=v12" + "node": ">=v18" + }, + "peerDependencies": { + "commitizen": "^4.0.3", + "inquirer": "^9.0.0" } }, - "node_modules/@commitlint/to-lines": { - "version": "16.2.1", - "resolved": "https://registry.npmjs.org/@commitlint/to-lines/-/to-lines-16.2.1.tgz", - "integrity": "sha512-9/VjpYj5j1QeY3eiog1zQWY6axsdWAc0AonUUfyZ7B0MVcRI0R56YsHAfzF6uK/g/WwPZaoe4Lb1QCyDVnpVaQ==", + "node_modules/@commitlint/cz-commitlint/node_modules/chalk": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.3.0.tgz", + "integrity": "sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==", "dev": true, "engines": { - "node": ">=v12" + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/@commitlint/top-level": { - "version": "16.2.1", - "resolved": "https://registry.npmjs.org/@commitlint/top-level/-/top-level-16.2.1.tgz", - "integrity": "sha512-lS6GSieHW9y6ePL73ied71Z9bOKyK+Ib9hTkRsB8oZFAyQZcyRwq2w6nIa6Fngir1QW51oKzzaXfJL94qwImyw==", + "node_modules/@commitlint/ensure": { + "version": "19.0.0", + "resolved": "https://registry.npmjs.org/@commitlint/ensure/-/ensure-19.0.0.tgz", + "integrity": "sha512-G0avCIwjKplTP1Oc9MlDhsYqi1yOWORtJSBpyMbQEnalQAW1tuRxG4LOLRZVKfFqlDWs2SfVQPN0Uw51Ge0f6w==", "dev": true, "dependencies": { - "find-up": "^5.0.0" + "@commitlint/types": "^19.0.0", + "lodash.camelcase": "^4.3.0", + "lodash.kebabcase": "^4.1.1", + "lodash.snakecase": "^4.1.1", + "lodash.startcase": "^4.4.0", + "lodash.upperfirst": "^4.3.1" }, "engines": { - "node": ">=v12" + "node": ">=v18" } }, - "node_modules/@commitlint/types": { - "version": "16.2.1", - "resolved": "https://registry.npmjs.org/@commitlint/types/-/types-16.2.1.tgz", - "integrity": "sha512-7/z7pA7BM0i8XvMSBynO7xsB3mVQPUZbVn6zMIlp/a091XJ3qAXRXc+HwLYhiIdzzS5fuxxNIHZMGHVD4HJxdA==", + "node_modules/@commitlint/execute-rule": { + "version": "19.0.0", + "resolved": "https://registry.npmjs.org/@commitlint/execute-rule/-/execute-rule-19.0.0.tgz", + "integrity": "sha512-mtsdpY1qyWgAO/iOK0L6gSGeR7GFcdW7tIjcNFxcWkfLDF5qVbPHKuGATFqRMsxcO8OUKNj0+3WOHB7EHm4Jdw==", "dev": true, - "dependencies": { - "chalk": "^4.0.0" - }, "engines": { - "node": ">=v12" + "node": ">=v18" } }, - "node_modules/@cspotcode/source-map-support": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", - "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", + "node_modules/@commitlint/format": { + "version": "19.0.0", + "resolved": "https://registry.npmjs.org/@commitlint/format/-/format-19.0.0.tgz", + "integrity": "sha512-36P4/2tpGSGQsYoSZEso5fTSTaMSArIK9fszy+5B8hwwAvOfnD4kQtrwfMhiXnf7PCgeX2lx5Jma+pY3Bq326A==", "dev": true, "dependencies": { - "@jridgewell/trace-mapping": "0.3.9" + "@commitlint/types": "^19.0.0", + "chalk": "^5.3.0" }, "engines": { - "node": ">=12" + "node": ">=v18" } }, - "node_modules/@hutson/parse-repository-url": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/@hutson/parse-repository-url/-/parse-repository-url-3.0.2.tgz", - "integrity": "sha512-H9XAx3hc0BQHY6l+IFSWHDySypcXsvsuLhgYLUGywmJ5pswRVQJUHpOsobnLYp2ZUaUlKiKDrgWWhosOwAEM8Q==", + "node_modules/@commitlint/format/node_modules/chalk": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.3.0.tgz", + "integrity": "sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==", "dev": true, "engines": { - "node": ">=6.9.0" + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/@jridgewell/resolve-uri": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz", - "integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==", + "node_modules/@commitlint/is-ignored": { + "version": "19.0.0", + "resolved": "https://registry.npmjs.org/@commitlint/is-ignored/-/is-ignored-19.0.0.tgz", + "integrity": "sha512-5b2nIrl8GEjzYAnOK2ZAUxBXvUonYrp3+8kJkUMl8QOtjt2O1gsd71jar7UtoDEqTWJhc+n7lG6lQYMXtcQJAw==", "dev": true, + "dependencies": { + "@commitlint/types": "^19.0.0", + "semver": "^7.6.0" + }, "engines": { - "node": ">=6.0.0" + "node": ">=v18" } }, - "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.4.14", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz", - "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==", - "dev": true - }, - "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.9", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", - "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", + "node_modules/@commitlint/lint": { + "version": "19.0.0", + "resolved": "https://registry.npmjs.org/@commitlint/lint/-/lint-19.0.0.tgz", + "integrity": "sha512-rAAisSpxhA+z4uhsveSt1CuTB+Jld5d7zyNSEK2UWjQaOxicwDP+LFiOdM32n/vwsLlOJqhrInA50UcbRSVaGg==", "dev": true, "dependencies": { - "@jridgewell/resolve-uri": "^3.0.3", - "@jridgewell/sourcemap-codec": "^1.4.10" + "@commitlint/is-ignored": "^19.0.0", + "@commitlint/parse": "^19.0.0", + "@commitlint/rules": "^19.0.0", + "@commitlint/types": "^19.0.0" + }, + "engines": { + "node": ">=v18" } }, - "node_modules/@tsconfig/node10": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz", - "integrity": "sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==", - "dev": true - }, - "node_modules/@tsconfig/node12": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", - "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", - "dev": true - }, - "node_modules/@tsconfig/node14": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", - "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", - "dev": true - }, - "node_modules/@tsconfig/node16": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.3.tgz", - "integrity": "sha512-yOlFc+7UtL/89t2ZhjPvvB/DeAr3r+Dq58IgzsFkOAvVC6NMJXmCGjbptdXdR9qsX7pKcTL+s87FtYREi2dEEQ==", - "dev": true - }, - "node_modules/@types/minimist": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/@types/minimist/-/minimist-1.2.2.tgz", - "integrity": "sha512-jhuKLIRrhvCPLqwPcx6INqmKeiA5EWrsCOPhrlFSrbrmU4ZMPjj5Ul/oLCMDO98XRUIwVm78xICz4EPCektzeQ==", - "dev": true - }, - "node_modules/@types/node": { - "version": "18.8.3", - "resolved": "https://registry.npmjs.org/@types/node/-/node-18.8.3.tgz", - "integrity": "sha512-0os9vz6BpGwxGe9LOhgP/ncvYN5Tx1fNcd2TM3rD/aCGBkysb+ZWpXEocG24h6ZzOi13+VB8HndAQFezsSOw1w==", - "dev": true + "node_modules/@commitlint/load": { + "version": "19.0.0", + "resolved": "https://registry.npmjs.org/@commitlint/load/-/load-19.0.0.tgz", + "integrity": "sha512-pC/6xDjkWPWgqfILY0KMMpxz0dTZqC7fUpxyWMLRrlbZCC9S54/gsg/8UltFrUH+C+F1zz4Ip8CQgzKonpH6rg==", + "dev": true, + "dependencies": { + "@commitlint/config-validator": "^19.0.0", + "@commitlint/execute-rule": "^19.0.0", + "@commitlint/resolve-extends": "^19.0.0", + "@commitlint/types": "^19.0.0", + "chalk": "^5.3.0", + "cosmiconfig": "^8.3.6", + "cosmiconfig-typescript-loader": "^5.0.0", + "lodash.isplainobject": "^4.0.6", + "lodash.merge": "^4.6.2", + "lodash.uniq": "^4.5.0" + }, + "engines": { + "node": ">=v18" + } }, - "node_modules/@types/normalize-package-data": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.1.tgz", - "integrity": "sha512-Gj7cI7z+98M282Tqmp2K5EIsoouUEzbBJhQQzDE3jSIRk6r9gsz0oUokqIUR4u1R3dMHo0pDHM7sNOHyhulypw==", - "dev": true + "node_modules/@commitlint/load/node_modules/chalk": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.3.0.tgz", + "integrity": "sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==", + "dev": true, + "engines": { + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } }, - "node_modules/@types/parse-json": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.0.tgz", - "integrity": "sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==", - "dev": true + "node_modules/@commitlint/message": { + "version": "19.0.0", + "resolved": "https://registry.npmjs.org/@commitlint/message/-/message-19.0.0.tgz", + "integrity": "sha512-c9czf6lU+9oF9gVVa2lmKaOARJvt4soRsVmbR7Njwp9FpbBgste5i7l/2l5o8MmbwGh4yE1snfnsy2qyA2r/Fw==", + "dev": true, + "engines": { + "node": ">=v18" + } }, - "node_modules/acorn": { - "version": "8.8.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.0.tgz", - "integrity": "sha512-QOxyigPVrpZ2GXT+PFyZTl6TtOFc5egxHIP9IlQ+RbupQuX4RkT/Bee4/kQuC02Xkzg84JcT7oLYtDIQxp+v7w==", + "node_modules/@commitlint/parse": { + "version": "19.0.0", + "resolved": "https://registry.npmjs.org/@commitlint/parse/-/parse-19.0.0.tgz", + "integrity": "sha512-/2hT08V/2Lh+aQ5cSAw5vO74FlA3LJGYzLfsNMcx6aW8Kmrsa9W7chNNY5hMWbucCF92s/JE3eVIHnzoEBKTTA==", "dev": true, - "bin": { - "acorn": "bin/acorn" + "dependencies": { + "@commitlint/types": "^19.0.0", + "conventional-changelog-angular": "^7.0.0", + "conventional-commits-parser": "^5.0.0" }, "engines": { - "node": ">=0.4.0" + "node": ">=v18" } }, - "node_modules/acorn-walk": { - "version": "8.2.0", - "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz", - "integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==", + "node_modules/@commitlint/parse/node_modules/conventional-changelog-angular": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/conventional-changelog-angular/-/conventional-changelog-angular-7.0.0.tgz", + "integrity": "sha512-ROjNchA9LgfNMTTFSIWPzebCwOGFdgkEq45EnvvrmSLvCtAw0HSmrCs7/ty+wAeYUZyNay0YMUNYFTRL72PkBQ==", "dev": true, + "dependencies": { + "compare-func": "^2.0.0" + }, "engines": { - "node": ">=0.4.0" + "node": ">=16" } }, - "node_modules/add-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/add-stream/-/add-stream-1.0.0.tgz", - "integrity": "sha512-qQLMr+8o0WC4FZGQTcJiKBVC59JylcPSrTtk6usvmIDFUOCKegapy1VHQwRbFMOFyb/inzUVqHs+eMYKDM1YeQ==", - "dev": true - }, - "node_modules/ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "node_modules/@commitlint/parse/node_modules/conventional-commits-parser": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/conventional-commits-parser/-/conventional-commits-parser-5.0.0.tgz", + "integrity": "sha512-ZPMl0ZJbw74iS9LuX9YIAiW8pfM5p3yh2o/NbXHbkFuZzY5jvdi5jFycEOkmBW5H5I7nA+D6f3UcsCLP2vvSEA==", "dev": true, "dependencies": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" + "is-text-path": "^2.0.0", + "JSONStream": "^1.3.5", + "meow": "^12.0.1", + "split2": "^4.0.0" }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" + "bin": { + "conventional-commits-parser": "cli.mjs" + }, + "engines": { + "node": ">=16" } }, - "node_modules/ansi-escapes": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", - "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", + "node_modules/@commitlint/parse/node_modules/is-text-path": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-text-path/-/is-text-path-2.0.0.tgz", + "integrity": "sha512-+oDTluR6WEjdXEJMnC2z6A4FRwFoYuvShVVEGsS7ewc0UTi2QtAKMDJuL4BDEVt+5T7MjFo12RP8ghOM75oKJw==", "dev": true, "dependencies": { - "type-fest": "^0.21.3" + "text-extensions": "^2.0.0" }, "engines": { "node": ">=8" + } + }, + "node_modules/@commitlint/parse/node_modules/meow": { + "version": "12.1.1", + "resolved": "https://registry.npmjs.org/meow/-/meow-12.1.1.tgz", + "integrity": "sha512-BhXM0Au22RwUneMPwSCnyhTOizdWoIEPU9sp0Aqa1PnDMR5Wv2FGXYDjuzJEIX+Eo2Rb8xuYe5jrnm5QowQFkw==", + "dev": true, + "engines": { + "node": ">=16.10" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "node_modules/@commitlint/parse/node_modules/split2": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz", + "integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==", "dev": true, "engines": { - "node": ">=8" + "node": ">= 10.x" } }, - "node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "node_modules/@commitlint/parse/node_modules/text-extensions": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/text-extensions/-/text-extensions-2.4.0.tgz", + "integrity": "sha512-te/NtwBwfiNRLf9Ijqx3T0nlqZiQ2XrrtBvu+cLL8ZRrGkO0NHTug8MYFKyoSrv/sHTaSKfilUkizV6XhxMJ3g==", "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, "engines": { "node": ">=8" }, "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/arg": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", - "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", - "dev": true - }, - "node_modules/argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true - }, - "node_modules/array-ify": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/array-ify/-/array-ify-1.0.0.tgz", - "integrity": "sha512-c5AMf34bKdvPhQ7tBGhqkgKNUzMr4WUs+WDtC2ZUGOUncbxKMTvqxYctiseW3+L4bA8ec+GcZ6/A/FW4m8ukng==", - "dev": true + "node_modules/@commitlint/read": { + "version": "19.0.0", + "resolved": "https://registry.npmjs.org/@commitlint/read/-/read-19.0.0.tgz", + "integrity": "sha512-AbK/fQjWrXGAAHl+KeOtZtWJryhzkTnynhkABF4IUFZqK71JSviSIPHYuUQjdwNrD0PJGs5f19ORjY8LOXP08w==", + "dev": true, + "dependencies": { + "@commitlint/top-level": "^19.0.0", + "@commitlint/types": "^19.0.0", + "git-raw-commits": "^4.0.0", + "minimist": "^1.2.8" + }, + "engines": { + "node": ">=v18" + } }, - "node_modules/arrify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz", - "integrity": "sha512-3CYzex9M9FGQjCGMGyi6/31c8GJbgb0qGyrx5HWxPd0aCwh4cB2YjMb2Xf9UuoogrMrlO9cTqnB5rI5GHZTcUA==", + "node_modules/@commitlint/read/node_modules/dargs": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/dargs/-/dargs-8.1.0.tgz", + "integrity": "sha512-wAV9QHOsNbwnWdNW2FYvE1P56wtgSbM+3SZcdGiWQILwVjACCXDCI3Ai8QlCjMDB8YK5zySiXZYBiwGmNY3lnw==", "dev": true, "engines": { - "node": ">=0.10.0" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/at-least-node": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz", - "integrity": "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==", + "node_modules/@commitlint/read/node_modules/git-raw-commits": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/git-raw-commits/-/git-raw-commits-4.0.0.tgz", + "integrity": "sha512-ICsMM1Wk8xSGMowkOmPrzo2Fgmfo4bMHLNX6ytHjajRJUqvHOw/TFapQ+QG75c3X/tTDDhOSRPGC52dDbNM8FQ==", "dev": true, + "dependencies": { + "dargs": "^8.0.0", + "meow": "^12.0.1", + "split2": "^4.0.0" + }, + "bin": { + "git-raw-commits": "cli.mjs" + }, "engines": { - "node": ">= 4.0.0" + "node": ">=16" } }, - "node_modules/balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true + "node_modules/@commitlint/read/node_modules/meow": { + "version": "12.1.1", + "resolved": "https://registry.npmjs.org/meow/-/meow-12.1.1.tgz", + "integrity": "sha512-BhXM0Au22RwUneMPwSCnyhTOizdWoIEPU9sp0Aqa1PnDMR5Wv2FGXYDjuzJEIX+Eo2Rb8xuYe5jrnm5QowQFkw==", + "dev": true, + "engines": { + "node": ">=16.10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } }, - "node_modules/base64-js": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", - "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "node_modules/@commitlint/read/node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] + "funding": { + "url": "https://github.com/sponsors/ljharb" + } }, - "node_modules/bl": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", - "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", + "node_modules/@commitlint/read/node_modules/split2": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz", + "integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==", "dev": true, - "dependencies": { - "buffer": "^5.5.0", - "inherits": "^2.0.4", - "readable-stream": "^3.4.0" + "engines": { + "node": ">= 10.x" } }, - "node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "node_modules/@commitlint/resolve-extends": { + "version": "19.0.0", + "resolved": "https://registry.npmjs.org/@commitlint/resolve-extends/-/resolve-extends-19.0.0.tgz", + "integrity": "sha512-ej0fALn5yZQOYKH8wPZnzw5LGvD0n5gJBPvV6DnMiSYudqgwYwhdNJ//MukZCXNpLIM1yMA8KUyrCP6D4WnUbg==", "dev": true, "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" + "@commitlint/config-validator": "^19.0.0", + "@commitlint/types": "^19.0.0", + "import-fresh": "^3.0.0", + "import-meta-resolve": "^4.0.0", + "lodash.mergewith": "^4.6.2", + "resolve-global": "^2.0.0" + }, + "engines": { + "node": ">=v18" } }, - "node_modules/braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "node_modules/@commitlint/rules": { + "version": "19.0.0", + "resolved": "https://registry.npmjs.org/@commitlint/rules/-/rules-19.0.0.tgz", + "integrity": "sha512-uwb5Ro5vvJlEjnWPezL3AcdlbLdJz24SD5VembgA6IXqqunphZr5LFsQL1z5efP7p3MUdJEXFynIx8o62+j2lA==", "dev": true, "dependencies": { - "fill-range": "^7.0.1" + "@commitlint/ensure": "^19.0.0", + "@commitlint/message": "^19.0.0", + "@commitlint/to-lines": "^19.0.0", + "@commitlint/types": "^19.0.0", + "execa": "^8.0.1" }, "engines": { - "node": ">=8" + "node": ">=v18" } }, - "node_modules/buffer": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", - "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "node_modules/@commitlint/rules/node_modules/execa": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-8.0.1.tgz", + "integrity": "sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==", "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], "dependencies": { - "base64-js": "^1.3.1", - "ieee754": "^1.1.13" + "cross-spawn": "^7.0.3", + "get-stream": "^8.0.1", + "human-signals": "^5.0.0", + "is-stream": "^3.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^5.1.0", + "onetime": "^6.0.0", + "signal-exit": "^4.1.0", + "strip-final-newline": "^3.0.0" + }, + "engines": { + "node": ">=16.17" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" } }, - "node_modules/buffer-from": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", - "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", - "dev": true + "node_modules/@commitlint/rules/node_modules/get-stream": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-8.0.1.tgz", + "integrity": "sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA==", + "dev": true, + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } }, - "node_modules/cachedir": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/cachedir/-/cachedir-2.3.0.tgz", - "integrity": "sha512-A+Fezp4zxnit6FanDmv9EqXNAi3vt9DWp51/71UEhXukb7QUuvtv9344h91dyAxuTLoSYJFU299qzR3tzwPAhw==", + "node_modules/@commitlint/rules/node_modules/human-signals": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-5.0.0.tgz", + "integrity": "sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==", "dev": true, "engines": { - "node": ">=6" + "node": ">=16.17.0" } }, - "node_modules/callsites": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", - "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "node_modules/@commitlint/rules/node_modules/is-stream": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-3.0.0.tgz", + "integrity": "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==", "dev": true, "engines": { - "node": ">=6" + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/camelcase": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "node_modules/@commitlint/rules/node_modules/mimic-fn": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-4.0.0.tgz", + "integrity": "sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==", "dev": true, "engines": { - "node": ">=6" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/camelcase-keys": { - "version": "6.2.2", - "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-6.2.2.tgz", - "integrity": "sha512-YrwaA0vEKazPBkn0ipTiMpSajYDSe+KjQfrjhcBMxJt/znbvlHd8Pw/Vamaz5EB4Wfhs3SUR3Z9mwRu/P3s3Yg==", + "node_modules/@commitlint/rules/node_modules/npm-run-path": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.3.0.tgz", + "integrity": "sha512-ppwTtiJZq0O/ai0z7yfudtBpWIoxM8yE6nHi1X47eFR2EWORqfbu6CnPlNsjeN683eT0qG6H/Pyf9fCcvjnnnQ==", "dev": true, "dependencies": { - "camelcase": "^5.3.1", - "map-obj": "^4.0.0", - "quick-lru": "^4.0.1" + "path-key": "^4.0.0" }, "engines": { - "node": ">=8" + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "node_modules/@commitlint/rules/node_modules/onetime": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-6.0.0.tgz", + "integrity": "sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==", "dev": true, "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" + "mimic-fn": "^4.0.0" }, "engines": { - "node": ">=10" + "node": ">=12" }, "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/chardet": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", - "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==", - "dev": true - }, - "node_modules/cli-cursor": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", - "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", + "node_modules/@commitlint/rules/node_modules/path-key": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz", + "integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==", "dev": true, - "dependencies": { - "restore-cursor": "^3.1.0" + "engines": { + "node": ">=12" }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@commitlint/rules/node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true, "engines": { - "node": ">=8" + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/cli-spinners": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.7.0.tgz", - "integrity": "sha512-qu3pN8Y3qHNgE2AFweciB1IfMnmZ/fsNTEE+NOFjmGB2F/7rLhnhzppvpCnN4FovtP26k8lHyy9ptEbNwWFLzw==", + "node_modules/@commitlint/rules/node_modules/strip-final-newline": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-3.0.0.tgz", + "integrity": "sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==", "dev": true, "engines": { - "node": ">=6" + "node": ">=12" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/cli-width": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-3.0.0.tgz", - "integrity": "sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw==", + "node_modules/@commitlint/to-lines": { + "version": "19.0.0", + "resolved": "https://registry.npmjs.org/@commitlint/to-lines/-/to-lines-19.0.0.tgz", + "integrity": "sha512-vkxWo+VQU5wFhiP9Ub9Sre0FYe019JxFikrALVoD5UGa8/t3yOJEpEhxC5xKiENKKhUkTpEItMTRAjHw2SCpZw==", "dev": true, "engines": { - "node": ">= 10" + "node": ">=v18" } }, - "node_modules/cliui": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", - "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "node_modules/@commitlint/top-level": { + "version": "19.0.0", + "resolved": "https://registry.npmjs.org/@commitlint/top-level/-/top-level-19.0.0.tgz", + "integrity": "sha512-KKjShd6u1aMGNkCkaX4aG1jOGdn7f8ZI8TR1VEuNqUOjWTOdcDSsmglinglJ18JTjuBX5I1PtjrhQCRcixRVFQ==", "dev": true, "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.1", - "wrap-ansi": "^7.0.0" + "find-up": "^7.0.0" }, "engines": { - "node": ">=12" + "node": ">=v18" } }, - "node_modules/clone": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", - "integrity": "sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==", + "node_modules/@commitlint/top-level/node_modules/find-up": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-7.0.0.tgz", + "integrity": "sha512-YyZM99iHrqLKjmt4LJDj58KI+fYyufRLBSYcqycxf//KpBk9FoewoGX0450m9nB44qrZnovzC2oeP5hUibxc/g==", "dev": true, + "dependencies": { + "locate-path": "^7.2.0", + "path-exists": "^5.0.0", + "unicorn-magic": "^0.1.0" + }, "engines": { - "node": ">=0.8" + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "node_modules/@commitlint/top-level/node_modules/locate-path": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-7.2.0.tgz", + "integrity": "sha512-gvVijfZvn7R+2qyPX8mAuKcFGDf6Nc61GdvGafQsHL0sBIxfKzA+usWn4GFC/bk+QdwPUD4kWFJLhElipq+0VA==", "dev": true, "dependencies": { - "color-name": "~1.1.4" + "p-locate": "^6.0.0" }, "engines": { - "node": ">=7.0.0" + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "node_modules/commitizen": { - "version": "4.2.5", - "resolved": "https://registry.npmjs.org/commitizen/-/commitizen-4.2.5.tgz", - "integrity": "sha512-9sXju8Qrz1B4Tw7kC5KhnvwYQN88qs2zbiB8oyMsnXZyJ24PPGiNM3nHr73d32dnE3i8VJEXddBFIbOgYSEXtQ==", + "node_modules/@commitlint/top-level/node_modules/p-limit": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-4.0.0.tgz", + "integrity": "sha512-5b0R4txpzjPWVw/cXXUResoD4hb6U/x9BH08L7nw+GN1sezDzPdxeRvpc9c433fZhBan/wusjbCsqwqm4EIBIQ==", "dev": true, "dependencies": { - "cachedir": "2.3.0", - "cz-conventional-changelog": "3.3.0", - "dedent": "0.7.0", - "detect-indent": "6.1.0", - "find-node-modules": "^2.1.2", - "find-root": "1.1.0", - "fs-extra": "9.1.0", - "glob": "7.2.3", - "inquirer": "8.2.4", - "is-utf8": "^0.2.1", - "lodash": "4.17.21", - "minimist": "1.2.6", - "strip-bom": "4.0.0", - "strip-json-comments": "3.1.1" - }, - "bin": { - "commitizen": "bin/commitizen", - "cz": "bin/git-cz", - "git-cz": "bin/git-cz" - }, - "engines": { - "node": ">= 12" - } - }, - "node_modules/commitizen/node_modules/fs-extra": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", - "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", - "dev": true, - "dependencies": { - "at-least-node": "^1.0.0", - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/compare-func": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/compare-func/-/compare-func-2.0.0.tgz", - "integrity": "sha512-zHig5N+tPWARooBnb0Zx1MFcdfpyJrfTJ3Y5L+IFvUm8rM74hHz66z0gw0x4tijh5CorKkKUCnW82R2vmpeCRA==", - "dev": true, - "dependencies": { - "array-ify": "^1.0.0", - "dot-prop": "^5.1.0" - } - }, - "node_modules/concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", - "dev": true - }, - "node_modules/concat-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-2.0.0.tgz", - "integrity": "sha512-MWufYdFw53ccGjCA+Ol7XJYpAlW6/prSMzuPOTRnJGcGzuhLn4Scrz7qf6o8bROZ514ltazcIFJZevcfbo0x7A==", - "dev": true, - "engines": [ - "node >= 6.0" - ], - "dependencies": { - "buffer-from": "^1.0.0", - "inherits": "^2.0.3", - "readable-stream": "^3.0.2", - "typedarray": "^0.0.6" - } - }, - "node_modules/conventional-changelog": { - "version": "3.1.25", - "resolved": "https://registry.npmjs.org/conventional-changelog/-/conventional-changelog-3.1.25.tgz", - "integrity": "sha512-ryhi3fd1mKf3fSjbLXOfK2D06YwKNic1nC9mWqybBHdObPd8KJ2vjaXZfYj1U23t+V8T8n0d7gwnc9XbIdFbyQ==", - "dev": true, - "dependencies": { - "conventional-changelog-angular": "^5.0.12", - "conventional-changelog-atom": "^2.0.8", - "conventional-changelog-codemirror": "^2.0.8", - "conventional-changelog-conventionalcommits": "^4.5.0", - "conventional-changelog-core": "^4.2.1", - "conventional-changelog-ember": "^2.0.9", - "conventional-changelog-eslint": "^3.0.9", - "conventional-changelog-express": "^2.0.6", - "conventional-changelog-jquery": "^3.0.11", - "conventional-changelog-jshint": "^2.0.9", - "conventional-changelog-preset-loader": "^2.3.4" + "yocto-queue": "^1.0.0" }, "engines": { - "node": ">=10" - } - }, - "node_modules/conventional-changelog-angular": { - "version": "5.0.13", - "resolved": "https://registry.npmjs.org/conventional-changelog-angular/-/conventional-changelog-angular-5.0.13.tgz", - "integrity": "sha512-i/gipMxs7s8L/QeuavPF2hLnJgH6pEZAttySB6aiQLWcX3puWDL3ACVmvBhJGxnAy52Qc15ua26BufY6KpmrVA==", - "dev": true, - "dependencies": { - "compare-func": "^2.0.0", - "q": "^1.5.1" + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" }, - "engines": { - "node": ">=10" + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/conventional-changelog-atom": { - "version": "2.0.8", - "resolved": "https://registry.npmjs.org/conventional-changelog-atom/-/conventional-changelog-atom-2.0.8.tgz", - "integrity": "sha512-xo6v46icsFTK3bb7dY/8m2qvc8sZemRgdqLb/bjpBsH2UyOS8rKNTgcb5025Hri6IpANPApbXMg15QLb1LJpBw==", + "node_modules/@commitlint/top-level/node_modules/p-locate": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-6.0.0.tgz", + "integrity": "sha512-wPrq66Llhl7/4AGC6I+cqxT07LhXvWL08LNXz1fENOw0Ap4sRZZ/gZpTTJ5jpurzzzfS2W/Ge9BY3LgLjCShcw==", "dev": true, "dependencies": { - "q": "^1.5.1" + "p-limit": "^4.0.0" }, "engines": { - "node": ">=10" - } - }, - "node_modules/conventional-changelog-codemirror": { - "version": "2.0.8", - "resolved": "https://registry.npmjs.org/conventional-changelog-codemirror/-/conventional-changelog-codemirror-2.0.8.tgz", - "integrity": "sha512-z5DAsn3uj1Vfp7po3gpt2Boc+Bdwmw2++ZHa5Ak9k0UKsYAO5mH1UBTN0qSCuJZREIhX6WU4E1p3IW2oRCNzQw==", - "dev": true, - "dependencies": { - "q": "^1.5.1" + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" }, - "engines": { - "node": ">=10" + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/conventional-changelog-config-spec": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/conventional-changelog-config-spec/-/conventional-changelog-config-spec-2.1.0.tgz", - "integrity": "sha512-IpVePh16EbbB02V+UA+HQnnPIohgXvJRxHcS5+Uwk4AT5LjzCZJm5sp/yqs5C6KZJ1jMsV4paEV13BN1pvDuxQ==", - "dev": true - }, - "node_modules/conventional-changelog-conventionalcommits": { - "version": "4.6.3", - "resolved": "https://registry.npmjs.org/conventional-changelog-conventionalcommits/-/conventional-changelog-conventionalcommits-4.6.3.tgz", - "integrity": "sha512-LTTQV4fwOM4oLPad317V/QNQ1FY4Hju5qeBIM1uTHbrnCE+Eg4CdRZ3gO2pUeR+tzWdp80M2j3qFFEDWVqOV4g==", + "node_modules/@commitlint/top-level/node_modules/path-exists": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-5.0.0.tgz", + "integrity": "sha512-RjhtfwJOxzcFmNOi6ltcbcu4Iu+FL3zEj83dk4kAS+fVpTxXLO1b38RvJgT/0QwvV/L3aY9TAnyv0EOqW4GoMQ==", "dev": true, - "dependencies": { - "compare-func": "^2.0.0", - "lodash": "^4.17.15", - "q": "^1.5.1" - }, "engines": { - "node": ">=10" + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" } }, - "node_modules/conventional-changelog-core": { - "version": "4.2.4", - "resolved": "https://registry.npmjs.org/conventional-changelog-core/-/conventional-changelog-core-4.2.4.tgz", - "integrity": "sha512-gDVS+zVJHE2v4SLc6B0sLsPiloR0ygU7HaDW14aNJE1v4SlqJPILPl/aJC7YdtRE4CybBf8gDwObBvKha8Xlyg==", + "node_modules/@commitlint/top-level/node_modules/yocto-queue": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-1.0.0.tgz", + "integrity": "sha512-9bnSc/HEW2uRy67wc+T8UwauLuPJVn28jb+GtJY16iiKWyvmYJRXVT4UamsAEGQfPohgr2q4Tq0sQbQlxTfi1g==", "dev": true, - "dependencies": { - "add-stream": "^1.0.0", - "conventional-changelog-writer": "^5.0.0", - "conventional-commits-parser": "^3.2.0", - "dateformat": "^3.0.0", - "get-pkg-repo": "^4.0.0", - "git-raw-commits": "^2.0.8", - "git-remote-origin-url": "^2.0.0", - "git-semver-tags": "^4.1.1", - "lodash": "^4.17.15", - "normalize-package-data": "^3.0.0", - "q": "^1.5.1", - "read-pkg": "^3.0.0", - "read-pkg-up": "^3.0.0", - "through2": "^4.0.0" - }, "engines": { - "node": ">=10" - } - }, - "node_modules/conventional-changelog-core/node_modules/find-up": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", - "integrity": "sha512-NWzkk0jSJtTt08+FBFMvXoeZnOJD+jTtsRmBYbAIzJdX6l7dLgR7CTubCM5/eDdPUBvLCeVasP1brfVR/9/EZQ==", - "dev": true, - "dependencies": { - "locate-path": "^2.0.0" + "node": ">=12.20" }, - "engines": { - "node": ">=4" + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/conventional-changelog-core/node_modules/hosted-git-info": { - "version": "2.8.9", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", - "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==", - "dev": true - }, - "node_modules/conventional-changelog-core/node_modules/locate-path": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", - "integrity": "sha512-NCI2kiDkyR7VeEKm27Kda/iQHyKJe1Bu0FlTbYp3CqJu+9IFe9bLyAjMxf5ZDDbEg+iMPzB5zYyUTSm8wVTKmA==", + "node_modules/@commitlint/types": { + "version": "19.0.0", + "resolved": "https://registry.npmjs.org/@commitlint/types/-/types-19.0.0.tgz", + "integrity": "sha512-qLjLUdYXKi0TIavONrjBkxrElp7KguqDbvzIRbqTdJBV/cAAr8QEhHe1qUq8OcCM3gFWTlUrDz3ISZbkRoGsAg==", "dev": true, "dependencies": { - "p-locate": "^2.0.0", - "path-exists": "^3.0.0" + "chalk": "^5.3.0" }, "engines": { - "node": ">=4" + "node": ">=v18" } }, - "node_modules/conventional-changelog-core/node_modules/p-limit": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", - "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", + "node_modules/@commitlint/types/node_modules/chalk": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.3.0.tgz", + "integrity": "sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==", "dev": true, - "dependencies": { - "p-try": "^1.0.0" - }, "engines": { - "node": ">=4" - } - }, - "node_modules/conventional-changelog-core/node_modules/p-locate": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", - "integrity": "sha512-nQja7m7gSKuewoVRen45CtVfODR3crN3goVQ0DDZ9N3yHxgpkuBhZqsaiotSQRrADUrne346peY7kT3TSACykg==", - "dev": true, - "dependencies": { - "p-limit": "^1.1.0" + "node": "^12.17.0 || ^14.13 || >=16.0.0" }, - "engines": { - "node": ">=4" + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/conventional-changelog-core/node_modules/path-exists": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", - "integrity": "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==", + "node_modules/@hutson/parse-repository-url": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@hutson/parse-repository-url/-/parse-repository-url-3.0.2.tgz", + "integrity": "sha512-H9XAx3hc0BQHY6l+IFSWHDySypcXsvsuLhgYLUGywmJ5pswRVQJUHpOsobnLYp2ZUaUlKiKDrgWWhosOwAEM8Q==", "dev": true, "engines": { - "node": ">=4" + "node": ">=6.9.0" } }, - "node_modules/conventional-changelog-core/node_modules/path-type": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz", - "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==", + "node_modules/@ljharb/through": { + "version": "2.3.12", + "resolved": "https://registry.npmjs.org/@ljharb/through/-/through-2.3.12.tgz", + "integrity": "sha512-ajo/heTlG3QgC8EGP6APIejksVAYt4ayz4tqoP3MolFELzcH1x1fzwEYRJTPO0IELutZ5HQ0c26/GqAYy79u3g==", "dev": true, + "peer": true, "dependencies": { - "pify": "^3.0.0" + "call-bind": "^1.0.5" }, "engines": { - "node": ">=4" - } - }, - "node_modules/conventional-changelog-core/node_modules/pify": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", - "integrity": "sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg==", - "dev": true, - "engines": { - "node": ">=4" + "node": ">= 0.4" } }, - "node_modules/conventional-changelog-core/node_modules/read-pkg": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz", - "integrity": "sha512-BLq/cCO9two+lBgiTYNqD6GdtK8s4NpaWrl6/rCO9w0TUS8oJl7cmToOZfRYllKTISY6nt1U7jQ53brmKqY6BA==", - "dev": true, - "dependencies": { - "load-json-file": "^4.0.0", - "normalize-package-data": "^2.3.2", - "path-type": "^3.0.0" - }, - "engines": { - "node": ">=4" - } + "node_modules/@types/minimist": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/@types/minimist/-/minimist-1.2.5.tgz", + "integrity": "sha512-hov8bUuiLiyFPGyFPE1lwWhmzYbirOXQNNo40+y3zow8aFVTeyn3VWL0VFFfdNddA8S4Vf0Tc062rzyNr7Paag==", + "dev": true }, - "node_modules/conventional-changelog-core/node_modules/read-pkg-up": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-3.0.0.tgz", - "integrity": "sha512-YFzFrVvpC6frF1sz8psoHDBGF7fLPc+llq/8NB43oagqWkx8ar5zYtsTORtOjw9W2RHLpWP+zTWwBvf1bCmcSw==", + "node_modules/@types/node": { + "version": "20.11.20", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.20.tgz", + "integrity": "sha512-7/rR21OS+fq8IyHTgtLkDK949uzsa6n8BkziAKtPVpugIkO6D+/ooXMvzXxDnZrmtXVfjb1bKQafYpb8s89LOg==", "dev": true, - "dependencies": { - "find-up": "^2.0.0", - "read-pkg": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/conventional-changelog-core/node_modules/read-pkg/node_modules/normalize-package-data": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", - "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", - "dev": true, - "dependencies": { - "hosted-git-info": "^2.1.4", - "resolve": "^1.10.0", - "semver": "2 || 3 || 4 || 5", - "validate-npm-package-license": "^3.0.1" - } - }, - "node_modules/conventional-changelog-core/node_modules/semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", - "dev": true, - "bin": { - "semver": "bin/semver" - } - }, - "node_modules/conventional-changelog-ember": { - "version": "2.0.9", - "resolved": "https://registry.npmjs.org/conventional-changelog-ember/-/conventional-changelog-ember-2.0.9.tgz", - "integrity": "sha512-ulzIReoZEvZCBDhcNYfDIsLTHzYHc7awh+eI44ZtV5cx6LVxLlVtEmcO+2/kGIHGtw+qVabJYjdI5cJOQgXh1A==", - "dev": true, - "dependencies": { - "q": "^1.5.1" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/conventional-changelog-eslint": { - "version": "3.0.9", - "resolved": "https://registry.npmjs.org/conventional-changelog-eslint/-/conventional-changelog-eslint-3.0.9.tgz", - "integrity": "sha512-6NpUCMgU8qmWmyAMSZO5NrRd7rTgErjrm4VASam2u5jrZS0n38V7Y9CzTtLT2qwz5xEChDR4BduoWIr8TfwvXA==", - "dev": true, - "dependencies": { - "q": "^1.5.1" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/conventional-changelog-express": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/conventional-changelog-express/-/conventional-changelog-express-2.0.6.tgz", - "integrity": "sha512-SDez2f3iVJw6V563O3pRtNwXtQaSmEfTCaTBPCqn0oG0mfkq0rX4hHBq5P7De2MncoRixrALj3u3oQsNK+Q0pQ==", - "dev": true, - "dependencies": { - "q": "^1.5.1" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/conventional-changelog-jquery": { - "version": "3.0.11", - "resolved": "https://registry.npmjs.org/conventional-changelog-jquery/-/conventional-changelog-jquery-3.0.11.tgz", - "integrity": "sha512-x8AWz5/Td55F7+o/9LQ6cQIPwrCjfJQ5Zmfqi8thwUEKHstEn4kTIofXub7plf1xvFA2TqhZlq7fy5OmV6BOMw==", - "dev": true, - "dependencies": { - "q": "^1.5.1" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/conventional-changelog-jshint": { - "version": "2.0.9", - "resolved": "https://registry.npmjs.org/conventional-changelog-jshint/-/conventional-changelog-jshint-2.0.9.tgz", - "integrity": "sha512-wMLdaIzq6TNnMHMy31hql02OEQ8nCQfExw1SE0hYL5KvU+JCTuPaDO+7JiogGT2gJAxiUGATdtYYfh+nT+6riA==", - "dev": true, - "dependencies": { - "compare-func": "^2.0.0", - "q": "^1.5.1" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/conventional-changelog-preset-loader": { - "version": "2.3.4", - "resolved": "https://registry.npmjs.org/conventional-changelog-preset-loader/-/conventional-changelog-preset-loader-2.3.4.tgz", - "integrity": "sha512-GEKRWkrSAZeTq5+YjUZOYxdHq+ci4dNwHvpaBC3+ENalzFWuCWa9EZXSuZBpkr72sMdKB+1fyDV4takK1Lf58g==", - "dev": true, - "engines": { - "node": ">=10" - } - }, - "node_modules/conventional-changelog-tf-a": { - "resolved": "tools/conventional-changelog-tf-a", - "link": true - }, - "node_modules/conventional-changelog-writer": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/conventional-changelog-writer/-/conventional-changelog-writer-5.0.1.tgz", - "integrity": "sha512-5WsuKUfxW7suLblAbFnxAcrvf6r+0b7GvNaWUwUIk0bXMnENP/PEieGKVUQrjPqwPT4o3EPAASBXiY6iHooLOQ==", - "dev": true, - "dependencies": { - "conventional-commits-filter": "^2.0.7", - "dateformat": "^3.0.0", - "handlebars": "^4.7.7", - "json-stringify-safe": "^5.0.1", - "lodash": "^4.17.15", - "meow": "^8.0.0", - "semver": "^6.0.0", - "split": "^1.0.0", - "through2": "^4.0.0" - }, - "bin": { - "conventional-changelog-writer": "cli.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/conventional-changelog-writer/node_modules/semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true, - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/conventional-commit-types": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/conventional-commit-types/-/conventional-commit-types-3.0.0.tgz", - "integrity": "sha512-SmmCYnOniSsAa9GqWOeLqc179lfr5TRu5b4QFDkbsrJ5TZjPJx85wtOr3zn+1dbeNiXDKGPbZ72IKbPhLXh/Lg==", - "dev": true - }, - "node_modules/conventional-commits-filter": { - "version": "2.0.7", - "resolved": "https://registry.npmjs.org/conventional-commits-filter/-/conventional-commits-filter-2.0.7.tgz", - "integrity": "sha512-ASS9SamOP4TbCClsRHxIHXRfcGCnIoQqkvAzCSbZzTFLfcTqJVugB0agRgsEELsqaeWgsXv513eS116wnlSSPA==", - "dev": true, - "dependencies": { - "lodash.ismatch": "^4.4.0", - "modify-values": "^1.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/conventional-commits-parser": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/conventional-commits-parser/-/conventional-commits-parser-3.2.4.tgz", - "integrity": "sha512-nK7sAtfi+QXbxHCYfhpZsfRtaitZLIA6889kFIouLvz6repszQDgxBu7wf2WbU+Dco7sAnNCJYERCwt54WPC2Q==", - "dev": true, - "dependencies": { - "is-text-path": "^1.0.1", - "JSONStream": "^1.0.4", - "lodash": "^4.17.15", - "meow": "^8.0.0", - "split2": "^3.0.0", - "through2": "^4.0.0" - }, - "bin": { - "conventional-commits-parser": "cli.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/conventional-recommended-bump": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/conventional-recommended-bump/-/conventional-recommended-bump-6.1.0.tgz", - "integrity": "sha512-uiApbSiNGM/kkdL9GTOLAqC4hbptObFo4wW2QRyHsKciGAfQuLU1ShZ1BIVI/+K2BE/W1AWYQMCXAsv4dyKPaw==", - "dev": true, - "dependencies": { - "concat-stream": "^2.0.0", - "conventional-changelog-preset-loader": "^2.3.4", - "conventional-commits-filter": "^2.0.7", - "conventional-commits-parser": "^3.2.0", - "git-raw-commits": "^2.0.8", - "git-semver-tags": "^4.1.1", - "meow": "^8.0.0", - "q": "^1.5.1" - }, - "bin": { - "conventional-recommended-bump": "cli.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/core-util-is": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", - "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", - "dev": true - }, - "node_modules/cosmiconfig": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.0.1.tgz", - "integrity": "sha512-a1YWNUV2HwGimB7dU2s1wUMurNKjpx60HxBB6xUM8Re+2s1g1IIfJvFR0/iCF+XHdE0GMTKTuLR32UQff4TEyQ==", - "dev": true, - "dependencies": { - "@types/parse-json": "^4.0.0", - "import-fresh": "^3.2.1", - "parse-json": "^5.0.0", - "path-type": "^4.0.0", - "yaml": "^1.10.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/cosmiconfig-typescript-loader": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/cosmiconfig-typescript-loader/-/cosmiconfig-typescript-loader-2.0.2.tgz", - "integrity": "sha512-KmE+bMjWMXJbkWCeY4FJX/npHuZPNr9XF9q9CIQ/bpFwi1qHfCmSiKarrCcRa0LO4fWjk93pVoeRtJAkTGcYNw==", - "dev": true, - "dependencies": { - "cosmiconfig": "^7", - "ts-node": "^10.8.1" - }, - "engines": { - "node": ">=12", - "npm": ">=6" - }, - "peerDependencies": { - "@types/node": "*", - "cosmiconfig": ">=7", - "typescript": ">=3" - } - }, - "node_modules/create-require": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", - "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", - "dev": true - }, - "node_modules/cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", - "dev": true, - "dependencies": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/cz-conventional-changelog": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/cz-conventional-changelog/-/cz-conventional-changelog-3.3.0.tgz", - "integrity": "sha512-U466fIzU5U22eES5lTNiNbZ+d8dfcHcssH4o7QsdWaCcRs/feIPCxKYSWkYBNs5mny7MvEfwpTLWjvbm94hecw==", - "dev": true, - "dependencies": { - "chalk": "^2.4.1", - "commitizen": "^4.0.3", - "conventional-commit-types": "^3.0.0", - "lodash.map": "^4.5.1", - "longest": "^2.0.1", - "word-wrap": "^1.0.3" - }, - "engines": { - "node": ">= 10" - }, - "optionalDependencies": { - "@commitlint/load": ">6.1.1" - } - }, - "node_modules/cz-conventional-changelog/node_modules/ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "dependencies": { - "color-convert": "^1.9.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/cz-conventional-changelog/node_modules/chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "dependencies": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/cz-conventional-changelog/node_modules/color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, - "dependencies": { - "color-name": "1.1.3" - } - }, - "node_modules/cz-conventional-changelog/node_modules/color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", - "dev": true - }, - "node_modules/cz-conventional-changelog/node_modules/has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/cz-conventional-changelog/node_modules/supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "dependencies": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/dargs": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/dargs/-/dargs-7.0.0.tgz", - "integrity": "sha512-2iy1EkLdlBzQGvbweYRFxmFath8+K7+AKB0TlhHWkNuH+TmovaMH/Wp7V7R4u7f4SnX3OgLsU9t1NI9ioDnUpg==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/dateformat": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/dateformat/-/dateformat-3.0.3.tgz", - "integrity": "sha512-jyCETtSl3VMZMWeRo7iY1FL19ges1t55hMo5yaam4Jrsm5EPL89UQkoQRyiI+Yf4k8r2ZpdngkV8hr1lIdjb3Q==", - "dev": true, - "engines": { - "node": "*" - } - }, - "node_modules/decamelize": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", - "integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/decamelize-keys": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/decamelize-keys/-/decamelize-keys-1.1.0.tgz", - "integrity": "sha512-ocLWuYzRPoS9bfiSdDd3cxvrzovVMZnRDVEzAs+hWIVXGDbHxWMECij2OBuyB/An0FFW/nLuq6Kv1i/YC5Qfzg==", - "dev": true, - "dependencies": { - "decamelize": "^1.1.0", - "map-obj": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/decamelize-keys/node_modules/map-obj": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz", - "integrity": "sha512-7N/q3lyZ+LVCp7PzuxrJr4KMbBE2hW7BT7YNia330OFxIf4d3r5zVpicP2650l7CPN6RM9zOJRl3NGpqSiw3Eg==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/dedent": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/dedent/-/dedent-0.7.0.tgz", - "integrity": "sha512-Q6fKUPqnAHAyhiUgFU7BUzLiv0kd8saH9al7tnu5Q/okj6dnupxyTgFIBjVzJATdfIAm9NAsvXNzjaKa+bxVyA==", - "dev": true - }, - "node_modules/defaults": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.4.tgz", - "integrity": "sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==", - "dev": true, - "dependencies": { - "clone": "^1.0.2" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/detect-file": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/detect-file/-/detect-file-1.0.0.tgz", - "integrity": "sha512-DtCOLG98P007x7wiiOmfI0fi3eIKyWiLTGJ2MDnVi/E04lWGbf+JzrRHMm0rgIIZJGtHpKpbVgLWHrv8xXpc3Q==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/detect-indent": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/detect-indent/-/detect-indent-6.1.0.tgz", - "integrity": "sha512-reYkTUJAZb9gUuZ2RvVCNhVHdg62RHnJ7WJl8ftMi4diZ6NWlciOzQN88pUhSELEwflJht4oQDv0F0BMlwaYtA==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/detect-newline": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", - "integrity": "sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/diff": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", - "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", - "dev": true, - "engines": { - "node": ">=0.3.1" - } - }, - "node_modules/dot-prop": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-5.3.0.tgz", - "integrity": "sha512-QM8q3zDe58hqUqjraQOmzZ1LIH9SWQJTlEKCH4kJ2oQvLZk7RbQXvtDM2XEq3fwkV9CCvvH4LA0AV+ogFsBM2Q==", - "dev": true, - "dependencies": { - "is-obj": "^2.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/dotgitignore": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/dotgitignore/-/dotgitignore-2.1.0.tgz", - "integrity": "sha512-sCm11ak2oY6DglEPpCB8TixLjWAxd3kJTs6UIcSasNYxXdFPV+YKlye92c8H4kKFqV5qYMIh7d+cYecEg0dIkA==", - "dev": true, - "dependencies": { - "find-up": "^3.0.0", - "minimatch": "^3.0.4" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/dotgitignore/node_modules/find-up": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", - "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", - "dev": true, - "dependencies": { - "locate-path": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/dotgitignore/node_modules/locate-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", - "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", - "dev": true, - "dependencies": { - "p-locate": "^3.0.0", - "path-exists": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/dotgitignore/node_modules/p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "dev": true, - "dependencies": { - "p-try": "^2.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/dotgitignore/node_modules/p-locate": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", - "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", - "dev": true, - "dependencies": { - "p-limit": "^2.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/dotgitignore/node_modules/p-try": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", - "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/dotgitignore/node_modules/path-exists": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", - "integrity": "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true - }, - "node_modules/error-ex": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", - "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", - "dev": true, - "dependencies": { - "is-arrayish": "^0.2.1" - } - }, - "node_modules/escalade": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", - "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", - "dev": true, - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/execa": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", - "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", - "dev": true, - "dependencies": { - "cross-spawn": "^7.0.3", - "get-stream": "^6.0.0", - "human-signals": "^2.1.0", - "is-stream": "^2.0.0", - "merge-stream": "^2.0.0", - "npm-run-path": "^4.0.1", - "onetime": "^5.1.2", - "signal-exit": "^3.0.3", - "strip-final-newline": "^2.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sindresorhus/execa?sponsor=1" - } - }, - "node_modules/expand-tilde": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/expand-tilde/-/expand-tilde-2.0.2.tgz", - "integrity": "sha512-A5EmesHW6rfnZ9ysHQjPdJRni0SRar0tjtG5MNtm9n5TUvsYU8oozprtRD4AqHxcZWWlVuAmQo2nWKfN9oyjTw==", - "dev": true, - "dependencies": { - "homedir-polyfill": "^1.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/external-editor": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz", - "integrity": "sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==", - "dev": true, - "dependencies": { - "chardet": "^0.7.0", - "iconv-lite": "^0.4.24", - "tmp": "^0.0.33" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/fast-deep-equal": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "dev": true - }, - "node_modules/fast-json-stable-stringify": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", - "dev": true - }, - "node_modules/figures": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", - "integrity": "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==", - "dev": true, - "dependencies": { - "escape-string-regexp": "^1.0.5" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", - "dev": true, - "dependencies": { - "to-regex-range": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/find-node-modules": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/find-node-modules/-/find-node-modules-2.1.3.tgz", - "integrity": "sha512-UC2I2+nx1ZuOBclWVNdcnbDR5dlrOdVb7xNjmT/lHE+LsgztWks3dG7boJ37yTS/venXw84B/mAW9uHVoC5QRg==", - "dev": true, - "dependencies": { - "findup-sync": "^4.0.0", - "merge": "^2.1.1" - } - }, - "node_modules/find-root": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/find-root/-/find-root-1.1.0.tgz", - "integrity": "sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng==", - "dev": true - }, - "node_modules/find-up": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", - "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", - "dev": true, - "dependencies": { - "locate-path": "^6.0.0", - "path-exists": "^4.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/findup-sync": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/findup-sync/-/findup-sync-4.0.0.tgz", - "integrity": "sha512-6jvvn/12IC4quLBL1KNokxC7wWTvYncaVUYSoxWw7YykPLuRrnv4qdHcSOywOI5RpkOVGeQRtWM8/q+G6W6qfQ==", - "dev": true, - "dependencies": { - "detect-file": "^1.0.0", - "is-glob": "^4.0.0", - "micromatch": "^4.0.2", - "resolve-dir": "^1.0.1" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/fs-extra": { - "version": "10.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", - "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", - "dev": true, - "dependencies": { - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", - "dev": true - }, - "node_modules/function-bind": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", - "dev": true - }, - "node_modules/get-caller-file": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", - "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", - "dev": true, - "engines": { - "node": "6.* || 8.* || >= 10.*" - } - }, - "node_modules/get-pkg-repo": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/get-pkg-repo/-/get-pkg-repo-4.2.1.tgz", - "integrity": "sha512-2+QbHjFRfGB74v/pYWjd5OhU3TDIC2Gv/YKUTk/tCvAz0pkn/Mz6P3uByuBimLOcPvN2jYdScl3xGFSrx0jEcA==", - "dev": true, - "dependencies": { - "@hutson/parse-repository-url": "^3.0.0", - "hosted-git-info": "^4.0.0", - "through2": "^2.0.0", - "yargs": "^16.2.0" - }, - "bin": { - "get-pkg-repo": "src/cli.js" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/get-pkg-repo/node_modules/cliui": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", - "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", - "dev": true, - "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.0", - "wrap-ansi": "^7.0.0" - } - }, - "node_modules/get-pkg-repo/node_modules/readable-stream": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", - "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", - "dev": true, - "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "node_modules/get-pkg-repo/node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "dev": true - }, - "node_modules/get-pkg-repo/node_modules/string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "dev": true, - "dependencies": { - "safe-buffer": "~5.1.0" - } - }, - "node_modules/get-pkg-repo/node_modules/through2": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", - "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", - "dev": true, - "dependencies": { - "readable-stream": "~2.3.6", - "xtend": "~4.0.1" - } - }, - "node_modules/get-pkg-repo/node_modules/yargs": { - "version": "16.2.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", - "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", - "dev": true, - "dependencies": { - "cliui": "^7.0.2", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.0", - "y18n": "^5.0.5", - "yargs-parser": "^20.2.2" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/get-stream": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", - "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/git-raw-commits": { - "version": "2.0.11", - "resolved": "https://registry.npmjs.org/git-raw-commits/-/git-raw-commits-2.0.11.tgz", - "integrity": "sha512-VnctFhw+xfj8Va1xtfEqCUD2XDrbAPSJx+hSrE5K7fGdjZruW7XV+QOrN7LF/RJyvspRiD2I0asWsxFp0ya26A==", - "dev": true, - "dependencies": { - "dargs": "^7.0.0", - "lodash": "^4.17.15", - "meow": "^8.0.0", - "split2": "^3.0.0", - "through2": "^4.0.0" - }, - "bin": { - "git-raw-commits": "cli.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/git-remote-origin-url": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/git-remote-origin-url/-/git-remote-origin-url-2.0.0.tgz", - "integrity": "sha512-eU+GGrZgccNJcsDH5LkXR3PB9M958hxc7sbA8DFJjrv9j4L2P/eZfKhM+QD6wyzpiv+b1BpK0XrYCxkovtjSLw==", - "dev": true, - "dependencies": { - "gitconfiglocal": "^1.0.0", - "pify": "^2.3.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/git-semver-tags": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/git-semver-tags/-/git-semver-tags-4.1.1.tgz", - "integrity": "sha512-OWyMt5zBe7xFs8vglMmhM9lRQzCWL3WjHtxNNfJTMngGym7pC1kh8sP6jevfydJ6LP3ZvGxfb6ABYgPUM0mtsA==", - "dev": true, - "dependencies": { - "meow": "^8.0.0", - "semver": "^6.0.0" - }, - "bin": { - "git-semver-tags": "cli.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/git-semver-tags/node_modules/semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true, - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/gitconfiglocal": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/gitconfiglocal/-/gitconfiglocal-1.0.0.tgz", - "integrity": "sha512-spLUXeTAVHxDtKsJc8FkFVgFtMdEN9qPGpL23VfSHx4fP4+Ds097IXLvymbnDH8FnmxX5Nr9bPw3A+AQ6mWEaQ==", - "dev": true, - "dependencies": { - "ini": "^1.3.2" - } - }, - "node_modules/glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "dev": true, - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/global-dirs": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/global-dirs/-/global-dirs-0.1.1.tgz", - "integrity": "sha512-NknMLn7F2J7aflwFOlGdNIuCDpN3VGoSoB+aap3KABFWbHVn1TCgFC+np23J8W2BiZbjfEw3BFBycSMv1AFblg==", - "dev": true, - "dependencies": { - "ini": "^1.3.4" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/global-modules": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/global-modules/-/global-modules-1.0.0.tgz", - "integrity": "sha512-sKzpEkf11GpOFuw0Zzjzmt4B4UZwjOcG757PPvrfhxcLFbq0wpsgpOqxpxtxFiCG4DtG93M6XRVbF2oGdev7bg==", - "dev": true, - "dependencies": { - "global-prefix": "^1.0.1", - "is-windows": "^1.0.1", - "resolve-dir": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/global-prefix": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/global-prefix/-/global-prefix-1.0.2.tgz", - "integrity": "sha512-5lsx1NUDHtSjfg0eHlmYvZKv8/nVqX4ckFbM+FrGcQ+04KWcWFo9P5MxPZYSzUvyzmdTbI7Eix8Q4IbELDqzKg==", - "dev": true, - "dependencies": { - "expand-tilde": "^2.0.2", - "homedir-polyfill": "^1.0.1", - "ini": "^1.3.4", - "is-windows": "^1.0.1", - "which": "^1.2.14" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/global-prefix/node_modules/which": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", - "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", - "dev": true, - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "which": "bin/which" - } - }, - "node_modules/graceful-fs": { - "version": "4.2.10", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz", - "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==", - "dev": true - }, - "node_modules/handlebars": { - "version": "4.7.7", - "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.7.tgz", - "integrity": "sha512-aAcXm5OAfE/8IXkcZvCepKU3VzW1/39Fb5ZuqMtgI/hT8X2YgoMvBY5dLhq/cpOvw7Lk1nK/UF71aLG/ZnVYRA==", - "dev": true, - "dependencies": { - "minimist": "^1.2.5", - "neo-async": "^2.6.0", - "source-map": "^0.6.1", - "wordwrap": "^1.0.0" - }, - "bin": { - "handlebars": "bin/handlebars" - }, - "engines": { - "node": ">=0.4.7" - }, - "optionalDependencies": { - "uglify-js": "^3.1.4" - } - }, - "node_modules/hard-rejection": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/hard-rejection/-/hard-rejection-2.1.0.tgz", - "integrity": "sha512-VIZB+ibDhx7ObhAe7OVtoEbuP4h/MuOTHJ+J8h/eBXotJYl0fBgR72xDFCKgIh22OJZIOVNxBMWuhAr10r8HdA==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/has": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", - "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", - "dev": true, - "dependencies": { - "function-bind": "^1.1.1" - }, - "engines": { - "node": ">= 0.4.0" - } - }, - "node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/homedir-polyfill": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/homedir-polyfill/-/homedir-polyfill-1.0.3.tgz", - "integrity": "sha512-eSmmWE5bZTK2Nou4g0AI3zZ9rswp7GRKoKXS1BLUkvPviOqs4YTN1djQIqrXy9k5gEtdLPy86JjRwsNM9tnDcA==", - "dev": true, - "dependencies": { - "parse-passwd": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/hosted-git-info": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-4.1.0.tgz", - "integrity": "sha512-kyCuEOWjJqZuDbRHzL8V93NzQhwIB71oFWSyzVo+KPZI+pnQPPxucdkrOZvkLRnrf5URsQM+IJ09Dw29cRALIA==", - "dev": true, - "dependencies": { - "lru-cache": "^6.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/human-signals": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", - "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", - "dev": true, - "engines": { - "node": ">=10.17.0" - } - }, - "node_modules/husky": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/husky/-/husky-7.0.4.tgz", - "integrity": "sha512-vbaCKN2QLtP/vD4yvs6iz6hBEo6wkSzs8HpRah1Z6aGmF2KW5PdYuAd7uX5a+OyBZHBhd+TFLqgjUgytQr4RvQ==", - "dev": true, - "bin": { - "husky": "lib/bin.js" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/typicode" - } - }, - "node_modules/iconv-lite": { - "version": "0.4.24", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", - "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", - "dev": true, - "dependencies": { - "safer-buffer": ">= 2.1.2 < 3" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/ieee754": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", - "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, - "node_modules/import-fresh": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", - "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", - "dev": true, - "dependencies": { - "parent-module": "^1.0.0", - "resolve-from": "^4.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/import-fresh/node_modules/resolve-from": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", - "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/indent-string": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", - "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", - "dev": true, - "dependencies": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "node_modules/inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "dev": true - }, - "node_modules/ini": { - "version": "1.3.8", - "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", - "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", - "dev": true - }, - "node_modules/inquirer": { - "version": "8.2.4", - "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-8.2.4.tgz", - "integrity": "sha512-nn4F01dxU8VeKfq192IjLsxu0/OmMZ4Lg3xKAns148rCaXP6ntAoEkVYZThWjwON8AlzdZZi6oqnhNbxUG9hVg==", - "dev": true, - "dependencies": { - "ansi-escapes": "^4.2.1", - "chalk": "^4.1.1", - "cli-cursor": "^3.1.0", - "cli-width": "^3.0.0", - "external-editor": "^3.0.3", - "figures": "^3.0.0", - "lodash": "^4.17.21", - "mute-stream": "0.0.8", - "ora": "^5.4.1", - "run-async": "^2.4.0", - "rxjs": "^7.5.5", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0", - "through": "^2.3.6", - "wrap-ansi": "^7.0.0" - }, - "engines": { - "node": ">=12.0.0" - } - }, - "node_modules/is-arrayish": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", - "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", - "dev": true - }, - "node_modules/is-core-module": { - "version": "2.10.0", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.10.0.tgz", - "integrity": "sha512-Erxj2n/LDAZ7H8WNJXd9tw38GYM3dv8rk8Zcs+jJuxYTW7sozH+SS8NtrSjVL1/vpLvWi1hxy96IzjJ3EHTJJg==", - "dev": true, - "dependencies": { - "has": "^1.0.3" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/is-glob": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", - "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", - "dev": true, - "dependencies": { - "is-extglob": "^2.1.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-interactive": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-1.0.0.tgz", - "integrity": "sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true, - "engines": { - "node": ">=0.12.0" - } - }, - "node_modules/is-obj": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz", - "integrity": "sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/is-plain-obj": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz", - "integrity": "sha512-yvkRyxmFKEOQ4pNXCmJG5AEQNlXJS5LaONXo5/cLdTZdWvsZ1ioJEonLGAosKlMWE8lwUy/bJzMjcw8az73+Fg==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-stream": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", - "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", - "dev": true, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/is-text-path": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-text-path/-/is-text-path-1.0.1.tgz", - "integrity": "sha512-xFuJpne9oFz5qDaodwmmG08e3CawH/2ZV8Qqza1Ko7Sk8POWbkRdwIoAWVhqvq0XeUzANEhKo2n0IXUGBm7A/w==", - "dev": true, - "dependencies": { - "text-extensions": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-unicode-supported": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", - "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/is-utf8": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz", - "integrity": "sha512-rMYPYvCzsXywIsldgLaSoPlw5PfoB/ssr7hY4pLfcodrA5M/eArza1a9VmTiNIBNMjOGr1Ow9mTyU2o69U6U9Q==", - "dev": true - }, - "node_modules/is-windows": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", - "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", - "dev": true - }, - "node_modules/isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "dev": true - }, - "node_modules/js-tokens": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "dev": true - }, - "node_modules/js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", - "dev": true, - "dependencies": { - "argparse": "^2.0.1" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, - "node_modules/json-parse-better-errors": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", - "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==", - "dev": true - }, - "node_modules/json-parse-even-better-errors": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", - "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", - "dev": true - }, - "node_modules/json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true - }, - "node_modules/json-stringify-safe": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", - "integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==", - "dev": true - }, - "node_modules/jsonfile": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", - "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", - "dev": true, - "dependencies": { - "universalify": "^2.0.0" - }, - "optionalDependencies": { - "graceful-fs": "^4.1.6" - } - }, - "node_modules/jsonparse": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/jsonparse/-/jsonparse-1.3.1.tgz", - "integrity": "sha512-POQXvpdL69+CluYsillJ7SUhKvytYjW9vG/GKpnf+xP8UWgYEM/RaMzHHofbALDiKbbP1W8UEYmgGl39WkPZsg==", - "dev": true, - "engines": [ - "node >= 0.2.0" - ] - }, - "node_modules/JSONStream": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/JSONStream/-/JSONStream-1.3.5.tgz", - "integrity": "sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ==", - "dev": true, - "dependencies": { - "jsonparse": "^1.2.0", - "through": ">=2.2.7 <3" - }, - "bin": { - "JSONStream": "bin.js" - }, - "engines": { - "node": "*" - } - }, - "node_modules/kind-of": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", - "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/lines-and-columns": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", - "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", - "dev": true - }, - "node_modules/load-json-file": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz", - "integrity": "sha512-Kx8hMakjX03tiGTLAIdJ+lL0htKnXjEZN6hk/tozf/WOuYGdZBJrZ+rCJRbVCugsjB3jMLn9746NsQIf5VjBMw==", - "dev": true, - "dependencies": { - "graceful-fs": "^4.1.2", - "parse-json": "^4.0.0", - "pify": "^3.0.0", - "strip-bom": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/load-json-file/node_modules/parse-json": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", - "integrity": "sha512-aOIos8bujGN93/8Ox/jPLh7RwVnPEysynVFE+fQZyg6jKELEHwzgKdLRFHUgXJL6kylijVSBC4BvN9OmsB48Rw==", - "dev": true, - "dependencies": { - "error-ex": "^1.3.1", - "json-parse-better-errors": "^1.0.1" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/load-json-file/node_modules/pify": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", - "integrity": "sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/load-json-file/node_modules/strip-bom": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", - "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/locate-path": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", - "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", - "dev": true, - "dependencies": { - "p-locate": "^5.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/lodash": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", - "dev": true - }, - "node_modules/lodash.ismatch": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/lodash.ismatch/-/lodash.ismatch-4.4.0.tgz", - "integrity": "sha512-fPMfXjGQEV9Xsq/8MTSgUf255gawYRbjwMyDbcvDhXgV7enSZA0hynz6vMPnpAb5iONEzBHBPsT+0zes5Z301g==", - "dev": true - }, - "node_modules/lodash.map": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/lodash.map/-/lodash.map-4.6.0.tgz", - "integrity": "sha512-worNHGKLDetmcEYDvh2stPCrrQRkP20E4l0iIS7F8EvzMqBBi7ltvFN5m1HvTf1P7Jk1txKhvFcmYsCr8O2F1Q==", - "dev": true - }, - "node_modules/log-symbols": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", - "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", - "dev": true, - "dependencies": { - "chalk": "^4.1.0", - "is-unicode-supported": "^0.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/longest": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/longest/-/longest-2.0.1.tgz", - "integrity": "sha512-Ajzxb8CM6WAnFjgiloPsI3bF+WCxcvhdIG3KNA2KN962+tdBsHcuQ4k4qX/EcS/2CRkcc0iAkR956Nib6aXU/Q==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dev": true, - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/make-error": { - "version": "1.3.6", - "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", - "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", - "dev": true - }, - "node_modules/map-obj": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-4.3.0.tgz", - "integrity": "sha512-hdN1wVrZbb29eBGiGjJbeP8JbKjq1urkHJ/LIP/NY48MZ1QVXUsQBV1G1zvYFHn1XE06cwjBsOI2K3Ulnj1YXQ==", - "dev": true, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/meow": { - "version": "8.1.2", - "resolved": "https://registry.npmjs.org/meow/-/meow-8.1.2.tgz", - "integrity": "sha512-r85E3NdZ+mpYk1C6RjPFEMSE+s1iZMuHtsHAqY0DT3jZczl0diWUZ8g6oU7h0M9cD2EL+PzaYghhCLzR0ZNn5Q==", - "dev": true, - "dependencies": { - "@types/minimist": "^1.2.0", - "camelcase-keys": "^6.2.2", - "decamelize-keys": "^1.1.0", - "hard-rejection": "^2.1.0", - "minimist-options": "4.1.0", - "normalize-package-data": "^3.0.0", - "read-pkg-up": "^7.0.1", - "redent": "^3.0.0", - "trim-newlines": "^3.0.0", - "type-fest": "^0.18.0", - "yargs-parser": "^20.2.3" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/meow/node_modules/type-fest": { - "version": "0.18.1", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.18.1.tgz", - "integrity": "sha512-OIAYXk8+ISY+qTOwkHtKqzAuxchoMiD9Udx+FSGQDuiRR+PJKJHc2NJAXlbhkGwTt/4/nKZxELY1w3ReWOL8mw==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/merge": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/merge/-/merge-2.1.1.tgz", - "integrity": "sha512-jz+Cfrg9GWOZbQAnDQ4hlVnQky+341Yk5ru8bZSe6sIDTCIg8n9i/u7hSQGSVOF3C7lH6mGtqjkiT9G4wFLL0w==", - "dev": true - }, - "node_modules/merge-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", - "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", - "dev": true - }, - "node_modules/micromatch": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", - "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", - "dev": true, - "dependencies": { - "braces": "^3.0.2", - "picomatch": "^2.3.1" - }, - "engines": { - "node": ">=8.6" - } - }, - "node_modules/mimic-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", - "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/min-indent": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz", - "integrity": "sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/minimist": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz", - "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==", - "dev": true - }, - "node_modules/minimist-options": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/minimist-options/-/minimist-options-4.1.0.tgz", - "integrity": "sha512-Q4r8ghd80yhO/0j1O3B2BjweX3fiHg9cdOwjJd2J76Q135c+NDxGCqdYKQ1SKBuFfgWbAUzBfvYjPUEeNgqN1A==", - "dev": true, - "dependencies": { - "arrify": "^1.0.1", - "is-plain-obj": "^1.1.0", - "kind-of": "^6.0.3" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/modify-values": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/modify-values/-/modify-values-1.0.1.tgz", - "integrity": "sha512-xV2bxeN6F7oYjZWTe/YPAy6MN2M+sL4u/Rlm2AHCIVGfo2p1yGmBHQ6vHehl4bRTZBdHu3TSkWdYgkwpYzAGSw==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/mute-stream": { - "version": "0.0.8", - "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz", - "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==", - "dev": true - }, - "node_modules/neo-async": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", - "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", - "dev": true - }, - "node_modules/normalize-package-data": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-3.0.3.tgz", - "integrity": "sha512-p2W1sgqij3zMMyRC067Dg16bfzVH+w7hyegmpIvZ4JNjqtGOVAIvLmjBx3yP7YTe9vKJgkoNOPjwQGogDoMXFA==", - "dev": true, - "dependencies": { - "hosted-git-info": "^4.0.1", - "is-core-module": "^2.5.0", - "semver": "^7.3.4", - "validate-npm-package-license": "^3.0.1" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/npm-run-path": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", - "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", - "dev": true, - "dependencies": { - "path-key": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", - "dev": true, - "dependencies": { - "wrappy": "1" - } - }, - "node_modules/onetime": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", - "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", - "dev": true, - "dependencies": { - "mimic-fn": "^2.1.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/ora": { - "version": "5.4.1", - "resolved": "https://registry.npmjs.org/ora/-/ora-5.4.1.tgz", - "integrity": "sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==", - "dev": true, - "dependencies": { - "bl": "^4.1.0", - "chalk": "^4.1.0", - "cli-cursor": "^3.1.0", - "cli-spinners": "^2.5.0", - "is-interactive": "^1.0.0", - "is-unicode-supported": "^0.1.0", - "log-symbols": "^4.1.0", - "strip-ansi": "^6.0.0", - "wcwidth": "^1.0.1" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/os-tmpdir": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", - "integrity": "sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/p-limit": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", - "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", - "dev": true, - "dependencies": { - "yocto-queue": "^0.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/p-locate": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", - "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", - "dev": true, - "dependencies": { - "p-limit": "^3.0.2" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/p-try": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", - "integrity": "sha512-U1etNYuMJoIz3ZXSrrySFjsXQTWOx2/jdi86L+2pRvph/qMKL6sbcCYdH23fqsbm8TH2Gn0OybpT4eSFlCVHww==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/parent-module": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", - "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", - "dev": true, - "dependencies": { - "callsites": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/parse-json": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", - "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", - "dev": true, - "dependencies": { - "@babel/code-frame": "^7.0.0", - "error-ex": "^1.3.1", - "json-parse-even-better-errors": "^2.3.0", - "lines-and-columns": "^1.1.6" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/parse-passwd": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/parse-passwd/-/parse-passwd-1.0.0.tgz", - "integrity": "sha512-1Y1A//QUXEZK7YKz+rD9WydcE1+EuPr6ZBgKecAB8tmoW6UFv0NREVJe1p+jRxtThkcbbKkfwIbWJe/IeE6m2Q==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/path-exists": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/path-parse": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", - "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", - "dev": true - }, - "node_modules/path-type": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", - "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "dev": true, - "engines": { - "node": ">=8.6" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/pify": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", - "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/process-nextick-args": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", - "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", - "dev": true - }, - "node_modules/punycode": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", - "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/q": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/q/-/q-1.5.1.tgz", - "integrity": "sha512-kV/CThkXo6xyFEZUugw/+pIOywXcDbFYgSct5cT3gqlbkBE1SJdwy6UQoZvodiWF/ckQLZyDE/Bu1M6gVu5lVw==", - "dev": true, - "engines": { - "node": ">=0.6.0", - "teleport": ">=0.2.0" - } - }, - "node_modules/quick-lru": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-4.0.1.tgz", - "integrity": "sha512-ARhCpm70fzdcvNQfPoy49IaanKkTlRWF2JMzqhcJbhSFRZv7nPTvZJdcY7301IPmvW+/p0RgIWnQDLJxifsQ7g==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/read-pkg": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz", - "integrity": "sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==", - "dev": true, - "dependencies": { - "@types/normalize-package-data": "^2.4.0", - "normalize-package-data": "^2.5.0", - "parse-json": "^5.0.0", - "type-fest": "^0.6.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/read-pkg-up": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-7.0.1.tgz", - "integrity": "sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg==", - "dev": true, - "dependencies": { - "find-up": "^4.1.0", - "read-pkg": "^5.2.0", - "type-fest": "^0.8.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/read-pkg-up/node_modules/find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", - "dev": true, - "dependencies": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/read-pkg-up/node_modules/locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", - "dev": true, - "dependencies": { - "p-locate": "^4.1.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/read-pkg-up/node_modules/p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "dev": true, - "dependencies": { - "p-try": "^2.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/read-pkg-up/node_modules/p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", - "dev": true, - "dependencies": { - "p-limit": "^2.2.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/read-pkg-up/node_modules/p-try": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", - "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/read-pkg-up/node_modules/type-fest": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", - "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/read-pkg/node_modules/hosted-git-info": { - "version": "2.8.9", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", - "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==", - "dev": true - }, - "node_modules/read-pkg/node_modules/normalize-package-data": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", - "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", - "dev": true, - "dependencies": { - "hosted-git-info": "^2.1.4", - "resolve": "^1.10.0", - "semver": "2 || 3 || 4 || 5", - "validate-npm-package-license": "^3.0.1" - } - }, - "node_modules/read-pkg/node_modules/semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", - "dev": true, - "bin": { - "semver": "bin/semver" - } - }, - "node_modules/read-pkg/node_modules/type-fest": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.6.0.tgz", - "integrity": "sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/readable-stream": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", - "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", - "dev": true, - "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/redent": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/redent/-/redent-3.0.0.tgz", - "integrity": "sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==", - "dev": true, - "dependencies": { - "indent-string": "^4.0.0", - "strip-indent": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/require-directory": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/resolve": { - "version": "1.22.1", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz", - "integrity": "sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==", - "dev": true, - "dependencies": { - "is-core-module": "^2.9.0", - "path-parse": "^1.0.7", - "supports-preserve-symlinks-flag": "^1.0.0" - }, - "bin": { - "resolve": "bin/resolve" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/resolve-dir": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/resolve-dir/-/resolve-dir-1.0.1.tgz", - "integrity": "sha512-R7uiTjECzvOsWSfdM0QKFNBVFcK27aHOUwdvK53BcW8zqnGdYp0Fbj82cy54+2A4P2tFM22J5kRfe1R+lM/1yg==", - "dev": true, - "dependencies": { - "expand-tilde": "^2.0.0", - "global-modules": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/resolve-from": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", - "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/resolve-global": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/resolve-global/-/resolve-global-1.0.0.tgz", - "integrity": "sha512-zFa12V4OLtT5XUX/Q4VLvTfBf+Ok0SPc1FNGM/z9ctUdiU618qwKpWnd0CHs3+RqROfyEg/DhuHbMWYqcgljEw==", - "dev": true, - "dependencies": { - "global-dirs": "^0.1.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/restore-cursor": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", - "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", - "dev": true, - "dependencies": { - "onetime": "^5.1.0", - "signal-exit": "^3.0.2" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/run-async": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz", - "integrity": "sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ==", - "dev": true, - "engines": { - "node": ">=0.12.0" - } - }, - "node_modules/rxjs": { - "version": "7.5.7", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.5.7.tgz", - "integrity": "sha512-z9MzKh/UcOqB3i20H6rtrlaE/CgjLOvheWK/9ILrbhROGTweAi1BaFsTT9FbwZi5Trr1qNRs+MXkhmR06awzQA==", - "dev": true, - "dependencies": { - "tslib": "^2.1.0" - } - }, - "node_modules/safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, - "node_modules/safer-buffer": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", - "dev": true - }, - "node_modules/semver": { - "version": "7.3.7", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz", - "integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==", - "dev": true, - "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/shebang-command": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "dev": true, - "dependencies": { - "shebang-regex": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/signal-exit": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", - "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", - "dev": true - }, - "node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/spdx-correct": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.1.tgz", - "integrity": "sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w==", - "dev": true, - "dependencies": { - "spdx-expression-parse": "^3.0.0", - "spdx-license-ids": "^3.0.0" - } - }, - "node_modules/spdx-exceptions": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz", - "integrity": "sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==", - "dev": true - }, - "node_modules/spdx-expression-parse": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", - "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", - "dev": true, - "dependencies": { - "spdx-exceptions": "^2.1.0", - "spdx-license-ids": "^3.0.0" - } - }, - "node_modules/spdx-license-ids": { - "version": "3.0.12", - "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.12.tgz", - "integrity": "sha512-rr+VVSXtRhO4OHbXUiAF7xW3Bo9DuuF6C5jH+q/x15j2jniycgKbxU09Hr0WqlSLUs4i4ltHGXqTe7VHclYWyA==", - "dev": true - }, - "node_modules/split": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/split/-/split-1.0.1.tgz", - "integrity": "sha512-mTyOoPbrivtXnwnIxZRFYRrPNtEFKlpB2fvjSnCQUiAA6qAZzqwna5envK4uk6OIeP17CsdF3rSBGYVBsU0Tkg==", - "dev": true, - "dependencies": { - "through": "2" - }, - "engines": { - "node": "*" - } - }, - "node_modules/split2": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/split2/-/split2-3.2.2.tgz", - "integrity": "sha512-9NThjpgZnifTkJpzTZ7Eue85S49QwpNhZTq6GRJwObb6jnLFNGB7Qm73V5HewTROPyxD0C29xqmaI68bQtV+hg==", - "dev": true, - "dependencies": { - "readable-stream": "^3.0.0" - } - }, - "node_modules/standard-version": { - "version": "9.5.0", - "resolved": "https://registry.npmjs.org/standard-version/-/standard-version-9.5.0.tgz", - "integrity": "sha512-3zWJ/mmZQsOaO+fOlsa0+QK90pwhNd042qEcw6hKFNoLFs7peGyvPffpEBbK/DSGPbyOvli0mUIFv5A4qTjh2Q==", - "dev": true, - "dependencies": { - "chalk": "^2.4.2", - "conventional-changelog": "3.1.25", - "conventional-changelog-config-spec": "2.1.0", - "conventional-changelog-conventionalcommits": "4.6.3", - "conventional-recommended-bump": "6.1.0", - "detect-indent": "^6.0.0", - "detect-newline": "^3.1.0", - "dotgitignore": "^2.1.0", - "figures": "^3.1.0", - "find-up": "^5.0.0", - "git-semver-tags": "^4.0.0", - "semver": "^7.1.1", - "stringify-package": "^1.0.1", - "yargs": "^16.0.0" - }, - "bin": { - "standard-version": "bin/cli.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/standard-version/node_modules/ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "dependencies": { - "color-convert": "^1.9.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/standard-version/node_modules/chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "dependencies": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/standard-version/node_modules/cliui": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", - "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", - "dev": true, - "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.0", - "wrap-ansi": "^7.0.0" - } - }, - "node_modules/standard-version/node_modules/color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, - "dependencies": { - "color-name": "1.1.3" - } - }, - "node_modules/standard-version/node_modules/color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", - "dev": true - }, - "node_modules/standard-version/node_modules/has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/standard-version/node_modules/supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "dependencies": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/standard-version/node_modules/yargs": { - "version": "16.2.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", - "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", - "dev": true, - "dependencies": { - "cliui": "^7.0.2", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.0", - "y18n": "^5.0.5", - "yargs-parser": "^20.2.2" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/string_decoder": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", - "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", - "dev": true, - "dependencies": { - "safe-buffer": "~5.2.0" - } - }, - "node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/stringify-package": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/stringify-package/-/stringify-package-1.0.1.tgz", - "integrity": "sha512-sa4DUQsYciMP1xhKWGuFM04fB0LG/9DlluZoSVywUMRNvzid6XucHK0/90xGxRoHrAaROrcHK1aPKaijCtSrhg==", - "dev": true - }, - "node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-bom": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", - "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-final-newline": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", - "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/strip-indent": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-3.0.0.tgz", - "integrity": "sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==", - "dev": true, - "dependencies": { - "min-indent": "^1.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-json-comments": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", - "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", - "dev": true, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/supports-preserve-symlinks-flag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", - "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", - "dev": true, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/text-extensions": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/text-extensions/-/text-extensions-1.9.0.tgz", - "integrity": "sha512-wiBrwC1EhBelW12Zy26JeOUkQ5mRu+5o8rpsJk5+2t+Y5vE7e842qtZDQ2g1NpX/29HdyFeJ4nSIhI47ENSxlQ==", - "dev": true, - "engines": { - "node": ">=0.10" - } - }, - "node_modules/through": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", - "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==", - "dev": true - }, - "node_modules/through2": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/through2/-/through2-4.0.2.tgz", - "integrity": "sha512-iOqSav00cVxEEICeD7TjLB1sueEL+81Wpzp2bY17uZjZN0pWZPuo4suZ/61VujxmqSGFfgOcNuTZ85QJwNZQpw==", - "dev": true, - "dependencies": { - "readable-stream": "3" - } - }, - "node_modules/tmp": { - "version": "0.0.33", - "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", - "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", - "dev": true, - "dependencies": { - "os-tmpdir": "~1.0.2" - }, - "engines": { - "node": ">=0.6.0" - } - }, - "node_modules/to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dev": true, - "dependencies": { - "is-number": "^7.0.0" - }, - "engines": { - "node": ">=8.0" - } - }, - "node_modules/trim-newlines": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-3.0.1.tgz", - "integrity": "sha512-c1PTsA3tYrIsLGkJkzHF+w9F2EyxfXGo4UyJc4pFL++FMjnq0HJS69T3M7d//gKrFKwy429bouPescbjecU+Zw==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/ts-node": { - "version": "10.9.1", - "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.1.tgz", - "integrity": "sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==", - "dev": true, - "dependencies": { - "@cspotcode/source-map-support": "^0.8.0", - "@tsconfig/node10": "^1.0.7", - "@tsconfig/node12": "^1.0.7", - "@tsconfig/node14": "^1.0.0", - "@tsconfig/node16": "^1.0.2", - "acorn": "^8.4.1", - "acorn-walk": "^8.1.1", - "arg": "^4.1.0", - "create-require": "^1.1.0", - "diff": "^4.0.1", - "make-error": "^1.1.1", - "v8-compile-cache-lib": "^3.0.1", - "yn": "3.1.1" - }, - "bin": { - "ts-node": "dist/bin.js", - "ts-node-cwd": "dist/bin-cwd.js", - "ts-node-esm": "dist/bin-esm.js", - "ts-node-script": "dist/bin-script.js", - "ts-node-transpile-only": "dist/bin-transpile.js", - "ts-script": "dist/bin-script-deprecated.js" - }, - "peerDependencies": { - "@swc/core": ">=1.2.50", - "@swc/wasm": ">=1.2.50", - "@types/node": "*", - "typescript": ">=2.7" - }, - "peerDependenciesMeta": { - "@swc/core": { - "optional": true - }, - "@swc/wasm": { - "optional": true - } - } - }, - "node_modules/tslib": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", - "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==", - "dev": true - }, - "node_modules/type-fest": { - "version": "0.21.3", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", - "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/typedarray": { - "version": "0.0.6", - "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", - "integrity": "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==", - "dev": true - }, - "node_modules/typescript": { - "version": "4.8.4", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.8.4.tgz", - "integrity": "sha512-QCh+85mCy+h0IGff8r5XWzOVSbBO+KfeYrMQh7NJ58QujwcE22u+NUSmUxqF+un70P9GXKxa2HCNiTTMJknyjQ==", - "dev": true, - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" - }, - "engines": { - "node": ">=4.2.0" - } - }, - "node_modules/uglify-js": { - "version": "3.17.3", - "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.17.3.tgz", - "integrity": "sha512-JmMFDME3iufZnBpyKL+uS78LRiC+mK55zWfM5f/pWBJfpOttXAqYfdDGRukYhJuyRinvPVAtUhvy7rlDybNtFg==", - "dev": true, - "optional": true, - "bin": { - "uglifyjs": "bin/uglifyjs" - }, - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/universalify": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", - "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==", - "dev": true, - "engines": { - "node": ">= 10.0.0" - } - }, - "node_modules/uri-js": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", - "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", - "dev": true, - "dependencies": { - "punycode": "^2.1.0" - } - }, - "node_modules/util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", - "dev": true - }, - "node_modules/v8-compile-cache-lib": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", - "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", - "dev": true - }, - "node_modules/validate-npm-package-license": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", - "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", - "dev": true, - "dependencies": { - "spdx-correct": "^3.0.0", - "spdx-expression-parse": "^3.0.0" - } - }, - "node_modules/wcwidth": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz", - "integrity": "sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==", - "dev": true, - "dependencies": { - "defaults": "^1.0.3" - } - }, - "node_modules/which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "node-which": "bin/node-which" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/word-wrap": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.4.tgz", - "integrity": "sha512-2V81OA4ugVo5pRo46hAoD2ivUJx8jXmWXfUkY4KFNw0hEptvN0QfH3K4nHiwzGeKl5rFKedV48QVoqYavy4YpA==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/wordwrap": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", - "integrity": "sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==", - "dev": true - }, - "node_modules/wrap-ansi": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", - "dev": true - }, - "node_modules/xtend": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", - "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", - "dev": true, - "engines": { - "node": ">=0.4" - } - }, - "node_modules/y18n": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", - "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", - "dev": true, - "engines": { - "node": ">=10" - } - }, - "node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true - }, - "node_modules/yaml": { - "version": "1.10.2", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", - "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==", - "dev": true, - "engines": { - "node": ">= 6" - } - }, - "node_modules/yargs": { - "version": "17.6.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.6.0.tgz", - "integrity": "sha512-8H/wTDqlSwoSnScvV2N/JHfLWOKuh5MVla9hqLjK3nsfyy6Y4kDSYSvkU5YCUEPOSnRXfIyx3Sq+B/IWudTo4g==", - "dev": true, - "dependencies": { - "cliui": "^8.0.1", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.3", - "y18n": "^5.0.5", - "yargs-parser": "^21.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/yargs-parser": { - "version": "20.2.9", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", - "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", - "dev": true, - "engines": { - "node": ">=10" - } - }, - "node_modules/yargs/node_modules/yargs-parser": { - "version": "21.1.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", - "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", - "dev": true, - "engines": { - "node": ">=12" - } - }, - "node_modules/yn": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", - "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/yocto-queue": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", - "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "tools/conventional-changelog-tf-a": { - "version": "2.9.0", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "conventional-changelog-conventionalcommits": "^4.6.1", - "execa": "^5.1.1", - "lodash": "^4.17.21", - "q": "^1.5.1" - } - } - }, - "dependencies": { - "@babel/code-frame": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.18.6.tgz", - "integrity": "sha512-TDCmlK5eOvH+eH7cdAFlNXeVJqWIQ7gW9tY1GJIpUtFb6CmjVyq2VM3u71bOyR8CRihcCgMUYoDNyLXao3+70Q==", - "dev": true, - "requires": { - "@babel/highlight": "^7.18.6" - } - }, - "@babel/helper-validator-identifier": { - "version": "7.19.1", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz", - "integrity": "sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==", - "dev": true - }, - "@babel/highlight": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.18.6.tgz", - "integrity": "sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g==", - "dev": true, - "requires": { - "@babel/helper-validator-identifier": "^7.18.6", - "chalk": "^2.0.0", - "js-tokens": "^4.0.0" - }, - "dependencies": { - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "requires": { - "color-convert": "^1.9.0" - } - }, - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - } - }, - "color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, - "requires": { - "color-name": "1.1.3" - } - }, - "color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", - "dev": true - }, - "has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", - "dev": true - }, - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } - } - }, - "@commitlint/cli": { - "version": "16.3.0", - "resolved": "https://registry.npmjs.org/@commitlint/cli/-/cli-16.3.0.tgz", - "integrity": "sha512-P+kvONlfsuTMnxSwWE1H+ZcPMY3STFaHb2kAacsqoIkNx66O0T7sTpBxpxkMrFPyhkJiLJnJWMhk4bbvYD3BMA==", - "dev": true, - "requires": { - "@commitlint/format": "^16.2.1", - "@commitlint/lint": "^16.2.4", - "@commitlint/load": "^16.3.0", - "@commitlint/read": "^16.2.1", - "@commitlint/types": "^16.2.1", - "lodash": "^4.17.19", - "resolve-from": "5.0.0", - "resolve-global": "1.0.0", - "yargs": "^17.0.0" - } - }, - "@commitlint/config-conventional": { - "version": "16.2.4", - "resolved": "https://registry.npmjs.org/@commitlint/config-conventional/-/config-conventional-16.2.4.tgz", - "integrity": "sha512-av2UQJa3CuE5P0dzxj/o/B9XVALqYzEViHrMXtDrW9iuflrqCStWBAioijppj9URyz6ONpohJKAtSdgAOE0gkA==", - "dev": true, - "requires": { - "conventional-changelog-conventionalcommits": "^4.3.1" - } - }, - "@commitlint/config-validator": { - "version": "16.2.1", - "resolved": "https://registry.npmjs.org/@commitlint/config-validator/-/config-validator-16.2.1.tgz", - "integrity": "sha512-hogSe0WGg7CKmp4IfNbdNES3Rq3UEI4XRPB8JL4EPgo/ORq5nrGTVzxJh78omibNuB8Ho4501Czb1Er1MoDWpw==", - "dev": true, - "requires": { - "@commitlint/types": "^16.2.1", - "ajv": "^6.12.6" - } - }, - "@commitlint/cz-commitlint": { - "version": "16.3.0", - "resolved": "https://registry.npmjs.org/@commitlint/cz-commitlint/-/cz-commitlint-16.3.0.tgz", - "integrity": "sha512-Q+QLQmSIHEgzI18F3/7mqq3vwL0IN9k+Tjp9Um4adFnRXMtUTnEa0er0CXAXxWvoA/x/6nt3t7faAv2HugDIGg==", - "dev": true, - "requires": { - "@commitlint/ensure": "^16.2.1", - "@commitlint/load": "^16.3.0", - "@commitlint/types": "^16.2.1", - "chalk": "^4.1.0", - "lodash": "^4.17.21", - "word-wrap": "^1.2.3" - } - }, - "@commitlint/ensure": { - "version": "16.2.1", - "resolved": "https://registry.npmjs.org/@commitlint/ensure/-/ensure-16.2.1.tgz", - "integrity": "sha512-/h+lBTgf1r5fhbDNHOViLuej38i3rZqTQnBTk+xEg+ehOwQDXUuissQ5GsYXXqI5uGy+261ew++sT4EA3uBJ+A==", - "dev": true, - "requires": { - "@commitlint/types": "^16.2.1", - "lodash": "^4.17.19" - } - }, - "@commitlint/execute-rule": { - "version": "16.2.1", - "resolved": "https://registry.npmjs.org/@commitlint/execute-rule/-/execute-rule-16.2.1.tgz", - "integrity": "sha512-oSls82fmUTLM6cl5V3epdVo4gHhbmBFvCvQGHBRdQ50H/690Uq1Dyd7hXMuKITCIdcnr9umyDkr8r5C6HZDF3g==", - "dev": true - }, - "@commitlint/format": { - "version": "16.2.1", - "resolved": "https://registry.npmjs.org/@commitlint/format/-/format-16.2.1.tgz", - "integrity": "sha512-Yyio9bdHWmNDRlEJrxHKglamIk3d6hC0NkEUW6Ti6ipEh2g0BAhy8Od6t4vLhdZRa1I2n+gY13foy+tUgk0i1Q==", - "dev": true, - "requires": { - "@commitlint/types": "^16.2.1", - "chalk": "^4.0.0" - } - }, - "@commitlint/is-ignored": { - "version": "16.2.4", - "resolved": "https://registry.npmjs.org/@commitlint/is-ignored/-/is-ignored-16.2.4.tgz", - "integrity": "sha512-Lxdq9aOAYCOOOjKi58ulbwK/oBiiKz+7Sq0+/SpFIEFwhHkIVugvDvWjh2VRBXmRC/x5lNcjDcYEwS/uYUvlYQ==", - "dev": true, - "requires": { - "@commitlint/types": "^16.2.1", - "semver": "7.3.7" - } - }, - "@commitlint/lint": { - "version": "16.2.4", - "resolved": "https://registry.npmjs.org/@commitlint/lint/-/lint-16.2.4.tgz", - "integrity": "sha512-AUDuwOxb2eGqsXbTMON3imUGkc1jRdtXrbbohiLSCSk3jFVXgJLTMaEcr39pR00N8nE9uZ+V2sYaiILByZVmxQ==", - "dev": true, - "requires": { - "@commitlint/is-ignored": "^16.2.4", - "@commitlint/parse": "^16.2.1", - "@commitlint/rules": "^16.2.4", - "@commitlint/types": "^16.2.1" - } - }, - "@commitlint/load": { - "version": "16.3.0", - "resolved": "https://registry.npmjs.org/@commitlint/load/-/load-16.3.0.tgz", - "integrity": "sha512-3tykjV/iwbkv2FU9DG+NZ/JqmP0Nm3b7aDwgCNQhhKV5P74JAuByULkafnhn+zsFGypG1qMtI5u+BZoa9APm0A==", - "dev": true, - "requires": { - "@commitlint/config-validator": "^16.2.1", - "@commitlint/execute-rule": "^16.2.1", - "@commitlint/resolve-extends": "^16.2.1", - "@commitlint/types": "^16.2.1", - "@types/node": ">=12", - "chalk": "^4.0.0", - "cosmiconfig": "^7.0.0", - "cosmiconfig-typescript-loader": "^2.0.0", - "lodash": "^4.17.19", - "resolve-from": "^5.0.0", - "typescript": "^4.4.3" - } - }, - "@commitlint/message": { - "version": "16.2.1", - "resolved": "https://registry.npmjs.org/@commitlint/message/-/message-16.2.1.tgz", - "integrity": "sha512-2eWX/47rftViYg7a3axYDdrgwKv32mxbycBJT6OQY/MJM7SUfYNYYvbMFOQFaA4xIVZt7t2Alyqslbl6blVwWw==", - "dev": true - }, - "@commitlint/parse": { - "version": "16.2.1", - "resolved": "https://registry.npmjs.org/@commitlint/parse/-/parse-16.2.1.tgz", - "integrity": "sha512-2NP2dDQNL378VZYioLrgGVZhWdnJO4nAxQl5LXwYb08nEcN+cgxHN1dJV8OLJ5uxlGJtDeR8UZZ1mnQ1gSAD/g==", - "dev": true, - "requires": { - "@commitlint/types": "^16.2.1", - "conventional-changelog-angular": "^5.0.11", - "conventional-commits-parser": "^3.2.2" - } - }, - "@commitlint/read": { - "version": "16.2.1", - "resolved": "https://registry.npmjs.org/@commitlint/read/-/read-16.2.1.tgz", - "integrity": "sha512-tViXGuaxLTrw2r7PiYMQOFA2fueZxnnt0lkOWqKyxT+n2XdEMGYcI9ID5ndJKXnfPGPppD0w/IItKsIXlZ+alw==", - "dev": true, - "requires": { - "@commitlint/top-level": "^16.2.1", - "@commitlint/types": "^16.2.1", - "fs-extra": "^10.0.0", - "git-raw-commits": "^2.0.0" - } - }, - "@commitlint/resolve-extends": { - "version": "16.2.1", - "resolved": "https://registry.npmjs.org/@commitlint/resolve-extends/-/resolve-extends-16.2.1.tgz", - "integrity": "sha512-NbbCMPKTFf2J805kwfP9EO+vV+XvnaHRcBy6ud5dF35dxMsvdJqke54W3XazXF1ZAxC4a3LBy4i/GNVBAthsEg==", - "dev": true, - "requires": { - "@commitlint/config-validator": "^16.2.1", - "@commitlint/types": "^16.2.1", - "import-fresh": "^3.0.0", - "lodash": "^4.17.19", - "resolve-from": "^5.0.0", - "resolve-global": "^1.0.0" - } - }, - "@commitlint/rules": { - "version": "16.2.4", - "resolved": "https://registry.npmjs.org/@commitlint/rules/-/rules-16.2.4.tgz", - "integrity": "sha512-rK5rNBIN2ZQNQK+I6trRPK3dWa0MtaTN4xnwOma1qxa4d5wQMQJtScwTZjTJeallFxhOgbNOgr48AMHkdounVg==", - "dev": true, - "requires": { - "@commitlint/ensure": "^16.2.1", - "@commitlint/message": "^16.2.1", - "@commitlint/to-lines": "^16.2.1", - "@commitlint/types": "^16.2.1", - "execa": "^5.0.0" - } - }, - "@commitlint/to-lines": { - "version": "16.2.1", - "resolved": "https://registry.npmjs.org/@commitlint/to-lines/-/to-lines-16.2.1.tgz", - "integrity": "sha512-9/VjpYj5j1QeY3eiog1zQWY6axsdWAc0AonUUfyZ7B0MVcRI0R56YsHAfzF6uK/g/WwPZaoe4Lb1QCyDVnpVaQ==", - "dev": true - }, - "@commitlint/top-level": { - "version": "16.2.1", - "resolved": "https://registry.npmjs.org/@commitlint/top-level/-/top-level-16.2.1.tgz", - "integrity": "sha512-lS6GSieHW9y6ePL73ied71Z9bOKyK+Ib9hTkRsB8oZFAyQZcyRwq2w6nIa6Fngir1QW51oKzzaXfJL94qwImyw==", - "dev": true, - "requires": { - "find-up": "^5.0.0" - } - }, - "@commitlint/types": { - "version": "16.2.1", - "resolved": "https://registry.npmjs.org/@commitlint/types/-/types-16.2.1.tgz", - "integrity": "sha512-7/z7pA7BM0i8XvMSBynO7xsB3mVQPUZbVn6zMIlp/a091XJ3qAXRXc+HwLYhiIdzzS5fuxxNIHZMGHVD4HJxdA==", - "dev": true, - "requires": { - "chalk": "^4.0.0" - } - }, - "@cspotcode/source-map-support": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", - "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", - "dev": true, - "requires": { - "@jridgewell/trace-mapping": "0.3.9" - } - }, - "@hutson/parse-repository-url": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/@hutson/parse-repository-url/-/parse-repository-url-3.0.2.tgz", - "integrity": "sha512-H9XAx3hc0BQHY6l+IFSWHDySypcXsvsuLhgYLUGywmJ5pswRVQJUHpOsobnLYp2ZUaUlKiKDrgWWhosOwAEM8Q==", - "dev": true - }, - "@jridgewell/resolve-uri": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz", - "integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==", - "dev": true - }, - "@jridgewell/sourcemap-codec": { - "version": "1.4.14", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz", - "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==", - "dev": true - }, - "@jridgewell/trace-mapping": { - "version": "0.3.9", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", - "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", - "dev": true, - "requires": { - "@jridgewell/resolve-uri": "^3.0.3", - "@jridgewell/sourcemap-codec": "^1.4.10" - } - }, - "@tsconfig/node10": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz", - "integrity": "sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==", - "dev": true - }, - "@tsconfig/node12": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", - "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", - "dev": true - }, - "@tsconfig/node14": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", - "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", - "dev": true - }, - "@tsconfig/node16": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.3.tgz", - "integrity": "sha512-yOlFc+7UtL/89t2ZhjPvvB/DeAr3r+Dq58IgzsFkOAvVC6NMJXmCGjbptdXdR9qsX7pKcTL+s87FtYREi2dEEQ==", - "dev": true - }, - "@types/minimist": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/@types/minimist/-/minimist-1.2.2.tgz", - "integrity": "sha512-jhuKLIRrhvCPLqwPcx6INqmKeiA5EWrsCOPhrlFSrbrmU4ZMPjj5Ul/oLCMDO98XRUIwVm78xICz4EPCektzeQ==", - "dev": true - }, - "@types/node": { - "version": "18.8.3", - "resolved": "https://registry.npmjs.org/@types/node/-/node-18.8.3.tgz", - "integrity": "sha512-0os9vz6BpGwxGe9LOhgP/ncvYN5Tx1fNcd2TM3rD/aCGBkysb+ZWpXEocG24h6ZzOi13+VB8HndAQFezsSOw1w==", - "dev": true - }, - "@types/normalize-package-data": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.1.tgz", - "integrity": "sha512-Gj7cI7z+98M282Tqmp2K5EIsoouUEzbBJhQQzDE3jSIRk6r9gsz0oUokqIUR4u1R3dMHo0pDHM7sNOHyhulypw==", - "dev": true - }, - "@types/parse-json": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.0.tgz", - "integrity": "sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==", - "dev": true - }, - "acorn": { - "version": "8.8.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.0.tgz", - "integrity": "sha512-QOxyigPVrpZ2GXT+PFyZTl6TtOFc5egxHIP9IlQ+RbupQuX4RkT/Bee4/kQuC02Xkzg84JcT7oLYtDIQxp+v7w==", - "dev": true + "peer": true, + "dependencies": { + "undici-types": "~5.26.4" + } }, - "acorn-walk": { - "version": "8.2.0", - "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz", - "integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==", + "node_modules/@types/normalize-package-data": { + "version": "2.4.4", + "resolved": "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.4.tgz", + "integrity": "sha512-37i+OaWTh9qeK4LSHPsyRC7NahnGotNuZvjLSgcPzblpHB3rrCJxAOgI5gCdKm7coonsaX1Of0ILiTcnZjbfxA==", "dev": true }, - "add-stream": { + "node_modules/add-stream": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/add-stream/-/add-stream-1.0.0.tgz", "integrity": "sha512-qQLMr+8o0WC4FZGQTcJiKBVC59JylcPSrTtk6usvmIDFUOCKegapy1VHQwRbFMOFyb/inzUVqHs+eMYKDM1YeQ==", "dev": true }, - "ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "node_modules/ajv": { + "version": "8.12.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", + "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", "dev": true, - "requires": { + "dependencies": { "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" } }, - "ansi-escapes": { + "node_modules/ansi-escapes": { "version": "4.3.2", "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", "dev": true, - "requires": { + "dependencies": { "type-fest": "^0.21.3" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "ansi-regex": { + "node_modules/ansi-regex": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true + "dev": true, + "engines": { + "node": ">=8" + } }, - "ansi-styles": { + "node_modules/ansi-styles": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "dev": true, - "requires": { + "dependencies": { "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "arg": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", - "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", - "dev": true - }, - "argparse": { + "node_modules/argparse": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", "dev": true }, - "array-ify": { + "node_modules/array-ify": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/array-ify/-/array-ify-1.0.0.tgz", "integrity": "sha512-c5AMf34bKdvPhQ7tBGhqkgKNUzMr4WUs+WDtC2ZUGOUncbxKMTvqxYctiseW3+L4bA8ec+GcZ6/A/FW4m8ukng==", "dev": true }, - "arrify": { + "node_modules/arrify": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz", "integrity": "sha512-3CYzex9M9FGQjCGMGyi6/31c8GJbgb0qGyrx5HWxPd0aCwh4cB2YjMb2Xf9UuoogrMrlO9cTqnB5rI5GHZTcUA==", - "dev": true + "dev": true, + "engines": { + "node": ">=0.10.0" + } }, - "at-least-node": { + "node_modules/at-least-node": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz", "integrity": "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==", - "dev": true + "dev": true, + "engines": { + "node": ">= 4.0.0" + } }, - "balanced-match": { + "node_modules/balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", "dev": true }, - "base64-js": { + "node_modules/base64-js": { "version": "1.5.1", "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", - "dev": true + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] }, - "bl": { + "node_modules/bl": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", "dev": true, - "requires": { + "dependencies": { "buffer": "^5.5.0", "inherits": "^2.0.4", "readable-stream": "^3.4.0" } }, - "brace-expansion": { + "node_modules/brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", "dev": true, - "requires": { + "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, - "braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", "dev": true, - "requires": { - "fill-range": "^7.0.1" + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" } }, - "buffer": { + "node_modules/buffer": { "version": "5.7.1", "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", "dev": true, - "requires": { + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { "base64-js": "^1.3.1", "ieee754": "^1.1.13" } }, - "buffer-from": { + "node_modules/buffer-from": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", "dev": true }, - "cachedir": { + "node_modules/cachedir": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/cachedir/-/cachedir-2.3.0.tgz", "integrity": "sha512-A+Fezp4zxnit6FanDmv9EqXNAi3vt9DWp51/71UEhXukb7QUuvtv9344h91dyAxuTLoSYJFU299qzR3tzwPAhw==", - "dev": true + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/call-bind": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz", + "integrity": "sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==", + "dev": true, + "peer": true, + "dependencies": { + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", + "set-function-length": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } }, - "callsites": { + "node_modules/callsites": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", - "dev": true + "dev": true, + "engines": { + "node": ">=6" + } }, - "camelcase": { + "node_modules/camelcase": { "version": "5.3.1", "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", - "dev": true + "dev": true, + "engines": { + "node": ">=6" + } }, - "camelcase-keys": { + "node_modules/camelcase-keys": { "version": "6.2.2", "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-6.2.2.tgz", "integrity": "sha512-YrwaA0vEKazPBkn0ipTiMpSajYDSe+KjQfrjhcBMxJt/znbvlHd8Pw/Vamaz5EB4Wfhs3SUR3Z9mwRu/P3s3Yg==", "dev": true, - "requires": { + "dependencies": { "camelcase": "^5.3.1", "map-obj": "^4.0.0", "quick-lru": "^4.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "chalk": { + "node_modules/chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", "dev": true, - "requires": { + "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "chardet": { + "node_modules/chardet": { "version": "0.7.0", "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==", "dev": true }, - "cli-cursor": { + "node_modules/cli-cursor": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", "dev": true, - "requires": { + "dependencies": { "restore-cursor": "^3.1.0" + }, + "engines": { + "node": ">=8" } }, - "cli-spinners": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.7.0.tgz", - "integrity": "sha512-qu3pN8Y3qHNgE2AFweciB1IfMnmZ/fsNTEE+NOFjmGB2F/7rLhnhzppvpCnN4FovtP26k8lHyy9ptEbNwWFLzw==", - "dev": true + "node_modules/cli-spinners": { + "version": "2.9.2", + "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.9.2.tgz", + "integrity": "sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==", + "dev": true, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } }, - "cli-width": { + "node_modules/cli-width": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-3.0.0.tgz", "integrity": "sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw==", - "dev": true + "dev": true, + "engines": { + "node": ">= 10" + } }, - "cliui": { + "node_modules/cliui": { "version": "8.0.1", "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", "dev": true, - "requires": { + "dependencies": { "string-width": "^4.2.0", "strip-ansi": "^6.0.1", "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/cliui/node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, - "clone": { + "node_modules/clone": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", "integrity": "sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==", - "dev": true + "dev": true, + "engines": { + "node": ">=0.8" + } }, - "color-convert": { + "node_modules/color-convert": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", "dev": true, - "requires": { + "dependencies": { "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" } }, - "color-name": { + "node_modules/color-name": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true }, - "commitizen": { - "version": "4.2.5", - "resolved": "https://registry.npmjs.org/commitizen/-/commitizen-4.2.5.tgz", - "integrity": "sha512-9sXju8Qrz1B4Tw7kC5KhnvwYQN88qs2zbiB8oyMsnXZyJ24PPGiNM3nHr73d32dnE3i8VJEXddBFIbOgYSEXtQ==", + "node_modules/commitizen": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/commitizen/-/commitizen-4.3.0.tgz", + "integrity": "sha512-H0iNtClNEhT0fotHvGV3E9tDejDeS04sN1veIebsKYGMuGscFaswRoYJKmT3eW85eIJAs0F28bG2+a/9wCOfPw==", "dev": true, - "requires": { + "dependencies": { "cachedir": "2.3.0", "cz-conventional-changelog": "3.3.0", "dedent": "0.7.0", @@ -4648,62 +1404,117 @@ "find-root": "1.1.0", "fs-extra": "9.1.0", "glob": "7.2.3", - "inquirer": "8.2.4", + "inquirer": "8.2.5", "is-utf8": "^0.2.1", "lodash": "4.17.21", - "minimist": "1.2.6", + "minimist": "1.2.7", "strip-bom": "4.0.0", "strip-json-comments": "3.1.1" }, + "bin": { + "commitizen": "bin/commitizen", + "cz": "bin/git-cz", + "git-cz": "bin/git-cz" + }, + "engines": { + "node": ">= 12" + } + }, + "node_modules/commitizen/node_modules/fs-extra": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", + "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", + "dev": true, + "dependencies": { + "at-least-node": "^1.0.0", + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/commitizen/node_modules/inquirer": { + "version": "8.2.5", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-8.2.5.tgz", + "integrity": "sha512-QAgPDQMEgrDssk1XiwwHoOGYF9BAbUcc1+j+FhEvaOt8/cKRqyLn0U5qA6F74fGhTMGxf92pOvPBeh29jQJDTQ==", + "dev": true, "dependencies": { - "fs-extra": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", - "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", - "dev": true, - "requires": { - "at-least-node": "^1.0.0", - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" - } - } + "ansi-escapes": "^4.2.1", + "chalk": "^4.1.1", + "cli-cursor": "^3.1.0", + "cli-width": "^3.0.0", + "external-editor": "^3.0.3", + "figures": "^3.0.0", + "lodash": "^4.17.21", + "mute-stream": "0.0.8", + "ora": "^5.4.1", + "run-async": "^2.4.0", + "rxjs": "^7.5.5", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0", + "through": "^2.3.6", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/commitizen/node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, - "compare-func": { + "node_modules/compare-func": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/compare-func/-/compare-func-2.0.0.tgz", "integrity": "sha512-zHig5N+tPWARooBnb0Zx1MFcdfpyJrfTJ3Y5L+IFvUm8rM74hHz66z0gw0x4tijh5CorKkKUCnW82R2vmpeCRA==", "dev": true, - "requires": { + "dependencies": { "array-ify": "^1.0.0", "dot-prop": "^5.1.0" } }, - "concat-map": { + "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", "dev": true }, - "concat-stream": { + "node_modules/concat-stream": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-2.0.0.tgz", "integrity": "sha512-MWufYdFw53ccGjCA+Ol7XJYpAlW6/prSMzuPOTRnJGcGzuhLn4Scrz7qf6o8bROZ514ltazcIFJZevcfbo0x7A==", "dev": true, - "requires": { + "engines": [ + "node >= 6.0" + ], + "dependencies": { "buffer-from": "^1.0.0", "inherits": "^2.0.3", "readable-stream": "^3.0.2", "typedarray": "^0.0.6" } }, - "conventional-changelog": { + "node_modules/conventional-changelog": { "version": "3.1.25", "resolved": "https://registry.npmjs.org/conventional-changelog/-/conventional-changelog-3.1.25.tgz", "integrity": "sha512-ryhi3fd1mKf3fSjbLXOfK2D06YwKNic1nC9mWqybBHdObPd8KJ2vjaXZfYj1U23t+V8T8n0d7gwnc9XbIdFbyQ==", "dev": true, - "requires": { + "dependencies": { "conventional-changelog-angular": "^5.0.12", "conventional-changelog-atom": "^2.0.8", "conventional-changelog-codemirror": "^2.0.8", @@ -4715,59 +1526,74 @@ "conventional-changelog-jquery": "^3.0.11", "conventional-changelog-jshint": "^2.0.9", "conventional-changelog-preset-loader": "^2.3.4" + }, + "engines": { + "node": ">=10" } }, - "conventional-changelog-angular": { + "node_modules/conventional-changelog-angular": { "version": "5.0.13", "resolved": "https://registry.npmjs.org/conventional-changelog-angular/-/conventional-changelog-angular-5.0.13.tgz", "integrity": "sha512-i/gipMxs7s8L/QeuavPF2hLnJgH6pEZAttySB6aiQLWcX3puWDL3ACVmvBhJGxnAy52Qc15ua26BufY6KpmrVA==", "dev": true, - "requires": { + "dependencies": { "compare-func": "^2.0.0", "q": "^1.5.1" + }, + "engines": { + "node": ">=10" } }, - "conventional-changelog-atom": { + "node_modules/conventional-changelog-atom": { "version": "2.0.8", "resolved": "https://registry.npmjs.org/conventional-changelog-atom/-/conventional-changelog-atom-2.0.8.tgz", "integrity": "sha512-xo6v46icsFTK3bb7dY/8m2qvc8sZemRgdqLb/bjpBsH2UyOS8rKNTgcb5025Hri6IpANPApbXMg15QLb1LJpBw==", "dev": true, - "requires": { + "dependencies": { "q": "^1.5.1" + }, + "engines": { + "node": ">=10" } }, - "conventional-changelog-codemirror": { + "node_modules/conventional-changelog-codemirror": { "version": "2.0.8", "resolved": "https://registry.npmjs.org/conventional-changelog-codemirror/-/conventional-changelog-codemirror-2.0.8.tgz", "integrity": "sha512-z5DAsn3uj1Vfp7po3gpt2Boc+Bdwmw2++ZHa5Ak9k0UKsYAO5mH1UBTN0qSCuJZREIhX6WU4E1p3IW2oRCNzQw==", "dev": true, - "requires": { + "dependencies": { "q": "^1.5.1" + }, + "engines": { + "node": ">=10" } }, - "conventional-changelog-config-spec": { + "node_modules/conventional-changelog-config-spec": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/conventional-changelog-config-spec/-/conventional-changelog-config-spec-2.1.0.tgz", "integrity": "sha512-IpVePh16EbbB02V+UA+HQnnPIohgXvJRxHcS5+Uwk4AT5LjzCZJm5sp/yqs5C6KZJ1jMsV4paEV13BN1pvDuxQ==", "dev": true }, - "conventional-changelog-conventionalcommits": { + "node_modules/conventional-changelog-conventionalcommits": { "version": "4.6.3", "resolved": "https://registry.npmjs.org/conventional-changelog-conventionalcommits/-/conventional-changelog-conventionalcommits-4.6.3.tgz", "integrity": "sha512-LTTQV4fwOM4oLPad317V/QNQ1FY4Hju5qeBIM1uTHbrnCE+Eg4CdRZ3gO2pUeR+tzWdp80M2j3qFFEDWVqOV4g==", "dev": true, - "requires": { + "dependencies": { "compare-func": "^2.0.0", "lodash": "^4.17.15", "q": "^1.5.1" + }, + "engines": { + "node": ">=10" } }, - "conventional-changelog-core": { + "node_modules/conventional-changelog-core": { "version": "4.2.4", "resolved": "https://registry.npmjs.org/conventional-changelog-core/-/conventional-changelog-core-4.2.4.tgz", "integrity": "sha512-gDVS+zVJHE2v4SLc6B0sLsPiloR0ygU7HaDW14aNJE1v4SlqJPILPl/aJC7YdtRE4CybBf8gDwObBvKha8Xlyg==", "dev": true, - "requires": { + "dependencies": { "add-stream": "^1.0.0", "conventional-changelog-writer": "^5.0.0", "conventional-commits-parser": "^3.2.0", @@ -4783,181 +1609,223 @@ "read-pkg-up": "^3.0.0", "through2": "^4.0.0" }, + "engines": { + "node": ">=10" + } + }, + "node_modules/conventional-changelog-core/node_modules/find-up": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", + "integrity": "sha512-NWzkk0jSJtTt08+FBFMvXoeZnOJD+jTtsRmBYbAIzJdX6l7dLgR7CTubCM5/eDdPUBvLCeVasP1brfVR/9/EZQ==", + "dev": true, "dependencies": { - "find-up": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", - "integrity": "sha512-NWzkk0jSJtTt08+FBFMvXoeZnOJD+jTtsRmBYbAIzJdX6l7dLgR7CTubCM5/eDdPUBvLCeVasP1brfVR/9/EZQ==", - "dev": true, - "requires": { - "locate-path": "^2.0.0" - } - }, - "hosted-git-info": { - "version": "2.8.9", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", - "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==", - "dev": true - }, - "locate-path": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", - "integrity": "sha512-NCI2kiDkyR7VeEKm27Kda/iQHyKJe1Bu0FlTbYp3CqJu+9IFe9bLyAjMxf5ZDDbEg+iMPzB5zYyUTSm8wVTKmA==", - "dev": true, - "requires": { - "p-locate": "^2.0.0", - "path-exists": "^3.0.0" - } - }, - "p-limit": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", - "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", - "dev": true, - "requires": { - "p-try": "^1.0.0" - } - }, - "p-locate": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", - "integrity": "sha512-nQja7m7gSKuewoVRen45CtVfODR3crN3goVQ0DDZ9N3yHxgpkuBhZqsaiotSQRrADUrne346peY7kT3TSACykg==", - "dev": true, - "requires": { - "p-limit": "^1.1.0" - } - }, - "path-exists": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", - "integrity": "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==", - "dev": true - }, - "path-type": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz", - "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==", - "dev": true, - "requires": { - "pify": "^3.0.0" - } - }, - "pify": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", - "integrity": "sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg==", - "dev": true - }, - "read-pkg": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz", - "integrity": "sha512-BLq/cCO9two+lBgiTYNqD6GdtK8s4NpaWrl6/rCO9w0TUS8oJl7cmToOZfRYllKTISY6nt1U7jQ53brmKqY6BA==", - "dev": true, - "requires": { - "load-json-file": "^4.0.0", - "normalize-package-data": "^2.3.2", - "path-type": "^3.0.0" - }, - "dependencies": { - "normalize-package-data": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", - "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", - "dev": true, - "requires": { - "hosted-git-info": "^2.1.4", - "resolve": "^1.10.0", - "semver": "2 || 3 || 4 || 5", - "validate-npm-package-license": "^3.0.1" - } - } - } - }, - "read-pkg-up": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-3.0.0.tgz", - "integrity": "sha512-YFzFrVvpC6frF1sz8psoHDBGF7fLPc+llq/8NB43oagqWkx8ar5zYtsTORtOjw9W2RHLpWP+zTWwBvf1bCmcSw==", - "dev": true, - "requires": { - "find-up": "^2.0.0", - "read-pkg": "^3.0.0" - } - }, - "semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", - "dev": true - } + "locate-path": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/conventional-changelog-core/node_modules/hosted-git-info": { + "version": "2.8.9", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", + "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==", + "dev": true + }, + "node_modules/conventional-changelog-core/node_modules/locate-path": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", + "integrity": "sha512-NCI2kiDkyR7VeEKm27Kda/iQHyKJe1Bu0FlTbYp3CqJu+9IFe9bLyAjMxf5ZDDbEg+iMPzB5zYyUTSm8wVTKmA==", + "dev": true, + "dependencies": { + "p-locate": "^2.0.0", + "path-exists": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/conventional-changelog-core/node_modules/p-limit": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", + "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", + "dev": true, + "dependencies": { + "p-try": "^1.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/conventional-changelog-core/node_modules/p-locate": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", + "integrity": "sha512-nQja7m7gSKuewoVRen45CtVfODR3crN3goVQ0DDZ9N3yHxgpkuBhZqsaiotSQRrADUrne346peY7kT3TSACykg==", + "dev": true, + "dependencies": { + "p-limit": "^1.1.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/conventional-changelog-core/node_modules/path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/conventional-changelog-core/node_modules/path-type": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz", + "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==", + "dev": true, + "dependencies": { + "pify": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/conventional-changelog-core/node_modules/pify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "integrity": "sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/conventional-changelog-core/node_modules/read-pkg": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz", + "integrity": "sha512-BLq/cCO9two+lBgiTYNqD6GdtK8s4NpaWrl6/rCO9w0TUS8oJl7cmToOZfRYllKTISY6nt1U7jQ53brmKqY6BA==", + "dev": true, + "dependencies": { + "load-json-file": "^4.0.0", + "normalize-package-data": "^2.3.2", + "path-type": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/conventional-changelog-core/node_modules/read-pkg-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-3.0.0.tgz", + "integrity": "sha512-YFzFrVvpC6frF1sz8psoHDBGF7fLPc+llq/8NB43oagqWkx8ar5zYtsTORtOjw9W2RHLpWP+zTWwBvf1bCmcSw==", + "dev": true, + "dependencies": { + "find-up": "^2.0.0", + "read-pkg": "^3.0.0" + }, + "engines": { + "node": ">=4" } }, - "conventional-changelog-ember": { + "node_modules/conventional-changelog-core/node_modules/read-pkg/node_modules/normalize-package-data": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", + "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", + "dev": true, + "dependencies": { + "hosted-git-info": "^2.1.4", + "resolve": "^1.10.0", + "semver": "2 || 3 || 4 || 5", + "validate-npm-package-license": "^3.0.1" + } + }, + "node_modules/conventional-changelog-core/node_modules/semver": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", + "dev": true, + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/conventional-changelog-ember": { "version": "2.0.9", "resolved": "https://registry.npmjs.org/conventional-changelog-ember/-/conventional-changelog-ember-2.0.9.tgz", "integrity": "sha512-ulzIReoZEvZCBDhcNYfDIsLTHzYHc7awh+eI44ZtV5cx6LVxLlVtEmcO+2/kGIHGtw+qVabJYjdI5cJOQgXh1A==", "dev": true, - "requires": { + "dependencies": { "q": "^1.5.1" + }, + "engines": { + "node": ">=10" } }, - "conventional-changelog-eslint": { + "node_modules/conventional-changelog-eslint": { "version": "3.0.9", "resolved": "https://registry.npmjs.org/conventional-changelog-eslint/-/conventional-changelog-eslint-3.0.9.tgz", "integrity": "sha512-6NpUCMgU8qmWmyAMSZO5NrRd7rTgErjrm4VASam2u5jrZS0n38V7Y9CzTtLT2qwz5xEChDR4BduoWIr8TfwvXA==", "dev": true, - "requires": { + "dependencies": { "q": "^1.5.1" + }, + "engines": { + "node": ">=10" } }, - "conventional-changelog-express": { + "node_modules/conventional-changelog-express": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/conventional-changelog-express/-/conventional-changelog-express-2.0.6.tgz", "integrity": "sha512-SDez2f3iVJw6V563O3pRtNwXtQaSmEfTCaTBPCqn0oG0mfkq0rX4hHBq5P7De2MncoRixrALj3u3oQsNK+Q0pQ==", "dev": true, - "requires": { + "dependencies": { "q": "^1.5.1" + }, + "engines": { + "node": ">=10" } }, - "conventional-changelog-jquery": { + "node_modules/conventional-changelog-jquery": { "version": "3.0.11", "resolved": "https://registry.npmjs.org/conventional-changelog-jquery/-/conventional-changelog-jquery-3.0.11.tgz", "integrity": "sha512-x8AWz5/Td55F7+o/9LQ6cQIPwrCjfJQ5Zmfqi8thwUEKHstEn4kTIofXub7plf1xvFA2TqhZlq7fy5OmV6BOMw==", "dev": true, - "requires": { + "dependencies": { "q": "^1.5.1" + }, + "engines": { + "node": ">=10" } }, - "conventional-changelog-jshint": { + "node_modules/conventional-changelog-jshint": { "version": "2.0.9", "resolved": "https://registry.npmjs.org/conventional-changelog-jshint/-/conventional-changelog-jshint-2.0.9.tgz", "integrity": "sha512-wMLdaIzq6TNnMHMy31hql02OEQ8nCQfExw1SE0hYL5KvU+JCTuPaDO+7JiogGT2gJAxiUGATdtYYfh+nT+6riA==", "dev": true, - "requires": { + "dependencies": { "compare-func": "^2.0.0", "q": "^1.5.1" + }, + "engines": { + "node": ">=10" } }, - "conventional-changelog-preset-loader": { + "node_modules/conventional-changelog-preset-loader": { "version": "2.3.4", "resolved": "https://registry.npmjs.org/conventional-changelog-preset-loader/-/conventional-changelog-preset-loader-2.3.4.tgz", "integrity": "sha512-GEKRWkrSAZeTq5+YjUZOYxdHq+ci4dNwHvpaBC3+ENalzFWuCWa9EZXSuZBpkr72sMdKB+1fyDV4takK1Lf58g==", - "dev": true - }, - "conventional-changelog-tf-a": { - "version": "file:tools/conventional-changelog-tf-a", - "requires": { - "conventional-changelog-conventionalcommits": "^4.6.1", - "execa": "^5.1.1", - "lodash": "^4.17.21", - "q": "^1.5.1" + "dev": true, + "engines": { + "node": ">=10" } }, - "conventional-changelog-writer": { + "node_modules/conventional-changelog-tf-a": { + "resolved": "tools/conventional-changelog-tf-a", + "link": true + }, + "node_modules/conventional-changelog-writer": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/conventional-changelog-writer/-/conventional-changelog-writer-5.0.1.tgz", "integrity": "sha512-5WsuKUfxW7suLblAbFnxAcrvf6r+0b7GvNaWUwUIk0bXMnENP/PEieGKVUQrjPqwPT4o3EPAASBXiY6iHooLOQ==", "dev": true, - "requires": { + "dependencies": { "conventional-commits-filter": "^2.0.7", "dateformat": "^3.0.0", "handlebars": "^4.7.7", @@ -4968,51 +1836,67 @@ "split": "^1.0.0", "through2": "^4.0.0" }, - "dependencies": { - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true - } + "bin": { + "conventional-changelog-writer": "cli.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/conventional-changelog-writer/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "bin": { + "semver": "bin/semver.js" } }, - "conventional-commit-types": { + "node_modules/conventional-commit-types": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/conventional-commit-types/-/conventional-commit-types-3.0.0.tgz", "integrity": "sha512-SmmCYnOniSsAa9GqWOeLqc179lfr5TRu5b4QFDkbsrJ5TZjPJx85wtOr3zn+1dbeNiXDKGPbZ72IKbPhLXh/Lg==", "dev": true }, - "conventional-commits-filter": { + "node_modules/conventional-commits-filter": { "version": "2.0.7", "resolved": "https://registry.npmjs.org/conventional-commits-filter/-/conventional-commits-filter-2.0.7.tgz", "integrity": "sha512-ASS9SamOP4TbCClsRHxIHXRfcGCnIoQqkvAzCSbZzTFLfcTqJVugB0agRgsEELsqaeWgsXv513eS116wnlSSPA==", "dev": true, - "requires": { + "dependencies": { "lodash.ismatch": "^4.4.0", "modify-values": "^1.0.0" + }, + "engines": { + "node": ">=10" } }, - "conventional-commits-parser": { + "node_modules/conventional-commits-parser": { "version": "3.2.4", "resolved": "https://registry.npmjs.org/conventional-commits-parser/-/conventional-commits-parser-3.2.4.tgz", "integrity": "sha512-nK7sAtfi+QXbxHCYfhpZsfRtaitZLIA6889kFIouLvz6repszQDgxBu7wf2WbU+Dco7sAnNCJYERCwt54WPC2Q==", "dev": true, - "requires": { + "dependencies": { "is-text-path": "^1.0.1", "JSONStream": "^1.0.4", "lodash": "^4.17.15", "meow": "^8.0.0", "split2": "^3.0.0", "through2": "^4.0.0" + }, + "bin": { + "conventional-commits-parser": "cli.js" + }, + "engines": { + "node": ">=10" } }, - "conventional-recommended-bump": { + "node_modules/conventional-recommended-bump": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/conventional-recommended-bump/-/conventional-recommended-bump-6.1.0.tgz", "integrity": "sha512-uiApbSiNGM/kkdL9GTOLAqC4hbptObFo4wW2QRyHsKciGAfQuLU1ShZ1BIVI/+K2BE/W1AWYQMCXAsv4dyKPaw==", "dev": true, - "requires": { + "dependencies": { "concat-stream": "^2.0.0", "conventional-changelog-preset-loader": "^2.3.4", "conventional-commits-filter": "^2.0.7", @@ -5021,61 +1905,83 @@ "git-semver-tags": "^4.1.1", "meow": "^8.0.0", "q": "^1.5.1" + }, + "bin": { + "conventional-recommended-bump": "cli.js" + }, + "engines": { + "node": ">=10" } }, - "core-util-is": { + "node_modules/core-util-is": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", "dev": true }, - "cosmiconfig": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.0.1.tgz", - "integrity": "sha512-a1YWNUV2HwGimB7dU2s1wUMurNKjpx60HxBB6xUM8Re+2s1g1IIfJvFR0/iCF+XHdE0GMTKTuLR32UQff4TEyQ==", + "node_modules/cosmiconfig": { + "version": "8.3.6", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-8.3.6.tgz", + "integrity": "sha512-kcZ6+W5QzcJ3P1Mt+83OUv/oHFqZHIx8DuxG6eZ5RGMERoLqp4BuGjhHLYGK+Kf5XVkQvqBSmAy/nGWN3qDgEA==", "dev": true, - "requires": { - "@types/parse-json": "^4.0.0", - "import-fresh": "^3.2.1", - "parse-json": "^5.0.0", - "path-type": "^4.0.0", - "yaml": "^1.10.0" + "dependencies": { + "import-fresh": "^3.3.0", + "js-yaml": "^4.1.0", + "parse-json": "^5.2.0", + "path-type": "^4.0.0" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/d-fischer" + }, + "peerDependencies": { + "typescript": ">=4.9.5" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } } }, - "cosmiconfig-typescript-loader": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/cosmiconfig-typescript-loader/-/cosmiconfig-typescript-loader-2.0.2.tgz", - "integrity": "sha512-KmE+bMjWMXJbkWCeY4FJX/npHuZPNr9XF9q9CIQ/bpFwi1qHfCmSiKarrCcRa0LO4fWjk93pVoeRtJAkTGcYNw==", + "node_modules/cosmiconfig-typescript-loader": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/cosmiconfig-typescript-loader/-/cosmiconfig-typescript-loader-5.0.0.tgz", + "integrity": "sha512-+8cK7jRAReYkMwMiG+bxhcNKiHJDM6bR9FD/nGBXOWdMLuYawjF5cGrtLilJ+LGd3ZjCXnJjR5DkfWPoIVlqJA==", "dev": true, - "requires": { - "cosmiconfig": "^7", - "ts-node": "^10.8.1" + "dependencies": { + "jiti": "^1.19.1" + }, + "engines": { + "node": ">=v16" + }, + "peerDependencies": { + "@types/node": "*", + "cosmiconfig": ">=8.2", + "typescript": ">=4" } }, - "create-require": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", - "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", - "dev": true - }, - "cross-spawn": { + "node_modules/cross-spawn": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", "dev": true, - "requires": { + "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" } }, - "cz-conventional-changelog": { + "node_modules/cz-conventional-changelog": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/cz-conventional-changelog/-/cz-conventional-changelog-3.3.0.tgz", "integrity": "sha512-U466fIzU5U22eES5lTNiNbZ+d8dfcHcssH4o7QsdWaCcRs/feIPCxKYSWkYBNs5mny7MvEfwpTLWjvbm94hecw==", "dev": true, - "requires": { - "@commitlint/load": ">6.1.1", + "dependencies": { "chalk": "^2.4.1", "commitizen": "^4.0.3", "conventional-commit-types": "^3.0.0", @@ -5083,237 +1989,347 @@ "longest": "^2.0.1", "word-wrap": "^1.0.3" }, + "engines": { + "node": ">= 10" + }, + "optionalDependencies": { + "@commitlint/load": ">6.1.1" + } + }, + "node_modules/cz-conventional-changelog/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, "dependencies": { - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "requires": { - "color-convert": "^1.9.0" - } - }, - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - } - }, - "color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, - "requires": { - "color-name": "1.1.3" - } - }, - "color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", - "dev": true - }, - "has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", - "dev": true - }, - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/cz-conventional-changelog/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/cz-conventional-changelog/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/cz-conventional-changelog/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true + }, + "node_modules/cz-conventional-changelog/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/cz-conventional-changelog/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" } }, - "dargs": { + "node_modules/dargs": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/dargs/-/dargs-7.0.0.tgz", "integrity": "sha512-2iy1EkLdlBzQGvbweYRFxmFath8+K7+AKB0TlhHWkNuH+TmovaMH/Wp7V7R4u7f4SnX3OgLsU9t1NI9ioDnUpg==", - "dev": true + "dev": true, + "engines": { + "node": ">=8" + } }, - "dateformat": { + "node_modules/dateformat": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/dateformat/-/dateformat-3.0.3.tgz", "integrity": "sha512-jyCETtSl3VMZMWeRo7iY1FL19ges1t55hMo5yaam4Jrsm5EPL89UQkoQRyiI+Yf4k8r2ZpdngkV8hr1lIdjb3Q==", - "dev": true + "dev": true, + "engines": { + "node": "*" + } }, - "decamelize": { + "node_modules/decamelize": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", "integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==", - "dev": true + "dev": true, + "engines": { + "node": ">=0.10.0" + } }, - "decamelize-keys": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/decamelize-keys/-/decamelize-keys-1.1.0.tgz", - "integrity": "sha512-ocLWuYzRPoS9bfiSdDd3cxvrzovVMZnRDVEzAs+hWIVXGDbHxWMECij2OBuyB/An0FFW/nLuq6Kv1i/YC5Qfzg==", + "node_modules/decamelize-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/decamelize-keys/-/decamelize-keys-1.1.1.tgz", + "integrity": "sha512-WiPxgEirIV0/eIOMcnFBA3/IJZAZqKnwAwWyvvdi4lsr1WCN22nhdf/3db3DoZcUjTV2SqfzIwNyp6y2xs3nmg==", "dev": true, - "requires": { + "dependencies": { "decamelize": "^1.1.0", "map-obj": "^1.0.0" }, - "dependencies": { - "map-obj": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz", - "integrity": "sha512-7N/q3lyZ+LVCp7PzuxrJr4KMbBE2hW7BT7YNia330OFxIf4d3r5zVpicP2650l7CPN6RM9zOJRl3NGpqSiw3Eg==", - "dev": true - } + "engines": { + "node": ">=0.10.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/decamelize-keys/node_modules/map-obj": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz", + "integrity": "sha512-7N/q3lyZ+LVCp7PzuxrJr4KMbBE2hW7BT7YNia330OFxIf4d3r5zVpicP2650l7CPN6RM9zOJRl3NGpqSiw3Eg==", + "dev": true, + "engines": { + "node": ">=0.10.0" } }, - "dedent": { + "node_modules/dedent": { "version": "0.7.0", "resolved": "https://registry.npmjs.org/dedent/-/dedent-0.7.0.tgz", "integrity": "sha512-Q6fKUPqnAHAyhiUgFU7BUzLiv0kd8saH9al7tnu5Q/okj6dnupxyTgFIBjVzJATdfIAm9NAsvXNzjaKa+bxVyA==", "dev": true }, - "defaults": { + "node_modules/defaults": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.4.tgz", "integrity": "sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==", "dev": true, - "requires": { + "dependencies": { "clone": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/define-data-property": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", + "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", + "dev": true, + "peer": true, + "dependencies": { + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "gopd": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "detect-file": { + "node_modules/detect-file": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/detect-file/-/detect-file-1.0.0.tgz", "integrity": "sha512-DtCOLG98P007x7wiiOmfI0fi3eIKyWiLTGJ2MDnVi/E04lWGbf+JzrRHMm0rgIIZJGtHpKpbVgLWHrv8xXpc3Q==", - "dev": true + "dev": true, + "engines": { + "node": ">=0.10.0" + } }, - "detect-indent": { + "node_modules/detect-indent": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/detect-indent/-/detect-indent-6.1.0.tgz", "integrity": "sha512-reYkTUJAZb9gUuZ2RvVCNhVHdg62RHnJ7WJl8ftMi4diZ6NWlciOzQN88pUhSELEwflJht4oQDv0F0BMlwaYtA==", - "dev": true + "dev": true, + "engines": { + "node": ">=8" + } }, - "detect-newline": { + "node_modules/detect-newline": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", "integrity": "sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==", - "dev": true - }, - "diff": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", - "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", - "dev": true + "dev": true, + "engines": { + "node": ">=8" + } }, - "dot-prop": { + "node_modules/dot-prop": { "version": "5.3.0", "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-5.3.0.tgz", "integrity": "sha512-QM8q3zDe58hqUqjraQOmzZ1LIH9SWQJTlEKCH4kJ2oQvLZk7RbQXvtDM2XEq3fwkV9CCvvH4LA0AV+ogFsBM2Q==", "dev": true, - "requires": { + "dependencies": { "is-obj": "^2.0.0" + }, + "engines": { + "node": ">=8" } }, - "dotgitignore": { + "node_modules/dotgitignore": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/dotgitignore/-/dotgitignore-2.1.0.tgz", "integrity": "sha512-sCm11ak2oY6DglEPpCB8TixLjWAxd3kJTs6UIcSasNYxXdFPV+YKlye92c8H4kKFqV5qYMIh7d+cYecEg0dIkA==", "dev": true, - "requires": { + "dependencies": { "find-up": "^3.0.0", "minimatch": "^3.0.4" }, + "engines": { + "node": ">=6" + } + }, + "node_modules/dotgitignore/node_modules/find-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "dev": true, + "dependencies": { + "locate-path": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/dotgitignore/node_modules/locate-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", + "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "dev": true, "dependencies": { - "find-up": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", - "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", - "dev": true, - "requires": { - "locate-path": "^3.0.0" - } - }, - "locate-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", - "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", - "dev": true, - "requires": { - "p-locate": "^3.0.0", - "path-exists": "^3.0.0" - } - }, - "p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "dev": true, - "requires": { - "p-try": "^2.0.0" - } - }, - "p-locate": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", - "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", - "dev": true, - "requires": { - "p-limit": "^2.0.0" - } - }, - "p-try": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", - "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", - "dev": true - }, - "path-exists": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", - "integrity": "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==", - "dev": true - } + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/dotgitignore/node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/dotgitignore/node_modules/p-locate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", + "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "dev": true, + "dependencies": { + "p-limit": "^2.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/dotgitignore/node_modules/p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "dev": true, + "engines": { + "node": ">=6" } }, - "emoji-regex": { + "node_modules/dotgitignore/node_modules/path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/emoji-regex": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", "dev": true }, - "error-ex": { + "node_modules/error-ex": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", "dev": true, - "requires": { + "dependencies": { "is-arrayish": "^0.2.1" } }, - "escalade": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", - "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", - "dev": true + "node_modules/es-define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz", + "integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==", + "dev": true, + "peer": true, + "dependencies": { + "get-intrinsic": "^1.2.4" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "dev": true, + "peer": true, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/escalade": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.2.tgz", + "integrity": "sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==", + "dev": true, + "engines": { + "node": ">=6" + } }, - "escape-string-regexp": { + "node_modules/escape-string-regexp": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", - "dev": true + "dev": true, + "engines": { + "node": ">=0.8.0" + } }, - "execa": { + "node_modules/execa": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", "dev": true, - "requires": { + "dependencies": { "cross-spawn": "^7.0.3", "get-stream": "^6.0.0", "human-signals": "^2.1.0", @@ -5323,761 +2339,1195 @@ "onetime": "^5.1.2", "signal-exit": "^3.0.3", "strip-final-newline": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" } }, - "expand-tilde": { + "node_modules/expand-tilde": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/expand-tilde/-/expand-tilde-2.0.2.tgz", "integrity": "sha512-A5EmesHW6rfnZ9ysHQjPdJRni0SRar0tjtG5MNtm9n5TUvsYU8oozprtRD4AqHxcZWWlVuAmQo2nWKfN9oyjTw==", "dev": true, - "requires": { + "dependencies": { "homedir-polyfill": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" } }, - "external-editor": { + "node_modules/external-editor": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz", "integrity": "sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==", "dev": true, - "requires": { + "dependencies": { "chardet": "^0.7.0", "iconv-lite": "^0.4.24", "tmp": "^0.0.33" + }, + "engines": { + "node": ">=4" } }, - "fast-deep-equal": { + "node_modules/fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", "dev": true }, - "fast-json-stable-stringify": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", - "dev": true - }, - "figures": { + "node_modules/figures": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", "integrity": "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==", "dev": true, - "requires": { + "dependencies": { "escape-string-regexp": "^1.0.5" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", "dev": true, - "requires": { + "dependencies": { "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" } }, - "find-node-modules": { + "node_modules/find-node-modules": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/find-node-modules/-/find-node-modules-2.1.3.tgz", "integrity": "sha512-UC2I2+nx1ZuOBclWVNdcnbDR5dlrOdVb7xNjmT/lHE+LsgztWks3dG7boJ37yTS/venXw84B/mAW9uHVoC5QRg==", "dev": true, - "requires": { + "dependencies": { "findup-sync": "^4.0.0", "merge": "^2.1.1" } }, - "find-root": { + "node_modules/find-root": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/find-root/-/find-root-1.1.0.tgz", "integrity": "sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng==", "dev": true }, - "find-up": { + "node_modules/find-up": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", "dev": true, - "requires": { + "dependencies": { "locate-path": "^6.0.0", "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "findup-sync": { + "node_modules/findup-sync": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/findup-sync/-/findup-sync-4.0.0.tgz", "integrity": "sha512-6jvvn/12IC4quLBL1KNokxC7wWTvYncaVUYSoxWw7YykPLuRrnv4qdHcSOywOI5RpkOVGeQRtWM8/q+G6W6qfQ==", "dev": true, - "requires": { + "dependencies": { "detect-file": "^1.0.0", "is-glob": "^4.0.0", "micromatch": "^4.0.2", "resolve-dir": "^1.0.1" + }, + "engines": { + "node": ">= 8" } }, - "fs-extra": { - "version": "10.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", - "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", - "dev": true, - "requires": { - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" - } - }, - "fs.realpath": { + "node_modules/fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", "dev": true }, - "function-bind": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", - "dev": true + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } }, - "get-caller-file": { + "node_modules/get-caller-file": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", - "dev": true + "dev": true, + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/get-intrinsic": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz", + "integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==", + "dev": true, + "peer": true, + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "has-proto": "^1.0.1", + "has-symbols": "^1.0.3", + "hasown": "^2.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } }, - "get-pkg-repo": { + "node_modules/get-pkg-repo": { "version": "4.2.1", "resolved": "https://registry.npmjs.org/get-pkg-repo/-/get-pkg-repo-4.2.1.tgz", "integrity": "sha512-2+QbHjFRfGB74v/pYWjd5OhU3TDIC2Gv/YKUTk/tCvAz0pkn/Mz6P3uByuBimLOcPvN2jYdScl3xGFSrx0jEcA==", "dev": true, - "requires": { + "dependencies": { "@hutson/parse-repository-url": "^3.0.0", "hosted-git-info": "^4.0.0", "through2": "^2.0.0", "yargs": "^16.2.0" }, + "bin": { + "get-pkg-repo": "src/cli.js" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/get-pkg-repo/node_modules/cliui": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "dev": true, "dependencies": { - "cliui": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", - "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", - "dev": true, - "requires": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.0", - "wrap-ansi": "^7.0.0" - } - }, - "readable-stream": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", - "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", - "dev": true, - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "dev": true - }, - "string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "dev": true, - "requires": { - "safe-buffer": "~5.1.0" - } - }, - "through2": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", - "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", - "dev": true, - "requires": { - "readable-stream": "~2.3.6", - "xtend": "~4.0.1" - } - }, - "yargs": { - "version": "16.2.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", - "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", - "dev": true, - "requires": { - "cliui": "^7.0.2", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.0", - "y18n": "^5.0.5", - "yargs-parser": "^20.2.2" - } - } + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" + } + }, + "node_modules/get-pkg-repo/node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "dev": true, + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/get-pkg-repo/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true + }, + "node_modules/get-pkg-repo/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/get-pkg-repo/node_modules/through2": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", + "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", + "dev": true, + "dependencies": { + "readable-stream": "~2.3.6", + "xtend": "~4.0.1" + } + }, + "node_modules/get-pkg-repo/node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/get-pkg-repo/node_modules/yargs": { + "version": "16.2.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", + "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "dev": true, + "dependencies": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" + }, + "engines": { + "node": ">=10" } }, - "get-stream": { + "node_modules/get-stream": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", - "dev": true + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } }, - "git-raw-commits": { + "node_modules/git-raw-commits": { "version": "2.0.11", "resolved": "https://registry.npmjs.org/git-raw-commits/-/git-raw-commits-2.0.11.tgz", "integrity": "sha512-VnctFhw+xfj8Va1xtfEqCUD2XDrbAPSJx+hSrE5K7fGdjZruW7XV+QOrN7LF/RJyvspRiD2I0asWsxFp0ya26A==", "dev": true, - "requires": { + "dependencies": { "dargs": "^7.0.0", "lodash": "^4.17.15", "meow": "^8.0.0", "split2": "^3.0.0", "through2": "^4.0.0" + }, + "bin": { + "git-raw-commits": "cli.js" + }, + "engines": { + "node": ">=10" } }, - "git-remote-origin-url": { + "node_modules/git-remote-origin-url": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/git-remote-origin-url/-/git-remote-origin-url-2.0.0.tgz", "integrity": "sha512-eU+GGrZgccNJcsDH5LkXR3PB9M958hxc7sbA8DFJjrv9j4L2P/eZfKhM+QD6wyzpiv+b1BpK0XrYCxkovtjSLw==", "dev": true, - "requires": { + "dependencies": { "gitconfiglocal": "^1.0.0", "pify": "^2.3.0" + }, + "engines": { + "node": ">=4" } }, - "git-semver-tags": { + "node_modules/git-semver-tags": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/git-semver-tags/-/git-semver-tags-4.1.1.tgz", "integrity": "sha512-OWyMt5zBe7xFs8vglMmhM9lRQzCWL3WjHtxNNfJTMngGym7pC1kh8sP6jevfydJ6LP3ZvGxfb6ABYgPUM0mtsA==", "dev": true, - "requires": { + "dependencies": { "meow": "^8.0.0", "semver": "^6.0.0" }, - "dependencies": { - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true - } + "bin": { + "git-semver-tags": "cli.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/git-semver-tags/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "bin": { + "semver": "bin/semver.js" } }, - "gitconfiglocal": { + "node_modules/gitconfiglocal": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/gitconfiglocal/-/gitconfiglocal-1.0.0.tgz", "integrity": "sha512-spLUXeTAVHxDtKsJc8FkFVgFtMdEN9qPGpL23VfSHx4fP4+Ds097IXLvymbnDH8FnmxX5Nr9bPw3A+AQ6mWEaQ==", "dev": true, - "requires": { + "dependencies": { "ini": "^1.3.2" } }, - "glob": { + "node_modules/glob": { "version": "7.2.3", "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", "dev": true, - "requires": { + "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", "inherits": "2", "minimatch": "^3.1.1", "once": "^1.3.0", "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/global-directory": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/global-directory/-/global-directory-4.0.1.tgz", + "integrity": "sha512-wHTUcDUoZ1H5/0iVqEudYW4/kAlN5cZ3j/bXn0Dpbizl9iaUVeWSHqiOjsgk6OW2bkLclbBjzewBz6weQ1zA2Q==", + "dev": true, + "dependencies": { + "ini": "4.1.1" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "global-dirs": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/global-dirs/-/global-dirs-0.1.1.tgz", - "integrity": "sha512-NknMLn7F2J7aflwFOlGdNIuCDpN3VGoSoB+aap3KABFWbHVn1TCgFC+np23J8W2BiZbjfEw3BFBycSMv1AFblg==", + "node_modules/global-directory/node_modules/ini": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ini/-/ini-4.1.1.tgz", + "integrity": "sha512-QQnnxNyfvmHFIsj7gkPcYymR8Jdw/o7mp5ZFihxn6h8Ci6fh3Dx4E1gPjpQEpIuPo9XVNY/ZUwh4BPMjGyL01g==", "dev": true, - "requires": { - "ini": "^1.3.4" + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, - "global-modules": { + "node_modules/global-modules": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/global-modules/-/global-modules-1.0.0.tgz", "integrity": "sha512-sKzpEkf11GpOFuw0Zzjzmt4B4UZwjOcG757PPvrfhxcLFbq0wpsgpOqxpxtxFiCG4DtG93M6XRVbF2oGdev7bg==", "dev": true, - "requires": { + "dependencies": { "global-prefix": "^1.0.1", "is-windows": "^1.0.1", "resolve-dir": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" } }, - "global-prefix": { + "node_modules/global-prefix": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/global-prefix/-/global-prefix-1.0.2.tgz", "integrity": "sha512-5lsx1NUDHtSjfg0eHlmYvZKv8/nVqX4ckFbM+FrGcQ+04KWcWFo9P5MxPZYSzUvyzmdTbI7Eix8Q4IbELDqzKg==", "dev": true, - "requires": { + "dependencies": { "expand-tilde": "^2.0.2", "homedir-polyfill": "^1.0.1", "ini": "^1.3.4", "is-windows": "^1.0.1", "which": "^1.2.14" }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/global-prefix/node_modules/which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "dev": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "which": "bin/which" + } + }, + "node_modules/gopd": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", + "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", + "dev": true, + "peer": true, "dependencies": { - "which": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", - "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", - "dev": true, - "requires": { - "isexe": "^2.0.0" - } - } + "get-intrinsic": "^1.1.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "graceful-fs": { - "version": "4.2.10", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz", - "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==", + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", "dev": true }, - "handlebars": { - "version": "4.7.7", - "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.7.tgz", - "integrity": "sha512-aAcXm5OAfE/8IXkcZvCepKU3VzW1/39Fb5ZuqMtgI/hT8X2YgoMvBY5dLhq/cpOvw7Lk1nK/UF71aLG/ZnVYRA==", + "node_modules/handlebars": { + "version": "4.7.8", + "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.8.tgz", + "integrity": "sha512-vafaFqs8MZkRrSX7sFVUdo3ap/eNiLnb4IakshzvP56X5Nr1iGKAIqdX6tMlm6HcNRIkr6AxO5jFEoJzzpT8aQ==", "dev": true, - "requires": { + "dependencies": { "minimist": "^1.2.5", - "neo-async": "^2.6.0", + "neo-async": "^2.6.2", "source-map": "^0.6.1", - "uglify-js": "^3.1.4", "wordwrap": "^1.0.0" + }, + "bin": { + "handlebars": "bin/handlebars" + }, + "engines": { + "node": ">=0.4.7" + }, + "optionalDependencies": { + "uglify-js": "^3.1.4" } }, - "hard-rejection": { + "node_modules/hard-rejection": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/hard-rejection/-/hard-rejection-2.1.0.tgz", "integrity": "sha512-VIZB+ibDhx7ObhAe7OVtoEbuP4h/MuOTHJ+J8h/eBXotJYl0fBgR72xDFCKgIh22OJZIOVNxBMWuhAr10r8HdA==", - "dev": true - }, - "has": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", - "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", "dev": true, - "requires": { - "function-bind": "^1.1.1" + "engines": { + "node": ">=6" } }, - "has-flag": { + "node_modules/has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/has-property-descriptors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", + "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", + "dev": true, + "peer": true, + "dependencies": { + "es-define-property": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-proto": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.3.tgz", + "integrity": "sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==", + "dev": true, + "peer": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-symbols": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", + "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", + "dev": true, + "peer": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.1.tgz", + "integrity": "sha512-1/th4MHjnwncwXsIW6QMzlvYL9kG5e/CpVvLRZe4XPa8TOUNbCELqmvhDmnkNsAjwaG4+I8gJJL0JBvTTLO9qA==", + "dev": true, + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } }, - "homedir-polyfill": { + "node_modules/homedir-polyfill": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/homedir-polyfill/-/homedir-polyfill-1.0.3.tgz", "integrity": "sha512-eSmmWE5bZTK2Nou4g0AI3zZ9rswp7GRKoKXS1BLUkvPviOqs4YTN1djQIqrXy9k5gEtdLPy86JjRwsNM9tnDcA==", "dev": true, - "requires": { + "dependencies": { "parse-passwd": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" } }, - "hosted-git-info": { + "node_modules/hosted-git-info": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-4.1.0.tgz", "integrity": "sha512-kyCuEOWjJqZuDbRHzL8V93NzQhwIB71oFWSyzVo+KPZI+pnQPPxucdkrOZvkLRnrf5URsQM+IJ09Dw29cRALIA==", "dev": true, - "requires": { + "dependencies": { "lru-cache": "^6.0.0" + }, + "engines": { + "node": ">=10" } }, - "human-signals": { + "node_modules/human-signals": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", - "dev": true + "dev": true, + "engines": { + "node": ">=10.17.0" + } }, - "husky": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/husky/-/husky-7.0.4.tgz", - "integrity": "sha512-vbaCKN2QLtP/vD4yvs6iz6hBEo6wkSzs8HpRah1Z6aGmF2KW5PdYuAd7uX5a+OyBZHBhd+TFLqgjUgytQr4RvQ==", - "dev": true + "node_modules/husky": { + "version": "9.0.11", + "resolved": "https://registry.npmjs.org/husky/-/husky-9.0.11.tgz", + "integrity": "sha512-AB6lFlbwwyIqMdHYhwPe+kjOC3Oc5P3nThEoW/AaO2BX3vJDjWPFxYLxokUZOo6RNX20He3AaT8sESs9NJcmEw==", + "dev": true, + "bin": { + "husky": "bin.mjs" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/typicode" + } }, - "iconv-lite": { + "node_modules/iconv-lite": { "version": "0.4.24", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", "dev": true, - "requires": { + "dependencies": { "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" } }, - "ieee754": { + "node_modules/ieee754": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", - "dev": true + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] }, - "import-fresh": { + "node_modules/import-fresh": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", "dev": true, - "requires": { + "dependencies": { "parent-module": "^1.0.0", "resolve-from": "^4.0.0" }, - "dependencies": { - "resolve-from": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", - "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", - "dev": true - } + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/import-fresh/node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true, + "engines": { + "node": ">=4" } }, - "indent-string": { + "node_modules/import-meta-resolve": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/import-meta-resolve/-/import-meta-resolve-4.0.0.tgz", + "integrity": "sha512-okYUR7ZQPH+efeuMJGlq4f8ubUgO50kByRPyt/Cy1Io4PSRsPjxME+YlVaCOx+NIToW7hCsZNFJyTPFFKepRSA==", + "dev": true, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/indent-string": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", - "dev": true + "dev": true, + "engines": { + "node": ">=8" + } }, - "inflight": { + "node_modules/inflight": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", "dev": true, - "requires": { + "dependencies": { "once": "^1.3.0", "wrappy": "1" } }, - "inherits": { + "node_modules/inherits": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", "dev": true }, - "ini": { + "node_modules/ini": { "version": "1.3.8", "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", "dev": true }, - "inquirer": { - "version": "8.2.4", - "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-8.2.4.tgz", - "integrity": "sha512-nn4F01dxU8VeKfq192IjLsxu0/OmMZ4Lg3xKAns148rCaXP6ntAoEkVYZThWjwON8AlzdZZi6oqnhNbxUG9hVg==", + "node_modules/inquirer": { + "version": "9.2.15", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-9.2.15.tgz", + "integrity": "sha512-vI2w4zl/mDluHt9YEQ/543VTCwPKWiHzKtm9dM2V0NdFcqEexDAjUHzO1oA60HRNaVifGXXM1tRRNluLVHa0Kg==", "dev": true, - "requires": { - "ansi-escapes": "^4.2.1", - "chalk": "^4.1.1", + "peer": true, + "dependencies": { + "@ljharb/through": "^2.3.12", + "ansi-escapes": "^4.3.2", + "chalk": "^5.3.0", "cli-cursor": "^3.1.0", - "cli-width": "^3.0.0", - "external-editor": "^3.0.3", - "figures": "^3.0.0", + "cli-width": "^4.1.0", + "external-editor": "^3.1.0", + "figures": "^3.2.0", "lodash": "^4.17.21", - "mute-stream": "0.0.8", + "mute-stream": "1.0.0", "ora": "^5.4.1", - "run-async": "^2.4.0", - "rxjs": "^7.5.5", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0", - "through": "^2.3.6", - "wrap-ansi": "^7.0.0" + "run-async": "^3.0.0", + "rxjs": "^7.8.1", + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^6.2.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/inquirer/node_modules/chalk": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.3.0.tgz", + "integrity": "sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==", + "dev": true, + "peer": true, + "engines": { + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/inquirer/node_modules/cli-width": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-4.1.0.tgz", + "integrity": "sha512-ouuZd4/dm2Sw5Gmqy6bGyNNNe1qt9RpmxveLSO7KcgsTnU7RXfsw+/bukWGo1abgBiMAic068rclZsO4IWmmxQ==", + "dev": true, + "peer": true, + "engines": { + "node": ">= 12" } }, - "is-arrayish": { + "node_modules/inquirer/node_modules/mute-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-1.0.0.tgz", + "integrity": "sha512-avsJQhyd+680gKXyG/sQc0nXaC6rBkPOfyHYcFb9+hdkqQkR9bdnkJ0AMZhke0oesPqIO+mFFJ+IdBc7mst4IA==", + "dev": true, + "peer": true, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/inquirer/node_modules/run-async": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/run-async/-/run-async-3.0.0.tgz", + "integrity": "sha512-540WwVDOMxA6dN6We19EcT9sc3hkXPw5mzRNGM3FkdN/vtE9NFvj5lFAPNwUDmJjXidm3v7TC1cTE7t17Ulm1Q==", + "dev": true, + "peer": true, + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-arrayish": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", "dev": true }, - "is-core-module": { - "version": "2.10.0", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.10.0.tgz", - "integrity": "sha512-Erxj2n/LDAZ7H8WNJXd9tw38GYM3dv8rk8Zcs+jJuxYTW7sozH+SS8NtrSjVL1/vpLvWi1hxy96IzjJ3EHTJJg==", + "node_modules/is-core-module": { + "version": "2.13.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.1.tgz", + "integrity": "sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==", "dev": true, - "requires": { - "has": "^1.0.3" + "dependencies": { + "hasown": "^2.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "is-extglob": { + "node_modules/is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", - "dev": true + "dev": true, + "engines": { + "node": ">=0.10.0" + } }, - "is-fullwidth-code-point": { + "node_modules/is-fullwidth-code-point": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true + "dev": true, + "engines": { + "node": ">=8" + } }, - "is-glob": { + "node_modules/is-glob": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", "dev": true, - "requires": { + "dependencies": { "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" } }, - "is-interactive": { + "node_modules/is-interactive": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-1.0.0.tgz", "integrity": "sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==", - "dev": true + "dev": true, + "engines": { + "node": ">=8" + } }, - "is-number": { + "node_modules/is-number": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true + "dev": true, + "engines": { + "node": ">=0.12.0" + } }, - "is-obj": { + "node_modules/is-obj": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz", "integrity": "sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==", - "dev": true + "dev": true, + "engines": { + "node": ">=8" + } }, - "is-plain-obj": { + "node_modules/is-plain-obj": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz", "integrity": "sha512-yvkRyxmFKEOQ4pNXCmJG5AEQNlXJS5LaONXo5/cLdTZdWvsZ1ioJEonLGAosKlMWE8lwUy/bJzMjcw8az73+Fg==", - "dev": true + "dev": true, + "engines": { + "node": ">=0.10.0" + } }, - "is-stream": { + "node_modules/is-stream": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", - "dev": true + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } }, - "is-text-path": { + "node_modules/is-text-path": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/is-text-path/-/is-text-path-1.0.1.tgz", "integrity": "sha512-xFuJpne9oFz5qDaodwmmG08e3CawH/2ZV8Qqza1Ko7Sk8POWbkRdwIoAWVhqvq0XeUzANEhKo2n0IXUGBm7A/w==", "dev": true, - "requires": { + "dependencies": { "text-extensions": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" } }, - "is-unicode-supported": { + "node_modules/is-unicode-supported": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", - "dev": true + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } }, - "is-utf8": { + "node_modules/is-utf8": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz", "integrity": "sha512-rMYPYvCzsXywIsldgLaSoPlw5PfoB/ssr7hY4pLfcodrA5M/eArza1a9VmTiNIBNMjOGr1Ow9mTyU2o69U6U9Q==", "dev": true }, - "is-windows": { + "node_modules/is-windows": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==", - "dev": true + "dev": true, + "engines": { + "node": ">=0.10.0" + } }, - "isarray": { + "node_modules/isarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", "dev": true }, - "isexe": { + "node_modules/isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", "dev": true }, - "js-tokens": { + "node_modules/jiti": { + "version": "1.21.0", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.21.0.tgz", + "integrity": "sha512-gFqAIbuKyyso/3G2qhiO2OM6shY6EPP/R0+mkDbyspxKazh8BXDC5FiFsUjlczgdNz/vfra0da2y+aHrusLG/Q==", + "dev": true, + "bin": { + "jiti": "bin/jiti.js" + } + }, + "node_modules/js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", "dev": true }, - "js-yaml": { + "node_modules/js-yaml": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", "dev": true, - "requires": { + "dependencies": { "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" } }, - "json-parse-better-errors": { + "node_modules/json-parse-better-errors": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==", "dev": true }, - "json-parse-even-better-errors": { + "node_modules/json-parse-even-better-errors": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", "dev": true }, - "json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", "dev": true }, - "json-stringify-safe": { + "node_modules/json-stringify-safe": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", "integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==", "dev": true }, - "jsonfile": { + "node_modules/jsonfile": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", "dev": true, - "requires": { - "graceful-fs": "^4.1.6", + "dependencies": { "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" } }, - "jsonparse": { + "node_modules/jsonparse": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/jsonparse/-/jsonparse-1.3.1.tgz", "integrity": "sha512-POQXvpdL69+CluYsillJ7SUhKvytYjW9vG/GKpnf+xP8UWgYEM/RaMzHHofbALDiKbbP1W8UEYmgGl39WkPZsg==", - "dev": true + "dev": true, + "engines": [ + "node >= 0.2.0" + ] }, - "JSONStream": { + "node_modules/JSONStream": { "version": "1.3.5", "resolved": "https://registry.npmjs.org/JSONStream/-/JSONStream-1.3.5.tgz", "integrity": "sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ==", "dev": true, - "requires": { + "dependencies": { "jsonparse": "^1.2.0", "through": ">=2.2.7 <3" + }, + "bin": { + "JSONStream": "bin.js" + }, + "engines": { + "node": "*" } }, - "kind-of": { + "node_modules/kind-of": { "version": "6.0.3", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", - "dev": true + "dev": true, + "engines": { + "node": ">=0.10.0" + } }, - "lines-and-columns": { + "node_modules/lines-and-columns": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", "dev": true }, - "load-json-file": { + "node_modules/load-json-file": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz", "integrity": "sha512-Kx8hMakjX03tiGTLAIdJ+lL0htKnXjEZN6hk/tozf/WOuYGdZBJrZ+rCJRbVCugsjB3jMLn9746NsQIf5VjBMw==", "dev": true, - "requires": { + "dependencies": { "graceful-fs": "^4.1.2", "parse-json": "^4.0.0", "pify": "^3.0.0", "strip-bom": "^3.0.0" }, + "engines": { + "node": ">=4" + } + }, + "node_modules/load-json-file/node_modules/parse-json": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", + "integrity": "sha512-aOIos8bujGN93/8Ox/jPLh7RwVnPEysynVFE+fQZyg6jKELEHwzgKdLRFHUgXJL6kylijVSBC4BvN9OmsB48Rw==", + "dev": true, "dependencies": { - "parse-json": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", - "integrity": "sha512-aOIos8bujGN93/8Ox/jPLh7RwVnPEysynVFE+fQZyg6jKELEHwzgKdLRFHUgXJL6kylijVSBC4BvN9OmsB48Rw==", - "dev": true, - "requires": { - "error-ex": "^1.3.1", - "json-parse-better-errors": "^1.0.1" - } - }, - "pify": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", - "integrity": "sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg==", - "dev": true - }, - "strip-bom": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", - "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", - "dev": true - } + "error-ex": "^1.3.1", + "json-parse-better-errors": "^1.0.1" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/load-json-file/node_modules/pify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "integrity": "sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/load-json-file/node_modules/strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", + "dev": true, + "engines": { + "node": ">=4" } }, - "locate-path": { + "node_modules/locate-path": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", "dev": true, - "requires": { + "dependencies": { "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "lodash": { + "node_modules/lodash": { "version": "4.17.21", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", "dev": true }, - "lodash.ismatch": { + "node_modules/lodash.camelcase": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", + "integrity": "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==", + "dev": true + }, + "node_modules/lodash.ismatch": { "version": "4.4.0", "resolved": "https://registry.npmjs.org/lodash.ismatch/-/lodash.ismatch-4.4.0.tgz", "integrity": "sha512-fPMfXjGQEV9Xsq/8MTSgUf255gawYRbjwMyDbcvDhXgV7enSZA0hynz6vMPnpAb5iONEzBHBPsT+0zes5Z301g==", "dev": true }, - "lodash.map": { + "node_modules/lodash.isplainobject": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", + "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==", + "dev": true + }, + "node_modules/lodash.kebabcase": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.kebabcase/-/lodash.kebabcase-4.1.1.tgz", + "integrity": "sha512-N8XRTIMMqqDgSy4VLKPnJ/+hpGZN+PHQiJnSenYqPaVV/NCqEogTnAdZLQiGKhxX+JCs8waWq2t1XHWKOmlY8g==", + "dev": true + }, + "node_modules/lodash.map": { "version": "4.6.0", "resolved": "https://registry.npmjs.org/lodash.map/-/lodash.map-4.6.0.tgz", "integrity": "sha512-worNHGKLDetmcEYDvh2stPCrrQRkP20E4l0iIS7F8EvzMqBBi7ltvFN5m1HvTf1P7Jk1txKhvFcmYsCr8O2F1Q==", "dev": true }, - "log-symbols": { + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true + }, + "node_modules/lodash.mergewith": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.mergewith/-/lodash.mergewith-4.6.2.tgz", + "integrity": "sha512-GK3g5RPZWTRSeLSpgP8Xhra+pnjBC56q9FZYe1d5RN3TJ35dbkGy3YqBSMbyCrlbi+CM9Z3Jk5yTL7RCsqboyQ==", + "dev": true + }, + "node_modules/lodash.snakecase": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.snakecase/-/lodash.snakecase-4.1.1.tgz", + "integrity": "sha512-QZ1d4xoBHYUeuouhEq3lk3Uq7ldgyFXGBhg04+oRLnIz8o9T65Eh+8YdroUwn846zchkA9yDsDl5CVVaV2nqYw==", + "dev": true + }, + "node_modules/lodash.startcase": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/lodash.startcase/-/lodash.startcase-4.4.0.tgz", + "integrity": "sha512-+WKqsK294HMSc2jEbNgpHpd0JfIBhp7rEV4aqXWqFr6AlXov+SlcgB1Fv01y2kGe3Gc8nMW7VA0SrGuSkRfIEg==", + "dev": true + }, + "node_modules/lodash.uniq": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz", + "integrity": "sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ==", + "dev": true + }, + "node_modules/lodash.upperfirst": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/lodash.upperfirst/-/lodash.upperfirst-4.3.1.tgz", + "integrity": "sha512-sReKOYJIJf74dhJONhU4e0/shzi1trVbSWDOhKYE5XV2O+H7Sb2Dihwuc7xWxVl+DgFPyTqIN3zMfT9cq5iWDg==", + "dev": true + }, + "node_modules/log-symbols": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", "dev": true, - "requires": { + "dependencies": { "chalk": "^4.1.0", "is-unicode-supported": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "longest": { + "node_modules/longest": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/longest/-/longest-2.0.1.tgz", "integrity": "sha512-Ajzxb8CM6WAnFjgiloPsI3bF+WCxcvhdIG3KNA2KN962+tdBsHcuQ4k4qX/EcS/2CRkcc0iAkR956Nib6aXU/Q==", - "dev": true + "dev": true, + "engines": { + "node": ">=0.10.0" + } }, - "lru-cache": { + "node_modules/lru-cache": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", "dev": true, - "requires": { + "dependencies": { "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" } }, - "make-error": { - "version": "1.3.6", - "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", - "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", - "dev": true - }, - "map-obj": { + "node_modules/map-obj": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-4.3.0.tgz", "integrity": "sha512-hdN1wVrZbb29eBGiGjJbeP8JbKjq1urkHJ/LIP/NY48MZ1QVXUsQBV1G1zvYFHn1XE06cwjBsOI2K3Ulnj1YXQ==", - "dev": true + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } }, - "meow": { + "node_modules/meow": { "version": "8.1.2", "resolved": "https://registry.npmjs.org/meow/-/meow-8.1.2.tgz", "integrity": "sha512-r85E3NdZ+mpYk1C6RjPFEMSE+s1iZMuHtsHAqY0DT3jZczl0diWUZ8g6oU7h0M9cD2EL+PzaYghhCLzR0ZNn5Q==", "dev": true, - "requires": { + "dependencies": { "@types/minimist": "^1.2.0", "camelcase-keys": "^6.2.2", "decamelize-keys": "^1.1.0", @@ -6090,138 +3540,181 @@ "type-fest": "^0.18.0", "yargs-parser": "^20.2.3" }, - "dependencies": { - "type-fest": { - "version": "0.18.1", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.18.1.tgz", - "integrity": "sha512-OIAYXk8+ISY+qTOwkHtKqzAuxchoMiD9Udx+FSGQDuiRR+PJKJHc2NJAXlbhkGwTt/4/nKZxELY1w3ReWOL8mw==", - "dev": true - } + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "merge": { + "node_modules/meow/node_modules/type-fest": { + "version": "0.18.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.18.1.tgz", + "integrity": "sha512-OIAYXk8+ISY+qTOwkHtKqzAuxchoMiD9Udx+FSGQDuiRR+PJKJHc2NJAXlbhkGwTt/4/nKZxELY1w3ReWOL8mw==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/merge": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/merge/-/merge-2.1.1.tgz", "integrity": "sha512-jz+Cfrg9GWOZbQAnDQ4hlVnQky+341Yk5ru8bZSe6sIDTCIg8n9i/u7hSQGSVOF3C7lH6mGtqjkiT9G4wFLL0w==", "dev": true }, - "merge-stream": { + "node_modules/merge-stream": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", "dev": true }, - "micromatch": { + "node_modules/micromatch": { "version": "4.0.5", "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", "dev": true, - "requires": { + "dependencies": { "braces": "^3.0.2", "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" } }, - "mimic-fn": { + "node_modules/mimic-fn": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", - "dev": true + "dev": true, + "engines": { + "node": ">=6" + } }, - "min-indent": { + "node_modules/min-indent": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz", "integrity": "sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==", - "dev": true + "dev": true, + "engines": { + "node": ">=4" + } }, - "minimatch": { + "node_modules/minimatch": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "dev": true, - "requires": { + "dependencies": { "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" } }, - "minimist": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz", - "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==", - "dev": true + "node_modules/minimist": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.7.tgz", + "integrity": "sha512-bzfL1YUZsP41gmu/qjrEk0Q6i2ix/cVeAhbCbqH9u3zYutS1cLg00qhrD0M2MVdCcx4Sc0UpP2eBWo9rotpq6g==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } }, - "minimist-options": { + "node_modules/minimist-options": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/minimist-options/-/minimist-options-4.1.0.tgz", "integrity": "sha512-Q4r8ghd80yhO/0j1O3B2BjweX3fiHg9cdOwjJd2J76Q135c+NDxGCqdYKQ1SKBuFfgWbAUzBfvYjPUEeNgqN1A==", "dev": true, - "requires": { + "dependencies": { "arrify": "^1.0.1", "is-plain-obj": "^1.1.0", "kind-of": "^6.0.3" + }, + "engines": { + "node": ">= 6" } }, - "modify-values": { + "node_modules/modify-values": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/modify-values/-/modify-values-1.0.1.tgz", "integrity": "sha512-xV2bxeN6F7oYjZWTe/YPAy6MN2M+sL4u/Rlm2AHCIVGfo2p1yGmBHQ6vHehl4bRTZBdHu3TSkWdYgkwpYzAGSw==", - "dev": true + "dev": true, + "engines": { + "node": ">=0.10.0" + } }, - "mute-stream": { + "node_modules/mute-stream": { "version": "0.0.8", "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz", "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==", "dev": true }, - "neo-async": { + "node_modules/neo-async": { "version": "2.6.2", "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", "dev": true }, - "normalize-package-data": { + "node_modules/normalize-package-data": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-3.0.3.tgz", "integrity": "sha512-p2W1sgqij3zMMyRC067Dg16bfzVH+w7hyegmpIvZ4JNjqtGOVAIvLmjBx3yP7YTe9vKJgkoNOPjwQGogDoMXFA==", "dev": true, - "requires": { + "dependencies": { "hosted-git-info": "^4.0.1", "is-core-module": "^2.5.0", "semver": "^7.3.4", "validate-npm-package-license": "^3.0.1" + }, + "engines": { + "node": ">=10" } }, - "npm-run-path": { + "node_modules/npm-run-path": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", "dev": true, - "requires": { + "dependencies": { "path-key": "^3.0.0" + }, + "engines": { + "node": ">=8" } }, - "once": { + "node_modules/once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", "dev": true, - "requires": { + "dependencies": { "wrappy": "1" } }, - "onetime": { + "node_modules/onetime": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", "dev": true, - "requires": { + "dependencies": { "mimic-fn": "^2.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "ora": { + "node_modules/ora": { "version": "5.4.1", "resolved": "https://registry.npmjs.org/ora/-/ora-5.4.1.tgz", "integrity": "sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==", "dev": true, - "requires": { + "dependencies": { "bl": "^4.1.0", "chalk": "^4.1.0", "cli-cursor": "^3.1.0", @@ -6231,429 +3724,620 @@ "log-symbols": "^4.1.0", "strip-ansi": "^6.0.0", "wcwidth": "^1.0.1" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "os-tmpdir": { + "node_modules/os-tmpdir": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", "integrity": "sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==", - "dev": true + "dev": true, + "engines": { + "node": ">=0.10.0" + } }, - "p-limit": { + "node_modules/p-limit": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", "dev": true, - "requires": { + "dependencies": { "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "p-locate": { + "node_modules/p-locate": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", "dev": true, - "requires": { + "dependencies": { "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "p-try": { + "node_modules/p-try": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", "integrity": "sha512-U1etNYuMJoIz3ZXSrrySFjsXQTWOx2/jdi86L+2pRvph/qMKL6sbcCYdH23fqsbm8TH2Gn0OybpT4eSFlCVHww==", - "dev": true + "dev": true, + "engines": { + "node": ">=4" + } }, - "parent-module": { + "node_modules/parent-module": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", "dev": true, - "requires": { + "dependencies": { "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" } }, - "parse-json": { + "node_modules/parse-json": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", "dev": true, - "requires": { + "dependencies": { "@babel/code-frame": "^7.0.0", "error-ex": "^1.3.1", "json-parse-even-better-errors": "^2.3.0", "lines-and-columns": "^1.1.6" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "parse-passwd": { + "node_modules/parse-passwd": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/parse-passwd/-/parse-passwd-1.0.0.tgz", "integrity": "sha512-1Y1A//QUXEZK7YKz+rD9WydcE1+EuPr6ZBgKecAB8tmoW6UFv0NREVJe1p+jRxtThkcbbKkfwIbWJe/IeE6m2Q==", - "dev": true + "dev": true, + "engines": { + "node": ">=0.10.0" + } }, - "path-exists": { + "node_modules/path-exists": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", - "dev": true + "dev": true, + "engines": { + "node": ">=8" + } }, - "path-is-absolute": { + "node_modules/path-is-absolute": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", - "dev": true + "dev": true, + "engines": { + "node": ">=0.10.0" + } }, - "path-key": { + "node_modules/path-key": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "dev": true + "dev": true, + "engines": { + "node": ">=8" + } }, - "path-parse": { + "node_modules/path-parse": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", "dev": true }, - "path-type": { + "node_modules/path-type": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", - "dev": true + "dev": true, + "engines": { + "node": ">=8" + } }, - "picomatch": { + "node_modules/picomatch": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "dev": true + "dev": true, + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } }, - "pify": { + "node_modules/pify": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", - "dev": true + "dev": true, + "engines": { + "node": ">=0.10.0" + } }, - "process-nextick-args": { + "node_modules/process-nextick-args": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", "dev": true }, - "punycode": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", - "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", - "dev": true + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dev": true, + "engines": { + "node": ">=6" + } }, - "q": { + "node_modules/q": { "version": "1.5.1", "resolved": "https://registry.npmjs.org/q/-/q-1.5.1.tgz", "integrity": "sha512-kV/CThkXo6xyFEZUugw/+pIOywXcDbFYgSct5cT3gqlbkBE1SJdwy6UQoZvodiWF/ckQLZyDE/Bu1M6gVu5lVw==", - "dev": true + "dev": true, + "engines": { + "node": ">=0.6.0", + "teleport": ">=0.2.0" + } }, - "quick-lru": { + "node_modules/quick-lru": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-4.0.1.tgz", "integrity": "sha512-ARhCpm70fzdcvNQfPoy49IaanKkTlRWF2JMzqhcJbhSFRZv7nPTvZJdcY7301IPmvW+/p0RgIWnQDLJxifsQ7g==", - "dev": true + "dev": true, + "engines": { + "node": ">=8" + } }, - "read-pkg": { + "node_modules/read-pkg": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz", "integrity": "sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==", "dev": true, - "requires": { + "dependencies": { "@types/normalize-package-data": "^2.4.0", "normalize-package-data": "^2.5.0", "parse-json": "^5.0.0", "type-fest": "^0.6.0" }, - "dependencies": { - "hosted-git-info": { - "version": "2.8.9", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", - "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==", - "dev": true - }, - "normalize-package-data": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", - "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", - "dev": true, - "requires": { - "hosted-git-info": "^2.1.4", - "resolve": "^1.10.0", - "semver": "2 || 3 || 4 || 5", - "validate-npm-package-license": "^3.0.1" - } - }, - "semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", - "dev": true - }, - "type-fest": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.6.0.tgz", - "integrity": "sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==", - "dev": true - } + "engines": { + "node": ">=8" } }, - "read-pkg-up": { + "node_modules/read-pkg-up": { "version": "7.0.1", "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-7.0.1.tgz", "integrity": "sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg==", "dev": true, - "requires": { + "dependencies": { "find-up": "^4.1.0", "read-pkg": "^5.2.0", "type-fest": "^0.8.1" }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/read-pkg-up/node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/read-pkg-up/node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, "dependencies": { - "find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", - "dev": true, - "requires": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" - } - }, - "locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", - "dev": true, - "requires": { - "p-locate": "^4.1.0" - } - }, - "p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "dev": true, - "requires": { - "p-try": "^2.0.0" - } - }, - "p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", - "dev": true, - "requires": { - "p-limit": "^2.2.0" - } - }, - "p-try": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", - "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", - "dev": true - }, - "type-fest": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", - "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", - "dev": true - } + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/read-pkg-up/node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/read-pkg-up/node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/read-pkg-up/node_modules/p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/read-pkg-up/node_modules/type-fest": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", + "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/read-pkg/node_modules/hosted-git-info": { + "version": "2.8.9", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", + "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==", + "dev": true + }, + "node_modules/read-pkg/node_modules/normalize-package-data": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", + "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", + "dev": true, + "dependencies": { + "hosted-git-info": "^2.1.4", + "resolve": "^1.10.0", + "semver": "2 || 3 || 4 || 5", + "validate-npm-package-license": "^3.0.1" + } + }, + "node_modules/read-pkg/node_modules/semver": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", + "dev": true, + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/read-pkg/node_modules/type-fest": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.6.0.tgz", + "integrity": "sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==", + "dev": true, + "engines": { + "node": ">=8" } }, - "readable-stream": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", - "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", "dev": true, - "requires": { + "dependencies": { "inherits": "^2.0.3", "string_decoder": "^1.1.1", "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" } }, - "redent": { + "node_modules/redent": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/redent/-/redent-3.0.0.tgz", "integrity": "sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==", "dev": true, - "requires": { + "dependencies": { "indent-string": "^4.0.0", "strip-indent": "^3.0.0" + }, + "engines": { + "node": ">=8" } }, - "require-directory": { + "node_modules/require-directory": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", - "dev": true + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } }, - "resolve": { - "version": "1.22.1", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz", - "integrity": "sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==", + "node_modules/resolve": { + "version": "1.22.8", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", + "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==", "dev": true, - "requires": { - "is-core-module": "^2.9.0", + "dependencies": { + "is-core-module": "^2.13.0", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "resolve-dir": { + "node_modules/resolve-dir": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/resolve-dir/-/resolve-dir-1.0.1.tgz", "integrity": "sha512-R7uiTjECzvOsWSfdM0QKFNBVFcK27aHOUwdvK53BcW8zqnGdYp0Fbj82cy54+2A4P2tFM22J5kRfe1R+lM/1yg==", "dev": true, - "requires": { + "dependencies": { "expand-tilde": "^2.0.0", "global-modules": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" } }, - "resolve-from": { + "node_modules/resolve-from": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", - "dev": true + "dev": true, + "engines": { + "node": ">=8" + } }, - "resolve-global": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/resolve-global/-/resolve-global-1.0.0.tgz", - "integrity": "sha512-zFa12V4OLtT5XUX/Q4VLvTfBf+Ok0SPc1FNGM/z9ctUdiU618qwKpWnd0CHs3+RqROfyEg/DhuHbMWYqcgljEw==", + "node_modules/resolve-global": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/resolve-global/-/resolve-global-2.0.0.tgz", + "integrity": "sha512-gnAQ0Q/KkupGkuiMyX4L0GaBV8iFwlmoXsMtOz+DFTaKmHhOO/dSlP1RMKhpvHv/dh6K/IQkowGJBqUG0NfBUw==", "dev": true, - "requires": { - "global-dirs": "^0.1.1" + "dependencies": { + "global-directory": "^4.0.1" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "restore-cursor": { + "node_modules/restore-cursor": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", "dev": true, - "requires": { + "dependencies": { "onetime": "^5.1.0", "signal-exit": "^3.0.2" + }, + "engines": { + "node": ">=8" } }, - "run-async": { + "node_modules/run-async": { "version": "2.4.1", "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz", "integrity": "sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ==", - "dev": true + "dev": true, + "engines": { + "node": ">=0.12.0" + } }, - "rxjs": { - "version": "7.5.7", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.5.7.tgz", - "integrity": "sha512-z9MzKh/UcOqB3i20H6rtrlaE/CgjLOvheWK/9ILrbhROGTweAi1BaFsTT9FbwZi5Trr1qNRs+MXkhmR06awzQA==", + "node_modules/rxjs": { + "version": "7.8.1", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz", + "integrity": "sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==", "dev": true, - "requires": { + "dependencies": { "tslib": "^2.1.0" } }, - "safe-buffer": { + "node_modules/safe-buffer": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "dev": true + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] }, - "safer-buffer": { + "node_modules/safer-buffer": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", "dev": true }, - "semver": { - "version": "7.3.7", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz", - "integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==", + "node_modules/semver": { + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz", + "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==", "dev": true, - "requires": { + "dependencies": { "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/set-function-length": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.1.tgz", + "integrity": "sha512-j4t6ccc+VsKwYHso+kElc5neZpjtq9EnRICFZtWyBsLojhmeF/ZBd/elqm22WJh/BziDe/SBiOeAt0m2mfLD0g==", + "dev": true, + "peer": true, + "dependencies": { + "define-data-property": "^1.1.2", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.3", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" } }, - "shebang-command": { + "node_modules/shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", "dev": true, - "requires": { + "dependencies": { "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" } }, - "shebang-regex": { + "node_modules/shebang-regex": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "dev": true + "dev": true, + "engines": { + "node": ">=8" + } }, - "signal-exit": { + "node_modules/signal-exit": { "version": "3.0.7", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", "dev": true }, - "source-map": { + "node_modules/source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true + "dev": true, + "engines": { + "node": ">=0.10.0" + } }, - "spdx-correct": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.1.tgz", - "integrity": "sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w==", + "node_modules/spdx-correct": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.2.0.tgz", + "integrity": "sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==", "dev": true, - "requires": { + "dependencies": { "spdx-expression-parse": "^3.0.0", "spdx-license-ids": "^3.0.0" } }, - "spdx-exceptions": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz", - "integrity": "sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==", + "node_modules/spdx-exceptions": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.5.0.tgz", + "integrity": "sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w==", "dev": true }, - "spdx-expression-parse": { + "node_modules/spdx-expression-parse": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", "dev": true, - "requires": { + "dependencies": { "spdx-exceptions": "^2.1.0", "spdx-license-ids": "^3.0.0" } }, - "spdx-license-ids": { - "version": "3.0.12", - "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.12.tgz", - "integrity": "sha512-rr+VVSXtRhO4OHbXUiAF7xW3Bo9DuuF6C5jH+q/x15j2jniycgKbxU09Hr0WqlSLUs4i4ltHGXqTe7VHclYWyA==", + "node_modules/spdx-license-ids": { + "version": "3.0.17", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.17.tgz", + "integrity": "sha512-sh8PWc/ftMqAAdFiBu6Fy6JUOYjqDJBJvIhpfDMyHrr0Rbp5liZqd4TjtQ/RgfLjKFZb+LMx5hpml5qOWy0qvg==", "dev": true }, - "split": { + "node_modules/split": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/split/-/split-1.0.1.tgz", "integrity": "sha512-mTyOoPbrivtXnwnIxZRFYRrPNtEFKlpB2fvjSnCQUiAA6qAZzqwna5envK4uk6OIeP17CsdF3rSBGYVBsU0Tkg==", "dev": true, - "requires": { + "dependencies": { "through": "2" + }, + "engines": { + "node": "*" } }, - "split2": { + "node_modules/split2": { "version": "3.2.2", "resolved": "https://registry.npmjs.org/split2/-/split2-3.2.2.tgz", "integrity": "sha512-9NThjpgZnifTkJpzTZ7Eue85S49QwpNhZTq6GRJwObb6jnLFNGB7Qm73V5HewTROPyxD0C29xqmaI68bQtV+hg==", "dev": true, - "requires": { + "dependencies": { "readable-stream": "^3.0.0" } }, - "standard-version": { + "node_modules/standard-version": { "version": "9.5.0", "resolved": "https://registry.npmjs.org/standard-version/-/standard-version-9.5.0.tgz", "integrity": "sha512-3zWJ/mmZQsOaO+fOlsa0+QK90pwhNd042qEcw6hKFNoLFs7peGyvPffpEBbK/DSGPbyOvli0mUIFv5A4qTjh2Q==", "dev": true, - "requires": { + "dependencies": { "chalk": "^2.4.2", "conventional-changelog": "3.1.25", "conventional-changelog-config-spec": "2.1.0", @@ -6669,407 +4353,565 @@ "stringify-package": "^1.0.1", "yargs": "^16.0.0" }, + "bin": { + "standard-version": "bin/cli.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/standard-version/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/standard-version/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/standard-version/node_modules/cliui": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "dev": true, + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" + } + }, + "node_modules/standard-version/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/standard-version/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true + }, + "node_modules/standard-version/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/standard-version/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/standard-version/node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/standard-version/node_modules/wrap-ansi/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/standard-version/node_modules/wrap-ansi/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/standard-version/node_modules/wrap-ansi/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/standard-version/node_modules/yargs": { + "version": "16.2.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", + "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "dev": true, "dependencies": { - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "requires": { - "color-convert": "^1.9.0" - } - }, - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - } - }, - "cliui": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", - "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", - "dev": true, - "requires": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.0", - "wrap-ansi": "^7.0.0" - } - }, - "color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, - "requires": { - "color-name": "1.1.3" - } - }, - "color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", - "dev": true - }, - "has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", - "dev": true - }, - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - }, - "yargs": { - "version": "16.2.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", - "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", - "dev": true, - "requires": { - "cliui": "^7.0.2", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.0", - "y18n": "^5.0.5", - "yargs-parser": "^20.2.2" - } - } + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" + }, + "engines": { + "node": ">=10" } }, - "string_decoder": { + "node_modules/string_decoder": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", "dev": true, - "requires": { + "dependencies": { "safe-buffer": "~5.2.0" } }, - "string-width": { + "node_modules/string-width": { "version": "4.2.3", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", "dev": true, - "requires": { + "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" } }, - "stringify-package": { + "node_modules/stringify-package": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/stringify-package/-/stringify-package-1.0.1.tgz", "integrity": "sha512-sa4DUQsYciMP1xhKWGuFM04fB0LG/9DlluZoSVywUMRNvzid6XucHK0/90xGxRoHrAaROrcHK1aPKaijCtSrhg==", + "deprecated": "This module is not used anymore, and has been replaced by @npmcli/package-json", "dev": true }, - "strip-ansi": { + "node_modules/strip-ansi": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "dev": true, - "requires": { + "dependencies": { "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" } }, - "strip-bom": { + "node_modules/strip-bom": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", - "dev": true + "dev": true, + "engines": { + "node": ">=8" + } }, - "strip-final-newline": { + "node_modules/strip-final-newline": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", - "dev": true + "dev": true, + "engines": { + "node": ">=6" + } }, - "strip-indent": { + "node_modules/strip-indent": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-3.0.0.tgz", "integrity": "sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==", "dev": true, - "requires": { + "dependencies": { "min-indent": "^1.0.0" + }, + "engines": { + "node": ">=8" } }, - "strip-json-comments": { + "node_modules/strip-json-comments": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", - "dev": true + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } }, - "supports-color": { + "node_modules/supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "dev": true, - "requires": { + "dependencies": { "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" } }, - "supports-preserve-symlinks-flag": { + "node_modules/supports-preserve-symlinks-flag": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", - "dev": true + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } }, - "text-extensions": { + "node_modules/text-extensions": { "version": "1.9.0", "resolved": "https://registry.npmjs.org/text-extensions/-/text-extensions-1.9.0.tgz", "integrity": "sha512-wiBrwC1EhBelW12Zy26JeOUkQ5mRu+5o8rpsJk5+2t+Y5vE7e842qtZDQ2g1NpX/29HdyFeJ4nSIhI47ENSxlQ==", - "dev": true + "dev": true, + "engines": { + "node": ">=0.10" + } }, - "through": { + "node_modules/through": { "version": "2.3.8", "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==", "dev": true }, - "through2": { + "node_modules/through2": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/through2/-/through2-4.0.2.tgz", "integrity": "sha512-iOqSav00cVxEEICeD7TjLB1sueEL+81Wpzp2bY17uZjZN0pWZPuo4suZ/61VujxmqSGFfgOcNuTZ85QJwNZQpw==", "dev": true, - "requires": { + "dependencies": { "readable-stream": "3" } }, - "tmp": { + "node_modules/tmp": { "version": "0.0.33", "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", "dev": true, - "requires": { + "dependencies": { "os-tmpdir": "~1.0.2" + }, + "engines": { + "node": ">=0.6.0" } }, - "to-regex-range": { + "node_modules/to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", "dev": true, - "requires": { + "dependencies": { "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" } }, - "trim-newlines": { + "node_modules/trim-newlines": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-3.0.1.tgz", "integrity": "sha512-c1PTsA3tYrIsLGkJkzHF+w9F2EyxfXGo4UyJc4pFL++FMjnq0HJS69T3M7d//gKrFKwy429bouPescbjecU+Zw==", - "dev": true + "dev": true, + "engines": { + "node": ">=8" + } }, - "ts-node": { - "version": "10.9.1", - "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.1.tgz", - "integrity": "sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==", - "dev": true, - "requires": { - "@cspotcode/source-map-support": "^0.8.0", - "@tsconfig/node10": "^1.0.7", - "@tsconfig/node12": "^1.0.7", - "@tsconfig/node14": "^1.0.0", - "@tsconfig/node16": "^1.0.2", - "acorn": "^8.4.1", - "acorn-walk": "^8.1.1", - "arg": "^4.1.0", - "create-require": "^1.1.0", - "diff": "^4.0.1", - "make-error": "^1.1.1", - "v8-compile-cache-lib": "^3.0.1", - "yn": "3.1.1" - } - }, - "tslib": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", - "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==", + "node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", "dev": true }, - "type-fest": { + "node_modules/type-fest": { "version": "0.21.3", "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", - "dev": true + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } }, - "typedarray": { + "node_modules/typedarray": { "version": "0.0.6", "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", "integrity": "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==", "dev": true }, - "typescript": { - "version": "4.8.4", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.8.4.tgz", - "integrity": "sha512-QCh+85mCy+h0IGff8r5XWzOVSbBO+KfeYrMQh7NJ58QujwcE22u+NUSmUxqF+un70P9GXKxa2HCNiTTMJknyjQ==", - "dev": true + "node_modules/typescript": { + "version": "5.3.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.3.3.tgz", + "integrity": "sha512-pXWcraxM0uxAS+tN0AG/BF2TyqmHO014Z070UsJ+pFvYuRSq8KH8DmWpnbXe0pEPDHXZV3FcAbJkijJ5oNEnWw==", + "dev": true, + "peer": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/uglify-js": { + "version": "3.17.4", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.17.4.tgz", + "integrity": "sha512-T9q82TJI9e/C1TAxYvfb16xO120tMVFZrGA3f9/P4424DNu6ypK103y0GPFVa17yotwSyZW5iYXgjYHkGrJW/g==", + "dev": true, + "optional": true, + "bin": { + "uglifyjs": "bin/uglifyjs" + }, + "engines": { + "node": ">=0.8.0" + } }, - "uglify-js": { - "version": "3.17.3", - "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.17.3.tgz", - "integrity": "sha512-JmMFDME3iufZnBpyKL+uS78LRiC+mK55zWfM5f/pWBJfpOttXAqYfdDGRukYhJuyRinvPVAtUhvy7rlDybNtFg==", + "node_modules/undici-types": { + "version": "5.26.5", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", + "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", "dev": true, - "optional": true + "peer": true }, - "universalify": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", - "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==", - "dev": true + "node_modules/unicorn-magic": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/unicorn-magic/-/unicorn-magic-0.1.0.tgz", + "integrity": "sha512-lRfVq8fE8gz6QMBuDM6a+LO3IAzTi05H6gCVaUpir2E1Rwpo4ZUog45KpNXKC/Mn3Yb9UDuHumeFTo9iV/D9FQ==", + "dev": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/universalify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "dev": true, + "engines": { + "node": ">= 10.0.0" + } }, - "uri-js": { + "node_modules/uri-js": { "version": "4.4.1", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", "dev": true, - "requires": { + "dependencies": { "punycode": "^2.1.0" } }, - "util-deprecate": { + "node_modules/util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", "dev": true }, - "v8-compile-cache-lib": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", - "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", - "dev": true - }, - "validate-npm-package-license": { + "node_modules/validate-npm-package-license": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", "dev": true, - "requires": { + "dependencies": { "spdx-correct": "^3.0.0", "spdx-expression-parse": "^3.0.0" } }, - "wcwidth": { + "node_modules/wcwidth": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz", "integrity": "sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==", "dev": true, - "requires": { + "dependencies": { "defaults": "^1.0.3" } }, - "which": { + "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", "dev": true, - "requires": { + "dependencies": { "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" } }, - "word-wrap": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.4.tgz", - "integrity": "sha512-2V81OA4ugVo5pRo46hAoD2ivUJx8jXmWXfUkY4KFNw0hEptvN0QfH3K4nHiwzGeKl5rFKedV48QVoqYavy4YpA==", - "dev": true + "node_modules/word-wrap": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } }, - "wordwrap": { + "node_modules/wordwrap": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", "integrity": "sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==", "dev": true }, - "wrap-ansi": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "node_modules/wrap-ansi": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", + "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", "dev": true, - "requires": { + "peer": true, + "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=8" } }, - "wrappy": { + "node_modules/wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", "dev": true }, - "xtend": { + "node_modules/xtend": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", - "dev": true + "dev": true, + "engines": { + "node": ">=0.4" + } }, - "y18n": { + "node_modules/y18n": { "version": "5.0.8", "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", - "dev": true + "dev": true, + "engines": { + "node": ">=10" + } }, - "yallist": { + "node_modules/yallist": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", "dev": true }, - "yaml": { - "version": "1.10.2", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", - "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==", - "dev": true - }, - "yargs": { - "version": "17.6.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.6.0.tgz", - "integrity": "sha512-8H/wTDqlSwoSnScvV2N/JHfLWOKuh5MVla9hqLjK3nsfyy6Y4kDSYSvkU5YCUEPOSnRXfIyx3Sq+B/IWudTo4g==", + "node_modules/yargs": { + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", "dev": true, - "requires": { + "dependencies": { "cliui": "^8.0.1", "escalade": "^3.1.1", "get-caller-file": "^2.0.5", "require-directory": "^2.1.1", "string-width": "^4.2.3", "y18n": "^5.0.5", - "yargs-parser": "^21.0.0" + "yargs-parser": "^21.1.1" }, - "dependencies": { - "yargs-parser": { - "version": "21.1.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", - "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", - "dev": true - } + "engines": { + "node": ">=12" } }, - "yargs-parser": { + "node_modules/yargs-parser": { "version": "20.2.9", "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", - "dev": true + "dev": true, + "engines": { + "node": ">=10" + } }, - "yn": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", - "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", - "dev": true + "node_modules/yargs/node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "dev": true, + "engines": { + "node": ">=12" + } }, - "yocto-queue": { + "node_modules/yocto-queue": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", - "dev": true + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "tools/conventional-changelog-tf-a": { + "version": "2.11.0", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "conventional-changelog-conventionalcommits": "^4.6.1", + "execa": "^5.1.1", + "lodash": "^4.17.21", + "q": "^1.5.1" + } } } } diff --git a/package.json b/package.json index 1c557fda..0908528f 100644 --- a/package.json +++ b/package.json @@ -1,23 +1,24 @@ { "name": "trusted-firmware-a", - "version": "2.10.0", + "version": "2.12.0", "license": "BSD-3-Clause", + "type": "module", "private": true, "scripts": { - "postinstall": "husky install", + "prepare": "husky", "release": "standard-version" }, "engines": { - "node": ">=16.0.0" + "node": ">=20" }, "devDependencies": { - "@commitlint/cli": "^16.1.0", - "@commitlint/config-conventional": "^16.0.0", - "@commitlint/cz-commitlint": "^16.1.0", - "commitizen": "^4.2.4", + "@commitlint/cli": "^19.0.0", + "@commitlint/config-conventional": "^19.0.0", + "@commitlint/cz-commitlint": "^19.0.0", + "commitizen": "^4.3.0", "conventional-changelog-tf-a": "file:tools/conventional-changelog-tf-a", - "husky": "^7.0.4", + "husky": "^9.0.11", "js-yaml": "^4.1.0", - "standard-version": "^9.3.2" + "standard-version": "^9.5.0" } } diff --git a/plat/allwinner/common/include/sunxi_private.h b/plat/allwinner/common/include/sunxi_private.h index 6a386574..b9ca3f60 100644 --- a/plat/allwinner/common/include/sunxi_private.h +++ b/plat/allwinner/common/include/sunxi_private.h @@ -58,4 +58,12 @@ static inline void sunxi_prepare_dtb(void *fdt) } #endif +#ifdef PLAT_sun50i_h616 +void sunxi_soc_fdt_fixup(void *dtb); +#else +static inline void sunxi_soc_fdt_fixup(void *dtb) +{ +} +#endif + #endif /* SUNXI_PRIVATE_H */ diff --git a/plat/allwinner/common/sunxi_bl31_setup.c b/plat/allwinner/common/sunxi_bl31_setup.c index a32124a1..263083bb 100644 --- a/plat/allwinner/common/sunxi_bl31_setup.c +++ b/plat/allwinner/common/sunxi_bl31_setup.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2022, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2017-2024, Arm Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -14,7 +14,6 @@ #include <arch_helpers.h> #include <common/debug.h> #include <common/fdt_fixup.h> -#include <common/fdt_wrappers.h> #include <drivers/arm/gicv2.h> #include <drivers/console.h> #include <drivers/generic_delay_timer.h> @@ -186,8 +185,6 @@ void bl31_plat_runtime_setup(void) { /* Change the DTB if the configuration requires so. */ sunxi_prepare_dtb(fdt); - - console_switch_state(CONSOLE_FLAG_RUNTIME); } entry_point_info_t *bl31_plat_get_next_image_ep_info(uint32_t type) diff --git a/plat/allwinner/common/sunxi_prepare_dtb.c b/plat/allwinner/common/sunxi_prepare_dtb.c index 66af35ab..0f689744 100644 --- a/plat/allwinner/common/sunxi_prepare_dtb.c +++ b/plat/allwinner/common/sunxi_prepare_dtb.c @@ -34,6 +34,8 @@ void sunxi_prepare_dtb(void *fdt) } #endif + sunxi_soc_fdt_fixup(fdt); + if (sunxi_psci_is_scpi()) { ret = fdt_add_cpu_idle_states(fdt, sunxi_idle_states); if (ret < 0) { diff --git a/plat/allwinner/sun50i_a64/platform.mk b/plat/allwinner/sun50i_a64/platform.mk index e3c7c529..cced7f0b 100644 --- a/plat/allwinner/sun50i_a64/platform.mk +++ b/plat/allwinner/sun50i_a64/platform.mk @@ -4,6 +4,10 @@ # SPDX-License-Identifier: BSD-3-Clause # +# To report supported idle states +# this has to be defined before allwinner-common.mk +SUNXI_AMEND_DTB := 1 + # The differences between the platform are covered by the include files. include plat/allwinner/common/allwinner-common.mk diff --git a/plat/allwinner/sun50i_a64/platform_defaults.mk b/plat/allwinner/sun50i_a64/platform_defaults.mk new file mode 100644 index 00000000..5b61029c --- /dev/null +++ b/plat/allwinner/sun50i_a64/platform_defaults.mk @@ -0,0 +1,9 @@ +# +# Copyright (c) 2024, ARM Limited and Contributors. All rights reserved. +# +# SPDX-License-Identifier: BSD-3-Clause +# + +# LTO has to be enabled on this platform due to memory constraints +ENABLE_LTO := 1 + diff --git a/plat/allwinner/sun50i_h616/platform.mk b/plat/allwinner/sun50i_h616/platform.mk index de494a2f..6f44e8c2 100644 --- a/plat/allwinner/sun50i_h616/platform.mk +++ b/plat/allwinner/sun50i_h616/platform.mk @@ -18,5 +18,8 @@ ifeq (${SUNXI_PSCI_USE_SCPI}, 1) $(error "H616 does not support SCPI PSCI ops") endif -BL31_SOURCES += drivers/allwinner/axp/axp805.c \ +BL31_SOURCES += common/fdt_wrappers.c \ + drivers/allwinner/axp/axp805.c \ drivers/allwinner/sunxi_rsb.c \ + drivers/mentor/i2c/mi2cv.c \ + ${AW_PLAT}/${PLAT}/sunxi_h616_dtb.c diff --git a/plat/allwinner/sun50i_h616/sunxi_h616_dtb.c b/plat/allwinner/sun50i_h616/sunxi_h616_dtb.c new file mode 100644 index 00000000..58bad539 --- /dev/null +++ b/plat/allwinner/sun50i_h616/sunxi_h616_dtb.c @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2024, ARM Limited. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + * + * Amend the device tree to adjust the L2 cache size, which is different + * between the revisions of the H616 chips: earlier versions have 256 KB of L2, + * later versions 1 MB. + * Read the cache ID registers and adjust the size and number of sets entries + * in the L2 cache DT node. + */ + +#include <common/fdt_wrappers.h> +#include <lib/utils_def.h> +#include <libfdt.h> + +#define CACHE_L1D 0x0 +#define CACHE_L1I 0x1 +#define CACHE_L2U 0x2 + +#define CCSIDR_SETS_SHIFT 13 +#define CCSIDR_SETS_MASK GENMASK(14, 0) +#define CCSIDR_ASSOC_SHIFT 3 +#define CCSIDR_ASSOC_MASK GENMASK(9, 0) +#define CCSIDR_LSIZE_SHIFT 0 +#define CCSIDR_LSIZE_MASK GENMASK(2, 0) + +static uint32_t armv8_get_ccsidr(unsigned int sel) +{ + uint32_t reg; + + __asm__ volatile ("msr CSSELR_EL1, %0\n" :: "r" (sel)); + __asm__ volatile ("mrs %0, CCSIDR_EL1\n" : "=r" (reg)); + + return reg; +} + +void sunxi_soc_fdt_fixup(void *dtb) +{ + int node = fdt_path_offset(dtb, "/cpus/cpu@0"); + uint32_t phandle, ccsidr, cell; + int sets, line_size, assoc; + int ret; + + if (node < 0) { + return; + } + + ret = fdt_read_uint32(dtb, node, "next-level-cache", &phandle); + if (ret != 0) { + return; + } + + node = fdt_node_offset_by_phandle(dtb, phandle); + if (node < 0) { + return; + } + + ccsidr = armv8_get_ccsidr(CACHE_L2U); + sets = ((ccsidr >> CCSIDR_SETS_SHIFT) & CCSIDR_SETS_MASK) + 1; + line_size = 16U << ((ccsidr >> CCSIDR_LSIZE_SHIFT) & CCSIDR_LSIZE_MASK); + assoc = ((ccsidr >> CCSIDR_ASSOC_SHIFT) & CCSIDR_ASSOC_MASK) + 1; + + cell = cpu_to_fdt32(sets); + fdt_setprop(dtb, node, "cache-sets", &cell, sizeof(cell)); + + cell = cpu_to_fdt32(line_size); + fdt_setprop(dtb, node, "cache-line-size", &cell, sizeof(cell)); + + cell = cpu_to_fdt32(sets * assoc * line_size); + fdt_setprop(dtb, node, "cache-size", &cell, sizeof(cell)); +} diff --git a/plat/allwinner/sun50i_h616/sunxi_power.c b/plat/allwinner/sun50i_h616/sunxi_power.c index dd6ebba9..cab7e464 100644 --- a/plat/allwinner/sun50i_h616/sunxi_power.c +++ b/plat/allwinner/sun50i_h616/sunxi_power.c @@ -10,97 +10,254 @@ #include <arch_helpers.h> #include <common/debug.h> +#include <common/fdt_wrappers.h> #include <drivers/allwinner/axp.h> #include <drivers/allwinner/sunxi_rsb.h> +#include <drivers/mentor/mi2cv.h> #include <lib/mmio.h> +#include <libfdt.h> #include <sunxi_cpucfg.h> #include <sunxi_def.h> #include <sunxi_mmap.h> #include <sunxi_private.h> -#define AXP305_I2C_ADDR 0x36 -#define AXP305_HW_ADDR 0x745 -#define AXP305_RT_ADDR 0x3a +static uint16_t pmic_bus_addr; +static uint8_t rsb_rt_addr; + +static bool is_using_rsb(void) +{ + return rsb_rt_addr != 0; +} static enum pmic_type { UNKNOWN, AXP305, + AXP313, + AXP717, } pmic; +static uint8_t get_rsb_rt_address(uint16_t hw_addr) +{ + switch (hw_addr) { + case 0x3a3: return 0x2d; + case 0x745: return 0x3a; + } + + return 0; +} + int axp_read(uint8_t reg) { - return rsb_read(AXP305_RT_ADDR, reg); + uint8_t val; + int ret; + + if (is_using_rsb()) { + return rsb_read(rsb_rt_addr, reg); + } + + ret = i2c_write(pmic_bus_addr, 0, 0, ®, 1); + if (ret == 0) { + ret = i2c_read(pmic_bus_addr, 0, 0, &val, 1); + } + if (ret) { + ERROR("PMIC: Cannot read PMIC register %02x\n", reg); + return ret; + } + + return val; } int axp_write(uint8_t reg, uint8_t val) { - return rsb_write(AXP305_RT_ADDR, reg, val); + int ret; + + if (is_using_rsb()) { + return rsb_write(rsb_rt_addr, reg, val); + } + + ret = i2c_write(pmic_bus_addr, reg, 1, &val, 1); + if (ret) { + ERROR("PMIC: Cannot write PMIC register %02x\n", reg); + } + + return ret; } -static int rsb_init(void) +static int rsb_init(int rsb_hw_addr) { int ret; ret = rsb_init_controller(); - if (ret) + if (ret) { return ret; + } /* Switch to the recommended 3 MHz bus clock. */ ret = rsb_set_bus_speed(SUNXI_OSC24M_CLK_IN_HZ, 3000000); - if (ret) + if (ret) { return ret; + } /* Initiate an I2C transaction to switch the PMIC to RSB mode. */ ret = rsb_set_device_mode(AXP20X_MODE_RSB << 16 | AXP20X_MODE_REG << 8); - if (ret) + if (ret) { return ret; + } /* Associate the 8-bit runtime address with the 12-bit bus address. */ - ret = rsb_assign_runtime_address(AXP305_HW_ADDR, AXP305_RT_ADDR); - if (ret) + ret = rsb_assign_runtime_address(rsb_hw_addr, rsb_rt_addr); + if (ret) { return ret; + } - return axp_check_id(); + return 0; } -int sunxi_pmic_setup(uint16_t socid, const void *fdt) +static int pmic_bus_init(uint16_t socid, uint16_t rsb_hw_addr) { int ret; - INFO("PMIC: Probing AXP305 on RSB\n"); - - ret = sunxi_init_platform_r_twi(socid, true); + ret = sunxi_init_platform_r_twi(socid, is_using_rsb()); if (ret) { INFO("Could not init platform bus: %d\n", ret); + pmic = UNKNOWN; return ret; } - ret = rsb_init(); + if (is_using_rsb()) { + ret = rsb_init(rsb_hw_addr); + if (ret) { + pmic = UNKNOWN; + return ret; + } + } else { + /* initialise mi2cv driver */ + i2c_init((void *)SUNXI_R_I2C_BASE); + } + + return 0; +} + +int sunxi_pmic_setup(uint16_t socid, const void *fdt) +{ + int node, parent, ret; + uint32_t reg; + + node = fdt_node_offset_by_compatible(fdt, 0, "x-powers,axp806"); + if (node >= 0) { + pmic = AXP305; + } + + if (pmic == UNKNOWN) { + node = fdt_node_offset_by_compatible(fdt, 0, "x-powers,axp313a"); + if (node >= 0) { + pmic = AXP313; + } + } + + if (pmic == UNKNOWN) { + node = fdt_node_offset_by_compatible(fdt, 0, "x-powers,axp717"); + if (node >= 0) { + pmic = AXP717; + } + } + + if (pmic == UNKNOWN) { + INFO("PMIC: No known PMIC in DT, skipping setup.\n"); + return -ENODEV; + } + + if (fdt_read_uint32(fdt, node, "reg", ®)) { + ERROR("PMIC: PMIC DT node does not contain reg property.\n"); + return -EINVAL; + } + + pmic_bus_addr = reg; + parent = fdt_parent_offset(fdt, node); + ret = fdt_node_check_compatible(fdt, parent, "allwinner,sun8i-a23-rsb"); + if (ret == 0) { + rsb_rt_addr = get_rsb_rt_address(pmic_bus_addr); + if (rsb_rt_addr == 0) { + ERROR("PMIC: no mapping for RSB address 0x%x\n", + pmic_bus_addr); + return -EINVAL; + } + } + + INFO("Probing for PMIC on %s:\n", is_using_rsb() ? "RSB" : "I2C"); + + ret = pmic_bus_init(socid, pmic_bus_addr); if (ret) { - INFO("Could not init RSB: %d\n", ret); return ret; } - pmic = AXP305; - axp_setup_regulators(fdt); + ret = axp_read(0x03); + switch (ret & 0xcf) { + case 0x40: /* AXP305 */ + if (pmic == AXP305) { + INFO("PMIC: found AXP305, setting up regulators\n"); + axp_setup_regulators(fdt); + } else { + pmic = UNKNOWN; + } + break; + case 0x48: /* AXP1530 */ + case 0x4b: /* AXP313A */ + case 0x4c: /* AXP313B */ + if (pmic == AXP313) { + INFO("PMIC: found AXP313\n"); + /* no regulators to set up */ + } else { + pmic = UNKNOWN; + } + break; + case 0xcf: /* version reg not implemented on AXP717 */ + if (pmic == AXP717) { + INFO("PMIC: found AXP717\n"); + /* no regulators to set up, U-Boot takes care of this */ + } else { + pmic = UNKNOWN; + } + break; + } - /* Switch the PMIC back to I2C mode. */ - ret = axp_write(AXP20X_MODE_REG, AXP20X_MODE_I2C); - if (ret) - return ret; + if (is_using_rsb()) { + /* Switch the PMIC back to I2C mode. */ + return rsb_write(rsb_rt_addr, AXP20X_MODE_REG, AXP20X_MODE_I2C); + } + + if (pmic == UNKNOWN) { + INFO("Incompatible or unknown PMIC found.\n"); + return -ENODEV; + } return 0; } void sunxi_power_down(void) { + int ret; + + if (pmic == UNKNOWN) { + return; + } + + /* Re-initialise after rich OS might have used it. */ + ret = pmic_bus_init(SUNXI_SOC_H616, pmic_bus_addr); + if (ret) { + return; + } + switch (pmic) { case AXP305: - /* Re-initialise after rich OS might have used it. */ - sunxi_init_platform_r_twi(SUNXI_SOC_H616, true); - rsb_init(); - axp_power_off(); + axp_setbits(0x32, BIT(7)); + break; + case AXP313: + axp_setbits(0x1a, BIT(7)); + break; + case AXP717: + axp_setbits(0x27, BIT(0)); break; default: break; diff --git a/plat/amd/versal2/aarch64/common.c b/plat/amd/versal2/aarch64/common.c new file mode 100644 index 00000000..0e46edc9 --- /dev/null +++ b/plat/amd/versal2/aarch64/common.c @@ -0,0 +1,152 @@ +/* + * Copyright (c) 2021-2022, Arm Limited and Contributors. All rights reserved. + * Copyright (c) 2018-2022, Xilinx, Inc. All rights reserved. + * Copyright (c) 2022-2024, Advanced Micro Devices, Inc. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <common/debug.h> +#include <common/runtime_svc.h> +#include <drivers/generic_delay_timer.h> +#include <lib/mmio.h> +#include <lib/xlat_tables/xlat_tables_v2.h> +#include <plat/common/platform.h> + +#include <def.h> +#include <plat_common.h> +#include <plat_ipi.h> +#include <plat_private.h> + +uint32_t platform_id, platform_version; + +/* + * Table of regions to map using the MMU. + * This doesn't include TZRAM as the 'mem_layout' argument passed to + * configure_mmu_elx() will give the available subset of that, + */ +const mmap_region_t plat_mmap[] = { + MAP_REGION_FLAT(DEVICE0_BASE, DEVICE0_SIZE, MT_DEVICE | MT_RW | MT_SECURE), + MAP_REGION_FLAT(DEVICE1_BASE, DEVICE1_SIZE, MT_DEVICE | MT_RW | MT_SECURE), + MAP_REGION_FLAT(DEVICE2_BASE, DEVICE2_SIZE, MT_DEVICE | MT_RW | MT_SECURE), + MAP_REGION_FLAT(CRF_BASE, CRF_SIZE, MT_DEVICE | MT_RW | MT_SECURE), + MAP_REGION_FLAT(IPI_BASE, IPI_SIZE, MT_DEVICE | MT_RW | MT_SECURE), +#if TRANSFER_LIST + MAP_REGION_FLAT(FW_HANDOFF_BASE, FW_HANDOFF_BASE + FW_HANDOFF_SIZE, + MT_MEMORY | MT_RW | MT_NS), +#endif + { 0 } +}; + +const mmap_region_t *plat_get_mmap(void) +{ + return plat_mmap; +} + +/* For saving cpu clock for certain platform */ +uint32_t cpu_clock; + +const char *board_name_decode(void) +{ + const char *platform; + + switch (platform_id) { + case SPP: + platform = "IPP"; + break; + case EMU: + platform = "EMU"; + break; + case SILICON: + platform = "Silicon"; + break; + case QEMU: + platform = "QEMU"; + break; + default: + platform = "Unknown"; + } + + return platform; +} + +void board_detection(void) +{ + uint32_t version_type; + + version_type = mmio_read_32(PMC_TAP_VERSION); + platform_id = FIELD_GET(PLATFORM_MASK, version_type); + platform_version = FIELD_GET(PLATFORM_VERSION_MASK, version_type); + + if (platform_id == QEMU_COSIM) { + platform_id = QEMU; + } + + /* Make sure that console is setup to see this message */ + VERBOSE("Platform id: %d version: %d.%d\n", platform_id, + platform_version / 10U, platform_version % 10U); +} + +uint32_t get_uart_clk(void) +{ + uint32_t uart_clock = 0; + + switch (platform_id) { + case SPP: + case SPP_MMD: + uart_clock = cpu_clock; + break; + case EMU: + case EMU_MMD: + uart_clock = 25000000; + break; + case QEMU: + /* Random values now */ + uart_clock = 25000000; + break; + case SILICON: + uart_clock = 100000000; + break; + default: + panic(); + } + + return uart_clock; +} + +void config_setup(void) +{ + uint32_t val; + uintptr_t crl_base, iou_scntrs_base, psx_base; + + crl_base = CRL; + iou_scntrs_base = IOU_SCNTRS; + psx_base = PSX_CRF; + + /* Reset for system timestamp generator in FPX */ + mmio_write_32(psx_base + PSX_CRF_RST_TIMESTAMP_OFFSET, 0); + + /* Global timer init - Program time stamp reference clk */ + val = mmio_read_32(crl_base + CRL_TIMESTAMP_REF_CTRL_OFFSET); + val |= CRL_APB_TIMESTAMP_REF_CTRL_CLKACT_BIT; + mmio_write_32(crl_base + CRL_TIMESTAMP_REF_CTRL_OFFSET, val); + + /* Clear reset of timestamp reg */ + mmio_write_32(crl_base + CRL_RST_TIMESTAMP_OFFSET, 0); + + /* Program freq register in System counter and enable system counter. */ + mmio_write_32(iou_scntrs_base + IOU_SCNTRS_BASE_FREQ_OFFSET, + cpu_clock); + mmio_write_32(iou_scntrs_base + IOU_SCNTRS_COUNTER_CONTROL_REG_OFFSET, + IOU_SCNTRS_CONTROL_EN); + + generic_delay_timer_init(); + + /* Configure IPI data */ + soc_ipi_config_table_init(); +} + +uint32_t plat_get_syscnt_freq2(void) +{ + return cpu_clock; +} diff --git a/plat/amd/versal2/aarch64/helpers.S b/plat/amd/versal2/aarch64/helpers.S new file mode 100644 index 00000000..580bb764 --- /dev/null +++ b/plat/amd/versal2/aarch64/helpers.S @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2018-2021, Arm Limited and Contributors. All rights reserved. + * Copyright (c) 2018-2022, Xilinx, Inc. All rights reserved. + * Copyright (c) 2022-2024, Advanced Micro Devices, Inc. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <arch.h> +#include <asm_macros.S> +#include <drivers/arm/gicv3.h> + +#include <platform_def.h> + + .globl plat_secondary_cold_boot_setup + .globl plat_is_my_cpu_primary + .globl platform_mem_init + .globl plat_my_core_pos + + /* ----------------------------------------------------- + * void plat_secondary_cold_boot_setup (void); + * + * This function performs any platform specific actions + * needed for a secondary cpu after a cold reset e.g + * mark the cpu's presence, mechanism to place it in a + * holding pen etc. + * TODO: Should we read the PSYS register to make sure + * that the request has gone through. + * ----------------------------------------------------- + */ +func plat_secondary_cold_boot_setup + mrs x0, mpidr_el1 + + /* + * There is no sane reason to come out of this wfi. This + * cpu will be powered on and reset by the cpu_on pm api + */ + dsb sy + bl plat_panic_handler +endfunc plat_secondary_cold_boot_setup + +func plat_is_my_cpu_primary + mov x9, x30 + bl plat_my_core_pos + cmp x0, #PRIMARY_CPU + cset x0, eq + ret x9 +endfunc plat_is_my_cpu_primary + + /* ----------------------------------------------------- + * unsigned int plat_my_core_pos(void) + * This function uses the plat_core_pos_by_mpidr() + * definition to get the index of the calling CPU. + * ----------------------------------------------------- + */ +func plat_my_core_pos + mrs x0, mpidr_el1 + b plat_core_pos_by_mpidr +endfunc plat_my_core_pos + + /* --------------------------------------------------------------------- + * We don't need to carry out any memory initialization on platform + * The Secure RAM is accessible straight away. + * --------------------------------------------------------------------- + */ +func platform_mem_init + ret +endfunc platform_mem_init diff --git a/plat/amd/versal2/bl31_setup.c b/plat/amd/versal2/bl31_setup.c new file mode 100644 index 00000000..47d4c2ca --- /dev/null +++ b/plat/amd/versal2/bl31_setup.c @@ -0,0 +1,264 @@ +/* + * Copyright (c) 2018-2020, Arm Limited and Contributors. All rights reserved. + * Copyright (c) 2018-2022, Xilinx, Inc. All rights reserved. + * Copyright (c) 2022-2024, Advanced Micro Devices, Inc. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <assert.h> +#include <errno.h> + +#include <bl31/bl31.h> +#include <common/bl_common.h> +#include <common/debug.h> +#include <drivers/arm/dcc.h> +#include <drivers/arm/pl011.h> +#include <drivers/console.h> +#include <lib/cpus/cpu_ops.h> +#include <lib/mmio.h> +#include <lib/xlat_tables/xlat_tables_v2.h> +#include <plat/common/platform.h> +#include <plat_arm.h> +#include <plat_console.h> +#include <scmi.h> + +#include <def.h> +#include <plat_fdt.h> +#include <plat_private.h> +#include <plat_startup.h> +#include <plat_xfer_list.h> +#include <pm_api_sys.h> +#include <pm_client.h> + +static entry_point_info_t bl32_image_ep_info; +static entry_point_info_t bl33_image_ep_info; + +/* + * Return a pointer to the 'entry_point_info' structure of the next image for + * the security state specified. BL33 corresponds to the non-secure image type + * while BL32 corresponds to the secure image type. A NULL pointer is returned + * if the image does not exist. + */ +entry_point_info_t *bl31_plat_get_next_image_ep_info(uint32_t type) +{ + assert(sec_state_is_valid(type)); + + if (type == NON_SECURE) { + return &bl33_image_ep_info; + } + + return &bl32_image_ep_info; +} + +/* + * Set the build time defaults,if we can't find any config data. + */ +static inline void bl31_set_default_config(void) +{ + bl32_image_ep_info.pc = BL32_BASE; + bl32_image_ep_info.spsr = arm_get_spsr_for_bl32_entry(); +#if defined(SPD_opteed) + /* NS dtb addr passed to optee_os */ + bl32_image_ep_info.args.arg3 = XILINX_OF_BOARD_DTB_ADDR; +#endif + bl33_image_ep_info.pc = plat_get_ns_image_entrypoint(); + bl33_image_ep_info.spsr = SPSR_64(MODE_EL2, MODE_SP_ELX, + DISABLE_ALL_EXCEPTIONS); +} + +/* + * Perform any BL31 specific platform actions. Here is an opportunity to copy + * parameters passed by the calling EL (S-EL1 in BL2 & S-EL3 in BL1) before they + * are lost (potentially). This needs to be done before the MMU is initialized + * so that the memory layout can be used while creating page tables. + */ +void bl31_early_platform_setup2(u_register_t arg0, u_register_t arg1, + u_register_t arg2, u_register_t arg3) +{ + (void)arg0; + (void)arg1; + (void)arg2; + (void)arg3; + uint32_t uart_clock; + int32_t rc; + + board_detection(); + + /* FIXME */ + switch (platform_id) { + case SPP: + switch (platform_version) { + case SPP_PSXC_MMI_V2_0: + cpu_clock = 770000; + break; + case SPP_PSXC_MMI_V3_0: + cpu_clock = 908000; + break; + default: + panic(); + } + break; + case SPP_MMD: + switch (platform_version) { + case SPP_PSXC_ISP_AIE_V2_0: + case SPP_PSXC_MMD_AIE_FRZ_EA: + case SPP_PSXC_MMD_AIE_V3_0: + cpu_clock = 760000; + break; + default: + panic(); + } + break; + case EMU: + case EMU_MMD: + cpu_clock = 112203; + break; + case QEMU: + /* Random values now */ + cpu_clock = 3333333; + break; + case SILICON: + cpu_clock = 100000000; + break; + default: + panic(); + } + + uart_clock = get_uart_clk(); + + setup_console(); + + NOTICE("TF-A running on %s %d.%d\n", board_name_decode(), + platform_version / 10U, platform_version % 10U); + + /* Initialize the platform config for future decision making */ + config_setup(); + + /* + * Do initial security configuration to allow DRAM/device access. On + * Base only DRAM security is programmable (via TrustZone), but + * other platforms might have more programmable security devices + * present. + */ + + /* Populate common information for BL32 and BL33 */ + SET_PARAM_HEAD(&bl32_image_ep_info, PARAM_EP, VERSION_1, 0); + SET_SECURITY_STATE(bl32_image_ep_info.h.attr, SECURE); + SET_PARAM_HEAD(&bl33_image_ep_info, PARAM_EP, VERSION_1, 0); + SET_SECURITY_STATE(bl33_image_ep_info.h.attr, NON_SECURE); + + rc = transfer_list_populate_ep_info(&bl32_image_ep_info, &bl33_image_ep_info); + if (rc == TL_OPS_NON || rc == TL_OPS_CUS) { + NOTICE("BL31: TL not found, using default config\n"); + bl31_set_default_config(); + } + + long rev_var = cpu_get_rev_var(); + + INFO("CPU Revision = 0x%lx\n", rev_var); + INFO("cpu_clock = %dHz, uart_clock = %dHz\n", cpu_clock, uart_clock); + NOTICE("BL31: Executing from 0x%x\n", BL31_BASE); + NOTICE("BL31: Secure code at 0x%lx\n", bl32_image_ep_info.pc); + NOTICE("BL31: Non secure code at 0x%lx\n", bl33_image_ep_info.pc); + +} + +static versal_intr_info_type_el3_t type_el3_interrupt_table[MAX_INTR_EL3]; + +int request_intr_type_el3(uint32_t id, interrupt_type_handler_t handler) +{ + static uint32_t index; + uint32_t i; + + /* Validate 'handler' and 'id' parameters */ + if ((handler == NULL) || (index >= MAX_INTR_EL3)) { + return -EINVAL; + } + + /* Check if a handler has already been registered */ + for (i = 0; i < index; i++) { + if (id == type_el3_interrupt_table[i].id) { + return -EALREADY; + } + } + + type_el3_interrupt_table[index].id = id; + type_el3_interrupt_table[index].handler = handler; + + index++; + + return 0; +} + +static uint64_t rdo_el3_interrupt_handler(uint32_t id, uint32_t flags, + void *handle, void *cookie) +{ + (void)id; + uint32_t intr_id; + uint32_t i; + interrupt_type_handler_t handler = NULL; + + intr_id = plat_ic_get_pending_interrupt_id(); + + for (i = 0; i < MAX_INTR_EL3; i++) { + if (intr_id == type_el3_interrupt_table[i].id) { + handler = type_el3_interrupt_table[i].handler; + } + } + + if (handler != NULL) { + (void)handler(intr_id, flags, handle, cookie); + } + + return 0; +} + +void bl31_platform_setup(void) +{ + prepare_dtb(); + + /* Initialize the gic cpu and distributor interfaces */ + plat_gic_driver_init(); + plat_gic_init(); + + if (platform_id != EMU) { + init_scmi_server(); + } +} + +void bl31_plat_runtime_setup(void) +{ + uint64_t flags = 0; + int32_t rc; + + set_interrupt_rm_flag(flags, NON_SECURE); + rc = register_interrupt_type_handler(INTR_TYPE_EL3, + rdo_el3_interrupt_handler, flags); + if (rc != 0) { + panic(); + } + + console_switch_state(CONSOLE_FLAG_RUNTIME); +} + +/* + * Perform the very early platform specific architectural setup here. + */ +void bl31_plat_arch_setup(void) +{ + const mmap_region_t bl_regions[] = { + MAP_REGION_FLAT(BL31_BASE, BL31_END - BL31_BASE, + MT_MEMORY | MT_RW | MT_SECURE), + MAP_REGION_FLAT(BL_CODE_BASE, BL_CODE_END - BL_CODE_BASE, + MT_CODE | MT_SECURE), + MAP_REGION_FLAT(BL_RO_DATA_BASE, BL_RO_DATA_END - BL_RO_DATA_BASE, + MT_RO_DATA | MT_SECURE), + MAP_REGION_FLAT(SMT_BUFFER_BASE, 0x1000, + MT_DEVICE | MT_RW | MT_NON_CACHEABLE | MT_EXECUTE_NEVER | MT_NS), + {0} + }; + + setup_page_tables(bl_regions, plat_get_mmap()); + enable_mmu(0); +} diff --git a/plat/xilinx/versal_net/versal_net_gicv3.c b/plat/amd/versal2/gicv3.c similarity index 72% rename from plat/xilinx/versal_net/versal_net_gicv3.c rename to plat/amd/versal2/gicv3.c index 2fdef12e..c7b44e16 100644 --- a/plat/xilinx/versal_net/versal_net_gicv3.c +++ b/plat/amd/versal2/gicv3.c @@ -1,7 +1,7 @@ /* * Copyright (c) 2018-2019, Arm Limited and Contributors. All rights reserved. * Copyright (c) 2018-2022, Xilinx, Inc. All rights reserved. - * Copyright (c) 2022-2023, Advanced Micro Devices, Inc. All rights reserved. + * Copyright (c) 2022-2024, Advanced Micro Devices, Inc. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -11,36 +11,36 @@ #include <drivers/arm/gicv3.h> #include <lib/utils.h> #include <plat/common/platform.h> +#include <platform_def.h> #include <plat_private.h> -#include <platform_def.h> /****************************************************************************** * The following functions are defined as weak to allow a platform to override * the way the GICv3 driver is initialised and used. *****************************************************************************/ -#pragma weak plat_versal_net_gic_driver_init -#pragma weak plat_versal_net_gic_init -#pragma weak plat_versal_net_gic_cpuif_enable -#pragma weak plat_versal_net_gic_cpuif_disable -#pragma weak plat_versal_net_gic_pcpu_init -#pragma weak plat_versal_net_gic_redistif_on -#pragma weak plat_versal_net_gic_redistif_off +#pragma weak plat_gic_driver_init +#pragma weak plat_gic_init +#pragma weak plat_gic_cpuif_enable +#pragma weak plat_gic_cpuif_disable +#pragma weak plat_gic_pcpu_init +#pragma weak plat_gic_redistif_on +#pragma weak plat_gic_redistif_off /* The GICv3 driver only needs to be initialized in EL3 */ static uintptr_t rdistif_base_addrs[PLATFORM_CORE_COUNT]; -static const interrupt_prop_t versal_net_interrupt_props[] = { - PLAT_VERSAL_NET_G1S_IRQ_PROPS(INTR_GROUP1S), - PLAT_VERSAL_NET_G0_IRQ_PROPS(INTR_GROUP0) +static const interrupt_prop_t _interrupt_props[] = { + PLAT_G1S_IRQ_PROPS(INTR_GROUP1S), + PLAT_G0_IRQ_PROPS(INTR_GROUP0) }; /* * We save and restore the GICv3 context on system suspend. Allocate the * data in the designated EL3 Secure carve-out memory. */ -static gicv3_redist_ctx_t rdist_ctx __section(".versal_net_el3_tzc_dram"); -static gicv3_dist_ctx_t dist_ctx __section(".versal_net_el3_tzc_dram"); +static gicv3_redist_ctx_t rdist_ctx __section("._el3_tzc_dram"); +static gicv3_dist_ctx_t dist_ctx __section("._el3_tzc_dram"); /* * MPIDR hashing function for translating MPIDRs read from GICR_TYPER register @@ -56,23 +56,23 @@ static gicv3_dist_ctx_t dist_ctx __section(".versal_net_el3_tzc_dram"); * - All CPUs implemented in the system have MPIDR_EL1.MT bit set; * - No CPUs implemented in the system use affinity level 3. */ -static uint32_t versal_net_gicv3_mpidr_hash(u_register_t mpidr) +static uint32_t _gicv3_mpidr_hash(u_register_t mpidr) { mpidr |= (read_mpidr_el1() & MPIDR_MT_MASK); return plat_core_pos_by_mpidr(mpidr); } -static const gicv3_driver_data_t versal_net_gic_data __unused = { +static const gicv3_driver_data_t _gic_data __unused = { .gicd_base = PLAT_GICD_BASE_VALUE, .gicr_base = PLAT_GICR_BASE_VALUE, - .interrupt_props = versal_net_interrupt_props, - .interrupt_props_num = ARRAY_SIZE(versal_net_interrupt_props), + .interrupt_props = _interrupt_props, + .interrupt_props_num = ARRAY_SIZE(_interrupt_props), .rdistif_num = PLATFORM_CORE_COUNT, .rdistif_base_addrs = rdistif_base_addrs, - .mpidr_to_core_pos = versal_net_gicv3_mpidr_hash + .mpidr_to_core_pos = _gicv3_mpidr_hash }; -void __init plat_versal_net_gic_driver_init(void) +void __init plat_gic_driver_init(void) { /* * The GICv3 driver is initialized in EL3 and does not need @@ -81,14 +81,14 @@ void __init plat_versal_net_gic_driver_init(void) * not need GIC interface base addresses to be configured. */ #if IMAGE_BL31 - gicv3_driver_init(&versal_net_gic_data); + gicv3_driver_init(&_gic_data); #endif } /****************************************************************************** - * Versal NET common helper to initialize the GIC. Only invoked by BL31 + * common helper to initialize the GIC. Only invoked by BL31 *****************************************************************************/ -void __init plat_versal_net_gic_init(void) +void __init plat_gic_init(void) { gicv3_distif_init(); gicv3_rdistif_init(plat_my_core_pos()); @@ -96,48 +96,46 @@ void __init plat_versal_net_gic_init(void) } /****************************************************************************** - * Versal NET common helper to enable the GIC CPU interface + * common helper to enable the GIC CPU interface *****************************************************************************/ -void plat_versal_net_gic_cpuif_enable(void) +void plat_gic_cpuif_enable(void) { gicv3_cpuif_enable(plat_my_core_pos()); } /****************************************************************************** - * Versal NET common helper to disable the GIC CPU interface + * common helper to disable the GIC CPU interface *****************************************************************************/ -void plat_versal_net_gic_cpuif_disable(void) +void plat_gic_cpuif_disable(void) { gicv3_cpuif_disable(plat_my_core_pos()); } /****************************************************************************** - * Versal NET common helper to initialize the per-cpu redistributor interface in - * GICv3 + * common helper to initialize the per-cpu redistributor interface in GICv3 *****************************************************************************/ -void plat_versal_net_gic_pcpu_init(void) +void plat_gic_pcpu_init(void) { gicv3_rdistif_init(plat_my_core_pos()); } /****************************************************************************** - * Versal NET common helpers to power GIC redistributor interface + * common helpers to power GIC redistributor interface *****************************************************************************/ -void plat_versal_net_gic_redistif_on(void) +void plat_gic_redistif_on(void) { gicv3_rdistif_on(plat_my_core_pos()); } -void plat_versal_net_gic_redistif_off(void) +void plat_gic_redistif_off(void) { gicv3_rdistif_off(plat_my_core_pos()); } /****************************************************************************** - * Versal NET common helper to save & restore the GICv3 on resume from system - * suspend + * common helper to save & restore the GICv3 on resume from system suspend *****************************************************************************/ -void plat_versal_net_gic_save(void) +void plat_gic_save(void) { /* * If an ITS is available, save its context before @@ -165,7 +163,7 @@ void plat_versal_net_gic_save(void) */ } -void plat_versal_net_gic_resume(void) +void plat_gic_resume(void) { /* Restore the GIC Distributor context */ gicv3_distif_init_restore(&dist_ctx); diff --git a/plat/amd/versal2/include/def.h b/plat/amd/versal2/include/def.h new file mode 100644 index 00000000..f3a79076 --- /dev/null +++ b/plat/amd/versal2/include/def.h @@ -0,0 +1,188 @@ +/* + * Copyright (c) 2022, Arm Limited and Contributors. All rights reserved. + * Copyright (c) 2021-2022, Xilinx, Inc. All rights reserved. + * Copyright (c) 2022-2024, Advanced Micro Devices, Inc. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef DEF_H +#define DEF_H + +#include <plat/arm/common/smccc_def.h> +#include <plat/common/common_def.h> + +#define MAX_INTR_EL3 2 + +/* List all consoles */ +#define VERSAL2_CONSOLE_ID_none 0 +#define VERSAL2_CONSOLE_ID_pl011 1 +#define VERSAL2_CONSOLE_ID_pl011_0 1 +#define VERSAL2_CONSOLE_ID_pl011_1 2 +#define VERSAL2_CONSOLE_ID_dcc 3 +#define VERSAL2_CONSOLE_ID_dtb 4 + +#define CONSOLE_IS(con) (VERSAL2_CONSOLE_ID_ ## con == VERSAL2_CONSOLE) + +/* Runtime console */ +#define RT_CONSOLE_ID_pl011 1 +#define RT_CONSOLE_ID_pl011_0 1 +#define RT_CONSOLE_ID_pl011_1 2 +#define RT_CONSOLE_ID_dcc 3 +#define RT_CONSOLE_ID_dtb 4 + +#define RT_CONSOLE_IS(con) (RT_CONSOLE_ID_ ## con == CONSOLE_RUNTIME) + +/* List all platforms */ +#define SILICON U(0) +#define SPP U(1) +#define EMU U(2) +#define QEMU U(3) +#define SPP_MMD U(5) +#define EMU_MMD U(6) +#define QEMU_COSIM U(7) + +/* For platform detection */ +#define PMC_TAP U(0xF11A0000) +#define PMC_TAP_VERSION (PMC_TAP + 0x4U) +# define PLATFORM_MASK GENMASK(27U, 24U) +# define PLATFORM_VERSION_MASK GENMASK(31U, 28U) + +/* Global timer reset */ +#define PSX_CRF U(0xEC200000) +#define ACPU0_CLK_CTRL U(0x10C) +#define ACPU_CLK_CTRL_CLKACT BIT(25) + +#define RST_APU0_OFFSET U(0x300) +#define RST_APU_COLD_RESET BIT(0) +#define RST_APU_WARN_RESET BIT(4) +#define RST_APU_CLUSTER_COLD_RESET BIT(8) +#define RST_APU_CLUSTER_WARM_RESET BIT(9) + +#define PSX_CRF_RST_TIMESTAMP_OFFSET U(0x33C) + +#define APU_PCLI (0xECB10000ULL) +#define APU_PCLI_CPU_STEP (0x30ULL) +#define APU_PCLI_CLUSTER_CPU_STEP (4ULL * APU_PCLI_CPU_STEP) +#define APU_PCLI_CLUSTER_OFFSET U(0x8000) +#define APU_PCLI_CLUSTER_STEP U(0x1000) +#define PCLI_PREQ_OFFSET U(0x4) +#define PREQ_CHANGE_REQUEST BIT(0) +#define PCLI_PSTATE_OFFSET U(0x8) +#define PCLI_PSTATE_VAL_SET U(0x48) +#define PCLI_PSTATE_VAL_CLEAR U(0x38) + +/* Firmware Image Package */ +#define PRIMARY_CPU U(0) + +#define CORE_0_ISR_WAKE_OFFSET (0x00000020ULL) +#define APU_PCIL_CORE_X_ISR_WAKE_REG(cpu_id) (APU_PCLI + (CORE_0_ISR_WAKE_OFFSET + \ + (APU_PCLI_CPU_STEP * (cpu_id)))) +#define APU_PCIL_CORE_X_ISR_WAKE_MASK (0x00000001U) +#define CORE_0_IEN_WAKE_OFFSET (0x00000028ULL) +#define APU_PCIL_CORE_X_IEN_WAKE_REG(cpu_id) (APU_PCLI + (CORE_0_IEN_WAKE_OFFSET + \ + (APU_PCLI_CPU_STEP * (cpu_id)))) +#define APU_PCIL_CORE_X_IEN_WAKE_MASK (0x00000001U) +#define CORE_0_IDS_WAKE_OFFSET (0x0000002CULL) +#define APU_PCIL_CORE_X_IDS_WAKE_REG(cpu_id) (APU_PCLI + (CORE_0_IDS_WAKE_OFFSET + \ + (APU_PCLI_CPU_STEP * (cpu_id)))) +#define APU_PCIL_CORE_X_IDS_WAKE_MASK (0x00000001U) +#define CORE_0_ISR_POWER_OFFSET (0x00000010ULL) +#define APU_PCIL_CORE_X_ISR_POWER_REG(cpu_id) (APU_PCLI + (CORE_0_ISR_POWER_OFFSET + \ + (APU_PCLI_CPU_STEP * (cpu_id)))) +#define APU_PCIL_CORE_X_ISR_POWER_MASK U(0x00000001) +#define CORE_0_IEN_POWER_OFFSET (0x00000018ULL) +#define APU_PCIL_CORE_X_IEN_POWER_REG(cpu_id) (APU_PCLI + (CORE_0_IEN_POWER_OFFSET + \ + (APU_PCLI_CPU_STEP * (cpu_id)))) +#define APU_PCIL_CORE_X_IEN_POWER_MASK (0x00000001U) +#define CORE_0_IDS_POWER_OFFSET (0x0000001CULL) +#define APU_PCIL_CORE_X_IDS_POWER_REG(cpu_id) (APU_PCLI + (CORE_0_IDS_POWER_OFFSET + \ + (APU_PCLI_CPU_STEP * (cpu_id)))) +#define APU_PCIL_CORE_X_IDS_POWER_MASK (0x00000001U) +#define CORE_PWRDN_EN_BIT_MASK (0x1U) + +/******************************************************************************* + * memory map related constants + ******************************************************************************/ +/* IPP 1.2/SPP 0.9 mapping */ +#define DEVICE0_BASE U(0xE8000000) /* psx, crl, iou */ +#define DEVICE0_SIZE U(0x08000000) +#define DEVICE1_BASE U(0xE2000000) /* gic */ +#define DEVICE1_SIZE U(0x00800000) +#define DEVICE2_BASE U(0xF1000000) /* uart, pmc_tap */ +#define DEVICE2_SIZE U(0x01000000) +#define CRF_BASE U(0xFD1A0000) +#define CRF_SIZE U(0x00600000) +#define IPI_BASE U(0xEB300000) +#define IPI_SIZE U(0x00100000) + +/* CRL */ +#define CRL U(0xEB5E0000) +#define CRL_TIMESTAMP_REF_CTRL_OFFSET U(0x14C) +#define CRL_RST_TIMESTAMP_OFFSET U(0x348) + +#define CRL_APB_TIMESTAMP_REF_CTRL_CLKACT_BIT (1U << 25U) + +/* IOU SCNTRS */ +#define IOU_SCNTRS U(0xEC920000) +#define IOU_SCNTRS_COUNTER_CONTROL_REG_OFFSET U(0) +#define IOU_SCNTRS_BASE_FREQ_OFFSET U(0x20) + +#define IOU_SCNTRS_CONTROL_EN U(1) + +#define APU_CLUSTER0 U(0xECC00000) +#define APU_RVBAR_L_0 U(0x40) +#define APU_RVBAR_H_0 U(0x44) +#define APU_CLUSTER_STEP U(0x100000) + +#define SLCR_OSPI_QSPI_IOU_AXI_MUX_SEL U(0xF1060504) +#define PMXC_IOU_SLCR_SRAM_CSR U(0xF106104C) +#define PMXC_IOU_SLCR_PHY_RESET U(0xF1061050) +#define PMXC_IOU_SLCR_TX_RX_CONFIG_RDY U(0xF1061054) +#define PMXC_CRP_RST_UFS U(0xF1260340) + +/******************************************************************************* + * IRQ constants + ******************************************************************************/ +#define IRQ_SEC_PHY_TIMER U(29) + +/******************************************************************************* + * UART related constants + ******************************************************************************/ +#define UART0_BASE U(0xF1920000) +#define UART1_BASE U(0xF1930000) + +#define UART_BAUDRATE 115200 + +#if CONSOLE_IS(pl011) || CONSOLE_IS(dtb) +#define UART_BASE UART0_BASE +# define UART_TYPE CONSOLE_PL011 +#elif CONSOLE_IS(pl011_1) +#define UART_BASE UART1_BASE +# define UART_TYPE CONSOLE_PL011 +#elif CONSOLE_IS(dcc) +# define UART_BASE 0x0 +# define UART_TYPE CONSOLE_DCC +#elif CONSOLE_IS(none) +# define UART_TYPE CONSOLE_NONE +#else +# error "invalid VERSAL2_CONSOLE" +#endif + +/* Runtime console */ +#if defined(CONSOLE_RUNTIME) +#if RT_CONSOLE_IS(pl011) || RT_CONSOLE_IS(dtb) +# define RT_UART_BASE UART0_BASE +# define RT_UART_TYPE CONSOLE_PL011 +#elif RT_CONSOLE_IS(pl011_1) +# define RT_UART_BASE UART1_BASE +# define RT_UART_TYPE CONSOLE_PL011 +#elif RT_CONSOLE_IS(dcc) +# define RT_UART_BASE 0x0 +# define RT_UART_TYPE CONSOLE_DCC +#else +# error "invalid CONSOLE_RUNTIME" +#endif +#endif + +#endif /* DEF_H */ diff --git a/plat/amd/versal2/include/plat_ipi.h b/plat/amd/versal2/include/plat_ipi.h new file mode 100644 index 00000000..503ec1fe --- /dev/null +++ b/plat/amd/versal2/include/plat_ipi.h @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2022, Xilinx, Inc. All rights reserved. + * Copyright (c) 2022-2024, Advanced Micro Devices, Inc. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +/* Versal Gen 2 IPI management enums and defines */ + +#ifndef PLAT_IPI_H +#define PLAT_IPI_H + +#include <stdint.h> + +#include <ipi.h> + +/********************************************************************* + * IPI agent IDs macros + ********************************************************************/ +#define IPI_ID_PMC 1U +#define IPI_ID_APU 2U +#define IPI_ID_RPU0 3U +#define IPI_ID_RPU1 4U +#define IPI_ID_3 5U +#define IPI_ID_4 6U +#define IPI_ID_5 7U +#define IPI_ID_MAX 8U + +/********************************************************************* + * IPI message buffers + ********************************************************************/ +#define IPI_BUFFER_BASEADDR (0xEB3F0000U) + +#define IPI_LOCAL_ID IPI_ID_APU +#define IPI_REMOTE_ID IPI_ID_PMC + +#define IPI_BUFFER_LOCAL_BASE (IPI_BUFFER_BASEADDR + (IPI_LOCAL_ID * 0x200U)) +#define IPI_BUFFER_REMOTE_BASE (IPI_BUFFER_BASEADDR + (IPI_REMOTE_ID * 0x200U)) + +#define IPI_BUFFER_TARGET_LOCAL_OFFSET (IPI_LOCAL_ID * 0x40U) +#define IPI_BUFFER_TARGET_REMOTE_OFFSET (IPI_REMOTE_ID * 0x40U) + +#define IPI_BUFFER_MAX_WORDS 8 + +#define IPI_BUFFER_REQ_OFFSET 0x0U +#define IPI_BUFFER_RESP_OFFSET 0x20U + +/********************************************************************* + * Platform specific IPI API declarations + ********************************************************************/ + +/* Configure IPI table */ +extern void soc_ipi_config_table_init(void); + +/******************************************************************************* + * IPI registers and bitfields + ******************************************************************************/ +#define IPI0_REG_BASE (0xEB330000U) +#define IPI0_TRIG_BIT (1 << 2) +#define PMC_IPI_TRIG_BIT (1 << 1) +#define IPI1_REG_BASE (0xEB340000U) +#define IPI1_TRIG_BIT (1 << 3) +#define IPI2_REG_BASE (0xEB350000U) +#define IPI2_TRIG_BIT (1 << 4) +#define IPI3_REG_BASE (0xEB360000U) +#define IPI3_TRIG_BIT (1 << 5) +#define IPI4_REG_BASE (0xEB370000U) +#define IPI4_TRIG_BIT (1 << 6) +#define IPI5_REG_BASE (0xEB380000U) +#define IPI5_TRIG_BIT (1 << 7) + +#endif /* PLAT_IPI_H */ diff --git a/plat/amd/versal2/include/plat_macros.S b/plat/amd/versal2/include/plat_macros.S new file mode 100644 index 00000000..d20f693d --- /dev/null +++ b/plat/amd/versal2/include/plat_macros.S @@ -0,0 +1,118 @@ +/* + * Copyright (c) 2018, Arm Limited and Contributors. All rights reserved. + * Copyright (c) 2021-2022, Xilinx, Inc. All rights reserved. + * Copyright (c) 2022-2024, Advanced Micro Devices, Inc. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef PLAT_MACROS_S +#define PLAT_MACROS_S + +#include <drivers/arm/gic_common.h> +#include <drivers/arm/gicv2.h> +#include <drivers/arm/gicv3.h> + +#include "../include/platform_def.h" + +.section .rodata.gic_reg_name, "aS" +/* Applicable only to GICv2 and GICv3 with SRE disabled (legacy mode) */ +gicc_regs: + .asciz "gicc_hppir", "gicc_ahppir", "gicc_ctlr", "" + +/* Applicable only to GICv3 with SRE enabled */ +icc_regs: + .asciz "icc_hppir0_el1", "icc_hppir1_el1", "icc_ctlr_el3", "" + +/* Registers common to both GICv2 and GICv3 */ +gicd_pend_reg: + .asciz "gicd_ispendr regs (Offsets 0x200 - 0x278)\n Offset:\t\t\tvalue\n" +newline: + .asciz "\n" +spacer: + .asciz ":\t\t0x" + + /* --------------------------------------------- + * The below utility macro prints out relevant GIC + * registers whenever an unhandled exception is + * taken in BL31 on platform. + * Expects: GICD base in x16, GICC base in x17 + * Clobbers: x0 - x10, sp + * --------------------------------------------- + */ + .macro _print_gic_regs + /* Check for GICv3 system register access */ + mrs x7, id_aa64pfr0_el1 + ubfx x7, x7, #ID_AA64PFR0_GIC_SHIFT, #ID_AA64PFR0_GIC_WIDTH + cmp x7, #1 + b.ne print_gicv2 + + /* Check for SRE enable */ + mrs x8, ICC_SRE_EL3 + tst x8, #ICC_SRE_SRE_BIT + b.eq print_gicv2 + + /* Load the icc reg list to x6 */ + adr x6, icc_regs + /* Load the icc regs to gp regs used by str_in_crash_buf_print */ + mrs x8, ICC_HPPIR0_EL1 + mrs x9, ICC_HPPIR1_EL1 + mrs x10, ICC_CTLR_EL3 + /* Store to the crash buf and print to console */ + bl str_in_crash_buf_print + b print_gic_common + +print_gicv2: + /* Load the gicc reg list to x6 */ + adr x6, gicc_regs + /* Load the gicc regs to gp regs used by str_in_crash_buf_print */ + ldr w8, [x17, #GICC_HPPIR] + ldr w9, [x17, #GICC_AHPPIR] + ldr w10, [x17, #GICC_CTLR] + /* Store to the crash buf and print to console */ + bl str_in_crash_buf_print + +print_gic_common: + /* Print the GICD_ISPENDR regs */ + add x7, x16, #GICD_ISPENDR + adr x4, gicd_pend_reg + bl asm_print_str +gicd_ispendr_loop: + sub x4, x7, x16 + cmp x4, #0x280 + b.eq exit_print_gic_regs + bl asm_print_hex + + adr x4, spacer + bl asm_print_str + + ldr x4, [x7], #8 + bl asm_print_hex + + adr x4, newline + bl asm_print_str + b gicd_ispendr_loop +exit_print_gic_regs: + .endm + + /* --------------------------------------------- + * The below required platform porting macro + * prints out relevant GIC and CCI registers + * whenever an unhandled exception is taken in + * BL31. + * Clobbers: x0 - x10, x16, x17, sp + * --------------------------------------------- + */ + .macro plat_crash_print_regs + /* + * Empty for now to handle more platforms variant. + * Uncomment it when versions are stable + */ + /* + mov_imm x17, PLAT_GICD_BASE_VALUE + mov_imm x16, PLAT_GICR_BASE_VALUE + _print_gic_regs + */ + .endm + +#endif /* PLAT_MACROS_S */ diff --git a/plat/amd/versal2/include/plat_pm_common.h b/plat/amd/versal2/include/plat_pm_common.h new file mode 100644 index 00000000..5e684720 --- /dev/null +++ b/plat/amd/versal2/include/plat_pm_common.h @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2022, Xilinx, Inc. All rights reserved. + * Copyright (c) 2022-2024, Advanced Micro Devices, Inc. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +/* + * Contains platform specific definitions of commonly used macros data types + * for PU Power Management. This file should be common for all PU's. + */ + +#ifndef PLAT_PM_COMMON_H +#define PLAT_PM_COMMON_H + +#include <stdint.h> + +#include <common/debug.h> + +#include "pm_defs.h" + +#define NON_SECURE_FLAG 1U +#define SECURE_FLAG 0U + +#endif /* PLAT_PM_COMMON_H */ diff --git a/plat/amd/versal2/include/plat_private.h b/plat/amd/versal2/include/plat_private.h new file mode 100644 index 00000000..5a2e5bd9 --- /dev/null +++ b/plat/amd/versal2/include/plat_private.h @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2018-2019, Arm Limited and Contributors. All rights reserved. + * Copyright (c) 2021-2022, Xilinx, Inc. All rights reserved. + * Copyright (c) 2022-2024, Advanced Micro Devices, Inc. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef PLAT_PRIVATE_H +#define PLAT_PRIVATE_H + +#include <bl31/interrupt_mgmt.h> +#include <lib/xlat_tables/xlat_tables_v2.h> + +#define SPP_PSXC_MMI_V2_0 U(6) +#define SPP_PSXC_MMI_V3_0 U(8) + +/* MMD */ +#define SPP_PSXC_ISP_AIE_V2_0 U(3) +#define SPP_PSXC_MMD_AIE_FRZ_EA U(4) +#define SPP_PSXC_MMD_AIE_V3_0 U(5) + +typedef struct versal_intr_info_type_el3 { + uint32_t id; + interrupt_type_handler_t handler; +} versal_intr_info_type_el3_t; + +void config_setup(void); +uint32_t get_uart_clk(void); + +const mmap_region_t *plat_get_mmap(void); + +void plat_gic_driver_init(void); +void plat_gic_init(void); +void plat_gic_cpuif_enable(void); +void plat_gic_cpuif_disable(void); +void plat_gic_pcpu_init(void); +void plat_gic_save(void); +void plat_gic_resume(void); +void plat_gic_redistif_on(void); +void plat_gic_redistif_off(void); + +extern uint32_t cpu_clock, platform_id, platform_version; +void board_detection(void); +const char *board_name_decode(void); +uint64_t smc_handler(uint32_t smc_fid, uint64_t x1, uint64_t x2, uint64_t x3, + uint64_t x4, void *cookie, void *handle, uint64_t flags); +int32_t sip_svc_setup_init(void); +/* + * Register handler to specific GIC entrance + * for INTR_TYPE_EL3 type of interrupt + */ +int request_intr_type_el3(uint32_t irq, interrupt_type_handler_t fiq_handler); + +#endif /* PLAT_PRIVATE_H */ diff --git a/plat/amd/versal2/include/platform_def.h b/plat/amd/versal2/include/platform_def.h new file mode 100644 index 00000000..42c9b08a --- /dev/null +++ b/plat/amd/versal2/include/platform_def.h @@ -0,0 +1,141 @@ +/* + * Copyright (c) 2018-2020, Arm Limited and Contributors. All rights reserved. + * Copyright (c) 2021-2022, Xilinx, Inc. All rights reserved. + * Copyright (c) 2022-2024, Advanced Micro Devices, Inc. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef PLATFORM_DEF_H +#define PLATFORM_DEF_H + +#include <arch.h> +#include "def.h" + +/******************************************************************************* + * Generic platform constants + ******************************************************************************/ + +/* Size of cacheable stacks */ +#define PLATFORM_STACK_SIZE U(0x440) + +#define PLATFORM_CLUSTER_COUNT U(4) +#define PLATFORM_CORE_COUNT_PER_CLUSTER U(2) /* 2 CPUs per cluster */ + +#define PLATFORM_CORE_COUNT (PLATFORM_CLUSTER_COUNT * PLATFORM_CORE_COUNT_PER_CLUSTER) + +#define PLAT_MAX_PWR_LVL U(2) +#define PLAT_MAX_RET_STATE U(1) +#define PLAT_MAX_OFF_STATE U(2) + +/******************************************************************************* + * BL31 specific defines. + ******************************************************************************/ +/* + * Put BL31 at the top of the Trusted SRAM (just below the shared memory, if + * present). BL31_BASE is calculated using the current BL31 debug size plus a + * little space for growth. + */ +#ifndef MEM_BASE +# define BL31_BASE U(0xBBF00000) +# define BL31_LIMIT U(0xBC000000) +#else +# define BL31_BASE U(MEM_BASE) +# define BL31_LIMIT U(MEM_BASE + MEM_SIZE) +# ifdef MEM_PROGBITS_SIZE +# define BL31_PROGBITS_LIMIT U(MEM_BASE + \ + MEM_PROGBITS_SIZE) +# endif +#endif + +/******************************************************************************* + * BL32 specific defines. + ******************************************************************************/ +#ifndef BL32_MEM_BASE +# define BL32_BASE U(0x60000000) +# define BL32_LIMIT U(0x80000000) +#else +# define BL32_BASE U(BL32_MEM_BASE) +# define BL32_LIMIT U(BL32_MEM_BASE + BL32_MEM_SIZE) +#endif + +/******************************************************************************* + * BL33 specific defines. + ******************************************************************************/ +#ifndef PRELOADED_BL33_BASE +# define PLAT_ARM_NS_IMAGE_BASE U(0x8000000) +#else +# define PLAT_ARM_NS_IMAGE_BASE U(PRELOADED_BL33_BASE) +#endif + +/******************************************************************************* + * TSP specific defines. + ******************************************************************************/ +#define TSP_SEC_MEM_BASE BL32_BASE +#define TSP_SEC_MEM_SIZE (BL32_LIMIT - BL32_BASE) + +/* ID of the secure physical generic timer interrupt used by the TSP */ +#define ARM_IRQ_SEC_PHY_TIMER U(29) +#define TSP_IRQ_SEC_PHY_TIMER ARM_IRQ_SEC_PHY_TIMER + +/******************************************************************************* + * Platform specific page table and MMU setup constants + ******************************************************************************/ +#define PLAT_DDR_LOWMEM_MAX U(0x80000000) + +#define PLAT_PHY_ADDR_SPACE_SIZE (1ULL << 32U) +#define PLAT_VIRT_ADDR_SPACE_SIZE (1ULL << 32U) + +#define XILINX_OF_BOARD_DTB_MAX_SIZE U(0x200000) + +#define PLAT_OCM_BASE U(0xBBF00000) +#define PLAT_OCM_LIMIT U(0xBC000000) + +#if TRANSFER_LIST +/* + * FIXME: This address should come from firmware before TF-A + * Having this to make sure the transfer list functionality works + */ +#define FW_HANDOFF_BASE U(0x70000000) +#define FW_HANDOFF_SIZE U(0x10000) +#endif + +#define IS_TFA_IN_OCM(x) ((x >= PLAT_OCM_BASE) && (x < PLAT_OCM_LIMIT)) + +#ifndef MAX_MMAP_REGIONS +#if (defined(XILINX_OF_BOARD_DTB_ADDR) && !IS_TFA_IN_OCM(BL31_BASE)) +#define MAX_MMAP_REGIONS 11 +#else +#define MAX_MMAP_REGIONS 10 +#endif +#endif + +#ifndef MAX_XLAT_TABLES +#define MAX_XLAT_TABLES U(12) +#endif + +#define CACHE_WRITEBACK_SHIFT U(6) +#define CACHE_WRITEBACK_GRANULE (1 << CACHE_WRITEBACK_SHIFT) + +#define PLAT_GICD_BASE_VALUE U(0xE2000000) +#define PLAT_GICR_BASE_VALUE U(0xE2060000) + +/* + * Define a list of Group 1 Secure and Group 0 interrupts as per GICv3 + * terminology. On a GICv2 system or mode, the lists will be merged and treated + * as Group 0 interrupts. + */ +#define PLAT_IPI_IRQ 89 +#define PLAT_VERSAL_IPI_IRQ PLAT_IPI_IRQ + +#define PLAT_G1S_IRQ_PROPS(grp) \ + INTR_PROP_DESC(IRQ_SEC_PHY_TIMER, GIC_HIGHEST_SEC_PRIORITY, grp, \ + GIC_INTR_CFG_LEVEL) + +#define PLAT_G0_IRQ_PROPS(grp) \ + INTR_PROP_DESC(PLAT_VERSAL_IPI_IRQ, GIC_HIGHEST_SEC_PRIORITY, grp, \ + GIC_INTR_CFG_EDGE), \ + +#define IRQ_MAX 200U + +#endif /* PLATFORM_DEF_H */ diff --git a/plat/amd/versal2/include/scmi.h b/plat/amd/versal2/include/scmi.h new file mode 100644 index 00000000..761535bd --- /dev/null +++ b/plat/amd/versal2/include/scmi.h @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2023-2024, Advanced Micro Devices, Inc. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef SCMI_H +#define SCMI_H + +#include "versal2-scmi.h" + +#define SIP_SCMI (0xC200ffffU) +#define SMT_BUFFER_BASE 0x7fffe000 + +void init_scmi_server(void); + +size_t plat_scmi_pd_count(unsigned int agent_id); +const char *plat_scmi_pd_get_name(unsigned int agent_id, unsigned int pd_id); +unsigned int plat_scmi_pd_statistics(unsigned int agent_id, unsigned long *pd_id); +unsigned int plat_scmi_pd_get_attributes(unsigned int agent_id, unsigned int pd_id); +unsigned int plat_scmi_pd_get_state(unsigned int agent_id, unsigned int pd_id); +int32_t plat_scmi_pd_set_state(unsigned int agent_id, unsigned int flags, unsigned int pd_id, + unsigned int state); + +#define SCMI_VENDOR "AMD" +#define SCMI_PRODUCT "Versal Gen 2" + +#endif /* DEF_H */ diff --git a/plat/amd/versal2/include/versal2-scmi.h b/plat/amd/versal2/include/versal2-scmi.h new file mode 100644 index 00000000..c08b4b1a --- /dev/null +++ b/plat/amd/versal2/include/versal2-scmi.h @@ -0,0 +1,144 @@ +// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) +/* + * Macros IDs for AMD Versal Gen 2 + * + * Copyright (c) 2023-2024, Advanced Micro Devices, Inc. All rights reserved. + * + * Michal Simek <michal.simek@amd.com> + */ + +#ifndef _VERSAL2_SCMI_H +#define _VERSAL2_SCMI_H + +#define CLK_GEM0_0 0 +#define CLK_GEM0_1 1 +#define CLK_GEM0_2 2 +#define CLK_GEM0_3 3 +#define CLK_GEM0_4 4 +#define CLK_GEM1_0 5 +#define CLK_GEM1_1 6 +#define CLK_GEM1_2 7 +#define CLK_GEM1_3 8 +#define CLK_GEM1_4 9 +#define CLK_SERIAL0_0 10 +#define CLK_SERIAL0_1 11 +#define CLK_SERIAL1_0 12 +#define CLK_SERIAL1_1 13 +#define CLK_UFS0_0 14 +#define CLK_UFS0_1 15 +#define CLK_UFS0_2 16 +#define CLK_USB0_0 17 +#define CLK_USB0_1 18 +#define CLK_USB0_2 19 +#define CLK_USB1_0 20 +#define CLK_USB1_1 21 +#define CLK_USB1_2 22 +#define CLK_MMC0_0 23 +#define CLK_MMC0_1 24 +#define CLK_MMC0_2 25 +#define CLK_MMC1_0 26 +#define CLK_MMC1_1 27 +#define CLK_MMC1_2 28 +#define CLK_TTC0_0 29 +#define CLK_TTC1_0 30 +#define CLK_TTC2_0 31 +#define CLK_TTC3_0 32 +#define CLK_TTC4_0 33 +#define CLK_TTC5_0 34 +#define CLK_TTC6_0 35 +#define CLK_TTC7_0 36 +#define CLK_I2C0_0 37 +#define CLK_I2C1_0 38 +#define CLK_I2C2_0 39 +#define CLK_I2C3_0 40 +#define CLK_I2C4_0 41 +#define CLK_I2C5_0 42 +#define CLK_I2C6_0 43 +#define CLK_I2C7_0 44 +#define CLK_OSPI0_0 45 +#define CLK_QSPI0_0 46 +#define CLK_QSPI0_1 47 +#define CLK_WWDT0_0 48 +#define CLK_WWDT1_0 49 +#define CLK_WWDT2_0 50 +#define CLK_WWDT3_0 51 +#define CLK_ADMA0_0 52 +#define CLK_ADMA0_1 53 +#define CLK_ADMA1_0 54 +#define CLK_ADMA1_1 55 +#define CLK_ADMA2_0 56 +#define CLK_ADMA2_1 57 +#define CLK_ADMA3_0 58 +#define CLK_ADMA3_1 59 +#define CLK_ADMA4_0 60 +#define CLK_ADMA4_1 61 +#define CLK_ADMA5_0 62 +#define CLK_ADMA5_1 63 +#define CLK_ADMA6_0 64 +#define CLK_ADMA6_1 65 +#define CLK_ADMA7_0 66 +#define CLK_ADMA7_1 67 +#define CLK_CAN0_0 68 +#define CLK_CAN0_1 69 +#define CLK_CAN1_0 70 +#define CLK_CAN1_1 71 +#define CLK_CAN2_0 72 +#define CLK_CAN2_1 73 +#define CLK_CAN3_0 74 +#define CLK_CAN3_1 75 +#define CLK_PS_GPIO_0 76 +#define CLK_PMC_GPIO_0 77 +#define CLK_SPI0_0 78 +#define CLK_SPI0_1 79 +#define CLK_SPI1_0 80 +#define CLK_SPI1_1 81 +#define CLK_I3C0_0 82 +#define CLK_I3C1_0 83 +#define CLK_I3C2_0 84 +#define CLK_I3C3_0 85 +#define CLK_I3C4_0 86 +#define CLK_I3C5_0 87 +#define CLK_I3C6_0 88 +#define CLK_I3C7_0 89 + +#define RESET_GEM0_0 0 +#define RESET_GEM1_0 1 +#define RESET_SERIAL0_0 2 +#define RESET_SERIAL1_0 3 +#define RESET_UFS0_0 4 +#define RESET_I2C0_0 5 +#define RESET_I2C1_0 6 +#define RESET_I2C2_0 7 +#define RESET_I2C3_0 8 +#define RESET_I2C4_0 9 +#define RESET_I2C5_0 10 +#define RESET_I2C6_0 11 +#define RESET_I2C7_0 12 +#define RESET_I2C8_0 13 +#define RESET_OSPI0_0 14 +#define RESET_USB0_0 15 +#define RESET_USB0_1 16 +#define RESET_USB0_2 17 +#define RESET_USB1_0 18 +#define RESET_USB1_1 19 +#define RESET_USB1_2 20 +#define RESET_MMC0_0 21 +#define RESET_MMC1_0 22 +#define RESET_SPI0_0 23 +#define RESET_SPI1_0 24 +#define RESET_QSPI0_0 25 +#define RESET_I3C0_0 26 +#define RESET_I3C1_0 27 +#define RESET_I3C2_0 28 +#define RESET_I3C3_0 29 +#define RESET_I3C4_0 30 +#define RESET_I3C5_0 31 +#define RESET_I3C6_0 32 +#define RESET_I3C7_0 33 +#define RESET_I3C8_0 34 +#define RESET_UFSPHY_0 35 + +#define PD_USB0 0 +#define PD_USB1 1 + +#endif /* _VERSAL2_SCMI_H */ diff --git a/plat/amd/versal2/plat_psci.c b/plat/amd/versal2/plat_psci.c new file mode 100644 index 00000000..eab032dd --- /dev/null +++ b/plat/amd/versal2/plat_psci.c @@ -0,0 +1,241 @@ +/* + * Copyright (c) 2018-2020, Arm Limited and Contributors. All rights reserved. + * Copyright (c) 2021-2022, Xilinx, Inc. All rights reserved. + * Copyright (c) 2022-2024, Advanced Micro Devices, Inc. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <assert.h> + +#include <common/debug.h> +#include <common/runtime_svc.h> +#include <lib/mmio.h> +#include <lib/psci/psci.h> +#include <plat/arm/common/plat_arm.h> +#include <plat/common/platform.h> +#include <plat_arm.h> + +#include <plat_private.h> +#include <pm_defs.h> + +#define PM_RET_ERROR_NOFEATURE U(19) +#define ALWAYSTRUE true +#define LINEAR_MODE BIT(1) + +static uintptr_t _sec_entry; + +static void zynqmp_cpu_standby(plat_local_state_t cpu_state) +{ + dsb(); + wfi(); +} + +#define MPIDR_MT_BIT (24) + +static int32_t zynqmp_nopmu_pwr_domain_on(u_register_t mpidr) +{ + uint32_t cpu_id = plat_core_pos_by_mpidr(mpidr) & ~BIT(MPIDR_MT_BIT); + uint32_t cpu = cpu_id % PLATFORM_CORE_COUNT_PER_CLUSTER; + uint32_t cluster = cpu_id / PLATFORM_CORE_COUNT_PER_CLUSTER; + uintptr_t apu_cluster_base = 0, apu_pcli_base, apu_pcli_cluster = 0; + uintptr_t rst_apu_cluster = PSX_CRF + RST_APU0_OFFSET + ((uint64_t)cluster * 0x4U); + + VERBOSE("%s: mpidr: 0x%lx, cpuid: %x, cpu: %x, cluster: %x\n", + __func__, mpidr, cpu_id, cpu, cluster); + + if (cpu_id == -1) { + return PSCI_E_INTERN_FAIL; + } + + if (cluster > 3) { + panic(); + } + + apu_pcli_cluster = APU_PCLI + APU_PCLI_CLUSTER_OFFSET + ((uint64_t)cluster * APU_PCLI_CLUSTER_STEP); + apu_cluster_base = APU_CLUSTER0 + ((uint64_t)cluster * APU_CLUSTER_STEP); + + /* Enable clock */ + mmio_setbits_32(PSX_CRF + ACPU0_CLK_CTRL + ((uint64_t)cluster * 0x4U), ACPU_CLK_CTRL_CLKACT); + + /* Enable cluster states */ + mmio_setbits_32(apu_pcli_cluster + PCLI_PSTATE_OFFSET, PCLI_PSTATE_VAL_SET); + mmio_setbits_32(apu_pcli_cluster + PCLI_PREQ_OFFSET, PREQ_CHANGE_REQUEST); + + /* assert core reset */ + mmio_setbits_32(rst_apu_cluster, ((RST_APU_COLD_RESET|RST_APU_WARN_RESET) << cpu)); + + /* program RVBAR */ + mmio_write_32(apu_cluster_base + APU_RVBAR_L_0 + (cpu << 3), + (uint32_t)_sec_entry); + mmio_write_32(apu_cluster_base + APU_RVBAR_H_0 + (cpu << 3), + _sec_entry >> 32); + + /* de-assert core reset */ + mmio_clrbits_32(rst_apu_cluster, ((RST_APU_COLD_RESET|RST_APU_WARN_RESET) << cpu)); + + /* clear cluster resets */ + mmio_clrbits_32(rst_apu_cluster, RST_APU_CLUSTER_WARM_RESET); + mmio_clrbits_32(rst_apu_cluster, RST_APU_CLUSTER_COLD_RESET); + + apu_pcli_base = APU_PCLI + (APU_PCLI_CPU_STEP * cpu) + + (APU_PCLI_CLUSTER_CPU_STEP * cluster); + + mmio_write_32(apu_pcli_base + PCLI_PSTATE_OFFSET, PCLI_PSTATE_VAL_CLEAR); + mmio_write_32(apu_pcli_base + PCLI_PREQ_OFFSET, PREQ_CHANGE_REQUEST); + + return PSCI_E_SUCCESS; +} + +static void zynqmp_nopmu_pwr_domain_off(const psci_power_state_t *target_state) +{ + plat_gic_cpuif_disable(); +} + +static void __dead2 zynqmp_nopmu_system_reset(void) +{ + while (ALWAYSTRUE) { + wfi(); + } +} + +static int32_t zynqmp_validate_ns_entrypoint(uint64_t ns_entrypoint) +{ + VERBOSE("Validate ns_entry point %lx\n", ns_entrypoint); + + if ((ns_entrypoint) != 0U) { + return PSCI_E_SUCCESS; + } else { + return PSCI_E_INVALID_ADDRESS; + } +} + +static void zynqmp_pwr_domain_on_finish(const psci_power_state_t *target_state) +{ + plat_gic_pcpu_init(); + plat_gic_cpuif_enable(); +} + +static void __dead2 zynqmp_system_off(void) +{ + while (ALWAYSTRUE) { + wfi(); + } +} + +static int32_t zynqmp_validate_power_state(uint32_t power_state, psci_power_state_t *req_state) +{ + return PSCI_E_SUCCESS; +} + +static const struct plat_psci_ops _nopmc_psci_ops = { + .cpu_standby = zynqmp_cpu_standby, + .pwr_domain_on = zynqmp_nopmu_pwr_domain_on, + .pwr_domain_off = zynqmp_nopmu_pwr_domain_off, + .system_reset = zynqmp_nopmu_system_reset, + .validate_ns_entrypoint = zynqmp_validate_ns_entrypoint, + .pwr_domain_on_finish = zynqmp_pwr_domain_on_finish, + .system_off = zynqmp_system_off, + .validate_power_state = zynqmp_validate_power_state, +}; + +/******************************************************************************* + * Export the platform specific power ops. + ******************************************************************************/ +int32_t plat_setup_psci_ops(uintptr_t sec_entrypoint, + const struct plat_psci_ops **psci_ops) +{ + _sec_entry = sec_entrypoint; + + VERBOSE("Setting up entry point %lx\n", _sec_entry); + + *psci_ops = &_nopmc_psci_ops; + + return 0; +} + +int sip_svc_setup_init(void) +{ + return 0; +} + +static int32_t no_pm_ioctl(uint32_t device_id, uint32_t ioctl_id, + uint32_t arg1, uint32_t arg2) +{ + int32_t ret = 0; + VERBOSE("%s: ioctl_id: %x, arg1: %x\n", __func__, ioctl_id, arg1); + + switch (ioctl_id) { + case IOCTL_OSPI_MUX_SELECT: + if ((arg1 == 0) || (arg1 == 1)) { + mmio_clrsetbits_32(SLCR_OSPI_QSPI_IOU_AXI_MUX_SEL, LINEAR_MODE, + (arg1 ? LINEAR_MODE : 0)); + } else { + ret = PM_RET_ERROR_ARGS; + } + break; + case IOCTL_UFS_TXRX_CFGRDY_GET: + ret = (int32_t) mmio_read_32(PMXC_IOU_SLCR_TX_RX_CONFIG_RDY); + break; + case IOCTL_UFS_SRAM_CSR_SEL: + if (arg1 == 1) { + ret = (int32_t) mmio_read_32(PMXC_IOU_SLCR_SRAM_CSR); + } else if (arg1 == 0) { + mmio_write_32(PMXC_IOU_SLCR_SRAM_CSR, arg2); + } + break; + case IOCTL_USB_SET_STATE: + break; + default: + ret = PM_RET_ERROR_NOFEATURE; + break; + } + + return ret; +} + +static uint64_t no_pm_handler(uint32_t smc_fid, uint64_t x1, uint64_t x2, uint64_t x3, + uint64_t x4, void *cookie, void *handle, uint64_t flags) +{ + int32_t ret; + uint32_t arg[4], api_id; + + arg[0] = (uint32_t)x1; + arg[1] = (uint32_t)(x1 >> 32); + arg[2] = (uint32_t)x2; + arg[3] = (uint32_t)(x2 >> 32); + + api_id = smc_fid & FUNCID_NUM_MASK; + VERBOSE("%s: smc_fid: %x, api_id=0x%x\n", __func__, smc_fid, api_id); + + switch (api_id) { + case PM_IOCTL: + { + ret = no_pm_ioctl(arg[0], arg[1], arg[2], arg[3]); + /* Firmware driver expects return code in upper 32 bits and + * status in lower 32 bits. + * status is always SUCCESS(0) for mmio low level register + * r/w calls and return value is the value returned from + * no_pm_ioctl + */ + SMC_RET1(handle, ((uint64_t)ret << 32)); + } + case PM_GET_CHIPID: + { + uint32_t idcode, version_type; + + idcode = mmio_read_32(PMC_TAP); + version_type = mmio_read_32(PMC_TAP_VERSION); + SMC_RET2(handle, ((uint64_t)idcode << 32), version_type); + } + default: + WARN("Unimplemented PM Service Call: 0x%x\n", smc_fid); + SMC_RET1(handle, SMC_UNK); + } +} + +uint64_t smc_handler(uint32_t smc_fid, uint64_t x1, uint64_t x2, uint64_t x3, uint64_t x4, + void *cookie, void *handle, uint64_t flags) +{ + return no_pm_handler(smc_fid, x1, x2, x3, x4, cookie, handle, flags); +} diff --git a/plat/amd/versal2/plat_topology.c b/plat/amd/versal2/plat_topology.c new file mode 100644 index 00000000..07631398 --- /dev/null +++ b/plat/amd/versal2/plat_topology.c @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2018, Arm Limited and Contributors. All rights reserved. + * Copyright (c) 2018-2022, Xilinx, Inc. All rights reserved. + * Copyright (c) 2022, Advanced Micro Devices, Inc. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <common/debug.h> +#include <plat/common/platform.h> +#include <platform_def.h> + +#include <plat_private.h> + +static const uint8_t plat_power_domain_tree_desc[] = { + /* Number of root nodes */ + 1, + /* Number of clusters */ + PLATFORM_CLUSTER_COUNT, + /* Number of children for the first cluster node */ + PLATFORM_CORE_COUNT_PER_CLUSTER, + /* Number of children for the second cluster node */ + PLATFORM_CORE_COUNT_PER_CLUSTER, + /* Number of children for the third cluster node */ + PLATFORM_CORE_COUNT_PER_CLUSTER, + /* Number of children for the fourth cluster node */ + PLATFORM_CORE_COUNT_PER_CLUSTER, +}; + +const uint8_t *plat_get_power_domain_tree_desc(void) +{ + return plat_power_domain_tree_desc; +} + +/******************************************************************************* + * This function implements a part of the critical interface between the psci + * generic layer and the platform that allows the former to query the platform + * to convert an MPIDR to a unique linear index. An error code (-1) is returned + * in case the MPIDR is invalid. + ******************************************************************************/ +int32_t plat_core_pos_by_mpidr(u_register_t mpidr) +{ + uint32_t cluster_id, cpu_id; + + mpidr &= MPIDR_AFFINITY_MASK; + + cluster_id = MPIDR_AFFLVL2_VAL(mpidr); + cpu_id = MPIDR_AFFLVL1_VAL(mpidr); + + if (cluster_id >= PLATFORM_CLUSTER_COUNT) { + return -3; + } + + /* + * Validate cpu_id by checking whether it represents a CPU in + * one of the two clusters present on the platform. + */ + if (cpu_id >= PLATFORM_CORE_COUNT_PER_CLUSTER) { + return -1; + } + + return (cpu_id + (cluster_id * PLATFORM_CORE_COUNT_PER_CLUSTER)); +} diff --git a/plat/amd/versal2/platform.mk b/plat/amd/versal2/platform.mk new file mode 100644 index 00000000..3114976b --- /dev/null +++ b/plat/amd/versal2/platform.mk @@ -0,0 +1,159 @@ +# Copyright (c) 2018-2022, Arm Limited and Contributors. All rights reserved. +# Copyright (c) 2021-2022, Xilinx, Inc. All rights reserved. +# Copyright (c) 2022-2024, Advanced Micro Devices, Inc. All rights reserved. +# +# SPDX-License-Identifier: BSD-3-Clause + +PLAT_PATH := plat/amd/versal2 + +override NEED_BL1 := no +override NEED_BL2 := no + +# A78 Erratum for SoC +ERRATA_A78_AE_1941500 := 1 +ERRATA_A78_AE_1951502 := 1 +ERRATA_A78_AE_2376748 := 1 +ERRATA_A78_AE_2395408 := 1 +ERRATA_ABI_SUPPORT := 1 + +# Platform Supports Armv8.2 extensions +ARM_ARCH_MAJOR := 8 +ARM_ARCH_MINOR := 2 + +override PROGRAMMABLE_RESET_ADDRESS := 1 +PSCI_EXTENDED_STATE_ID := 1 +SEPARATE_CODE_AND_RODATA := 1 +override RESET_TO_BL31 := 1 +PL011_GENERIC_UART := 1 +IPI_CRC_CHECK := 0 +GIC_ENABLE_V4_EXTN := 0 +GICV3_SUPPORT_GIC600 := 1 + +override CTX_INCLUDE_AARCH32_REGS := 0 + +# Platform to support Dynamic XLAT Table by default +override PLAT_XLAT_TABLES_DYNAMIC := 1 +$(eval $(call add_define,PLAT_XLAT_TABLES_DYNAMIC)) + +ifdef MEM_BASE + $(eval $(call add_define,MEM_BASE)) + + ifndef MEM_SIZE + $(error "MEM_BASE defined without MEM_SIZE") + endif + $(eval $(call add_define,MEM_SIZE)) + + ifdef MEM_PROGBITS_SIZE + $(eval $(call add_define,MEM_PROGBITS_SIZE)) + endif +endif + +ifdef BL32_MEM_BASE + $(eval $(call add_define,BL32_MEM_BASE)) + + ifndef BL32_MEM_SIZE + $(error "BL32_MEM_BASE defined without BL32_MEM_SIZE") + endif + $(eval $(call add_define,BL32_MEM_SIZE)) +endif + +ifdef IPI_CRC_CHECK + $(eval $(call add_define,IPI_CRC_CHECK)) +endif + +USE_COHERENT_MEM := 0 +HW_ASSISTED_COHERENCY := 1 + +VERSAL2_CONSOLE ?= pl011 +ifeq (${VERSAL2_CONSOLE}, $(filter ${VERSAL2_CONSOLE},pl011 pl011_0 pl011_1 dcc dtb none)) + else + $(error "Please define VERSAL2_CONSOLE") + endif + +$(eval $(call add_define_val,VERSAL2_CONSOLE,VERSAL2_CONSOLE_ID_${VERSAL2_CONSOLE})) + +# Runtime console in default console in DEBUG build +ifeq ($(DEBUG), 1) +CONSOLE_RUNTIME ?= pl011 +endif + +# Runtime console +ifdef CONSOLE_RUNTIME +ifeq (${CONSOLE_RUNTIME}, $(filter ${CONSOLE_RUNTIME},pl011 pl011_0 pl011_1 dcc dtb)) +$(eval $(call add_define_val,CONSOLE_RUNTIME,RT_CONSOLE_ID_${CONSOLE_RUNTIME})) +else + $(error "Please define CONSOLE_RUNTIME") +endif +endif + + +ifdef XILINX_OF_BOARD_DTB_ADDR +$(eval $(call add_define,XILINX_OF_BOARD_DTB_ADDR)) +endif + +PLAT_INCLUDES := -Iinclude/plat/arm/common/ \ + -Iplat/xilinx/common/include/ \ + -Iplat/xilinx/common/ipi_mailbox_service/ \ + -I${PLAT_PATH}/include/ \ + -Iplat/xilinx/versal/pm_service/ + +# Include GICv3 driver files +include drivers/arm/gic/v3/gicv3.mk +include lib/xlat_tables_v2/xlat_tables.mk +include lib/libfdt/libfdt.mk + +PLAT_BL_COMMON_SOURCES := \ + drivers/arm/dcc/dcc_console.c \ + drivers/delay_timer/delay_timer.c \ + drivers/delay_timer/generic_delay_timer.c \ + ${GICV3_SOURCES} \ + drivers/arm/pl011/aarch64/pl011_console.S \ + plat/common/aarch64/crash_console_helpers.S \ + plat/arm/common/arm_common.c \ + plat/common/plat_gicv3.c \ + ${PLAT_PATH}/aarch64/helpers.S \ + ${PLAT_PATH}/aarch64/common.c \ + ${PLAT_PATH}/plat_topology.c \ + ${XLAT_TABLES_LIB_SRCS} + +BL31_SOURCES += drivers/arm/cci/cci.c \ + lib/cpus/aarch64/cortex_a78_ae.S \ + lib/cpus/aarch64/cortex_a78.S \ + plat/common/plat_psci_common.c \ + drivers/scmi-msg/base.c \ + drivers/scmi-msg/entry.c \ + drivers/scmi-msg/smt.c \ + drivers/scmi-msg/clock.c \ + drivers/scmi-msg/power_domain.c \ + drivers/scmi-msg/reset_domain.c \ + ${PLAT_PATH}/scmi.c + +BL31_SOURCES += ${PLAT_PATH}/plat_psci.c + +BL31_SOURCES += plat/xilinx/common/plat_fdt.c \ + common/fdt_wrappers.c \ + plat/xilinx/common/plat_fdt.c \ + plat/xilinx/common/plat_console.c \ + plat/xilinx/common/plat_startup.c \ + plat/xilinx/common/ipi.c \ + plat/xilinx/common/ipi_mailbox_service/ipi_mailbox_svc.c \ + ${PLAT_PATH}/soc_ipi.c \ + plat/xilinx/common/versal.c \ + ${PLAT_PATH}/bl31_setup.c \ + common/fdt_fixup.c \ + common/fdt_wrappers.c \ + ${LIBFDT_SRCS} \ + ${PLAT_PATH}/sip_svc_setup.c \ + ${PLAT_PATH}/gicv3.c + +ifeq (${ERRATA_ABI_SUPPORT}, 1) +# enable the cpu macros for errata abi interface +CORTEX_A78_AE_H_INC := 1 +$(eval $(call add_define, CORTEX_A78_AE_H_INC)) +endif + +# Enable Handoff protocol using transfer lists +TRANSFER_LIST := 1 + +include lib/transfer_list/transfer_list.mk +BL31_SOURCES += plat/xilinx/common/plat_xfer_list.c diff --git a/plat/amd/versal2/scmi.c b/plat/amd/versal2/scmi.c new file mode 100644 index 00000000..6375df3b --- /dev/null +++ b/plat/amd/versal2/scmi.c @@ -0,0 +1,671 @@ +/* + * Copyright (c) 2023-2024, Advanced Micro Devices, Inc. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <assert.h> +#include <errno.h> +#include <inttypes.h> + +#include <drivers/scmi-msg.h> +#include <drivers/scmi.h> +#include <lib/mmio.h> +#include <lib/utils_def.h> +#include <platform_def.h> +#include <scmi.h> + +#include "plat_private.h" + +#define HIGH (1) +#define LOW (0) + +struct scmi_clk { + unsigned long clock_id; + unsigned long rate; + const char *name; + bool enabled; +}; + +#define CLOCK_CELL(_scmi_id, _id, _name, _init_enabled, _rate) \ + [_scmi_id] = { \ + .clock_id = (_id), \ + .name = (_name), \ + .enabled = (_init_enabled), \ + .rate = (_rate), \ + } + +static struct scmi_clk scmi0_clock[] = { + CLOCK_CELL(CLK_GEM0_0, CLK_GEM0_0, "gem0_pclk", true, 100000000), + CLOCK_CELL(CLK_GEM0_1, CLK_GEM0_1, "gem0_hclk", true, 100000000), + CLOCK_CELL(CLK_GEM0_2, CLK_GEM0_2, "gem0_tx_clk", true, 125000000), + CLOCK_CELL(CLK_GEM0_3, CLK_GEM0_3, "gem0_rx_clk", true, 100000000), + CLOCK_CELL(CLK_GEM0_4, CLK_GEM0_4, "gem0_tsu_clk", true, 100000000), + CLOCK_CELL(CLK_GEM1_0, CLK_GEM1_0, "gem1_pclk", true, 100000000), + CLOCK_CELL(CLK_GEM1_1, CLK_GEM1_1, "gem1_hclk", true, 100000000), + CLOCK_CELL(CLK_GEM1_2, CLK_GEM1_2, "gem1_tx_clk", true, 125000000), + CLOCK_CELL(CLK_GEM1_3, CLK_GEM1_3, "gem1_rx_clk", true, 100000000), + CLOCK_CELL(CLK_GEM1_4, CLK_GEM1_4, "gem1_tsu_clk", true, 100000000), + CLOCK_CELL(CLK_SERIAL0_0, CLK_SERIAL0_0, "uart0_uartclk", true, 100000000), + CLOCK_CELL(CLK_SERIAL0_1, CLK_SERIAL0_1, "uart0_apb_pclk", true, 100000000), + CLOCK_CELL(CLK_SERIAL1_0, CLK_SERIAL1_0, "uart1_uartclk", true, 100000000), + CLOCK_CELL(CLK_SERIAL1_1, CLK_SERIAL1_1, "uart1_apb_pclk", true, 100000000), + CLOCK_CELL(CLK_UFS0_0, CLK_UFS0_0, "ufs_core_clk", true, 100000000), + CLOCK_CELL(CLK_UFS0_1, CLK_UFS0_1, "ufs_phy_clk", true, 26000000), + CLOCK_CELL(CLK_UFS0_2, CLK_UFS0_2, "ufs_ref_pclk", true, 26000000), + CLOCK_CELL(CLK_USB0_0, CLK_USB0_0, "usb0_bus_clk", true, 100000000), + CLOCK_CELL(CLK_USB0_1, CLK_USB0_1, "usb0_ref_clk", true, 100000000), + CLOCK_CELL(CLK_USB0_2, CLK_USB0_2, "usb0_dwc_clk", true, 100000000), + CLOCK_CELL(CLK_USB1_0, CLK_USB1_0, "usb1_bus_clk", true, 100000000), + CLOCK_CELL(CLK_USB1_1, CLK_USB1_1, "usb1_ref_clk", true, 100000000), + CLOCK_CELL(CLK_USB1_2, CLK_USB1_2, "usb1_dwc_clk", true, 100000000), + CLOCK_CELL(CLK_MMC0_0, CLK_MMC0_0, "mmc0_xin_clk", true, 100000000), + CLOCK_CELL(CLK_MMC0_1, CLK_MMC0_1, "mmc0_ahb_clk", true, 100000000), + CLOCK_CELL(CLK_MMC0_2, CLK_MMC0_2, "mmc0_gate_clk", true, 100000000), + CLOCK_CELL(CLK_MMC1_0, CLK_MMC1_0, "mmc1_xin_clk", true, 100000000), + CLOCK_CELL(CLK_MMC1_1, CLK_MMC1_1, "mmc1_ahb_clk", true, 100000000), + CLOCK_CELL(CLK_MMC1_2, CLK_MMC1_2, "mmc1_gate_clk", true, 100000000), + CLOCK_CELL(CLK_TTC0_0, CLK_TTC0_0, "ttc0_clk", true, 100000000), + CLOCK_CELL(CLK_TTC1_0, CLK_TTC1_0, "ttc1_clk", true, 100000000), + CLOCK_CELL(CLK_TTC2_0, CLK_TTC2_0, "ttc2_clk", true, 100000000), + CLOCK_CELL(CLK_TTC3_0, CLK_TTC3_0, "ttc3_clk", true, 100000000), + CLOCK_CELL(CLK_TTC4_0, CLK_TTC4_0, "ttc4_clk", true, 100000000), + CLOCK_CELL(CLK_TTC5_0, CLK_TTC5_0, "ttc5_clk", true, 100000000), + CLOCK_CELL(CLK_TTC6_0, CLK_TTC6_0, "ttc6_clk", true, 100000000), + CLOCK_CELL(CLK_TTC7_0, CLK_TTC7_0, "ttc7_clk", true, 100000000), + CLOCK_CELL(CLK_I2C0_0, CLK_I2C0_0, "i2c0_clk", true, 100000000), + CLOCK_CELL(CLK_I2C1_0, CLK_I2C1_0, "i2c1_clk", true, 100000000), + CLOCK_CELL(CLK_I2C2_0, CLK_I2C2_0, "i2c2_clk", true, 100000000), + CLOCK_CELL(CLK_I2C3_0, CLK_I2C3_0, "i2c3_clk", true, 100000000), + CLOCK_CELL(CLK_I2C4_0, CLK_I2C4_0, "i2c4_clk", true, 100000000), + CLOCK_CELL(CLK_I2C5_0, CLK_I2C5_0, "i2c5_clk", true, 100000000), + CLOCK_CELL(CLK_I2C6_0, CLK_I2C6_0, "i2c6_clk", true, 100000000), + CLOCK_CELL(CLK_I2C7_0, CLK_I2C7_0, "i2c7_clk", true, 100000000), + CLOCK_CELL(CLK_OSPI0_0, CLK_OSPI0_0, "ospi0_clk", true, 100000000), + CLOCK_CELL(CLK_QSPI0_0, CLK_QSPI0_0, "qpsi0_ref_clk", true, 100000000), + CLOCK_CELL(CLK_QSPI0_1, CLK_QSPI0_1, "qspi0_pclk", true, 100000000), + CLOCK_CELL(CLK_WWDT0_0, CLK_WWDT0_0, "wwdt0_clk", true, 100000000), + CLOCK_CELL(CLK_WWDT1_0, CLK_WWDT1_0, "wwdt1_clk", true, 100000000), + CLOCK_CELL(CLK_WWDT2_0, CLK_WWDT2_0, "wwdt2_clk", true, 100000000), + CLOCK_CELL(CLK_WWDT3_0, CLK_WWDT3_0, "wwdt3_clk", true, 100000000), + CLOCK_CELL(CLK_ADMA0_0, CLK_ADMA0_0, "adma0_main_clk", true, 100000000), + CLOCK_CELL(CLK_ADMA0_1, CLK_ADMA0_1, "adma0_apb_clk", true, 100000000), + CLOCK_CELL(CLK_ADMA1_0, CLK_ADMA1_0, "adma1_main_clk", true, 100000000), + CLOCK_CELL(CLK_ADMA1_1, CLK_ADMA1_1, "adma1_apb_clk", true, 100000000), + CLOCK_CELL(CLK_ADMA2_0, CLK_ADMA2_0, "adma2_main_clk", true, 100000000), + CLOCK_CELL(CLK_ADMA2_1, CLK_ADMA2_1, "adma2_apb_clk", true, 100000000), + CLOCK_CELL(CLK_ADMA3_0, CLK_ADMA3_0, "adma3_main_clk", true, 100000000), + CLOCK_CELL(CLK_ADMA3_1, CLK_ADMA3_1, "adma3_apb_clk", true, 100000000), + CLOCK_CELL(CLK_ADMA4_0, CLK_ADMA4_0, "adma4_main_clk", true, 100000000), + CLOCK_CELL(CLK_ADMA4_1, CLK_ADMA4_1, "adma4_apb_clk", true, 100000000), + CLOCK_CELL(CLK_ADMA5_0, CLK_ADMA5_0, "adma5_main_clk", true, 100000000), + CLOCK_CELL(CLK_ADMA5_1, CLK_ADMA5_1, "adma5_apb_clk", true, 100000000), + CLOCK_CELL(CLK_ADMA6_0, CLK_ADMA6_0, "adma6_main_clk", true, 100000000), + CLOCK_CELL(CLK_ADMA6_1, CLK_ADMA6_1, "adma6_apb_clk", true, 100000000), + CLOCK_CELL(CLK_ADMA7_0, CLK_ADMA7_0, "adma7_main_clk", true, 100000000), + CLOCK_CELL(CLK_ADMA7_1, CLK_ADMA7_1, "adma7_apb_clk", true, 100000000), + CLOCK_CELL(CLK_CAN0_0, CLK_CAN0_0, "can0_can_clk", true, 100000000), + CLOCK_CELL(CLK_CAN0_1, CLK_CAN0_1, "can0_axi_clk", true, 100000000), + CLOCK_CELL(CLK_CAN1_0, CLK_CAN1_0, "can1_can_clk", true, 100000000), + CLOCK_CELL(CLK_CAN1_1, CLK_CAN1_1, "can1_axi_clk", true, 100000000), + CLOCK_CELL(CLK_CAN2_0, CLK_CAN2_0, "can2_can_clk", true, 100000000), + CLOCK_CELL(CLK_CAN2_1, CLK_CAN2_1, "can2_axi_clk", true, 100000000), + CLOCK_CELL(CLK_CAN3_0, CLK_CAN3_0, "can3_can_clk", true, 100000000), + CLOCK_CELL(CLK_CAN3_1, CLK_CAN3_1, "can3_axi_clk", true, 100000000), + CLOCK_CELL(CLK_PS_GPIO_0, CLK_PS_GPIO_0, "ps_gpio_clk", true, 100000000), + CLOCK_CELL(CLK_PMC_GPIO_0, CLK_PMC_GPIO_0, "pmc_gpio_clk", true, 100000000), + CLOCK_CELL(CLK_SPI0_0, CLK_SPI0_0, "spi0_ref_clk", true, 100000000), + CLOCK_CELL(CLK_SPI0_1, CLK_SPI0_1, "spi0_pclk", true, 100000000), + CLOCK_CELL(CLK_SPI1_0, CLK_SPI1_0, "spi1_ref_clk", true, 100000000), + CLOCK_CELL(CLK_SPI1_1, CLK_SPI1_1, "spi1_pclk", true, 100000000), + CLOCK_CELL(CLK_I3C0_0, CLK_I3C0_0, "i3c0_clk", true, 100000000), + CLOCK_CELL(CLK_I3C1_0, CLK_I3C1_0, "i3c1_clk", true, 100000000), + CLOCK_CELL(CLK_I3C2_0, CLK_I3C2_0, "i3c2_clk", true, 100000000), + CLOCK_CELL(CLK_I3C3_0, CLK_I3C3_0, "i3c3_clk", true, 100000000), + CLOCK_CELL(CLK_I3C4_0, CLK_I3C4_0, "i3c4_clk", true, 100000000), + CLOCK_CELL(CLK_I3C5_0, CLK_I3C5_0, "i3c5_clk", true, 100000000), + CLOCK_CELL(CLK_I3C6_0, CLK_I3C6_0, "i3c6_clk", true, 100000000), + CLOCK_CELL(CLK_I3C7_0, CLK_I3C7_0, "i3c7_clk", true, 100000000), +}; + +/* + * struct scmi_reset - Data for the exposed reset controller + * @reset_id: Reset identifier in RCC reset driver + * @name: Reset string ID exposed to agent + */ +struct scmi_reset { + unsigned long reset_id; + const char *name; +}; + +#define RESET_CELL(_scmi_id, _id, _name) \ + [_scmi_id] = { \ + .reset_id = (_id), \ + .name = (_name), \ + } + +static struct scmi_reset scmi0_reset[] = { + RESET_CELL(RESET_GEM0_0, RESET_GEM0_0, "gem0"), + RESET_CELL(RESET_GEM1_0, RESET_GEM1_0, "gem1"), + RESET_CELL(RESET_SERIAL0_0, RESET_SERIAL0_0, "serial0"), + RESET_CELL(RESET_SERIAL1_0, RESET_SERIAL1_0, "serial1"), + RESET_CELL(RESET_UFS0_0, RESET_UFS0_0, "ufs0"), + RESET_CELL(RESET_I2C0_0, RESET_I2C0_0, "i2c0"), + RESET_CELL(RESET_I2C1_0, RESET_I2C1_0, "i2c1"), + RESET_CELL(RESET_I2C2_0, RESET_I2C2_0, "i2c2"), + RESET_CELL(RESET_I2C3_0, RESET_I2C3_0, "i2c3"), + RESET_CELL(RESET_I2C4_0, RESET_I2C4_0, "i2c4"), + RESET_CELL(RESET_I2C5_0, RESET_I2C5_0, "i2c5"), + RESET_CELL(RESET_I2C6_0, RESET_I2C6_0, "i2c6"), + RESET_CELL(RESET_I2C7_0, RESET_I2C7_0, "i2c7"), + RESET_CELL(RESET_I2C8_0, RESET_I2C8_0, "i2c8"), + RESET_CELL(RESET_OSPI0_0, RESET_OSPI0_0, "ospi"), + RESET_CELL(RESET_USB0_0, RESET_USB0_0, "usb0_0"), + RESET_CELL(RESET_USB0_1, RESET_USB0_1, "usb0_1"), + RESET_CELL(RESET_USB0_2, RESET_USB0_2, "usb0_2"), + RESET_CELL(RESET_USB1_0, RESET_USB1_0, "usb1_0"), + RESET_CELL(RESET_USB1_1, RESET_USB1_1, "usb1_1"), + RESET_CELL(RESET_USB1_2, RESET_USB1_2, "usb1_2"), + RESET_CELL(RESET_MMC0_0, RESET_MMC0_0, "mmc0"), + RESET_CELL(RESET_MMC1_0, RESET_MMC1_0, "mmc1"), + RESET_CELL(RESET_SPI0_0, RESET_SPI0_0, "spi0"), + RESET_CELL(RESET_SPI1_0, RESET_SPI1_0, "spi1"), + RESET_CELL(RESET_QSPI0_0, RESET_QSPI0_0, "qspi"), + RESET_CELL(RESET_I3C0_0, RESET_I3C0_0, "i3c0"), + RESET_CELL(RESET_I3C1_0, RESET_I3C1_0, "i3c1"), + RESET_CELL(RESET_I3C2_0, RESET_I3C2_0, "i3c2"), + RESET_CELL(RESET_I3C3_0, RESET_I3C3_0, "i3c3"), + RESET_CELL(RESET_I3C4_0, RESET_I3C4_0, "i3c4"), + RESET_CELL(RESET_I3C5_0, RESET_I3C5_0, "i3c5"), + RESET_CELL(RESET_I3C6_0, RESET_I3C6_0, "i3c6"), + RESET_CELL(RESET_I3C7_0, RESET_I3C7_0, "i3c7"), + RESET_CELL(RESET_I3C8_0, RESET_I3C8_0, "i3c8"), + RESET_CELL(RESET_UFSPHY_0, RESET_UFSPHY_0, "ufsphy0"), +}; + +/** + * struct scmi_pd - Data for the exposed power domain controller + * @pd_id: pd identifier in RCC reset driver + * @name: pd string ID exposed to agent + * @state: keep state setting + */ +struct scmi_pd { + unsigned long pd_id; + const char *name; + unsigned int state; +}; + +#define PD_CELL(_scmi_id, _id, _name, _state) \ + [_scmi_id] = { \ + .pd_id = _id, \ + .name = _name, \ + .state = _state, \ + } + +static struct scmi_pd scmi0_pd[] = { + PD_CELL(PD_USB0, PD_USB0, "usb0", 0), + PD_CELL(PD_USB1, PD_USB1, "usb1", 0), +}; + +struct scmi_resources { + struct scmi_clk *clock; + size_t clock_count; + struct scmi_reset *reset; + size_t reset_count; + struct scmi_pd *pd; + size_t pd_count; +}; + +static const struct scmi_resources resources[] = { + [0] = { + .clock = scmi0_clock, + .clock_count = ARRAY_SIZE(scmi0_clock), + .reset = scmi0_reset, + .reset_count = ARRAY_SIZE(scmi0_reset), + .pd = scmi0_pd, + .pd_count = ARRAY_SIZE(scmi0_pd), + }, +}; + +static const struct scmi_resources *find_resource(unsigned int agent_id) +{ + assert(agent_id < ARRAY_SIZE(resources)); + + return &resources[agent_id]; +} + +static struct scmi_clk *clk_find(unsigned int agent_id, unsigned int scmi_id) +{ + const struct scmi_resources *resource = find_resource(agent_id); + size_t n = 0U; + struct scmi_clk *ret = NULL; + + if (resource != NULL) { + for (n = 0U; n < resource->clock_count; n++) { + if (n == scmi_id) { + ret = &resource->clock[n]; + break; + } + } + } + + return ret; +} + +size_t plat_scmi_clock_count(unsigned int agent_id) +{ + const struct scmi_resources *resource = find_resource(agent_id); + size_t ret; + + if (resource == NULL) { + ret = 0U; + } else { + VERBOSE("SCMI: CLK: %d clocks\n", (unsigned int)resource->clock_count); + + ret = resource->clock_count; + } + return ret; +} + +const char *plat_scmi_clock_get_name(unsigned int agent_id, unsigned int scmi_id) +{ + const struct scmi_clk *clock = clk_find(agent_id, scmi_id); + const char *ret; + + if (clock == NULL) { + ret = NULL; + } else { + VERBOSE("SCMI: CLK: id: %d, get_name: %s\n", scmi_id, clock->name); + + ret = clock->name; + } + return ret; +}; + +/* Called by Linux */ +int32_t plat_scmi_clock_rates_array(unsigned int agent_id, unsigned int scmi_id, + unsigned long *array, size_t *nb_elts, + uint32_t start_idx) +{ + const struct scmi_clk *clock = clk_find(agent_id, scmi_id); + + if (clock == NULL) { + return SCMI_NOT_FOUND; + } + + if (start_idx > 0) { + return SCMI_OUT_OF_RANGE; + } + + if (array == NULL) { + *nb_elts = 1U; + } else if (*nb_elts == 1U) { + *array = clock->rate; + VERBOSE("SCMI: CLK: id: %d, clk_name: %s, get_rate %lu\n", + scmi_id, clock->name, *array); + } else { + return SCMI_GENERIC_ERROR; + } + + return SCMI_SUCCESS; +} + +unsigned long plat_scmi_clock_get_rate(unsigned int agent_id, unsigned int scmi_id) +{ + const struct scmi_clk *clock = clk_find(agent_id, scmi_id); + unsigned long ret; + + if ((clock == NULL)) { + ret = SCMI_NOT_FOUND; + } else { + VERBOSE("SCMI: CLK: id: %d, get_rate: %lu\n", scmi_id, clock->rate); + ret = clock->rate; + } + return ret; +} + +int32_t plat_scmi_clock_set_rate(unsigned int agent_id, unsigned int scmi_id, + unsigned long rate) +{ + struct scmi_clk *clock = clk_find(agent_id, scmi_id); + unsigned long ret = UL(SCMI_SUCCESS); + + if ((clock == NULL)) { + ret = SCMI_NOT_FOUND; + } else { + VERBOSE("SCMI: CLK: id: %d, set_rate: %lu\n", scmi_id, rate); + clock->rate = rate; + } + return ret; +} + +int32_t plat_scmi_clock_get_state(unsigned int agent_id, unsigned int scmi_id) +{ + const struct scmi_clk *clock = clk_find(agent_id, scmi_id); + int32_t ret; + + if ((clock == NULL)) { + ret = SCMI_NOT_FOUND; + } else { + VERBOSE("SCMI: CLK: id: %d, get_state: %d\n", scmi_id, clock->enabled); + + if (clock->enabled) { + ret = HIGH; + } else { + ret = LOW; + } + } + return ret; +} + +int32_t plat_scmi_clock_set_state(unsigned int agent_id, unsigned int scmi_id, + bool enable_not_disable) +{ + struct scmi_clk *clock = clk_find(agent_id, scmi_id); + int32_t ret; + + if (clock == NULL) { + ret = SCMI_NOT_FOUND; + } else { + if (enable_not_disable) { + if (!clock->enabled) { + VERBOSE("SCMI: clock: %u enable\n", scmi_id); + clock->enabled = true; + } + } else { + if (clock->enabled) { + VERBOSE("SCMI: clock: %u disable\n", scmi_id); + clock->enabled = false; + } + } + + VERBOSE("SCMI: CLK: id: %d, set_state: %d\n", scmi_id, clock->enabled); + + ret = SCMI_SUCCESS; + } + + return ret; +} + + +/* + * Platform SCMI reset domains + */ +static struct scmi_reset *find_reset(unsigned int agent_id, + unsigned int scmi_id) +{ + const struct scmi_resources *resource = find_resource(agent_id); + size_t n; + + if (resource != NULL) { + for (n = 0U; n < resource->reset_count; n++) { + if (n == scmi_id) { + return &resource->reset[n]; + } + } + } + + return NULL; +} + +const char *plat_scmi_rstd_get_name(unsigned int agent_id, unsigned int scmi_id) +{ + const struct scmi_reset *reset = find_reset(agent_id, scmi_id); + + if (reset == NULL) { + return NULL; + } + + return reset->name; +} + +size_t plat_scmi_rstd_count(unsigned int agent_id) +{ + const struct scmi_resources *resource = find_resource(agent_id); + + if (resource == NULL) { + return 0U; + } + + return resource->reset_count; +} + +int32_t plat_scmi_rstd_autonomous(unsigned int agent_id, unsigned int scmi_id, + uint32_t state) +{ + const struct scmi_reset *reset = find_reset(agent_id, scmi_id); + + if (reset == NULL) { + return SCMI_NOT_FOUND; + } + + /* Supports only reset with context loss */ + if (state != 0U) { + return SCMI_NOT_SUPPORTED; + } + + NOTICE("SCMI reset on ID %lu/%s\n", + reset->reset_id, plat_scmi_rstd_get_name(agent_id, scmi_id)); + + return SCMI_SUCCESS; +} + +int32_t plat_scmi_rstd_set_state(unsigned int agent_id, unsigned int scmi_id, + bool assert_not_deassert) +{ + const struct scmi_reset *reset = find_reset(agent_id, scmi_id); + + if (reset == NULL) { + return SCMI_NOT_FOUND; + } + + if (assert_not_deassert) { + NOTICE("SCMI reset %lu/%s set\n", + reset->reset_id, plat_scmi_rstd_get_name(agent_id, scmi_id)); + + switch (scmi_id) { + case RESET_UFS0_0: + mmio_write_32(PMXC_CRP_RST_UFS, 1); + break; + case RESET_UFSPHY_0: + mmio_write_32(PMXC_IOU_SLCR_PHY_RESET, 1); + break; + default: + break; + } + } else { + NOTICE("SCMI reset %lu/%s release\n", + reset->reset_id, plat_scmi_rstd_get_name(agent_id, scmi_id)); + + switch (scmi_id) { + case RESET_UFS0_0: + mmio_write_32(PMXC_CRP_RST_UFS, 0); + break; + case RESET_UFSPHY_0: + mmio_write_32(PMXC_IOU_SLCR_PHY_RESET, 0); + break; + default: + break; + } + } + + return SCMI_SUCCESS; +} + +/* + * Platform SCMI reset domains + */ +static struct scmi_pd *find_pd(unsigned int agent_id, unsigned int pd_id) +{ + const struct scmi_resources *resource = find_resource(agent_id); + size_t n; + + if (resource != NULL) { + for (n = 0U; n < resource->pd_count; n++) { + if (n == pd_id) { + return &resource->pd[n]; + } + } + } + + return NULL; +} + +size_t plat_scmi_pd_count(unsigned int agent_id) +{ + const struct scmi_resources *resource = find_resource(agent_id); + size_t ret; + + if (resource == NULL) { + ret = 0U; + } else { + ret = resource->pd_count; + + NOTICE("SCMI: PD: %d\n", (unsigned int)ret); + } + return ret; +} + +const char *plat_scmi_pd_get_name(unsigned int agent_id, unsigned int pd_id) +{ + const struct scmi_pd *pd = find_pd(agent_id, pd_id); + + if (pd == NULL) { + return NULL; + } + + return pd->name; +} + +unsigned int plat_scmi_pd_statistics(unsigned int agent_id, unsigned long *pd_id) +{ + return 0U; +} + +unsigned int plat_scmi_pd_get_attributes(unsigned int agent_id, unsigned int pd_id) +{ + return 0U; +} + +unsigned int plat_scmi_pd_get_state(unsigned int agent_id, unsigned int pd_id) +{ + const struct scmi_pd *pd = find_pd(agent_id, pd_id); + + if (pd == NULL) { + return SCMI_NOT_SUPPORTED; + } + + NOTICE("SCMI: PD: get id: %d, state: %x\n", pd_id, pd->state); + + return pd->state; +} + +int32_t plat_scmi_pd_set_state(unsigned int agent_id, unsigned int flags, unsigned int pd_id, + unsigned int state) +{ + struct scmi_pd *pd = find_pd(agent_id, pd_id); + + if (pd == NULL) { + return SCMI_NOT_SUPPORTED; + } + + NOTICE("SCMI: PD: set id: %d, orig state: %x, new state: %x, flags: %x\n", + pd_id, pd->state, state, flags); + + pd->state = state; + + return 0U; +} + + +/* Currently only one channel is supported. Expectation is that channel 0 is used by NS SW */ +static struct scmi_msg_channel scmi_channel[] = { + [0] = { + .shm_addr = SMT_BUFFER_BASE, + .shm_size = SMT_BUF_SLOT_SIZE, + }, +}; + +struct scmi_msg_channel *plat_scmi_get_channel(unsigned int agent_id) +{ + assert(agent_id < ARRAY_SIZE(scmi_channel)); + + VERBOSE("%d: SCMI asking for channel\n", agent_id); + + /* Just in case that code is reused */ + return &scmi_channel[agent_id]; +} + +/* Base protocol implementations */ +const char *plat_scmi_vendor_name(void) +{ + return SCMI_VENDOR; +} + +const char *plat_scmi_sub_vendor_name(void) +{ + return SCMI_PRODUCT; +} + +/* Currently supporting Clocks and Reset Domains */ +static const uint8_t plat_protocol_list[] = { + SCMI_PROTOCOL_ID_BASE, + SCMI_PROTOCOL_ID_CLOCK, + SCMI_PROTOCOL_ID_RESET_DOMAIN, + SCMI_PROTOCOL_ID_POWER_DOMAIN, + /* SCMI_PROTOCOL_ID_SENSOR, */ + 0U /* Null termination */ +}; + +size_t plat_scmi_protocol_count(void) +{ + const size_t count = ARRAY_SIZE(plat_protocol_list) - 1U; + + VERBOSE("SCMI: Protocol count: %d\n", (int32_t)count); + + return count; +} + +const uint8_t *plat_scmi_protocol_list(unsigned int agent_id __unused) +{ + return plat_protocol_list; +} + +void init_scmi_server(void) +{ + size_t i; + int32_t ret; + + for (i = 0U; i < ARRAY_SIZE(scmi_channel); i++) + scmi_smt_init_agent_channel(&scmi_channel[i]); + + INFO("SCMI: Server initialized\n"); + + if (platform_id == QEMU) { + /* default setting is for QEMU */ + } else if (platform_id == SPP) { + for (i = 0U; i < ARRAY_SIZE(scmi0_clock); i++) { + + /* Keep i2c on 100MHz to calculate rates properly */ + if ((i >= CLK_I2C0_0) && (i <= CLK_I2C7_0)) + continue; + + /* Keep UFS clocks to default values to get the expected rates */ + if (i >= CLK_UFS0_0 && i <= CLK_UFS0_2) + continue; + + /* + * SPP supports multiple versions. + * The cpu_clock value is set to corresponding SPP + * version in early platform setup, resuse the same + * value here. + */ + ret = plat_scmi_clock_set_rate(0, i, cpu_clock); + if (ret < 0) { + NOTICE("Failed to set clock rate for SPP scmi_id=%ld\n", i); + } + } + } else { + /* Making MISRA C 2012 15.7 compliant */ + } +} diff --git a/plat/amd/versal2/sip_svc_setup.c b/plat/amd/versal2/sip_svc_setup.c new file mode 100644 index 00000000..68500306 --- /dev/null +++ b/plat/amd/versal2/sip_svc_setup.c @@ -0,0 +1,117 @@ +/* + * Copyright (c) 2018-2019, Arm Limited and Contributors. All rights reserved. + * Copyright (c) 2018-2022, Xilinx, Inc. All rights reserved. + * Copyright (c) 2022-2024, Advanced Micro Devices, Inc. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +/* Top level SMC handler for SiP calls. Dispatch PM calls to PM SMC handler. */ + +#include <errno.h> +#include <inttypes.h> + +#include <common/debug.h> +#include <common/runtime_svc.h> +#include <drivers/scmi-msg.h> +#include <scmi.h> +#include <tools_share/uuid.h> + +#include "ipi_mailbox_svc.h" +#include "plat_private.h" +#include "pm_svc_main.h" + +/* SMC function IDs for SiP Service queries */ +#define SIP_SVC_UID (0x8200ff01U) +#define SIP_SVC_VERSION (0x8200ff03U) + +/* SiP Service Calls version numbers */ +#define SIP_SVC_VERSION_MAJOR (0U) +#define SIP_SVC_VERSION_MINOR (1U) + +/* These macros are used to identify PM calls from the SMC function ID */ +#define SIP_FID_MASK GENMASK(23, 16) +#define XLNX_FID_MASK GENMASK(23, 12) +#define PM_FID_VALUE 0u +#define IPI_FID_VALUE 0x1000u +#define is_pm_fid(_fid) (((_fid) & XLNX_FID_MASK) == PM_FID_VALUE) +#define is_ipi_fid(_fid) (((_fid) & XLNX_FID_MASK) == IPI_FID_VALUE) + +/* SiP Service UUID */ +DEFINE_SVC_UUID2(_sip_uuid, + 0x0499eb70, 0x5ed0, 0x11ee, 0xb3, 0x0a, + 0x87, 0xd1, 0x1d, 0x4f, 0x8a, 0x9b); + +/** + * sip_svc_setup() - Setup SiP Service + * + * Return: 0 on success, negative error code on failure. + * + */ +static int32_t sip_svc_setup(void) +{ + return sip_svc_setup_init(); +} + +/* + * sip_svc_smc_handler() - Top-level SiP Service SMC handler + * + * Handler for all SiP SMC calls. Handles standard SIP requests + * and calls PM SMC handler if the call is for a PM-API function. + */ +static uintptr_t sip_svc_smc_handler(uint32_t smc_fid, + u_register_t x1, + u_register_t x2, + u_register_t x3, + u_register_t x4, + void *cookie, + void *handle, + u_register_t flags) +{ + VERBOSE("SMCID: 0x%08x, x1: 0x%016" PRIx64 ", x2: 0x%016" PRIx64 ", x3: 0x%016" PRIx64 ", x4: 0x%016" PRIx64 "\n", + smc_fid, x1, x2, x3, x4); + + if ((smc_fid & SIP_FID_MASK) != 0) { + WARN("SMC out of SiP assinged range: 0x%x\n", smc_fid); + SMC_RET1(handle, SMC_UNK); + } + + /* Let PM SMC handler deal with PM-related requests */ + if (is_pm_fid(smc_fid)) { + return smc_handler(smc_fid, x1, x2, x3, x4, cookie, handle, flags); + } + + /* Let IPI SMC handler deal with IPI-related requests if platform */ + if (is_ipi_fid(smc_fid)) { + return ipi_smc_handler(smc_fid, x1, x2, x3, x4, cookie, handle, flags); + } + + /* Let PM SMC handler deal with PM-related requests */ + switch (smc_fid) { + case SIP_SVC_UID: + SMC_UUID_RET(handle, _sip_uuid); + + case SIP_SVC_VERSION: + SMC_RET2(handle, SIP_SVC_VERSION_MAJOR, SIP_SVC_VERSION_MINOR); + + case SIP_SCMI: + if (platform_id != EMU) { + scmi_smt_fastcall_smc_entry(0); + SMC_RET1(handle, 0); + } + WARN("SCMI is not working on EMU\n"); + SMC_RET1(handle, SMC_UNK); + default: + WARN("Unimplemented SiP Service Call: 0x%x\n", smc_fid); + SMC_RET1(handle, SMC_UNK); + } +} + +/* Register PM Service Calls as runtime service */ +DECLARE_RT_SVC( + sip_svc, + OEN_SIP_START, + OEN_SIP_END, + SMC_TYPE_FAST, + sip_svc_setup, + sip_svc_smc_handler); diff --git a/plat/amd/versal2/soc_ipi.c b/plat/amd/versal2/soc_ipi.c new file mode 100644 index 00000000..85d1bcdd --- /dev/null +++ b/plat/amd/versal2/soc_ipi.c @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2022, Xilinx, Inc. All rights reserved. + * Copyright (c) 2022-2024, Advanced Micro Devices, Inc. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +/* + * SoC IPI agent registers access management + */ + +#include <lib/utils_def.h> +#include <plat_ipi.h> + +/* versal2 ipi configuration table */ +static const struct ipi_config ipi_table[IPI_ID_MAX] = { + /* A78 IPI */ + [IPI_ID_APU] = { + .ipi_bit_mask = IPI0_TRIG_BIT, + .ipi_reg_base = IPI0_REG_BASE, + .secure_only = 0, + }, + + /* PMC IPI */ + [IPI_ID_PMC] = { + .ipi_bit_mask = PMC_IPI_TRIG_BIT, + .ipi_reg_base = IPI0_REG_BASE, + .secure_only = IPI_SECURE_MASK, + }, + + /* RPU0 IPI */ + [IPI_ID_RPU0] = { + .ipi_bit_mask = IPI1_TRIG_BIT, + .ipi_reg_base = IPI1_REG_BASE, + .secure_only = 0, + }, + + /* RPU1 IPI */ + [IPI_ID_RPU1] = { + .ipi_bit_mask = IPI2_TRIG_BIT, + .ipi_reg_base = IPI2_REG_BASE, + .secure_only = 0, + }, + + /* IPI3 IPI */ + [IPI_ID_3] = { + .ipi_bit_mask = IPI3_TRIG_BIT, + .ipi_reg_base = IPI3_REG_BASE, + .secure_only = 0, + }, + + /* IPI4 IPI */ + [IPI_ID_4] = { + .ipi_bit_mask = IPI4_TRIG_BIT, + .ipi_reg_base = IPI4_REG_BASE, + .secure_only = 0, + }, + + /* IPI5 IPI */ + [IPI_ID_5] = { + .ipi_bit_mask = IPI5_TRIG_BIT, + .ipi_reg_base = IPI5_REG_BASE, + .secure_only = 0, + }, +}; + +/** + * soc_ipi_config_table_init() - Initialize versal2 IPI configuration data. + */ +void soc_ipi_config_table_init(void) +{ + ipi_config_table_init(ipi_table, ARRAY_SIZE(ipi_table)); +} diff --git a/plat/amd/versal2/tsp/tsp-versal2.mk b/plat/amd/versal2/tsp/tsp-versal2.mk new file mode 100644 index 00000000..422ed173 --- /dev/null +++ b/plat/amd/versal2/tsp/tsp-versal2.mk @@ -0,0 +1,10 @@ +# +# Copyright (c) 2023-2024, Advanced Micro Devices, Inc. All rights reserved. +# +# SPDX-License-Identifier: BSD-3-Clause + +# TSP source files specific to Versal Gen 2 platform + +PLAT_XILINX_COMMON := plat/xilinx/common/ + +include ${PLAT_XILINX_COMMON}/tsp/tsp.mk diff --git a/plat/amlogic/axg/platform.mk b/plat/amlogic/axg/platform.mk index 3560b0cd..aadecab5 100644 --- a/plat/amlogic/axg/platform.mk +++ b/plat/amlogic/axg/platform.mk @@ -1,5 +1,5 @@ # -# Copyright (c) 2020, ARM Limited and Contributors. All rights reserved. +# Copyright (c) 2020-2024, Arm Limited and Contributors. All rights reserved. # # SPDX-License-Identifier: BSD-3-Clause # @@ -41,9 +41,9 @@ BL31_SOURCES += lib/cpus/aarch64/cortex_a53.S \ ${GIC_SOURCES} # Tune compiler for Cortex-A53 -ifeq ($(notdir $(CC)),armclang) +ifeq ($($(ARCH)-cc-id),arm-clang) TF_CFLAGS_aarch64 += -mcpu=cortex-a53 -else ifneq ($(findstring clang,$(notdir $(CC))),) +else ifneq ($(filter %-clang,$($(ARCH)-cc-id)),) TF_CFLAGS_aarch64 += -mcpu=cortex-a53 else TF_CFLAGS_aarch64 += -mtune=cortex-a53 @@ -85,11 +85,10 @@ all: ${BUILD_PLAT}/bl31.img distclean realclean clean: cleanimage cleanimage: - ${Q}${MAKE} -C ${DOIMAGEPATH} clean + $(q)${MAKE} -C ${DOIMAGEPATH} clean ${DOIMAGETOOL}: - ${Q}${MAKE} -C ${DOIMAGEPATH} + $(q)${MAKE} -C ${DOIMAGEPATH} ${BUILD_PLAT}/bl31.img: ${BUILD_PLAT}/bl31.bin ${DOIMAGETOOL} ${DOIMAGETOOL} ${BUILD_PLAT}/bl31.bin ${BUILD_PLAT}/bl31.img - diff --git a/plat/amlogic/g12a/platform.mk b/plat/amlogic/g12a/platform.mk index b0c91b06..15658d34 100644 --- a/plat/amlogic/g12a/platform.mk +++ b/plat/amlogic/g12a/platform.mk @@ -1,5 +1,5 @@ # -# Copyright (c) 2019, ARM Limited and Contributors. All rights reserved. +# Copyright (c) 2019-2024, Arm Limited and Contributors. All rights reserved. # # SPDX-License-Identifier: BSD-3-Clause # @@ -41,9 +41,9 @@ BL31_SOURCES += lib/cpus/aarch64/cortex_a53.S \ ${GIC_SOURCES} # Tune compiler for Cortex-A53 -ifeq ($(notdir $(CC)),armclang) +ifeq ($($(ARCH)-cc-id),arm-clang) TF_CFLAGS_aarch64 += -mcpu=cortex-a53 -else ifneq ($(findstring clang,$(notdir $(CC))),) +else ifneq ($(filter %-clang,$($(ARCH)-cc-id)),) TF_CFLAGS_aarch64 += -mcpu=cortex-a53 else TF_CFLAGS_aarch64 += -mtune=cortex-a53 @@ -81,11 +81,10 @@ all: ${BUILD_PLAT}/bl31.img distclean realclean clean: cleanimage cleanimage: - ${Q}${MAKE} -C ${DOIMAGEPATH} clean + $(q)${MAKE} -C ${DOIMAGEPATH} clean ${DOIMAGETOOL}: - ${Q}${MAKE} -C ${DOIMAGEPATH} + $(q)${MAKE} -C ${DOIMAGEPATH} ${BUILD_PLAT}/bl31.img: ${BUILD_PLAT}/bl31.bin ${DOIMAGETOOL} ${DOIMAGETOOL} ${BUILD_PLAT}/bl31.bin ${BUILD_PLAT}/bl31.img - diff --git a/plat/amlogic/gxbb/platform.mk b/plat/amlogic/gxbb/platform.mk index 62384d2a..fbebd3e7 100644 --- a/plat/amlogic/gxbb/platform.mk +++ b/plat/amlogic/gxbb/platform.mk @@ -1,5 +1,5 @@ # -# Copyright (c) 2018-2019, ARM Limited and Contributors. All rights reserved. +# Copyright (c) 2018-2024, Arm Limited and Contributors. All rights reserved. # # SPDX-License-Identifier: BSD-3-Clause # @@ -37,9 +37,9 @@ BL31_SOURCES += lib/cpus/aarch64/cortex_a53.S \ ${GIC_SOURCES} # Tune compiler for Cortex-A53 -ifeq ($(notdir $(CC)),armclang) +ifeq ($($(ARCH)-cc-id),arm-clang) TF_CFLAGS_aarch64 += -mcpu=cortex-a53 -else ifneq ($(findstring clang,$(notdir $(CC))),) +else ifneq ($(filter %-clang,$($(ARCH)-cc-id)),) TF_CFLAGS_aarch64 += -mcpu=cortex-a53 else TF_CFLAGS_aarch64 += -mtune=cortex-a53 diff --git a/plat/amlogic/gxl/platform.mk b/plat/amlogic/gxl/platform.mk index 641d177b..31063a97 100644 --- a/plat/amlogic/gxl/platform.mk +++ b/plat/amlogic/gxl/platform.mk @@ -1,5 +1,5 @@ # -# Copyright (c) 2018-2019, ARM Limited and Contributors. All rights reserved. +# Copyright (c) 2018-2024, Arm Limited and Contributors. All rights reserved. # # SPDX-License-Identifier: BSD-3-Clause # @@ -41,9 +41,9 @@ BL31_SOURCES += lib/cpus/aarch64/cortex_a53.S \ ${GIC_SOURCES} # Tune compiler for Cortex-A53 -ifeq ($(notdir $(CC)),armclang) +ifeq ($($(ARCH)-cc-id),arm-clang) TF_CFLAGS_aarch64 += -mcpu=cortex-a53 -else ifneq ($(findstring clang,$(notdir $(CC))),) +else ifneq ($(filter %-clang,$($(ARCH)-cc-id)),) TF_CFLAGS_aarch64 += -mcpu=cortex-a53 else TF_CFLAGS_aarch64 += -mtune=cortex-a53 @@ -81,11 +81,10 @@ all: ${BUILD_PLAT}/bl31.img distclean realclean clean: cleanimage cleanimage: - ${Q}${MAKE} -C ${DOIMAGEPATH} clean + $(q)${MAKE} -C ${DOIMAGEPATH} clean ${DOIMAGETOOL}: - ${Q}${MAKE} -C ${DOIMAGEPATH} + $(q)${MAKE} -C ${DOIMAGEPATH} ${BUILD_PLAT}/bl31.img: ${BUILD_PLAT}/bl31.bin ${DOIMAGETOOL} ${DOIMAGETOOL} ${BUILD_PLAT}/bl31.bin ${BUILD_PLAT}/bl31.img - diff --git a/plat/arm/board/a5ds/fdts/a5ds_fw_config.dts b/plat/arm/board/a5ds/fdts/a5ds_fw_config.dts index b9ff8bff..5bf6a3f6 100644 --- a/plat/arm/board/a5ds/fdts/a5ds_fw_config.dts +++ b/plat/arm/board/a5ds/fdts/a5ds_fw_config.dts @@ -1,10 +1,11 @@ /* - * Copyright (c) 2019-2020, Arm Limited. All rights reserved. + * Copyright (c) 2019-2024, Arm Limited. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ #include <common/tbbr/tbbr_img_def.h> +#include <platform_def.h> /dts-v1/; @@ -20,7 +21,7 @@ hw-config { load-address = <0x0 0x83000000>; - max-size = <0x01000000>; + max-size = <PLAT_ARM_HW_CONFIG_SIZE>; id = <HW_CONFIG_ID>; }; }; diff --git a/plat/arm/board/a5ds/include/platform_def.h b/plat/arm/board/a5ds/include/platform_def.h index 9f3df1ef..b1349117 100644 --- a/plat/arm/board/a5ds/include/platform_def.h +++ b/plat/arm/board/a5ds/include/platform_def.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019-2020, Arm Limited. All rights reserved. + * Copyright (c) 2019-2024, Arm Limited. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -208,6 +208,9 @@ */ #define ARM_FW_CONFIGS_LIMIT (ARM_BL_RAM_BASE + (PAGE_SIZE * 2)) +/* Define memory configuration for device tree files. */ +#define PLAT_ARM_HW_CONFIG_SIZE U(0x01000000) + /******************************************************************************* * BL1 specific defines. * BL1 RW data is relocated from ROM to RAM at runtime so we need 2 sets of diff --git a/plat/arm/board/arm_fpga/fpga_def.h b/plat/arm/board/arm_fpga/fpga_def.h index 2884ea6d..5e3a0a99 100644 --- a/plat/arm/board/arm_fpga/fpga_def.h +++ b/plat/arm/board/arm_fpga/fpga_def.h @@ -21,7 +21,7 @@ #define FPGA_MAX_CLUSTER_COUNT 4 #define FPGA_MAX_CPUS_PER_CLUSTER 8 -#define FPGA_MAX_PE_PER_CPU 4 +#define FPGA_MAX_PE_PER_CPU 2 #define FPGA_PRIMARY_CPU 0x0 /******************************************************************************* diff --git a/plat/arm/board/arm_fpga/platform.mk b/plat/arm/board/arm_fpga/platform.mk index f44b37d4..967bf217 100644 --- a/plat/arm/board/arm_fpga/platform.mk +++ b/plat/arm/board/arm_fpga/platform.mk @@ -1,5 +1,5 @@ # -# Copyright (c) 2021-2023, Arm Limited. All rights reserved. +# Copyright (c) 2021-2024, Arm Limited. All rights reserved. # # SPDX-License-Identifier: BSD-3-Clause # @@ -41,8 +41,13 @@ ENABLE_FEAT_CSV2_2 := 2 ENABLE_FEAT_ECV := 2 ENABLE_FEAT_FGT := 2 ENABLE_FEAT_HCX := 2 +ENABLE_FEAT_MTE2 := 2 +ENABLE_FEAT_TCR2 := 2 ENABLE_SYS_REG_TRACE_FOR_NS := 2 ENABLE_TRF_FOR_NS := 2 +ENABLE_SME_FOR_NS := 2 +ENABLE_SME2_FOR_NS := 2 +ENABLE_FEAT_LS64_ACCDATA := 2 # Treating this as a memory-constrained port for now USE_COHERENT_MEM := 0 @@ -77,8 +82,8 @@ else lib/cpus/aarch64/neoverse_n1.S \ lib/cpus/aarch64/neoverse_n2.S \ lib/cpus/aarch64/neoverse_v1.S \ - lib/cpus/aarch64/cortex_chaberton.S \ - lib/cpus/aarch64/cortex_blackhawk.S + lib/cpus/aarch64/cortex_a725.S \ + lib/cpus/aarch64/cortex_x925.S # AArch64/AArch32 cores FPGA_CPU_LIBS += lib/cpus/aarch64/cortex_a55.S \ @@ -123,12 +128,20 @@ BL31_SOURCES += common/fdt_fixup.c \ BL31_SOURCES += ${FDT_WRAPPERS_SOURCES} -$(eval $(call MAKE_S,$(BUILD_PLAT),plat/arm/board/arm_fpga/rom_trampoline.S,bl31)) -$(eval $(call MAKE_S,$(BUILD_PLAT),plat/arm/board/arm_fpga/kernel_trampoline.S,bl31)) -$(eval $(call MAKE_LD,$(BUILD_PLAT)/build_axf.ld,plat/arm/board/arm_fpga/build_axf.ld.S,bl31)) +$(eval $(call MAKE_S,$(BUILD_PLAT),plat/arm/board/arm_fpga/rom_trampoline.S,bl31,BL31)) +$(eval $(call MAKE_S,$(BUILD_PLAT),plat/arm/board/arm_fpga/kernel_trampoline.S,bl31,BL31)) +$(eval $(call MAKE_LD,$(BUILD_PLAT)/build_axf.ld,plat/arm/board/arm_fpga/build_axf.ld.S,bl31,BL31)) + +ifeq ($($(ARCH)-ld-id),gnu-gcc) + AXF_LDFLAGS += -Wl,--build-id=none -mno-fix-cortex-a53-843419 +else + AXF_LDFLAGS += --build-id=none +endif + +AXF_LDFLAGS += -nostdlib -no-pie bl31.axf: bl31 dtbs ${BUILD_PLAT}/rom_trampoline.o ${BUILD_PLAT}/kernel_trampoline.o ${BUILD_PLAT}/build_axf.ld - $(ECHO) " LD $@" - $(Q)$(LD) -T ${BUILD_PLAT}/build_axf.ld -L ${BUILD_PLAT} --strip-debug -s -n -o ${BUILD_PLAT}/bl31.axf + $(s)echo " LD $@" + $(q)$($(ARCH)-ld) -T ${BUILD_PLAT}/build_axf.ld -L ${BUILD_PLAT} ${AXF_LDFLAGS} -s -n -o ${BUILD_PLAT}/bl31.axf all: bl31.axf diff --git a/plat/arm/board/automotive_rd/platform/rd1ae/fdts/rd1ae_fw_config.dts b/plat/arm/board/automotive_rd/platform/rd1ae/fdts/rd1ae_fw_config.dts new file mode 100644 index 00000000..53cd3b0f --- /dev/null +++ b/plat/arm/board/automotive_rd/platform/rd1ae/fdts/rd1ae_fw_config.dts @@ -0,0 +1,21 @@ +/* + * Copyright (c) 2024, Arm Limited. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <common/tbbr/tbbr_img_def.h> + +/dts-v1/; + +/ { + dtb-registry { + compatible = "fconf,dyn_cfg-dtb_registry"; + + hw-config { + load-address = <0x0 0x83000000>; + max-size = <0x8000>; + id = <HW_CONFIG_ID>; + }; + }; +}; diff --git a/plat/arm/board/automotive_rd/platform/rd1ae/include/plat_macros.S b/plat/arm/board/automotive_rd/platform/rd1ae/include/plat_macros.S new file mode 100644 index 00000000..8efe8acb --- /dev/null +++ b/plat/arm/board/automotive_rd/platform/rd1ae/include/plat_macros.S @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2024, Arm Limited. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef PLAT_MACROS_S +#define PLAT_MACROS_S + +#include <arm_macros.S> + +/* --------------------------------------------- + * The below required platform porting macro + * prints out relevant platform registers + * whenever an unhandled exception is taken in + * BL31. + * + * There are currently no platform specific regs + * to print. + * --------------------------------------------- + */ + .macro plat_crash_print_regs + .endm + +#endif /* PLAT_MACROS_S */ diff --git a/plat/arm/board/automotive_rd/platform/rd1ae/include/platform_def.h b/plat/arm/board/automotive_rd/platform/rd1ae/include/platform_def.h new file mode 100644 index 00000000..44c8ee3c --- /dev/null +++ b/plat/arm/board/automotive_rd/platform/rd1ae/include/platform_def.h @@ -0,0 +1,157 @@ +/* + * Copyright (c) 2024, Arm Limited. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef PLATFORM_DEF_H +#define PLATFORM_DEF_H + +#include <lib/utils_def.h> +#include <lib/xlat_tables/xlat_tables_defs.h> + +/* These are referenced by arm_def.h #included next, so #define first. */ +#define PLAT_ARM_TRUSTED_SRAM_BASE UL(0x0) + +#include <plat/arm/common/arm_def.h> +#include <plat/arm/css/common/css_def.h> +#include <plat/common/common_def.h> + +#define PLATFORM_CORE_COUNT U(16) +#define PLAT_ARM_CLUSTER_COUNT U(16) +#define PLAT_MAX_CPUS_PER_CLUSTER U(1) +#define PLAT_MAX_PE_PER_CPU U(1) + +#define PLATFORM_STACK_SIZE UL(0x1000) + +/* BL1 is not supported */ +#define PLAT_ARM_TRUSTED_ROM_BASE UL(0x0) +#define PLAT_ARM_TRUSTED_ROM_SIZE UL(0x0) + +#define PLAT_ARM_TRUSTED_SRAM_SIZE UL(0x00080000) + +/* USE_ROMLIB is not supported */ +#define PLAT_ARM_MAX_ROMLIB_RW_SIZE U(0) +#define PLAT_ARM_MAX_ROMLIB_RO_SIZE U(0) + +/* Defined based on actual binary sizes */ +#define PLAT_ARM_MAX_BL1_RW_SIZE 0x0 +#define PLAT_ARM_MAX_BL2_SIZE 0x20000 +#define PLAT_ARM_MAX_BL31_SIZE 0x70000 + +#define PLAT_ARM_DRAM2_BASE ULL(0x8080000000) +#define PLAT_ARM_DRAM2_SIZE ULL(0x180000000) + +#define PLAT_CSS_MHU_BASE UL(0x2A920000) +#define PLAT_ARM_NSTIMER_FRAME_ID U(0) + +#define SOC_CSS_SEC_UART_BASE UL(0x2A410000) +#define SOC_CSS_NSEC_UART_BASE UL(0x2A400000) +#define SOC_CSS_UART_SIZE UL(0x10000) +#define SOC_CSS_UART_CLK_IN_HZ UL(7372800) +#define PLAT_ARM_BOOT_UART_BASE SOC_CSS_SEC_UART_BASE +#define PLAT_ARM_BOOT_UART_CLK_IN_HZ SOC_CSS_UART_CLK_IN_HZ +#define PLAT_ARM_RUN_UART_BASE SOC_CSS_SEC_UART_BASE +#define PLAT_ARM_RUN_UART_CLK_IN_HZ SOC_CSS_UART_CLK_IN_HZ +#define PLAT_ARM_CRASH_UART_BASE SOC_CSS_SEC_UART_BASE +#define PLAT_ARM_CRASH_UART_CLK_IN_HZ SOC_CSS_UART_CLK_IN_HZ + +/* Physical and virtual address space limits for MMU */ +#define PLAT_PHY_ADDR_SPACE_SIZE (1ULL << 42) +#define PLAT_VIRT_ADDR_SPACE_SIZE (1ULL << 42) + +/* GIC related constants */ +#define PLAT_ARM_GICD_BASE UL(0x30000000) +#define PLAT_ARM_GICR_BASE UL(0x301C0000) +#define PLAT_ARM_GICC_BASE UL(0x2C000000) +#define PLAT_ARM_G1S_IRQ_PROPS(grp) CSS_G1S_IRQ_PROPS(grp) +#define PLAT_ARM_G0_IRQ_PROPS(grp) ARM_G0_IRQ_PROPS(grp) + +/* Virtual address used by dynamic mem_protect for chunk_base */ +#define PLAT_ARM_MEM_PROTEC_VA_FRAME UL(0xC0000000) + +/* Secure Watchdog Constants */ +#define SBSA_SECURE_WDOG_BASE UL(0x2A480000) +#define SBSA_SECURE_WDOG_TIMEOUT UL(100) + +#define V2M_SYS_LED_SS_SHIFT U(0) +#define V2M_SYS_LED_EL_SHIFT U(1) +#define V2M_SYS_LED_EC_SHIFT U(3) + +#define V2M_SYS_LED_SS_MASK U(0x01) +#define V2M_SYS_LED_EL_MASK U(0x03) +#define V2M_SYS_LED_EC_MASK U(0x1f) + +#define V2M_SYSREGS_BASE UL(0x0C010000) +#define V2M_SYS_LED U(0x8) + +#define PLAT_ARM_SCMI_CHANNEL_COUNT U(1) +#define CSS_SYSTEM_PWR_DMN_LVL ARM_PWR_LVL2 +#define PLAT_MAX_PWR_LVL ARM_PWR_LVL1 + +#define MAX_IO_DEVICES U(3) +#define MAX_IO_HANDLES U(4) + +#ifdef IMAGE_BL2 +#define PLAT_ARM_MMAP_ENTRIES U(5) +#else +#define PLAT_ARM_MMAP_ENTRIES U(6) +#endif +#define MAX_XLAT_TABLES U(6) + +#define V2M_FLASH0_BASE UL(0x08000000) +#define V2M_FLASH0_SIZE UL(0x04000000) +#define V2M_FLASH_BLOCK_SIZE UL(0x00040000) /* 256 KB */ +#define PLAT_ARM_FLASH_IMAGE_BASE V2M_FLASH0_BASE +#define PLAT_ARM_FLASH_IMAGE_MAX_SIZE (V2M_FLASH0_SIZE - V2M_FLASH_BLOCK_SIZE) + +#define PLAT_FW_CONFIG_MAX_SIZE (ARM_FW_CONFIG_LIMIT - ARM_FW_CONFIG_BASE) +#define PLAT_FW_CONFIG_BASE ARM_FW_CONFIG_BASE + +/* RD1AE-specific memory mappings */ +#define RD1AE_EXTERNAL_FLASH MAP_REGION_FLAT(V2M_FLASH0_BASE, \ + V2M_FLASH0_SIZE, \ + MT_DEVICE | MT_RO | \ + MT_SECURE) + +#define RD1AE_MAP_NS_DRAM1 MAP_REGION_FLAT(ARM_DRAM1_BASE, \ + ARM_DRAM1_SIZE, \ + MT_MEMORY | MT_RW | \ + MT_NS) + +#define RD1AE_DEVICE_BASE (0x20000000) +#define RD1AE_DEVICE_SIZE (0x20000000) +#define RD1AE_MAP_DEVICE MAP_REGION_FLAT(RD1AE_DEVICE_BASE, \ + RD1AE_DEVICE_SIZE, \ + MT_DEVICE | MT_RW | \ + MT_SECURE) + +#define SOC_PLATFORM_PERIPH_BASE UL(0x0E000000) +#define SOC_PLATFORM_PERIPH_SIZE UL(0x02000000) +#define SOC_PLATFORM_PERIPH_MAP_DEVICE MAP_REGION_FLAT(SOC_PLATFORM_PERIPH_BASE, \ + SOC_PLATFORM_PERIPH_SIZE, \ + MT_DEVICE | MT_RW | MT_SECURE) + +/* Non-volatile counters */ +#define TRUSTED_NVCTR_BASE_OFFSET UL(0x00E70000) +#define TFW_NVCTR_BASE_OFFSET 0x0000 +#define NTFW_CTR_BASE_OFFSET 0x0004 +#define SOC_TRUSTED_NVCTR_BASE (SOC_PLATFORM_PERIPH_BASE + TRUSTED_NVCTR_BASE_OFFSET) +#define TFW_NVCTR_BASE (SOC_TRUSTED_NVCTR_BASE + TFW_NVCTR_BASE_OFFSET) +#define TFW_NVCTR_SIZE U(4) +#define NTFW_CTR_BASE (SOC_TRUSTED_NVCTR_BASE + NTFW_CTR_BASE_OFFSET) +#define NTFW_CTR_SIZE U(4) + +/******************************************************************************* + * Memprotect definitions + ******************************************************************************/ +/* PSCI memory protect definitions: + * This variable is stored in a non-secure flash because some ARM reference + * platforms do not have secure NVRAM. Real systems that provided MEM_PROTECT + * support must use a secure NVRAM to store the PSCI MEM_PROTECT definitions. + */ +#define PLAT_ARM_MEM_PROT_ADDR (V2M_FLASH0_BASE + \ + V2M_FLASH0_SIZE - \ + V2M_FLASH_BLOCK_SIZE) + +#endif /* PLATFORM_DEF_H */ diff --git a/plat/arm/board/automotive_rd/platform/rd1ae/include/rd1ae_helpers.S b/plat/arm/board/automotive_rd/platform/rd1ae/include/rd1ae_helpers.S new file mode 100644 index 00000000..32260efc --- /dev/null +++ b/plat/arm/board/automotive_rd/platform/rd1ae/include/rd1ae_helpers.S @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2024, Arm Limited. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <arch.h> +#include <asm_macros.S> +#include <platform_def.h> + + .globl plat_arm_calc_core_pos + + /* --------------------------------------------------------------------- + * unsigned int plat_arm_calc_core_pos(u_register_t mpidr) + * + * Function to calculate the core position on rd1ae. + * + * (ClusterId * PLAT_MAX_CPUS_PER_CLUSTER * PLAT_MAX_PE_PER_CPU) + + * (CPUId * PLAT_MAX_PE_PER_CPU) + + * ThreadId + * + * which can be simplified as: + * + * ((ClusterId * PLAT_MAX_CPUS_PER_CLUSTER + CPUId) * PLAT_MAX_PE_PER_CPU) + * + ThreadId + * --------------------------------------------------------------------- + */ +func plat_arm_calc_core_pos + mov x4, x0 + + /* Extract individual affinity fields from MPIDR */ + ubfx x0, x4, #MPIDR_AFF0_SHIFT, #MPIDR_AFFINITY_BITS + ubfx x1, x4, #MPIDR_AFF1_SHIFT, #MPIDR_AFFINITY_BITS + ubfx x2, x4, #MPIDR_AFF2_SHIFT, #MPIDR_AFFINITY_BITS + ubfx x3, x4, #MPIDR_AFF3_SHIFT, #MPIDR_AFFINITY_BITS + + /* Compute linear position */ + mov x4, #PLAT_ARM_CLUSTER_COUNT + madd x2, x3, x4, x2 + mov x4, #PLAT_MAX_CPUS_PER_CLUSTER + madd x1, x2, x4, x1 + mov x4, #PLAT_MAX_PE_PER_CPU + madd x0, x1, x4, x0 + ret +endfunc plat_arm_calc_core_pos diff --git a/plat/arm/board/automotive_rd/platform/rd1ae/platform.mk b/plat/arm/board/automotive_rd/platform/rd1ae/platform.mk new file mode 100644 index 00000000..35cd8a19 --- /dev/null +++ b/plat/arm/board/automotive_rd/platform/rd1ae/platform.mk @@ -0,0 +1,88 @@ +# Copyright (c) 2024, Arm Limited. All rights reserved. +# +# SPDX-License-Identifier: BSD-3-Clause +# + +# RD1AE (Kronos) platform. +$(info Platform ${PLAT} is (kronos) specific.) + +RD1AE_BASE = plat/arm/board/automotive_rd/platform/rd1ae + +PLAT_INCLUDES += -I${RD1AE_BASE}/include/ + +override ARM_FW_CONFIG_LOAD_ENABLE := 1 +override ARM_PLAT_MT := 1 +override ARM_RECOM_STATE_ID_ENC := 1 +override CSS_LOAD_SCP_IMAGES := 0 +override CTX_INCLUDE_AARCH32_REGS := 0 +override ENABLE_SVE_FOR_NS := 1 +override ENABLE_SVE_FOR_SWD := 1 +override NEED_BL1 := 0 +override NEED_BL2U := 0 +override PSCI_EXTENDED_STATE_ID := 1 + +ARM_ARCH_MAJOR := 9 +ARM_ARCH_MINOR := 2 +CSS_USE_SCMI_SDS_DRIVER := 1 +ENABLE_FEAT_AMU := 1 +ENABLE_FEAT_ECV := 1 +ENABLE_FEAT_FGT := 1 +ENABLE_FEAT_MTE2 := 1 +ENABLE_MPAM_FOR_LOWER_ELS := 1 +GIC_ENABLE_V4_EXTN := 1 +GICV3_SUPPORT_GIC600 := 1 +HW_ASSISTED_COHERENCY := 1 +PLAT_MHU_VERSION := 1 +RESET_TO_BL2 := 1 +SVE_VECTOR_LEN := 128 +USE_COHERENT_MEM := 0 + +RD1AE_CPU_SOURCES := lib/cpus/aarch64/neoverse_v3.S + +include drivers/arm/gic/v3/gicv3.mk +RD1AE_GIC_SOURCES := ${GICV3_SOURCES} \ + plat/common/plat_gicv3.c \ + plat/arm/common/arm_gicv3.c + +PLAT_BL_COMMON_SOURCES += ${RD1AE_BASE}/rd1ae_plat.c \ + ${RD1AE_BASE}/include/rd1ae_helpers.S + +BL2_SOURCES += ${RD1AE_CPU_SOURCES} \ + ${RD1AE_BASE}/rd1ae_err.c \ + ${RD1AE_BASE}/rd1ae_bl2_mem_params_desc.c \ + lib/utils/mem_region.c \ + plat/arm/common/arm_nor_psci_mem_protect.c \ + drivers/arm/sbsa/sbsa.c + +BL31_SOURCES += ${RD1AE_CPU_SOURCES} \ + ${RD1AE_GIC_SOURCES} \ + ${RD1AE_BASE}/rd1ae_bl31_setup.c \ + ${RD1AE_BASE}/rd1ae_topology.c \ + drivers/cfi/v2m/v2m_flash.c \ + lib/utils/mem_region.c \ + plat/arm/common/arm_nor_psci_mem_protect.c + +ifeq (${TRUSTED_BOARD_BOOT},1) +BL2_SOURCES += ${RD1AE_BASE}/rd1ae_tbb.c +endif + +# Add the FDT_SOURCES and options for Dynamic Config +FDT_SOURCES += ${RD1AE_BASE}/fdts/${PLAT}_fw_config.dts \ + fdts/${PLAT}.dts + +FW_CONFIG := ${BUILD_PLAT}/fdts/${PLAT}_fw_config.dtb +HW_CONFIG := ${BUILD_PLAT}/fdts/${PLAT}.dtb + +# Add the FW_CONFIG to FIP and specify the same to certtool +$(eval $(call TOOL_ADD_PAYLOAD,${FW_CONFIG},--fw-config,${FW_CONFIG})) +# Add the HW_CONFIG to FIP and specify the same to certtool +$(eval $(call TOOL_ADD_PAYLOAD,${HW_CONFIG},--hw-config,${HW_CONFIG})) + +ifeq (${TRUSTED_BOARD_BOOT},1) +FIP_BL2_ARGS := tb-fw +$(eval $(call TOOL_ADD_PAYLOAD,${BUILD_PLAT}/tb_fw.crt,--tb-fw-cert)) +endif + +include plat/arm/common/arm_common.mk +include plat/arm/css/common/css_common.mk +include plat/arm/board/common/board_common.mk diff --git a/plat/arm/board/automotive_rd/platform/rd1ae/rd1ae_bl2_mem_params_desc.c b/plat/arm/board/automotive_rd/platform/rd1ae/rd1ae_bl2_mem_params_desc.c new file mode 100644 index 00000000..30cc90f0 --- /dev/null +++ b/plat/arm/board/automotive_rd/platform/rd1ae/rd1ae_bl2_mem_params_desc.c @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2024, Arm Limited. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <common/bl_common.h> +#include <common/desc_image_load.h> +#include <platform_def.h> + +/******************************************************************************* + * Following descriptor provides BL image/ep information that gets used + * by BL2 to load the images and also subset of this information is + * passed to next BL image. The image loading sequence is managed by + * populating the images in required loading order. The image execution + * sequence is managed by populating the `next_handoff_image_id` with + * the next executable image id. + ******************************************************************************/ +static bl_mem_params_node_t bl2_mem_params_descs[] = { + /* Fill BL31 related information */ + { + .image_id = BL31_IMAGE_ID, + + SET_STATIC_PARAM_HEAD(ep_info, PARAM_EP, + VERSION_2, entry_point_info_t, + SECURE | EXECUTABLE | EP_FIRST_EXE), + .ep_info.pc = BL31_BASE, + .ep_info.spsr = SPSR_64(MODE_EL3, MODE_SP_ELX, + DISABLE_ALL_EXCEPTIONS), +#if DEBUG + .ep_info.args.arg3 = ARM_BL31_PLAT_PARAM_VAL, +#endif + + SET_STATIC_PARAM_HEAD(image_info, PARAM_EP, + VERSION_2, image_info_t, IMAGE_ATTRIB_PLAT_SETUP), + .image_info.image_base = BL31_BASE, + .image_info.image_max_size = BL31_LIMIT - BL31_BASE, + + .next_handoff_image_id = BL33_IMAGE_ID, + }, + /* Fill HW_CONFIG related information */ + { + .image_id = HW_CONFIG_ID, + SET_STATIC_PARAM_HEAD(ep_info, PARAM_IMAGE_BINARY, + VERSION_2, entry_point_info_t, + NON_SECURE | NON_EXECUTABLE), + SET_STATIC_PARAM_HEAD(image_info, PARAM_IMAGE_BINARY, + VERSION_2, image_info_t, IMAGE_ATTRIB_SKIP_LOADING), + .next_handoff_image_id = INVALID_IMAGE_ID, + }, + /* Fill BL33 related information */ + { + .image_id = BL33_IMAGE_ID, + SET_STATIC_PARAM_HEAD(ep_info, PARAM_EP, + VERSION_2, entry_point_info_t, NON_SECURE | EXECUTABLE), + .ep_info.pc = PLAT_ARM_NS_IMAGE_BASE, + + SET_STATIC_PARAM_HEAD(image_info, PARAM_EP, + VERSION_2, image_info_t, 0), + .image_info.image_base = PLAT_ARM_NS_IMAGE_BASE, + .image_info.image_max_size = ARM_DRAM1_BASE + ARM_DRAM1_SIZE + - PLAT_ARM_NS_IMAGE_BASE, + .next_handoff_image_id = INVALID_IMAGE_ID, + }, +}; + +REGISTER_BL_IMAGE_DESCS(bl2_mem_params_descs) diff --git a/plat/arm/board/automotive_rd/platform/rd1ae/rd1ae_bl31_setup.c b/plat/arm/board/automotive_rd/platform/rd1ae/rd1ae_bl31_setup.c new file mode 100644 index 00000000..ce7bad75 --- /dev/null +++ b/plat/arm/board/automotive_rd/platform/rd1ae/rd1ae_bl31_setup.c @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2024, Arm Limited. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <drivers/arm/css/css_mhu_doorbell.h> +#include <drivers/arm/css/scmi.h> + +static scmi_channel_plat_info_t plat_rd_scmi_info[] = { + { + .scmi_mbx_mem = CSS_SCMI_PAYLOAD_BASE, + .db_reg_addr = PLAT_CSS_MHU_BASE + MHU_V3_SENDER_REG_SET(0), + .db_preserve_mask = 0xfffffffe, + .db_modify_mask = 0x1, + .ring_doorbell = &mhu_ring_doorbell, + }, +}; + +scmi_channel_plat_info_t *plat_css_get_scmi_info(unsigned int channel_id) +{ + return &plat_rd_scmi_info[channel_id]; +} + +const plat_psci_ops_t *plat_arm_psci_override_pm_ops(plat_psci_ops_t *ops) +{ + return css_scmi_override_pm_ops(ops); +} diff --git a/plat/arm/board/automotive_rd/platform/rd1ae/rd1ae_err.c b/plat/arm/board/automotive_rd/platform/rd1ae/rd1ae_err.c new file mode 100644 index 00000000..62544733 --- /dev/null +++ b/plat/arm/board/automotive_rd/platform/rd1ae/rd1ae_err.c @@ -0,0 +1,22 @@ +/* + * Copyright (c) 2024, Arm Limited. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <drivers/arm/sbsa.h> +#include <plat/arm/common/plat_arm.h> + +/* + * rd1ae error handler + */ +void __dead2 plat_arm_error_handler(int err) +{ + console_flush(); + + sbsa_wdog_refresh(SBSA_SECURE_WDOG_BASE); + + while (1) { + wfi(); + } +} diff --git a/plat/arm/board/automotive_rd/platform/rd1ae/rd1ae_plat.c b/plat/arm/board/automotive_rd/platform/rd1ae/rd1ae_plat.c new file mode 100644 index 00000000..e917330b --- /dev/null +++ b/plat/arm/board/automotive_rd/platform/rd1ae/rd1ae_plat.c @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2024, Arm Limited. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <common/debug.h> +#include <drivers/arm/sbsa.h> +#include <lib/fconf/fconf.h> +#include <lib/fconf/fconf_dyn_cfg_getter.h> +#include <plat/arm/common/plat_arm.h> +#include <plat/common/platform.h> + +const mmap_region_t plat_arm_mmap[] = { + ARM_MAP_SHARED_RAM, + RD1AE_MAP_DEVICE, + RD1AE_EXTERNAL_FLASH, + SOC_PLATFORM_PERIPH_MAP_DEVICE, +#if IMAGE_BL2 + RD1AE_MAP_NS_DRAM1, +#endif + {0} +}; + +void plat_arm_secure_wdt_start(void) +{ + sbsa_wdog_start(SBSA_SECURE_WDOG_BASE, SBSA_SECURE_WDOG_TIMEOUT); +} + +void plat_arm_secure_wdt_stop(void) +{ + sbsa_wdog_stop(SBSA_SECURE_WDOG_BASE); +} + +/* + * For rd1ae we should not do anything in these interface functions. + * They are used to override the weak functions in cci drivers + */ +void plat_arm_interconnect_init(void) +{ +} + +void plat_arm_interconnect_enter_coherency(void) +{ +} + +void plat_arm_interconnect_exit_coherency(void) +{ +} + +/* + * TZC programming is currently not done. + */ +void plat_arm_security_setup(void) +{ +} diff --git a/plat/arm/board/automotive_rd/platform/rd1ae/rd1ae_tbb.c b/plat/arm/board/automotive_rd/platform/rd1ae/rd1ae_tbb.c new file mode 100644 index 00000000..01fbcce8 --- /dev/null +++ b/plat/arm/board/automotive_rd/platform/rd1ae/rd1ae_tbb.c @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2024, ARM Limited. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <plat/arm/common/plat_arm.h> + +int plat_get_mbedtls_heap(void **heap_addr, size_t *heap_size) +{ + assert(heap_addr != NULL); + assert(heap_size != NULL); + + return arm_get_mbedtls_heap(heap_addr, heap_size); +} + +/* + * Return the ROTPK hash in the following ASN.1 structure in DER format: + * + * AlgorithmIdentifier ::= SEQUENCE { + * algorithm OBJECT IDENTIFIER, + * parameters ANY DEFINED BY algorithm OPTIONAL + * } + * + * DigestInfo ::= SEQUENCE { + * digestAlgorithm AlgorithmIdentifier, + * digest OCTET STRING + * } + */ +int plat_get_rotpk_info(void *cookie, void **key_ptr, unsigned int *key_len, + unsigned int *flags) +{ + return arm_get_rotpk_info(cookie, key_ptr, key_len, flags); +} diff --git a/plat/arm/board/automotive_rd/platform/rd1ae/rd1ae_topology.c b/plat/arm/board/automotive_rd/platform/rd1ae/rd1ae_topology.c new file mode 100644 index 00000000..25331840 --- /dev/null +++ b/plat/arm/board/automotive_rd/platform/rd1ae/rd1ae_topology.c @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2024, Arm Limited. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <plat/arm/common/plat_arm.h> +#include <plat/arm/css/common/css_pm.h> + +/****************************************************************************** + * The power domain tree descriptor. + * + * This descriptor defines the layout of the power domain tree for the RD1AE + * platform, which consists of 16 clusters. + ******************************************************************************/ +const unsigned char rd1_ae_pd_tree_desc[] = { + (PLAT_ARM_CLUSTER_COUNT), + PLAT_MAX_CPUS_PER_CLUSTER, + PLAT_MAX_CPUS_PER_CLUSTER, + PLAT_MAX_CPUS_PER_CLUSTER, + PLAT_MAX_CPUS_PER_CLUSTER, + PLAT_MAX_CPUS_PER_CLUSTER, + PLAT_MAX_CPUS_PER_CLUSTER, + PLAT_MAX_CPUS_PER_CLUSTER, + PLAT_MAX_CPUS_PER_CLUSTER, + PLAT_MAX_CPUS_PER_CLUSTER, + PLAT_MAX_CPUS_PER_CLUSTER, + PLAT_MAX_CPUS_PER_CLUSTER, + PLAT_MAX_CPUS_PER_CLUSTER, + PLAT_MAX_CPUS_PER_CLUSTER, + PLAT_MAX_CPUS_PER_CLUSTER, + PLAT_MAX_CPUS_PER_CLUSTER, + PLAT_MAX_CPUS_PER_CLUSTER, +}; + +/******************************************************************************* + * This function returns the topology tree information. + ******************************************************************************/ +const unsigned char *plat_get_power_domain_tree_desc(void) +{ + return rd1_ae_pd_tree_desc; +} + +/******************************************************************************* + * The array mapping platform core position (implemented by plat_my_core_pos()) + * to the SCMI power domain ID implemented by SCP. + ******************************************************************************/ +const uint32_t plat_css_core_pos_to_scmi_dmn_id_map[] = { + (SET_SCMI_CHANNEL_ID(0x0) | SET_SCMI_DOMAIN_ID(0x0)), + (SET_SCMI_CHANNEL_ID(0x0) | SET_SCMI_DOMAIN_ID(0x1)), + (SET_SCMI_CHANNEL_ID(0x0) | SET_SCMI_DOMAIN_ID(0x2)), + (SET_SCMI_CHANNEL_ID(0x0) | SET_SCMI_DOMAIN_ID(0x3)), + (SET_SCMI_CHANNEL_ID(0x0) | SET_SCMI_DOMAIN_ID(0x4)), + (SET_SCMI_CHANNEL_ID(0x0) | SET_SCMI_DOMAIN_ID(0x5)), + (SET_SCMI_CHANNEL_ID(0x0) | SET_SCMI_DOMAIN_ID(0x6)), + (SET_SCMI_CHANNEL_ID(0x0) | SET_SCMI_DOMAIN_ID(0x7)), + (SET_SCMI_CHANNEL_ID(0x0) | SET_SCMI_DOMAIN_ID(0x8)), + (SET_SCMI_CHANNEL_ID(0x0) | SET_SCMI_DOMAIN_ID(0x9)), + (SET_SCMI_CHANNEL_ID(0x0) | SET_SCMI_DOMAIN_ID(0xA)), + (SET_SCMI_CHANNEL_ID(0x0) | SET_SCMI_DOMAIN_ID(0xB)), + (SET_SCMI_CHANNEL_ID(0x0) | SET_SCMI_DOMAIN_ID(0xC)), + (SET_SCMI_CHANNEL_ID(0x0) | SET_SCMI_DOMAIN_ID(0xD)), + (SET_SCMI_CHANNEL_ID(0x0) | SET_SCMI_DOMAIN_ID(0xE)), + (SET_SCMI_CHANNEL_ID(0x0) | SET_SCMI_DOMAIN_ID(0xF)), +}; + +unsigned int plat_arm_get_cluster_core_count(u_register_t mpidr) +{ + return PLAT_MAX_CPUS_PER_CLUSTER; +} diff --git a/plat/arm/board/common/board_arm_trusted_boot.c b/plat/arm/board/common/board_arm_trusted_boot.c index 655a4d2a..4a2572fc 100644 --- a/plat/arm/board/common/board_arm_trusted_boot.c +++ b/plat/arm/board/common/board_arm_trusted_boot.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015-2023, Arm Limited and Contributors. All rights reserved. + * Copyright (c) 2015-2024, Arm Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -219,6 +219,15 @@ int plat_get_nv_ctr(void *cookie, unsigned int *nv_ctr) } else if (strcmp(oid, NON_TRUSTED_FW_NVCOUNTER_OID) == 0) { nv_ctr_addr = (uint32_t *)FCONF_GET_PROPERTY(cot, nv_cntr_addr, NON_TRUSTED_NV_CTR_ID); +#if defined(ARM_COT_cca) + } else if (strcmp(oid, CCA_FW_NVCOUNTER_OID) == 0) { + /* + * Use Trusted NV counter for platforms that don't support + * the CCA NV Counter. + */ + nv_ctr_addr = (uint32_t *)FCONF_GET_PROPERTY(cot, nv_cntr_addr, + TRUSTED_NV_CTR_ID); +#endif } else { return 1; } diff --git a/plat/arm/board/common/rotpk/arm_dev_rotpk.S b/plat/arm/board/common/rotpk/arm_dev_rotpk.S index a7fadf6a..22ae9d35 100644 --- a/plat/arm/board/common/rotpk/arm_dev_rotpk.S +++ b/plat/arm/board/common/rotpk/arm_dev_rotpk.S @@ -1,17 +1,10 @@ /* - * Copyright (c) 2021-2022, ARM Limited. All rights reserved. + * Copyright (c) 2021-2024, ARM Limited. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ -/* corstone1000 platform provides custom values for the macros defined in - * arm_def.h , so only platform_def.h needs to be included - */ -#if !defined(TARGET_PLATFORM_FVP) && !defined(TARGET_PLATFORM_FPGA) -#include "plat/arm/common/arm_def.h" -#else -#include <platform_def.h> -#endif +#include <plat/arm/board/common/rotpk/rotpk_def.h> .global arm_rotpk_header .section .rodata.arm_rotpk_hash, "a" diff --git a/plat/arm/board/common/swd_rotpk/arm_swd_rotpk_rsa_sha256.bin b/plat/arm/board/common/swd_rotpk/arm_swd_rotpk_rsa_sha256.bin deleted file mode 100644 index b2f3e60a..00000000 --- a/plat/arm/board/common/swd_rotpk/arm_swd_rotpk_rsa_sha256.bin +++ /dev/null @@ -1 +0,0 @@ -0¾âÃ’æœÈË“(ì¨0ŠwIÓÕéã¡gk \ No newline at end of file diff --git a/plat/arm/board/corstone1000/common/corstone1000_bl2_mem_params_desc.c b/plat/arm/board/corstone1000/common/corstone1000_bl2_mem_params_desc.c index fe521a9f..de6a15a5 100644 --- a/plat/arm/board/corstone1000/common/corstone1000_bl2_mem_params_desc.c +++ b/plat/arm/board/corstone1000/common/corstone1000_bl2_mem_params_desc.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021-2022, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2021-2022, 2024 ARM Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -34,9 +34,14 @@ static bl_mem_params_node_t bl2_mem_params_descs[] = { .image_info.image_base = BL31_BASE, .image_info.image_max_size = BL31_LIMIT - BL31_BASE, +#ifdef CORSTONE1000_WITH_BL32 .next_handoff_image_id = BL32_IMAGE_ID, +#else + .next_handoff_image_id = BL33_IMAGE_ID, +#endif }, +#ifdef CORSTONE1000_WITH_BL32 /* Fill BL32 related information */ { .image_id = BL32_IMAGE_ID, @@ -65,14 +70,15 @@ static bl_mem_params_node_t bl2_mem_params_descs[] = { VERSION_2, image_info_t, 0), .next_handoff_image_id = INVALID_IMAGE_ID, }, - +#endif /* Fill BL33 related information */ { .image_id = BL33_IMAGE_ID, SET_STATIC_PARAM_HEAD(ep_info, PARAM_EP, VERSION_2, entry_point_info_t, NON_SECURE | EXECUTABLE), .ep_info.pc = BL33_BASE, - + .ep_info.spsr = SPSR_64(MODE_EL2, MODE_SP_ELX, + DISABLE_ALL_EXCEPTIONS), SET_STATIC_PARAM_HEAD(image_info, PARAM_EP, VERSION_2, image_info_t, 0), .image_info.image_base = BL33_BASE, diff --git a/plat/arm/board/corstone1000/common/corstone1000_helpers.S b/plat/arm/board/corstone1000/common/corstone1000_helpers.S index cbe27c3b..a4ca9fe9 100644 --- a/plat/arm/board/corstone1000/common/corstone1000_helpers.S +++ b/plat/arm/board/corstone1000/common/corstone1000_helpers.S @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021-2022, Arm Limited and Contributors. All rights reserved. + * Copyright (c) 2021-2024 Arm Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -21,8 +21,34 @@ * -------------------------------------------------------------------- */ func plat_secondary_cold_boot_setup +#if defined(CORSTONE1000_FVP_MULTICORE) + + /* Calculate the address of our hold entry */ + bl plat_my_core_pos + lsl x0, x0, #CORSTONE1000_SECONDARY_CORE_HOLD_SHIFT + mov_imm x2, CORSTONE1000_SECONDARY_CORE_HOLD_BASE + + /* Set the wait state for the secondary core */ + mov_imm x3, CORSTONE1000_SECONDARY_CORE_STATE_WAIT + str x3, [x2, x0] + dmb ish + + /* Poll until the primary core signals to go */ +poll_mailbox: + ldr x1, [x2, x0] + cmp x1, #CORSTONE1000_SECONDARY_CORE_STATE_WAIT + beq 1f + mov_imm x0, PLAT_ARM_TRUSTED_MAILBOX_BASE + ldr x1, [x0] + br x1 +1: + wfe + b poll_mailbox +#else cb_panic: b cb_panic +#endif + endfunc plat_secondary_cold_boot_setup /* --------------------------------------------------------------------- diff --git a/plat/arm/board/corstone1000/common/corstone1000_plat.c b/plat/arm/board/corstone1000/common/corstone1000_plat.c index ed3801ca..e388c82f 100644 --- a/plat/arm/board/corstone1000/common/corstone1000_plat.c +++ b/plat/arm/board/corstone1000/common/corstone1000_plat.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021-2023, Arm Limited and Contributors. All rights reserved. + * Copyright (c) 2021-2024, Arm Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -23,7 +23,6 @@ const mmap_region_t plat_arm_mmap[] = { ARM_MAP_SHARED_RAM, - ARM_MAP_NS_SHARED_RAM, ARM_MAP_NS_DRAM1, CORSTONE1000_MAP_DEVICE, CORSTONE1000_EXTERNAL_FLASH, diff --git a/plat/arm/board/corstone1000/common/corstone1000_pm.c b/plat/arm/board/corstone1000/common/corstone1000_pm.c index 4b0a791e..bd3faec8 100644 --- a/plat/arm/board/corstone1000/common/corstone1000_pm.c +++ b/plat/arm/board/corstone1000/common/corstone1000_pm.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, Arm Limited and Contributors. All rights reserved. + * Copyright (c) 2021-2024, Arm Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -7,6 +7,8 @@ #include <lib/psci/psci.h> #include <plat/arm/common/plat_arm.h> #include <platform_def.h> +#include <plat/common/platform.h> +#include <drivers/arm/gicv2.h> /******************************************************************************* * Export the platform handlers via plat_arm_psci_pm_ops. The ARM Standard * platform layer will take care of registering the handlers with PSCI. @@ -18,6 +20,15 @@ static void __dead2 corstone1000_system_reset(void) uint32_t volatile * const watchdog_ctrl_reg = (uint32_t *) SECURE_WATCHDOG_ADDR_CTRL_REG; uint32_t volatile * const watchdog_val_reg = (uint32_t *) SECURE_WATCHDOG_ADDR_VAL_REG; + /* + * Disable GIC CPU interface to prevent pending interrupt + * from waking up the AP from WFI. + */ + gicv2_cpuif_disable(); + + /* Flush and invalidate data cache */ + dcsw_op_all(DCCISW); + *(watchdog_val_reg) = SECURE_WATCHDOG_COUNTDOWN_VAL; *watchdog_ctrl_reg = SECURE_WATCHDOG_MASK_ENABLE; while (1) { @@ -25,9 +36,52 @@ static void __dead2 corstone1000_system_reset(void) } } +#if defined(CORSTONE1000_FVP_MULTICORE) +int corstone1000_validate_ns_entrypoint(uintptr_t entrypoint) +{ + /* + * Check if the non secure entrypoint lies within the non + * secure DRAM. + */ + if ((entrypoint >= ARM_NS_DRAM1_BASE) && (entrypoint < (ARM_NS_DRAM1_BASE + ARM_NS_DRAM1_SIZE))) { + return PSCI_E_SUCCESS; + } + return PSCI_E_INVALID_ADDRESS; +} + +int corstone1000_pwr_domain_on(u_register_t mpidr) +{ + int core_index = plat_core_pos_by_mpidr(mpidr); + uint64_t *secondary_core_hold_base = (uint64_t *)CORSTONE1000_SECONDARY_CORE_HOLD_BASE; + + /* Validate the core index */ + if (core_index < 0 || core_index > PLATFORM_CORE_COUNT) { + return PSCI_E_INVALID_PARAMS; + } + secondary_core_hold_base[core_index] = CORSTONE1000_SECONDARY_CORE_STATE_GO; + dsbish(); + sev(); + + return PSCI_E_SUCCESS; +} + +void corstone1000_pwr_domain_on_finish(const psci_power_state_t *target_state) +{ + (void)target_state; + plat_arm_gic_init(); +} +#endif + plat_psci_ops_t plat_arm_psci_pm_ops = { +#if defined(CORSTONE1000_FVP_MULTICORE) + .pwr_domain_on = corstone1000_pwr_domain_on, + .pwr_domain_on_finish = corstone1000_pwr_domain_on_finish, + .validate_ns_entrypoint = corstone1000_validate_ns_entrypoint, + .system_reset = corstone1000_system_reset, +#else + .validate_ns_entrypoint = NULL, .system_reset = corstone1000_system_reset, - .validate_ns_entrypoint = NULL +#endif }; const plat_psci_ops_t *plat_arm_psci_override_pm_ops(plat_psci_ops_t *ops) diff --git a/plat/arm/board/corstone1000/common/include/platform_def.h b/plat/arm/board/corstone1000/common/include/platform_def.h index 442d187f..caf3d462 100644 --- a/plat/arm/board/corstone1000/common/include/platform_def.h +++ b/plat/arm/board/corstone1000/common/include/platform_def.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021-2023, Arm Limited and Contributors. All rights reserved. + * Copyright (c) 2021-2024, Arm Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -10,15 +10,13 @@ #include <common/tbbr/tbbr_img_def.h> #include <lib/utils_def.h> #include <lib/xlat_tables/xlat_tables_defs.h> +#include <plat/arm/board/common/rotpk/rotpk_def.h> #include <plat/arm/board/common/v2m_def.h> #include <plat/arm/common/arm_spm_def.h> #include <plat/arm/common/smccc_def.h> #include <plat/common/common_def.h> #include <plat/arm/soc/common/soc_css_def.h> -#define ARM_ROTPK_HEADER_LEN 19 -#define ARM_ROTPK_HASH_LEN 32 - /* Special value used to verify platform parameters from BL2 to BL31 */ #define ARM_BL31_PLAT_PARAM_VAL ULL(0x0f1e2d3c4b5a6978) @@ -57,42 +55,42 @@ /* Memory related constants */ -/* SRAM (CVM) memory layout +/* Memory mappings of where the BLs in the FIP are copied to * - * <ARM_TRUSTED_SRAM_BASE> + * <ARM_TRUSTED_SRAM_BASE> = 0x02000000 * partition size: sizeof(meminfo_t) = 16 bytes * content: memory info area used by the next BL * - * <ARM_FW_CONFIG_BASE> + * <ARM_FW_CONFIG_BASE> = 0x02000010 * partition size: 4080 bytes * - * <ARM_BL2_MEM_DESC_BASE> + * <ARM_BL2_MEM_DESC_BASE> = 0x02001000 * partition size: 4 KB * content: Area where BL2 copies the images descriptors * - * <ARM_BL_RAM_BASE> = <BL32_BASE> - * partition size: 688 KB + * <ARM_BL_RAM_BASE> = <BL32_BASE> = 0x02002000 + * partition size: 3752 KB * content: BL32 (optee-os) * - * <CORSTONE1000_TOS_FW_CONFIG_BASE> = 0x20ae000 + * <CORSTONE1000_TOS_FW_CONFIG_BASE> = 0x023AC000 * partition size: 8 KB * content: BL32 config (TOS_FW_CONFIG) * - * <BL31_BASE> + * <BL31_BASE> = 0x023AE000 * partition size: 140 KB * content: BL31 * - * <BL2_SIGNATURE_BASE> + * <BL2_SIGNATURE_BASE> = 0x023D1000 * partition size: 4 KB * content: MCUBOOT data needed to verify TF-A BL2 * - * <BL2_BASE> + * <BL2_BASE> = 0x023D2000 * partition size: 176 KB * content: BL2 * - * <ARM_NS_SHARED_RAM_BASE> = <ARM_TRUSTED_SRAM_BASE> + 1 MB - * partition size: 512 KB - * content: BL33 (u-boot) + * <BL33_BASE> = 0x80000000 + * partition size: 12 MB + * content: BL33 (U-Boot) */ /* DDR memory */ @@ -117,11 +115,8 @@ /* The remaining Trusted SRAM is used to load the BL images */ #define TOTAL_SRAM_SIZE (SZ_4M) /* 4 MB */ -/* Last 512KB of CVM is allocated for shared RAM as an example openAMP */ -#define ARM_NS_SHARED_RAM_SIZE (512 * SZ_1K) #define PLAT_ARM_TRUSTED_SRAM_SIZE (TOTAL_SRAM_SIZE - \ - ARM_NS_SHARED_RAM_SIZE - \ ARM_SHARED_RAM_SIZE) #define PLAT_ARM_MAX_BL2_SIZE (180 * SZ_1K) /* 180 KB */ @@ -160,11 +155,6 @@ /* NS memory */ -/* The last 512KB of the SRAM is allocated as shared memory */ -#define ARM_NS_SHARED_RAM_BASE (ARM_TRUSTED_SRAM_BASE + TOTAL_SRAM_SIZE - \ - (PLAT_ARM_MAX_BL31_SIZE + \ - PLAT_ARM_MAX_BL32_SIZE)) - #define BL33_BASE ARM_DRAM1_BASE #define PLAT_ARM_MAX_BL33_SIZE (12 * SZ_1M) /* 12 MB*/ #define BL33_LIMIT (ARM_DRAM1_BASE + PLAT_ARM_MAX_BL33_SIZE) @@ -264,9 +254,22 @@ #define ARM_LOCAL_STATE_OFF U(2) #define PLAT_ARM_TRUSTED_MAILBOX_BASE ARM_TRUSTED_SRAM_BASE + +#if defined(CORSTONE1000_FVP_MULTICORE) +/* The secondary core entrypoint address points to bl31_warm_entrypoint + * and the address size is 8 bytes */ +#define CORSTONE1000_SECONDARY_CORE_ENTRYPOINT_ADDRESS_SIZE UL(0x8) + +#define CORSTONE1000_SECONDARY_CORE_HOLD_BASE (PLAT_ARM_TRUSTED_MAILBOX_BASE + \ + CORSTONE1000_SECONDARY_CORE_ENTRYPOINT_ADDRESS_SIZE) +#define CORSTONE1000_SECONDARY_CORE_STATE_WAIT ULL(0) +#define CORSTONE1000_SECONDARY_CORE_STATE_GO ULL(1) +#define CORSTONE1000_SECONDARY_CORE_HOLD_SHIFT ULL(3) +#endif + #define PLAT_ARM_NSTIMER_FRAME_ID U(1) -#define PLAT_ARM_NS_IMAGE_BASE (ARM_NS_SHARED_RAM_BASE) +#define PLAT_ARM_NS_IMAGE_BASE (BL33_BASE) #define PLAT_PHY_ADDR_SPACE_SIZE (1ULL << 32) #define PLAT_VIRT_ADDR_SPACE_SIZE (1ULL << 32) @@ -295,11 +298,6 @@ ARM_SHARED_RAM_SIZE, \ MT_MEMORY | MT_RW | MT_SECURE) -#define ARM_MAP_NS_SHARED_RAM MAP_REGION_FLAT( \ - ARM_NS_SHARED_RAM_BASE, \ - ARM_NS_SHARED_RAM_SIZE, \ - MT_MEMORY | MT_RW | MT_NS) - #define ARM_MAP_NS_DRAM1 MAP_REGION_FLAT( \ ARM_NS_DRAM1_BASE, \ ARM_NS_DRAM1_SIZE, \ diff --git a/plat/arm/board/corstone1000/platform.mk b/plat/arm/board/corstone1000/platform.mk index dcd0df84..dfde5aaf 100644 --- a/plat/arm/board/corstone1000/platform.mk +++ b/plat/arm/board/corstone1000/platform.mk @@ -1,12 +1,12 @@ # -# Copyright (c) 2021-2023, Arm Limited and Contributors. All rights reserved. +# Copyright (c) 2021-2024 Arm Limited and Contributors. All rights reserved. # # SPDX-License-Identifier: BSD-3-Clause # # Making sure the corstone1000 platform type is specified ifeq ($(filter ${TARGET_PLATFORM}, fpga fvp),) - $(error TARGET_PLATFORM must be fpga or fvp) + $(error TARGET_PLATFORM must be fpga or fvp) endif CORSTONE1000_CPU_LIBS +=lib/cpus/aarch64/cortex_a35.S @@ -28,9 +28,21 @@ FIP_BL2_ARGS := tb-fw override NEED_BL2U := no override NEED_BL31 := yes -NEED_BL32 := yes +NEED_BL32 ?= yes override NEED_BL33 := yes +# Add CORSTONE1000_WITH_BL32 as a preprocessor define (-D option) +ifeq (${NEED_BL32},yes) +$(eval $(call add_define,CORSTONE1000_WITH_BL32)) +endif + +ENABLE_MULTICORE := 0 +ifneq ($(filter ${TARGET_PLATFORM}, fvp),) +ifeq (${ENABLE_MULTICORE},1) +$(eval $(call add_define,CORSTONE1000_FVP_MULTICORE)) +endif +endif + # Include GICv2 driver files include drivers/arm/gic/v2/gicv2.mk diff --git a/plat/arm/board/fvp/aarch64/fvp_helpers.S b/plat/arm/board/fvp/aarch64/fvp_helpers.S index 8efc2386..46fb44a7 100644 --- a/plat/arm/board/fvp/aarch64/fvp_helpers.S +++ b/plat/arm/board/fvp/aarch64/fvp_helpers.S @@ -6,9 +6,10 @@ #include <arch.h> #include <asm_macros.S> +#include <drivers/arm/fvp/fvp_cpu_pwr.h> +#include <drivers/arm/fvp/fvp_pwrc.h> #include <drivers/arm/gicv2.h> #include <drivers/arm/gicv3.h> -#include <drivers/arm/fvp/fvp_pwrc.h> #include <platform_def.h> .globl plat_secondary_cold_boot_setup @@ -29,6 +30,21 @@ */ func plat_secondary_cold_boot_setup #ifndef EL3_PAYLOAD_BASE + + /* -------------------------------------------- + * Check if core supports powering down, if it + * supports power down then set core power down + * bit before requesting for the cores to be + * powered off from base power controller. + * --------------------------------------------- + */ + bl check_cpupwrctrl_el1_is_available + cbz x0, base_power_off + + mrs x1, CPUPWRCTLR_EL1 + orr x1, x1, #CPUPWRCTLR_EL1_CORE_PWRDN_BIT + msr CPUPWRCTLR_EL1, x1 + /* --------------------------------------------- * Power down this cpu. * TODO: Do we need to worry about powering the @@ -37,6 +53,7 @@ func plat_secondary_cold_boot_setup * loader zeroes out the zi section. * --------------------------------------------- */ +base_power_off: mrs x0, mpidr_el1 mov_imm x1, PWRC_BASE str w0, [x1, #PPOFFR_OFF] diff --git a/plat/arm/board/fvp/aarch64/fvp_lsp_ras_sp.c b/plat/arm/board/fvp/aarch64/fvp_lsp_ras_sp.c new file mode 100644 index 00000000..5324fec9 --- /dev/null +++ b/plat/arm/board/fvp/aarch64/fvp_lsp_ras_sp.c @@ -0,0 +1,121 @@ +/* + * Copyright (c) 2024, Arm Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <inttypes.h> +#include <stdint.h> + +#include <lib/el3_runtime/context_mgmt.h> +#include <lib/extensions/ras.h> + +#include <plat/common/platform.h> +#include <services/el3_spmd_logical_sp.h> +#include <services/ffa_svc.h> +#include <services/sdei.h> + + +#define CACTUS_SP_RAS_DELEGATE_CMD 0x72617365 +#define EVENT_NOTIFY_OS_RAS_ERROR U(5000) + +/* + * Note: Typical RAS error handling flow with Firmware First Handling + * + * Step 1: Exception resulting from a RAS error in the normal world is routed to + * EL3. + * Step 2: This exception is typically signaled as either a synchronous external + * abort or SError or interrupt. TF-A (EL3 firmware) delegates the + * control to platform specific handler built on top of the RAS helper + * utilities. + * Step 3: With the help of a Logical Secure Partition, TF-A sends a direct + * message to dedicated S-EL0 (or S-EL1) RAS Partition managed by SPMC. + * TF-A also populates a shared buffer with a data structure containing + * enough information (such as system registers) to identify and triage + * the RAS error. + * Step 4: RAS SP generates the Common Platform Error Record (CPER) and shares + * it with normal world firmware and/or OS kernel through a reserved + * buffer memory. + * Step 5: RAS SP responds to the direct message with information necessary for + * TF-A to notify the OS kernel. + * Step 6: Consequently, TF-A dispatches an SDEI event to notify the OS kernel + * about the CPER records for further logging. + */ + +static int injected_fault_handler(const struct err_record_info *info, + int probe_data, const struct err_handler_data *const data) +{ + /* + * At the moment, an FF-A compatible SP that supports RAS firmware is + * not available. Hence the sequence below does not exactly follow the + * steps outlined above. Therefore, some steps are essentially spoofed. + * The handling of RAS error is completely done in EL3 firmware. + */ + uint64_t status, cactus_cmd_ret; + int ret, event_num; + cpu_context_t *ns_cpu_context; + + /* Get a reference to the non-secure context */ + ns_cpu_context = cm_get_context(NON_SECURE); + assert(ns_cpu_context != NULL); + + /* + * The faulting error record is already selected by the SER probe + * function. + */ + status = read_erxstatus_el1(); + + ERROR("Fault reported by system error record %d on 0x%lx: status=0x%" PRIx64 "\n", + probe_data, read_mpidr_el1(), status); + ERROR(" exception reason=%u syndrome=0x%" PRIx64 "\n", data->ea_reason, + data->flags); + + /* Clear error */ + write_erxstatus_el1(status); + + /* + * Initiate an EL3 direct message from LSP to Cactus RAS Secure + * Partition (ID 8001). Currently, the payload is being spoofed. + * The direct message response contains the SDEI event ID for the + * associated RAS error. + */ + (void)plat_spmd_logical_sp_smc_handler(0, 0, 0, CACTUS_SP_RAS_DELEGATE_CMD, + EVENT_NOTIFY_OS_RAS_ERROR, + NULL, ns_cpu_context, 0); + + cactus_cmd_ret = read_ctx_reg(get_gpregs_ctx(ns_cpu_context), CTX_GPREG_X3); + event_num = (int)read_ctx_reg(get_gpregs_ctx(ns_cpu_context), CTX_GPREG_X4); + + if (cactus_cmd_ret != 0) { + ERROR("RAS error could not be handled by SP: %lx\n", cactus_cmd_ret); + panic(); + } + + if (event_num != EVENT_NOTIFY_OS_RAS_ERROR) { + ERROR("Unexpected event id sent by RAS SP: %d\n", event_num); + panic(); + } + + /* Dispatch the event to the SDEI client */ + ret = sdei_dispatch_event(event_num); + if (ret < 0) { + ERROR("Can't dispatch event to SDEI\n"); + panic(); + } else { + INFO("SDEI event dispatched\n"); + } + + return 0; +} + +struct ras_interrupt fvp_ras_interrupts[] = { +}; + +struct err_record_info fvp_err_records[] = { + /* Record for injected fault */ + ERR_RECORD_SYSREG_V1(0, 2, ras_err_ser_probe_sysreg, + injected_fault_handler, NULL), +}; + +REGISTER_ERR_RECORD_INFO(fvp_err_records); +REGISTER_RAS_INTERRUPTS(fvp_ras_interrupts); diff --git a/plat/arm/board/fvp/fdts/fvp_cactus_sp_manifest.dts b/plat/arm/board/fvp/fdts/fvp_cactus_sp_manifest.dts new file mode 100644 index 00000000..de804e05 --- /dev/null +++ b/plat/arm/board/fvp/fdts/fvp_cactus_sp_manifest.dts @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2024, Arm Limited. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + * + * This file is a Partition Manifest (PM) for a minimal Secure Partition (SP) + * that will be consumed by EL3 SPMC. + * + */ + +/dts-v1/; + +/ { + compatible = "arm,ffa-manifest-1.0"; + #address-cells = <2>; + #size-cells = <1>; + + /* Properties */ + ffa-version = <0x00010001>; /* 31:16 - Major, 15:0 - Minor */ + id = <0x8001>; + uuid = <0x1e67b5b4 0xe14f904a 0x13fb1fb8 0xcbdae1da>; + messaging-method = <3>; /* Direct messaging only */ + exception-level = <2>; /* S-EL1 */ + execution-state = <0>; /* AARCH64 */ + execution-ctx-count = <8>; + /* Boot protocol */ + gp-register-num = <0>; +}; diff --git a/plat/arm/board/fvp/fdts/fvp_cot_desc.dtsi b/plat/arm/board/fvp/fdts/fvp_cot_desc.dtsi new file mode 100644 index 00000000..9c8328bb --- /dev/null +++ b/plat/arm/board/fvp/fdts/fvp_cot_desc.dtsi @@ -0,0 +1,16 @@ +/* + * Copyright (c) 2024, Arm Limited. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + + +#if COT_DESC_IN_DTB + #if defined(ARM_COT_cca) + #include "cca_cot_descriptors.dtsi" + #elif defined(ARM_COT_dualroot) + #include "dualroot_cot_descriptors.dtsi" + #elif defined(ARM_COT_tbbr) + #include "tbbr_cot_descriptors.dtsi" + #endif +#endif diff --git a/plat/arm/board/fvp/fdts/fvp_fw_config.dts b/plat/arm/board/fvp/fdts/fvp_fw_config.dts index 4adf5d58..5d587311 100644 --- a/plat/arm/board/fvp/fdts/fvp_fw_config.dts +++ b/plat/arm/board/fvp/fdts/fvp_fw_config.dts @@ -1,10 +1,11 @@ /* - * Copyright (c) 2019-2023, Arm Limited. All rights reserved. + * Copyright (c) 2019-2024, Arm Limited. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ #include <common/tbbr/tbbr_img_def.h> +#include <platform_def.h> /dts-v1/; @@ -20,7 +21,7 @@ hw-config { load-address = <0x0 0x07f00000>; - max-size = <0x00100000>; + max-size = <PLAT_ARM_HW_CONFIG_SIZE>; id = <HW_CONFIG_ID>; secondary-load-address = <0x0 0x82000000>; }; diff --git a/plat/arm/board/fvp/fdts/fvp_spmc_el1_optee_manifest.dts b/plat/arm/board/fvp/fdts/fvp_spmc_el1_optee_manifest.dts new file mode 100644 index 00000000..36a22a1b --- /dev/null +++ b/plat/arm/board/fvp/fdts/fvp_spmc_el1_optee_manifest.dts @@ -0,0 +1,22 @@ +/* SPDX-License-Identifier: BSD-3-Clause */ +/* + * Copyright (c) 2024, Arm Limited. All rights reserved. + */ + +/dts-v1/; + +/ { + compatible = "arm,ffa-core-manifest-1.0"; + #address-cells = <2>; + #size-cells = <1>; + + attribute { + spmc_id = <0x8000>; + maj_ver = <0x1>; + min_ver = <0x1>; + exec_state = <0x0>; + load_address = <0x0 0x6000000>; + entrypoint = <0x0 0x6000000>; + binary_size = <0x80000>; + }; +}; diff --git a/plat/arm/board/fvp/fdts/fvp_spmc_manifest.dts b/plat/arm/board/fvp/fdts/fvp_spmc_manifest.dts index e159248b..bf0e7f38 100644 --- a/plat/arm/board/fvp/fdts/fvp_spmc_manifest.dts +++ b/plat/arm/board/fvp/fdts/fvp_spmc_manifest.dts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020-2023, Arm Limited. All rights reserved. + * Copyright (c) 2020-2024, Arm Limited. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -20,7 +20,7 @@ attribute { spmc_id = <0x8000>; maj_ver = <0x1>; - min_ver = <0x1>; + min_ver = <0x2>; exec_state = <0x0>; load_address = <0x0 0x6000000>; entrypoint = <0x0 0x6000000>; @@ -92,10 +92,25 @@ memory@1 { device_type = "ns-memory"; - reg = <0x00008800 0x80000000 0x0 0x7f000000>, - <0x0 0x88000000 0x0 0x10000000>; + reg = <0x0 0x80000000 0x0 0x7c000000>, + <0x8 0x80000000 0x1 0x80000000>, + <0x00008800 0x80000000 0x0 0x7f000000>; }; + memory@2 { + device_type = "device-memory"; + reg = <0x0 0x1c0b0000 0x0 0x20000>, /* UART 2-3 */ + <0x0 0x2bfe0000 0x0 0x20000>, /* SMMUv3TestEngine */ + <0x0 0x2a490000 0x0 0x20000>, /* SP805 Trusted Watchdog */ + <0x0 0x1c130000 0x0 0x10000>; /* Virtio block device */ + }; + + memory@3 { + device_type = "ns-device-memory"; + reg = <0x0 0x1c090000 0x0 0x20000>; /* UART 0-1 */ + }; + + #if MEASURED_BOOT #include "event_log.dtsi" #endif diff --git a/plat/arm/board/fvp/fdts/fvp_spmc_optee_sp_manifest.dts b/plat/arm/board/fvp/fdts/fvp_spmc_optee_sp_manifest.dts index 041dade7..234ab587 100644 --- a/plat/arm/board/fvp/fdts/fvp_spmc_optee_sp_manifest.dts +++ b/plat/arm/board/fvp/fdts/fvp_spmc_optee_sp_manifest.dts @@ -1,8 +1,9 @@ /* - * Copyright (c) 2020-2021, Arm Limited. All rights reserved. + * Copyright (c) 2020-2024, Arm Limited. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ + /dts-v1/; #define AFF 00 @@ -15,12 +16,12 @@ / { compatible = "arm,ffa-core-manifest-1.0"; #address-cells = <2>; - #size-cells = <1>; + #size-cells = <2>; attribute { spmc_id = <0x8000>; maj_ver = <0x1>; - min_ver = <0x1>; + min_ver = <0x2>; exec_state = <0x0>; load_address = <0x0 0x6000000>; entrypoint = <0x0 0x6000000>; @@ -34,7 +35,7 @@ debug_name = "op-tee"; load_address = <0x6280000>; vcpu_count = <8>; - mem_size = <1048576>; + mem_size = <0xd80000>; }; }; @@ -59,6 +60,18 @@ memory@6000000 { device_type = "memory"; - reg = <0x0 0x6000000 0x2000000>; /* Trusted DRAM */ + reg = <0x0 0x6000000 0x0 0x2000000>; /* Trusted DRAM */ + }; + + memory@80000000 { + device_type = "ns-memory"; + reg = <0x0 0x80000000 0x0 0x7c000000>, + <0x8 0x80000000 0x1 0x80000000>, + <0x00008800 0x80000000 0x0 0x7f000000>; + }; + + memory@0 { + device_type = "device-memory"; + reg = <0x0 0x1c090000 0x0 0x40000>; /* UART */ }; }; diff --git a/plat/arm/board/fvp/fdts/fvp_tb_fw_config.dts b/plat/arm/board/fvp/fdts/fvp_tb_fw_config.dts index 6ba76db6..f5d7f658 100644 --- a/plat/arm/board/fvp/fdts/fvp_tb_fw_config.dts +++ b/plat/arm/board/fvp/fdts/fvp_tb_fw_config.dts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020-2023, ARM Limited. All rights reserved. + * Copyright (c) 2020-2024, Arm Limited. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -103,9 +103,7 @@ #endif /* ARM_BL2_SP_LIST_DTS */ }; -#if COT_DESC_IN_DTB - #include "cot_descriptors.dtsi" -#endif +#include "fvp_cot_desc.dtsi" #if MEASURED_BOOT #include "event_log.dtsi" @@ -117,11 +115,18 @@ #include "../fvp_def.h" -&trusted_nv_counter { +#if defined(ARM_COT_cca) +/* FVP does not support the CCA NV Counter so use the Trusted one. */ +&cca_nv_ctr { + reg = <TFW_NVCTR_BASE>; +}; +#endif + +&trusted_nv_ctr { reg = <TFW_NVCTR_BASE>; }; -&non_trusted_nv_counter { +&non_trusted_nv_ctr { reg = <NTFW_CTR_BASE>; }; #endif diff --git a/plat/arm/board/fvp/fdts/optee_sp_manifest.dts b/plat/arm/board/fvp/fdts/optee_sp_manifest.dts index 27f4724c..4611f806 100644 --- a/plat/arm/board/fvp/fdts/optee_sp_manifest.dts +++ b/plat/arm/board/fvp/fdts/optee_sp_manifest.dts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020-2022, Arm Limited. All rights reserved. + * Copyright (c) 2020-2024, Arm Limited. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause * @@ -15,23 +15,30 @@ /* Properties */ description = "op-tee"; - ffa-version = <0x00010000>; /* 31:16 - Major, 15:0 - Minor */ + ffa-version = <0x00010001>; /* 31:16 - Major, 15:0 - Minor */ uuid = <0xe0786148 0xe311f8e7 0x02005ebc 0x1bc5d5a5>; id = <1>; execution-ctx-count = <8>; exception-level = <2>; /* S-EL1 */ execution-state = <0>; /* AARCH64 */ load-address = <0x6280000>; + mem-size = <0xd80000>; /* OP-TEE specific extension */ entrypoint-offset = <0x4000>; xlat-granule = <0>; /* 4KiB */ boot-order = <0>; messaging-method = <0x3>; /* Direct request/response supported. */ - managed-exit; + ns-interrupts-action = <1>; /* NS_ACTION_ME */ run-time-model = <1>; /* SP pre-emptible. */ /* Boot protocol */ gp-register-num = <0x0>; + /* Boot Info */ + boot-info { + compatible = "arm,ffa-manifest-boot-info"; + ffa_manifest; + }; + device-regions { compatible = "arm,ffa-manifest-device-regions"; @@ -39,6 +46,7 @@ base-address = <0x00000000 0x1c0a0000>; pages-count = <1>; attributes = <0x3>; /* read-write */ + interrupts = <38 0x900>; }; }; }; diff --git a/plat/arm/board/fvp/fvp_bl1_measured_boot.c b/plat/arm/board/fvp/fvp_bl1_measured_boot.c index dc95ba1c..f14dbffa 100644 --- a/plat/arm/board/fvp/fvp_bl1_measured_boot.c +++ b/plat/arm/board/fvp/fvp_bl1_measured_boot.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021-2023, Arm Limited. All rights reserved. + * Copyright (c) 2021-2024, Arm Limited. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -7,7 +7,7 @@ #include <stdint.h> #include <drivers/measured_boot/event_log/event_log.h> -#include <drivers/measured_boot/rss/rss_measured_boot.h> +#include <drivers/measured_boot/metadata.h> #include <plat/arm/common/plat_arm.h> #include <tools_share/zero_oid.h> @@ -16,49 +16,17 @@ static uint8_t event_log[PLAT_ARM_EVENT_LOG_MAX_SIZE]; /* FVP table with platform specific image IDs, names and PCRs */ const event_log_metadata_t fvp_event_log_metadata[] = { - { FW_CONFIG_ID, EVLOG_FW_CONFIG_STRING, PCR_0 }, - { TB_FW_CONFIG_ID, EVLOG_TB_FW_CONFIG_STRING, PCR_0 }, - { BL2_IMAGE_ID, EVLOG_BL2_STRING, PCR_0 }, + { FW_CONFIG_ID, MBOOT_FW_CONFIG_STRING, PCR_0 }, + { TB_FW_CONFIG_ID, MBOOT_TB_FW_CONFIG_STRING, PCR_0 }, + { BL2_IMAGE_ID, MBOOT_BL2_IMAGE_STRING, PCR_0 }, { EVLOG_INVALID_ID, NULL, (unsigned int)(-1) } /* Terminator */ }; -/* FVP table with platform specific image IDs and metadata. Intentionally not a - * const struct, some members might set by bootloaders during trusted boot. - */ -struct rss_mboot_metadata fvp_rss_mboot_metadata[] = { - { - .id = FW_CONFIG_ID, - .slot = U(6), - .signer_id_size = SIGNER_ID_MIN_SIZE, - .sw_type = RSS_MBOOT_FW_CONFIG_STRING, - .pk_oid = ZERO_OID, - .lock_measurement = true }, - { - .id = TB_FW_CONFIG_ID, - .slot = U(7), - .signer_id_size = SIGNER_ID_MIN_SIZE, - .sw_type = RSS_MBOOT_TB_FW_CONFIG_STRING, - .pk_oid = ZERO_OID, - .lock_measurement = true }, - { - .id = BL2_IMAGE_ID, - .slot = U(8), - .signer_id_size = SIGNER_ID_MIN_SIZE, - .sw_type = RSS_MBOOT_BL2_STRING, - .pk_oid = ZERO_OID, - .lock_measurement = true }, - - { - .id = RSS_MBOOT_INVALID_ID } -}; - void bl1_plat_mboot_init(void) { event_log_init(event_log, event_log + sizeof(event_log)); event_log_write_header(); - - rss_measured_boot_init(fvp_rss_mboot_metadata); } void bl1_plat_mboot_finish(void) diff --git a/plat/arm/board/fvp/fvp_bl2_measured_boot.c b/plat/arm/board/fvp/fvp_bl2_measured_boot.c index 349e064d..8bf7dad6 100644 --- a/plat/arm/board/fvp/fvp_bl2_measured_boot.c +++ b/plat/arm/board/fvp/fvp_bl2_measured_boot.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021-2023, Arm Limited. All rights reserved. + * Copyright (c) 2021-2024, Arm Limited. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -8,7 +8,7 @@ #include <common/tbbr/tbbr_img_def.h> #include <drivers/measured_boot/event_log/event_log.h> -#include <drivers/measured_boot/rss/rss_measured_boot.h> +#include <drivers/measured_boot/metadata.h> #if defined(ARM_COT_cca) #include <tools_share/cca_oid.h> #else @@ -29,27 +29,27 @@ static uint64_t event_log_base; /* FVP table with platform specific image IDs, names and PCRs */ const event_log_metadata_t fvp_event_log_metadata[] = { - { BL31_IMAGE_ID, EVLOG_BL31_STRING, PCR_0 }, - { BL32_IMAGE_ID, EVLOG_BL32_STRING, PCR_0 }, - { BL32_EXTRA1_IMAGE_ID, EVLOG_BL32_EXTRA1_STRING, PCR_0 }, - { BL32_EXTRA2_IMAGE_ID, EVLOG_BL32_EXTRA2_STRING, PCR_0 }, - { BL33_IMAGE_ID, EVLOG_BL33_STRING, PCR_0 }, - { HW_CONFIG_ID, EVLOG_HW_CONFIG_STRING, PCR_0 }, - { NT_FW_CONFIG_ID, EVLOG_NT_FW_CONFIG_STRING, PCR_0 }, - { SCP_BL2_IMAGE_ID, EVLOG_SCP_BL2_STRING, PCR_0 }, - { SOC_FW_CONFIG_ID, EVLOG_SOC_FW_CONFIG_STRING, PCR_0 }, - { TOS_FW_CONFIG_ID, EVLOG_TOS_FW_CONFIG_STRING, PCR_0 }, - { RMM_IMAGE_ID, EVLOG_RMM_STRING, PCR_0}, + { BL31_IMAGE_ID, MBOOT_BL31_IMAGE_STRING, PCR_0 }, + { BL32_IMAGE_ID, MBOOT_BL32_IMAGE_STRING, PCR_0 }, + { BL32_EXTRA1_IMAGE_ID, MBOOT_BL32_EXTRA1_IMAGE_STRING, PCR_0 }, + { BL32_EXTRA2_IMAGE_ID, MBOOT_BL32_EXTRA2_IMAGE_STRING, PCR_0 }, + { BL33_IMAGE_ID, MBOOT_BL33_IMAGE_STRING, PCR_0 }, + { HW_CONFIG_ID, MBOOT_HW_CONFIG_STRING, PCR_0 }, + { NT_FW_CONFIG_ID, MBOOT_NT_FW_CONFIG_STRING, PCR_0 }, + { SCP_BL2_IMAGE_ID, MBOOT_SCP_BL2_IMAGE_STRING, PCR_0 }, + { SOC_FW_CONFIG_ID, MBOOT_SOC_FW_CONFIG_STRING, PCR_0 }, + { TOS_FW_CONFIG_ID, MBOOT_TOS_FW_CONFIG_STRING, PCR_0 }, + { RMM_IMAGE_ID, MBOOT_RMM_IMAGE_STRING, PCR_0}, #if defined(SPD_spmd) - { SP_PKG1_ID, EVLOG_SP1_STRING, PCR_0 }, - { SP_PKG2_ID, EVLOG_SP2_STRING, PCR_0 }, - { SP_PKG3_ID, EVLOG_SP3_STRING, PCR_0 }, - { SP_PKG4_ID, EVLOG_SP4_STRING, PCR_0 }, - { SP_PKG5_ID, EVLOG_SP5_STRING, PCR_0 }, - { SP_PKG6_ID, EVLOG_SP6_STRING, PCR_0 }, - { SP_PKG7_ID, EVLOG_SP7_STRING, PCR_0 }, - { SP_PKG8_ID, EVLOG_SP8_STRING, PCR_0 }, + { SP_PKG1_ID, MBOOT_SP1_STRING, PCR_0 }, + { SP_PKG2_ID, MBOOT_SP2_STRING, PCR_0 }, + { SP_PKG3_ID, MBOOT_SP3_STRING, PCR_0 }, + { SP_PKG4_ID, MBOOT_SP4_STRING, PCR_0 }, + { SP_PKG5_ID, MBOOT_SP5_STRING, PCR_0 }, + { SP_PKG6_ID, MBOOT_SP6_STRING, PCR_0 }, + { SP_PKG7_ID, MBOOT_SP7_STRING, PCR_0 }, + { SP_PKG8_ID, MBOOT_SP8_STRING, PCR_0 }, #endif { CRITICAL_DATA_ID, EVLOG_CRITICAL_DATA_STRING, PCR_1 }, @@ -57,44 +57,6 @@ const event_log_metadata_t fvp_event_log_metadata[] = { { EVLOG_INVALID_ID, NULL, (unsigned int)(-1) } /* Terminator */ }; -/* FVP table with platform specific image IDs and metadata. Intentionally not a - * const struct, some members might set by bootloaders during trusted boot. - */ -struct rss_mboot_metadata fvp_rss_mboot_metadata[] = { - { - .id = BL31_IMAGE_ID, - .slot = U(9), - .signer_id_size = SIGNER_ID_MIN_SIZE, - .sw_type = RSS_MBOOT_BL31_STRING, - .pk_oid = BL31_IMAGE_KEY_OID, - .lock_measurement = true }, - { - .id = HW_CONFIG_ID, - .slot = U(10), - .signer_id_size = SIGNER_ID_MIN_SIZE, - .sw_type = RSS_MBOOT_HW_CONFIG_STRING, - .pk_oid = HW_CONFIG_KEY_OID, - .lock_measurement = true }, - { - .id = SOC_FW_CONFIG_ID, - .slot = U(11), - .signer_id_size = SIGNER_ID_MIN_SIZE, - .sw_type = RSS_MBOOT_SOC_FW_CONFIG_STRING, - .pk_oid = SOC_FW_CONFIG_KEY_OID, - .lock_measurement = true }, -#if ENABLE_RME - { - .id = RMM_IMAGE_ID, - .slot = U(12), - .signer_id_size = SIGNER_ID_MIN_SIZE, - .sw_type = RSS_MBOOT_RMM_STRING, - .pk_oid = RMM_IMAGE_KEY_OID, - .lock_measurement = true }, -#endif /* ENABLE_RME */ - { - .id = RSS_MBOOT_INVALID_ID } -}; - void bl2_plat_mboot_init(void) { uint8_t *event_log_start; @@ -126,8 +88,6 @@ void bl2_plat_mboot_init(void) event_log_max_size); event_log_init((uint8_t *)event_log_start, event_log_finish); - - rss_measured_boot_init(fvp_rss_mboot_metadata); } int plat_mboot_measure_critical_data(unsigned int critical_data_id, diff --git a/plat/arm/board/fvp/fvp_bl2_setup.c b/plat/arm/board/fvp/fvp_bl2_setup.c index ebd52664..ebdd80d4 100644 --- a/plat/arm/board/fvp/fvp_bl2_setup.c +++ b/plat/arm/board/fvp/fvp_bl2_setup.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013-2023, Arm Limited and Contributors. All rights reserved. + * Copyright (c) 2013-2024, Arm Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -9,6 +9,7 @@ #include <common/debug.h> #include <common/desc_image_load.h> #include <drivers/arm/sp804_delay_timer.h> +#include <fvp_pas_def.h> #include <lib/fconf/fconf.h> #include <lib/fconf/fconf_dyn_cfg_getter.h> #include <lib/transfer_list.h> @@ -19,10 +20,39 @@ #include "fvp_private.h" -static struct transfer_list_header *ns_tl __unused; +#if ENABLE_RME +/* + * The GPT library might modify the gpt regions structure to optimize + * the layout, so the array cannot be constant. + */ +static pas_region_t pas_regions[] = { + ARM_PAS_KERNEL, + ARM_PAS_SECURE, + ARM_PAS_REALM, + ARM_PAS_EL3_DRAM, + ARM_PAS_GPTS, + ARM_PAS_KERNEL_1 +}; + +static const arm_gpt_info_t arm_gpt_info = { + .pas_region_base = pas_regions, + .pas_region_count = (unsigned int)ARRAY_SIZE(pas_regions), + .l0_base = (uintptr_t)ARM_L0_GPT_BASE, + .l1_base = (uintptr_t)ARM_L1_GPT_BASE, + .l0_size = (size_t)ARM_L0_GPT_SIZE, + .l1_size = (size_t)ARM_L1_GPT_SIZE, + .pps = GPCCR_PPS_64GB, + .pgs = GPCCR_PGS_4K +}; +#endif void bl2_early_platform_setup2(u_register_t arg0, u_register_t arg1, u_register_t arg2, u_register_t arg3) { + struct transfer_list_entry *te __unused; + +#if TRANSFER_LIST + arg0 = arg3; +#endif arm_bl2_early_platform_setup((uintptr_t)arg0, (meminfo_t *)arg1); /* Initialize the platform config for future decision making */ @@ -33,30 +63,32 @@ void bl2_platform_setup(void) { arm_bl2_platform_setup(); -#if TRANSFER_LIST - ns_tl = transfer_list_init((void *)FW_NS_HANDOFF_BASE, FW_HANDOFF_SIZE); - assert(ns_tl != NULL); -#endif /* Initialize System level generic or SP804 timer */ fvp_timer_init(); } +#if ENABLE_RME +const arm_gpt_info_t *plat_arm_get_gpt_info(void) +{ + return &arm_gpt_info; +} +#endif /* ENABLE_RME */ + /******************************************************************************* * This function returns the list of executable images ******************************************************************************/ struct bl_params *plat_get_next_bl_params(void) { struct bl_params *arm_bl_params; - const struct dyn_cfg_dtb_info_t *hw_config_info __unused; - struct transfer_list_entry *te __unused; bl_mem_params_node_t *param_node __unused; + const struct dyn_cfg_dtb_info_t *fw_config_info __unused; + const struct dyn_cfg_dtb_info_t *hw_config_info __unused; + entry_point_info_t *ep __unused; + uint32_t next_exe_img_id __unused; + uintptr_t fw_config_base __unused; arm_bl_params = arm_get_next_bl_params(); -#if !RESET_TO_BL2 && !EL3_PAYLOAD_BASE - const struct dyn_cfg_dtb_info_t *fw_config_info; - uintptr_t fw_config_base = 0UL; - #if __aarch64__ /* Get BL31 image node */ param_node = get_bl_mem_params_node(BL31_IMAGE_ID); @@ -66,6 +98,15 @@ struct bl_params *plat_get_next_bl_params(void) #endif /* __aarch64__ */ assert(param_node != NULL); +#if TRANSFER_LIST + arm_bl_params->head = ¶m_node->params_node_mem; + arm_bl_params->head->ep_info = ¶m_node->ep_info; + arm_bl_params->head->image_id = param_node->image_id; + + arm_bl2_setup_next_ep_info(param_node); +#elif !RESET_TO_BL2 && !EL3_PAYLOAD_BASE + fw_config_base = 0UL; + /* Update the next image's ep info with the FW config address */ fw_config_info = FCONF_GET_PROPERTY(dyn_cfg, dtb, FW_CONFIG_ID); assert(fw_config_info != NULL); @@ -79,49 +120,29 @@ struct bl_params *plat_get_next_bl_params(void) param_node = get_bl_mem_params_node(BL33_IMAGE_ID); assert(param_node != NULL); -#if TRANSFER_LIST - /* Update BL33's ep info with NS HW config address */ - te = transfer_list_find(ns_tl, TL_TAG_FDT); - assert(te != NULL); - - param_node->ep_info.args.arg1 = TRANSFER_LIST_SIGNATURE | - REGISTER_CONVENTION_VERSION_MASK; - param_node->ep_info.args.arg2 = 0; - param_node->ep_info.args.arg3 = (uintptr_t)ns_tl; - param_node->ep_info.args.arg0 = - te ? (uintptr_t)transfer_list_entry_data(te) : 0; -#else hw_config_info = FCONF_GET_PROPERTY(dyn_cfg, dtb, HW_CONFIG_ID); assert(hw_config_info != NULL); param_node->ep_info.args.arg1 = hw_config_info->secondary_config_addr; #endif /* TRANSFER_LIST */ -#endif /* !RESET_TO_BL2 && !EL3_PAYLOAD_BASE */ return arm_bl_params; } int bl2_plat_handle_post_image_load(unsigned int image_id) { -#if !RESET_TO_BL2 && !EL3_PAYLOAD_BASE +#if !RESET_TO_BL2 && !EL3_PAYLOAD_BASE && !TRANSFER_LIST if (image_id == HW_CONFIG_ID) { - const struct dyn_cfg_dtb_info_t *hw_config_info; + const struct dyn_cfg_dtb_info_t *hw_config_info __unused; struct transfer_list_entry *te __unused; + bl_mem_params_node_t *param_node __unused; - const bl_mem_params_node_t *param_node = - get_bl_mem_params_node(image_id); + param_node = get_bl_mem_params_node(image_id); assert(param_node != NULL); hw_config_info = FCONF_GET_PROPERTY(dyn_cfg, dtb, HW_CONFIG_ID); assert(hw_config_info != NULL); -#if TRANSFER_LIST - /* Update BL33's ep info with NS HW config address */ - te = transfer_list_add(ns_tl, TL_TAG_FDT, - param_node->image_info.image_size, - (void *)hw_config_info->config_addr); - assert(te != NULL); -#else memcpy((void *)hw_config_info->secondary_config_addr, (void *)hw_config_info->config_addr, (size_t)param_node->image_info.image_size); @@ -132,9 +153,8 @@ int bl2_plat_handle_post_image_load(unsigned int image_id) */ flush_dcache_range(hw_config_info->secondary_config_addr, param_node->image_info.image_size); -#endif /* TRANSFER_LIST */ } -#endif /* !RESET_TO_BL2 && !EL3_PAYLOAD_BASE */ +#endif /* !RESET_TO_BL2 && !EL3_PAYLOAD_BASE && !TRANSFER_LIST*/ return arm_bl2_plat_handle_post_image_load(image_id); } diff --git a/plat/arm/board/fvp/fvp_bl31_setup.c b/plat/arm/board/fvp/fvp_bl31_setup.c index e46dbc91..e0875656 100644 --- a/plat/arm/board/fvp/fvp_bl31_setup.c +++ b/plat/arm/board/fvp/fvp_bl31_setup.c @@ -1,16 +1,19 @@ /* - * Copyright (c) 2013-2023, Arm Limited and Contributors. All rights reserved. + * Copyright (c) 2013-2024, Arm Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ #include <assert.h> + +#include <common/bl_common.h> #include <common/debug.h> #include <drivers/arm/smmu_v3.h> #include <fconf_hw_config_getter.h> #include <lib/fconf/fconf.h> #include <lib/fconf/fconf_dyn_cfg_getter.h> #include <lib/mmio.h> + #include <plat/arm/common/arm_config.h> #include <plat/arm/common/plat_arm.h> #include <plat/common/platform.h> @@ -25,6 +28,9 @@ void __init bl31_early_platform_setup2(u_register_t arg0, /* Initialize the console to provide early debug support */ arm_console_boot_init(); +#if TRANSFER_LIST + arm_bl31_early_platform_setup(arg0, arg1, arg2, arg3); +#else #if !RESET_TO_BL31 && !RESET_TO_BL2 const struct dyn_cfg_dtb_info_t *soc_fw_config_info; @@ -48,8 +54,8 @@ void __init bl31_early_platform_setup2(u_register_t arg0, assert(hw_config_info->secondary_config_addr != 0UL); arg2 = hw_config_info->secondary_config_addr; #endif /* !RESET_TO_BL31 && !RESET_TO_BL2 */ - arm_bl31_early_platform_setup((void *)arg0, arg1, arg2, (void *)arg3); +#endif /* TRANSFER_LIST */ /* Initialize the platform config for future decision making */ fvp_config_setup(); @@ -73,10 +79,22 @@ void __init bl31_early_platform_setup2(u_register_t arg0, fvp_timer_init(); /* On FVP RevC, initialize SMMUv3 */ - if ((arm_config.flags & ARM_CONFIG_FVP_HAS_SMMUV3) != 0U) - smmuv3_init(PLAT_FVP_SMMUV3_BASE); + if ((arm_config.flags & ARM_CONFIG_FVP_HAS_SMMUV3) != 0U) { + if (smmuv3_security_init(PLAT_FVP_SMMUV3_BASE) != 0) { + /* + * Don't proceed for smmuv3 initialization if the + * security init failed. + */ + return; + } + /* SMMUv3 initialization failure is not fatal */ + if (smmuv3_init(PLAT_FVP_SMMUV3_BASE) != 0) { + WARN("Failed initializing SMMU.\n"); + } + } } +#if !TRANSFER_LIST void __init bl31_plat_arch_setup(void) { int rc __unused; @@ -131,6 +149,7 @@ void __init bl31_plat_arch_setup(void) } #endif /* !RESET_TO_BL31 && !RESET_TO_BL2 && !ARM_XLAT_TABLES_LIB_V1 */ } +#endif /* TRANSFER_LIST */ unsigned int plat_get_syscnt_freq2(void) { diff --git a/plat/arm/board/fvp/fvp_common.c b/plat/arm/board/fvp/fvp_common.c index c40a3ced..bdc2cacf 100644 --- a/plat/arm/board/fvp/fvp_common.c +++ b/plat/arm/board/fvp/fvp_common.c @@ -1,10 +1,11 @@ /* - * Copyright (c) 2013-2023, Arm Limited and Contributors. All rights reserved. + * Copyright (c) 2013-2024, Arm Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ #include <assert.h> +#include <string.h> #include <common/debug.h> #include <drivers/arm/cci.h> @@ -24,7 +25,6 @@ #endif #include <plat/arm/common/arm_config.h> -#include <plat/arm/common/arm_pas_def.h> #include <plat/arm/common/plat_arm.h> #include <plat/common/platform.h> @@ -34,6 +34,14 @@ #define FVP_GICV2 1 #define FVP_GICV3 2 +/* Defines for RMM Console*/ +#define FVP_RMM_CONSOLE_BASE UL(0x1c0c0000) +#define FVP_RMM_CONSOLE_BAUD UL(115200) +#define FVP_RMM_CONSOLE_CLK_IN_HZ UL(14745600) +#define FVP_RMM_CONSOLE_NAME "pl011" + +#define FVP_RMM_CONSOLE_COUNT UL(1) + /******************************************************************************* * arm_config holds the characteristics of the differences between the three FVP * platforms (Base, A53_A57 & Foundation). It will be populated during cold boot @@ -73,9 +81,14 @@ arm_config_t arm_config; #if TRANSFER_LIST #ifdef FW_NS_HANDOFF_BASE -#define MAP_FW_NS_HANDOFF MAP_REGION_FLAT(FW_NS_HANDOFF_BASE, \ - FW_HANDOFF_SIZE, \ - MT_MEMORY | MT_RW | MT_NS) +#define MAP_FW_NS_HANDOFF \ + MAP_REGION_FLAT(FW_NS_HANDOFF_BASE, PLAT_ARM_FW_HANDOFF_SIZE, \ + MT_MEMORY | MT_RW | MT_NS) +#endif +#ifdef PLAT_ARM_EL3_FW_HANDOFF_BASE +#define MAP_EL3_FW_HANDOFF \ + MAP_REGION_FLAT(PLAT_ARM_EL3_FW_HANDOFF_BASE, \ + PLAT_ARM_FW_HANDOFF_SIZE, MT_MEMORY | MT_RW | EL3_PAS) #endif #endif @@ -157,7 +170,10 @@ defined(SPD_spmd)) ARM_MAP_OPTEE_CORE_MEM, ARM_OPTEE_PAGEABLE_LOAD_MEM, #endif - {0} +#ifdef MAP_EL3_FW_HANDOFF + MAP_EL3_FW_HANDOFF, +#endif + { 0 } }; #endif #ifdef IMAGE_BL2U @@ -194,12 +210,20 @@ const mmap_region_t plat_arm_mmap[] = { #ifdef MAP_FW_NS_HANDOFF MAP_FW_NS_HANDOFF, #endif - {0} +#if defined(MAP_EL3_FW_HANDOFF) && !RESET_TO_BL31 + MAP_EL3_FW_HANDOFF, +#endif + { 0 } }; #if defined(IMAGE_BL31) && SPM_MM const mmap_region_t plat_arm_secure_partition_mmap[] = { V2M_MAP_IOFPGA_EL0, /* for the UART */ + V2M_MAP_SECURE_SYSTEMREG_EL0, /* for initializing flash */ +#if PSA_FWU_SUPPORT + V2M_MAP_FLASH0_RW_EL0, /* for firmware update service in standalone mm */ +#endif + V2M_MAP_FLASH1_RW_EL0, /* for secure variable service in standalone mm */ MAP_REGION_FLAT(DEVICE0_BASE, DEVICE0_SIZE, MT_DEVICE | MT_RO | MT_SECURE | MT_USER), @@ -551,10 +575,28 @@ size_t plat_rmmd_get_el3_rmm_shared_mem(uintptr_t *shared) return (size_t)RMM_SHARED_SIZE; } +/* + * Calculate checksum of 64-bit words @buffer with @size length + */ +static uint64_t checksum_calc(uint64_t *buffer, size_t size) +{ + uint64_t sum = 0UL; + + assert(((uintptr_t)buffer & (sizeof(uint64_t) - 1UL)) == 0UL); + assert((size & (sizeof(uint64_t) - 1UL)) == 0UL); + + for (unsigned long i = 0UL; i < (size / sizeof(uint64_t)); i++) { + sum += buffer[i]; + } + + return sum; +} + int plat_rmmd_load_manifest(struct rmm_manifest *manifest) { - uint64_t checksum, num_banks; + uint64_t checksum, num_banks, num_consoles; struct ns_dram_bank *bank_ptr; + struct console_info *console_ptr; assert(manifest != NULL); @@ -562,62 +604,110 @@ int plat_rmmd_load_manifest(struct rmm_manifest *manifest) num_banks = FCONF_GET_PROPERTY(hw_config, dram_layout, num_banks); assert(num_banks <= ARM_DRAM_NUM_BANKS); + /* Set number of consoles */ + num_consoles = FVP_RMM_CONSOLE_COUNT; + manifest->version = RMMD_MANIFEST_VERSION; manifest->padding = 0U; /* RES0 */ manifest->plat_data = (uintptr_t)NULL; manifest->plat_dram.num_banks = num_banks; + manifest->plat_console.num_consoles = num_consoles; /* - * Array ns_dram_banks[] follows ns_dram_info structure: + * Boot Manifest structure illustration, with two dram banks and + * a single console. * - * +-----------------------------------+ - * | offset | field | comment | - * +----------+-----------+------------+ - * | 0 | version | 0x00000002 | - * +----------+-----------+------------+ - * | 4 | padding | 0x00000000 | - * +----------+-----------+------------+ - * | 8 | plat_data | NULL | - * +----------+-----------+------------+ - * | 16 | num_banks | | - * +----------+-----------+ | - * | 24 | banks | plat_dram | - * +----------+-----------+ | - * | 32 | checksum | | - * +----------+-----------+------------+ - * | 40 | base 0 | | - * +----------+-----------+ bank[0] | - * | 48 | size 0 | | - * +----------+-----------+------------+ - * | 56 | base 1 | | - * +----------+-----------+ bank[1] | - * | 64 | size 1 | | - * +----------+-----------+------------+ + * +----------------------------------------+ + * | offset | field | comment | + * +--------+----------------+--------------+ + * | 0 | version | 0x00000003 | + * +--------+----------------+--------------+ + * | 4 | padding | 0x00000000 | + * +--------+----------------+--------------+ + * | 8 | plat_data | NULL | + * +--------+----------------+--------------+ + * | 16 | num_banks | | + * +--------+----------------+ | + * | 24 | banks | plat_dram | + * +--------+----------------+ | + * | 32 | checksum | | + * +--------+----------------+--------------+ + * | 40 | num_consoles | | + * +--------+----------------+ | + * | 48 | consoles | plat_console | + * +--------+----------------+ | + * | 56 | checksum | | + * +--------+----------------+--------------+ + * | 64 | base 0 | | + * +--------+----------------+ bank[0] | + * | 72 | size 0 | | + * +--------+----------------+--------------+ + * | 80 | base 1 | | + * +--------+----------------+ bank[1] | + * | 88 | size 1 | | + * +--------+----------------+--------------+ + * | 96 | base | | + * +--------+----------------+ | + * | 104 | map_pages | | + * +--------+----------------+ | + * | 112 | name | | + * +--------+----------------+ consoles[0] | + * | 120 | clk_in_hz | | + * +--------+----------------+ | + * | 128 | baud_rate | | + * +--------+----------------+ | + * | 136 | flags | | + * +--------+----------------+--------------+ */ + bank_ptr = (struct ns_dram_bank *) - ((uintptr_t)&manifest->plat_dram.checksum + - sizeof(manifest->plat_dram.checksum)); + (((uintptr_t)manifest) + sizeof(*manifest)); + console_ptr = (struct console_info *) + ((uintptr_t)bank_ptr + (num_banks * sizeof(*bank_ptr))); manifest->plat_dram.banks = bank_ptr; + manifest->plat_console.consoles = console_ptr; + + /* Ensure the manifest is not larger than the shared buffer */ + assert((sizeof(struct rmm_manifest) + + (sizeof(struct console_info) * manifest->plat_console.num_consoles) + + (sizeof(struct ns_dram_bank) * manifest->plat_dram.num_banks)) <= ARM_EL3_RMM_SHARED_SIZE); /* Calculate checksum of plat_dram structure */ checksum = num_banks + (uint64_t)bank_ptr; /* Store FVP DRAM banks data in Boot Manifest */ for (unsigned long i = 0UL; i < num_banks; i++) { - uintptr_t base = FCONF_GET_PROPERTY(hw_config, dram_layout, dram_bank[i].base); - uint64_t size = FCONF_GET_PROPERTY(hw_config, dram_layout, dram_bank[i].size); - - bank_ptr[i].base = base; - bank_ptr[i].size = size; - - /* Update checksum */ - checksum += base + size; + bank_ptr[i].base = FCONF_GET_PROPERTY(hw_config, dram_layout, dram_bank[i].base); + bank_ptr[i].size = FCONF_GET_PROPERTY(hw_config, dram_layout, dram_bank[i].size); } + /* Update checksum */ + checksum += checksum_calc((uint64_t *)bank_ptr, sizeof(struct ns_dram_bank) * num_banks); + /* Checksum must be 0 */ manifest->plat_dram.checksum = ~checksum + 1UL; + /* Calculate the checksum of the plat_consoles structure */ + checksum = num_consoles + (uint64_t)console_ptr; + + /* Zero out the console info struct */ + (void)memset((void *)console_ptr, '\0', sizeof(struct console_info) * num_consoles); + + console_ptr[0].base = FVP_RMM_CONSOLE_BASE; + console_ptr[0].map_pages = 1UL; + console_ptr[0].clk_in_hz = FVP_RMM_CONSOLE_CLK_IN_HZ; + console_ptr[0].baud_rate = FVP_RMM_CONSOLE_BAUD; + + (void)strlcpy(console_ptr[0].name, FVP_RMM_CONSOLE_NAME, RMM_CONSOLE_MAX_NAME_LEN - 1UL); + + /* Update checksum */ + checksum += checksum_calc((uint64_t *)console_ptr, + sizeof(struct console_info) * num_consoles); + + /* Checksum must be 0 */ + manifest->plat_console.checksum = ~checksum + 1UL; + return 0; } #endif /* ENABLE_RME */ diff --git a/plat/arm/board/fvp/fvp_common_measured_boot.c b/plat/arm/board/fvp/fvp_common_measured_boot.c index 0c1d5e70..605f0ff6 100644 --- a/plat/arm/board/fvp/fvp_common_measured_boot.c +++ b/plat/arm/board/fvp/fvp_common_measured_boot.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021-2023, Arm Limited. All rights reserved. + * Copyright (c) 2021-2024, Arm Limited. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -9,17 +9,14 @@ #include <common/desc_image_load.h> #include <drivers/measured_boot/event_log/event_log.h> -#include <drivers/measured_boot/rss/rss_measured_boot.h> #include <plat/arm/common/plat_arm.h> #include <plat/common/platform.h> extern event_log_metadata_t fvp_event_log_metadata[]; -extern struct rss_mboot_metadata fvp_rss_mboot_metadata[]; int plat_mboot_measure_image(unsigned int image_id, image_info_t *image_data) { int err; - int rc = 0; /* Calculate image hash and record data in Event Log */ err = event_log_measure_and_record(image_data->image_base, @@ -29,26 +26,14 @@ int plat_mboot_measure_image(unsigned int image_id, image_info_t *image_data) if (err != 0) { ERROR("%s%s image id %u (%i)\n", "Failed to ", "record in event log", image_id, err); - rc = err; + return err; } - /* Calculate image hash and record data in RSS */ - err = rss_mboot_measure_and_record(fvp_rss_mboot_metadata, - image_data->image_base, - image_data->image_size, - image_id); - if (err != 0) { - ERROR("%s%s image id %u (%i)\n", - "Failed to ", "record in RSS", image_id, err); - rc = (rc == 0) ? err : -1; - } - - return rc; + return 0; } int plat_mboot_measure_key(const void *pk_oid, const void *pk_ptr, size_t pk_len) { - return rss_mboot_set_signer_id(fvp_rss_mboot_metadata, pk_oid, pk_ptr, - pk_len); + return 0; } diff --git a/plat/arm/board/fvp/fvp_cpu_errata.mk b/plat/arm/board/fvp/fvp_cpu_errata.mk index b8fa4ea8..b26fa803 100644 --- a/plat/arm/board/fvp/fvp_cpu_errata.mk +++ b/plat/arm/board/fvp/fvp_cpu_errata.mk @@ -1,63 +1,32 @@ # -# Copyright (c) 2023, Arm Limited and Contributors. All rights reserved. +# Copyright (c) 2023-2024, Arm Limited and Contributors. All rights reserved. # # SPDX-License-Identifier: BSD-3-Clause # - -#/* -# * TODO: below lines of code to be removed -# * after abi and framework are synchronized -# */ +# Flags to enable the cpu structures in the Errata ABI file +# file: services/std_svc/errata_abi/errata_abi_main.c. This is specifically +# for platforms that need to enable errata based on non-arm interconnect IP. ifeq (${ERRATA_ABI_SUPPORT}, 1) -# enable the cpu macros for errata abi interface -ifeq (${ARCH}, aarch64) -ifeq (${HW_ASSISTED_COHERENCY}, 0) -CORTEX_A35_H_INC := 1 -CORTEX_A53_H_INC := 1 -CORTEX_A57_H_INC := 1 -CORTEX_A72_H_INC := 1 -CORTEX_A73_H_INC := 1 -$(eval $(call add_define, CORTEX_A35_H_INC)) -$(eval $(call add_define, CORTEX_A53_H_INC)) -$(eval $(call add_define, CORTEX_A57_H_INC)) -$(eval $(call add_define, CORTEX_A72_H_INC)) -$(eval $(call add_define, CORTEX_A73_H_INC)) -else +ifeq (${ERRATA_NON_ARM_INTERCONNECT}, 1) ifeq (${CTX_INCLUDE_AARCH32_REGS}, 0) -CORTEX_A76_H_INC := 1 -CORTEX_A77_H_INC := 1 +CORTEX_A710_H_INC := 1 CORTEX_A78_H_INC := 1 -NEOVERSE_N1_H_INC := 1 -NEOVERSE_N2_H_INC := 1 -NEOVERSE_V1_H_INC := 1 CORTEX_A78_AE_H_INC := 1 -CORTEX_A510_H_INC := 1 -CORTEX_A710_H_INC := 1 -CORTEX_A715_H_INC := 1 CORTEX_A78C_H_INC := 1 -CORTEX_X2_H_INC := 1 -$(eval $(call add_define, CORTEX_A76_H_INC)) -$(eval $(call add_define, CORTEX_A77_H_INC)) +CORTEX_X3_H_INC := 1 +CORTEX_X4_H_INC := 1 +NEOVERSE_N2_H_INC := 1 +NEOVERSE_V1_H_INC := 1 +$(eval $(call add_define, CORTEX_A710_H_INC)) $(eval $(call add_define, CORTEX_A78_H_INC)) -$(eval $(call add_define, NEOVERSE_N1_H_INC)) -$(eval $(call add_define, NEOVERSE_N2_H_INC)) -$(eval $(call add_define, NEOVERSE_V1_H_INC)) $(eval $(call add_define, CORTEX_A78_AE_H_INC)) -$(eval $(call add_define, CORTEX_A510_H_INC)) -$(eval $(call add_define, CORTEX_A710_H_INC)) -$(eval $(call add_define, CORTEX_A715_H_INC)) $(eval $(call add_define, CORTEX_A78C_H_INC)) -$(eval $(call add_define, CORTEX_X2_H_INC)) -endif -CORTEX_A55_H_INC := 1 -CORTEX_A75_H_INC := 1 -$(eval $(call add_define, CORTEX_A55_H_INC)) -$(eval $(call add_define, CORTEX_A75_H_INC)) +$(eval $(call add_define, CORTEX_X3_H_INC)) +$(eval $(call add_define, CORTEX_X4_H_INC)) +$(eval $(call add_define, NEOVERSE_N2_H_INC)) +$(eval $(call add_define, NEOVERSE_V1_H_INC)) endif -else -CORTEX_A32_H_INC := 1 -$(eval $(call add_define, CORTEX_A32_H_INC)) endif endif diff --git a/plat/arm/board/fvp/fvp_cpu_pwr.c b/plat/arm/board/fvp/fvp_cpu_pwr.c new file mode 100644 index 00000000..f2771c23 --- /dev/null +++ b/plat/arm/board/fvp/fvp_cpu_pwr.c @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2024, ARM Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#if __aarch64__ + +#include <aem_generic.h> +#include <arch_helpers.h> +#include <cortex_a35.h> +#include <cortex_a53.h> +#include <cortex_a57.h> +#include <cortex_a72.h> +#include <cortex_a73.h> +#include <cortex_a78_ae.h> +#include <drivers/arm/fvp/fvp_cpu_pwr.h> +#include <lib/utils_def.h> +#include <neoverse_e1.h> + +bool check_cpupwrctrl_el1_is_available(void) +{ + /* Poupulate list of CPU midr that doesn't support CPUPWRCTL_EL1 */ + const unsigned int midr_no_cpupwrctl[] = { + BASE_AEM_MIDR, + CORTEX_A35_MIDR, + CORTEX_A53_MIDR, + CORTEX_A57_MIDR, + CORTEX_A72_MIDR, + CORTEX_A73_MIDR, + CORTEX_A78_AE_MIDR, + NEOVERSE_E1_MIDR + }; + unsigned int midr = (unsigned int)read_midr(); + + for (unsigned int i = 0U; i < ARRAY_SIZE(midr_no_cpupwrctl); i++) { + if (midr_no_cpupwrctl[i] == midr) { + return false; + } + } + + return true; +} + +#endif /* __arch64__ */ diff --git a/plat/arm/board/fvp/fvp_el3_spmc.c b/plat/arm/board/fvp/fvp_el3_spmc.c index 6b44f634..c57a2444 100644 --- a/plat/arm/board/fvp/fvp_el3_spmc.c +++ b/plat/arm/board/fvp/fvp_el3_spmc.c @@ -7,23 +7,14 @@ #include <platform_def.h> -/* - * On the FVP platform when using the EL3 SPMC implementation allocate the - * datastore for tracking shared memory descriptors in the TZC DRAM section - * to ensure sufficient storage can be allocated. - * Provide an implementation of the accessor method to allow the datastore - * details to be retrieved by the SPMC. - * The SPMC will take care of initializing the memory region. - */ - -#define PLAT_SPMC_SHMEM_DATASTORE_SIZE 512 * 1024 +IMPORT_SYM(uintptr_t, __PLAT_SPMC_SHMEM_DATASTORE_START__, DATASTORE_BASE); -__section(".arm_el3_tzc_dram") static uint8_t +__section(".arm_el3_tzc_dram") __unused static uint8_t plat_spmc_shmem_datastore[PLAT_SPMC_SHMEM_DATASTORE_SIZE]; int plat_spmc_shmem_datastore_get(uint8_t **datastore, size_t *size) { - *datastore = plat_spmc_shmem_datastore; + *datastore = (uint8_t *)DATASTORE_BASE; *size = PLAT_SPMC_SHMEM_DATASTORE_SIZE; return 0; } diff --git a/plat/arm/board/fvp/fvp_el3_token_sign.c b/plat/arm/board/fvp/fvp_el3_token_sign.c new file mode 100644 index 00000000..282f94af --- /dev/null +++ b/plat/arm/board/fvp/fvp_el3_token_sign.c @@ -0,0 +1,98 @@ +/* + * Copyright (c) 2024, NVIDIA Corporation. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <errno.h> +#include <string.h> + +#include <plat/common/platform.h> +#include <services/rmm_el3_token_sign.h> + +static struct el3_token_sign_request el3_req = { 0 }; +static bool el3_req_valid; + +/* + * According to https://www.secg.org/sec1-v2.pdf 2.3.3 + * the size of the ECDSA P384 public key is 97 bytes, + * with the first byte being 0x04. + */ +static uint8_t sample_attest_pub_key[] = { + 0x04, 0x76, 0xf9, 0x88, 0x09, 0x1b, 0xe5, 0x85, 0xed, 0x41, + 0x80, 0x1a, 0xec, 0xfa, 0xb8, 0x58, 0x54, 0x8c, 0x63, 0x05, + 0x7e, 0x16, 0xb0, 0xe6, 0x76, 0x12, 0x0b, 0xbd, 0x0d, 0x2f, + 0x9c, 0x29, 0xe0, 0x56, 0xc5, 0xd4, 0x1a, 0x01, 0x30, 0xeb, + 0x9c, 0x21, 0x51, 0x78, 0x99, 0xdc, 0x23, 0x14, 0x6b, 0x28, + 0xe1, 0xb0, 0x62, 0xbd, 0x3e, 0xa4, 0xb3, 0x15, 0xfd, 0x21, + 0x9f, 0x1c, 0xbb, 0x52, 0x8c, 0xb6, 0xe7, 0x4c, 0xa4, 0x9b, + 0xe1, 0x67, 0x73, 0x73, 0x4f, 0x61, 0xa1, 0xca, 0x61, 0x03, + 0x1b, 0x2b, 0xbf, 0x3d, 0x91, 0x8f, 0x2f, 0x94, 0xff, 0xc4, + 0x22, 0x8e, 0x50, 0x91, 0x95, 0x44, 0xae +}; + +/* + * FVP does not support HES, so provide 0's as keys. + */ +int plat_rmmd_el3_token_sign_get_rak_pub(uintptr_t buf, size_t *len, + unsigned int type) +{ + (void)type; + if (*len < sizeof(sample_attest_pub_key)) { + return E_RMM_INVAL; + } + + if (type != ATTEST_KEY_CURVE_ECC_SECP384R1) { + ERROR("Invalid ECC curve specified\n"); + return E_RMM_INVAL; + } + + *len = sizeof(sample_attest_pub_key); + + (void)memcpy((void *)buf, sample_attest_pub_key, + sizeof(sample_attest_pub_key)); + + return 0; +} + +int plat_rmmd_el3_token_sign_push_req(const struct el3_token_sign_request *req) +{ + /* + * TODO: Today this function is called with a lock held on the + * RMM<->EL3 shared buffer. In the future, we may move to a + * different design that may require handling multi-threaded + * calls to this function, for example, if we have a per CPU + * buffer between RMM and EL3. + */ + if (el3_req_valid) { + return E_RMM_AGAIN; + } + + el3_req = *req; + + if ((el3_req.hash_alg_id != EL3_TOKEN_SIGN_HASH_ALG_SHA384) || + (el3_req.sig_alg_id != ATTEST_KEY_CURVE_ECC_SECP384R1)) { + return E_RMM_INVAL; + } + + el3_req_valid = true; + + return 0; +} + +int plat_rmmd_el3_token_sign_pull_resp(struct el3_token_sign_response *resp) +{ + if (!el3_req_valid) { + return E_RMM_AGAIN; + } + + resp->rec_granule = el3_req.rec_granule; + resp->req_ticket = el3_req.req_ticket; + resp->sig_len = (uint16_t)sizeof(resp->signature_buf); + /* TODO: Provide real signature */ + memset(resp->signature_buf, 0, sizeof(resp->signature_buf)); + + el3_req_valid = false; + + return 0; +} diff --git a/plat/arm/board/fvp/fvp_plat_attest_token.c b/plat/arm/board/fvp/fvp_plat_attest_token.c index 5fb3141c..0894bf76 100644 --- a/plat/arm/board/fvp/fvp_plat_attest_token.c +++ b/plat/arm/board/fvp/fvp_plat_attest_token.c @@ -1,5 +1,6 @@ /* - * Copyright (c) 2022-2023, Arm Limited and Contributors. All rights reserved. + * Copyright (c) 2022-2024, Arm Limited and Contributors. All rights reserved. + * Copyright (c) 2024, Linaro Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -9,110 +10,250 @@ #include <plat/common/platform.h> +/* + * This is the CBOR serialization of the CCA platform token described at + * https://git.trustedfirmware.org/TF-M/tf-m-tools/+/refs/heads/main/iat-verifier/tests/data/cca_example_platform_token.yaml + */ static const uint8_t sample_platform_token[] = { - 0xD2, 0x84, 0x44, 0xA1, 0x01, 0x38, 0x22, 0xA0, - 0x59, 0x02, 0x33, 0xA9, 0x19, 0x01, 0x09, 0x78, - 0x1C, 0x68, 0x74, 0x74, 0x70, 0x3A, 0x2F, 0x2F, - 0x61, 0x72, 0x6D, 0x2E, 0x63, 0x6F, 0x6D, 0x2F, - 0x43, 0x43, 0x41, 0x2D, 0x53, 0x53, 0x44, 0x2F, - 0x31, 0x2E, 0x30, 0x2E, 0x30, 0x0A, 0x58, 0x20, - 0xB5, 0x97, 0x3C, 0xB6, 0x8B, 0xAA, 0x9F, 0xC5, - 0x55, 0x58, 0x78, 0x6B, 0x7E, 0xC6, 0x7F, 0x69, - 0xE4, 0x0D, 0xF5, 0xBA, 0x5A, 0xA9, 0x21, 0xCD, - 0x0C, 0x27, 0xF4, 0x05, 0x87, 0xA0, 0x11, 0xEA, - 0x19, 0x09, 0x5C, 0x58, 0x20, 0x7F, 0x45, 0x4C, - 0x46, 0x02, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x3E, - 0x00, 0x01, 0x00, 0x00, 0x00, 0x50, 0x58, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x19, 0x01, 0x00, - 0x58, 0x21, 0x01, 0x07, 0x06, 0x05, 0x04, 0x03, - 0x02, 0x01, 0x00, 0x0F, 0x0E, 0x0D, 0x0C, 0x0B, - 0x0A, 0x09, 0x08, 0x17, 0x16, 0x15, 0x14, 0x13, - 0x12, 0x11, 0x10, 0x1F, 0x1E, 0x1D, 0x1C, 0x1B, - 0x1A, 0x19, 0x18, 0x19, 0x09, 0x61, 0x58, 0x21, - 0x01, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, - 0x00, 0x0F, 0x0E, 0x0D, 0x0C, 0x0B, 0x0A, 0x09, - 0x08, 0x17, 0x16, 0x15, 0x14, 0x13, 0x12, 0x11, - 0x10, 0x1F, 0x1E, 0x1D, 0x1C, 0x1B, 0x1A, 0x19, - 0x18, 0x19, 0x09, 0x5B, 0x19, 0x30, 0x03, 0x19, - 0x09, 0x62, 0x67, 0x73, 0x68, 0x61, 0x2D, 0x32, - 0x35, 0x36, 0x19, 0x09, 0x5F, 0x84, 0xA5, 0x01, - 0x62, 0x42, 0x4C, 0x05, 0x58, 0x20, 0x07, 0x06, - 0x05, 0x04, 0x03, 0x02, 0x01, 0x00, 0x0F, 0x0E, - 0x0D, 0x0C, 0x0B, 0x0A, 0x09, 0x08, 0x17, 0x16, - 0x15, 0x14, 0x13, 0x12, 0x11, 0x10, 0x1F, 0x1E, - 0x1D, 0x1C, 0x1B, 0x1A, 0x19, 0x18, 0x04, 0x65, - 0x33, 0x2E, 0x34, 0x2E, 0x32, 0x02, 0x58, 0x20, - 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, 0x00, - 0x0F, 0x0E, 0x0D, 0x0C, 0x0B, 0x0A, 0x09, 0x08, - 0x17, 0x16, 0x15, 0x14, 0x13, 0x12, 0x11, 0x10, - 0x1F, 0x1E, 0x1D, 0x1C, 0x1B, 0x1A, 0x19, 0x18, - 0x06, 0x74, 0x54, 0x46, 0x2D, 0x4D, 0x5F, 0x53, - 0x48, 0x41, 0x32, 0x35, 0x36, 0x4D, 0x65, 0x6D, - 0x50, 0x72, 0x65, 0x58, 0x49, 0x50, 0xA4, 0x01, - 0x62, 0x4D, 0x31, 0x05, 0x58, 0x20, 0x07, 0x06, - 0x05, 0x04, 0x03, 0x02, 0x01, 0x00, 0x0F, 0x0E, - 0x0D, 0x0C, 0x0B, 0x0A, 0x09, 0x08, 0x17, 0x16, - 0x15, 0x14, 0x13, 0x12, 0x11, 0x10, 0x1F, 0x1E, - 0x1D, 0x1C, 0x1B, 0x1A, 0x19, 0x18, 0x04, 0x63, - 0x31, 0x2E, 0x32, 0x02, 0x58, 0x20, 0x07, 0x06, - 0x05, 0x04, 0x03, 0x02, 0x01, 0x00, 0x0F, 0x0E, - 0x0D, 0x0C, 0x0B, 0x0A, 0x09, 0x08, 0x17, 0x16, - 0x15, 0x14, 0x13, 0x12, 0x11, 0x10, 0x1F, 0x1E, - 0x1D, 0x1C, 0x1B, 0x1A, 0x19, 0x18, 0xA4, 0x01, - 0x62, 0x4D, 0x32, 0x05, 0x58, 0x20, 0x07, 0x06, - 0x05, 0x04, 0x03, 0x02, 0x01, 0x00, 0x0F, 0x0E, - 0x0D, 0x0C, 0x0B, 0x0A, 0x09, 0x08, 0x17, 0x16, - 0x15, 0x14, 0x13, 0x12, 0x11, 0x10, 0x1F, 0x1E, - 0x1D, 0x1C, 0x1B, 0x1A, 0x19, 0x18, 0x04, 0x65, - 0x31, 0x2E, 0x32, 0x2E, 0x33, 0x02, 0x58, 0x20, - 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, 0x00, - 0x0F, 0x0E, 0x0D, 0x0C, 0x0B, 0x0A, 0x09, 0x08, - 0x17, 0x16, 0x15, 0x14, 0x13, 0x12, 0x11, 0x10, - 0x1F, 0x1E, 0x1D, 0x1C, 0x1B, 0x1A, 0x19, 0x18, - 0xA4, 0x01, 0x62, 0x4D, 0x33, 0x05, 0x58, 0x20, - 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, 0x00, - 0x0F, 0x0E, 0x0D, 0x0C, 0x0B, 0x0A, 0x09, 0x08, - 0x17, 0x16, 0x15, 0x14, 0x13, 0x12, 0x11, 0x10, - 0x1F, 0x1E, 0x1D, 0x1C, 0x1B, 0x1A, 0x19, 0x18, - 0x04, 0x61, 0x31, 0x02, 0x58, 0x20, 0x07, 0x06, - 0x05, 0x04, 0x03, 0x02, 0x01, 0x00, 0x0F, 0x0E, - 0x0D, 0x0C, 0x0B, 0x0A, 0x09, 0x08, 0x17, 0x16, - 0x15, 0x14, 0x13, 0x12, 0x11, 0x10, 0x1F, 0x1E, - 0x1D, 0x1C, 0x1B, 0x1A, 0x19, 0x18, 0x19, 0x09, - 0x60, 0x6C, 0x77, 0x68, 0x61, 0x74, 0x65, 0x76, - 0x65, 0x72, 0x2E, 0x63, 0x6F, 0x6D, 0x58, 0x60, - 0xE6, 0xB6, 0x38, 0x4F, 0xAE, 0x3F, 0x6E, 0x67, - 0xF5, 0xD4, 0x97, 0x4B, 0x3F, 0xFD, 0x0A, 0xFA, - 0x1D, 0xF0, 0x2F, 0x73, 0xB8, 0xFF, 0x5F, 0x02, - 0xC0, 0x0F, 0x40, 0xAC, 0xF3, 0xA2, 0x9D, 0xB5, - 0x31, 0x50, 0x16, 0x4F, 0xFA, 0x34, 0x3D, 0x0E, - 0xAF, 0xE0, 0xD0, 0xD1, 0x6C, 0xF0, 0x9D, 0xC1, - 0x01, 0x42, 0xA2, 0x3C, 0xCE, 0xD4, 0x4A, 0x59, - 0xDC, 0x29, 0x0A, 0x30, 0x93, 0x5F, 0xB4, 0x98, - 0x61, 0xBA, 0xE3, 0x91, 0x22, 0x95, 0x24, 0xF4, - 0xAE, 0x47, 0x93, 0xD3, 0x84, 0xA3, 0x76, 0xD0, - 0xC1, 0x26, 0x96, 0x53, 0xA3, 0x60, 0x3F, 0x6C, - 0x75, 0x96, 0x90, 0x6A, 0xF9, 0x4E, 0xDA, 0x30 + 0xd2, 0x84, 0x44, 0xa1, 0x01, 0x38, 0x22, 0xa0, + 0x59, 0x05, 0x81, 0xa9, 0x19, 0x01, 0x09, 0x78, + 0x23, 0x74, 0x61, 0x67, 0x3a, 0x61, 0x72, 0x6d, + 0x2e, 0x63, 0x6f, 0x6d, 0x2c, 0x32, 0x30, 0x32, + 0x33, 0x3a, 0x63, 0x63, 0x61, 0x5f, 0x70, 0x6c, + 0x61, 0x74, 0x66, 0x6f, 0x72, 0x6d, 0x23, 0x31, + 0x2e, 0x30, 0x2e, 0x30, 0x0a, 0x58, 0x20, 0x0d, + 0x22, 0xe0, 0x8a, 0x98, 0x46, 0x90, 0x58, 0x48, + 0x63, 0x18, 0x28, 0x34, 0x89, 0xbd, 0xb3, 0x6f, + 0x09, 0xdb, 0xef, 0xeb, 0x18, 0x64, 0xdf, 0x43, + 0x3f, 0xa6, 0xe5, 0x4e, 0xa2, 0xd7, 0x11, 0x19, + 0x09, 0x5c, 0x58, 0x20, 0x7f, 0x45, 0x4c, 0x46, + 0x02, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x3e, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x50, 0x58, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x19, 0x01, 0x00, 0x58, + 0x21, 0x01, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, + 0x01, 0x00, 0x0f, 0x0e, 0x0d, 0x0c, 0x0b, 0x0a, + 0x09, 0x08, 0x17, 0x16, 0x15, 0x14, 0x13, 0x12, + 0x11, 0x10, 0x1f, 0x1e, 0x1d, 0x1c, 0x1b, 0x1a, + 0x19, 0x18, 0x19, 0x09, 0x61, 0x44, 0xcf, 0xcf, + 0xcf, 0xcf, 0x19, 0x09, 0x5b, 0x19, 0x30, 0x03, + 0x19, 0x09, 0x62, 0x67, 0x73, 0x68, 0x61, 0x2d, + 0x32, 0x35, 0x36, 0x19, 0x09, 0x60, 0x78, 0x3a, + 0x68, 0x74, 0x74, 0x70, 0x73, 0x3a, 0x2f, 0x2f, + 0x76, 0x65, 0x72, 0x61, 0x69, 0x73, 0x6f, 0x6e, + 0x2e, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, + 0x2f, 0x2e, 0x77, 0x65, 0x6c, 0x6c, 0x2d, 0x6b, + 0x6e, 0x6f, 0x77, 0x6e, 0x2f, 0x76, 0x65, 0x72, + 0x61, 0x69, 0x73, 0x6f, 0x6e, 0x2f, 0x76, 0x65, + 0x72, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x19, 0x09, 0x5f, 0x8d, 0xa4, 0x01, + 0x69, 0x52, 0x53, 0x45, 0x5f, 0x42, 0x4c, 0x31, + 0x5f, 0x32, 0x05, 0x58, 0x20, 0x53, 0x78, 0x79, + 0x63, 0x07, 0x53, 0x5d, 0xf3, 0xec, 0x8d, 0x8b, + 0x15, 0xa2, 0xe2, 0xdc, 0x56, 0x41, 0x41, 0x9c, + 0x3d, 0x30, 0x60, 0xcf, 0xe3, 0x22, 0x38, 0xc0, + 0xfa, 0x97, 0x3f, 0x7a, 0xa3, 0x02, 0x58, 0x20, + 0x9a, 0x27, 0x1f, 0x2a, 0x91, 0x6b, 0x0b, 0x6e, + 0xe6, 0xce, 0xcb, 0x24, 0x26, 0xf0, 0xb3, 0x20, + 0x6e, 0xf0, 0x74, 0x57, 0x8b, 0xe5, 0x5d, 0x9b, + 0xc9, 0x4f, 0x6f, 0x3f, 0xe3, 0xab, 0x86, 0xaa, + 0x06, 0x67, 0x73, 0x68, 0x61, 0x2d, 0x32, 0x35, + 0x36, 0xa4, 0x01, 0x67, 0x52, 0x53, 0x45, 0x5f, + 0x42, 0x4c, 0x32, 0x05, 0x58, 0x20, 0x53, 0x78, + 0x79, 0x63, 0x07, 0x53, 0x5d, 0xf3, 0xec, 0x8d, + 0x8b, 0x15, 0xa2, 0xe2, 0xdc, 0x56, 0x41, 0x41, + 0x9c, 0x3d, 0x30, 0x60, 0xcf, 0xe3, 0x22, 0x38, + 0xc0, 0xfa, 0x97, 0x3f, 0x7a, 0xa3, 0x02, 0x58, + 0x20, 0x53, 0xc2, 0x34, 0xe5, 0xe8, 0x47, 0x2b, + 0x6a, 0xc5, 0x1c, 0x1a, 0xe1, 0xca, 0xb3, 0xfe, + 0x06, 0xfa, 0xd0, 0x53, 0xbe, 0xb8, 0xeb, 0xfd, + 0x89, 0x77, 0xb0, 0x10, 0x65, 0x5b, 0xfd, 0xd3, + 0xc3, 0x06, 0x67, 0x73, 0x68, 0x61, 0x2d, 0x32, + 0x35, 0x36, 0xa4, 0x01, 0x65, 0x52, 0x53, 0x45, + 0x5f, 0x53, 0x05, 0x58, 0x20, 0x53, 0x78, 0x79, + 0x63, 0x07, 0x53, 0x5d, 0xf3, 0xec, 0x8d, 0x8b, + 0x15, 0xa2, 0xe2, 0xdc, 0x56, 0x41, 0x41, 0x9c, + 0x3d, 0x30, 0x60, 0xcf, 0xe3, 0x22, 0x38, 0xc0, + 0xfa, 0x97, 0x3f, 0x7a, 0xa3, 0x02, 0x58, 0x20, + 0x11, 0x21, 0xcf, 0xcc, 0xd5, 0x91, 0x3f, 0x0a, + 0x63, 0xfe, 0xc4, 0x0a, 0x6f, 0xfd, 0x44, 0xea, + 0x64, 0xf9, 0xdc, 0x13, 0x5c, 0x66, 0x63, 0x4b, + 0xa0, 0x01, 0xd1, 0x0b, 0xcf, 0x43, 0x02, 0xa2, + 0x06, 0x67, 0x73, 0x68, 0x61, 0x2d, 0x32, 0x35, + 0x36, 0xa4, 0x01, 0x66, 0x41, 0x50, 0x5f, 0x42, + 0x4c, 0x31, 0x05, 0x58, 0x20, 0x53, 0x78, 0x79, + 0x63, 0x07, 0x53, 0x5d, 0xf3, 0xec, 0x8d, 0x8b, + 0x15, 0xa2, 0xe2, 0xdc, 0x56, 0x41, 0x41, 0x9c, + 0x3d, 0x30, 0x60, 0xcf, 0xe3, 0x22, 0x38, 0xc0, + 0xfa, 0x97, 0x3f, 0x7a, 0xa3, 0x02, 0x58, 0x20, + 0x15, 0x71, 0xb5, 0xec, 0x78, 0xbd, 0x68, 0x51, + 0x2b, 0xf7, 0x83, 0x0b, 0xb6, 0xa2, 0xa4, 0x4b, + 0x20, 0x47, 0xc7, 0xdf, 0x57, 0xbc, 0xe7, 0x9e, + 0xb8, 0xa1, 0xc0, 0xe5, 0xbe, 0xa0, 0xa5, 0x01, + 0x06, 0x67, 0x73, 0x68, 0x61, 0x2d, 0x32, 0x35, + 0x36, 0xa4, 0x01, 0x66, 0x41, 0x50, 0x5f, 0x42, + 0x4c, 0x32, 0x05, 0x58, 0x20, 0x53, 0x78, 0x79, + 0x63, 0x07, 0x53, 0x5d, 0xf3, 0xec, 0x8d, 0x8b, + 0x15, 0xa2, 0xe2, 0xdc, 0x56, 0x41, 0x41, 0x9c, + 0x3d, 0x30, 0x60, 0xcf, 0xe3, 0x22, 0x38, 0xc0, + 0xfa, 0x97, 0x3f, 0x7a, 0xa3, 0x02, 0x58, 0x20, + 0x10, 0x15, 0x9b, 0xaf, 0x26, 0x2b, 0x43, 0xa9, + 0x2d, 0x95, 0xdb, 0x59, 0xda, 0xe1, 0xf7, 0x2c, + 0x64, 0x51, 0x27, 0x30, 0x16, 0x61, 0xe0, 0xa3, + 0xce, 0x4e, 0x38, 0xb2, 0x95, 0xa9, 0x7c, 0x58, + 0x06, 0x67, 0x73, 0x68, 0x61, 0x2d, 0x32, 0x35, + 0x36, 0xa4, 0x01, 0x67, 0x53, 0x43, 0x50, 0x5f, + 0x42, 0x4c, 0x31, 0x05, 0x58, 0x20, 0x53, 0x78, + 0x79, 0x63, 0x07, 0x53, 0x5d, 0xf3, 0xec, 0x8d, + 0x8b, 0x15, 0xa2, 0xe2, 0xdc, 0x56, 0x41, 0x41, + 0x9c, 0x3d, 0x30, 0x60, 0xcf, 0xe3, 0x22, 0x38, + 0xc0, 0xfa, 0x97, 0x3f, 0x7a, 0xa3, 0x02, 0x58, + 0x20, 0x10, 0x12, 0x2e, 0x85, 0x6b, 0x3f, 0xcd, + 0x49, 0xf0, 0x63, 0x63, 0x63, 0x17, 0x47, 0x61, + 0x49, 0xcb, 0x73, 0x0a, 0x1a, 0xa1, 0xcf, 0xaa, + 0xd8, 0x18, 0x55, 0x2b, 0x72, 0xf5, 0x6d, 0x6f, + 0x68, 0x06, 0x67, 0x73, 0x68, 0x61, 0x2d, 0x32, + 0x35, 0x36, 0xa4, 0x01, 0x67, 0x53, 0x43, 0x50, + 0x5f, 0x42, 0x4c, 0x32, 0x05, 0x58, 0x20, 0xf1, + 0x4b, 0x49, 0x87, 0x90, 0x4b, 0xcb, 0x58, 0x14, + 0xe4, 0x45, 0x9a, 0x05, 0x7e, 0xd4, 0xd2, 0x0f, + 0x58, 0xa6, 0x33, 0x15, 0x22, 0x88, 0xa7, 0x61, + 0x21, 0x4d, 0xcd, 0x28, 0x78, 0x0b, 0x56, 0x02, + 0x58, 0x20, 0xaa, 0x67, 0xa1, 0x69, 0xb0, 0xbb, + 0xa2, 0x17, 0xaa, 0x0a, 0xa8, 0x8a, 0x65, 0x34, + 0x69, 0x20, 0xc8, 0x4c, 0x42, 0x44, 0x7c, 0x36, + 0xba, 0x5f, 0x7e, 0xa6, 0x5f, 0x42, 0x2c, 0x1f, + 0xe5, 0xd8, 0x06, 0x67, 0x73, 0x68, 0x61, 0x2d, + 0x32, 0x35, 0x36, 0xa4, 0x01, 0x67, 0x41, 0x50, + 0x5f, 0x42, 0x4c, 0x33, 0x31, 0x05, 0x58, 0x20, + 0x53, 0x78, 0x79, 0x63, 0x07, 0x53, 0x5d, 0xf3, + 0xec, 0x8d, 0x8b, 0x15, 0xa2, 0xe2, 0xdc, 0x56, + 0x41, 0x41, 0x9c, 0x3d, 0x30, 0x60, 0xcf, 0xe3, + 0x22, 0x38, 0xc0, 0xfa, 0x97, 0x3f, 0x7a, 0xa3, + 0x02, 0x58, 0x20, 0x2e, 0x6d, 0x31, 0xa5, 0x98, + 0x3a, 0x91, 0x25, 0x1b, 0xfa, 0xe5, 0xae, 0xfa, + 0x1c, 0x0a, 0x19, 0xd8, 0xba, 0x3c, 0xf6, 0x01, + 0xd0, 0xe8, 0xa7, 0x06, 0xb4, 0xcf, 0xa9, 0x66, + 0x1a, 0x6b, 0x8a, 0x06, 0x67, 0x73, 0x68, 0x61, + 0x2d, 0x32, 0x35, 0x36, 0xa4, 0x01, 0x63, 0x52, + 0x4d, 0x4d, 0x05, 0x58, 0x20, 0x53, 0x78, 0x79, + 0x63, 0x07, 0x53, 0x5d, 0xf3, 0xec, 0x8d, 0x8b, + 0x15, 0xa2, 0xe2, 0xdc, 0x56, 0x41, 0x41, 0x9c, + 0x3d, 0x30, 0x60, 0xcf, 0xe3, 0x22, 0x38, 0xc0, + 0xfa, 0x97, 0x3f, 0x7a, 0xa3, 0x02, 0x58, 0x20, + 0xa1, 0xfb, 0x50, 0xe6, 0xc8, 0x6f, 0xae, 0x16, + 0x79, 0xef, 0x33, 0x51, 0x29, 0x6f, 0xd6, 0x71, + 0x34, 0x11, 0xa0, 0x8c, 0xf8, 0xdd, 0x17, 0x90, + 0xa4, 0xfd, 0x05, 0xfa, 0xe8, 0x68, 0x81, 0x64, + 0x06, 0x67, 0x73, 0x68, 0x61, 0x2d, 0x32, 0x35, + 0x36, 0xa4, 0x01, 0x69, 0x48, 0x57, 0x5f, 0x43, + 0x4f, 0x4e, 0x46, 0x49, 0x47, 0x05, 0x58, 0x20, + 0x53, 0x78, 0x79, 0x63, 0x07, 0x53, 0x5d, 0xf3, + 0xec, 0x8d, 0x8b, 0x15, 0xa2, 0xe2, 0xdc, 0x56, + 0x41, 0x41, 0x9c, 0x3d, 0x30, 0x60, 0xcf, 0xe3, + 0x22, 0x38, 0xc0, 0xfa, 0x97, 0x3f, 0x7a, 0xa3, + 0x02, 0x58, 0x20, 0x1a, 0x25, 0x24, 0x02, 0x97, + 0x2f, 0x60, 0x57, 0xfa, 0x53, 0xcc, 0x17, 0x2b, + 0x52, 0xb9, 0xff, 0xca, 0x69, 0x8e, 0x18, 0x31, + 0x1f, 0xac, 0xd0, 0xf3, 0xb0, 0x6e, 0xca, 0xae, + 0xf7, 0x9e, 0x17, 0x06, 0x67, 0x73, 0x68, 0x61, + 0x2d, 0x32, 0x35, 0x36, 0xa4, 0x01, 0x69, 0x46, + 0x57, 0x5f, 0x43, 0x4f, 0x4e, 0x46, 0x49, 0x47, + 0x05, 0x58, 0x20, 0x53, 0x78, 0x79, 0x63, 0x07, + 0x53, 0x5d, 0xf3, 0xec, 0x8d, 0x8b, 0x15, 0xa2, + 0xe2, 0xdc, 0x56, 0x41, 0x41, 0x9c, 0x3d, 0x30, + 0x60, 0xcf, 0xe3, 0x22, 0x38, 0xc0, 0xfa, 0x97, + 0x3f, 0x7a, 0xa3, 0x02, 0x58, 0x20, 0x9a, 0x92, + 0xad, 0xbc, 0x0c, 0xee, 0x38, 0xef, 0x65, 0x8c, + 0x71, 0xce, 0x1b, 0x1b, 0xf8, 0xc6, 0x56, 0x68, + 0xf1, 0x66, 0xbf, 0xb2, 0x13, 0x64, 0x4c, 0x89, + 0x5c, 0xcb, 0x1a, 0xd0, 0x7a, 0x25, 0x06, 0x67, + 0x73, 0x68, 0x61, 0x2d, 0x32, 0x35, 0x36, 0xa4, + 0x01, 0x6c, 0x54, 0x42, 0x5f, 0x46, 0x57, 0x5f, + 0x43, 0x4f, 0x4e, 0x46, 0x49, 0x47, 0x05, 0x58, + 0x20, 0x53, 0x78, 0x79, 0x63, 0x07, 0x53, 0x5d, + 0xf3, 0xec, 0x8d, 0x8b, 0x15, 0xa2, 0xe2, 0xdc, + 0x56, 0x41, 0x41, 0x9c, 0x3d, 0x30, 0x60, 0xcf, + 0xe3, 0x22, 0x38, 0xc0, 0xfa, 0x97, 0x3f, 0x7a, + 0xa3, 0x02, 0x58, 0x20, 0x23, 0x89, 0x03, 0x18, + 0x0c, 0xc1, 0x04, 0xec, 0x2c, 0x5d, 0x8b, 0x3f, + 0x20, 0xc5, 0xbc, 0x61, 0xb3, 0x89, 0xec, 0x0a, + 0x96, 0x7d, 0xf8, 0xcc, 0x20, 0x8c, 0xdc, 0x7c, + 0xd4, 0x54, 0x17, 0x4f, 0x06, 0x67, 0x73, 0x68, + 0x61, 0x2d, 0x32, 0x35, 0x36, 0xa4, 0x01, 0x6d, + 0x53, 0x4f, 0x43, 0x5f, 0x46, 0x57, 0x5f, 0x43, + 0x4f, 0x4e, 0x46, 0x49, 0x47, 0x05, 0x58, 0x20, + 0x53, 0x78, 0x79, 0x63, 0x07, 0x53, 0x5d, 0xf3, + 0xec, 0x8d, 0x8b, 0x15, 0xa2, 0xe2, 0xdc, 0x56, + 0x41, 0x41, 0x9c, 0x3d, 0x30, 0x60, 0xcf, 0xe3, + 0x22, 0x38, 0xc0, 0xfa, 0x97, 0x3f, 0x7a, 0xa3, + 0x02, 0x58, 0x20, 0xe6, 0xc2, 0x1e, 0x8d, 0x26, + 0x0f, 0xe7, 0x18, 0x82, 0xde, 0xbd, 0xb3, 0x39, + 0xd2, 0x40, 0x2a, 0x2c, 0xa7, 0x64, 0x85, 0x29, + 0xbc, 0x23, 0x03, 0xf4, 0x86, 0x49, 0xbc, 0xe0, + 0x38, 0x00, 0x17, 0x06, 0x67, 0x73, 0x68, 0x61, + 0x2d, 0x32, 0x35, 0x36, 0x58, 0x60, 0x31, 0xd0, + 0x4d, 0x52, 0xcc, 0xde, 0x95, 0x2c, 0x1e, 0x32, + 0xcb, 0xa1, 0x81, 0x88, 0x5a, 0x40, 0xb8, 0xcc, + 0x38, 0xe0, 0x52, 0x8c, 0x1e, 0x89, 0x58, 0x98, + 0x07, 0x64, 0x2a, 0xa5, 0xe3, 0xf2, 0xbc, 0x37, + 0xf9, 0x53, 0x74, 0x50, 0x6b, 0xff, 0x4d, 0x2e, + 0x4b, 0xe7, 0x06, 0x3c, 0x4d, 0x72, 0x41, 0x92, + 0x70, 0xc7, 0x22, 0xe8, 0xd4, 0xd9, 0x3e, 0xe8, + 0xb6, 0xc9, 0xfa, 0xce, 0x3b, 0x43, 0xc9, 0x76, + 0x1a, 0x49, 0x94, 0x1a, 0xb6, 0xf3, 0x8f, 0xfd, + 0xff, 0x49, 0x6a, 0xd4, 0x63, 0xb4, 0xcb, 0xfa, + 0x11, 0xd8, 0x3e, 0x23, 0xe3, 0x1f, 0x7f, 0x62, + 0x32, 0x9d, 0xe3, 0x0c, 0x1c, 0xc8 }; +static uint64_t platform_token_offset; /* * Get the hardcoded platform attestation token as FVP does not support - * RSS. + * RSE. + * + * Note: This implementation caters for retrieval of the platform token + * in hunks to facilitate EL3-RMM interface testing. For most platforms, + * since the shared buffer size is known, the implementation can be more + * optimized. */ int plat_rmmd_get_cca_attest_token(uintptr_t buf, size_t *len, - uintptr_t hash, size_t hash_size) + uintptr_t hash, size_t hash_size, + size_t *remaining_len) { (void)hash; (void)hash_size; + size_t platform_token_size = sizeof(sample_platform_token); + size_t local_hunk_len; + size_t local_remaining_len; - if (*len < sizeof(sample_platform_token)) { + if (hash_size != 0) { + platform_token_offset = 0; + } else if (platform_token_offset == 0) { return -EINVAL; } - (void)memcpy((void *)buf, (const void *)sample_platform_token, - sizeof(sample_platform_token)); - *len = sizeof(sample_platform_token); + local_hunk_len = *len; + local_remaining_len = platform_token_size - platform_token_offset; + + /* + * If the buffer is enough to fit the remaining bytes of the token, + * return only the remaining bytes of the token. + */ + if (local_hunk_len >= local_remaining_len) { + local_hunk_len = local_remaining_len; + } + /* Update remaining bytes according to hunk size */ + local_remaining_len -= local_hunk_len; + + (void)memcpy((void *)buf, + (const void *)sample_platform_token + + platform_token_offset, + local_hunk_len); + + platform_token_offset += local_hunk_len; + *len = local_hunk_len; + *remaining_len = local_remaining_len; return 0; } diff --git a/plat/arm/board/fvp/fvp_pm.c b/plat/arm/board/fvp/fvp_pm.c index 51dda9ec..80dfd2a9 100644 --- a/plat/arm/board/fvp/fvp_pm.c +++ b/plat/arm/board/fvp/fvp_pm.c @@ -1,17 +1,15 @@ /* - * Copyright (c) 2013-2021, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2013-2024, Arm Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ #include <assert.h> -#include <arch_features.h> #include <arch_helpers.h> #include <common/debug.h> #include <drivers/arm/gicv3.h> #include <drivers/arm/fvp/fvp_pwrc.h> -#include <lib/extensions/spe.h> #include <lib/mmio.h> #include <lib/psci/psci.h> #include <plat/arm/common/arm_config.h> @@ -54,14 +52,6 @@ static void fvp_cluster_pwrdwn_common(void) { uint64_t mpidr = read_mpidr_el1(); - /* - * On power down we need to disable statistical profiling extensions - * before exiting coherency. - */ - if (is_feat_spe_supported()) { - spe_disable(); - } - /* Disable coherency if this cluster is to be turned off */ fvp_interconnect_disable(); @@ -333,13 +323,13 @@ static int fvp_node_hw_state(u_register_t target_cpu, unsigned int power_level) { unsigned int psysr; - int ret; + int ret = 0; /* * The format of 'power_level' is implementation-defined, but 0 must * mean a CPU. We also allow 1 to denote the cluster */ - if ((power_level != ARM_PWR_LVL0) && (power_level != ARM_PWR_LVL1)) + if ((power_level < ARM_PWR_LVL0) || (power_level > ARM_PWR_LVL1)) return PSCI_E_INVALID_PARAMS; /* @@ -353,9 +343,14 @@ static int fvp_node_hw_state(u_register_t target_cpu, if (power_level == ARM_PWR_LVL0) { ret = ((psysr & PSYSR_AFF_L0) != 0U) ? HW_ON : HW_OFF; - } else { - /* power_level == ARM_PWR_LVL1 */ - ret = ((psysr & PSYSR_AFF_L1) != 0U) ? HW_ON : HW_OFF; + } else if (power_level == ARM_PWR_LVL1) { + /* + * Use L1 affinity if MPIDR_EL1.MT bit is not set else use L2 affinity. + */ + if ((read_mpidr_el1() & MPIDR_MT_MASK) == 0U) + ret = ((psysr & PSYSR_AFF_L1) != 0U) ? HW_ON : HW_OFF; + else + ret = ((psysr & PSYSR_AFF_L2) != 0U) ? HW_ON : HW_OFF; } return ret; diff --git a/plat/arm/board/fvp/fvp_realm_attest_key.c b/plat/arm/board/fvp/fvp_realm_attest_key.c index fe0cde72..150608d6 100644 --- a/plat/arm/board/fvp/fvp_realm_attest_key.c +++ b/plat/arm/board/fvp/fvp_realm_attest_key.c @@ -19,7 +19,7 @@ static const uint8_t sample_delegated_key[] = { /* * Get the hardcoded delegated realm attestation key as FVP - * does not support RSS. + * does not support RSE. */ int plat_rmmd_get_cca_realm_attest_key(uintptr_t buf, size_t *len, unsigned int type) diff --git a/plat/arm/board/fvp/fvp_topology.c b/plat/arm/board/fvp/fvp_topology.c index 971e35b5..1db05023 100644 --- a/plat/arm/board/fvp/fvp_topology.c +++ b/plat/arm/board/fvp/fvp_topology.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013-2023, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2013-2024, Arm Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -36,7 +36,7 @@ const unsigned char *plat_get_power_domain_tree_desc(void) * fconf APIs are not supported for RESET_TO_SP_MIN, RESET_TO_BL31 and * RESET_TO_BL2 systems. */ -#if RESET_TO_SP_MIN || RESET_TO_BL31 || RESET_TO_BL2 +#if RESET_TO_SP_MIN || RESET_TO_BL31 || RESET_TO_BL2 || IMAGE_BL1 cluster_count = FVP_CLUSTER_COUNT; cpus_per_cluster = FVP_MAX_CPUS_PER_CLUSTER * FVP_MAX_PE_PER_CPU; #else @@ -106,8 +106,10 @@ int plat_core_pos_by_mpidr(u_register_t mpidr) if (thread_id >= FVP_MAX_PE_PER_CPU) return -1; +#if !IMAGE_BL1 if (fvp_pwrc_read_psysr(mpidr) == PSYSR_INVALID) return -1; +#endif /* IMAGE_BL1 */ /* * Core position calculation for FVP platform depends on the MT bit in diff --git a/include/plat/arm/common/arm_pas_def.h b/plat/arm/board/fvp/include/fvp_pas_def.h similarity index 95% rename from include/plat/arm/common/arm_pas_def.h rename to plat/arm/board/fvp/include/fvp_pas_def.h index fba8d2c7..46843872 100644 --- a/include/plat/arm/common/arm_pas_def.h +++ b/plat/arm/board/fvp/include/fvp_pas_def.h @@ -1,13 +1,13 @@ /* - * Copyright (c) 2021-2022, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2021-2024, ARM Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ -#ifndef ARM_PAS_DEF_H -#define ARM_PAS_DEF_H +#ifndef FVP_PAS_DEF_H +#define FVP_PAS_DEF_H #include <lib/gpt_rme/gpt_rme.h> -#include <plat/arm/common/arm_def.h> +#include <platform_def.h> /***************************************************************************** * PAS regions used to initialize the Granule Protection Table (GPT) @@ -107,11 +107,11 @@ ARM_EL3_TZC_DRAM1_SIZE, \ GPT_GPI_ROOT) -#define ARM_PAS_GPTS GPT_MAP_REGION_GRANULE(ARM_L1_GPT_ADDR_BASE, \ +#define ARM_PAS_GPTS GPT_MAP_REGION_GRANULE(ARM_L1_GPT_BASE, \ ARM_L1_GPT_SIZE, \ GPT_GPI_ROOT) /* GPT Configuration options */ #define PLATFORM_L0GPTSZ GPCCR_L0GPTSZ_30BITS -#endif /* ARM_PAS_DEF_H */ +#endif /* FVP_PAS_DEF_H */ diff --git a/plat/arm/board/fvp/include/plat.ld.S b/plat/arm/board/fvp/include/plat.ld.S index 7c8bf065..2f999990 100644 --- a/plat/arm/board/fvp/include/plat.ld.S +++ b/plat/arm/board/fvp/include/plat.ld.S @@ -1,12 +1,38 @@ /* - * Copyright (c) 2017-2019, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2017-2024, ARM Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ #ifndef PLAT_LD_S #define PLAT_LD_S -#include <plat/arm/common/arm_tzc_dram.ld.S> +#include <lib/xlat_tables/xlat_tables_defs.h> + +MEMORY { + EL3_SEC_DRAM (rw): ORIGIN = ARM_EL3_TZC_DRAM1_BASE, LENGTH = ARM_EL3_TZC_DRAM1_SIZE +} + +SECTIONS +{ + . = ARM_EL3_TZC_DRAM1_BASE; + ASSERT(. == ALIGN(PAGE_SIZE), + "ARM_EL3_TZC_DRAM_BASE address is not aligned on a page boundary.") + .el3_tzc_dram (NOLOAD) : ALIGN(PAGE_SIZE) { + __PLAT_SPMC_SHMEM_DATASTORE_START__ = .; + *(.arm_spmc_shmem_datastore) + __PLAT_SPMC_SHMEM_DATASTORE_END__ = .; + __EL3_SEC_DRAM_START__ = .; + *(.arm_el3_tzc_dram) +#if SEPARATE_SIMD_SECTION + . = ALIGN(16); + *(.simd_context) +#endif + __EL3_SEC_DRAM_UNALIGNED_END__ = .; + + . = ALIGN(PAGE_SIZE); + __EL3_SEC_DRAM_END__ = .; + } >EL3_SEC_DRAM +} #if RECLAIM_INIT_CODE #include <plat/arm/common/arm_reclaim_init.ld.S> diff --git a/plat/arm/board/fvp/include/platform_def.h b/plat/arm/board/fvp/include/platform_def.h index aad04173..e0c9725c 100644 --- a/plat/arm/board/fvp/include/platform_def.h +++ b/plat/arm/board/fvp/include/platform_def.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2023, Arm Limited and Contributors. All rights reserved. + * Copyright (c) 2014-2024, Arm Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -99,6 +99,20 @@ FVP_DTB_DRAM_MAP_SIZE, \ MT_MEMORY | MT_RO | MT_NS) +/* + * On the FVP platform when using the EL3 SPMC implementation allocate the + * datastore for tracking shared memory descriptors in the TZC DRAM section + * to ensure sufficient storage can be allocated. + * Provide an implementation of the accessor method to allow the datastore + * details to be retrieved by the SPMC. + * The SPMC will take care of initializing the memory region. + */ + +#define PLAT_SPMC_SHMEM_DATASTORE_SIZE 512 * 1024 + +/* Define memory configuration for device tree files. */ +#define PLAT_ARM_HW_CONFIG_SIZE U(0x4000) + #if SPMC_AT_EL3 /* * Number of Secure Partitions supported. @@ -129,8 +143,18 @@ #define PLAT_ARM_NS_IMAGE_BASE (ARM_DRAM1_BASE + UL(0x8000000)) #if TRANSFER_LIST -#define FW_HANDOFF_SIZE 0x4000 -#define FW_NS_HANDOFF_BASE (PLAT_ARM_NS_IMAGE_BASE - FW_HANDOFF_SIZE) +#define PLAT_ARM_FW_HANDOFF_SIZE U(0x5000) + +#define FW_NS_HANDOFF_BASE (PLAT_ARM_NS_IMAGE_BASE - PLAT_ARM_FW_HANDOFF_SIZE) +#define PLAT_ARM_EL3_FW_HANDOFF_BASE ARM_BL_RAM_BASE +#define PLAT_ARM_EL3_FW_HANDOFF_LIMIT PLAT_ARM_EL3_FW_HANDOFF_BASE + PLAT_ARM_FW_HANDOFF_SIZE + +#if RESET_TO_BL31 +#define PLAT_ARM_TRANSFER_LIST_DTB_OFFSET FW_NS_HANDOFF_BASE + TRANSFER_LIST_DTB_OFFSET +#endif + +#else +#define PLAT_ARM_FW_HANDOFF_SIZE U(0) #endif /* @@ -234,7 +258,13 @@ defined(IMAGE_BL2) && MEASURED_BOOT /* When ARM_BL31_IN_DRAM is set, BL2 can use almost all of Trusted SRAM. */ # define PLAT_ARM_MAX_BL2_SIZE (UL(0x1F000) - FVP_BL2_ROMLIB_OPTIMIZATION) #else -# define PLAT_ARM_MAX_BL2_SIZE (UL(0x13000) - FVP_BL2_ROMLIB_OPTIMIZATION) +/** + * Default to just under half of SRAM to ensure there's enough room for really + * large BL31 build configurations when using the default SRAM size (256 Kb). + */ +#define PLAT_ARM_MAX_BL2_SIZE \ + (((PLAT_ARM_TRUSTED_SRAM_SIZE / 3) & ~PAGE_SIZE_MASK) - PAGE_SIZE - \ + FVP_BL2_ROMLIB_OPTIMIZATION) #endif #if RESET_TO_BL31 @@ -249,9 +279,15 @@ defined(IMAGE_BL2) && MEASURED_BOOT * BL2 and BL1-RW. * Size of the BL31 PROGBITS increases as the SRAM size increases. */ +#if TRANSFER_LIST +#define PLAT_ARM_MAX_BL31_SIZE \ + (PLAT_ARM_TRUSTED_SRAM_SIZE - ARM_SHARED_RAM_SIZE - \ + PLAT_ARM_FW_HANDOFF_SIZE - ARM_L0_GPT_SIZE) +#else #define PLAT_ARM_MAX_BL31_SIZE (PLAT_ARM_TRUSTED_SRAM_SIZE - \ ARM_SHARED_RAM_SIZE - \ ARM_FW_CONFIGS_SIZE - ARM_L0_GPT_SIZE) +#endif /* TRANSFER_LIST */ #endif /* RESET_TO_BL31 */ #ifndef __aarch64__ @@ -265,7 +301,9 @@ defined(IMAGE_BL2) && MEASURED_BOOT * calculated using the current SP_MIN PROGBITS debug size plus the sizes of * BL2 and BL1-RW */ -# define PLAT_ARM_MAX_BL32_SIZE UL(0x3B000) +# define PLAT_ARM_MAX_BL32_SIZE (PLAT_ARM_TRUSTED_SRAM_SIZE - \ + ARM_SHARED_RAM_SIZE - \ + ARM_FW_CONFIGS_SIZE) #endif /* RESET_TO_SP_MIN */ #endif @@ -416,7 +454,7 @@ defined(IMAGE_BL2) && MEASURED_BOOT #define PLAT_SDEI_DP_EVENT_MAX_CNT ARM_SDEI_DP_EVENT_MAX_CNT #define PLAT_SDEI_DS_EVENT_MAX_CNT ARM_SDEI_DS_EVENT_MAX_CNT #else - #if PLATFORM_TEST_RAS_FFH + #if PLATFORM_TEST_RAS_FFH || PLATFORM_TEST_FFH_LSP_RAS_SP #define PLAT_ARM_PRIVATE_SDEI_EVENTS \ ARM_SDEI_PRIVATE_EVENTS, \ SDEI_EXPLICIT_EVENT(5000, SDEI_MAPF_NORMAL), \ diff --git a/plat/arm/board/fvp/jmptbl.i b/plat/arm/board/fvp/jmptbl.i index dc8032f3..077283e4 100644 --- a/plat/arm/board/fvp/jmptbl.i +++ b/plat/arm/board/fvp/jmptbl.i @@ -36,7 +36,6 @@ fdt fdt_get_alias_namelen fdt fdt_get_name fdt fdt_get_alias fdt fdt_node_offset_by_phandle -fdt fdt_subnode_offset fdt fdt_add_subnode mbedtls mbedtls_asn1_get_alg mbedtls mbedtls_asn1_get_alg_null diff --git a/plat/arm/board/fvp/platform.mk b/plat/arm/board/fvp/platform.mk index 2fdff348..0156b31f 100644 --- a/plat/arm/board/fvp/platform.mk +++ b/plat/arm/board/fvp/platform.mk @@ -1,5 +1,5 @@ # -# Copyright (c) 2013-2023, Arm Limited and Contributors. All rights reserved. +# Copyright (c) 2013-2024, Arm Limited and Contributors. All rights reserved. # # SPDX-License-Identifier: BSD-3-Clause # @@ -7,89 +7,80 @@ include common/fdt_wrappers.mk # Use the GICv3 driver on the FVP by default -FVP_USE_GIC_DRIVER := FVP_GICV3 +FVP_USE_GIC_DRIVER := FVP_GICV3 # Default cluster count for FVP -FVP_CLUSTER_COUNT := 2 +FVP_CLUSTER_COUNT := 2 # Default number of CPUs per cluster on FVP FVP_MAX_CPUS_PER_CLUSTER := 4 # Default number of threads per CPU on FVP -FVP_MAX_PE_PER_CPU := 1 +FVP_MAX_PE_PER_CPU := 1 # Disable redistributor frame of inactive/fused CPU cores by marking it as read # only; enable redistributor frames of all CPU cores by default. -FVP_GICR_REGION_PROTECTION := 0 +FVP_GICR_REGION_PROTECTION := 0 -FVP_DT_PREFIX := fvp-base-gicv3-psci +FVP_DT_PREFIX := fvp-base-gicv3-psci -# Size (in kilobytes) of the Trusted SRAM region to utilize when building for +# Size (in kilobytes) of the Trusted SRAM region to utilize when building for # the FVP platform. This option defaults to 256. -FVP_TRUSTED_SRAM_SIZE := 256 +FVP_TRUSTED_SRAM_SIZE := 256 # Macro to enable helpers for running SPM tests. Disabled by default. PLAT_TEST_SPM := 0 -# This is a very trickly TEMPORARY fix. Enabling ALL features exceeds BL31's -# progbits limit. We need a way to build all useful configurations while waiting -# on the fvp to increase its SRAM size. The problem is twofild: -# 1. the cleanup that introduced these enables cleaned up tf-a a little too -# well and things that previously (incorrectly) were enabled, no longer are. -# A bunch of CI configs build subtly incorrectly and this combo makes it -# necessary to forcefully and unconditionally enable them here. -# 2. the progbits limit is exceeded only when the tsp is involved. However, -# there are tsp CI configs that run on very high architecture revisions so -# disabling everything isn't an option. -# The fix is to enable everything, as before. When the tsp is included, though, -# we need to slim the size down. In that case, disable all optional features, -# that will not be present in CI when the tsp is. -# Similarly, DRTM support is only tested on v8.0 models. Disable everything just -# for it. -# TODO: make all of this unconditional (or only base the condition on -# ARM_ARCH_* when the makefile supports it). -ifneq (${DRTM_SUPPORT}, 1) -ifneq (${SPD}, tspd) - ENABLE_FEAT_AMU := 2 - ENABLE_FEAT_AMUv1p1 := 2 - ENABLE_FEAT_HCX := 2 - ENABLE_FEAT_RNG := 2 - ENABLE_FEAT_TWED := 2 - ENABLE_FEAT_GCS := 2 +# By default dont build CPUs with no FVP model. +BUILD_CPUS_WITH_NO_FVP_MODEL ?= 0 + +ENABLE_FEAT_AMU := 2 +ENABLE_FEAT_AMUv1p1 := 2 +ENABLE_FEAT_HCX := 2 +ENABLE_FEAT_RNG := 2 +ENABLE_FEAT_TWED := 2 +ENABLE_FEAT_GCS := 2 + ifeq (${ARCH}, aarch64) -ifneq (${SPD}, spmd) + ifeq (${SPM_MM}, 0) ifeq (${CTX_INCLUDE_FPREGS}, 0) - ENABLE_SME_FOR_NS := 2 - ENABLE_SME2_FOR_NS := 2 -endif -endif -endif + ENABLE_SME_FOR_NS := 2 + ENABLE_SME2_FOR_NS := 2 +else + ENABLE_SVE_FOR_NS := 0 + ENABLE_SME_FOR_NS := 0 + ENABLE_SME2_FOR_NS := 0 endif endif -# enable unconditionally for all builds -ifeq (${ARCH}, aarch64) - ENABLE_BRBE_FOR_NS := 2 - ENABLE_TRBE_FOR_NS := 2 + ENABLE_BRBE_FOR_NS := 2 + ENABLE_TRBE_FOR_NS := 2 + ENABLE_FEAT_D128 := 2 endif + ENABLE_SYS_REG_TRACE_FOR_NS := 2 ENABLE_FEAT_CSV2_2 := 2 +ENABLE_FEAT_CSV2_3 := 2 +ENABLE_FEAT_DEBUGV8P9 := 2 ENABLE_FEAT_DIT := 2 ENABLE_FEAT_PAN := 2 -ENABLE_FEAT_MTE_PERM := 2 ENABLE_FEAT_VHE := 2 CTX_INCLUDE_NEVE_REGS := 2 ENABLE_FEAT_SEL2 := 2 ENABLE_TRF_FOR_NS := 2 ENABLE_FEAT_ECV := 2 ENABLE_FEAT_FGT := 2 +ENABLE_FEAT_FGT2 := 2 +ENABLE_FEAT_THE := 2 ENABLE_FEAT_TCR2 := 2 ENABLE_FEAT_S2PIE := 2 ENABLE_FEAT_S1PIE := 2 ENABLE_FEAT_S2POE := 2 ENABLE_FEAT_S1POE := 2 -endif +ENABLE_FEAT_SCTLR2 := 2 +ENABLE_FEAT_MTE2 := 2 +ENABLE_FEAT_LS64_ACCDATA := 2 # The FVP platform depends on this macro to build with correct GIC driver. $(eval $(call add_define,FVP_USE_GIC_DRIVER)) @@ -205,21 +196,31 @@ else lib/cpus/aarch64/cortex_a78_ae.S \ lib/cpus/aarch64/cortex_a78c.S \ lib/cpus/aarch64/cortex_a710.S \ + lib/cpus/aarch64/cortex_a715.S \ + lib/cpus/aarch64/cortex_a720.S \ + lib/cpus/aarch64/cortex_a720_ae.S \ lib/cpus/aarch64/neoverse_n_common.S \ lib/cpus/aarch64/neoverse_n1.S \ lib/cpus/aarch64/neoverse_n2.S \ lib/cpus/aarch64/neoverse_v1.S \ lib/cpus/aarch64/neoverse_e1.S \ lib/cpus/aarch64/cortex_x2.S \ - lib/cpus/aarch64/cortex_gelas.S \ - lib/cpus/aarch64/nevis.S \ - lib/cpus/aarch64/travis.S + lib/cpus/aarch64/cortex_x4.S endif # AArch64/AArch32 cores FVP_CPU_LIBS += lib/cpus/aarch64/cortex_a55.S \ lib/cpus/aarch64/cortex_a75.S endif +#Build AArch64-only CPUs with no FVP model yet. +ifeq (${BUILD_CPUS_WITH_NO_FVP_MODEL},1) + FVP_CPU_LIBS += lib/cpus/aarch64/neoverse_n3.S \ + lib/cpus/aarch64/cortex_gelas.S \ + lib/cpus/aarch64/nevis.S \ + lib/cpus/aarch64/travis.S \ + lib/cpus/aarch64/cortex_arcadia.S +endif + else FVP_CPU_LIBS += lib/cpus/aarch32/cortex_a32.S \ lib/cpus/aarch32/cortex_a57.S \ @@ -234,8 +235,10 @@ BL1_SOURCES += drivers/arm/smmu/smmu_v3.c \ lib/semihosting/${ARCH}/semihosting_call.S \ plat/arm/board/fvp/${ARCH}/fvp_helpers.S \ plat/arm/board/fvp/fvp_bl1_setup.c \ + plat/arm/board/fvp/fvp_cpu_pwr.c \ plat/arm/board/fvp/fvp_err.c \ plat/arm/board/fvp/fvp_io_storage.c \ + plat/arm/board/fvp/fvp_topology.c \ ${FVP_CPU_LIBS} \ ${FVP_INTERCONNECT_SOURCES} @@ -263,10 +266,12 @@ BL2_SOURCES += plat/arm/common/fconf/fconf_nv_cntr_getter.c endif ifeq (${ENABLE_RME},1) -BL2_SOURCES += plat/arm/board/fvp/aarch64/fvp_helpers.S +BL2_SOURCES += plat/arm/board/fvp/aarch64/fvp_helpers.S \ + plat/arm/board/fvp/fvp_cpu_pwr.c BL31_SOURCES += plat/arm/board/fvp/fvp_plat_attest_token.c \ - plat/arm/board/fvp/fvp_realm_attest_key.c + plat/arm/board/fvp/fvp_realm_attest_key.c \ + plat/arm/board/fvp/fvp_el3_token_sign.c endif ifeq (${ENABLE_FEAT_RNG_TRAP},1) @@ -275,6 +280,7 @@ endif ifeq (${RESET_TO_BL2},1) BL2_SOURCES += plat/arm/board/fvp/${ARCH}/fvp_helpers.S \ + plat/arm/board/fvp/fvp_cpu_pwr.c \ plat/arm/board/fvp/fvp_bl2_el3_setup.c \ ${FVP_CPU_LIBS} \ ${FVP_INTERCONNECT_SOURCES} @@ -301,6 +307,7 @@ BL31_SOURCES += drivers/arm/fvp/fvp_pwrc.c \ plat/arm/board/fvp/fvp_pm.c \ plat/arm/board/fvp/fvp_topology.c \ plat/arm/board/fvp/aarch64/fvp_helpers.S \ + plat/arm/board/fvp/fvp_cpu_pwr.c \ plat/arm/common/arm_nor_psci_mem_protect.c \ ${FVP_CPU_LIBS} \ ${FVP_GIC_SOURCES} \ @@ -330,7 +337,17 @@ endif # Add the FDT_SOURCES and options for Dynamic Config (only for Unix env) ifdef UNIX_MK +FVP_TB_FW_CONFIG := ${BUILD_PLAT}/fdts/${PLAT}_tb_fw_config.dtb FVP_HW_CONFIG_DTS := fdts/${FVP_DT_PREFIX}.dts + +FDT_SOURCES += ${FVP_HW_CONFIG_DTS} +$(eval FVP_HW_CONFIG := ${BUILD_PLAT}/$(patsubst %.dts,%.dtb,$(FVP_HW_CONFIG_DTS))) + +ifeq (${TRANSFER_LIST}, 1) +FDT_SOURCES += $(addprefix plat/arm/board/fvp/fdts/, \ + ${PLAT}_tb_fw_config.dts \ + ) +else FDT_SOURCES += $(addprefix plat/arm/board/fvp/fdts/, \ ${PLAT}_fw_config.dts \ ${PLAT}_tb_fw_config.dts \ @@ -339,7 +356,6 @@ FDT_SOURCES += $(addprefix plat/arm/board/fvp/fdts/, \ ) FVP_FW_CONFIG := ${BUILD_PLAT}/fdts/${PLAT}_fw_config.dtb -FVP_TB_FW_CONFIG := ${BUILD_PLAT}/fdts/${PLAT}_tb_fw_config.dtb FVP_SOC_FW_CONFIG := ${BUILD_PLAT}/fdts/${PLAT}_soc_fw_config.dtb FVP_NT_FW_CONFIG := ${BUILD_PLAT}/fdts/${PLAT}_nt_fw_config.dtb @@ -351,10 +367,6 @@ FVP_TOS_FW_CONFIG := ${BUILD_PLAT}/fdts/${PLAT}_tsp_fw_config.dtb $(eval $(call TOOL_ADD_PAYLOAD,${FVP_TOS_FW_CONFIG},--tos-fw-config,${FVP_TOS_FW_CONFIG})) endif -ifeq (${TRANSFER_LIST}, 1) -include lib/transfer_list/transfer_list.mk -endif - ifeq (${SPD},spmd) ifeq ($(ARM_SPMC_MANIFEST_DTS),) @@ -370,20 +382,30 @@ endif # Add the FW_CONFIG to FIP and specify the same to certtool $(eval $(call TOOL_ADD_PAYLOAD,${FVP_FW_CONFIG},--fw-config,${FVP_FW_CONFIG})) -# Add the TB_FW_CONFIG to FIP and specify the same to certtool -$(eval $(call TOOL_ADD_PAYLOAD,${FVP_TB_FW_CONFIG},--tb-fw-config,${FVP_TB_FW_CONFIG})) # Add the SOC_FW_CONFIG to FIP and specify the same to certtool $(eval $(call TOOL_ADD_PAYLOAD,${FVP_SOC_FW_CONFIG},--soc-fw-config,${FVP_SOC_FW_CONFIG})) # Add the NT_FW_CONFIG to FIP and specify the same to certtool $(eval $(call TOOL_ADD_PAYLOAD,${FVP_NT_FW_CONFIG},--nt-fw-config,${FVP_NT_FW_CONFIG})) +endif -FDT_SOURCES += ${FVP_HW_CONFIG_DTS} -$(eval FVP_HW_CONFIG := ${BUILD_PLAT}/$(patsubst %.dts,%.dtb,$(FVP_HW_CONFIG_DTS))) - +# Add the TB_FW_CONFIG to FIP and specify the same to certtool +$(eval $(call TOOL_ADD_PAYLOAD,${FVP_TB_FW_CONFIG},--tb-fw-config,${FVP_TB_FW_CONFIG})) # Add the HW_CONFIG to FIP and specify the same to certtool $(eval $(call TOOL_ADD_PAYLOAD,${FVP_HW_CONFIG},--hw-config,${FVP_HW_CONFIG})) endif +ifeq (${TRANSFER_LIST}, 1) +include lib/transfer_list/transfer_list.mk + +ifeq ($(RESET_TO_BL31), 1) +HW_CONFIG := ${FVP_HW_CONFIG} +FW_HANDOFF_SIZE := 20000 + +TRANSFER_LIST_DTB_OFFSET := 0x20 +$(eval $(call add_define,TRANSFER_LIST_DTB_OFFSET)) +endif +endif + # Enable dynamic mitigation support by default DYNAMIC_WORKAROUND_CVE_2018_3639 := 1 @@ -398,11 +420,15 @@ endif endif ifeq (${HANDLE_EA_EL3_FIRST_NS},1) -ifeq (${ENABLE_FEAT_RAS},1) -BL31_SOURCES += plat/arm/board/fvp/aarch64/fvp_ras.c -else -BL31_SOURCES += plat/arm/board/fvp/aarch64/fvp_ea.c -endif + ifeq (${ENABLE_FEAT_RAS},1) + ifeq (${PLATFORM_TEST_FFH_LSP_RAS_SP},1) + BL31_SOURCES += plat/arm/board/fvp/aarch64/fvp_lsp_ras_sp.c + else + BL31_SOURCES += plat/arm/board/fvp/aarch64/fvp_ras.c + endif + else + BL31_SOURCES += plat/arm/board/fvp/aarch64/fvp_ea.c + endif endif ifneq (${ENABLE_STACK_PROTECTOR},0) @@ -440,26 +466,6 @@ ifneq (${RESET_TO_BL2}, 0) override BL1_SOURCES = endif -# RSS is not supported on FVP right now. Thus, we use the mocked version -# of the provided PSA APIs. They return with success and hard-coded token/key. -PLAT_RSS_NOT_SUPPORTED := 1 - -# Include Measured Boot makefile before any Crypto library makefile. -# Crypto library makefile may need default definitions of Measured Boot build -# flags present in Measured Boot makefile. -ifeq (${MEASURED_BOOT},1) - RSS_MEASURED_BOOT_MK := drivers/measured_boot/rss/rss_measured_boot.mk - $(info Including ${RSS_MEASURED_BOOT_MK}) - include ${RSS_MEASURED_BOOT_MK} - - ifneq (${MBOOT_RSS_HASH_ALG}, sha256) - $(eval $(call add_define,TF_MBEDTLS_MBOOT_USE_SHA512)) - endif - - BL1_SOURCES += ${MEASURED_BOOT_SOURCES} - BL2_SOURCES += ${MEASURED_BOOT_SOURCES} -endif - include plat/arm/board/common/board_common.mk include plat/arm/common/arm_common.mk @@ -471,23 +477,6 @@ BL1_SOURCES += plat/arm/board/fvp/fvp_common_measured_boot.c \ BL2_SOURCES += plat/arm/board/fvp/fvp_common_measured_boot.c \ plat/arm/board/fvp/fvp_bl2_measured_boot.c \ lib/psa/measured_boot.c - -# Even though RSS is not supported on FVP (see above), we support overriding -# PLAT_RSS_NOT_SUPPORTED from the command line, just for the purpose of building -# the code to detect any build regressions. The resulting firmware will not be -# functional. -ifneq (${PLAT_RSS_NOT_SUPPORTED},1) - $(warning "RSS is not supported on FVP. The firmware will not be functional.") - include drivers/arm/rss/rss_comms.mk - BL1_SOURCES += ${RSS_COMMS_SOURCES} - BL2_SOURCES += ${RSS_COMMS_SOURCES} - BL31_SOURCES += ${RSS_COMMS_SOURCES} - - BL1_CFLAGS += -DPLAT_RSS_COMMS_PAYLOAD_MAX_SIZE=0 - BL2_CFLAGS += -DPLAT_RSS_COMMS_PAYLOAD_MAX_SIZE=0 - BL31_CFLAGS += -DPLAT_RSS_COMMS_PAYLOAD_MAX_SIZE=0 -endif - endif ifeq (${DRTM_SUPPORT}, 1) @@ -538,6 +527,22 @@ ifeq (${PLATFORM_TEST_RAS_FFH}, 1) endif endif +$(eval $(call add_define,PLATFORM_TEST_FFH_LSP_RAS_SP)) +ifeq (${PLATFORM_TEST_FFH_LSP_RAS_SP}, 1) + ifeq (${PLATFORM_TEST_RAS_FFH}, 1) + $(error "PLATFORM_TEST_RAS_FFH is incompatible with PLATFORM_TEST_FFH_LSP_RAS_SP") + endif + ifeq (${ENABLE_SPMD_LP}, 0) + $(error "PLATFORM_TEST_FFH_LSP_RAS_SP expects ENABLE_SPMD_LP to be 1") + endif + ifeq (${ENABLE_FEAT_RAS}, 0) + $(error "PLATFORM_TEST_FFH_LSP_RAS_SP expects ENABLE_FEAT_RAS to be 1") + endif + ifeq (${HANDLE_EA_EL3_FIRST_NS}, 0) + $(error "PLATFORM_TEST_FFH_LSP_RAS_SP expects HANDLE_EA_EL3_FIRST_NS to be 1") + endif +endif + ifeq (${ERRATA_ABI_SUPPORT}, 1) include plat/arm/board/fvp/fvp_cpu_errata.mk endif diff --git a/plat/arm/board/fvp_r/fvp_r_bl1_main.c b/plat/arm/board/fvp_r/fvp_r_bl1_main.c index 252fc31f..6fe2b5b3 100644 --- a/plat/arm/board/fvp_r/fvp_r_bl1_main.c +++ b/plat/arm/board/fvp_r/fvp_r_bl1_main.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2021-2024, Arm Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -12,6 +12,7 @@ #include <arch_helpers.h> #include <bl1/bl1.h> #include <common/bl_common.h> +#include <common/build_message.h> #include <common/debug.h> #include <drivers/auth/auth_mod.h> #include <drivers/console.h> @@ -91,27 +92,6 @@ void bl1_load_bl33(void) NOTICE("BL1: Booting BL33\n"); } -/******************************************************************************* - * Helper utility to calculate the BL2 memory layout taking into consideration - * the BL1 RW data assuming that it is at the top of the memory layout. - ******************************************************************************/ -void bl1_calc_bl2_mem_layout(const meminfo_t *bl1_mem_layout, - meminfo_t *bl2_mem_layout) -{ - assert(bl1_mem_layout != NULL); - assert(bl2_mem_layout != NULL); - - /* - * Remove BL1 RW data from the scope of memory visible to BL2. - * This is assuming BL1 RW data is at the top of bl1_mem_layout. - */ - assert(bl1_mem_layout->total_base < BL1_RW_BASE); - bl2_mem_layout->total_base = bl1_mem_layout->total_base; - bl2_mem_layout->total_size = BL1_RW_BASE - bl1_mem_layout->total_base; - - flush_dcache_range((uintptr_t)bl2_mem_layout, sizeof(meminfo_t)); -} - /******************************************************************************* * This function prepares for entry to BL33 ******************************************************************************/ @@ -182,7 +162,7 @@ void bl1_main(void) /* Announce our arrival */ NOTICE(FIRMWARE_WELCOME_STR); - NOTICE("BL1: %s\n", version_string); + NOTICE("BL1: %s\n", build_version_string); NOTICE("BL1: %s\n", build_message); INFO("BL1: RAM %p - %p\n", (void *)BL1_RAM_BASE, (void *)BL1_RAM_LIMIT); @@ -265,4 +245,3 @@ void print_debug_loop_message(void) NOTICE("BL1: Please connect the debugger to continue\n"); } #endif - diff --git a/plat/arm/board/fvp_r/fvp_r_bl1_setup.c b/plat/arm/board/fvp_r/fvp_r_bl1_setup.c index 6a7c0c80..dcf5e046 100644 --- a/plat/arm/board/fvp_r/fvp_r_bl1_setup.c +++ b/plat/arm/board/fvp_r/fvp_r_bl1_setup.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2021-2024, Arm Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -235,7 +235,7 @@ int bl1_plat_handle_post_image_load(unsigned int image_id) */ bl33_secram_layout = (meminfo_t *) bl1_secram_layout->total_base; - bl1_calc_bl2_mem_layout(bl1_secram_layout, bl33_secram_layout); + bl1_plat_calc_bl2_layout(bl1_secram_layout, bl33_secram_layout); ep_info->args.arg1 = (uintptr_t)bl33_secram_layout; diff --git a/plat/arm/board/fvp_r/include/platform_def.h b/plat/arm/board/fvp_r/include/platform_def.h index ea3a258f..1fdec150 100644 --- a/plat/arm/board/fvp_r/include/platform_def.h +++ b/plat/arm/board/fvp_r/include/platform_def.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2021-2024, Arm Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -75,13 +75,6 @@ #define PLAT_BL1_RO_LIMIT (BL1_RO_BASE \ + PLAT_ARM_TRUSTED_ROM_SIZE) -#define PLAT_ARM_SYS_CNTCTL_BASE UL(0xaa430000) -#define PLAT_ARM_SYS_CNTREAD_BASE UL(0xaa800000) -#define PLAT_ARM_SYS_TIMCTL_BASE UL(0xaa810000) -#define PLAT_ARM_SYS_CNT_BASE_S UL(0xaa820000) -#define PLAT_ARM_SYS_CNT_BASE_NS UL(0xaa830000) -#define PLAT_ARM_SP805_TWDG_BASE UL(0xaa490000) - /* virtual address used by dynamic mem_protect for chunk_base */ #define PLAT_ARM_MEM_PROTEC_VA_FRAME UL(0xc0000000) @@ -90,11 +83,11 @@ #define PLAT_ARM_DRAM2_SIZE UL(0x80000000) -#define PLAT_HW_CONFIG_DTB_SIZE ULL(0x8000) +#define PLAT_ARM_HW_CONFIG_SIZE ULL(0x8000) #define ARM_DTB_DRAM_NS MAP_REGION_FLAT( \ PLAT_HW_CONFIG_DTB_BASE, \ - PLAT_HW_CONFIG_DTB_SIZE, \ + PLAT_ARM_HW_CONFIG_SIZE, \ MT_MEMORY | MT_RO | MT_NS) #define V2M_FVP_R_SYSREGS_BASE UL(0x9c010000) diff --git a/plat/arm/board/fvp_r/platform.mk b/plat/arm/board/fvp_r/platform.mk index f14ea544..71cb9e2d 100644 --- a/plat/arm/board/fvp_r/platform.mk +++ b/plat/arm/board/fvp_r/platform.mk @@ -1,5 +1,5 @@ # -# Copyright (c) 2021, ARM Limited and Contributors. All rights reserved. +# Copyright (c) 2021-2024, Arm Limited and Contributors. All rights reserved. # # SPDX-License-Identifier: BSD-3-Clause # @@ -25,7 +25,7 @@ FVP_R_INTERCONNECT_SOURCES := drivers/arm/cci/cci.c include plat/arm/board/common/board_common.mk include plat/arm/common/arm_common.mk -PLAT_INCLUDES := -Iplat/arm/board/fvp_r/include +PLAT_INCLUDES += -Iplat/arm/board/fvp_r/include FVP_R_BL_COMMON_SOURCES := plat/arm/board/fvp_r/fvp_r_common.c \ plat/arm/board/fvp_r/fvp_r_context_mgmt.c \ diff --git a/plat/arm/board/fvp_ve/fdts/fvp_ve_fw_config.dts b/plat/arm/board/fvp_ve/fdts/fvp_ve_fw_config.dts index 6e5691bd..422ef40f 100644 --- a/plat/arm/board/fvp_ve/fdts/fvp_ve_fw_config.dts +++ b/plat/arm/board/fvp_ve/fdts/fvp_ve_fw_config.dts @@ -1,10 +1,11 @@ /* - * Copyright (c) 2019-2020, Arm Limited. All rights reserved. + * Copyright (c) 2019-2024, Arm Limited. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ #include <common/tbbr/tbbr_img_def.h> +#include <platform_def.h> /dts-v1/; @@ -20,7 +21,7 @@ hw-config { load-address = <0x0 0x82000000>; - max-size = <0x01000000>; + max-size = <PLAT_ARM_HW_CONFIG_SIZE>; id = <HW_CONFIG_ID>; }; }; diff --git a/plat/arm/board/fvp_ve/fvp_ve_def.h b/plat/arm/board/fvp_ve/fvp_ve_def.h index 98de5f66..cb4b74c2 100644 --- a/plat/arm/board/fvp_ve/fvp_ve_def.h +++ b/plat/arm/board/fvp_ve/fvp_ve_def.h @@ -70,15 +70,4 @@ #define FVP_VE_IRQ_TZ_WDOG 56 #define FVP_VE_IRQ_SEC_SYS_TIMER 57 -#define V2M_FLASH1_BASE UL(0x0C000000) -#define V2M_FLASH1_SIZE UL(0x04000000) - -#define V2M_MAP_FLASH1_RW MAP_REGION_FLAT(V2M_FLASH1_BASE,\ - V2M_FLASH1_SIZE, \ - MT_DEVICE | MT_RW | MT_SECURE) - -#define V2M_MAP_FLASH1_RO MAP_REGION_FLAT(V2M_FLASH1_BASE,\ - V2M_FLASH1_SIZE, \ - MT_RO_DATA | MT_SECURE) - #endif /* FVP_VE_DEF_H */ diff --git a/plat/arm/board/fvp_ve/include/platform_def.h b/plat/arm/board/fvp_ve/include/platform_def.h index bd8ef6af..e09ea02e 100644 --- a/plat/arm/board/fvp_ve/include/platform_def.h +++ b/plat/arm/board/fvp_ve/include/platform_def.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019-2020, Arm Limited. All rights reserved. + * Copyright (c) 2019-2024, Arm Limited. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -158,6 +158,9 @@ #define PLAT_PHY_ADDR_SPACE_SIZE (1ULL << 32) #define PLAT_VIRT_ADDR_SPACE_SIZE (1ULL << 32) +/* Define memory configuration for device tree files. */ +#define PLAT_ARM_HW_CONFIG_SIZE U(0x01000000) + /* * This macro defines the deepest retention state possible. A higher state * id will represent an invalid or a power down state. diff --git a/plat/arm/board/juno/fdts/juno_fw_config.dts b/plat/arm/board/juno/fdts/juno_fw_config.dts index 2d79ac7a..11e95741 100644 --- a/plat/arm/board/juno/fdts/juno_fw_config.dts +++ b/plat/arm/board/juno/fdts/juno_fw_config.dts @@ -1,10 +1,11 @@ /* - * Copyright (c) 2019-2023, ARM Limited. All rights reserved. + * Copyright (c) 2019-2024, Arm Limited. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ #include <common/tbbr/tbbr_img_def.h> +#include <platform_def.h> /dts-v1/; @@ -20,7 +21,7 @@ hw-config { load-address = <0x0 0x82000000>; - max-size = <0x8000>; + max-size = <PLAT_ARM_HW_CONFIG_SIZE>; id = <HW_CONFIG_ID>; }; }; diff --git a/plat/arm/board/juno/include/platform_def.h b/plat/arm/board/juno/include/platform_def.h index 5c9a7a3a..777729ef 100644 --- a/plat/arm/board/juno/include/platform_def.h +++ b/plat/arm/board/juno/include/platform_def.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2023, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2014-2024, Arm Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -36,6 +36,9 @@ * Other platform porting definitions are provided by included headers */ +/* Define memory configuration for device tree files. */ +#define PLAT_ARM_HW_CONFIG_SIZE U(0x8000) + /* * Required ARM standard platform porting definitions */ @@ -246,12 +249,14 @@ /* MHU related constants */ #define PLAT_CSS_MHU_BASE UL(0x2b1f0000) +#if CSS_USE_SCMI_SDS_DRIVER +/* Index of SDS region used in the communication between AP and SCP */ +#define SDS_SCP_AP_REGION_ID U(0) +#else /* * Base address of the first memory region used for communication between AP * and SCP. Used by the BOM and SCPI protocols. - */ -#if !CSS_USE_SCMI_SDS_DRIVER -/* + * * Note that this is located at the same address as SCP_BOOT_CFG_ADDR, which * means the SCP/AP configuration data gets overwritten when the AP initiates * communication with the SCP. The configuration data is expected to be a @@ -261,7 +266,7 @@ #define PLAT_CSS_SCP_COM_SHARED_MEM_BASE (ARM_TRUSTED_SRAM_BASE + UL(0x80)) #define PLAT_CSS_PRIMARY_CPU_SHIFT 8 #define PLAT_CSS_PRIMARY_CPU_BIT_WIDTH 4 -#endif +#endif /* CSS_USE_SCMI_SDS_DRIVER */ /* * SCP_BL2 uses up whatever remaining space is available as it is loaded before diff --git a/plat/arm/board/juno/juno_bl1_setup.c b/plat/arm/board/juno/juno_bl1_setup.c index a9d5cc37..2bc948d6 100644 --- a/plat/arm/board/juno/juno_bl1_setup.c +++ b/plat/arm/board/juno/juno_bl1_setup.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015-2021, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2015-2024, Arm Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -32,13 +32,14 @@ static int is_watchdog_reset(void) int ret; uint32_t scp_reset_synd_flags; - ret = sds_init(); + ret = sds_init(SDS_SCP_AP_REGION_ID); if (ret != SDS_OK) { ERROR("SCP SDS initialization failed\n"); panic(); } - ret = sds_struct_read(SDS_RESET_SYNDROME_STRUCT_ID, + ret = sds_struct_read(SDS_SCP_AP_REGION_ID, + SDS_RESET_SYNDROME_STRUCT_ID, SDS_RESET_SYNDROME_OFFSET, &scp_reset_synd_flags, SDS_RESET_SYNDROME_SIZE, diff --git a/plat/arm/board/juno/juno_common.c b/plat/arm/board/juno/juno_common.c index 02614da4..2cd01e41 100644 --- a/plat/arm/board/juno/juno_common.c +++ b/plat/arm/board/juno/juno_common.c @@ -1,14 +1,16 @@ /* - * Copyright (c) 2015-2023, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2015-2024, Arm Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ +#include <drivers/arm/css/sds.h> #include <lib/smccc.h> -#include <platform_def.h> +#include <lib/utils_def.h> #include <services/arm_arch_svc.h> #include <plat/arm/common/plat_arm.h> +#include <platform_def.h> /* * Table of memory regions for different BL stages to map using the MMU. @@ -138,3 +140,16 @@ int32_t plat_get_soc_revision(void) return (int32_t)(((sys_id >> V2M_SYS_ID_REV_SHIFT) & V2M_SYS_ID_REV_MASK) & SOC_ID_REV_MASK); } + +#if CSS_USE_SCMI_SDS_DRIVER +static sds_region_desc_t juno_sds_regions[] = { + { .base = PLAT_ARM_SDS_MEM_BASE }, +}; + +sds_region_desc_t *plat_sds_get_regions(unsigned int *region_count) +{ + *region_count = ARRAY_SIZE(juno_sds_regions); + + return juno_sds_regions; +} +#endif /* CSS_USE_SCMI_SDS_DRIVER */ diff --git a/plat/arm/board/juno/juno_tbbr_cot_bl2.c b/plat/arm/board/juno/juno_tbbr_cot_bl2.c index 8930dbbd..d001dbe6 100644 --- a/plat/arm/board/juno/juno_tbbr_cot_bl2.c +++ b/plat/arm/board/juno/juno_tbbr_cot_bl2.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015-2023, Arm Limited and Contributors. All rights reserved. + * Copyright (c) 2015-2024, Arm Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -745,6 +745,21 @@ static const auth_img_desc_t npu_fw_image = { }; #endif /* ETHOSN_NPU_TZMP1 */ +/* HW Config */ +static const auth_img_desc_t hw_config = { + .img_id = HW_CONFIG_ID, + .img_type = IMG_RAW, + .parent = &trusted_boot_fw_cert, + .img_auth_methods = (const auth_method_desc_t[AUTH_METHOD_NUM]) { + [0] = { + .type = AUTH_METHOD_HASH, + .param.hash = { + .data = &raw_data, + .hash = &hw_config_hash + } + } + } +}; static const auth_img_desc_t * const cot_desc[] = { [TRUSTED_BOOT_FW_CERT_ID] = &trusted_boot_fw_cert, diff --git a/plat/arm/board/juno/platform.mk b/plat/arm/board/juno/platform.mk index a00bf261..8eca0c5e 100644 --- a/plat/arm/board/juno/platform.mk +++ b/plat/arm/board/juno/platform.mk @@ -1,5 +1,5 @@ # -# Copyright (c) 2013-2023, Arm Limited and Contributors. All rights reserved. +# Copyright (c) 2013-2024, Arm Limited and Contributors. All rights reserved. # # SPDX-License-Identifier: BSD-3-Clause # @@ -134,7 +134,7 @@ all : bl1_romlib.bin endif bl1_romlib.bin : $(BUILD_PLAT)/bl1.bin romlib.bin - @echo "Building combined BL1 and ROMLIB binary for Juno $@" + $(s)echo "Building combined BL1 and ROMLIB binary for Juno $@" ./lib/romlib/gen_combined_bl1_romlib.sh -o bl1_romlib.bin $(BUILD_PLAT) # Errata workarounds for Cortex-A53: diff --git a/plat/arm/board/morello/fdts/morello_fw_config.dts b/plat/arm/board/morello/fdts/morello_fw_config.dts index a63d7eb1..a93d6c43 100644 --- a/plat/arm/board/morello/fdts/morello_fw_config.dts +++ b/plat/arm/board/morello/fdts/morello_fw_config.dts @@ -1,10 +1,11 @@ /* - * Copyright (c) 2021-2023, Arm Limited. All rights reserved. + * Copyright (c) 2021-2024, Arm Limited. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ #include <common/tbbr/tbbr_img_def.h> +#include <platform_def.h> /dts-v1/; / { @@ -25,7 +26,7 @@ hw-config { load-address = <0x0 0xFEFF8000>; - max-size = <0x8000>; + max-size = <PLAT_ARM_HW_CONFIG_SIZE>; id = <HW_CONFIG_ID>; }; }; diff --git a/plat/arm/board/morello/include/platform_def.h b/plat/arm/board/morello/include/platform_def.h index 993aa468..7ec89db2 100644 --- a/plat/arm/board/morello/include/platform_def.h +++ b/plat/arm/board/morello/include/platform_def.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020-2023, Arm Limited. All rights reserved. + * Copyright (c) 2020-2024, Arm Limited. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -59,6 +59,10 @@ #if CSS_USE_SCMI_SDS_DRIVER #define MORELLO_SCMI_PAYLOAD_BASE ULL(0x45400000) +/* + * Index of SDS region used in the communication with SCP + */ +#define SDS_SCP_AP_REGION_ID U(0) #else #define PLAT_CSS_SCP_COM_SHARED_MEM_BASE ULL(0x45400000) #endif @@ -71,6 +75,9 @@ */ #define PLAT_ARM_MAX_BL1_RW_SIZE UL(0xC000) +/* Define memory configuration for device tree files. */ +#define PLAT_ARM_HW_CONFIG_SIZE U(0x8000) + /* * PLAT_ARM_MAX_ROMLIB_RW_SIZE is define to use a full page */ diff --git a/plat/arm/board/morello/morello_bl2_setup.c b/plat/arm/board/morello/morello_bl2_setup.c index 39020e23..38e2e6a2 100644 --- a/plat/arm/board/morello/morello_bl2_setup.c +++ b/plat/arm/board/morello/morello_bl2_setup.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021-2023, Arm Limited. All rights reserved. + * Copyright (c) 2021-2024, Arm Limited. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -155,13 +155,14 @@ void bl2_platform_setup(void) int ret; struct morello_plat_info plat_info; - ret = sds_init(); + ret = sds_init(SDS_SCP_AP_REGION_ID); if (ret != SDS_OK) { ERROR("SDS initialization failed. ret:%d\n", ret); panic(); } - ret = sds_struct_read(MORELLO_SDS_PLATFORM_INFO_STRUCT_ID, + ret = sds_struct_read(SDS_SCP_AP_REGION_ID, + MORELLO_SDS_PLATFORM_INFO_STRUCT_ID, MORELLO_SDS_PLATFORM_INFO_OFFSET, &plat_info, MORELLO_SDS_PLATFORM_INFO_SIZE, diff --git a/plat/arm/board/morello/morello_bl31_setup.c b/plat/arm/board/morello/morello_bl31_setup.c index 8469cd13..63738255 100644 --- a/plat/arm/board/morello/morello_bl31_setup.c +++ b/plat/arm/board/morello/morello_bl31_setup.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020-2023, Arm Limited. All rights reserved. + * Copyright (c) 2020-2024, Arm Limited. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -43,13 +43,14 @@ void bl31_platform_setup(void) #ifdef TARGET_PLATFORM_SOC int ret; - ret = sds_init(); + ret = sds_init(SDS_SCP_AP_REGION_ID); if (ret != SDS_OK) { ERROR("SDS initialization failed. ret:%d\n", ret); panic(); } - ret = sds_struct_read(MORELLO_SDS_PLATFORM_INFO_STRUCT_ID, + ret = sds_struct_read(SDS_SCP_AP_REGION_ID, + MORELLO_SDS_PLATFORM_INFO_STRUCT_ID, MORELLO_SDS_PLATFORM_INFO_OFFSET, &plat_info, MORELLO_SDS_PLATFORM_INFO_SIZE, diff --git a/plat/arm/board/morello/morello_image_load.c b/plat/arm/board/morello/morello_image_load.c index 4ea2bb30..b9590319 100644 --- a/plat/arm/board/morello/morello_image_load.c +++ b/plat/arm/board/morello/morello_image_load.c @@ -1,10 +1,11 @@ /* - * Copyright (c) 2021-2023, Arm Limited. All rights reserved. + * Copyright (c) 2021-2024, Arm Limited. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ #include <arch_helpers.h> +#include <common/build_message.h> #include <common/debug.h> #include <common/desc_image_load.h> #include <drivers/arm/css/sds.h> @@ -13,6 +14,7 @@ #include "morello_def.h" #include <plat/arm/common/plat_arm.h> #include <plat/common/platform.h> +#include <platform_def.h> /* In client mode, a part of the DDR memory is reserved for Tag bits. * Calculate the usable memory size after subtracting the Tag memory. @@ -141,7 +143,7 @@ static int plat_morello_append_config_node(struct morello_plat_info *plat_info, return -1; } - err = fdt_setprop_string(fdt, nodeoffset_fw, "tfa-fw-version", version_string); + err = fdt_setprop_string(fdt, nodeoffset_fw, "tfa-fw-version", build_version_string); if (err < 0) { WARN("NT_FW_CONFIG: Unable to set tfa-fw-version\n"); } @@ -167,13 +169,14 @@ bl_params_t *plat_get_next_bl_params(void) struct morello_plat_info plat_info; struct morello_firmware_version fw_version; - ret = sds_init(); + ret = sds_init(SDS_SCP_AP_REGION_ID); if (ret != SDS_OK) { ERROR("SDS initialization failed. ret:%d\n", ret); panic(); } - ret = sds_struct_read(MORELLO_SDS_PLATFORM_INFO_STRUCT_ID, + ret = sds_struct_read(SDS_SCP_AP_REGION_ID, + MORELLO_SDS_PLATFORM_INFO_STRUCT_ID, MORELLO_SDS_PLATFORM_INFO_OFFSET, &plat_info, MORELLO_SDS_PLATFORM_INFO_SIZE, @@ -183,7 +186,8 @@ bl_params_t *plat_get_next_bl_params(void) panic(); } - ret = sds_struct_read(MORELLO_SDS_FIRMWARE_VERSION_STRUCT_ID, + ret = sds_struct_read(SDS_SCP_AP_REGION_ID, + MORELLO_SDS_FIRMWARE_VERSION_STRUCT_ID, MORELLO_SDS_FIRMWARE_VERSION_OFFSET, &fw_version, MORELLO_SDS_FIRMWARE_VERSION_SIZE, diff --git a/plat/arm/board/morello/morello_plat.c b/plat/arm/board/morello/morello_plat.c index 2ca3d08c..61fed642 100644 --- a/plat/arm/board/morello/morello_plat.c +++ b/plat/arm/board/morello/morello_plat.c @@ -1,12 +1,14 @@ /* - * Copyright (c) 2020-2023, Arm Limited. All rights reserved. + * Copyright (c) 2020-2024, Arm Limited. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ #include <assert.h> +#include <drivers/arm/css/sds.h> #include <drivers/arm/sbsa.h> +#include <lib/utils_def.h> #include <plat/arm/common/plat_arm.h> #include "morello_def.h" @@ -68,3 +70,16 @@ void plat_arm_secure_wdt_stop(void) { sbsa_wdog_stop(SBSA_SECURE_WDOG_BASE); } + +#if CSS_USE_SCMI_SDS_DRIVER +static sds_region_desc_t morello_sds_regions[] = { + { .base = PLAT_ARM_SDS_MEM_BASE }, +}; + +sds_region_desc_t *plat_sds_get_regions(unsigned int *region_count) +{ + *region_count = ARRAY_SIZE(morello_sds_regions); + + return morello_sds_regions; +} +#endif /* CSS_USE_SCMI_SDS_DRIVER */ diff --git a/plat/arm/board/n1sdp/include/platform_def.h b/plat/arm/board/n1sdp/include/platform_def.h index 74d0c911..eb878edb 100644 --- a/plat/arm/board/n1sdp/include/platform_def.h +++ b/plat/arm/board/n1sdp/include/platform_def.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018-2023, Arm Limited. All rights reserved. + * Copyright (c) 2018-2024, Arm Limited. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -19,9 +19,6 @@ #define PLAT_ARM_RUN_UART_BASE 0x1C090000 #define PLAT_ARM_RUN_UART_CLK_IN_HZ 24000000 -#define PLAT_ARM_SP_MIN_RUN_UART_BASE 0x2A410000 -#define PLAT_ARM_SP_MIN_RUN_UART_CLK_IN_HZ 50000000 - #define PLAT_ARM_CRASH_UART_BASE PLAT_ARM_RUN_UART_BASE #define PLAT_ARM_CRASH_UART_CLK_IN_HZ PLAT_ARM_RUN_UART_CLK_IN_HZ @@ -77,6 +74,10 @@ #if CSS_USE_SCMI_SDS_DRIVER #define N1SDP_SCMI_PAYLOAD_BASE 0x45400000 +/* + * Index of SDS region used in the communication with SCP + */ +#define SDS_SCP_AP_REGION_ID U(0) #else #define PLAT_CSS_SCP_COM_SHARED_MEM_BASE 0x45400000 #endif diff --git a/plat/arm/board/n1sdp/n1sdp_bl2_setup.c b/plat/arm/board/n1sdp/n1sdp_bl2_setup.c index 5f8af9f3..5a5b9a5f 100644 --- a/plat/arm/board/n1sdp/n1sdp_bl2_setup.c +++ b/plat/arm/board/n1sdp/n1sdp_bl2_setup.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, Arm Limited. All rights reserved. + * Copyright (c) 2022-2024, Arm Limited. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -11,6 +11,7 @@ #include "n1sdp_def.h" #include <plat/arm/common/plat_arm.h> +#include <platform_def.h> struct n1sdp_plat_info { bool multichip_mode; @@ -60,13 +61,14 @@ void bl2_platform_setup(void) int ret; struct n1sdp_plat_info plat_info; - ret = sds_init(); + ret = sds_init(SDS_SCP_AP_REGION_ID); if (ret != SDS_OK) { ERROR("SDS initialization failed\n"); panic(); } - ret = sds_struct_read(N1SDP_SDS_PLATFORM_INFO_STRUCT_ID, + ret = sds_struct_read(SDS_SCP_AP_REGION_ID, + N1SDP_SDS_PLATFORM_INFO_STRUCT_ID, N1SDP_SDS_PLATFORM_INFO_OFFSET, &plat_info, N1SDP_SDS_PLATFORM_INFO_SIZE, diff --git a/plat/arm/board/n1sdp/n1sdp_bl31_setup.c b/plat/arm/board/n1sdp/n1sdp_bl31_setup.c index 430aab68..27ea7f7d 100644 --- a/plat/arm/board/n1sdp/n1sdp_bl31_setup.c +++ b/plat/arm/board/n1sdp/n1sdp_bl31_setup.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018-2023, Arm Limited. All rights reserved. + * Copyright (c) 2018-2024, Arm Limited. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -127,13 +127,14 @@ void bl31_platform_setup(void) int ret; struct n1sdp_plat_info plat_info; - ret = sds_init(); + ret = sds_init(SDS_SCP_AP_REGION_ID); if (ret != SDS_OK) { ERROR("SDS initialization failed\n"); panic(); } - ret = sds_struct_read(N1SDP_SDS_PLATFORM_INFO_STRUCT_ID, + ret = sds_struct_read(SDS_SCP_AP_REGION_ID, + N1SDP_SDS_PLATFORM_INFO_STRUCT_ID, N1SDP_SDS_PLATFORM_INFO_OFFSET, &plat_info, N1SDP_SDS_PLATFORM_INFO_SIZE, diff --git a/plat/arm/board/n1sdp/n1sdp_image_load.c b/plat/arm/board/n1sdp/n1sdp_image_load.c index 6c3528ce..6ae2b262 100644 --- a/plat/arm/board/n1sdp/n1sdp_image_load.c +++ b/plat/arm/board/n1sdp/n1sdp_image_load.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, Arm Limited. All rights reserved. + * Copyright (c) 2022-2024, Arm Limited. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -13,6 +13,7 @@ #include "n1sdp_def.h" #include <plat/arm/common/plat_arm.h> +#include <platform_def.h> /* * Platform information structure stored in SDS. @@ -108,13 +109,14 @@ bl_params_t *plat_get_next_bl_params(void) int ret; struct n1sdp_plat_info plat_info; - ret = sds_init(); + ret = sds_init(SDS_SCP_AP_REGION_ID); if (ret != SDS_OK) { ERROR("SDS initialization failed. ret:%d\n", ret); panic(); } - ret = sds_struct_read(N1SDP_SDS_PLATFORM_INFO_STRUCT_ID, + ret = sds_struct_read(SDS_SCP_AP_REGION_ID, + N1SDP_SDS_PLATFORM_INFO_STRUCT_ID, N1SDP_SDS_PLATFORM_INFO_OFFSET, &plat_info, N1SDP_SDS_PLATFORM_INFO_SIZE, diff --git a/plat/arm/board/n1sdp/n1sdp_plat.c b/plat/arm/board/n1sdp/n1sdp_plat.c index 747ff068..42efdeef 100644 --- a/plat/arm/board/n1sdp/n1sdp_plat.c +++ b/plat/arm/board/n1sdp/n1sdp_plat.c @@ -1,12 +1,14 @@ /* - * Copyright (c) 2018-2023, Arm Limited. All rights reserved. + * Copyright (c) 2018-2024, Arm Limited. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ #include <assert.h> +#include <drivers/arm/css/sds.h> #include <drivers/arm/sbsa.h> +#include <lib/utils_def.h> #include <plat/arm/common/plat_arm.h> #include "n1sdp_def.h" @@ -71,3 +73,16 @@ void plat_arm_secure_wdt_stop(void) { sbsa_wdog_stop(SBSA_SECURE_WDOG_BASE); } + +#if CSS_USE_SCMI_SDS_DRIVER +static sds_region_desc_t n1sdp_sds_regions[] = { + { .base = PLAT_ARM_SDS_MEM_BASE }, +}; + +sds_region_desc_t *plat_sds_get_regions(unsigned int *region_count) +{ + *region_count = ARRAY_SIZE(n1sdp_sds_regions); + + return n1sdp_sds_regions; +} +#endif /* CSS_USE_SCMI_SDS_DRIVER */ diff --git a/plat/arm/css/sgi/aarch64/sgi_helper.S b/plat/arm/board/neoverse_rd/common/arch/aarch64/nrd_helper.S similarity index 84% rename from plat/arm/css/sgi/aarch64/sgi_helper.S rename to plat/arm/board/neoverse_rd/common/arch/aarch64/nrd_helper.S index ced59e8d..8d9c0d71 100644 --- a/plat/arm/css/sgi/aarch64/sgi_helper.S +++ b/plat/arm/board/neoverse_rd/common/arch/aarch64/nrd_helper.S @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018-2020, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2018-2024, Arm Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -21,15 +21,15 @@ * * Helper function to calculate the core position. * (ChipId * PLAT_ARM_CLUSTER_COUNT * - * CSS_SGI_MAX_CPUS_PER_CLUSTER * CSS_SGI_MAX_PE_PER_CPU) + - * (ClusterId * CSS_SGI_MAX_CPUS_PER_CLUSTER * CSS_SGI_MAX_PE_PER_CPU) + - * (CPUId * CSS_SGI_MAX_PE_PER_CPU) + + * NRD_MAX_CPUS_PER_CLUSTER * NRD_MAX_PE_PER_CPU) + + * (ClusterId * NRD_MAX_CPUS_PER_CLUSTER * NRD_MAX_PE_PER_CPU) + + * (CPUId * NRD_MAX_PE_PER_CPU) + * ThreadId * * which can be simplified as: * * ((((ChipId * PLAT_ARM_CLUSTER_COUNT) + ClusterId) * - * CSS_SGI_MAX_CPUS_PER_CLUSTER) + CPUId) * CSS_SGI_MAX_PE_PER_CPU + + * NRD_MAX_CPUS_PER_CLUSTER) + CPUId) * NRD_MAX_PE_PER_CPU + * ThreadId * ------------------------------------------------------ */ @@ -38,7 +38,7 @@ func plat_arm_calc_core_pos mov x4, x0 /* - * The MT bit in MPIDR is always set for SGI platforms + * The MT bit in MPIDR is always set for Neoverse RD platforms * and the affinity level 0 corresponds to thread affinity level. */ @@ -51,9 +51,9 @@ func plat_arm_calc_core_pos /* Compute linear position */ mov x4, #PLAT_ARM_CLUSTER_COUNT madd x2, x3, x4, x2 - mov x4, #CSS_SGI_MAX_CPUS_PER_CLUSTER + mov x4, #NRD_MAX_CPUS_PER_CLUSTER madd x1, x2, x4, x1 - mov x4, #CSS_SGI_MAX_PE_PER_CPU + mov x4, #NRD_MAX_PE_PER_CPU madd x0, x1, x4, x0 ret endfunc plat_arm_calc_core_pos diff --git a/plat/arm/board/neoverse_rd/common/include/nrd1/nrd_css_def1.h b/plat/arm/board/neoverse_rd/common/include/nrd1/nrd_css_def1.h new file mode 100644 index 00000000..74835f69 --- /dev/null +++ b/plat/arm/board/neoverse_rd/common/include/nrd1/nrd_css_def1.h @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2024, Arm Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + * + * This file is limited to include the CSS specific memory and interrupt map + * definitions for the first generation platforms based on the A75, N1 and V1 + * CPUs. There are minor differences in the memory map of these platforms and + * those differences are not in the scope of this file. + */ + +#ifndef NRD_CSS_DEF1_H +#define NRD_CSS_DEF1_H + +/******************************************************************************* + * CSS memory map related defines + ******************************************************************************/ + +/* On-Chip ROM */ +#define NRD_CSS_TRUSTED_ROM_BASE UL(0x00000000) +#define NRD_CSS_TRUSTED_ROM_SIZE UL(0x00080000) /* 512KB */ + +/* On-Chip RAM */ +#define NRD_CSS_TRUSTED_SRAM_SIZE UL(0x00080000) /* 512KB */ +#define NRD_CSS_NONTRUSTED_SRAM_BASE UL(0x06000000) +#define NRD_CSS_NONTRUSTED_SRAM_SIZE UL(0x00080000) /* 512KB */ + +/* PL011 UART */ +#define NRD_CSS_SEC_UART_BASE UL(0x2A410000) +#define NRD_CSS_UART_SIZE UL(0x10000) + +/* CSS peripherals */ +#define NRD_CSS_PERIPH_BASE UL(0x20000000) +#define NRD_CSS_PERIPH_SIZE UL(0x40000000) + +/* Secure Watchdog */ +#define NRD_CSS_WDOG_BASE UL(0x2A480000) + +/* DRAM2 */ +#define NRD_CSS_DRAM2_BASE ULL(0x8080000000) +#define NRD_CSS_DRAM2_SIZE ULL(0x180000000) + +#endif /* NRD_CSS_DEF1_H */ diff --git a/plat/arm/board/neoverse_rd/common/include/nrd1/nrd_css_fw_def1.h b/plat/arm/board/neoverse_rd/common/include/nrd1/nrd_css_fw_def1.h new file mode 100644 index 00000000..70a7d49b --- /dev/null +++ b/plat/arm/board/neoverse_rd/common/include/nrd1/nrd_css_fw_def1.h @@ -0,0 +1,92 @@ +/* + * Copyright (c) 2024, Arm Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + * + * This file is limited to include the CSS firmware specific definitions for + * the first generation platforms based on the A75, N1 and V1 CPUs. + */ + +#ifndef NRD1_CSS_FW_DEF1_H +#define NRD1_CSS_FW_DEF1_H + +#include <nrd_css_def1.h> + +/******************************************************************************* + * BL sizes + ******************************************************************************/ + +#define NRD_CSS_BL1_RW_SIZE UL(64 * 1024) /* 64KB */ + +#if TRUSTED_BOARD_BOOT +# define NRD_CSS_BL2_SIZE UL(0x28000) +#else +# define NRD_CSS_BL2_SIZE UL(0x14000) +#endif + +/* + * Since BL31 NOBITS overlays BL2 and BL1-RW, PLAT_ARM_MAX_BL31_SIZE is + * calculated using the current BL31 PROGBITS debug size plus the sizes of BL2 + * and BL1-RW. + */ +#define NRD_CSS_BL31_SIZE UL(116 * 1024) /* 116 KB */ + +/******************************************************************************* + * Console config + ******************************************************************************/ + +#define NRD_CSS_UART_CLK_IN_HZ UL(7372800) + +/******************************************************************************* + * Watchdog config + ******************************************************************************/ + +#define NRD_CSS_WDOG_TIMEOUT UL(100) + +/******************************************************************************* + * Platform ID + ******************************************************************************/ + +/* Platform ID address */ +#define SSC_VERSION (SSC_REG_BASE + SSC_VERSION_OFFSET) +#ifndef __ASSEMBLER__ +/* SSC_VERSION related accessors */ +/* Returns the part number of the platform */ +#define GET_NRD_PART_NUM \ + GET_SSC_VERSION_PART_NUM(mmio_read_32(SSC_VERSION)) +/* Returns the configuration number of the platform */ +#define GET_NRD_CONFIG_NUM \ + GET_SSC_VERSION_CONFIG(mmio_read_32(SSC_VERSION)) +#endif /* __ASSEMBLER__ */ + +/******************************************************************************* + * MMU mappings + ******************************************************************************/ + +#define NRD_CSS_PERIPH_MMAP(n) \ + MAP_REGION_FLAT( \ + NRD_REMOTE_CHIP_MEM_OFFSET(n) + \ + NRD_CSS_PERIPH_BASE, \ + NRD_CSS_PERIPH_SIZE, \ + MT_DEVICE | MT_RW | MT_SECURE) + +#define NRD_CSS_SHARED_RAM_MMAP(n) \ + MAP_REGION_FLAT( \ + NRD_REMOTE_CHIP_MEM_OFFSET(n) + \ + ARM_SHARED_RAM_BASE, \ + ARM_SHARED_RAM_SIZE, \ + MT_NON_CACHEABLE | MT_RW | MT_SECURE) + +#if SPM_MM +/* + * Stand-alone MM logs would be routed via secure UART. Define page table + * entry for secure UART which would be common to all platforms. + */ +#define NRD_CSS_SECURE_UART_MMAP \ + MAP_REGION_FLAT( \ + NRD_CSS_SEC_UART_BASE, \ + NRD_CSS_UART_SIZE, \ + MT_DEVICE | MT_RW | MT_SECURE | MT_USER) +#endif + +#endif /* NRD_CSS_FW_DEF1_H */ diff --git a/plat/arm/board/neoverse_rd/common/include/nrd1/nrd_plat_arm_def1.h b/plat/arm/board/neoverse_rd/common/include/nrd1/nrd_plat_arm_def1.h new file mode 100644 index 00000000..bca095cd --- /dev/null +++ b/plat/arm/board/neoverse_rd/common/include/nrd1/nrd_plat_arm_def1.h @@ -0,0 +1,249 @@ +/* + * Copyright (c) 2024, Arm Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + * + * This file is limited to include the platform port definitions for the + * first generation platforms based on the A75, N1 and V1 CPUs. + */ + +#ifndef NRD_PLAT_ARM_DEF1_H +#define NRD_PLAT_ARM_DEF1_H + +#ifndef __ASSEMBLER__ +#include <lib/mmio.h> +#endif /* __ASSEMBLER__ */ + +#include <lib/utils_def.h> +#include <lib/xlat_tables/xlat_tables_defs.h> +#include <plat/arm/board/common/v2m_def.h> +#include <plat/arm/common/arm_def.h> +#include <plat/arm/common/arm_spm_def.h> +#include <plat/arm/css/common/css_def.h> +#include <plat/arm/soc/common/soc_css_def.h> +#include <plat/common/common_def.h> +#include <nrd_css_fw_def1.h> +#include <nrd_ros_fw_def1.h> + +/******************************************************************************* + * Core count + ******************************************************************************/ + +#define PLATFORM_CORE_COUNT (NRD_CHIP_COUNT * \ + PLAT_ARM_CLUSTER_COUNT * \ + NRD_MAX_CPUS_PER_CLUSTER * \ + NRD_MAX_PE_PER_CPU) + +/******************************************************************************* + * PA/VA config + ******************************************************************************/ + +#ifdef __aarch64__ +#define PLAT_PHY_ADDR_SPACE_SIZE NRD_REMOTE_CHIP_MEM_OFFSET( \ + NRD_CHIP_COUNT) +#define PLAT_VIRT_ADDR_SPACE_SIZE NRD_REMOTE_CHIP_MEM_OFFSET( \ + NRD_CHIP_COUNT) +#else +#define PLAT_PHY_ADDR_SPACE_SIZE (1ULL << 32) +#define PLAT_VIRT_ADDR_SPACE_SIZE (1ULL << 32) +#endif + +/******************************************************************************* + * XLAT definitions + ******************************************************************************/ + +#if defined(IMAGE_BL31) +# if SPM_MM || (SPMC_AT_EL3 && SPMC_AT_EL3_SEL0_SP) +# define PLAT_ARM_MMAP_ENTRIES (10 + ((NRD_CHIP_COUNT - 1) * 3)) +# define MAX_XLAT_TABLES (11 + ((NRD_CHIP_COUNT - 1) * 3)) +# define PLAT_SP_IMAGE_MMAP_REGIONS U(12) +# define PLAT_SP_IMAGE_MAX_XLAT_TABLES U(14) +# else +# define PLAT_ARM_MMAP_ENTRIES (5 + ((NRD_CHIP_COUNT - 1) * 3)) +# define MAX_XLAT_TABLES (6 + ((NRD_CHIP_COUNT - 1) * 3)) +# endif +#elif defined(IMAGE_BL32) +# define PLAT_ARM_MMAP_ENTRIES U(8) +# define MAX_XLAT_TABLES U(5) +#elif defined(IMAGE_BL2) +# define PLAT_ARM_MMAP_ENTRIES (11 + (NRD_CHIP_COUNT - 1)) + +/* + * MAX_XLAT_TABLES entries need to be doubled because when the address width + * exceeds 40 bits an additional level of translation is required. In case of + * multichip platforms peripherals also fall into address space with width + * > 40 bits. + */ +# define MAX_XLAT_TABLES (11 + ((NRD_CHIP_COUNT - 1) * 2)) +#elif !USE_ROMLIB +# define PLAT_ARM_MMAP_ENTRIES U(11) +# define MAX_XLAT_TABLES U(7) +#else +# define PLAT_ARM_MMAP_ENTRIES U(12) +# define MAX_XLAT_TABLES U(6) +#endif + +/******************************************************************************* + * Stack size + ******************************************************************************/ + +#if defined(IMAGE_BL1) +# if TRUSTED_BOARD_BOOT +# define PLATFORM_STACK_SIZE U(0x1000) +# else +# define PLATFORM_STACK_SIZE U(0x440) +# endif +#elif defined(IMAGE_BL2) +# if TRUSTED_BOARD_BOOT +# define PLATFORM_STACK_SIZE U(0x1000) +# else +# define PLATFORM_STACK_SIZE U(0x400) +# endif +#elif defined(IMAGE_BL2U) +# define PLATFORM_STACK_SIZE U(0x400) +#elif defined(IMAGE_BL31) +# if SPM_MM +# define PLATFORM_STACK_SIZE U(0x500) +# else +# define PLATFORM_STACK_SIZE U(0x400) +# endif +#elif defined(IMAGE_BL32) +# define PLATFORM_STACK_SIZE U(0x440) +#endif + +#if (SPM_MM || (SPMC_AT_EL3 && SPMC_AT_EL3_SEL0_SP)) +/* + * Secure partition stack follows right after the memory region that is shared + * between EL3 and S-EL0. + */ +#define PLAT_ARM_SP_IMAGE_STACK_BASE (PLAT_SP_IMAGE_NS_BUF_BASE + \ + PLAT_SP_IMAGE_NS_BUF_SIZE) +#endif /* SPM_MM || (SPMC_AT_EL3 && SPMC_AT_EL3_SEL0_SP) */ + +/******************************************************************************* + * BL sizes + ******************************************************************************/ + +#if USE_ROMLIB +#define PLAT_ARM_MAX_ROMLIB_RW_SIZE U(0x1000) +#define PLAT_ARM_MAX_ROMLIB_RO_SIZE U(0xe000) +#else +#define PLAT_ARM_MAX_ROMLIB_RW_SIZE U(0) +#define PLAT_ARM_MAX_ROMLIB_RO_SIZE U(0) +#endif + +#define PLAT_ARM_MAX_BL1_RW_SIZE NRD_CSS_BL1_RW_SIZE + +/* + * PLAT_ARM_MAX_BL2_SIZE is calculated using the current BL2 debug size plus a + * little space for growth. Additional 8KiB space is added per chip in + * order to accommodate the additional level of translation required for "TZC" + * peripheral access which lies in >4TB address space. + * + */ +#define PLAT_ARM_MAX_BL2_SIZE (NRD_CSS_BL2_SIZE + \ + ((NRD_CHIP_COUNT - 1) * 0x2000)) + +#define PLAT_ARM_MAX_BL31_SIZE (NRD_CSS_BL31_SIZE + \ + PLAT_ARM_MAX_BL2_SIZE + \ + PLAT_ARM_MAX_BL1_RW_SIZE) + +/******************************************************************************* + * ROM, SRAM and DRAM config + ******************************************************************************/ + +#define PLAT_ARM_TRUSTED_SRAM_SIZE NRD_CSS_TRUSTED_SRAM_SIZE + +#define PLAT_ARM_TRUSTED_ROM_BASE NRD_CSS_TRUSTED_ROM_BASE +#define PLAT_ARM_TRUSTED_ROM_SIZE NRD_CSS_TRUSTED_ROM_SIZE + +#define PLAT_ARM_NSRAM_BASE NRD_CSS_NONTRUSTED_SRAM_BASE +#define PLAT_ARM_NSRAM_SIZE NRD_CSS_NONTRUSTED_SRAM_SIZE + +#define PLAT_ARM_DRAM2_BASE NRD_CSS_DRAM2_BASE +#define PLAT_ARM_DRAM2_SIZE NRD_CSS_DRAM2_SIZE + +/******************************************************************************* + * Console config + ******************************************************************************/ + +#define PLAT_ARM_BOOT_UART_BASE NRD_CSS_SEC_UART_BASE +#define PLAT_ARM_BOOT_UART_CLK_IN_HZ NRD_CSS_UART_CLK_IN_HZ + +#define PLAT_ARM_RUN_UART_BASE NRD_CSS_SEC_UART_BASE +#define PLAT_ARM_RUN_UART_CLK_IN_HZ NRD_CSS_UART_CLK_IN_HZ + +#define PLAT_ARM_CRASH_UART_BASE NRD_CSS_SEC_UART_BASE +#define PLAT_ARM_CRASH_UART_CLK_IN_HZ NRD_CSS_UART_CLK_IN_HZ + +/******************************************************************************* + * Timer config + ******************************************************************************/ + +#define PLAT_ARM_NSTIMER_FRAME_ID (0) + +/******************************************************************************* + * Power config + ******************************************************************************/ + +#define CSS_SYSTEM_PWR_DMN_LVL ARM_PWR_LVL2 +#define PLAT_MAX_PWR_LVL ARM_PWR_LVL1 + +/******************************************************************************* + * Flash config + ******************************************************************************/ + +#define PLAT_ARM_FLASH_IMAGE_BASE V2M_FLASH0_BASE +#define PLAT_ARM_FLASH_IMAGE_MAX_SIZE (V2M_FLASH0_SIZE - V2M_FLASH_BLOCK_SIZE) +#define PLAT_ARM_NVM_BASE V2M_FLASH0_BASE +#define PLAT_ARM_NVM_SIZE (V2M_FLASH0_SIZE - V2M_FLASH_BLOCK_SIZE) +#define PLAT_ARM_MEM_PROT_ADDR (V2M_FLASH0_BASE + \ + V2M_FLASH0_SIZE - V2M_FLASH_BLOCK_SIZE) +/* IO storage framework */ +#define MAX_IO_DEVICES U(3) +#define MAX_IO_HANDLES U(4) + +/******************************************************************************* + * SCMI config + ******************************************************************************/ + +/* Number of SCMI channels on the platform */ +#define PLAT_ARM_SCMI_CHANNEL_COUNT NRD_CHIP_COUNT + +/******************************************************************************* + * SDS config + ******************************************************************************/ + +/* Index of SDS region used in the communication with SCP */ +#define SDS_SCP_AP_REGION_ID U(0) +/* SDS ID for unusable CPU MPID list structure */ +#define SDS_ISOLATED_CPU_LIST_ID U(128) + +/******************************************************************************* + * GIC/EHF config + ******************************************************************************/ + +#define PLAT_ARM_G1S_IRQ_PROPS(grp) CSS_G1S_IRQ_PROPS(grp) +#define PLAT_ARM_G0_IRQ_PROPS(grp) ARM_G0_IRQ_PROPS(grp) +#define PLAT_SP_PRI U(0x10) + +/******************************************************************************* + * Platform type identification macro + ******************************************************************************/ + +/* Platform ID related accessors */ +#define BOARD_CSS_PLAT_ID_REG_ID_MASK U(0x0f) +#define BOARD_CSS_PLAT_ID_REG_ID_SHIFT U(0x0) +#define BOARD_CSS_PLAT_TYPE_EMULATOR U(0x02) + +#ifndef __ASSEMBLER__ +#define BOARD_CSS_GET_PLAT_TYPE(addr) \ + ((mmio_read_32(addr) & BOARD_CSS_PLAT_ID_REG_ID_MASK) \ + >> BOARD_CSS_PLAT_ID_REG_ID_SHIFT) +#endif /* __ASSEMBLER__ */ + +/* Platform ID address */ +#define BOARD_CSS_PLAT_ID_REG_ADDR NRD_ROS_PLATFORM_BASE + \ + UL(0x00fe00e0) + +#endif /* NRD_PLAT_ARM_DEF1_H */ diff --git a/plat/arm/board/neoverse_rd/common/include/nrd1/nrd_ros_def1.h b/plat/arm/board/neoverse_rd/common/include/nrd1/nrd_ros_def1.h new file mode 100644 index 00000000..b86ab21d --- /dev/null +++ b/plat/arm/board/neoverse_rd/common/include/nrd1/nrd_ros_def1.h @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2024, Arm Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + * + * This file is limited to include the RoS specific definitions for the first + * generation platforms based on the A75, N1 and V1 CPUs. RoS (Rest Of System) + * is used to refer to the part of the reference design platform that excludes + * CSS. + */ + +#ifndef NRD_ROS_DEF1_H +#define NRD_ROS_DEF1_H + +/******************************************************************************* + * ROS configs + ******************************************************************************/ + +/* RoS Peripherals */ +#define NRD_ROS_PERIPH_BASE UL(0x60000000) +#define NRD_ROS_PERIPH_SIZE UL(0x20000000) + +/* System Reg */ +#define NRD_ROS_SYSTEMREG_BASE UL(0x1C010000) +#define NRD_ROS_SYSTEMREG_SIZE UL(0x00010000) + +/* NOR Flash 2 */ +#define NRD_ROS_NOR2_FLASH_BASE UL(0x10000000) +#define NRD_ROS_NOR2_FLASH_SIZE UL(0x04000000) + +/* RoS Platform */ +#define NRD_ROS_PLATFORM_BASE UL(0x7F000000) +#define NRD_ROS_PLATFORM_SIZE UL(0x20000000) + +#endif /* NRD_ROS_DEF1_H */ diff --git a/plat/arm/board/neoverse_rd/common/include/nrd1/nrd_ros_fw_def1.h b/plat/arm/board/neoverse_rd/common/include/nrd1/nrd_ros_fw_def1.h new file mode 100644 index 00000000..c5210432 --- /dev/null +++ b/plat/arm/board/neoverse_rd/common/include/nrd1/nrd_ros_fw_def1.h @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2024, Arm Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + * + * This file is limited to include the RoS firmware specific definitions for the + * first generation platforms based on the A75, N1 and V1 CPUs. RoS (Rest Of + * System) is used to refer to the part of the reference design platform that + * excludes CSS. + */ + +#ifndef NRD_ROS_FW_DEF1_H +#define NRD_ROS_FW_DEF1_H + +#include <nrd_ros_def1.h> + +/******************************************************************************* + * MMU mapping + ******************************************************************************/ + +#define NRD_ROS_PERIPH_MMAP(n) \ + MAP_REGION_FLAT( \ + NRD_REMOTE_CHIP_MEM_OFFSET(n) + \ + NRD_ROS_PERIPH_BASE, \ + NRD_ROS_PERIPH_SIZE, \ + MT_DEVICE | MT_RW | MT_SECURE) + +#define NRD_ROS_SECURE_SYSTEMREG_USER_MMAP \ + MAP_REGION_FLAT( \ + NRD_ROS_SYSTEMREG_BASE, \ + NRD_ROS_SYSTEMREG_SIZE, \ + MT_DEVICE | MT_RW | MT_SECURE | MT_USER) + +#define NRD_ROS_SECURE_NOR2_USER_MMAP \ + MAP_REGION_FLAT( \ + NRD_ROS_NOR2_FLASH_BASE, \ + NRD_ROS_NOR2_FLASH_SIZE, \ + MT_DEVICE | MT_RW | MT_SECURE | MT_USER) + +#define NRD_MAP_FLASH0_RO \ + MAP_REGION_FLAT( \ + V2M_FLASH0_BASE, \ + V2M_FLASH0_SIZE, \ + MT_DEVICE | MT_RO | MT_SECURE) + +/******************************************************************************* + * TZ config + ******************************************************************************/ + +/* + * Mapping definition of the TrustZone Controller for Arm Neoverse RD platforms + * where both the DRAM regions are marked for non-secure access. This applies + * to multi-chip platforms. + */ +#define NRD_ROS_TZC_NS_REMOTE_REGIONS_DEF(n) \ + {NRD_REMOTE_CHIP_MEM_OFFSET(n) + ARM_DRAM1_BASE, \ + NRD_REMOTE_CHIP_MEM_OFFSET(n) + ARM_DRAM1_END, \ + ARM_TZC_NS_DRAM_S_ACCESS, PLAT_ARM_TZC_NS_DEV_ACCESS}, \ + {NRD_REMOTE_CHIP_MEM_OFFSET(n) + ARM_DRAM2_BASE, \ + NRD_REMOTE_CHIP_MEM_OFFSET(n) + ARM_DRAM2_END, \ + ARM_TZC_NS_DRAM_S_ACCESS, PLAT_ARM_TZC_NS_DEV_ACCESS} + +#endif /* NRD_ROS_FW_DEF1_H */ diff --git a/plat/arm/board/neoverse_rd/common/include/nrd2/nrd_css_def2.h b/plat/arm/board/neoverse_rd/common/include/nrd2/nrd_css_def2.h new file mode 100644 index 00000000..6cf57d4e --- /dev/null +++ b/plat/arm/board/neoverse_rd/common/include/nrd2/nrd_css_def2.h @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2024, Arm Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + * + * This file contains the CSS specific definitions for the second generation + * platforms based on the N2/V2 CPU. + */ + +#ifndef NRD_CSS_DEF2_H +#define NRD_CSS_DEF2_H + +/******************************************************************************* + * CSS memory map related defines + ******************************************************************************/ + +/* Boot ROM */ +#define NRD_CSS_SECURE_ROM_BASE UL(0x00000000) + +/* DRAM2 */ +#define NRD_CSS_DRAM2_BASE ULL(0x8080000000) + +/* NS SRAM */ +#define NRD_CSS_NS_SRAM_BASE UL(0x06000000) + +/* PL011 UART */ +#define NRD_CSS_SEC_UART_BASE UL(0x2A410000) +#define NRD_CSS_NSEC_UART_BASE UL(0x2A400000) +#define NRD_CSS_UART_SIZE UL(0x10000) + +/* General Peripherals */ +#define NRD_CSS_PERIPH_BASE UL(0x20000000) +#define NRD_CSS_PERIPH_SIZE UL(0x20000000) + +/* NS RAM Error record */ +#define NRD_CSS_NS_RAM_ERR_REC_BASE UL(0x2A4C0000) + +/*Secure Watchdog */ +#define NRD_CSS_SECURE_WDOG_BASE UL(0x2A480000) + +/* MHU */ +#define NRD_CSS_AP_SCP_S_MHU_BASE UL(0x2A920000) + +/* GIC */ +#define NRD_CSS_GIC_BASE UL(0x30000000) + +#endif /* NRD_CSS_DEF2_H */ diff --git a/plat/arm/board/neoverse_rd/common/include/nrd2/nrd_css_fw_def2.h b/plat/arm/board/neoverse_rd/common/include/nrd2/nrd_css_fw_def2.h new file mode 100644 index 00000000..481632bf --- /dev/null +++ b/plat/arm/board/neoverse_rd/common/include/nrd2/nrd_css_fw_def2.h @@ -0,0 +1,107 @@ +/* + * Copyright (c) 2024, Arm Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + * + * This file contains the CSS-firmware specific definitions for the second + * generation platforms based on the N2/V2 CPU. + */ + +#ifndef NRD_CSS_FW_DEF2_H +#define NRD_CSS_FW_DEF2_H + +#include <nrd_css_def2.h> + +/******************************************************************************* + * BL sizes + ******************************************************************************/ + +#define NRD_CSS_BL1_RW_SIZE UL(64 * 1024) /* 64KB */ + +#if TRUSTED_BOARD_BOOT +# define NRD_CSS_BL2_SIZE UL(0x20000) +#else +# define NRD_CSS_BL2_SIZE UL(0x14000) +#endif +/* + * Since BL31 NOBITS overlays BL2 and BL1-RW, PLAT_ARM_MAX_BL31_SIZE is + * calculated using the current BL31 PROGBITS debug size plus the sizes of BL2 + * and BL1-RW. NRD_BL31_SIZE - is tuned with respect to the actual BL31 + * PROGBITS size which is around 64-68KB at the time this change is being made. + * A buffer of ~35KB is added to account for future expansion of the image, + * making it a total of 100KB. + */ +#define NRD_CSS_BL31_SIZE UL(116 * 1024) /* 116 KB */ + +/******************************************************************************* + * Console config + ******************************************************************************/ + +#define NRD_CSS_UART_CLK_IN_HZ UL(7372800) + +/******************************************************************************* + * Watchdog config + ******************************************************************************/ + +#define NRD_CSS_SECURE_WDOG_TIMEOUT UL(100) + +/******************************************************************************* + * RAS config + ******************************************************************************/ + +#define NRD_CSS_NS_RAM_ECC_CE_INT U(87) +#define NRD_CSS_NS_RAM_ECC_UE_INT U(88) + +#if (SPM_MM || (SPMC_AT_EL3 && SPMC_AT_EL3_SEL0_SP)) \ + && ENABLE_FEAT_RAS && FFH_SUPPORT +/* + * CPER buffer memory of 128KB is reserved and it is placed adjacent to the + * memory shared between EL3 and S-EL0. + */ +#define NRD_CSS_SP_CPER_BUF_BASE (PLAT_SP_IMAGE_NS_BUF_BASE + \ + PLAT_SP_IMAGE_NS_BUF_SIZE) +#define NRD_CSS_SP_CPER_BUF_SIZE UL(0x10000) +#endif /* SPM_MM && ENABLE_FEAT_RAS && FFH_SUPPORT */ + +/******************************************************************************* + * MMU mapping + ******************************************************************************/ + +#define NRD_CSS_SHARED_RAM_MMAP(n) \ + MAP_REGION_FLAT( \ + NRD_REMOTE_CHIP_MEM_OFFSET(n) + \ + ARM_SHARED_RAM_BASE, \ + ARM_SHARED_RAM_SIZE, \ + MT_NON_CACHEABLE | MT_RW | MT_SECURE) + +#define NRD_CSS_PERIPH_MMAP(n) \ + MAP_REGION_FLAT( \ + NRD_REMOTE_CHIP_MEM_OFFSET(n) + \ + NRD_CSS_PERIPH_BASE, \ + NRD_CSS_PERIPH_SIZE, \ + MT_DEVICE | MT_RW | MT_SECURE) + +#if (SPM_MM || (SPMC_AT_EL3 && SPMC_AT_EL3_SEL0_SP)) && \ +ENABLE_FEAT_RAS && FFH_SUPPORT +/* + * CPER buffer memory of 128KB is reserved and it is placed adjacent to the + * memory shared between EL3 and S-EL0. + */ +#define NRD_CSS_SP_CPER_BUF_MMAP \ + MAP_REGION2( \ + NRD_CSS_SP_CPER_BUF_BASE, \ + NRD_CSS_SP_CPER_BUF_BASE, \ + NRD_CSS_SP_CPER_BUF_SIZE, \ + MT_RW_DATA | MT_NS | MT_USER, \ + PAGE_SIZE) +#endif + +#if SPM_MM +#define NRD_CSS_SECURE_UART_USER_MMAP \ + MAP_REGION_FLAT( \ + NRD_CSS_SEC_UART_BASE, \ + NRD_CSS_UART_SIZE, \ + MT_DEVICE | MT_RW | MT_SECURE | MT_USER) +#endif + +#endif /* NRD_CSS_FW_DEF2_H */ diff --git a/plat/arm/board/neoverse_rd/common/include/nrd2/nrd_plat_arm_def2.h b/plat/arm/board/neoverse_rd/common/include/nrd2/nrd_plat_arm_def2.h new file mode 100644 index 00000000..3ee413f6 --- /dev/null +++ b/plat/arm/board/neoverse_rd/common/include/nrd2/nrd_plat_arm_def2.h @@ -0,0 +1,366 @@ +/* + * Copyright (c) 2024, Arm Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + * + * This file is limited to include the trusted firmware required platform port + * definitions for the second generation platforms based on the N2/V2 CPUs. The + * common platform support for Arm platforms expect platforms to define certain + * definitions and those definitions are referred to as the platform port + * definitions. + */ + +#ifndef NRD_PLAT_ARM_DEF2_H +#define NRD_PLAT_ARM_DEF2_H + +#ifndef __ASSEMBLER__ +#include <lib/mmio.h> +#endif /* __ASSEMBLER__ */ + +#include <plat/arm/common/arm_def.h> +#include <plat/arm/common/arm_spm_def.h> +#include <plat/arm/css/common/css_def.h> +#include <nrd_css_fw_def2.h> +#include <nrd_ros_fw_def2.h> + +/******************************************************************************* + * Core count + ******************************************************************************/ + +#define PLATFORM_CORE_COUNT (NRD_CHIP_COUNT * \ + PLAT_ARM_CLUSTER_COUNT * \ + NRD_MAX_CPUS_PER_CLUSTER * \ + NRD_MAX_PE_PER_CPU) + +#if (NRD_PLATFORM_VARIANT == 1) +#define PLAT_ARM_CLUSTER_COUNT U(8) +#elif (NRD_PLATFORM_VARIANT == 2) +#define PLAT_ARM_CLUSTER_COUNT U(4) +#else +#define PLAT_ARM_CLUSTER_COUNT U(16) +#endif + +/******************************************************************************* + * PA/VA config + ******************************************************************************/ + +#ifdef __aarch64__ +#define PLAT_PHY_ADDR_SPACE_SIZE NRD_REMOTE_CHIP_MEM_OFFSET( \ + NRD_CHIP_COUNT) +#define PLAT_VIRT_ADDR_SPACE_SIZE NRD_REMOTE_CHIP_MEM_OFFSET( \ + NRD_CHIP_COUNT) +#else +#define PLAT_PHY_ADDR_SPACE_SIZE (1ULL << 32) +#define PLAT_VIRT_ADDR_SPACE_SIZE (1ULL << 32) +#endif + +/******************************************************************************* + * XLAT definitions + ******************************************************************************/ + +#if defined(IMAGE_BL31) +# if SPM_MM || (SPMC_AT_EL3 && SPMC_AT_EL3_SEL0_SP) +# define PLAT_ARM_MMAP_ENTRIES (10 + ((NRD_CHIP_COUNT - 1) * 3)) +# define MAX_XLAT_TABLES (8 + ((NRD_CHIP_COUNT - 1) * 3)) +# define PLAT_SP_IMAGE_MMAP_REGIONS U(12) +# define PLAT_SP_IMAGE_MAX_XLAT_TABLES U(14) +# else +# define PLAT_ARM_MMAP_ENTRIES (5 + ((NRD_CHIP_COUNT - 1) * 3)) +# define MAX_XLAT_TABLES (6 + ((NRD_CHIP_COUNT - 1) * 3)) +# endif +#elif defined(IMAGE_BL32) +# define PLAT_ARM_MMAP_ENTRIES U(8) +# define MAX_XLAT_TABLES U(5) +#elif defined(IMAGE_BL2) +# define PLAT_ARM_MMAP_ENTRIES (11 + (NRD_CHIP_COUNT - 1)) + +/* + * MAX_XLAT_TABLES entries need to be doubled because when the address width + * exceeds 40 bits an additional level of translation is required. In case of + * multichip platforms peripherals also fall into address space with width + * > 40 bits + * + */ +# define MAX_XLAT_TABLES (7 + ((NRD_CHIP_COUNT - 1) * 2)) +#elif !USE_ROMLIB +# define PLAT_ARM_MMAP_ENTRIES U(11) +# define MAX_XLAT_TABLES U(7) +#else +# define PLAT_ARM_MMAP_ENTRIES U(12) +# define MAX_XLAT_TABLES U(6) +#endif + +/******************************************************************************* + * BL sizes + ******************************************************************************/ + +/* + * PLAT_ARM_MAX_ROMLIB_RW_SIZE is define to use a full page + */ + +#if USE_ROMLIB +#define PLAT_ARM_MAX_ROMLIB_RW_SIZE UL(0x1000) +#define PLAT_ARM_MAX_ROMLIB_RO_SIZE UL(0xe000) +#else +#define PLAT_ARM_MAX_ROMLIB_RW_SIZE UL(0) +#define PLAT_ARM_MAX_ROMLIB_RO_SIZE UL(0) +#endif + +#define PLAT_ARM_MAX_BL1_RW_SIZE NRD_CSS_BL1_RW_SIZE + +/* + * PLAT_ARM_MAX_BL2_SIZE is calculated using the current BL2 debug size plus a + * little space for growth. Additional 8KiB space is added per chip in + * order to accommodate the additional level of translation required for "TZC" + * peripheral access which lies in >4TB address space. + * + */ +#define PLAT_ARM_MAX_BL2_SIZE (NRD_CSS_BL2_SIZE + \ + ((NRD_CHIP_COUNT - 1) * 0x2000)) + +#define PLAT_ARM_MAX_BL31_SIZE (NRD_CSS_BL31_SIZE + \ + PLAT_ARM_MAX_BL2_SIZE + \ + PLAT_ARM_MAX_BL1_RW_SIZE) + +/******************************************************************************* + * Stack sizes + ******************************************************************************/ + +#if defined(IMAGE_BL1) +# if TRUSTED_BOARD_BOOT +# define PLATFORM_STACK_SIZE UL(0x1000) +# else +# define PLATFORM_STACK_SIZE UL(0x440) +# endif +#elif defined(IMAGE_BL2) +# if TRUSTED_BOARD_BOOT +# define PLATFORM_STACK_SIZE UL(0x1000) +# else +# define PLATFORM_STACK_SIZE UL(0x400) +# endif +#elif defined(IMAGE_BL2U) +# define PLATFORM_STACK_SIZE UL(0x400) +#elif defined(IMAGE_BL31) +# if SPM_MM +# define PLATFORM_STACK_SIZE UL(0x500) +# else +# define PLATFORM_STACK_SIZE UL(0x400) +# endif +#elif defined(IMAGE_BL32) +# define PLATFORM_STACK_SIZE UL(0x440) +#endif + +#if (SPM_MM || (SPMC_AT_EL3 && SPMC_AT_EL3_SEL0_SP)) && \ +ENABLE_FEAT_RAS && FFH_SUPPORT +/* + * Secure partition stack follows right after the memory space reserved for + * CPER buffer memory. + */ +#define PLAT_ARM_SP_IMAGE_STACK_BASE (PLAT_SP_IMAGE_NS_BUF_BASE + \ + PLAT_SP_IMAGE_NS_BUF_SIZE + \ + NRD_CSS_SP_CPER_BUF_SIZE) +#elif (SPM_MM || (SPMC_AT_EL3 && SPMC_AT_EL3_SEL0_SP)) +/* + * Secure partition stack follows right after the memory region that is shared + * between EL3 and S-EL0. + */ +#define PLAT_ARM_SP_IMAGE_STACK_BASE (PLAT_SP_IMAGE_NS_BUF_BASE + \ + PLAT_SP_IMAGE_NS_BUF_SIZE) +#endif /* SPM_MM && ENABLE_FEAT_RAS && FFH_SUPPORT */ + +/******************************************************************************* + * Console config + ******************************************************************************/ + +#define PLAT_ARM_BOOT_UART_BASE NRD_CSS_SEC_UART_BASE +#define PLAT_ARM_BOOT_UART_CLK_IN_HZ NRD_CSS_UART_CLK_IN_HZ + +#define PLAT_ARM_RUN_UART_BASE NRD_CSS_SEC_UART_BASE +#define PLAT_ARM_RUN_UART_CLK_IN_HZ NRD_CSS_UART_CLK_IN_HZ + +#define PLAT_ARM_CRASH_UART_BASE NRD_CSS_SEC_UART_BASE +#define PLAT_ARM_CRASH_UART_CLK_IN_HZ NRD_CSS_UART_CLK_IN_HZ + +/******************************************************************************* + * SCMI config + ******************************************************************************/ + +/* Number of SCMI channels on the platform */ +#define PLAT_ARM_SCMI_CHANNEL_COUNT NRD_CHIP_COUNT + +/******************************************************************************* + * ROM, SRAM and DRAM config + ******************************************************************************/ + +#define PLAT_ARM_TRUSTED_ROM_BASE NRD_CSS_SECURE_ROM_BASE +#define PLAT_ARM_TRUSTED_ROM_SIZE NRD_CSS_SECURE_ROM_SIZE + +#define PLAT_ARM_DRAM2_BASE NRD_CSS_DRAM2_BASE +#define PLAT_ARM_DRAM2_SIZE NRD_CSS_DRAM2_SIZE + +#define PLAT_ARM_TRUSTED_SRAM_SIZE NRD_CSS_SECURE_SRAM_SIZE + +#define PLAT_ARM_NSTIMER_FRAME_ID 0 + +#define PLAT_ARM_NSRAM_BASE NRD_CSS_NS_SRAM_BASE +#define PLAT_ARM_NSRAM_SIZE NRD_CSS_NS_SRAM_SIZE +/* + * Required platform porting definitions common to all ARM CSS SoCs + */ +/* 2MB used for SCP DDR retraining */ +#define PLAT_ARM_SCP_TZC_DRAM1_SIZE UL(0x00200000) + +/******************************************************************************* + * GIC/EHF config + ******************************************************************************/ + +/* GIC related constants */ +#define PLAT_ARM_GICD_BASE NRD_CSS_GIC_BASE + +#if (NRD_PLATFORM_VARIANT == 1) +#define PLAT_ARM_GICR_BASE NRD_CSS_GIC_BASE + UL(0x00100000) +#elif (NRD_PLATFORM_VARIANT == 3) +#define PLAT_ARM_GICR_BASE NRD_CSS_GIC_BASE + UL(0x00300000) +#else +#define PLAT_ARM_GICR_BASE NRD_CSS_GIC_BASE + UL(0x001C0000) +#endif + +#define PLAT_ARM_G1S_IRQ_PROPS(grp) CSS_G1S_IRQ_PROPS(grp) +#define PLAT_ARM_G0_IRQ_PROPS(grp) ARM_G0_IRQ_PROPS(grp) + +#if ENABLE_FEAT_RAS && FFH_SUPPORT +#define PLAT_SP_PRI PLAT_RAS_PRI +#else +#define PLAT_SP_PRI (0x10) +#endif + +/* Interrupt priority level for shutdown/reboot */ +#define PLAT_REBOOT_PRI GIC_HIGHEST_SEC_PRIORITY +#define PLAT_EHF_DESC EHF_PRI_DESC(PLAT_PRI_BITS, PLAT_REBOOT_PRI) + +/******************************************************************************* + * Secure world config + ******************************************************************************/ + +#define SECURE_PARTITION_COUNT 1 +#define NS_PARTITION_COUNT 1 +#define MAX_EL3_LP_DESCS_COUNT 1 + +/******************************************************************************* + * MHU config + ******************************************************************************/ + +#define PLAT_CSS_MHU_BASE NRD_CSS_AP_SCP_S_MHU_BASE +#define PLAT_MHUV2_BASE PLAT_CSS_MHU_BASE + +/******************************************************************************* + * Power config + ******************************************************************************/ + +#define CSS_SYSTEM_PWR_DMN_LVL ARM_PWR_LVL2 +#define PLAT_MAX_PWR_LVL ARM_PWR_LVL1 + +/******************************************************************************* + * TZ config + ******************************************************************************/ + +#define PLAT_ARM_TZC_BASE NRD_ROS_MEMCNTRL_BASE + UL(0x00720000) +#define PLAT_ARM_TZC_FILTERS TZC_400_REGION_ATTR_FILTER_BIT(0) + +/******************************************************************************* + * SDS config + ******************************************************************************/ + +/* SDS ID for unusable CPU MPID list structure */ +#define SDS_ISOLATED_CPU_LIST_ID U(128) + +/* Index of SDS region used in the communication with SCP */ +#define SDS_SCP_AP_REGION_ID U(0) + +/******************************************************************************* + * Flash config + ******************************************************************************/ + +#define MAX_IO_DEVICES U(3) +#define MAX_IO_HANDLES U(4) + +#define V2M_SYS_LED U(0x8) + +#define V2M_SYS_LED_SS_SHIFT U(0) +#define V2M_SYS_LED_EL_SHIFT U(1) +#define V2M_SYS_LED_EC_SHIFT U(3) + +#define V2M_SYS_LED_SS_MASK U(0x01) +#define V2M_SYS_LED_EL_MASK U(0x03) +#define V2M_SYS_LED_EC_MASK U(0x1f) + +#define PLAT_ARM_MEM_PROTEC_VA_FRAME UL(0xC0000000) + +#define V2M_SYSREGS_BASE NRD_ROS_SYSTEM_PERIPH_BASE + \ + UL(0x00010000) +#define V2M_FLASH0_BASE NRD_ROS_SMC0_BASE +#define V2M_FLASH0_SIZE NRD_ROS_SMC0_SIZE +#define V2M_FLASH_BLOCK_SIZE UL(0x00040000) /* 256 KB */ + +#define PLAT_ARM_FLASH_IMAGE_BASE V2M_FLASH0_BASE +#define PLAT_ARM_FLASH_IMAGE_MAX_SIZE (V2M_FLASH0_SIZE - V2M_FLASH_BLOCK_SIZE) + +#define PLAT_ARM_MEM_PROT_ADDR (V2M_FLASH0_BASE + \ + V2M_FLASH0_SIZE - V2M_FLASH_BLOCK_SIZE) + +#define PLAT_ARM_NVM_BASE V2M_FLASH0_BASE +#define PLAT_ARM_NVM_SIZE (V2M_FLASH0_SIZE - V2M_FLASH_BLOCK_SIZE) + +/******************************************************************************* + * Platform type identification macro + ******************************************************************************/ + +/* Platform ID related accessors */ +#define BOARD_CSS_PLAT_ID_REG_ID_MASK U(0x0F) +#define BOARD_CSS_PLAT_ID_REG_ID_SHIFT U(0x00) +#define BOARD_CSS_PLAT_ID_REG_VERSION_MASK U(0xF00) +#define BOARD_CSS_PLAT_ID_REG_VERSION_SHIFT U(0x08) +#define BOARD_CSS_PLAT_TYPE_RTL U(0x00) +#define BOARD_CSS_PLAT_TYPE_FPGA U(0x01) +#define BOARD_CSS_PLAT_TYPE_EMULATOR U(0x02) +#define BOARD_CSS_PLAT_TYPE_FVP U(0x03) + +#ifndef __ASSEMBLER__ +#define BOARD_CSS_GET_PLAT_TYPE(addr) \ + ((mmio_read_32(addr) & BOARD_CSS_PLAT_ID_REG_ID_MASK) \ + >> BOARD_CSS_PLAT_ID_REG_ID_SHIFT) +#endif /* __ASSEMBLER__ */ + +/* Platform ID address */ +#define BOARD_CSS_PLAT_ID_REG_ADDR NRD_ROS_PLATFORM_PERIPH_BASE + \ + UL(0x00FE00E0) + +/******************************************************************************* + * ROS peripheral config + ******************************************************************************/ + +/* Non-volatile counters */ +#define SOC_TRUSTED_NVCTR_BASE NRD_ROS_PLATFORM_PERIPH_BASE + \ + UL(0x00E70000) +#define TFW_NVCTR_BASE (SOC_TRUSTED_NVCTR_BASE + 0x0000) +#define TFW_NVCTR_SIZE U(4) +#define NTFW_CTR_BASE (SOC_TRUSTED_NVCTR_BASE + 0x0004) +#define NTFW_CTR_SIZE U(4) + +/******************************************************************************* + * MMU config + ******************************************************************************/ + +#define V2M_MAP_FLASH0_RW \ + MAP_REGION_FLAT( \ + V2M_FLASH0_BASE, \ + V2M_FLASH0_SIZE, \ + MT_DEVICE | MT_RW | MT_SECURE) + +#define V2M_MAP_FLASH0_RO \ + MAP_REGION_FLAT( \ + V2M_FLASH0_BASE, \ + V2M_FLASH0_SIZE, \ + MT_RO_DATA | MT_SECURE) + +#endif /* NRD_PLAT_ARM_DEF2_H */ diff --git a/plat/arm/board/neoverse_rd/common/include/nrd2/nrd_ros_def2.h b/plat/arm/board/neoverse_rd/common/include/nrd2/nrd_ros_def2.h new file mode 100644 index 00000000..3558cfc6 --- /dev/null +++ b/plat/arm/board/neoverse_rd/common/include/nrd2/nrd_ros_def2.h @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2024, Arm Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + * + * This file contains the RoS specific definitions for the second generation + * platforms based on the N2/V2 CPU. + */ + +#ifndef NRD_ROS_DEF2_H +#define NRD_ROS_DEF2_H + +/******************************************************************************* + * SoC memory map related defines + ******************************************************************************/ + +/* System Reg */ +#define NRD_ROS_SYSTEMREG_BASE UL(0x0C010000) +#define NRD_ROS_SYSTEMREG_SIZE UL(0x00010000) + +/* NOR flash 2 */ +#define NRD_ROS_NOR2_FLASH_BASE ULL(0x001054000000) +#define NRD_ROS_NOR2_FLASH_SIZE UL(0x000004000000) + +/* Memory controller */ +#define NRD_ROS_MEMCNTRL_BASE UL(0x10000000) +#define NRD_ROS_MEMCNTRL_SIZE UL(0x10000000) + +/* System peripherals */ +#define NRD_ROS_SYSTEM_PERIPH_BASE UL(0x0C000000) +#define NRD_ROS_SYSTEM_PERIPH_SIZE UL(0x02000000) + +/* Platform peripherals */ +#define NRD_ROS_PLATFORM_PERIPH_BASE UL(0x0E000000) +#define NRD_ROS_PLATFORM_PERIPH_SIZE UL(0x02000000) + +/* SMC0 */ +#define NRD_ROS_SMC0_BASE UL(0x08000000) +#define NRD_ROS_SMC0_SIZE UL(0x04000000) + +#endif /* NRD_ROS_DEF2_H */ diff --git a/plat/arm/board/neoverse_rd/common/include/nrd2/nrd_ros_fw_def2.h b/plat/arm/board/neoverse_rd/common/include/nrd2/nrd_ros_fw_def2.h new file mode 100644 index 00000000..60916729 --- /dev/null +++ b/plat/arm/board/neoverse_rd/common/include/nrd2/nrd_ros_fw_def2.h @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2024, Arm Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + * + * This file is limited to include the RoS firmware specific definitions for the + * second generation platforms based on the N2/V2 CPUs. RoS (Rest Of System) is + * used to refer to the part of the reference design platform that excludes CSS. + */ + +#ifndef NRD_ROS_FW_DEF2_H +#define NRD_ROS_FW_DEF2_H + +#include <nrd_ros_def2.h> + +/******************************************************************************* + * MMU mapping + ******************************************************************************/ + +#define NRD_ROS_PLATFORM_PERIPH_MMAP \ + MAP_REGION_FLAT( \ + NRD_ROS_PLATFORM_PERIPH_BASE, \ + NRD_ROS_PLATFORM_PERIPH_SIZE, \ + MT_DEVICE | MT_RW | MT_SECURE) + +#if SPM_MM + +#define NRD_ROS_PLATFORM_PERIPH_USER_MMAP \ + MAP_REGION_FLAT( \ + NRD_ROS_PLATFORM_PERIPH_BASE, \ + NRD_ROS_PLATFORM_PERIPH_SIZE, \ + MT_DEVICE | MT_RW | MT_SECURE | MT_USER) +#endif + +#define NRD_ROS_SYSTEM_PERIPH_MMAP \ + MAP_REGION_FLAT( \ + NRD_ROS_SYSTEM_PERIPH_BASE, \ + NRD_ROS_SYSTEM_PERIPH_SIZE, \ + MT_DEVICE | MT_RW | MT_SECURE) + +#define NRD_ROS_MEMCNTRL_MMAP(n) \ + MAP_REGION_FLAT( \ + NRD_REMOTE_CHIP_MEM_OFFSET(n) + \ + NRD_ROS_MEMCNTRL_BASE, \ + NRD_ROS_MEMCNTRL_SIZE, \ + MT_DEVICE | MT_RW | MT_SECURE) + +#define NRD_ROS_SECURE_SYSTEMREG_USER_MMAP \ + MAP_REGION_FLAT( \ + NRD_ROS_SYSTEMREG_BASE, \ + NRD_ROS_SYSTEMREG_SIZE, \ + MT_DEVICE | MT_RW | MT_SECURE | MT_USER) + +#define NRD_ROS_SECURE_NOR2_USER_MMAP \ + MAP_REGION_FLAT( \ + NRD_ROS_NOR2_FLASH_BASE, \ + NRD_ROS_NOR2_FLASH_SIZE, \ + MT_DEVICE | MT_RW | MT_SECURE | MT_USER) + + +#define NRD_ROS_FLASH0_RO_MMAP \ + MAP_REGION_FLAT( \ + V2M_FLASH0_BASE, \ + V2M_FLASH0_SIZE, \ + MT_DEVICE | MT_RO | MT_SECURE) + +/******************************************************************************* + * TZ config + ******************************************************************************/ + +/* + * Mapping definition of the TrustZone Controller for Arm Neoverse RD platforms + * where both the DRAM regions are marked for non-secure access. This applies + * to multi-chip platforms. + */ +#define NRD_ROS_TZC_NS_REMOTE_REGIONS_DEF(n) \ + {NRD_REMOTE_CHIP_MEM_OFFSET(n) + ARM_DRAM1_BASE, \ + NRD_REMOTE_CHIP_MEM_OFFSET(n) + ARM_DRAM1_END, \ + ARM_TZC_NS_DRAM_S_ACCESS, PLAT_ARM_TZC_NS_DEV_ACCESS}, \ + {NRD_REMOTE_CHIP_MEM_OFFSET(n) + ARM_DRAM2_BASE, \ + NRD_REMOTE_CHIP_MEM_OFFSET(n) + ARM_DRAM2_END, \ + ARM_TZC_NS_DRAM_S_ACCESS, PLAT_ARM_TZC_NS_DEV_ACCESS} + +#endif /* NRD_ROS_FW_DEF2_H */ diff --git a/plat/arm/board/neoverse_rd/common/include/nrd3/nrd_css_def3.h b/plat/arm/board/neoverse_rd/common/include/nrd3/nrd_css_def3.h new file mode 100644 index 00000000..bd2e682a --- /dev/null +++ b/plat/arm/board/neoverse_rd/common/include/nrd3/nrd_css_def3.h @@ -0,0 +1,268 @@ +/* + * Copyright (c) 2024, Arm Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + * + * This file contains the CSS specific definitions for the third generation of + * platforms. + */ + +#ifndef NRD_CSS_DEF3_H +#define NRD_CSS_DEF3_H + +/******************************************************************************* + * CSS memory map related defines + ******************************************************************************/ + +/* Shared RAM */ +#define NRD_CSS_SHARED_SRAM_BASE UL(0x00000000) + +/* General Peripherals */ +#define NRD_CSS_PERIPH_BASE UL(0x20000000) +#define NRD_CSS_PERIPH_SIZE UL(0x20000000) + +/* System NCI */ +#define NRD_CSS_SYSTEM_NCI_BASE UL(0x20000000) +#define NRD_CSS_SYSTEM_NCI_SIZE UL(0x04000000) + +/* Debug NIC */ +#define NRD_CSS_DEBUG_NIC_BASE UL(0x28000000) +#define NRD_CSS_DEBUG_NIC_SIZE UL(0x01000000) + +/* NS UART */ +#define NRD_CSS_NS_UART_BASE UL(0x2A400000) +#define NRD_CSS_NS_UART_SIZE UL(0x00010000) + +/* Secure UART */ +#define NRD_CSS_SECURE_UART_BASE UL(0x2A410000) +#define NRD_CSS_SECURE_UART_SIZE UL(0x00010000) + +/* Realm UART */ +#define NRD_CSS_REALM_UART_BASE UL(0x2A420000) +#define NRD_CSS_REALM_UART_SIZE UL(0x00010000) + +/* Generic Refclk */ +#define NRD_CSS_GENERIC_REFCLK_BASE UL(0x2A430000) +#define NRD_CSS_GENERIC_REFCLK_SIZE UL(0x00010000) + +/* NS Watchdog */ +#define NRD_CSS_AP_NS_WDOG_BASE UL(0x2A440000) +#define NRD_CSS_AP_NS_WDOG_SIZE UL(0x00020000) + +/* Root Watchdog */ +#define NRD_CSS_AP_ROOT_WDOG_BASE UL(0x2A460000) +#define NRD_CSS_AP_ROOT_WDOG_SIZE UL(0x00020000) + +/* Secure Watchdog */ +#define NRD_CSS_AP_SECURE_WDOG_BASE UL(0x2A480000) +#define NRD_CSS_AP_SECURE_WDOG_SIZE UL(0x00020000) + +/* SID */ +#define NRD_CSS_SID_BASE UL(0x2A4A0000) +#define NRD_CSS_SID_SIZE UL(0x00010000) + +/* SRAM Secure Error Record Block - AP */ +#define NRD_CSS_SECURE_SRAM_ERB_AP_BASE UL(0x2A4B0000) +#define NRD_CSS_SECURE_SRAM_ERB_AP_SIZE UL(0x00010000) + +/* SRAM NS Error Record Block - AP */ +#define NRD_CSS_NS_SRAM_ERB_AP_BASE UL(0x2A4C0000) +#define NRD_CSS_NS_SRAM_ERB_AP_SIZE UL(0x00010000) + +/* SRAM Root Error Record Block - AP */ +#define NRD_CSS_ROOT_SRAM_ERB_AP_BASE UL(0x2A4D0000) +#define NRD_CSS_ROOT_SRAM_ERB_AP_SIZE UL(0x00010000) + +/* SRAM Realm Error Record Block - AP */ +#define NRD_CSS_REALM_SRAM_ERB_AP_BASE UL(0x2A4E0000) +#define NRD_CSS_REALM_SRAM_ERB_AP_SIZE UL(0x00010000) + +/* SRAM Secure Error Record Block - SCP */ +#define NRD_CSS_SECURE_SRAM_ERB_SCP_BASE UL(0x2A4F0000) +#define NRD_CSS_SECURE_SRAM_ERB_SCP_SIZE UL(0x00010000) + +/* SRAM NS Error Record Block - SCP */ +#define NRD_CSS_NS_SRAM_ERB_SCP_BASE UL(0x2A500000) +#define NRD_CSS_NS_SRAM_ERB_SCP_SIZE UL(0x00010000) + +/* SRAM Root Error Record Block - SCP */ +#define NRD_CSS_ROOT_SRAM_ERB_SCP_BASE UL(0x2A510000) +#define NRD_CSS_ROOT_SRAM_ERB_SCP_SIZE UL(0x00010000) + +/* SRAM Realm Error Record Block - SCP */ +#define NRD_CSS_REALM_SRAM_ERB_SCP_BASE UL(0x2A520000) +#define NRD_CSS_REALM_SRAM_ERB_SCP_SIZE UL(0x00010000) + +/* SRAM Secure Error Record Block - MCP */ +#define NRD_CSS_SECURE_SRAM_ERB_MCP_BASE UL(0x2A530000) +#define NRD_CSS_SECURE_SRAM_ERB_MCP_SIZE UL(0x00010000) + +/* SRAM NS Error Record Block - MCP */ +#define NRD_CSS_NS_SRAM_ERB_MCP_BASE UL(0x2A540000) +#define NRD_CSS_NS_SRAM_ERB_MCP_SIZE UL(0x00010000) + +/* SRAM Root Error Record Block - MCP */ +#define NRD_CSS_ROOT_SRAM_ERB_MCP_BASE UL(0x2A550000) +#define NRD_CSS_ROOT_SRAM_ERB_MCP_SIZE UL(0x00010000) + +/* SRAM Realm Error Record Block - MCP */ +#define NRD_CSS_REALM_SRAM_ERB_MCP_BASE UL(0x2A560000) +#define NRD_CSS_REALM_SRAM_ERB_MCP_SIZE UL(0x00010000) + +/* SRAM Secure Error Record Block - RSE */ +#define NRD_CSS_SECURE_SRAM_ERB_RSE_BASE UL(0x2A570000) +#define NRD_CSS_SECURE_SRAM_ERB_RSE_SIZE UL(0x00010000) + +/* SRAM NS Error Record Block - RSE */ +#define NRD_CSS_NS_SRAM_ERB_RSE_BASE UL(0x2A580000) +#define NRD_CSS_NS_SRAM_ERB_RSE_SIZE UL(0x00010000) + +/* SRAM Root Error Record Block - RSE */ +#define NRD_CSS_ROOT_SRAM_ERB_RSE_BASE UL(0x2A590000) +#define NRD_CSS_ROOT_SRAM_ERB_RSE_SIZE UL(0x00010000) + +/* SRAM Realm Error Record Block - RSE */ +#define NRD_CSS_REALM_SRAM_ERB_RSE_BASE UL(0x2A5A0000) +#define NRD_CSS_REALM_SRAM_ERB_RSE_SIZE UL(0x00010000) + +/* RSE SRAM Secure Error Record Block - RSM */ +#define NRD_CSS_RSE_SECURE_SRAM_ERB_RSM_BASE UL(0x2A5B0000) +#define NRD_CSS_RSE_SECURE_SRAM_ERB_RSM_SIZE UL(0x00010000) + +/* RSE SRAM Secure Error Record Block - RSM */ +#define NRD_CSS_RSE_NS_SRAM_ERB_RSM_BASE UL(0x2A5C0000) +#define NRD_CSS_RSE_NS_SRAM_ERB_RSM_SIZE UL(0x00010000) + +/* SCP SRAM Secure Error Record Block - RSM */ +#define NRD_CSS_SCP_SECURE_SRAM_ERB_RSM_BASE UL(0x2A5D0000) +#define NRD_CSS_SCP_SECURE_SRAM_ERB_RSM_SIZE UL(0x00010000) + +/* SCP SRAM NS Error Record Block - RSM */ +#define NRD_CSS_SCP_NS_SRAM_ERB_RSM_BASE UL(0x2A5E0000) +#define NRD_CSS_SCP_NS_SRAM_ERB_RSM_SIZE UL(0x00010000) + +/* MCP SRAM Secure Error Record Block - RSM */ +#define NRD_CSS_MCP_SECURE_SRAM_ERB_RSM_BASE UL(0x2A5F0000) +#define NRD_CSS_MCP_SECURE_SRAM_ERB_RSM_SIZE UL(0x00010000) + +/* MCP SRAM NS Error Record Block - RSM */ +#define NRD_CSS_MCP_NS_SRAM_ERB_RSM_BASE UL(0x2A600000) +#define NRD_CSS_MCP_NS_SRAM_ERB_RSM_SIZE UL(0x00010000) + +/* CNTCTL Refclk Readframe */ +#define NRD_CSS_CNTCTL_REFCLK_READFRAME_BASE UL(0x2A800000) +#define NRD_CSS_CNTCTL_REFCLK_READFRAME_SIZE UL(0x00020000) + +/* CNTCTL base */ +#define NRD_CSS_SYS_TIMCTL_BASE UL(0x2A810000) + +/* Secure Timer */ +#define NRD_CSS_SECURE_TIMER_CTL_BASE UL(0x2A820000) +#define NRD_CSS_SECURE_TIMER_CTL_SIZE UL(0x00010000) + +/* NS Timer */ +#define NRD_CSS_NS_TIMER_CTL_BASE UL(0x2A830000) +#define NRD_CSS_NS_TIMER_CTL_SIZE UL(0x00010000) + +/* AP - SCP NS MHU */ +#define NRD_CSS_AP_SCP_NS_MHU_BASE UL(0x2A900000) +#define NRD_CSS_AP_SCP_NS_MHU_SIZE UL(0x00020000) + +/* AP - SCP Secure MHU */ +#define NRD_CSS_AP_SCP_SECURE_MHU_BASE UL(0x2A920000) +#define NRD_CSS_AP_SCP_SECURE_MHU_SIZE UL(0x00020000) + +/* AP - SCP Root MHU */ +#define NRD_CSS_AP_SCP_ROOT_MHU_BASE UL(0x2A940000) +#define NRD_CSS_AP_SCP_ROOT_MHU_SIZE UL(0x00020000) + +/* AP - MCP NS MHU */ +#define NRD_CSS_AP_MCP_NS_MHU_BASE UL(0x2AA00000) +#define NRD_CSS_AP_MCP_NS_MHU_SIZE UL(0x00020000) + +/* AP - MCP Secure MHU */ +#define NRD_CSS_AP_MCP_SECURE_MHU_BASE UL(0x2AA20000) +#define NRD_CSS_AP_MCP_SECURE_MHU_SIZE UL(0x00020000) + +/* AP - MCP Root MHU */ +#define NRD_CSS_AP_MCP_ROOT_MHU_BASE UL(0x2AA40000) +#define NRD_CSS_AP_MCP_ROOT_MHU_SIZE UL(0x00020000) + +/* AP - RSE NS MHU */ +#define NRD_CSS_AP_RSE_NS_MHU_BASE UL(0x2AB00000) +#define NRD_CSS_AP_RSE_NS_MHU_SIZE UL(0x00020000) + +/* AP - RSE Secure MHU */ +#define NRD_CSS_AP_RSE_SECURE_MHU_BASE UL(0x2AB20000) +#define NRD_CSS_AP_RSE_SECURE_MHU_SIZE UL(0x00020000) + +/* AP - RSE Root MHU */ +#define NRD_CSS_AP_RSE_ROOT_MHU_BASE UL(0x2AB40000) +#define NRD_CSS_AP_RSE_ROOT_MHU_SIZE UL(0x00020000) + +/* AP - RSE Realm MHU */ +#define NRD_CSS_AP_RSE_REALM_MHU_BASE UL(0x2AB60000) +#define NRD_CSS_AP_RSE_REALM_MHU_SIZE UL(0x00020000) + +/* SCP - MCP - RSE Cross chip MHU */ +#define NRD_CSS_SCP_MCP_RSE_CROSS_CHIP_MHU_BASE UL(0x2AC00000) +#define NRD_CSS_SCP_MCP_RSE_CROSS_CHIP_MHU_SIZE UL(0x00120000) + +/* Synchronization Master Tupdate */ +#define NRD_CSS_SYNCNT_MSTUPDTVAL_ADDR_BASE UL(0x2B100000) +#define NRD_CSS_SYNCNT_MSTUPDTVAL_ADDR_SIZE UL(0x00030000) + +/* AP - RSE NS MHU */ +#define NRD_CSS_STM_SYSTEM_ITS_BASE UL(0x2CF00000) +#define NRD_CSS_STM_SYSTEM_ITS_SIZE UL(0x02100000) + +/* SCP - MCP - RSE Shared SRAM */ +#define NRD_CSS_SCP_MCP_RSE_SHARED_SRAM_BASE UL(0x2F000000) +#define NRD_CSS_SCP_MCP_RSE_SHARED_SRAM_SIZE UL(0x00400000) + +/* GIC base */ +#define NRD_CSS_GIC_BASE UL(0x30000000) +#define NRD_CSS_GIC_SIZE UL(0x08000000) + +/* CMN */ +#define NRD_CSS_CMN_BASE ULL(0x100000000) +#define NRD_CSS_CMN_SIZE UL(0x40000000) + +/* LCP Peripherals */ +#define NRD_CSS_LCP_PERIPHERAL_BASE ULL(0x200000000) +#define NRD_CSS_LCP_PERIPHERAL_SIZE UL(0x40000000) + +/* DDR IO */ +#define NRD_CSS_DDR_IO_BASE ULL(0x240000000) +#define NRD_CSS_DDR_IO_SIZE UL(0x40000000) + +/* SMMU & NCI IO */ +#define NRD_CSS_SMMU_NCI_IO_BASE ULL(0x280000000) +#define NRD_CSS_SMMU_NCI_IO_SIZE UL(0x60000000) + +/* GPC SMMU */ +#define NRD_CSS_GPC_SMMUV3_BASE UL(0x300000000) +#define NRD_CSS_GPC_SMMUV3_SIZE UL(0x8000000) + +/* DRAM1 */ +#define NRD_CSS_DRAM1_BASE UL(0x80000000) + +/* DRAM2 */ +#define NRD_CSS_DRAM2_BASE ULL(0x8080000000) + +/******************************************************************************* + * MHUv3 related definitions + ******************************************************************************/ + +#define MHU_V3_MBX_FRAME_OFFSET UL(0x10000) + +/* MHUv3 Postbox and Mailbox register frame base */ +#define AP_RSE_ROOT_MHU_V3_PBX NRD_CSS_AP_RSE_ROOT_MHU_BASE +#define AP_RSE_ROOT_MHU_V3_MBX NRD_CSS_AP_RSE_ROOT_MHU_BASE + \ + MHU_V3_MBX_FRAME_OFFSET + +#define AP_RSE_SECURE_MHU_V3_PBX NRD_CSS_AP_RSE_SECURE_MHU_BASE +#define AP_RSE_SECURE_MHU_V3_MBX NRD_CSS_AP_RSE_SECURE_MHU_BASE + \ + MHU_V3_MBX_FRAME_OFFSET + +#endif /* NRD_CSS_DEF3_H */ diff --git a/plat/arm/board/neoverse_rd/common/include/nrd3/nrd_css_fw_def3.h b/plat/arm/board/neoverse_rd/common/include/nrd3/nrd_css_fw_def3.h new file mode 100644 index 00000000..3fbc125c --- /dev/null +++ b/plat/arm/board/neoverse_rd/common/include/nrd3/nrd_css_fw_def3.h @@ -0,0 +1,128 @@ +/* + * Copyright (c) 2024, Arm Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + * + * This file contains the CSS-firmware specific definitions for the third + * generation of platforms. + */ + +#ifndef NRD_CSS_FW_DEF3_H +#define NRD_CSS_FW_DEF3_H + +#include <nrd_css_def3.h> + +/******************************************************************************* + * BL sizes + ******************************************************************************/ + +#define NRD_CSS_BL1_RW_SIZE UL(64 * 1024) /* 64KB */ + +#define NRD_CSS_BL1_RO_BASE NRD_CSS_SHARED_SRAM_BASE +#define NRD_CSS_BL1_RO_SIZE UL(0x00019000) + +# define NRD_CSS_BL2_SIZE UL(0x30000) + +/* + * Since BL31 NOBITS overlays BL2 and BL1-RW, PLAT_ARM_MAX_BL31_SIZE is + * calculated using the current BL31 PROGBITS debug size plus the sizes of BL2 + * and BL1-RW. NRD_BL31_SIZE - is tuned with respect to the actual BL31 + * PROGBITS size which is around 64-68KB at the time this change is being made. + * A buffer of ~35KB is added to account for future expansion of the image, + * making it a total of 100KB. + */ +#define NRD_CSS_BL31_SIZE UL(116 * 1024) /* 116 KB */ + +#define NRD_CSS_DRAM1_CARVEOUT_SIZE UL(0x0C000000) /* 117MB */ + +/******************************************************************************* + * Console config + ******************************************************************************/ + +#define NRD_CSS_UART_CLK_IN_HZ UL(7372800) + +/******************************************************************************* + * Watchdog config + ******************************************************************************/ + +#define NRD_CSS_AP_SECURE_WDOG_TIMEOUT UL(100) + +/******************************************************************************* + * RMM Console Config + ******************************************************************************/ + +#define NRD_CSS_RMM_CONSOLE_BASE NRD_CSS_REALM_UART_BASE +#define NRD_CSS_RMM_CONSOLE_BAUD ARM_CONSOLE_BAUDRATE +#define NRD_CSS_RMM_CONSOLE_CLK_IN_HZ UL(14745600) +#define NRD_CSS_RMM_CONSOLE_NAME "pl011" +#define NRD_CSS_RMM_CONSOLE_COUNT UL(1) + +/******************************************************************************* + * MMU mapping + ******************************************************************************/ + +#define NRD_CSS_PERIPH_MMAP(n) \ + MAP_REGION_FLAT( \ + NRD_REMOTE_CHIP_MEM_OFFSET(n) + \ + NRD_CSS_PERIPH_BASE, \ + NRD_CSS_PERIPH_SIZE, \ + MT_DEVICE | MT_RW | EL3_PAS) + +#define NRD_CSS_SHARED_RAM_MMAP(n) \ + MAP_REGION_FLAT( \ + NRD_REMOTE_CHIP_MEM_OFFSET(n) + \ + ARM_SHARED_RAM_BASE, \ + ARM_SHARED_RAM_SIZE, \ + MT_MEMORY | MT_RW | EL3_PAS) + +#define NRD_CSS_GPC_SMMU_SMMUV3_MMAP \ + MAP_REGION_FLAT( \ + NRD_CSS_GPC_SMMUV3_BASE, \ + NRD_CSS_GPC_SMMUV3_SIZE, \ + MT_DEVICE | MT_RW | EL3_PAS) + +#define NRD_CSS_BL1_RW_MMAP \ + MAP_REGION_FLAT( \ + BL1_RW_BASE, \ + BL1_RW_LIMIT - BL1_RW_BASE, \ + MT_MEMORY | MT_RW | EL3_PAS) + +#define NRD_CSS_NS_DRAM1_MMAP \ + MAP_REGION_FLAT( \ + ARM_NS_DRAM1_BASE, \ + ARM_NS_DRAM1_SIZE, \ + MT_MEMORY | MT_RW | MT_NS) + +#define NRD_CSS_GPT_L1_DRAM_MMAP \ + MAP_REGION_FLAT( \ + ARM_L1_GPT_BASE, \ + ARM_L1_GPT_SIZE, \ + MT_MEMORY | MT_RW | EL3_PAS) + +#define NRD_CSS_EL3_RMM_SHARED_MEM_MMAP \ + MAP_REGION_FLAT( \ + ARM_EL3_RMM_SHARED_BASE, \ + ARM_EL3_RMM_SHARED_SIZE, \ + MT_MEMORY | MT_RW | MT_REALM) + +#define NRD_CSS_RMM_REGION_MMAP \ + MAP_REGION_FLAT( \ + ARM_REALM_BASE, \ + ARM_REALM_SIZE, \ + MT_MEMORY | MT_RW | MT_REALM) + +#if RESET_TO_BL31 +/******************************************************************************* + * BL31 specific defines. + ******************************************************************************/ + +/* Define the DTB image base and size */ +#define NRD_CSS_BL31_PRELOAD_DTB_BASE UL(0xF3000000) +#define NRD_CSS_BL31_PRELOAD_DTB_SIZE UL(0x1000) +#define NRD_CSS_MAP_BL31_DTB MAP_REGION_FLAT( \ + NRD_CSS_BL31_PRELOAD_DTB_BASE, \ + NRD_CSS_BL31_PRELOAD_DTB_SIZE, \ + MT_RW_DATA | MT_NS) +#endif /* RESET_TO_BL31 */ + +#endif /* NRD_CSS_FW_DEF3_H */ diff --git a/plat/arm/board/neoverse_rd/common/include/nrd3/nrd_pas_def3.h b/plat/arm/board/neoverse_rd/common/include/nrd3/nrd_pas_def3.h new file mode 100644 index 00000000..f9a62ae3 --- /dev/null +++ b/plat/arm/board/neoverse_rd/common/include/nrd3/nrd_pas_def3.h @@ -0,0 +1,584 @@ +/* + * Copyright (c) 2024, Arm Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef NRD_PAS_DEF3_H +#define NRD_PAS_DEF3_H + +#ifndef __ASSEMBLER__ +#include <stddef.h> +#include <lib/gpt_rme/gpt_rme.h> +#endif + +#include <nrd_css_def3.h> + +/***************************************************************************** + * PAS regions used to initialize the Granule Protection Table (GPT) + ****************************************************************************/ + +/* + * ===================================================================== + * Base Addr |Size |L? GPT |PAS |Content | + * ===================================================================== + * 0x00000000 |256MB |L0 GPT |ANY |SHARED RAM | + * 0x0FFFFFFF | | | |AP EXPANSION | + * --------------------------------------------------------------------- + * 0x20000000 |64MB |L1 GPT |ROOT |SYSTEM NCI | + * 0x23FFFFFF | | | | | + * --------------------------------------------------------------------- + * 0x28000000 |16MB |L1 GPT |SECURE |DEBUG NIC | + * 0x28FFFFFF | | | | | + * --------------------------------------------------------------------- + * 0x2A400000 |64KB |L1 GPT |NS |NS UART | + * 0x2A40FFFF | | | | | + * --------------------------------------------------------------------- + * 0x2A410000 |64KB |L1 GPT |SECURE |SECURE UART | + * 0x2A41FFFF | | | | | + * --------------------------------------------------------------------- + * 0x2A420000 |64KB |L1 GPT |REALM |REALM UART | + * 0x2A42FFFF | | | | | + * --------------------------------------------------------------------- + * 0x2A430000 |64KB |L1 GPT |SECURE |GENERIC REFCLK | + * 0x2A43FFFF | | | | | + * --------------------------------------------------------------------- + * 0x2A440000 |128KB |L1 GPT |NS |AP NS WDOG | + * 0x2A45FFFF | | | | | + * --------------------------------------------------------------------- + * 0x2A460000 |128KB |L1 GPT |ROOT |AP ROOT WDOG | + * 0x2A47FFFF | | | | | + * --------------------------------------------------------------------- + * 0x2A480000 |128KB |L1 GPT |SECURE |AP SECURE WDOG | + * 0x2A49FFFF | | | | | + * --------------------------------------------------------------------- + * 0x2A4A0000 |64KB |L1 GPT |NS |SID | + * 0x2A4AFFFF | | | | | + * --------------------------------------------------------------------- + * 0x2A4B0000 |64KB |L1 GPT |SECURE |SECURE SRAM ERROR | + * 0x2A4BFFFF | | | |RECORD BLOCK - AP | + * --------------------------------------------------------------------- + * 0x2A4C0000 |64KB |L1 GPT |NS |NS SRAM ERROR | + * 0x2A4CFFFF | | | |RECORD BLOCK - AP | + * --------------------------------------------------------------------- + * 0x2A4D0000 |64KB |L1 GPT |ROOT |ROOT SRAM ERROR | + * 0x2A4DFFFF | | | |RECORD BLOCK - AP | + * --------------------------------------------------------------------- + * 0x2A4E0000 |64KB |L1 GPT |REALM |REALM SRAM ERROR | + * 0x2A4EFFFF | | | |RECORD BLOCK - AP | + * --------------------------------------------------------------------- + * 0x2A4F0000 |64KB |L1 GPT |SECURE |SECURE SRAM ERROR | + * 0x2A4FFFFF | | | |RECORD BLOCK - SCP | + * --------------------------------------------------------------------- + * 0x2A500000 |64KB |L1 GPT |NS |NS SRAM ERROR | + * 0x2A50FFFF | | | |RECORD BLOCK - SCP | + * --------------------------------------------------------------------- + * 0x2A510000 |64KB |L1 GPT |ROOT |ROOT SRAM ERROR | + * 0x2A51FFFF | | | |RECORD BLOCK - SCP | + * --------------------------------------------------------------------- + * 0x2A520000 |64KB |L1 GPT |REALM |REALM SRAM ERROR | + * 0x2A52FFFF | | | |RECORD BLOCK - SCP | + * --------------------------------------------------------------------- + * 0x2A530000 |64KB |L1 GPT |SECURE |SECURE SRAM ERROR | + * 0x2A53FFFF | | | |RECORD BLOCK - MCP | + * --------------------------------------------------------------------- + * 0x2A540000 |64KB |L1 GPT |NS |NS SRAM ERROR | + * 0x2A54FFFF | | | |RECORD BLOCK - MCP | + * --------------------------------------------------------------------- + * 0x2A550000 |64KB |L1 GPT |ROOT |ROOT SRAM ERROR | + * 0x2A55FFFF | | | |RECORD BLOCK - MCP | + * --------------------------------------------------------------------- + * 0x2A560000 |64KB |L1 GPT |REALM |REALM SRAM ERROR | + * 0x2A56FFFF | | | |RECORD BLOCK - MCP | + * --------------------------------------------------------------------- + * 0x2A570000 |64KB |L1 GPT |SECURE |SECURE SRAM ERROR | + * 0x2A57FFFF | | | |RECORD BLOCK - RSE | + * --------------------------------------------------------------------- + * 0x2A580000 |64KB |L1 GPT |NS |NS SRAM ERROR | + * 0x2A58FFFF | | | |RECORD BLOCK - RSE | + * --------------------------------------------------------------------- + * 0x2A590000 |64KB |L1 GPT |ROOT |ROOT SRAM ERROR | + * 0x2A59FFFF | | | |RECORD BLOCK - RSE | + * --------------------------------------------------------------------- + * 0x2A5A0000 |64KB |L1 GPT |REALM |REALM SRAM ERROR | + * 0x2A5AFFFF | | | |RECORD BLOCK - RSE | + * --------------------------------------------------------------------- + * 0x2A5B0000 |64KB |L1 GPT |SECURE |RSE SECURE SRAM ERROR | + * 0x2A5BFFFF | | | |RECORD BLOCK - RSM | + * --------------------------------------------------------------------- + * 0x2A5C0000 |64KB |L1 GPT |NS |RSE NS SRAM ERROR | + * 0x2A5CFFFF | | | |RECORD BLOCK - RSM | + * --------------------------------------------------------------------- + * 0x2A5D0000 |64KB |L1 GPT |SECURE |SCP SECURE SRAM ERROR | + * 0x2A5DFFFF | | | |RECORD BLOCK - RSM | + * --------------------------------------------------------------------- + * 0x2A5E0000 |64KB |L1 GPT |NS |SCP NS SRAM ERROR | + * 0x2A5EFFFF | | | |RECORD BLOCK - RSM | + * --------------------------------------------------------------------- + * 0x2A5F0000 |64KB |L1 GPT |SECURE |MCP SECURE SRAM ERROR | + * 0x2A5FFFFF | | | |RECORD BLOCK - RSM | + * --------------------------------------------------------------------- + * 0x2A600000 |64KB |L1 GPT |NS |MCP NS SRAM ERROR | + * 0x2A60FFFF | | | |RECORD BLOCK - RSM | + * --------------------------------------------------------------------- + * 0x2A800000 |128KB |L1 GPT |NS |CNTCTL REFCLK | + * 0x2A81FFFF | | | |READ FRAME | + * --------------------------------------------------------------------- + * 0x2A820000 |64KB |L1 GPT |SECURE |SECURE TIMER CTL | + * 0x2A82FFFF | | | |BASE FRAME | + * --------------------------------------------------------------------- + * 0x2A830000 |64KB |L1 GPT |NS |NS TIMER CTL | + * 0x2A83FFFF | | | |BASE FRAME | + * --------------------------------------------------------------------- + * 0x2A900000 |128KB |L1 GPT |NS |AP-SCP NS | + * 0x2A91FFFF | | | |MHU | + * --------------------------------------------------------------------- + * 0x2A920000 |128KB |L1 GPT |SECURE |AP-SCP SECURE | + * 0x2A93FFFF | | | |MHU | + * --------------------------------------------------------------------- + * 0x2A940000 |128KB |L1 GPT |ROOT |AP-SCP ROOT | + * 0x2A95FFFF | | | |MHU | + * --------------------------------------------------------------------- + * 0x2AA00000 |128KB |L1 GPT |NS |AP-MCP NS | + * 0x2AA1FFFF | | | |MHU | + * --------------------------------------------------------------------- + * 0x2AA20000 |128KB |L1 GPT |SECURE |AP-MCP SECURE | + * 0x2AA3FFFF | | | |MHU | + * --------------------------------------------------------------------- + * 0x2AA40000 |128KB |L1 GPT |ROOT |AP-MCP ROOT | + * 0x2AA5FFFF | | | |MHU | + * --------------------------------------------------------------------- + * 0x2AB00000 |128KB |L1 GPT |NS |AP-MCP NS | + * 0x2AB1FFFF | | | |MHU | + * --------------------------------------------------------------------- + * 0x2AB20000 |128KB |L1 GPT |SECURE |AP-RSE SECURE | + * 0x2AB3FFFF | | | |MHU | + * --------------------------------------------------------------------- + * 0x2AB40000 |128KB |L1 GPT |ROOT |AP-RSE ROOT | + * 0x2AB5FFFF | | | |MHU | + * --------------------------------------------------------------------- + * 0x2AB60000 |128KB |L1 GPT |REALM |AP-RSE REALM | + * 0x2AB7FFFF | | | |MHU | + * --------------------------------------------------------------------- + * 0x2AC00000 |1152KB |L1 GPT |ROOT |SCP MCP RSE | + * 0x2ACEFFFF | | | |CROSS CHIP MHU | + * --------------------------------------------------------------------- + * 0x2B100000 |192KB |L1 GPT |SECURE |SYNCNT | + * 0x2B12FFFF | | | |MSTUPDTVAL_ADDR | + * --------------------------------------------------------------------- + * 0x2CF00000 |~33MB |L1 GPT |NS |STM SYSTEM ITS | + * 0x2EFFFFFF | | | | | + * --------------------------------------------------------------------- + * 0x2F000000 |4MB |L1 GPT |ANY |SHARED SRAM | + * 0x2F3FFFFF | | | | | + * --------------------------------------------------------------------- + * 0x30000000 |128MB |L1 GPT |ANY |GIC CLAYTON | + * 0x37FFFFFF | | | | | + * --------------------------------------------------------------------- + * 0x80000000 |2GB - |L1 GPT |NS |NS DRAM | + * 0xFFFE2BFF |117MB | | | | + * --------------------------------------------------------------------- + * 0x80000000 |26MB |L1 GPT |REALM |RMM | + * 0x37FFFFFF | | | |TF-A SHARED | + * --------------------------------------------------------------------- + * 0x80000000 |2MB |L1 GPT |ROOT |L1GPT | + * 0x37FFFFFF | | | | | + * --------------------------------------------------------------------- + * 0x100080000000 |2GB |L1 GPT |NS |DRAM 1 CHIP 3 | + * 0x1000FFFFFFFF | | | | | + * --------------------------------------------------------------------- + * 0x200080000000 |2GB |L1 GPT |NS |DRAM 1 CHIP 2 | + * 0x2000FFFFFFFF | | | | | + * --------------------------------------------------------------------- + * 0x300080000000 |2GB |L1 GPT |NS |DRAM 1 CHIP 1 | + * 0x3000FFFFFFFF | | | | | + * --------------------------------------------------------------------- + * 0x100000000 |1GB |L1 GPT |ANY |CMN | + * 0x13FFFFFFF | | | | | + * --------------------------------------------------------------------- + * 0x200000000 |1GB |L1 GPT |ANY |LCP PERIPHERALS | + * 0x23FFFFFFF | | | |DDR | + * --------------------------------------------------------------------- + * 0x240000000 |1GB |L1 GPT |ANY |DDR IO | + * 0x27FFFFFFF | | | | | + * --------------------------------------------------------------------- + * 0x280000000 |1.5GB |L1 GPT |ANY |SMMU & NCI IO | + * 0x2DFFFFFFF | | | | | + * --------------------------------------------------------------------- + * 0x300000000 |128MB |L1 GPT |ROOT |GPC SMMU | + * 0x308000000 | | | | | + * --------------------------------------------------------------------- + * 0x8080000000 |6GB |L1 GPT |ANY |DRAM 2 CHIP 0 | + * 0x81FFFFFFFF | | | | | + * --------------------------------------------------------------------- + * 0x108080000000 |6GB |L1 GPT |NS |DRAM 2 CHIP 1 | + * 0x1081FFFFFFFF | | | | | + * --------------------------------------------------------------------- + * 0x208080000000 |6GB |L1 GPT |NS |DRAM 2 CHIP 2 | + * 0x2081FFFFFFFF | | | | | + * --------------------------------------------------------------------- + * 0x308080000000 |6GB |L1 GPT |NS |DRAM 2 CHIP 3 | + * 0x3081FFFFFFFF | | | | | + * ===================================================================== + */ + +/******************************************************************************* + * Multichip config + ******************************************************************************/ + +#define NRD_MC_BASE(base, n) (NRD_REMOTE_CHIP_MEM_OFFSET(n) + base) + +/******************************************************************************* + * PAS mappings + ******************************************************************************/ + +#define NRD_PAS_SHARED_SRAM \ + GPT_MAP_REGION_GRANULE( \ + NRD_CSS_SHARED_SRAM_BASE, \ + NRD_CSS_SHARED_SRAM_SIZE, \ + GPT_GPI_ANY) + +#define NRD_PAS_SYSTEM_NCI \ + GPT_MAP_REGION_GRANULE( \ + NRD_CSS_SYSTEM_NCI_BASE, \ + NRD_CSS_SYSTEM_NCI_SIZE, \ + GPT_GPI_ROOT) + +#define NRD_PAS_DEBUG_NIC \ + GPT_MAP_REGION_GRANULE( \ + NRD_CSS_DEBUG_NIC_BASE, \ + NRD_CSS_DEBUG_NIC_SIZE, \ + GPT_GPI_SECURE) + +#define NRD_PAS_NS_UART \ + GPT_MAP_REGION_GRANULE( \ + NRD_CSS_NS_UART_BASE, \ + NRD_CSS_NS_UART_SIZE, \ + GPT_GPI_NS) + +#define NRD_PAS_REALM_UART \ + GPT_MAP_REGION_GRANULE( \ + NRD_CSS_REALM_UART_BASE, \ + NRD_CSS_REALM_UART_SIZE, \ + GPT_GPI_REALM) + +#define NRD_PAS_AP_NS_WDOG \ + GPT_MAP_REGION_GRANULE( \ + NRD_CSS_AP_NS_WDOG_BASE, \ + NRD_CSS_AP_NS_WDOG_SIZE, \ + GPT_GPI_NS) + +#define NRD_PAS_AP_ROOT_WDOG \ + GPT_MAP_REGION_GRANULE( \ + NRD_CSS_AP_ROOT_WDOG_BASE, \ + NRD_CSS_AP_ROOT_WDOG_SIZE, \ + GPT_GPI_ROOT) + +#define NRD_PAS_AP_SECURE_WDOG \ + GPT_MAP_REGION_GRANULE( \ + NRD_CSS_AP_SECURE_WDOG_BASE, \ + NRD_CSS_AP_SECURE_WDOG_SIZE, \ + GPT_GPI_SECURE) + +#define NRD_PAS_SECURE_SRAM_ERB_AP \ + GPT_MAP_REGION_GRANULE( \ + NRD_CSS_SECURE_SRAM_ERB_AP_BASE, \ + NRD_CSS_SECURE_SRAM_ERB_AP_SIZE, \ + GPT_GPI_SECURE) + +#define NRD_PAS_NS_SRAM_ERB_AP \ + GPT_MAP_REGION_GRANULE( \ + NRD_CSS_NS_SRAM_ERB_AP_BASE, \ + NRD_CSS_NS_SRAM_ERB_AP_SIZE, \ + GPT_GPI_NS) + +#define NRD_PAS_ROOT_SRAM_ERB_AP \ + GPT_MAP_REGION_GRANULE( \ + NRD_CSS_ROOT_SRAM_ERB_AP_BASE, \ + NRD_CSS_ROOT_SRAM_ERB_AP_SIZE, \ + GPT_GPI_ROOT) + +#define NRD_PAS_REALM_SRAM_ERB_AP \ + GPT_MAP_REGION_GRANULE( \ + NRD_CSS_REALM_SRAM_ERB_AP_BASE, \ + NRD_CSS_REALM_SRAM_ERB_AP_SIZE, \ + GPT_GPI_REALM) + +#define NRD_PAS_SECURE_SRAM_ERB_SCP \ + GPT_MAP_REGION_GRANULE( \ + NRD_CSS_SECURE_SRAM_ERB_SCP_BASE, \ + NRD_CSS_SECURE_SRAM_ERB_SCP_SIZE, \ + GPT_GPI_SECURE) + +#define NRD_PAS_NS_SRAM_ERB_SCP \ + GPT_MAP_REGION_GRANULE( \ + NRD_CSS_NS_SRAM_ERB_SCP_BASE, \ + NRD_CSS_NS_SRAM_ERB_SCP_SIZE, \ + GPT_GPI_NS) + +#define NRD_PAS_ROOT_SRAM_ERB_SCP \ + GPT_MAP_REGION_GRANULE( \ + NRD_CSS_ROOT_SRAM_ERB_SCP_BASE, \ + NRD_CSS_ROOT_SRAM_ERB_SCP_SIZE, \ + GPT_GPI_ROOT) + +#define NRD_PAS_REALM_SRAM_ERB_SCP \ + GPT_MAP_REGION_GRANULE( \ + NRD_CSS_REALM_SRAM_ERB_SCP_BASE, \ + NRD_CSS_REALM_SRAM_ERB_SCP_SIZE, \ + GPT_GPI_REALM) + +#define NRD_PAS_SECURE_SRAM_ERB_MCP \ + GPT_MAP_REGION_GRANULE( \ + NRD_CSS_SECURE_SRAM_ERB_MCP_BASE, \ + NRD_CSS_SECURE_SRAM_ERB_MCP_SIZE, \ + GPT_GPI_SECURE) + +#define NRD_PAS_NS_SRAM_ERB_MCP \ + GPT_MAP_REGION_GRANULE( \ + NRD_CSS_NS_SRAM_ERB_MCP_BASE, \ + NRD_CSS_NS_SRAM_ERB_MCP_SIZE, \ + GPT_GPI_NS) + +#define NRD_PAS_ROOT_SRAM_ERB_MCP \ + GPT_MAP_REGION_GRANULE( \ + NRD_CSS_ROOT_SRAM_ERB_MCP_BASE, \ + NRD_CSS_ROOT_SRAM_ERB_MCP_SIZE, \ + GPT_GPI_ROOT) + +#define NRD_PAS_REALM_SRAM_ERB_MCP \ + GPT_MAP_REGION_GRANULE( \ + NRD_CSS_REALM_SRAM_ERB_MCP_BASE, \ + NRD_CSS_REALM_SRAM_ERB_MCP_SIZE, \ + GPT_GPI_REALM) + +#define NRD_PAS_SECURE_SRAM_ERB_RSE \ + GPT_MAP_REGION_GRANULE( \ + NRD_CSS_SECURE_SRAM_ERB_RSE_BASE, \ + NRD_CSS_SECURE_SRAM_ERB_RSE_SIZE, \ + GPT_GPI_SECURE) + +#define NRD_PAS_NS_SRAM_ERB_RSE \ + GPT_MAP_REGION_GRANULE( \ + NRD_CSS_NS_SRAM_ERB_RSE_BASE, \ + NRD_CSS_NS_SRAM_ERB_RSE_SIZE, \ + GPT_GPI_NS) + +#define NRD_PAS_ROOT_SRAM_ERB_RSE \ + GPT_MAP_REGION_GRANULE( \ + NRD_CSS_ROOT_SRAM_ERB_RSE_BASE, \ + NRD_CSS_ROOT_SRAM_ERB_RSE_SIZE, \ + GPT_GPI_ROOT) + +#define NRD_PAS_REALM_SRAM_ERB_RSE \ + GPT_MAP_REGION_GRANULE( \ + NRD_CSS_REALM_SRAM_ERB_RSE_BASE, \ + NRD_CSS_REALM_SRAM_ERB_RSE_SIZE, \ + GPT_GPI_REALM) + +#define NRD_PAS_RSE_SECURE_SRAM_ERB_RSM \ + GPT_MAP_REGION_GRANULE( \ + NRD_CSS_RSE_SECURE_SRAM_ERB_RSM_BASE, \ + NRD_CSS_RSE_SECURE_SRAM_ERB_RSM_SIZE, \ + GPT_GPI_SECURE) + +#define NRD_PAS_RSE_NS_SRAM_ERB_RSM \ + GPT_MAP_REGION_GRANULE( \ + NRD_CSS_RSE_NS_SRAM_ERB_RSM_BASE, \ + NRD_CSS_RSE_NS_SRAM_ERB_RSM_SIZE, \ + GPT_GPI_NS) + +#define NRD_PAS_SCP_SECURE_SRAM_ERB_RSM \ + GPT_MAP_REGION_GRANULE( \ + NRD_CSS_SCP_SECURE_SRAM_ERB_RSM_BASE, \ + NRD_CSS_SCP_SECURE_SRAM_ERB_RSM_SIZE, \ + GPT_GPI_SECURE) + +#define NRD_PAS_SCP_NS_SRAM_ERB_RSM \ + GPT_MAP_REGION_GRANULE( \ + NRD_CSS_SCP_NS_SRAM_ERB_RSM_BASE, \ + NRD_CSS_SCP_NS_SRAM_ERB_RSM_SIZE, \ + GPT_GPI_NS) + +#define NRD_PAS_MCP_SECURE_SRAM_ERB_RSM \ + GPT_MAP_REGION_GRANULE( \ + NRD_CSS_MCP_SECURE_SRAM_ERB_RSM_BASE, \ + NRD_CSS_MCP_SECURE_SRAM_ERB_RSM_SIZE, \ + GPT_GPI_SECURE) + +#define NRD_PAS_MCP_NS_SRAM_ERB_RSM \ + GPT_MAP_REGION_GRANULE( \ + NRD_CSS_MCP_NS_SRAM_ERB_RSM_BASE, \ + NRD_CSS_MCP_NS_SRAM_ERB_RSM_SIZE, \ + GPT_GPI_NS) + +#define NRD_PAS_AP_SCP_ROOT_MHU \ + GPT_MAP_REGION_GRANULE( \ + NRD_CSS_AP_SCP_ROOT_MHU_BASE, \ + NRD_CSS_AP_SCP_ROOT_MHU_SIZE, \ + GPT_GPI_ROOT) + +#define NRD_PAS_AP_MCP_NS_MHU \ + GPT_MAP_REGION_GRANULE( \ + NRD_CSS_AP_MCP_NS_MHU_BASE, \ + NRD_CSS_AP_MCP_NS_MHU_SIZE, \ + GPT_GPI_NS) + +#define NRD_PAS_AP_MCP_SECURE_MHU \ + GPT_MAP_REGION_GRANULE( \ + NRD_CSS_AP_MCP_SECURE_MHU_BASE, \ + NRD_CSS_AP_MCP_SECURE_MHU_SIZE, \ + GPT_GPI_SECURE) + +#define NRD_PAS_AP_MCP_ROOT_MHU \ + GPT_MAP_REGION_GRANULE( \ + NRD_CSS_AP_MCP_ROOT_MHU_BASE, \ + NRD_CSS_AP_MCP_ROOT_MHU_SIZE, \ + GPT_GPI_ROOT) + +#define NRD_PAS_AP_RSE_NS_MHU \ + GPT_MAP_REGION_GRANULE( \ + NRD_CSS_AP_RSE_NS_MHU_BASE, \ + NRD_CSS_AP_RSE_NS_MHU_SIZE, \ + GPT_GPI_NS) + +#define NRD_PAS_AP_RSE_SECURE_MHU \ + GPT_MAP_REGION_GRANULE( \ + NRD_CSS_AP_RSE_SECURE_MHU_BASE, \ + NRD_CSS_AP_RSE_SECURE_MHU_SIZE, \ + GPT_GPI_SECURE) + +#define NRD_PAS_AP_RSE_ROOT_MHU \ + GPT_MAP_REGION_GRANULE( \ + NRD_CSS_AP_RSE_ROOT_MHU_BASE, \ + NRD_CSS_AP_RSE_ROOT_MHU_SIZE, \ + GPT_GPI_ROOT) + +#define NRD_PAS_AP_RSE_REALM_MHU \ + GPT_MAP_REGION_GRANULE( \ + NRD_CSS_AP_RSE_REALM_MHU_BASE, \ + NRD_CSS_AP_RSE_REALM_MHU_SIZE, \ + GPT_GPI_REALM) + +#define NRD_PAS_SCP_MCP_RSE_CROSS_CHIP_MHU \ + GPT_MAP_REGION_GRANULE( \ + NRD_CSS_SCP_MCP_RSE_CROSS_CHIP_MHU_BASE, \ + NRD_CSS_SCP_MCP_RSE_CROSS_CHIP_MHU_SIZE, \ + GPT_GPI_ROOT) + +#define NRD_PAS_SYNCNT_MSTUPDTVAL_ADDR \ + GPT_MAP_REGION_GRANULE( \ + NRD_CSS_SYNCNT_MSTUPDTVAL_ADDR_BASE, \ + NRD_CSS_SYNCNT_MSTUPDTVAL_ADDR_SIZE, \ + GPT_GPI_SECURE) + +#define NRD_PAS_STM_SYSTEM_ITS \ + GPT_MAP_REGION_GRANULE( \ + NRD_CSS_STM_SYSTEM_ITS_BASE, \ + NRD_CSS_STM_SYSTEM_ITS_SIZE, \ + GPT_GPI_NS) + +#define NRD_PAS_SCP_MCP_RSE_SHARED_SRAM \ + GPT_MAP_REGION_GRANULE( \ + NRD_CSS_SCP_MCP_RSE_SHARED_SRAM_BASE, \ + NRD_CSS_SCP_MCP_RSE_SHARED_SRAM_SIZE, \ + GPT_GPI_ANY) + +#define NRD_PAS_GIC \ + GPT_MAP_REGION_GRANULE( \ + NRD_CSS_GIC_BASE, \ + NRD_CSS_GIC_SIZE, \ + GPT_GPI_ANY) + +#define NRD_PAS_NS_DRAM \ + GPT_MAP_REGION_GRANULE( \ + ARM_NS_DRAM1_BASE, \ + ARM_NS_DRAM1_SIZE, \ + GPT_GPI_NS) + +#define NRD_PAS_DRAM1_CHIP1 \ + GPT_MAP_REGION_GRANULE( \ + NRD_MC_BASE(NRD_CSS_DRAM1_BASE, 1), \ + ARM_DRAM1_SIZE, \ + GPT_GPI_NS) + +#define NRD_PAS_DRAM1_CHIP2 \ + GPT_MAP_REGION_GRANULE( \ + NRD_MC_BASE(NRD_CSS_DRAM1_BASE, 2), \ + ARM_DRAM1_SIZE, \ + GPT_GPI_NS) + +#define NRD_PAS_DRAM1_CHIP3 \ + GPT_MAP_REGION_GRANULE( \ + NRD_MC_BASE(NRD_CSS_DRAM1_BASE, 3), \ + ARM_DRAM1_SIZE, \ + GPT_GPI_NS) + +#define NRD_PAS_RMM \ + GPT_MAP_REGION_GRANULE( \ + ARM_REALM_BASE, \ + ARM_REALM_SIZE + \ + ARM_EL3_RMM_SHARED_SIZE, \ + GPT_GPI_REALM) + +#define NRD_PAS_L1GPT \ + GPT_MAP_REGION_GRANULE( \ + ARM_L1_GPT_BASE, \ + ARM_L1_GPT_SIZE, \ + GPT_GPI_ROOT) + +#define NRD_PAS_CMN \ + GPT_MAP_REGION_GRANULE( \ + NRD_CSS_CMN_BASE, \ + NRD_CSS_CMN_SIZE, \ + GPT_GPI_ANY) + +#define NRD_PAS_LCP_PERIPHERAL \ + GPT_MAP_REGION_GRANULE( \ + NRD_CSS_LCP_PERIPHERAL_BASE, \ + NRD_CSS_LCP_PERIPHERAL_SIZE, \ + GPT_GPI_ANY) + +#define NRD_PAS_DDR_IO \ + GPT_MAP_REGION_GRANULE( \ + NRD_CSS_DDR_IO_BASE, \ + NRD_CSS_DDR_IO_SIZE, \ + GPT_GPI_ANY) + +#define NRD_PAS_SMMU_NCI_IO \ + GPT_MAP_REGION_GRANULE( \ + NRD_CSS_SMMU_NCI_IO_BASE, \ + NRD_CSS_SMMU_NCI_IO_SIZE, \ + GPT_GPI_ANY) + +#define NRD_PAS_GPC_SMMUV3 \ + GPT_MAP_REGION_GRANULE( \ + NRD_CSS_GPC_SMMUV3_BASE, \ + NRD_CSS_GPC_SMMUV3_SIZE, \ + GPT_GPI_ROOT) + +#define NRD_PAS_DRAM2_CHIP0 \ + GPT_MAP_REGION_GRANULE( \ + NRD_MC_BASE(NRD_CSS_DRAM2_BASE, 0), \ + ARM_DRAM2_SIZE, \ + GPT_GPI_NS) + +#define NRD_PAS_DRAM2_CHIP1 \ + GPT_MAP_REGION_GRANULE( \ + NRD_MC_BASE(NRD_CSS_DRAM2_BASE, 1), \ + ARM_DRAM2_SIZE, \ + GPT_GPI_NS) + +#define NRD_PAS_DRAM2_CHIP2 \ + GPT_MAP_REGION_GRANULE( \ + NRD_MC_BASE(NRD_CSS_DRAM2_BASE, 2), \ + ARM_DRAM2_SIZE, \ + GPT_GPI_NS) + +#define NRD_PAS_DRAM2_CHIP3 \ + GPT_MAP_REGION_GRANULE( \ + NRD_MC_BASE(NRD_CSS_DRAM2_BASE, 3), \ + ARM_DRAM2_SIZE, \ + GPT_GPI_NS) + +#endif /* NRD_PAS_DEF3_H */ diff --git a/plat/arm/board/neoverse_rd/common/include/nrd3/nrd_plat_arm_def3.h b/plat/arm/board/neoverse_rd/common/include/nrd3/nrd_plat_arm_def3.h new file mode 100644 index 00000000..8d6d1cb3 --- /dev/null +++ b/plat/arm/board/neoverse_rd/common/include/nrd3/nrd_plat_arm_def3.h @@ -0,0 +1,784 @@ +/* + * Copyright (c) 2024, Arm Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + * + * This file is limited to include the platform port definitions for the + * third generation of platforms. + */ + +#ifndef NRD_PLAT_ARM_DEF3_H +#define NRD_PLAT_ARM_DEF3_H + +#include <common/tbbr/tbbr_img_def.h> + +#ifndef __ASSEMBLER__ +#include <lib/mmio.h> +#endif /* __ASSEMBLER__ */ + +#include <plat/arm/common/arm_spm_def.h> +#include <plat/common/common_def.h> +#include <nrd_css_fw_def3.h> +#include <nrd_ros_fw_def3.h> + +/******************************************************************************* + * Core count + ******************************************************************************/ + +#define PLATFORM_CORE_COUNT (NRD_CHIP_COUNT * \ + PLAT_ARM_CLUSTER_COUNT * \ + NRD_MAX_CPUS_PER_CLUSTER * \ + NRD_MAX_PE_PER_CPU) + +/******************************************************************************* + * PA/VA config + ******************************************************************************/ + +#ifdef __aarch64__ +#define PLAT_PHY_ADDR_SPACE_SIZE NRD_REMOTE_CHIP_MEM_OFFSET( \ + NRD_CHIP_COUNT) +#define PLAT_VIRT_ADDR_SPACE_SIZE NRD_REMOTE_CHIP_MEM_OFFSET( \ + NRD_CHIP_COUNT) +#else +#define PLAT_PHY_ADDR_SPACE_SIZE (1ULL << 32) +#define PLAT_VIRT_ADDR_SPACE_SIZE (1ULL << 32) +#endif + +/******************************************************************************* + * XLAT definitions + ******************************************************************************/ + +/* + * PLAT_ARM_MMAP_ENTRIES depends on the number of entries in the + * plat_arm_mmap array defined for each BL stage. In addition to that, on + * multi-chip platforms, address regions on each of the remote chips are + * also mapped. In BL31, for instance, three address regions on the remote + * chips are accessed - secure ram, css device and soc device regions. + */ +#if defined(IMAGE_BL31) +# define PLAT_ARM_MMAP_ENTRIES (10 + ((NRD_CHIP_COUNT - 1) * 3)) +# define MAX_XLAT_TABLES (10 + ((NRD_CHIP_COUNT - 1) * 3)) +#elif defined(IMAGE_BL32) +# define PLAT_ARM_MMAP_ENTRIES U(8) +# define MAX_XLAT_TABLES U(5) +#elif defined(IMAGE_BL2) +# define PLAT_ARM_MMAP_ENTRIES (16 + (NRD_CHIP_COUNT - 1)) +# define MAX_XLAT_TABLES (11 + ((NRD_CHIP_COUNT - 1) * 2)) +#else +# define PLAT_ARM_MMAP_ENTRIES U(7) +# define MAX_XLAT_TABLES U(7) +#endif + +/******************************************************************************* + * BL sizes + ******************************************************************************/ + +#define PLAT_ARM_MAX_ROMLIB_RW_SIZE UL(0) +#define PLAT_ARM_MAX_ROMLIB_RO_SIZE UL(0) + +#define PLAT_ARM_MAX_BL1_RW_SIZE NRD_CSS_BL1_RW_SIZE + +/* + * PLAT_ARM_MAX_BL2_SIZE is calculated using the current BL2 debug size plus a + * little space for growth. Additional 8KiB space is added per chip in + * order to accommodate the additional level of translation required for "TZC" + * peripheral access which lies in >4TB address space. + * + */ +#define PLAT_ARM_MAX_BL2_SIZE (NRD_CSS_BL2_SIZE + \ + ((NRD_CHIP_COUNT - 1) * 0x2000)) + +#define PLAT_ARM_MAX_BL31_SIZE (NRD_CSS_BL31_SIZE + \ + PLAT_ARM_MAX_BL2_SIZE + \ + PLAT_ARM_MAX_BL1_RW_SIZE) + +/******************************************************************************* + * BL31 plat param + ******************************************************************************/ + +/* Special value used to verify platform parameters from BL2 to BL31 */ +#define ARM_BL31_PLAT_PARAM_VAL ULL(0x0f1e2d3c4b5a6978) + +/******************************************************************************* + * Stack sizes + ******************************************************************************/ + +#if defined(IMAGE_BL1) +# if TRUSTED_BOARD_BOOT +# define PLATFORM_STACK_SIZE UL(0x1000) +# else +# define PLATFORM_STACK_SIZE UL(0x440) +# endif +#elif defined(IMAGE_BL2) +# if TRUSTED_BOARD_BOOT +# define PLATFORM_STACK_SIZE UL(0x1000) +# else +# define PLATFORM_STACK_SIZE UL(0x400) +# endif +#elif defined(IMAGE_BL2U) +# define PLATFORM_STACK_SIZE UL(0x400) +#elif defined(IMAGE_BL31) +# if SPM_MM +# define PLATFORM_STACK_SIZE UL(0x500) +# else +# define PLATFORM_STACK_SIZE UL(0x400) +# endif +#elif defined(IMAGE_BL32) +# define PLATFORM_STACK_SIZE UL(0x440) +#endif + +/******************************************************************************* + * Console config + ******************************************************************************/ + +#define ARM_CONSOLE_BAUDRATE (115200) + +/* UART related constants */ +#define PLAT_ARM_BOOT_UART_BASE NRD_CSS_SECURE_UART_BASE +#define PLAT_ARM_BOOT_UART_CLK_IN_HZ NRD_CSS_UART_CLK_IN_HZ + +#define PLAT_ARM_RUN_UART_BASE NRD_CSS_SECURE_UART_BASE +#define PLAT_ARM_RUN_UART_CLK_IN_HZ NRD_CSS_UART_CLK_IN_HZ + +#define PLAT_ARM_CRASH_UART_BASE NRD_CSS_SECURE_UART_BASE +#define PLAT_ARM_CRASH_UART_CLK_IN_HZ NRD_CSS_UART_CLK_IN_HZ + +/******************************************************************************* + * System counter and timer config + ******************************************************************************/ + +#define ARM_SYS_CNTCTL_BASE NRD_CSS_GENERIC_REFCLK_BASE +#define ARM_SYS_CNTREAD_BASE NRD_CSS_CNTCTL_REFCLK_READFRAME_BASE +#define ARM_SYS_TIMCTL_BASE NRD_CSS_SYS_TIMCTL_BASE +#define ARM_SYS_CNT_BASE_S NRD_CSS_SECURE_TIMER_CTL_BASE +#define ARM_SYS_CNT_BASE_NS NRD_CSS_NS_TIMER_CTL_BASE + +/******************************************************************************* + * SRAM and DRAM config for FW + ******************************************************************************/ + +#define PLAT_ARM_TRUSTED_ROM_BASE NRD_CSS_SECURE_ROM_BASE +#define PLAT_ARM_TRUSTED_ROM_SIZE NRD_CSS_SECURE_ROM_SIZE + +#define PLAT_ARM_DRAM2_BASE NRD_CSS_DRAM2_BASE +#define PLAT_ARM_DRAM2_SIZE NRD_CSS_DRAM2_SIZE + +#define PLAT_ARM_TRUSTED_SRAM_SIZE NRD_CSS_SECURE_SRAM_SIZE + +#define PLAT_ARM_NSTIMER_FRAME_ID (0) + +#define PLAT_ARM_NSRAM_BASE NRD_CSS_NS_SRAM_BASE +#define PLAT_ARM_NSRAM_SIZE NRD_CSS_NS_SRAM_SIZE + +/******************************************************************************* + * Power config + ******************************************************************************/ + +/* + * Macros mapping the MPIDR Affinity levels to ARM Platform Power levels. The + * power levels have a 1:1 mapping with the MPIDR affinity levels. + */ +#define ARM_PWR_LVL0 MPIDR_AFFLVL0 +#define ARM_PWR_LVL1 MPIDR_AFFLVL1 +#define ARM_PWR_LVL2 MPIDR_AFFLVL2 +#define ARM_PWR_LVL3 MPIDR_AFFLVL3 + +/* Local power state for power domains in Run state. */ +#define ARM_LOCAL_STATE_RUN U(0) +/* Local power state for retention. Valid only for CPU power domains */ +#define ARM_LOCAL_STATE_RET U(1) +/* + * Local power state for OFF/power-down. Valid for CPU and cluster power + * domains + */ +#define ARM_LOCAL_STATE_OFF U(2) +/* + * This macro defines the deepest retention state possible. A higher state + * id will represent an invalid or a power down state. + */ +#define PLAT_MAX_RET_STATE ARM_LOCAL_STATE_RET +/* + * This macro defines the deepest power down states possible. Any state ID + * higher than this is invalid. + */ +#define PLAT_MAX_OFF_STATE ARM_LOCAL_STATE_OFF + +#define CSS_SYSTEM_PWR_DMN_LVL ARM_PWR_LVL2 +#define PLAT_MAX_PWR_LVL ARM_PWR_LVL1 + +/******************************************************************************* + * MHU config + ******************************************************************************/ + +#define PLAT_CSS_MHU_BASE NRD_CSS_AP_SCP_SECURE_MHU_BASE +#define PLAT_MHUV2_BASE PLAT_CSS_MHU_BASE + +/******************************************************************************* + * Cache config + ******************************************************************************/ + +#define ARM_CACHE_WRITEBACK_SHIFT U(6) + +/* + * Some data must be aligned on the biggest cache line size in the platform. + * This is known only to the platform as it might have a combination of + * integrated and external caches. + */ +#define CACHE_WRITEBACK_GRANULE (U(1) << ARM_CACHE_WRITEBACK_SHIFT) + +/******************************************************************************* + * SCMI config + ******************************************************************************/ + +/* Number of SCMI channels on the platform */ +#define PLAT_ARM_SCMI_CHANNEL_COUNT NRD_CHIP_COUNT + +/******************************************************************************* + * GIC/EHF config + ******************************************************************************/ + +/* CPU Fault Handling Interrupt(FHI) PPI interrupt ID */ +#define PLAT_CORE_FAULT_IRQ U(17) + +/* ARM platforms use 3 upper bits of secure interrupt priority */ +#define PLAT_PRI_BITS U(3) + +#if ENABLE_FEAT_RAS && FFH_SUPPORT +#define PLAT_RAS_PRI U(0x10) +#endif + +#if ENABLE_FEAT_RAS && FFH_SUPPORT +#define PLAT_SP_PRI PLAT_RAS_PRI +#else +#define PLAT_SP_PRI U(0x10) +#endif + +#define ARM_IRQ_SEC_PHY_TIMER U(29) + +#define ARM_IRQ_SEC_SGI_0 U(8) +#define ARM_IRQ_SEC_SGI_1 U(9) +#define ARM_IRQ_SEC_SGI_2 U(10) +#define ARM_IRQ_SEC_SGI_3 U(11) +#define ARM_IRQ_SEC_SGI_4 U(12) +#define ARM_IRQ_SEC_SGI_5 U(13) +#define ARM_IRQ_SEC_SGI_6 U(14) +#define ARM_IRQ_SEC_SGI_7 U(15) + +#define ARM_G0_IRQ_PROPS(grp) \ + INTR_PROP_DESC(ARM_IRQ_SEC_SGI_0, PLAT_SDEI_NORMAL_PRI, \ + (grp), GIC_INTR_CFG_EDGE), \ + INTR_PROP_DESC(ARM_IRQ_SEC_SGI_6, GIC_HIGHEST_SEC_PRIORITY, \ + (grp), GIC_INTR_CFG_EDGE) + +/* + * Define a list of Group 1 Secure and Group 0 interrupt properties as per GICv3 + * terminology. On a GICv2 system or mode, the lists will be merged and treated + * as Group 0 interrupts. + */ +#define ARM_G1S_IRQ_PROPS(grp) \ + INTR_PROP_DESC(ARM_IRQ_SEC_PHY_TIMER, GIC_HIGHEST_SEC_PRIORITY, \ + (grp), GIC_INTR_CFG_LEVEL), \ + INTR_PROP_DESC(ARM_IRQ_SEC_SGI_1, GIC_HIGHEST_SEC_PRIORITY, \ + (grp), GIC_INTR_CFG_EDGE), \ + INTR_PROP_DESC(ARM_IRQ_SEC_SGI_2, GIC_HIGHEST_SEC_PRIORITY, \ + (grp), GIC_INTR_CFG_EDGE), \ + INTR_PROP_DESC(ARM_IRQ_SEC_SGI_3, GIC_HIGHEST_SEC_PRIORITY, \ + (grp), GIC_INTR_CFG_EDGE), \ + INTR_PROP_DESC(ARM_IRQ_SEC_SGI_4, GIC_HIGHEST_SEC_PRIORITY, \ + (grp), GIC_INTR_CFG_EDGE), \ + INTR_PROP_DESC(ARM_IRQ_SEC_SGI_5, GIC_HIGHEST_SEC_PRIORITY, \ + (grp), GIC_INTR_CFG_EDGE), \ + INTR_PROP_DESC(ARM_IRQ_SEC_SGI_7, GIC_HIGHEST_SEC_PRIORITY, \ + (grp), GIC_INTR_CFG_EDGE) + +#define ARM_G0_IRQ_PROPS(grp) \ + INTR_PROP_DESC(ARM_IRQ_SEC_SGI_0, PLAT_SDEI_NORMAL_PRI, \ + (grp), GIC_INTR_CFG_EDGE), \ + INTR_PROP_DESC(ARM_IRQ_SEC_SGI_6, GIC_HIGHEST_SEC_PRIORITY, \ + (grp), GIC_INTR_CFG_EDGE) + +#define PLAT_ARM_G1S_IRQ_PROPS(grp) CSS_G1S_IRQ_PROPS(grp) +#define PLAT_ARM_G0_IRQ_PROPS(grp) ARM_G0_IRQ_PROPS(grp) + +#define PLAT_ARM_GICD_BASE NRD_CSS_GIC_BASE +#if (NRD_PLATFORM_VARIANT == 1) +#define PLAT_ARM_GICR_BASE NRD_CSS_GIC_BASE + UL(0x00100000) +#else +#define PLAT_ARM_GICR_BASE NRD_CSS_GIC_BASE + UL(0x001C0000) +#endif + +/******************************************************************************* + * SDEI config + ******************************************************************************/ + +#define PLAT_SDEI_CRITICAL_PRI U(0x60) +#define PLAT_SDEI_NORMAL_PRI U(0x70) + +/* SGI used for SDEI signalling */ +#define ARM_SDEI_SGI ARM_IRQ_SEC_SGI_0 + +#if SDEI_IN_FCONF +/* ARM SDEI dynamic private event max count */ +#define ARM_SDEI_DP_EVENT_MAX_CNT U(3) + +/* ARM SDEI dynamic shared event max count */ +#define ARM_SDEI_DS_EVENT_MAX_CNT U(3) +#else +/* ARM SDEI dynamic private event numbers */ +#define ARM_SDEI_DP_EVENT_0 UL(1000) +#define ARM_SDEI_DP_EVENT_1 UL(1001) +#define ARM_SDEI_DP_EVENT_2 UL(1002) + +/* ARM SDEI dynamic shared event numbers */ +#define ARM_SDEI_DS_EVENT_0 UL(2000) +#define ARM_SDEI_DS_EVENT_1 UL(2001) +#define ARM_SDEI_DS_EVENT_2 UL(2002) + +#define ARM_SDEI_PRIVATE_EVENTS \ + SDEI_DEFINE_EVENT_0(ARM_SDEI_SGI), \ + SDEI_PRIVATE_EVENT(ARM_SDEI_DP_EVENT_0, \ + SDEI_DYN_IRQ, SDEI_MAPF_DYNAMIC), \ + SDEI_PRIVATE_EVENT(ARM_SDEI_DP_EVENT_1, \ + SDEI_DYN_IRQ, SDEI_MAPF_DYNAMIC), \ + SDEI_PRIVATE_EVENT(ARM_SDEI_DP_EVENT_2, SDEI_DYN_IRQ, SDEI_MAPF_DYNAMIC) + +#define ARM_SDEI_SHARED_EVENTS \ + SDEI_SHARED_EVENT(ARM_SDEI_DS_EVENT_0, \ + SDEI_DYN_IRQ, SDEI_MAPF_DYNAMIC), \ + SDEI_SHARED_EVENT(ARM_SDEI_DS_EVENT_1, \ + SDEI_DYN_IRQ, SDEI_MAPF_DYNAMIC), \ + SDEI_SHARED_EVENT(ARM_SDEI_DS_EVENT_2, \ + SDEI_DYN_IRQ, SDEI_MAPF_DYNAMIC) +#endif /* SDEI_IN_FCONF */ + +/******************************************************************************* + * SDS config + ******************************************************************************/ + +/* SDS ID for unusable CPU MPID list structure */ +#define SDS_ISOLATED_CPU_LIST_ID U(128) + +/* Index of SDS region used in the communication with SCP */ +#define SDS_SCP_AP_REGION_ID U(0) + +/******************************************************************************* + * SMMUv3 Config + ******************************************************************************/ + +/* SMMUv3 root offset register */ +#define PLAT_ARM_SMMUV3_ROOT_REG_OFFSET UL(0xA0000) + +/******************************************************************************* + * Platform type identification macro + ******************************************************************************/ + +/* Platform ID related accessors */ +#define BOARD_CSS_PLAT_ID_REG_ID_MASK U(0x0F) +#define BOARD_CSS_PLAT_ID_REG_ID_SHIFT U(0x00) +#define BOARD_CSS_PLAT_ID_REG_VERSION_MASK U(0xF00) +#define BOARD_CSS_PLAT_ID_REG_VERSION_SHIFT U(0x08) +#define BOARD_CSS_PLAT_TYPE_RTL U(0x00) +#define BOARD_CSS_PLAT_TYPE_FPGA U(0x01) +#define BOARD_CSS_PLAT_TYPE_EMULATOR U(0x02) +#define BOARD_CSS_PLAT_TYPE_FVP U(0x03) + +#ifndef __ASSEMBLER__ +#define BOARD_CSS_GET_PLAT_TYPE(addr) \ + ((mmio_read_32(addr) & BOARD_CSS_PLAT_ID_REG_ID_MASK) \ + >> BOARD_CSS_PLAT_ID_REG_ID_SHIFT) +#endif /* __ASSEMBLER__ */ + +/* Platform ID address */ +#define BOARD_CSS_PLAT_ID_REG_ADDR NRD_ROS_PLATFORM_PERIPH_BASE + \ + UL(0x00FE00E0) + +/******************************************************************************* + * Flash config + ******************************************************************************/ + +#define MAX_IO_DEVICES U(3) +#define MAX_IO_HANDLES U(4) + +#define V2M_SYS_LED U(0x8) + +#define V2M_SYS_LED_SS_SHIFT U(0) +#define V2M_SYS_LED_EL_SHIFT U(1) +#define V2M_SYS_LED_EC_SHIFT U(3) + +#define V2M_SYS_LED_SS_MASK U(0x01) +#define V2M_SYS_LED_EL_MASK U(0x03) +#define V2M_SYS_LED_EC_MASK U(0x1f) + +#define PLAT_ARM_MEM_PROTEC_VA_FRAME UL(0xC0000000) + +#define V2M_SYSREGS_BASE NRD_ROS_SYSTEM_PERIPH_BASE + \ + UL(0x00010000) +#define V2M_FLASH0_BASE NRD_ROS_SMC0_BASE +#define V2M_FLASH0_SIZE NRD_ROS_SMC0_SIZE +#define V2M_FLASH_BLOCK_SIZE UL(0x00040000) /* 256 KB */ + +#define PLAT_ARM_FLASH_IMAGE_BASE V2M_FLASH0_BASE +#define PLAT_ARM_FLASH_IMAGE_MAX_SIZE (V2M_FLASH0_SIZE - V2M_FLASH_BLOCK_SIZE) + +#define PLAT_ARM_MEM_PROT_ADDR (V2M_FLASH0_BASE + \ + V2M_FLASH0_SIZE - V2M_FLASH_BLOCK_SIZE) + +#define PLAT_ARM_NVM_BASE V2M_FLASH0_BASE +#define PLAT_ARM_NVM_SIZE (V2M_FLASH0_SIZE - V2M_FLASH_BLOCK_SIZE) + +/******************************************************************************* + * ROS peripheral config + ******************************************************************************/ + +/* Non-volatile counters */ +#define SOC_TRUSTED_NVCTR_BASE NRD_ROS_PLATFORM_PERIPH_BASE + \ + UL(0x00E70000) +#define TFW_NVCTR_BASE (SOC_TRUSTED_NVCTR_BASE + 0x0000) +#define TFW_NVCTR_SIZE U(4) +#define NTFW_CTR_BASE (SOC_TRUSTED_NVCTR_BASE + 0x0004) +#define NTFW_CTR_SIZE U(4) + +/******************************************************************************* + * SRAM layout + ******************************************************************************/ + +/* if !RESET_TO_BL31 + * Trusted SRAM + * 0x00100000 +--------------+ + * | L0 GPT | + * 0x000E0000 +--------------+ loaded by BL2 +----------------+ + * | BL1 (rw) | <<<<<<<<<<<<< | | + * |--------------| <<<<<<<<<<<<< | BL31 NOBITS | + * | BL2 | <<<<<<<<<<<<< | | + * |--------------| <<<<<<<<<<<<< |----------------| + * | | <<<<<<<<<<<<< | BL31 PROGBITS | + * | | +----------------+ + * +--------------+ + * | CONFIG | + * 0x0001A000 +--------------+ + * | Shared | + * 0x00019000 +--------------+ + * | BL1 (ro) | + * 0x00000000 +--------------+ + * + * else + * + * Trusted SRAM + * 0x00100000 +--------------+ + * | L0 GPT | + * 0x000E0000 +-------------- + * | | side-loaded +----------------+ + * | | <<<<<<<<<<<<< | | + * | | <<<<<<<<<<<<< | BL31 NOBITS | + * | | <<<<<<<<<<<<< | | + * | | <<<<<<<<<<<<< |----------------| + * | | <<<<<<<<<<<<< | BL31 PROGBITS | + * 0x00063000 | | +----------------+ + * 0x0001A000 +--------------+ + * | Shared | + * 0x00019000 +--------------+ + * | BL1 (ro) | + * 0x00000000 +--------------+ + * endif + */ + +/******************************************************************************* + * BL1 RO specifics + ******************************************************************************/ + +/* + * SRAM region to store BL1 code and RO. This has been carved out at the bottom + * of SRAM + */ + +#define BL1_RO_BASE NRD_CSS_BL1_RO_BASE +#define BL1_RO_LIMIT (NRD_CSS_BL1_RO_BASE \ + + NRD_CSS_BL1_RO_SIZE) + +/******************************************************************************* + * L0 GPT specifics + ******************************************************************************/ + +/* + * L0 GPT has to be aligned to its size. In order to avoid holes due to + * alignment, place L0 GPT at the top of SRAM. + */ +#define ARM_L0_GPT_SIZE UL(0x00020000) /* 128KB */ +#define ARM_L0_GPT_BASE NRD_CSS_SHARED_SRAM_SIZE - \ + ARM_L0_GPT_SIZE + +#define ARM_L0_GPT_LIMIT (ARM_L0_GPT_BASE + ARM_L0_GPT_SIZE) + +/******************************************************************************* + * Arm shared RAM specifics + ******************************************************************************/ + +#define ARM_SHARED_RAM_BASE (NRD_CSS_BL1_RO_BASE + \ + NRD_CSS_BL1_RO_SIZE) +#define ARM_SHARED_RAM_SIZE UL(0x00001000) /* 4 KB */ + +/******************************************************************************* + * Arm BL RAM specifics + ******************************************************************************/ + +/*Rest of SRAM till L0 GPT base */ +#define ARM_BL_RAM_BASE (ARM_SHARED_RAM_BASE + \ + ARM_SHARED_RAM_SIZE) +#define ARM_BL_RAM_SIZE (ARM_L0_GPT_BASE - \ + ARM_BL_RAM_BASE) + +/******************************************************************************* + * FW_CONFIG specifics + ******************************************************************************/ + +/* + * To enable FW_CONFIG to be loaded by BL1, define the corresponding base + * and limit. Leave enough space of BL2 meminfo. + */ +#define ARM_FW_CONFIG_BASE (ARM_BL_RAM_BASE + sizeof(meminfo_t)) +#define ARM_FW_CONFIG_LIMIT ((ARM_BL_RAM_BASE + PAGE_SIZE) \ + + (PAGE_SIZE / 2U)) + +/* + * Boot parameters passed from BL2 to BL31/BL32 are stored here + */ +#define ARM_BL2_MEM_DESC_BASE (ARM_FW_CONFIG_LIMIT) +#define ARM_BL2_MEM_DESC_LIMIT (ARM_BL2_MEM_DESC_BASE \ + + (PAGE_SIZE / 2U)) + +/* + * Define limit of firmware configuration memory: + * ARM_FW_CONFIG + ARM_BL2_MEM_DESC memory + */ +#define ARM_FW_CONFIGS_SIZE (PAGE_SIZE * 2) +#if RESET_TO_BL31 +#define ARM_FW_CONFIGS_LIMIT (ARM_BL_RAM_BASE) +#else +#define ARM_FW_CONFIGS_LIMIT (ARM_BL_RAM_BASE + ARM_FW_CONFIGS_SIZE) +#endif + +/******************************************************************************* + * BL1 RW specifics + ******************************************************************************/ + +#define BL1_RW_BASE (ARM_BL_RAM_BASE + \ + ARM_BL_RAM_SIZE - \ + PLAT_ARM_MAX_BL1_RW_SIZE) +#define BL1_RW_LIMIT (ARM_BL_RAM_BASE + \ + ARM_BL_RAM_SIZE) + +/******************************************************************************* + * BL2 specific defines. + ******************************************************************************/ + +/* Put BL2 just below BL1. */ +#define BL2_BASE (BL1_RW_BASE - PLAT_ARM_MAX_BL2_SIZE) +#define BL2_LIMIT BL1_RW_BASE + +/******************************************************************************* + * BL31 specific defines. + ******************************************************************************/ + +/* Keep BL31 below BL2 in the Trusted SRAM.*/ +#if RESET_TO_BL31 +#define BL31_BASE (0x63000) +#else +#define BL31_BASE ((ARM_BL_RAM_BASE + \ + ARM_BL_RAM_SIZE) - \ + PLAT_ARM_MAX_BL31_SIZE) +#endif +#define BL31_PROGBITS_LIMIT BL2_BASE +#define BL31_LIMIT (ARM_BL_RAM_BASE + ARM_BL_RAM_SIZE) + +/* + * The max number of regions like RO(code), coherent and data required by + * different BL stages which need to be mapped in the MMU. + */ +#define ARM_BL_REGIONS 7 + +#define MAX_MMAP_REGIONS (PLAT_ARM_MMAP_ENTRIES + \ + ARM_BL_REGIONS) + +/******************************************************************************* + * DRAM layout + ******************************************************************************/ + +/* + * The top 100MB of DRAM1 is configured as follows: + * - L1 GPT DRAM: Reserved for L1 GPT if RME is enabled + * - TF-A <-> RMM SHARED: Area shared for communication between TF-A and RMM + * - REALM DRAM: Reserved for Realm world if RME is enabled + * + * DRAM layout + * +------------------+ + * | REALM (RMM) | + * | (32MB - 4KB) | + * +------------------+ + * | | + * | TF-A <-> RMM | + * | SHARED (4KB) | + * +------------------+ + * | L1 GPT | + * | | + * DRAM1 End +------------------+ + */ + +/* Number of DRAM banks */ +#if (NRD_PLATFORM_VARIANT == 2) +#define ARM_DRAM_NUM_BANKS U(8) +#else +#define ARM_DRAM_NUM_BANKS U(2) +#endif + +/******************************************************************************* + * DRAM bank1 specific defines. + ******************************************************************************/ + +/* Bank-1 DRAM */ +#define ARM_DRAM1_BASE UL(0x80000000) +#define ARM_DRAM1_SIZE UL(0x80000000) +#define ARM_DRAM1_END (ARM_DRAM1_BASE + \ + ARM_DRAM1_SIZE - 1U) + +/******************************************************************************* + * DRAM bank2 specific defines. + ******************************************************************************/ + +/* Bank-2 DRAM */ +#define ARM_DRAM2_BASE PLAT_ARM_DRAM2_BASE +#define ARM_DRAM2_SIZE PLAT_ARM_DRAM2_SIZE +#define ARM_DRAM2_END (ARM_DRAM2_BASE + \ + ARM_DRAM2_SIZE - 1U) + +/******************************************************************************* + * L1GPT specific defines. + ******************************************************************************/ + +/* 2MB per L1 entry, PPS - 48 bits, PGS - 4KB, L0GPTSZ - 16GB */ +#define ARM_L1_GPT_SIZE (UL(40 * 1024 * 1024) + \ + ((NRD_CHIP_COUNT - 1) * \ + (4 * 1024 * 1024))) + +#define ARM_L1_GPT_BASE (ARM_DRAM1_BASE + \ + ARM_DRAM1_SIZE - \ + ARM_L1_GPT_SIZE) +#define ARM_L1_GPT_END (ARM_L1_GPT_BASE + \ + ARM_L1_GPT_SIZE - 1U) + +/******************************************************************************* + * "RMM TF-A shared region" specific defines. + ******************************************************************************/ + +/* PLAT_ARM_EL3_RMM_SHARED_SIZE */ +#define ARM_EL3_RMM_SHARED_SIZE (PAGE_SIZE) /* 4KB */ + +#define ARM_EL3_RMM_SHARED_BASE (ARM_L1_GPT_BASE - \ + ARM_EL3_RMM_SHARED_SIZE) + +#define ARM_EL3_RMM_SHARED_END (ARM_EL3_RMM_SHARED_BASE + \ + ARM_EL3_RMM_SHARED_SIZE - 1U) + +/******************************************************************************* + * RMM specific defines. + ******************************************************************************/ + +/* ARM_REALM_SIZE */ +#define ARM_REALM_SIZE (UL(0x02600000) - \ + ARM_EL3_RMM_SHARED_SIZE) +#define ARM_REALM_BASE (ARM_EL3_RMM_SHARED_BASE - \ + ARM_REALM_SIZE) + +#define ARM_REALM_END (ARM_REALM_BASE + ARM_REALM_SIZE - 1U) + +#define RMM_BASE (ARM_REALM_BASE) +#define RMM_LIMIT (RMM_BASE + ARM_REALM_SIZE) +#define RMM_SHARED_BASE (ARM_EL3_RMM_SHARED_BASE) +#define RMM_SHARED_SIZE (ARM_EL3_RMM_SHARED_SIZE) + +/******************************************************************************* + * NRD_CSS_CARVEOUT_RESERVED region specific defines. + ******************************************************************************/ + +#define NRD_CSS_CARVEOUT_RESERVED_BASE (ARM_DRAM1_BASE + \ + ARM_DRAM1_SIZE - \ + NRD_CSS_DRAM1_CARVEOUT_SIZE) +#define NRD_CSS_CARVEOUT_RESERVED_SIZE (NRD_CSS_DRAM1_CARVEOUT_SIZE - \ + (ARM_EL3_RMM_SHARED_SIZE + \ + ARM_REALM_SIZE + \ + ARM_L1_GPT_SIZE)) + +#define NRD_CSS_CARVEOUT_RESERVED_END (NRD_CSS_CARVEOUT_RESERVED_BASE +\ + NRD_CSS_CARVEOUT_RESERVED_SIZE - 1U) + +/******************************************************************************* + * NS RAM specific defines specific defines. + ******************************************************************************/ + +#define ARM_NS_DRAM1_BASE ARM_DRAM1_BASE +#define ARM_NS_DRAM1_SIZE (ARM_DRAM1_SIZE - \ + NRD_CSS_DRAM1_CARVEOUT_SIZE) + +#define ARM_NS_DRAM1_END (ARM_NS_DRAM1_BASE + \ + ARM_NS_DRAM1_SIZE - 1U) + +/******************************************************************************* + * MMU mapping + ******************************************************************************/ + +#define V2M_MAP_FLASH0_RW \ + MAP_REGION_FLAT( \ + V2M_FLASH0_BASE, \ + V2M_FLASH0_SIZE, \ + MT_DEVICE | MT_RW | EL3_PAS) + +#define V2M_MAP_FLASH0_RO \ + MAP_REGION_FLAT( \ + V2M_FLASH0_BASE, \ + V2M_FLASH0_SIZE, \ + MT_RO_DATA | EL3_PAS) + +#define ARM_MAP_L0_GPT_REGION \ + MAP_REGION_FLAT( \ + ARM_L0_GPT_BASE, \ + ARM_L0_GPT_SIZE, \ + MT_MEMORY | MT_RW | MT_ROOT) + +#define ARM_MAP_BL_CONFIG_REGION \ + MAP_REGION_FLAT( \ + ARM_BL_RAM_BASE, \ + (ARM_FW_CONFIGS_LIMIT - ARM_BL_RAM_BASE), \ + MT_MEMORY | MT_RW | EL3_PAS) + +#if SEPARATE_CODE_AND_RODATA +#define ARM_MAP_BL_RO \ + MAP_REGION_FLAT( \ + BL_CODE_BASE, \ + (BL_CODE_END - BL_CODE_BASE), \ + MT_CODE | EL3_PAS), \ + MAP_REGION_FLAT( \ + BL_RO_DATA_BASE, \ + (BL_RO_DATA_END - BL_RO_DATA_BASE), \ + MT_RO_DATA | EL3_PAS) +#else +#define ARM_MAP_BL_RO \ + MAP_REGION_FLAT( \ + BL_CODE_BASE, \ + (BL_CODE_END - BL_CODE_BASE), \ + MT_CODE | EL3_PAS) +#endif + +#if USE_COHERENT_MEM +#define ARM_MAP_BL_COHERENT_RAM \ + MAP_REGION_FLAT( \ + BL_COHERENT_RAM_BASE, \ + BL_COHERENT_RAM_END - BL_COHERENT_RAM_BASE, \ + MT_DEVICE | MT_RW | EL3_PAS) +#endif + +#define ARM_MAP_DRAM2 \ + MAP_REGION_FLAT( \ + ARM_DRAM2_BASE, \ + ARM_DRAM2_SIZE, \ + MT_MEMORY | MT_RW | MT_NS) + +#endif /* NRD_PLAT_ARM_DEF3_H */ diff --git a/plat/arm/board/neoverse_rd/common/include/nrd3/nrd_ros_def3.h b/plat/arm/board/neoverse_rd/common/include/nrd3/nrd_ros_def3.h new file mode 100644 index 00000000..c9876217 --- /dev/null +++ b/plat/arm/board/neoverse_rd/common/include/nrd3/nrd_ros_def3.h @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2024, Arm Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + * + * This file contains the RoS specific definitions for the third generation of + * platforms. + */ + +#ifndef NRD_ROS_DEF3_H +#define NRD_ROS_DEF3_H + +/******************************************************************************* + * RoS memory map related defines + ******************************************************************************/ + +/* System peripherals */ +#define NRD_ROS_SYSTEM_PERIPH_BASE UL(0x0C000000) +#define NRD_ROS_SYSTEM_PERIPH_SIZE UL(0x02000000) + +/* Platform peripherals */ +#define NRD_ROS_PLATFORM_PERIPH_BASE UL(0x0E000000) +#define NRD_ROS_PLATFORM_PERIPH_SIZE UL(0x02000000) + +/* SMC0 */ +#define NRD_ROS_SMC0_BASE UL(0x08000000) +#define NRD_ROS_SMC0_SIZE UL(0x04000000) + +#endif /* NRD_ROS_DEF3_H */ diff --git a/plat/arm/board/neoverse_rd/common/include/nrd3/nrd_ros_fw_def3.h b/plat/arm/board/neoverse_rd/common/include/nrd3/nrd_ros_fw_def3.h new file mode 100644 index 00000000..c0fde85d --- /dev/null +++ b/plat/arm/board/neoverse_rd/common/include/nrd3/nrd_ros_fw_def3.h @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2024, Arm Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + * + * This file is limited to include the RoS firmware specific definitions for the + * third generation of platforms. RoS (Rest Of System) is used to refer to the + * part of the reference design platform that excludes CSS. + */ + +#ifndef NRD_ROS_FW_DEF3_H +#define NRD_ROS_FW_DEF3_H + +#include <nrd_ros_def3.h> + +/******************************************************************************* + * MMU mapping + ******************************************************************************/ + +#define NRD_ROS_PLATFORM_PERIPH_MMAP \ + MAP_REGION_FLAT( \ + NRD_ROS_PLATFORM_PERIPH_BASE, \ + NRD_ROS_PLATFORM_PERIPH_SIZE, \ + MT_DEVICE | MT_RW | MT_SECURE) + +#define NRD_ROS_SYSTEM_PERIPH_MMAP \ + MAP_REGION_FLAT( \ + NRD_ROS_SYSTEM_PERIPH_BASE, \ + NRD_ROS_SYSTEM_PERIPH_SIZE, \ + MT_DEVICE | MT_RW | MT_SECURE) + +#define NRD_ROS_V2M_MEM_PROTECT_MMAP \ + MAP_REGION_FLAT( \ + PLAT_ARM_MEM_PROT_ADDR, \ + V2M_FLASH_BLOCK_SIZE, \ + MT_DEVICE | MT_RW | EL3_PAS) + +#define NRD_ROS_FLASH0_RO_MMAP \ + MAP_REGION_FLAT( \ + V2M_FLASH0_BASE, \ + V2M_FLASH0_SIZE, \ + MT_DEVICE | MT_RO | MT_SECURE) + +#endif /* NRD_ROS_FW_DEF3_H */ diff --git a/plat/arm/css/sgi/include/sgi_dmc620_tzc_regions.h b/plat/arm/board/neoverse_rd/common/include/nrd_dmc620_tzc_regions.h similarity index 72% rename from plat/arm/css/sgi/include/sgi_dmc620_tzc_regions.h rename to plat/arm/board/neoverse_rd/common/include/nrd_dmc620_tzc_regions.h index e9391631..c63d7505 100644 --- a/plat/arm/css/sgi/include/sgi_dmc620_tzc_regions.h +++ b/plat/arm/board/neoverse_rd/common/include/nrd_dmc620_tzc_regions.h @@ -1,16 +1,16 @@ /* - * Copyright (c) 2021, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2021-2024, Arm Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ -#ifndef SGI_DMC620_TZC_REGIONS_H -#define SGI_DMC620_TZC_REGIONS_H +#ifndef NRD_DMC620_TZC_REGIONS_H +#define NRD_DMC620_TZC_REGIONS_H #include <drivers/arm/tzc_dmc620.h> #if SPM_MM -#define CSS_SGI_DMC620_TZC_REGIONS_DEF \ +#define NRD_DMC620_TZC_REGIONS_DEF \ { \ .region_base = ARM_AP_TZC_DRAM1_BASE, \ .region_top = PLAT_SP_IMAGE_NS_BUF_BASE - 1, \ @@ -25,7 +25,7 @@ .sec_attr = TZC_DMC620_REGION_S_RDWR \ } #else -#define CSS_SGI_DMC620_TZC_REGIONS_DEF \ +#define NRD_DMC620_TZC_REGIONS_DEF \ { \ .region_base = ARM_AP_TZC_DRAM1_BASE, \ .region_top = ARM_AP_TZC_DRAM1_END, \ @@ -33,4 +33,4 @@ } #endif /* SPM_MM */ -#endif /* SGI_DMC620_TZC_REGIONS_H */ +#endif /* NRD_DMC620_TZC_REGIONS_H */ diff --git a/plat/arm/board/neoverse_rd/common/include/nrd_plat.h b/plat/arm/board/neoverse_rd/common/include/nrd_plat.h new file mode 100644 index 00000000..775f233a --- /dev/null +++ b/plat/arm/board/neoverse_rd/common/include/nrd_plat.h @@ -0,0 +1,13 @@ +/* + * Copyright (c) 2020-2024, Arm Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef NRD_PLAT_H +#define NRD_PLAT_H + +/* BL31 platform setup common to all Neoverse RD platforms */ +void nrd_bl31_common_platform_setup(void); + +#endif /* NRD_PLAT_H */ diff --git a/plat/arm/css/sgi/include/sgi_ras.h b/plat/arm/board/neoverse_rd/common/include/nrd_ras.h similarity index 66% rename from plat/arm/css/sgi/include/sgi_ras.h rename to plat/arm/board/neoverse_rd/common/include/nrd_ras.h index d311807e..768689c0 100644 --- a/plat/arm/css/sgi/include/sgi_ras.h +++ b/plat/arm/board/neoverse_rd/common/include/nrd_ras.h @@ -1,22 +1,22 @@ /* - * Copyright (c) 2018-2023, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2018-2024, Arm Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ -#ifndef SGI_RAS_H -#define SGI_RAS_H +#ifndef NRD_RAS_H +#define NRD_RAS_H #include <lib/extensions/ras.h> #include <plat/common/platform.h> /* * Interrupt type supported. - * - SGI_RAS_INTR_TYPE_SPI: Denotes a SPI interrupt - * - SGI_RAS_INTR_TYPE_PPI: Denotes a PPI interrupt + * - NRD_RAS_INTR_TYPE_SPI: Denotes a SPI interrupt + * - NRD_RAS_INTR_TYPE_PPI: Denotes a PPI interrupt */ -#define SGI_RAS_INTR_TYPE_SPI 0 -#define SGI_RAS_INTR_TYPE_PPI 1 +#define NRD_RAS_INTR_TYPE_SPI 0 +#define NRD_RAS_INTR_TYPE_PPI 1 /* * MM Communicate information structure. Required to generate MM Communicate @@ -29,15 +29,15 @@ typedef struct mm_communicate_header { } mm_communicate_header_t; /* RAS error info data structure. */ -struct sgi_ras_ev_map { +struct nrd_ras_ev_map { int sdei_ev_num; /* SDEI Event number */ int intr; /* Physical intr number */ int intr_type; /* Interrupt Type (SPI or PPI)*/ }; /* RAS config data structure. Must be defined by each platform. */ -struct plat_sgi_ras_config { - struct sgi_ras_ev_map *ev_map; +struct plat_nrd_ras_config { + struct nrd_ras_ev_map *ev_map; int ev_map_size; }; @@ -45,7 +45,7 @@ struct plat_sgi_ras_config { * Find event map for a given interrupt number. On success, returns pointer * to the event map. On error, returns NULL. */ -struct sgi_ras_ev_map *sgi_find_ras_event_map_by_intr(uint32_t intr_num); +struct nrd_ras_ev_map *nrd_find_ras_event_map_by_intr(uint32_t intr_num); /* * Initialization function for the framework. @@ -53,16 +53,16 @@ struct sgi_ras_ev_map *sgi_find_ras_event_map_by_intr(uint32_t intr_num); * Registers RAS config provided by the platform and then configures and * enables interrupt for each registered error. On success, return 0. */ -int sgi_ras_platform_setup(struct plat_sgi_ras_config *config); +int nrd_ras_platform_setup(struct plat_nrd_ras_config *config); /* Base element RAM RAS interrupt handler function. */ -int sgi_ras_sram_intr_handler(const struct err_record_info *err_rec, +int nrd_ras_sram_intr_handler(const struct err_record_info *err_rec, int probe_data, const struct err_handler_data *const data); /* CPU RAS interrupt handler */ -int sgi_ras_cpu_intr_handler(const struct err_record_info *err_rec, +int nrd_ras_cpu_intr_handler(const struct err_record_info *err_rec, int probe_data, const struct err_handler_data *const data); -#endif /* SGI_RAS_H */ +#endif /* NRD_RAS_H */ diff --git a/plat/arm/board/neoverse_rd/common/include/nrd_sdei.h b/plat/arm/board/neoverse_rd/common/include/nrd_sdei.h new file mode 100644 index 00000000..f1b60154 --- /dev/null +++ b/plat/arm/board/neoverse_rd/common/include/nrd_sdei.h @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2021-2024, Arm Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef NRD_SDEI_H +#define NRD_SDEI_H + +#if SDEI_SUPPORT + +/* ARM SDEI dynamic shared event numbers */ +#define NRD_SDEI_DS_EVENT_0 U(804) +#define NRD_SDEI_DS_EVENT_1 U(805) + +#define PLAT_ARM_PRIVATE_SDEI_EVENTS \ + SDEI_DEFINE_EVENT_0(ARM_SDEI_SGI), \ + SDEI_EXPLICIT_EVENT(NRD_SDEI_DS_EVENT_0, SDEI_MAPF_CRITICAL), \ + SDEI_EXPLICIT_EVENT(NRD_SDEI_DS_EVENT_1, SDEI_MAPF_CRITICAL), + +#define PLAT_ARM_SHARED_SDEI_EVENTS + +#endif /* SDEI_SUPPORT */ + +#endif /* NRD_SDEI_H */ diff --git a/plat/arm/css/sgi/include/sgi_variant.h b/plat/arm/board/neoverse_rd/common/include/nrd_variant.h similarity index 60% rename from plat/arm/css/sgi/include/sgi_variant.h rename to plat/arm/board/neoverse_rd/common/include/nrd_variant.h index 8f9529aa..86d82e27 100644 --- a/plat/arm/css/sgi/include/sgi_variant.h +++ b/plat/arm/board/neoverse_rd/common/include/nrd_variant.h @@ -1,11 +1,11 @@ /* - * Copyright (c) 2018-2023, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2018-2024, Arm Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ -#ifndef SGI_VARIANT_H -#define SGI_VARIANT_H +#ifndef NRD_VARIANT_H +#define NRD_VARIANT_H /* SSC_VERSION values for SGI575 */ #define SGI575_SSC_VER_PART_NUM 0x0783 @@ -28,23 +28,31 @@ #define RD_V2_SID_VER_PART_NUM 0x07F2 #define RD_V2_CONFIG_ID 0x1 -/* Structure containing SGI platform variant information */ -typedef struct sgi_platform_info { +/* SID Version values for RD-V3 */ +#define RD_V3_SID_VER_PART_NUM 0x07EE +#define RD_V3_CONFIG_ID 0x0 + +/* SID Version values for RD-V3 variants */ +#define RD_V3_CFG1_SID_VER_PART_NUM 0x07F9 +#define RD_V3_CFG2_SID_VER_PART_NUM 0x07EE + +/* Structure containing Neoverse RD platform variant information */ +typedef struct nrd_platform_info { unsigned int platform_id; /* Part Number of the platform */ unsigned int config_id; /* Config Id of the platform */ unsigned int chip_id; /* Chip Id or Node number */ unsigned int multi_chip_mode; /* Multi-chip mode availability */ -} sgi_platform_info_t; +} nrd_platform_info_t; -extern sgi_platform_info_t sgi_plat_info; +extern nrd_platform_info_t nrd_plat_info; /* returns the part number of the platform*/ -unsigned int plat_arm_sgi_get_platform_id(void); +unsigned int plat_arm_nrd_get_platform_id(void); /* returns the configuration id of the platform */ -unsigned int plat_arm_sgi_get_config_id(void); +unsigned int plat_arm_nrd_get_config_id(void); /* returns true if operating in multi-chip configuration */ -unsigned int plat_arm_sgi_get_multi_chip_mode(void); +unsigned int plat_arm_nrd_get_multi_chip_mode(void); -#endif /* SGI_VARIANT_H */ +#endif /* NRD_VARIANT_H */ diff --git a/plat/arm/css/sgi/include/plat_macros.S b/plat/arm/board/neoverse_rd/common/include/plat_macros.S similarity index 86% rename from plat/arm/css/sgi/include/plat_macros.S rename to plat/arm/board/neoverse_rd/common/include/plat_macros.S index 521bcc32..df7cfb63 100644 --- a/plat/arm/css/sgi/include/plat_macros.S +++ b/plat/arm/board/neoverse_rd/common/include/plat_macros.S @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2018-2024, Arm Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ diff --git a/plat/arm/css/sgi/sgi-common.mk b/plat/arm/board/neoverse_rd/common/nrd-common.mk similarity index 61% rename from plat/arm/css/sgi/sgi-common.mk rename to plat/arm/board/neoverse_rd/common/nrd-common.mk index 2cd70342..a09f369c 100644 --- a/plat/arm/css/sgi/sgi-common.mk +++ b/plat/arm/board/neoverse_rd/common/nrd-common.mk @@ -1,12 +1,12 @@ # -# Copyright (c) 2018-2022, Arm Limited and Contributors. All rights reserved. +# Copyright (c) 2018-2024, Arm Limited and Contributors. All rights reserved. # # SPDX-License-Identifier: BSD-3-Clause # CSS_USE_SCMI_SDS_DRIVER := 1 -CSS_ENT_BASE := plat/arm/css/sgi +NRD_COMMON_BASE := plat/arm/board/neoverse_rd/common ENABLE_FEAT_RAS := 1 @@ -16,18 +16,18 @@ EL3_EXCEPTION_HANDLING := 0 HANDLE_EA_EL3_FIRST_NS := 0 -CSS_SGI_CHIP_COUNT := 1 +NRD_CHIP_COUNT := 1 -CSS_SGI_PLATFORM_VARIANT := 0 +NRD_PLATFORM_VARIANT := 0 # Do not enable SVE ENABLE_SVE_FOR_NS := 0 CTX_INCLUDE_FPREGS := 1 -INTERCONNECT_SOURCES := ${CSS_ENT_BASE}/sgi_interconnect.c +INTERCONNECT_SOURCES := ${NRD_COMMON_BASE}/nrd_interconnect.c -PLAT_INCLUDES += -I${CSS_ENT_BASE}/include +PLAT_INCLUDES += -I${NRD_COMMON_BASE}/include # GIC-600 configuration GICV3_SUPPORT_GIC600 := 1 @@ -39,29 +39,24 @@ ENT_GIC_SOURCES := ${GICV3_SOURCES} \ plat/common/plat_gicv3.c \ plat/arm/common/arm_gicv3.c -PLAT_BL_COMMON_SOURCES += ${CSS_ENT_BASE}/aarch64/sgi_helper.S +PLAT_BL_COMMON_SOURCES += ${NRD_COMMON_BASE}/arch/aarch64/nrd_helper.S BL1_SOURCES += ${INTERCONNECT_SOURCES} \ + ${NRD_COMMON_BASE}/nrd_bl1_setup.c \ drivers/arm/sbsa/sbsa.c -BL2_SOURCES += ${CSS_ENT_BASE}/sgi_image_load.c \ +BL2_SOURCES += ${NRD_COMMON_BASE}/nrd_image_load.c \ drivers/arm/css/sds/sds.c BL31_SOURCES += ${INTERCONNECT_SOURCES} \ ${ENT_GIC_SOURCES} \ - ${CSS_ENT_BASE}/sgi_bl31_setup.c \ - ${CSS_ENT_BASE}/sgi_topology.c + ${NRD_COMMON_BASE}/nrd_bl31_setup.c \ + ${NRD_COMMON_BASE}/nrd_topology.c \ + drivers/delay_timer/generic_delay_timer.c -ifneq (${RESET_TO_BL31},0) - $(error "Using BL31 as the reset vector is not supported on ${PLAT} platform. \ - Please set RESET_TO_BL31 to 0.") -endif +$(eval $(call add_define,NRD_CHIP_COUNT)) -$(eval $(call add_define,SGI_PLAT)) - -$(eval $(call add_define,CSS_SGI_CHIP_COUNT)) - -$(eval $(call add_define,CSS_SGI_PLATFORM_VARIANT)) +$(eval $(call add_define,NRD_PLATFORM_VARIANT)) override CSS_LOAD_SCP_IMAGES := 0 override NEED_BL2U := no @@ -78,5 +73,4 @@ USE_COHERENT_MEM := 0 include plat/arm/common/arm_common.mk include plat/arm/css/common/css_common.mk -include plat/arm/soc/common/soc_css.mk include plat/arm/board/common/board_common.mk diff --git a/plat/arm/board/neoverse_rd/common/nrd_bl1_setup.c b/plat/arm/board/neoverse_rd/common/nrd_bl1_setup.c new file mode 100644 index 00000000..a0f656f4 --- /dev/null +++ b/plat/arm/board/neoverse_rd/common/nrd_bl1_setup.c @@ -0,0 +1,19 @@ +/* + * Copyright (c) 2024, Arm Limited. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <plat/arm/soc/common/soc_css.h> + +/******************************************************************************* + * Perform any BL1 specific platform actions. + ******************************************************************************/ + +void soc_css_init_nic400(void) +{ +} + +void soc_css_init_pcie(void) +{ +} diff --git a/plat/arm/board/neoverse_rd/common/nrd_bl31_setup.c b/plat/arm/board/neoverse_rd/common/nrd_bl31_setup.c new file mode 100644 index 00000000..bce88347 --- /dev/null +++ b/plat/arm/board/neoverse_rd/common/nrd_bl31_setup.c @@ -0,0 +1,259 @@ +/* + * Copyright (c) 2018-2024, Arm Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <assert.h> + +#include <libfdt.h> + +#include <common/bl_common.h> +#include <common/debug.h> +#include <drivers/arm/css/css_mhu_doorbell.h> +#include <drivers/arm/css/scmi.h> +#include <drivers/generic_delay_timer.h> +#include <plat/arm/common/plat_arm.h> +#include <plat/arm/css/common/css_pm.h> +#include <plat/common/platform.h> + +#include <nrd_ras.h> +#include <nrd_variant.h> + +nrd_platform_info_t nrd_plat_info; + +static scmi_channel_plat_info_t sgi575_scmi_plat_info = { + .scmi_mbx_mem = CSS_SCMI_PAYLOAD_BASE, + .db_reg_addr = PLAT_CSS_MHU_BASE + CSS_SCMI_MHU_DB_REG_OFF, + .db_preserve_mask = 0xfffffffe, + .db_modify_mask = 0x1, + .ring_doorbell = &mhu_ring_doorbell, +}; + +static scmi_channel_plat_info_t plat_rd_scmi_info[] = { + { + .scmi_mbx_mem = CSS_SCMI_PAYLOAD_BASE, + .db_reg_addr = PLAT_CSS_MHU_BASE + SENDER_REG_SET(0), + .db_preserve_mask = 0xfffffffe, + .db_modify_mask = 0x1, + .ring_doorbell = &mhuv2_ring_doorbell, + }, + #if (NRD_CHIP_COUNT > 1) + { + .scmi_mbx_mem = CSS_SCMI_PAYLOAD_BASE + + NRD_REMOTE_CHIP_MEM_OFFSET(1), + .db_reg_addr = PLAT_CSS_MHU_BASE + + NRD_REMOTE_CHIP_MEM_OFFSET(1) + SENDER_REG_SET(0), + .db_preserve_mask = 0xfffffffe, + .db_modify_mask = 0x1, + .ring_doorbell = &mhuv2_ring_doorbell, + }, + #endif + #if (NRD_CHIP_COUNT > 2) + { + .scmi_mbx_mem = CSS_SCMI_PAYLOAD_BASE + + NRD_REMOTE_CHIP_MEM_OFFSET(2), + .db_reg_addr = PLAT_CSS_MHU_BASE + + NRD_REMOTE_CHIP_MEM_OFFSET(2) + SENDER_REG_SET(0), + .db_preserve_mask = 0xfffffffe, + .db_modify_mask = 0x1, + .ring_doorbell = &mhuv2_ring_doorbell, + }, + #endif + #if (NRD_CHIP_COUNT > 3) + { + .scmi_mbx_mem = CSS_SCMI_PAYLOAD_BASE + + NRD_REMOTE_CHIP_MEM_OFFSET(3), + .db_reg_addr = PLAT_CSS_MHU_BASE + + NRD_REMOTE_CHIP_MEM_OFFSET(3) + SENDER_REG_SET(0), + .db_preserve_mask = 0xfffffffe, + .db_modify_mask = 0x1, + .ring_doorbell = &mhuv2_ring_doorbell, + }, + #endif +}; + +static scmi_channel_plat_info_t plat3_rd_scmi_info[] = { + { + .scmi_mbx_mem = CSS_SCMI_PAYLOAD_BASE, + .db_reg_addr = PLAT_CSS_MHU_BASE + MHU_V3_SENDER_REG_SET(0), + .db_preserve_mask = 0xfffffffe, + .db_modify_mask = 0x1, + .ring_doorbell = &mhu_ring_doorbell, + }, + #if (NRD_CHIP_COUNT > 1) + { + .scmi_mbx_mem = CSS_SCMI_PAYLOAD_BASE + + NRD_REMOTE_CHIP_MEM_OFFSET(1), + .db_reg_addr = PLAT_CSS_MHU_BASE + + NRD_REMOTE_CHIP_MEM_OFFSET(1) + + MHU_V3_SENDER_REG_SET(0), + .db_preserve_mask = 0xfffffffe, + .db_modify_mask = 0x1, + .ring_doorbell = &mhu_ring_doorbell, + }, + #endif + #if (NRD_CHIP_COUNT > 2) + { + .scmi_mbx_mem = CSS_SCMI_PAYLOAD_BASE + + NRD_REMOTE_CHIP_MEM_OFFSET(2), + .db_reg_addr = PLAT_CSS_MHU_BASE + + NRD_REMOTE_CHIP_MEM_OFFSET(2) + + MHU_V3_SENDER_REG_SET(0), + .db_preserve_mask = 0xfffffffe, + .db_modify_mask = 0x1, + .ring_doorbell = &mhu_ring_doorbell, + }, + #endif + #if (NRD_CHIP_COUNT > 3) + { + .scmi_mbx_mem = CSS_SCMI_PAYLOAD_BASE + + NRD_REMOTE_CHIP_MEM_OFFSET(3), + .db_reg_addr = PLAT_CSS_MHU_BASE + + NRD_REMOTE_CHIP_MEM_OFFSET(3) + + MHU_V3_SENDER_REG_SET(0), + .db_preserve_mask = 0xfffffffe, + .db_modify_mask = 0x1, + .ring_doorbell = &mhu_ring_doorbell, + }, + #endif +}; + +scmi_channel_plat_info_t *plat_css_get_scmi_info(unsigned int channel_id) +{ + if (nrd_plat_info.platform_id == RD_N1E1_EDGE_SID_VER_PART_NUM || + nrd_plat_info.platform_id == RD_V1_SID_VER_PART_NUM || + nrd_plat_info.platform_id == RD_N2_SID_VER_PART_NUM || + nrd_plat_info.platform_id == RD_V2_SID_VER_PART_NUM || + nrd_plat_info.platform_id == RD_N2_CFG1_SID_VER_PART_NUM || + nrd_plat_info.platform_id == RD_N2_CFG3_SID_VER_PART_NUM) { + if (channel_id >= ARRAY_SIZE(plat_rd_scmi_info)) { + panic(); + } + return &plat_rd_scmi_info[channel_id]; + } else if (nrd_plat_info.platform_id == RD_V3_SID_VER_PART_NUM || + nrd_plat_info.platform_id == RD_V3_CFG1_SID_VER_PART_NUM || + nrd_plat_info.platform_id == RD_V3_CFG2_SID_VER_PART_NUM) { + if (channel_id >= ARRAY_SIZE(plat3_rd_scmi_info)) { + panic(); + } + return &plat3_rd_scmi_info[channel_id]; + } else if (nrd_plat_info.platform_id == SGI575_SSC_VER_PART_NUM) { + return &sgi575_scmi_plat_info; + } else { + panic(); + } +} + +void bl31_early_platform_setup2(u_register_t arg0, u_register_t arg1, + u_register_t arg2, u_register_t arg3) +{ + nrd_plat_info.platform_id = plat_arm_nrd_get_platform_id(); + nrd_plat_info.config_id = plat_arm_nrd_get_config_id(); + nrd_plat_info.multi_chip_mode = plat_arm_nrd_get_multi_chip_mode(); + + arm_bl31_early_platform_setup((void *)arg0, arg1, arg2, (void *)arg3); +} + +/******************************************************************************* + * This function inserts platform information via device tree nodes as, + * system-id { + * platform-id = <0>; + * config-id = <0>; + * } + ******************************************************************************/ +#if RESET_TO_BL31 +static int append_config_node(uintptr_t fdt_base_addr, uintptr_t fdt_base_size) +{ + void *fdt; + int nodeoffset, err; + unsigned int platid = 0, platcfg = 0; + + if (fdt_base_addr == 0) { + ERROR("NT_FW CONFIG base address is NULL\n"); + return -1; + } + + fdt = (void *)fdt_base_addr; + + /* Check the validity of the fdt */ + if (fdt_check_header(fdt) != 0) { + ERROR("Invalid NT_FW_CONFIG DTB passed\n"); + return -1; + } + + nodeoffset = fdt_subnode_offset(fdt, 0, "system-id"); + if (nodeoffset < 0) { + ERROR("Failed to get system-id node offset\n"); + return -1; + } + + platid = plat_arm_nrd_get_platform_id(); + err = fdt_setprop_u32(fdt, nodeoffset, "platform-id", platid); + if (err < 0) { + ERROR("Failed to set platform-id\n"); + return -1; + } + + platcfg = plat_arm_nrd_get_config_id(); + err = fdt_setprop_u32(fdt, nodeoffset, "config-id", platcfg); + if (err < 0) { + ERROR("Failed to set config-id\n"); + return -1; + } + + platcfg = plat_arm_nrd_get_multi_chip_mode(); + err = fdt_setprop_u32(fdt, nodeoffset, "multi-chip-mode", platcfg); + if (err < 0) { + ERROR("Failed to set multi-chip-mode\n"); + return -1; + } + + flush_dcache_range((uintptr_t)fdt, fdt_base_size); + return 0; +} +#endif + +void nrd_bl31_common_platform_setup(void) +{ + generic_delay_timer_init(); + + arm_bl31_platform_setup(); + + /* Configure the warm reboot SGI for primary core */ + css_setup_cpu_pwr_down_intr(); + +#if CSS_SYSTEM_GRACEFUL_RESET + /* Register priority level handlers for reboot */ + ehf_register_priority_handler(PLAT_REBOOT_PRI, + css_reboot_interrupt_handler); +#endif + +#if RESET_TO_BL31 + int ret = append_config_node(NRD_CSS_BL31_PRELOAD_DTB_BASE, + NRD_CSS_BL31_PRELOAD_DTB_SIZE); + + if (ret != 0) { + panic(); + } +#endif +} + +const plat_psci_ops_t *plat_arm_psci_override_pm_ops(plat_psci_ops_t *ops) +{ + /* + * For RD-E1-Edge, only CPU power ON/OFF, PSCI platform callbacks are + * supported. + */ + if (((nrd_plat_info.platform_id == RD_N1E1_EDGE_SID_VER_PART_NUM) && + (nrd_plat_info.config_id == RD_E1_EDGE_CONFIG_ID))) { + ops->cpu_standby = NULL; + ops->system_off = NULL; + ops->system_reset = NULL; + ops->get_sys_suspend_power_state = NULL; + ops->pwr_domain_suspend = NULL; + ops->pwr_domain_suspend_finish = NULL; + } + + return css_scmi_override_pm_ops(ops); +} diff --git a/plat/arm/css/sgi/sgi_image_load.c b/plat/arm/board/neoverse_rd/common/nrd_image_load.c similarity index 84% rename from plat/arm/css/sgi/sgi_image_load.c rename to plat/arm/board/neoverse_rd/common/nrd_image_load.c index ac4bfd29..15d90beb 100644 --- a/plat/arm/css/sgi/sgi_image_load.c +++ b/plat/arm/board/neoverse_rd/common/nrd_image_load.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018-2022, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2018-2024, Arm Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -12,10 +12,9 @@ #include <drivers/arm/css/sds.h> #include <plat/arm/common/plat_arm.h> #include <plat/common/platform.h> - #include <platform_def.h> -#include <sgi_base_platform_def.h> -#include <sgi_variant.h> + +#include <nrd_variant.h> /* * Information about the isolated CPUs obtained from SDS. @@ -26,17 +25,18 @@ struct isolated_cpu_mpid_list { }; /* Function to read isolated CPU MPID list from SDS. */ -void plat_arm_sgi_get_isolated_cpu_list(struct isolated_cpu_mpid_list *list) +void plat_arm_nrd_get_isolated_cpu_list(struct isolated_cpu_mpid_list *list) { int ret; - ret = sds_init(); + ret = sds_init(SDS_SCP_AP_REGION_ID); if (ret != SDS_OK) { ERROR("SDS initialization failed, error: %d\n", ret); panic(); } - ret = sds_struct_read(SDS_ISOLATED_CPU_LIST_ID, 0, &list->num_entries, + ret = sds_struct_read(SDS_SCP_AP_REGION_ID, + SDS_ISOLATED_CPU_LIST_ID, 0, &list->num_entries, sizeof(list->num_entries), SDS_ACCESS_MODE_CACHED); if (ret != SDS_OK) { INFO("SDS CPU num elements read failed, error: %d\n", ret); @@ -54,7 +54,8 @@ void plat_arm_sgi_get_isolated_cpu_list(struct isolated_cpu_mpid_list *list) return; } - ret = sds_struct_read(SDS_ISOLATED_CPU_LIST_ID, + ret = sds_struct_read(SDS_SCP_AP_REGION_ID, + SDS_ISOLATED_CPU_LIST_ID, sizeof(list->num_entries), &list->mpid_list, sizeof(list->mpid_list[0]) * list->num_entries, @@ -73,7 +74,7 @@ void plat_arm_sgi_get_isolated_cpu_list(struct isolated_cpu_mpid_list *list) * isolated-cpu-list = <0> * } ******************************************************************************/ -static int plat_sgi_append_config_node(void) +static int plat_nrd_append_config_node(void) { bl_mem_params_node_t *mem_params; void *fdt; @@ -101,28 +102,28 @@ static int plat_sgi_append_config_node(void) return -1; } - platid = plat_arm_sgi_get_platform_id(); + platid = plat_arm_nrd_get_platform_id(); err = fdt_setprop_u32(fdt, nodeoffset, "platform-id", platid); if (err < 0) { ERROR("Failed to set platform-id\n"); return -1; } - platcfg = plat_arm_sgi_get_config_id(); + platcfg = plat_arm_nrd_get_config_id(); err = fdt_setprop_u32(fdt, nodeoffset, "config-id", platcfg); if (err < 0) { ERROR("Failed to set config-id\n"); return -1; } - platcfg = plat_arm_sgi_get_multi_chip_mode(); + platcfg = plat_arm_nrd_get_multi_chip_mode(); err = fdt_setprop_u32(fdt, nodeoffset, "multi-chip-mode", platcfg); if (err < 0) { ERROR("Failed to set multi-chip-mode\n"); return -1; } - plat_arm_sgi_get_isolated_cpu_list(&cpu_mpid_list); + plat_arm_nrd_get_isolated_cpu_list(&cpu_mpid_list); if (cpu_mpid_list.num_entries > 0) { err = fdt_setprop(fdt, nodeoffset, "isolated-cpu-list", &cpu_mpid_list, @@ -146,10 +147,9 @@ bl_params_t *plat_get_next_bl_params(void) { int ret; - ret = plat_sgi_append_config_node(); + ret = plat_nrd_append_config_node(); if (ret != 0) panic(); return arm_get_next_bl_params(); } - diff --git a/plat/arm/css/sgi/sgi_interconnect.c b/plat/arm/board/neoverse_rd/common/nrd_interconnect.c similarity index 94% rename from plat/arm/css/sgi/sgi_interconnect.c rename to plat/arm/board/neoverse_rd/common/nrd_interconnect.c index e9cd8125..4f9cc851 100644 --- a/plat/arm/css/sgi/sgi_interconnect.c +++ b/plat/arm/board/neoverse_rd/common/nrd_interconnect.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018-2019, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2018-2024, Arm Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ diff --git a/plat/arm/css/sgi/sgi_plat.c b/plat/arm/board/neoverse_rd/common/nrd_plat1.c similarity index 79% rename from plat/arm/css/sgi/sgi_plat.c rename to plat/arm/board/neoverse_rd/common/nrd_plat1.c index 01b426e8..32444f4f 100644 --- a/plat/arm/css/sgi/sgi_plat.c +++ b/plat/arm/board/neoverse_rd/common/nrd_plat1.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018-2023, Arm Limited and Contributors. All rights reserved. + * Copyright (c) 2018-2024, Arm Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -11,18 +11,16 @@ #include <common/bl_common.h> #include <common/debug.h> #include <drivers/arm/ccn.h> +#include <drivers/arm/css/sds.h> +#include <lib/utils_def.h> #include <plat/arm/common/plat_arm.h> #include <plat/common/platform.h> #include <drivers/arm/sbsa.h> -#include <sgi_base_platform_def.h> #if SPM_MM #include <services/spm_mm_partition.h> #endif -#define SGI_MAP_FLASH0_RO MAP_REGION_FLAT(V2M_FLASH0_BASE,\ - V2M_FLASH0_SIZE, \ - MT_DEVICE | MT_RO | MT_SECURE) /* * Table of regions for different BL stages to map using the MMU. * This doesn't include Trusted RAM as the 'mem_layout' argument passed to @@ -33,30 +31,30 @@ #if IMAGE_BL1 const mmap_region_t plat_arm_mmap[] = { ARM_MAP_SHARED_RAM, - SGI_MAP_FLASH0_RO, - CSS_SGI_MAP_DEVICE, - SOC_CSS_MAP_DEVICE, + NRD_MAP_FLASH0_RO, + NRD_CSS_PERIPH_MMAP(0), + NRD_ROS_PERIPH_MMAP(0), {0} }; #endif #if IMAGE_BL2 const mmap_region_t plat_arm_mmap[] = { ARM_MAP_SHARED_RAM, - SGI_MAP_FLASH0_RO, + NRD_MAP_FLASH0_RO, #ifdef PLAT_ARM_MEM_PROT_ADDR ARM_V2M_MAP_MEM_PROTECT, #endif - CSS_SGI_MAP_DEVICE, - SOC_CSS_MAP_DEVICE, + NRD_CSS_PERIPH_MMAP(0), + NRD_ROS_PERIPH_MMAP(0), ARM_MAP_NS_DRAM1, -#if CSS_SGI_CHIP_COUNT > 1 - CSS_SGI_MAP_DEVICE_REMOTE_CHIP(1), +#if NRD_CHIP_COUNT > 1 + NRD_CSS_PERIPH_MMAP(1), #endif -#if CSS_SGI_CHIP_COUNT > 2 - CSS_SGI_MAP_DEVICE_REMOTE_CHIP(2), +#if NRD_CHIP_COUNT > 2 + NRD_CSS_PERIPH_MMAP(2), #endif -#if CSS_SGI_CHIP_COUNT > 3 - CSS_SGI_MAP_DEVICE_REMOTE_CHIP(3), +#if NRD_CHIP_COUNT > 3 + NRD_CSS_PERIPH_MMAP(3), #endif #if ARM_BL31_IN_DRAM ARM_MAP_BL31_SEC_DRAM, @@ -74,11 +72,11 @@ const mmap_region_t plat_arm_mmap[] = { const mmap_region_t plat_arm_mmap[] = { ARM_MAP_SHARED_RAM, V2M_MAP_IOFPGA, - CSS_SGI_MAP_DEVICE, + NRD_CSS_PERIPH_MMAP(0), #ifdef PLAT_ARM_MEM_PROT_ADDR ARM_V2M_MAP_MEM_PROTECT, #endif - SOC_CSS_MAP_DEVICE, + NRD_ROS_PERIPH_MMAP(0), #if SPM_MM ARM_SPM_BUF_EL3_MMAP, #endif @@ -87,15 +85,11 @@ const mmap_region_t plat_arm_mmap[] = { #if SPM_MM && defined(IMAGE_BL31) const mmap_region_t plat_arm_secure_partition_mmap[] = { - PLAT_ARM_SECURE_MAP_SYSTEMREG, - PLAT_ARM_SECURE_MAP_NOR2, - SOC_PLATFORM_SECURE_UART, - PLAT_ARM_SECURE_MAP_DEVICE, + NRD_ROS_SECURE_SYSTEMREG_USER_MMAP, + NRD_ROS_SECURE_NOR2_USER_MMAP, + NRD_CSS_SECURE_UART_MMAP, ARM_SP_IMAGE_MMAP, ARM_SP_IMAGE_NS_BUF_MMAP, -#if ENABLE_FEAT_RAS && FFH_SUPPORT - CSS_SGI_SP_CPER_BUF_MMAP, -#endif ARM_SP_IMAGE_RW_MMAP, ARM_SPM_BUF_EL0_MMAP, {0} @@ -167,10 +161,21 @@ int plat_get_mbedtls_heap(void **heap_addr, size_t *heap_size) void plat_arm_secure_wdt_start(void) { - sbsa_wdog_start(SBSA_SECURE_WDOG_BASE, SBSA_SECURE_WDOG_TIMEOUT); + sbsa_wdog_start(NRD_CSS_WDOG_BASE, NRD_CSS_WDOG_TIMEOUT); } void plat_arm_secure_wdt_stop(void) { - sbsa_wdog_stop(SBSA_SECURE_WDOG_BASE); + sbsa_wdog_stop(NRD_CSS_WDOG_BASE); +} + +static sds_region_desc_t nrd_sds_regions[] = { + { .base = PLAT_ARM_SDS_MEM_BASE }, +}; + +sds_region_desc_t *plat_sds_get_regions(unsigned int *region_count) +{ + *region_count = ARRAY_SIZE(nrd_sds_regions); + + return nrd_sds_regions; } diff --git a/plat/arm/css/sgi/sgi_plat_v2.c b/plat/arm/board/neoverse_rd/common/nrd_plat2.c similarity index 73% rename from plat/arm/css/sgi/sgi_plat_v2.c rename to plat/arm/board/neoverse_rd/common/nrd_plat2.c index 624fed34..4a7b0214 100644 --- a/plat/arm/css/sgi/sgi_plat_v2.c +++ b/plat/arm/board/neoverse_rd/common/nrd_plat2.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021-2023, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2021-2024, Arm Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -8,9 +8,11 @@ #include <platform_def.h> +#include <lib/utils_def.h> +#include <drivers/arm/css/sds.h> +#include <drivers/arm/sbsa.h> #include <plat/arm/common/plat_arm.h> #include <plat/common/platform.h> -#include <drivers/arm/sbsa.h> #if SPM_MM #include <services/spm_mm_partition.h> @@ -21,35 +23,35 @@ */ #if IMAGE_BL1 const mmap_region_t plat_arm_mmap[] = { - ARM_MAP_SHARED_RAM, - SGI_MAP_FLASH0_RO, - CSS_SGI_MAP_DEVICE, - SOC_PLATFORM_PERIPH_MAP_DEVICE, - SOC_SYSTEM_PERIPH_MAP_DEVICE, + NRD_CSS_SHARED_RAM_MMAP(0), + NRD_ROS_FLASH0_RO_MMAP, + NRD_CSS_PERIPH_MMAP(0), + NRD_ROS_PLATFORM_PERIPH_MMAP, + NRD_ROS_SYSTEM_PERIPH_MMAP, {0} }; #endif #if IMAGE_BL2 const mmap_region_t plat_arm_mmap[] = { - ARM_MAP_SHARED_RAM, - SGI_MAP_FLASH0_RO, + NRD_CSS_SHARED_RAM_MMAP(0), + NRD_ROS_FLASH0_RO_MMAP, #ifdef PLAT_ARM_MEM_PROT_ADDR ARM_V2M_MAP_MEM_PROTECT, #endif - CSS_SGI_MAP_DEVICE, - SOC_MEMCNTRL_MAP_DEVICE, - SOC_PLATFORM_PERIPH_MAP_DEVICE, - SOC_SYSTEM_PERIPH_MAP_DEVICE, + NRD_CSS_PERIPH_MMAP(0), + NRD_ROS_MEMCNTRL_MMAP(0), + NRD_ROS_PLATFORM_PERIPH_MMAP, + NRD_ROS_SYSTEM_PERIPH_MMAP, ARM_MAP_NS_DRAM1, -#if CSS_SGI_CHIP_COUNT > 1 - SOC_MEMCNTRL_MAP_DEVICE_REMOTE_CHIP(1), +#if NRD_CHIP_COUNT > 1 + NRD_ROS_MEMCNTRL_MMAP(1), #endif -#if CSS_SGI_CHIP_COUNT > 2 - SOC_MEMCNTRL_MAP_DEVICE_REMOTE_CHIP(2), +#if NRD_CHIP_COUNT > 2 + NRD_ROS_MEMCNTRL_MMAP(2), #endif -#if CSS_SGI_CHIP_COUNT > 3 - SOC_MEMCNTRL_MAP_DEVICE_REMOTE_CHIP(3), +#if NRD_CHIP_COUNT > 3 + NRD_ROS_MEMCNTRL_MMAP(3), #endif #if ARM_BL31_IN_DRAM ARM_MAP_BL31_SEC_DRAM, @@ -66,13 +68,13 @@ const mmap_region_t plat_arm_mmap[] = { #if IMAGE_BL31 const mmap_region_t plat_arm_mmap[] = { - ARM_MAP_SHARED_RAM, + NRD_CSS_SHARED_RAM_MMAP(0), #ifdef PLAT_ARM_MEM_PROT_ADDR ARM_V2M_MAP_MEM_PROTECT, #endif - CSS_SGI_MAP_DEVICE, - SOC_PLATFORM_PERIPH_MAP_DEVICE, - SOC_SYSTEM_PERIPH_MAP_DEVICE, + NRD_CSS_PERIPH_MMAP(0), + NRD_ROS_PLATFORM_PERIPH_MMAP, + NRD_ROS_SYSTEM_PERIPH_MMAP, #if SPM_MM || (SPMC_AT_EL3 && SPMC_AT_EL3_SEL0_SP) ARM_SPM_BUF_EL3_MMAP, #endif @@ -81,14 +83,14 @@ const mmap_region_t plat_arm_mmap[] = { #if SPM_MM && defined(IMAGE_BL31) const mmap_region_t plat_arm_secure_partition_mmap[] = { - PLAT_ARM_SECURE_MAP_SYSTEMREG, - PLAT_ARM_SECURE_MAP_NOR2, - SOC_PLATFORM_SECURE_UART, - SOC_PLATFORM_PERIPH_MAP_DEVICE_USER, + NRD_ROS_SECURE_SYSTEMREG_USER_MMAP, + NRD_ROS_SECURE_NOR2_USER_MMAP, + NRD_CSS_SECURE_UART_USER_MMAP, + NRD_ROS_PLATFORM_PERIPH_USER_MMAP, ARM_SP_IMAGE_MMAP, ARM_SP_IMAGE_NS_BUF_MMAP, #if ENABLE_FEAT_RAS && FFH_SUPPORT - CSS_SGI_SP_CPER_BUF_MMAP, + NRD_CSS_SP_CPER_BUF_MMAP, #endif ARM_SP_IMAGE_RW_MMAP, ARM_SPM_BUF_EL0_MMAP, @@ -169,10 +171,21 @@ int plat_get_mbedtls_heap(void **heap_addr, size_t *heap_size) void plat_arm_secure_wdt_start(void) { - sbsa_wdog_start(SBSA_SECURE_WDOG_BASE, SBSA_SECURE_WDOG_TIMEOUT); + sbsa_wdog_start(NRD_CSS_SECURE_WDOG_BASE, NRD_CSS_SECURE_WDOG_TIMEOUT); } void plat_arm_secure_wdt_stop(void) { - sbsa_wdog_stop(SBSA_SECURE_WDOG_BASE); + sbsa_wdog_stop(NRD_CSS_SECURE_WDOG_BASE); +} + +static sds_region_desc_t nrd_sds_regions[] = { + { .base = PLAT_ARM_SDS_MEM_BASE }, +}; + +sds_region_desc_t *plat_sds_get_regions(unsigned int *region_count) +{ + *region_count = ARRAY_SIZE(nrd_sds_regions); + + return nrd_sds_regions; } diff --git a/plat/arm/board/neoverse_rd/common/nrd_plat3.c b/plat/arm/board/neoverse_rd/common/nrd_plat3.c new file mode 100644 index 00000000..00f346ec --- /dev/null +++ b/plat/arm/board/neoverse_rd/common/nrd_plat3.c @@ -0,0 +1,102 @@ +/* + * Copyright (c) 2024, Arm Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <assert.h> + +#include <drivers/arm/css/sds.h> +#include <drivers/arm/sbsa.h> +#include <lib/utils_def.h> +#include <plat/arm/common/plat_arm.h> +#include <plat/common/platform.h> + +#include <platform_def.h> + +/* + * Table of regions for different BL stages to map using the MMU. + */ +#if IMAGE_BL1 +const mmap_region_t plat_arm_mmap[] = { + NRD_CSS_SHARED_RAM_MMAP(0), + NRD_ROS_FLASH0_RO_MMAP, + NRD_CSS_PERIPH_MMAP(0), + NRD_ROS_PLATFORM_PERIPH_MMAP, + NRD_ROS_SYSTEM_PERIPH_MMAP, + {0} +}; +#endif /* IMAGE_BL3 */ + +#if IMAGE_BL2 +const mmap_region_t plat_arm_mmap[] = { + NRD_CSS_SHARED_RAM_MMAP(0), + NRD_ROS_FLASH0_RO_MMAP, +#ifdef PLAT_ARM_MEM_PROT_ADDR + NRD_ROS_V2M_MEM_PROTECT_MMAP, +#endif + NRD_CSS_PERIPH_MMAP(0), + NRD_ROS_PLATFORM_PERIPH_MMAP, + NRD_ROS_SYSTEM_PERIPH_MMAP, + NRD_CSS_NS_DRAM1_MMAP, +#if TRUSTED_BOARD_BOOT && !RESET_TO_BL2 + NRD_CSS_BL1_RW_MMAP, +#endif + NRD_CSS_GPT_L1_DRAM_MMAP, + NRD_CSS_RMM_REGION_MMAP, + {0} +}; +#endif /* IMAGE_BL2 */ + +#if IMAGE_BL31 +const mmap_region_t plat_arm_mmap[] = { + NRD_CSS_SHARED_RAM_MMAP(0), +#ifdef PLAT_ARM_MEM_PROT_ADDR + NRD_ROS_V2M_MEM_PROTECT_MMAP, +#endif + NRD_CSS_PERIPH_MMAP(0), + NRD_ROS_PLATFORM_PERIPH_MMAP, + NRD_ROS_SYSTEM_PERIPH_MMAP, + NRD_CSS_GPT_L1_DRAM_MMAP, + NRD_CSS_EL3_RMM_SHARED_MEM_MMAP, + NRD_CSS_GPC_SMMU_SMMUV3_MMAP, +#if RESET_TO_BL31 + NRD_CSS_MAP_BL31_DTB, +#endif + {0} +}; +#endif /* IMAGE_BL31 */ + +ARM_CASSERT_MMAP + +#if TRUSTED_BOARD_BOOT +int plat_get_mbedtls_heap(void **heap_addr, size_t *heap_size) +{ + assert(heap_addr != NULL); + assert(heap_size != NULL); + + return arm_get_mbedtls_heap(heap_addr, heap_size); +} +#endif + +void plat_arm_secure_wdt_start(void) +{ + sbsa_wdog_start(NRD_CSS_AP_SECURE_WDOG_BASE, + NRD_CSS_AP_SECURE_WDOG_TIMEOUT); +} + +void plat_arm_secure_wdt_stop(void) +{ + sbsa_wdog_stop(NRD_CSS_AP_SECURE_WDOG_BASE); +} + +static sds_region_desc_t nrd_sds_regions[] = { + { .base = PLAT_ARM_SDS_MEM_BASE }, +}; + +sds_region_desc_t *plat_sds_get_regions(unsigned int *region_count) +{ + *region_count = ARRAY_SIZE(nrd_sds_regions); + + return nrd_sds_regions; +} diff --git a/plat/arm/css/sgi/sgi_topology.c b/plat/arm/board/neoverse_rd/common/nrd_topology.c similarity index 78% rename from plat/arm/css/sgi/sgi_topology.c rename to plat/arm/board/neoverse_rd/common/nrd_topology.c index 1c3b5bfc..ff04b2b2 100644 --- a/plat/arm/css/sgi/sgi_topology.c +++ b/plat/arm/board/neoverse_rd/common/nrd_topology.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018-2020, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2018-2024, Arm Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -7,7 +7,7 @@ #include <plat/arm/common/plat_arm.h> /* - * Common topology related methods for SGI and RD based platforms + * Common topology related methods for Neoverse RD platforms */ /******************************************************************************* * This function returns the core count within the cluster corresponding to @@ -15,7 +15,7 @@ ******************************************************************************/ unsigned int plat_arm_get_cluster_core_count(u_register_t mpidr) { - return CSS_SGI_MAX_CPUS_PER_CLUSTER; + return NRD_MAX_CPUS_PER_CLUSTER; } #if ARM_PLAT_MT @@ -24,6 +24,6 @@ unsigned int plat_arm_get_cluster_core_count(u_register_t mpidr) *****************************************************************************/ unsigned int plat_arm_get_cpu_pe_count(u_register_t mpidr) { - return CSS_SGI_MAX_PE_PER_CPU; + return NRD_MAX_PE_PER_CPU; } #endif diff --git a/plat/arm/css/sgi/ras/sgi_ras_common.c b/plat/arm/board/neoverse_rd/common/ras/nrd_ras_common.c similarity index 65% rename from plat/arm/css/sgi/ras/sgi_ras_common.c rename to plat/arm/board/neoverse_rd/common/ras/nrd_ras_common.c index 9789670d..24f4506a 100644 --- a/plat/arm/css/sgi/ras/sgi_ras_common.c +++ b/plat/arm/board/neoverse_rd/common/ras/nrd_ras_common.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018-2023, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2018-2024, Arm Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -11,27 +11,27 @@ #include <plat/common/platform.h> #include <platform_def.h> -#include <sgi_ras.h> +#include <nrd_ras.h> -static struct plat_sgi_ras_config *sgi_ras_config; +static struct plat_nrd_ras_config *nrd_ras_config; /* * Find event map for a given interrupt number. On success, returns pointer to * the event map. On error, returns NULL. */ -struct sgi_ras_ev_map *sgi_find_ras_event_map_by_intr(uint32_t intr_num) +struct nrd_ras_ev_map *nrd_find_ras_event_map_by_intr(uint32_t intr_num) { - struct sgi_ras_ev_map *map; + struct nrd_ras_ev_map *map; int size; int i; - if (sgi_ras_config == NULL) { + if (nrd_ras_config == NULL) { ERROR("RAS config is NULL\n"); return NULL; } - map = sgi_ras_config->ev_map; - size = sgi_ras_config->ev_map_size; + map = nrd_ras_config->ev_map; + size = nrd_ras_config->ev_map_size; for (i = 0; i < size; i++) { if (map->intr == intr_num) @@ -47,14 +47,14 @@ struct sgi_ras_ev_map *sgi_find_ras_event_map_by_intr(uint32_t intr_num) * Programs GIC registers and configures interrupt ID's as Group0 EL3 * interrupts. Current support is to register PPI and SPI interrupts. */ -static void sgi_ras_intr_configure(int intr, int intr_type) +static void nrd_ras_intr_configure(int intr, int intr_type) { plat_ic_set_interrupt_type(intr, INTR_TYPE_EL3); plat_ic_set_interrupt_priority(intr, PLAT_RAS_PRI); plat_ic_clear_interrupt_pending(intr); /* Routing mode option available only for SPI interrupts */ - if (intr_type == SGI_RAS_INTR_TYPE_SPI) { + if (intr_type == NRD_RAS_INTR_TYPE_SPI) { plat_ic_set_spi_routing(intr, INTR_ROUTING_MODE_ANY, (u_register_t)read_mpidr_el1()); } @@ -67,15 +67,15 @@ static void sgi_ras_intr_configure(int intr, int intr_type) * Registers RAS config provided by the platform and then configures and * enables interrupt for each registered error. On success, return 0. */ -int sgi_ras_platform_setup(struct plat_sgi_ras_config *config) +int nrd_ras_platform_setup(struct plat_nrd_ras_config *config) { - struct sgi_ras_ev_map *map; + struct nrd_ras_ev_map *map; int size; int i; /* Check if parameter is valid. */ if (config == NULL) { - ERROR("SGI: Failed to register RAS config\n"); + ERROR("NRD: Failed to register RAS config\n"); return -1; } @@ -83,17 +83,17 @@ int sgi_ras_platform_setup(struct plat_sgi_ras_config *config) * Maintain a reference to the platform RAS config data for later * use. */ - sgi_ras_config = config; + nrd_ras_config = config; - map = sgi_ras_config->ev_map; - size = sgi_ras_config->ev_map_size; + map = nrd_ras_config->ev_map; + size = nrd_ras_config->ev_map_size; for (i = 0; i < size; i++) { - sgi_ras_intr_configure(map->intr, map->intr_type); + nrd_ras_intr_configure(map->intr, map->intr_type); map++; } - INFO("SGI: Platform RAS setup successful\n"); + INFO("NRD: Platform RAS setup successful\n"); return 0; } diff --git a/plat/arm/css/sgi/ras/sgi_ras_cpu.c b/plat/arm/board/neoverse_rd/common/ras/nrd_ras_cpu.c similarity index 57% rename from plat/arm/css/sgi/ras/sgi_ras_cpu.c rename to plat/arm/board/neoverse_rd/common/ras/nrd_ras_cpu.c index 5e77dbb8..dcee92c8 100644 --- a/plat/arm/css/sgi/ras/sgi_ras_cpu.c +++ b/plat/arm/board/neoverse_rd/common/ras/nrd_ras_cpu.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2023-2024, Arm Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -14,7 +14,7 @@ #include <services/sdei.h> #include <services/spm_mm_svc.h> -#include <sgi_ras.h> +#include <nrd_ras.h> #define CPU_CONTEXT_REG_GPR_ARR_SIZE 32 #define CPU_CONTEXT_REG_EL1_ARR_SIZE 17 @@ -62,72 +62,70 @@ static void populate_cpu_err_data(cpu_err_info *cpu_info, cpu_info->SecurityState = security_state; /* populate CPU EL1 context information. */ - cpu_info->ErrCtxEl1Reg[0] = read_ctx_reg(get_el1_sysregs_ctx(ctx), - CTX_ELR_EL1); - cpu_info->ErrCtxEl1Reg[1] = read_ctx_reg(get_el1_sysregs_ctx(ctx), - CTX_ESR_EL1); - cpu_info->ErrCtxEl1Reg[2] = read_ctx_reg(get_el1_sysregs_ctx(ctx), - CTX_FAR_EL1); + cpu_info->ErrCtxEl1Reg[0] = read_el1_ctx_common(get_el1_sysregs_ctx(ctx), + elr_el1); + cpu_info->ErrCtxEl1Reg[1] = read_el1_ctx_common(get_el1_sysregs_ctx(ctx), + esr_el1); + cpu_info->ErrCtxEl1Reg[2] = read_el1_ctx_common(get_el1_sysregs_ctx(ctx), + far_el1); cpu_info->ErrCtxEl1Reg[3] = read_isr_el1(); - cpu_info->ErrCtxEl1Reg[4] = read_ctx_reg(get_el1_sysregs_ctx(ctx), - CTX_MAIR_EL1); + cpu_info->ErrCtxEl1Reg[4] = read_el1_ctx_common(get_el1_sysregs_ctx(ctx), + mair_el1); cpu_info->ErrCtxEl1Reg[5] = read_midr_el1(); cpu_info->ErrCtxEl1Reg[6] = read_mpidr_el1(); - cpu_info->ErrCtxEl1Reg[7] = read_ctx_reg(get_el1_sysregs_ctx(ctx), - CTX_SCTLR_EL1); + cpu_info->ErrCtxEl1Reg[7] = read_ctx_sctlr_el1_reg_errata(ctx); cpu_info->ErrCtxEl1Reg[8] = read_ctx_reg(get_gpregs_ctx(ctx), CTX_GPREG_SP_EL0); - cpu_info->ErrCtxEl1Reg[9] = read_ctx_reg(get_el1_sysregs_ctx(ctx), - CTX_SP_EL1); - cpu_info->ErrCtxEl1Reg[10] = read_ctx_reg(get_el1_sysregs_ctx(ctx), - CTX_SPSR_EL1); - cpu_info->ErrCtxEl1Reg[11] = read_ctx_reg(get_el1_sysregs_ctx(ctx), - CTX_TCR_EL1); - cpu_info->ErrCtxEl1Reg[12] = read_ctx_reg(get_el1_sysregs_ctx(ctx), - CTX_TPIDR_EL0); - cpu_info->ErrCtxEl1Reg[13] = read_ctx_reg(get_el1_sysregs_ctx(ctx), - CTX_TPIDR_EL1); - cpu_info->ErrCtxEl1Reg[14] = read_ctx_reg(get_el1_sysregs_ctx(ctx), - CTX_TPIDRRO_EL0); - cpu_info->ErrCtxEl1Reg[15] = read_ctx_reg(get_el1_sysregs_ctx(ctx), - CTX_TTBR0_EL1); - cpu_info->ErrCtxEl1Reg[16] = read_ctx_reg(get_el1_sysregs_ctx(ctx), - CTX_TTBR1_EL1); - -#if CTX_INCLUDE_EL2_REGS - cpu_info->ErrCtxEl2Reg[0] = read_ctx_reg(get_el2_sysregs_ctx(ctx), - CTX_ELR_EL2); - cpu_info->ErrCtxEl2Reg[1] = read_ctx_reg(get_el2_sysregs_ctx(ctx), - CTX_ESR_EL2); - cpu_info->ErrCtxEl2Reg[2] = read_ctx_reg(get_el2_sysregs_ctx(ctx), - CTX_FAR_EL2); - cpu_info->ErrCtxEl2Reg[3] = read_ctx_reg(get_el2_sysregs_ctx(ctx), - CTX_HACR_EL2); - cpu_info->ErrCtxEl2Reg[4] = read_ctx_reg(get_el2_sysregs_ctx(ctx), - CTX_HCR_EL2); - cpu_info->ErrCtxEl2Reg[5] = read_ctx_reg(get_el2_sysregs_ctx(ctx), - CTX_HPFAR_EL2); - cpu_info->ErrCtxEl2Reg[6] = read_ctx_reg(get_el2_sysregs_ctx(ctx), - CTX_MAIR_EL2); - cpu_info->ErrCtxEl2Reg[7] = read_ctx_reg(get_el2_sysregs_ctx(ctx), - CTX_SCTLR_EL2); - cpu_info->ErrCtxEl2Reg[8] = read_ctx_reg(get_el2_sysregs_ctx(ctx), - CTX_SP_EL2); - cpu_info->ErrCtxEl2Reg[9] = read_ctx_reg(get_el2_sysregs_ctx(ctx), - CTX_SPSR_EL2); - cpu_info->ErrCtxEl2Reg[10] = read_ctx_reg(get_el2_sysregs_ctx(ctx), - CTX_TCR_EL2); - cpu_info->ErrCtxEl2Reg[11] = read_ctx_reg(get_el2_sysregs_ctx(ctx), - CTX_TPIDR_EL2); - cpu_info->ErrCtxEl2Reg[12] = read_ctx_reg(get_el2_sysregs_ctx(ctx), - CTX_TTBR0_EL2); - cpu_info->ErrCtxEl2Reg[13] = read_ctx_reg(get_el2_sysregs_ctx(ctx), - CTX_VTCR_EL2); - cpu_info->ErrCtxEl2Reg[14] = read_ctx_reg(get_el2_sysregs_ctx(ctx), - CTX_VTTBR_EL2); - cpu_info->ErrCtxEl2Reg[15] = read_ctx_reg(get_el2_sysregs_ctx(ctx), - CTX_ESR_EL2); -#endif + cpu_info->ErrCtxEl1Reg[9] = read_el1_ctx_common(get_el1_sysregs_ctx(ctx), + sp_el1); + cpu_info->ErrCtxEl1Reg[10] = read_el1_ctx_common(get_el1_sysregs_ctx(ctx), + spsr_el1); + cpu_info->ErrCtxEl1Reg[11] = read_ctx_tcr_el1_reg_errata(ctx); + cpu_info->ErrCtxEl1Reg[12] = read_el1_ctx_common(get_el1_sysregs_ctx(ctx), + tpidr_el0); + cpu_info->ErrCtxEl1Reg[13] = read_el1_ctx_common(get_el1_sysregs_ctx(ctx), + tpidr_el1); + cpu_info->ErrCtxEl1Reg[14] = read_el1_ctx_common(get_el1_sysregs_ctx(ctx), + tpidrro_el0); + cpu_info->ErrCtxEl1Reg[15] = read_el1_ctx_common(get_el1_sysregs_ctx(ctx), + ttbr0_el1); + cpu_info->ErrCtxEl1Reg[16] = read_el1_ctx_common(get_el1_sysregs_ctx(ctx), + ttbr1_el1); + +#if (CTX_INCLUDE_EL2_REGS && IMAGE_BL31) + cpu_info->ErrCtxEl2Reg[0] = read_el2_ctx_common(get_el2_sysregs_ctx(ctx), + elr_el2); + cpu_info->ErrCtxEl2Reg[1] = read_el2_ctx_common(get_el2_sysregs_ctx(ctx), + esr_el2); + cpu_info->ErrCtxEl2Reg[2] = read_el2_ctx_common(get_el2_sysregs_ctx(ctx), + far_el2); + cpu_info->ErrCtxEl2Reg[3] = read_el2_ctx_common(get_el2_sysregs_ctx(ctx), + hacr_el2); + cpu_info->ErrCtxEl2Reg[4] = read_el2_ctx_common(get_el2_sysregs_ctx(ctx), + hcr_el2); + cpu_info->ErrCtxEl2Reg[5] = read_el2_ctx_common(get_el2_sysregs_ctx(ctx), + hpfar_el2); + cpu_info->ErrCtxEl2Reg[6] = read_el2_ctx_common(get_el2_sysregs_ctx(ctx), + mair_el2); + cpu_info->ErrCtxEl2Reg[7] = read_el2_ctx_common(get_el2_sysregs_ctx(ctx), + sctlr_el2); + cpu_info->ErrCtxEl2Reg[8] = read_el2_ctx_common(get_el2_sysregs_ctx(ctx), + sp_el2); + cpu_info->ErrCtxEl2Reg[9] = read_el2_ctx_common(get_el2_sysregs_ctx(ctx), + spsr_el2); + cpu_info->ErrCtxEl2Reg[10] = read_el2_ctx_common(get_el2_sysregs_ctx(ctx), + tcr_el2); + cpu_info->ErrCtxEl2Reg[11] = read_el2_ctx_common(get_el2_sysregs_ctx(ctx), + tpidr_el2); + cpu_info->ErrCtxEl2Reg[12] = read_el2_ctx_common(get_el2_sysregs_ctx(ctx), + ttbr0_el2); + cpu_info->ErrCtxEl2Reg[13] = read_el2_ctx_common(get_el2_sysregs_ctx(ctx), + vtcr_el2); + cpu_info->ErrCtxEl2Reg[14] = read_el2_ctx_common(get_el2_sysregs_ctx(ctx), + vttbr_el2); + cpu_info->ErrCtxEl2Reg[15] = read_el2_ctx_common(get_el2_sysregs_ctx(ctx), + esr_el2); +#endif /* (CTX_INCLUDE_EL2_REGS && IMAGE_BL31) */ cpu_info->ErrCtxEl3Reg[0] = read_ctx_reg(get_el3state_ctx(ctx), CTX_ELR_EL3); @@ -143,11 +141,11 @@ static void populate_cpu_err_data(cpu_err_info *cpu_info, } /* CPU RAS interrupt handler */ -int sgi_ras_cpu_intr_handler(const struct err_record_info *err_rec, +int nrd_ras_cpu_intr_handler(const struct err_record_info *err_rec, int probe_data, const struct err_handler_data *const data) { - struct sgi_ras_ev_map *ras_map; + struct nrd_ras_ev_map *ras_map; mm_communicate_header_t *header; cpu_err_info cpu_info = {0}; uint64_t clear_status; @@ -186,9 +184,9 @@ int sgi_ras_cpu_intr_handler(const struct err_record_info *err_rec, * Find if this is a RAS interrupt. There must be an event against * this interrupt */ - ras_map = sgi_find_ras_event_map_by_intr(intr); + ras_map = nrd_find_ras_event_map_by_intr(intr); if (ras_map == NULL) { - ERROR("SGI: RAS error info for interrupt id: %d not found\n", + ERROR("NRD: RAS error info for interrupt id: %d not found\n", intr); return -1; } diff --git a/plat/arm/css/sgi/ras/sgi_ras_sram.c b/plat/arm/board/neoverse_rd/common/ras/nrd_ras_sram.c similarity index 81% rename from plat/arm/css/sgi/ras/sgi_ras_sram.c rename to plat/arm/board/neoverse_rd/common/ras/nrd_ras_sram.c index b1007003..96aa8643 100644 --- a/plat/arm/css/sgi/ras/sgi_ras_sram.c +++ b/plat/arm/board/neoverse_rd/common/ras/nrd_ras_sram.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2023-2024, Arm Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -11,7 +11,7 @@ #include <services/spm_mm_svc.h> #include <platform_def.h> -#include <sgi_ras.h> +#include <nrd_ras.h> /* Base Element RAM Error Record offsets. */ #define ERRSTATUS U(0) @@ -22,11 +22,11 @@ * Base Element RAM error information data structure communicated as part of MM * Communication data payload. */ -typedef struct sgi_sram_err_info { +typedef struct nrd_sram_err_info { uint32_t err_status; uint32_t err_code; uint32_t err_addr; -} sgi_sram_err_info_t; +} nrd_sram_err_info_t; /* * MM Communicate message header GUID to indicate the payload is intended for @@ -38,13 +38,13 @@ struct efi_guid sram_ecc_event_guid = { }; /* Base element RAM RAS error interrupt handler */ -int sgi_ras_sram_intr_handler(const struct err_record_info *err_rec, +int nrd_ras_sram_intr_handler(const struct err_record_info *err_rec, int probe_data, const struct err_handler_data *const data) { - struct sgi_ras_ev_map *ras_map; + struct nrd_ras_ev_map *ras_map; mm_communicate_header_t *header; - sgi_sram_err_info_t sram_info; + nrd_sram_err_info_t sram_info; uintptr_t base_addr; uint32_t clear_status, intr; int ret; @@ -52,12 +52,13 @@ int sgi_ras_sram_intr_handler(const struct err_record_info *err_rec, cm_el1_sysregs_context_save(NON_SECURE); intr = data->interrupt; - INFO("SGI: Base element RAM interrupt [%d] handler\n", intr); + INFO("NRD: Base element RAM interrupt [%d] handler\n", intr); /* Determine error record base address to read. */ base_addr = 0; - if (intr == NS_RAM_ECC_CE_INT || intr == NS_RAM_ECC_UE_INT) { - base_addr = SOC_NS_RAM_ERR_REC_BASE; + if (intr == NRD_CSS_NS_RAM_ECC_CE_INT || + intr == NRD_CSS_NS_RAM_ECC_UE_INT) { + base_addr = NRD_CSS_NS_RAM_ERR_REC_BASE; } sram_info.err_status = mmio_read_32(base_addr + ERRSTATUS); sram_info.err_code = mmio_read_32(base_addr + ERRCODE); @@ -87,9 +88,9 @@ int sgi_ras_sram_intr_handler(const struct err_record_info *err_rec, * Find if this is a RAS interrupt. There must be an event against * this interrupt */ - ras_map = sgi_find_ras_event_map_by_intr(intr); + ras_map = nrd_find_ras_event_map_by_intr(intr); if (ras_map == NULL) { - ERROR("SGI: RAS error info for interrupt id: %d not found\n", + ERROR("NRD: RAS error info for interrupt id: %d not found\n", intr); return -1; } diff --git a/plat/arm/board/rdn1edge/fdts/rdn1edge_fw_config.dts b/plat/arm/board/neoverse_rd/platform/rdn1edge/fdts/rdn1edge_fw_config.dts similarity index 86% rename from plat/arm/board/rdn1edge/fdts/rdn1edge_fw_config.dts rename to plat/arm/board/neoverse_rd/platform/rdn1edge/fdts/rdn1edge_fw_config.dts index d3b7fba4..085a42a5 100644 --- a/plat/arm/board/rdn1edge/fdts/rdn1edge_fw_config.dts +++ b/plat/arm/board/neoverse_rd/platform/rdn1edge/fdts/rdn1edge_fw_config.dts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018-2020, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2018-2024, Arm Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ diff --git a/plat/arm/board/rdn1edge/fdts/rdn1edge_nt_fw_config.dts b/plat/arm/board/neoverse_rd/platform/rdn1edge/fdts/rdn1edge_nt_fw_config.dts similarity index 86% rename from plat/arm/board/rdn1edge/fdts/rdn1edge_nt_fw_config.dts rename to plat/arm/board/neoverse_rd/platform/rdn1edge/fdts/rdn1edge_nt_fw_config.dts index 68366c5c..3cef0d15 100644 --- a/plat/arm/board/rdn1edge/fdts/rdn1edge_nt_fw_config.dts +++ b/plat/arm/board/neoverse_rd/platform/rdn1edge/fdts/rdn1edge_nt_fw_config.dts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018-2020, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2018-2024, Arm Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ diff --git a/plat/arm/board/rdn1edge/fdts/rdn1edge_tb_fw_config.dts b/plat/arm/board/neoverse_rd/platform/rdn1edge/fdts/rdn1edge_tb_fw_config.dts similarity index 89% rename from plat/arm/board/rdn1edge/fdts/rdn1edge_tb_fw_config.dts rename to plat/arm/board/neoverse_rd/platform/rdn1edge/fdts/rdn1edge_tb_fw_config.dts index 257ef4a3..78cd5a82 100644 --- a/plat/arm/board/rdn1edge/fdts/rdn1edge_tb_fw_config.dts +++ b/plat/arm/board/neoverse_rd/platform/rdn1edge/fdts/rdn1edge_tb_fw_config.dts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2020-2024, Arm Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ diff --git a/plat/arm/board/neoverse_rd/platform/rdn1edge/include/platform_def.h b/plat/arm/board/neoverse_rd/platform/rdn1edge/include/platform_def.h new file mode 100644 index 00000000..5357c31e --- /dev/null +++ b/plat/arm/board/neoverse_rd/platform/rdn1edge/include/platform_def.h @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2018-2024, Arm Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef PLATFORM_DEF_H +#define PLATFORM_DEF_H + +#include <lib/utils_def.h> +#include <nrd_css_fw_def1.h> +#include <nrd_plat_arm_def1.h> +#include <nrd_ros_fw_def1.h> +#include <nrd_sdei.h> + +/* Remote chip address offset */ +#define NRD_REMOTE_CHIP_MEM_OFFSET(n) \ + ((ULL(1) << NRD_ADDR_BITS_PER_CHIP) * (n)) + +#define PLAT_ARM_CLUSTER_COUNT U(2) +#define NRD_MAX_CPUS_PER_CLUSTER U(4) +#define NRD_MAX_PE_PER_CPU U(1) + +#define PLAT_CSS_MHU_BASE UL(0x45400000) + +/* Base address of DMC-620 instances */ +#define RDN1EDGE_DMC620_BASE0 UL(0x4e000000) +#define RDN1EDGE_DMC620_BASE1 UL(0x4e100000) + +/* Virtual address used by dynamic mem_protect for chunk_base */ +#define PLAT_ARM_MEM_PROTEC_VA_FRAME UL(0xc0000000) + +/* Maximum number of address bits used per chip */ +#define NRD_ADDR_BITS_PER_CHIP U(42) + +/* GIC related constants */ +#define PLAT_ARM_GICD_BASE UL(0x30000000) +#define PLAT_ARM_GICR_BASE UL(0x300C0000) + +/* GIC SPI range for multichip */ +#define NRD_CHIP0_SPI_MIN U(32) +#define NRD_CHIP0_SPI_MAX U(991) + +#endif /* PLATFORM_DEF_H */ diff --git a/plat/arm/board/rdn1edge/platform.mk b/plat/arm/board/neoverse_rd/platform/rdn1edge/platform.mk similarity index 65% rename from plat/arm/board/rdn1edge/platform.mk rename to plat/arm/board/neoverse_rd/platform/rdn1edge/platform.mk index 95753aaf..4892804f 100644 --- a/plat/arm/board/rdn1edge/platform.mk +++ b/plat/arm/board/neoverse_rd/platform/rdn1edge/platform.mk @@ -1,25 +1,24 @@ # -# Copyright (c) 2018-2020, ARM Limited and Contributors. All rights reserved. +# Copyright (c) 2018-2024, Arm Limited and Contributors. All rights reserved. # # SPDX-License-Identifier: BSD-3-Clause # -$(warning Platform ${PLAT} is deprecated. Some of the features might not work as expected) - # GIC-600 configuration GICV3_IMPL_GIC600_MULTICHIP := 1 -include plat/arm/css/sgi/sgi-common.mk +include plat/arm/board/neoverse_rd/common/nrd-common.mk -RDN1EDGE_BASE = plat/arm/board/rdn1edge +RDN1EDGE_BASE = plat/arm/board/neoverse_rd/platform/rdn1edge -PLAT_INCLUDES += -I${RDN1EDGE_BASE}/include/ +PLAT_INCLUDES += -I${NRD_COMMON_BASE}/include/nrd1/ \ + -I${RDN1EDGE_BASE}/include/ -SGI_CPU_SOURCES := lib/cpus/aarch64/neoverse_n1.S +NRD_CPU_SOURCES := lib/cpus/aarch64/neoverse_n1.S -PLAT_BL_COMMON_SOURCES += ${CSS_ENT_BASE}/sgi_plat.c +PLAT_BL_COMMON_SOURCES += ${NRD_COMMON_BASE}/nrd_plat1.c -BL1_SOURCES += ${SGI_CPU_SOURCES} \ +BL1_SOURCES += ${NRD_CPU_SOURCES} \ ${RDN1EDGE_BASE}/rdn1edge_err.c BL2_SOURCES += ${RDN1EDGE_BASE}/rdn1edge_plat.c \ @@ -29,7 +28,7 @@ BL2_SOURCES += ${RDN1EDGE_BASE}/rdn1edge_plat.c \ lib/utils/mem_region.c \ plat/arm/common/arm_nor_psci_mem_protect.c -BL31_SOURCES += ${SGI_CPU_SOURCES} \ +BL31_SOURCES += ${NRD_CPU_SOURCES} \ ${RDN1EDGE_BASE}/rdn1edge_plat.c \ ${RDN1EDGE_BASE}/rdn1edge_topology.c \ drivers/cfi/v2m/v2m_flash.c \ @@ -62,14 +61,23 @@ NT_FW_CONFIG := ${BUILD_PLAT}/fdts/${PLAT}_nt_fw_config.dtb $(eval $(call TOOL_ADD_PAYLOAD,${NT_FW_CONFIG},--nt-fw-config,${NT_FW_CONFIG})) $(eval $(call CREATE_SEQ,SEQ,2)) -ifneq ($(CSS_SGI_CHIP_COUNT),$(filter $(CSS_SGI_CHIP_COUNT),$(SEQ))) +ifneq ($(NRD_CHIP_COUNT),$(filter $(NRD_CHIP_COUNT),$(SEQ))) $(error "Chip count for RDN1Edge platform should be one of $(SEQ), currently \ - set to ${CSS_SGI_CHIP_COUNT}.") + set to ${NRD_CHIP_COUNT}.") +endif + +ifneq ($(NRD_PLATFORM_VARIANT),0) + $(error "NRD_PLATFORM_VARIANT for RD-N1-Edge should always be 0, \ + currently set to ${NRD_PLATFORM_VARIANT}.") endif -ifneq ($(CSS_SGI_PLATFORM_VARIANT),0) - $(error "CSS_SGI_PLATFORM_VARIANT for RD-N1-Edge should always be 0, \ - currently set to ${CSS_SGI_PLATFORM_VARIANT}.") +ifneq (${RESET_TO_BL31},0) + $(error "Using BL31 as the reset vector is not supported on ${PLAT} platform. \ + Please set RESET_TO_BL31 to 0.") endif override CTX_INCLUDE_AARCH32_REGS := 0 +override SPMD_SPM_AT_SEL2 := 0 + +# Enable the flag since RD-N1-EDGE has a system level cache +NEOVERSE_Nx_EXTERNAL_LLC := 1 diff --git a/plat/arm/board/rdn1edge/rdn1edge_err.c b/plat/arm/board/neoverse_rd/platform/rdn1edge/rdn1edge_err.c similarity index 71% rename from plat/arm/board/rdn1edge/rdn1edge_err.c rename to plat/arm/board/neoverse_rd/platform/rdn1edge/rdn1edge_err.c index 46d318c7..273e1f45 100644 --- a/plat/arm/board/rdn1edge/rdn1edge_err.c +++ b/plat/arm/board/neoverse_rd/platform/rdn1edge/rdn1edge_err.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019-2020, Arm Limited. All rights reserved. + * Copyright (c) 2019-2024, Arm Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ diff --git a/plat/arm/board/rdn1edge/rdn1edge_plat.c b/plat/arm/board/neoverse_rd/platform/rdn1edge/rdn1edge_plat.c similarity index 70% rename from plat/arm/board/rdn1edge/rdn1edge_plat.c rename to plat/arm/board/neoverse_rd/platform/rdn1edge/rdn1edge_plat.c index 6da8bcd6..ccabe229 100644 --- a/plat/arm/board/rdn1edge/rdn1edge_plat.c +++ b/plat/arm/board/neoverse_rd/platform/rdn1edge/rdn1edge_plat.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018-2020, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2018-2024, Arm Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -8,26 +8,28 @@ #include <drivers/arm/gic600_multichip.h> #include <plat/arm/common/plat_arm.h> #include <plat/common/platform.h> -#include <sgi_soc_platform_def.h> -#include <sgi_plat.h> + +#include <nrd_plat.h> #if defined(IMAGE_BL31) static const mmap_region_t rdn1edge_dynamic_mmap[] = { - ARM_MAP_SHARED_RAM_REMOTE_CHIP(1), - CSS_SGI_MAP_DEVICE_REMOTE_CHIP(1), - SOC_CSS_MAP_DEVICE_REMOTE_CHIP(1) + NRD_CSS_SHARED_RAM_MMAP(1), + NRD_CSS_PERIPH_MMAP(1), + NRD_ROS_PERIPH_MMAP(1) }; static struct gic600_multichip_data rdn1e1_multichip_data __init = { .rt_owner_base = PLAT_ARM_GICD_BASE, .rt_owner = 0, - .chip_count = CSS_SGI_CHIP_COUNT, + .chip_count = NRD_CHIP_COUNT, .chip_addrs = { PLAT_ARM_GICD_BASE >> 16, - (PLAT_ARM_GICD_BASE + CSS_SGI_REMOTE_CHIP_MEM_OFFSET(1)) >> 16 + (PLAT_ARM_GICD_BASE + NRD_REMOTE_CHIP_MEM_OFFSET(1)) >> 16 }, .spi_ids = { - {PLAT_ARM_GICD_BASE, 32, 255}, + {PLAT_ARM_GICD_BASE, + NRD_CHIP0_SPI_MIN, + NRD_CHIP0_SPI_MAX}, {0, 0, 0} } }; @@ -35,23 +37,23 @@ static struct gic600_multichip_data rdn1e1_multichip_data __init = { static uintptr_t rdn1e1_multichip_gicr_frames[] = { PLAT_ARM_GICR_BASE, /* Chip 0's GICR Base */ PLAT_ARM_GICR_BASE + - CSS_SGI_REMOTE_CHIP_MEM_OFFSET(1), /* Chip 1's GICR BASE */ + NRD_REMOTE_CHIP_MEM_OFFSET(1), /* Chip 1's GICR BASE */ UL(0) /* Zero Termination */ }; #endif /* IMAGE_BL31 */ -unsigned int plat_arm_sgi_get_platform_id(void) +unsigned int plat_arm_nrd_get_platform_id(void) { return mmio_read_32(SID_REG_BASE + SID_SYSTEM_ID_OFFSET) & SID_SYSTEM_ID_PART_NUM_MASK; } -unsigned int plat_arm_sgi_get_config_id(void) +unsigned int plat_arm_nrd_get_config_id(void) { return mmio_read_32(SID_REG_BASE + SID_SYSTEM_CFG_OFFSET); } -unsigned int plat_arm_sgi_get_multi_chip_mode(void) +unsigned int plat_arm_nrd_get_multi_chip_mode(void) { return (mmio_read_32(SID_REG_BASE + SID_NODE_ID_OFFSET) & SID_MULTI_CHIP_MODE_MASK) >> SID_MULTI_CHIP_MODE_SHIFT; @@ -68,12 +70,12 @@ void bl31_platform_setup(void) unsigned int i; int ret; - if (plat_arm_sgi_get_multi_chip_mode() == 0 && CSS_SGI_CHIP_COUNT > 1) { + if (plat_arm_nrd_get_multi_chip_mode() == 0 && NRD_CHIP_COUNT > 1) { ERROR("Chip Count is set to %d but multi-chip mode not enabled\n", - CSS_SGI_CHIP_COUNT); + NRD_CHIP_COUNT); panic(); - } else if (plat_arm_sgi_get_multi_chip_mode() == 1 && - CSS_SGI_CHIP_COUNT > 1) { + } else if (plat_arm_nrd_get_multi_chip_mode() == 1 && + NRD_CHIP_COUNT > 1) { INFO("Enabling support for multi-chip in RD-N1-Edge\n"); for (i = 0; i < ARRAY_SIZE(rdn1edge_dynamic_mmap); i++) { @@ -93,6 +95,6 @@ void bl31_platform_setup(void) gic600_multichip_init(&rdn1e1_multichip_data); } - sgi_bl31_common_platform_setup(); + nrd_bl31_common_platform_setup(); } #endif /* IMAGE_BL31 */ diff --git a/plat/arm/board/rdn1edge/rdn1edge_security.c b/plat/arm/board/neoverse_rd/platform/rdn1edge/rdn1edge_security.c similarity index 85% rename from plat/arm/board/rdn1edge/rdn1edge_security.c rename to plat/arm/board/neoverse_rd/platform/rdn1edge/rdn1edge_security.c index 49435329..f3f6238f 100644 --- a/plat/arm/board/rdn1edge/rdn1edge_security.c +++ b/plat/arm/board/neoverse_rd/platform/rdn1edge/rdn1edge_security.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019-2021, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2019-2024, Arm Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -7,7 +7,7 @@ #include <platform_def.h> #include <common/debug.h> -#include <sgi_dmc620_tzc_regions.h> +#include <nrd_dmc620_tzc_regions.h> uintptr_t rdn1edge_dmc_base[] = { RDN1EDGE_DMC620_BASE0, @@ -20,7 +20,7 @@ static const tzc_dmc620_driver_data_t rdn1edge_plat_driver_data = { }; static const tzc_dmc620_acc_addr_data_t rdn1edge_acc_addr_data[] = { - CSS_SGI_DMC620_TZC_REGIONS_DEF + NRD_DMC620_TZC_REGIONS_DEF }; static const tzc_dmc620_config_data_t rdn1edge_plat_config_data = { diff --git a/plat/arm/board/rdn1edge/rdn1edge_topology.c b/plat/arm/board/neoverse_rd/platform/rdn1edge/rdn1edge_topology.c similarity index 86% rename from plat/arm/board/rdn1edge/rdn1edge_topology.c rename to plat/arm/board/neoverse_rd/platform/rdn1edge/rdn1edge_topology.c index 5bbea699..133eb166 100644 --- a/plat/arm/board/rdn1edge/rdn1edge_topology.c +++ b/plat/arm/board/neoverse_rd/platform/rdn1edge/rdn1edge_topology.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019-2020, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2019-2024, Arm Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -11,12 +11,12 @@ * The power domain tree descriptor. ******************************************************************************/ static const unsigned char rdn1edge_pd_tree_desc[] = { - (PLAT_ARM_CLUSTER_COUNT) * (CSS_SGI_CHIP_COUNT), - CSS_SGI_MAX_CPUS_PER_CLUSTER, - CSS_SGI_MAX_CPUS_PER_CLUSTER, -#if (CSS_SGI_CHIP_COUNT > 1) - CSS_SGI_MAX_CPUS_PER_CLUSTER, - CSS_SGI_MAX_CPUS_PER_CLUSTER + (PLAT_ARM_CLUSTER_COUNT) * (NRD_CHIP_COUNT), + NRD_MAX_CPUS_PER_CLUSTER, + NRD_MAX_CPUS_PER_CLUSTER, +#if (NRD_CHIP_COUNT > 1) + NRD_MAX_CPUS_PER_CLUSTER, + NRD_MAX_CPUS_PER_CLUSTER #endif }; @@ -41,7 +41,7 @@ const uint32_t plat_css_core_pos_to_scmi_dmn_id_map[] = { (SET_SCMI_CHANNEL_ID(0x0) | SET_SCMI_DOMAIN_ID(0x5)), (SET_SCMI_CHANNEL_ID(0x0) | SET_SCMI_DOMAIN_ID(0x6)), (SET_SCMI_CHANNEL_ID(0x0) | SET_SCMI_DOMAIN_ID(0x7)), -#if (CSS_SGI_CHIP_COUNT > 1) +#if (NRD_CHIP_COUNT > 1) (SET_SCMI_CHANNEL_ID(0x1) | SET_SCMI_DOMAIN_ID(0x0)), (SET_SCMI_CHANNEL_ID(0x1) | SET_SCMI_DOMAIN_ID(0x1)), (SET_SCMI_CHANNEL_ID(0x1) | SET_SCMI_DOMAIN_ID(0x2)), diff --git a/plat/arm/board/rdn1edge/rdn1edge_trusted_boot.c b/plat/arm/board/neoverse_rd/platform/rdn1edge/rdn1edge_trusted_boot.c similarity index 88% rename from plat/arm/board/rdn1edge/rdn1edge_trusted_boot.c rename to plat/arm/board/neoverse_rd/platform/rdn1edge/rdn1edge_trusted_boot.c index 4592b8fb..84622d00 100644 --- a/plat/arm/board/rdn1edge/rdn1edge_trusted_boot.c +++ b/plat/arm/board/neoverse_rd/platform/rdn1edge/rdn1edge_trusted_boot.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, Arm Limited. All rights reserved. + * Copyright (c) 2020-2024, Arm Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ diff --git a/plat/arm/board/rde1edge/fdts/rde1edge_fw_config.dts b/plat/arm/board/neoverse_rd/platform/rdn2/fdts/rdn2_fw_config.dts similarity index 64% rename from plat/arm/board/rde1edge/fdts/rde1edge_fw_config.dts rename to plat/arm/board/neoverse_rd/platform/rdn2/fdts/rdn2_fw_config.dts index 69fb0d49..f857f72a 100644 --- a/plat/arm/board/rde1edge/fdts/rde1edge_fw_config.dts +++ b/plat/arm/board/neoverse_rd/platform/rdn2/fdts/rdn2_fw_config.dts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019-2020, ARM Limited. All rights reserved. + * Copyright (c) 2020-2024, Arm Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -18,6 +18,14 @@ id = <TB_FW_CONFIG_ID>; }; +#if SPMC_AT_EL3 + tos_fw-config { + load-address = <0x0 0x04001500>; + max-size = <0x1000>; + id = <TOS_FW_CONFIG_ID>; + }; + +#endif nt_fw-config { load-address = <0x0 0xFEF00000>; max-size = <0x0100000>; diff --git a/plat/arm/board/rdn2/fdts/rdn2_nt_fw_config.dts b/plat/arm/board/neoverse_rd/platform/rdn2/fdts/rdn2_nt_fw_config.dts similarity index 90% rename from plat/arm/board/rdn2/fdts/rdn2_nt_fw_config.dts rename to plat/arm/board/neoverse_rd/platform/rdn2/fdts/rdn2_nt_fw_config.dts index dd70141d..8e585656 100644 --- a/plat/arm/board/rdn2/fdts/rdn2_nt_fw_config.dts +++ b/plat/arm/board/neoverse_rd/platform/rdn2/fdts/rdn2_nt_fw_config.dts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020 - 2022, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2020-2024, Arm Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ diff --git a/plat/arm/board/neoverse_rd/platform/rdn2/fdts/rdn2_stmm_sel0_manifest.dts b/plat/arm/board/neoverse_rd/platform/rdn2/fdts/rdn2_stmm_sel0_manifest.dts new file mode 100644 index 00000000..dbdc7e59 --- /dev/null +++ b/plat/arm/board/neoverse_rd/platform/rdn2/fdts/rdn2_stmm_sel0_manifest.dts @@ -0,0 +1,147 @@ +/* + * Copyright (c) 2023-2024, Arm Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +/dts-v1/; + +#include <platform_def.h> + +/ { +#define MODE_SEL0 (0x1) + +#define SECURE_RO 0x1 +#define SECURE_RW 0x3 +#define SECURE_EXECUTE_RO 0x5 +#define SECURE_EXECUTE_RW 0x7 +#define NON_SECURE_RO 0x9 +#define NON_SECURE_RW 0xB +#define NON_SECURE_EXECUTE_RO 0xD +#define NON_SECURE_EXECUTE_RW 0xF + /* + * FF-A compatible Secure Partition Manager parses the + * manifest file and fetch the following booting arguments to + * pass on to the StandAloneMM(StMM) Secure Partition. + */ + compatible = "arm,ffa-manifest-1.0"; + + description = "RDN2 StMM"; + ffa-version = <0x00010001>; /* 31:16 - Major, 15:0 - Minor */ + uuid = <0x378daedc 0xf06b4446 0x831440ab 0x933c87a3>; + id = <0x8001>; + execution-ctx-count = <1>; + exception-level = <MODE_SEL0>; /* S-EL0 */ + execution-state = <0>; /* AArch64 */ + load-address = <0x0 0xFF200000>; + image-size = <0x0 0x280000>; + xlat-granule = <0>; /* 4KiB */ + boot-order = <0>; + messaging-method = <0x3>; /* Direct request/response supported. */ + power-management-messages = <0>; + gp-register-num = <0>; + + device-regions { + compatible = "arm,ffa-manifest-device-regions"; + + /* + * System registers region for access from S-EL0. + * Similar to PLAT_ARM_SECURE_MAP_SYSTEMREG. + */ + sys-regs { + base-address = <0x0 0x0C010000>; + pages-count = <0x10>; + attributes = <SECURE_RW>; + }; + + rtc { + base-address = <0x0 0x0C170000>; + pages-count = <0x1>; + attributes = <SECURE_RW>; + }; + + /* + * ARM CSS SoC Expansion Peripherals. + */ + soc_components { + base-address = <0x0 0x0E000000>; + pages-count = <0x2000>; + attributes = <SECURE_RW>; + }; + + cluster_utility { + base-address = <0x0 0x20000000>; + pages-count = <0x20000>; + attributes = <SECURE_RW>; + }; + + secure_uart { + base-address = <0x0 0x2A410000>; + pages-count = <0x10>; + attributes = <SECURE_RW>; + }; + + /* + * Used for Secure booting. + */ + nor_flash2 { + base-address = <0x10 0x54000000>; + pages-count = <0x4000>; + attributes = <SECURE_RW>; + }; + }; + + memory-regions { + compatible = "arm,ffa-manifest-memory-regions"; + + /* + * SPM Payload memory. Mapped as code region for S-EL0 + * Similar to ARM_SP_IMAGE_MMAP macro used for defining base of + * the SP image. + */ + stmm_region { + base-address = <0x0 0xff200000>; + pages-count = <0x300>; + /* StMM will remap the regions during runtime. */ + attributes = <SECURE_EXECUTE_RO>; + }; + + /* + * Memory shared between EL3 SPMC and S-EL0. + */ + rx-tx-buffers { + description = "shared-buff"; + base-address = <0x0 0xff500000>; + pages-count = <0x100>; + attributes = <SECURE_RW>; + }; + + /* + * Memory shared between Normal world and S-EL0. + */ + ns_comm_buffer { + /* + * Description is needed for StMM to identify + * ns-communication buffer. + */ + description = "ns-comm"; + base-address = <0x0 0xff600000>; + pages-count = <0x30>; + attributes = <NON_SECURE_RW>; + }; + + /* + * Heap used by SP to allocate memory for DMA. + */ + heap { + /* + * Description is needed for StMM to identify + * heap buffer. + */ + description = "heap"; + base-address = <0x0 0xFF630000>; + pages-count = <0x5D0>; + attributes = <SECURE_RW>; + }; + }; +}; diff --git a/plat/arm/board/rdn2/fdts/rdn2_tb_fw_config.dts b/plat/arm/board/neoverse_rd/platform/rdn2/fdts/rdn2_tb_fw_config.dts similarity index 89% rename from plat/arm/board/rdn2/fdts/rdn2_tb_fw_config.dts rename to plat/arm/board/neoverse_rd/platform/rdn2/fdts/rdn2_tb_fw_config.dts index 49eda273..c370623b 100644 --- a/plat/arm/board/rdn2/fdts/rdn2_tb_fw_config.dts +++ b/plat/arm/board/neoverse_rd/platform/rdn2/fdts/rdn2_tb_fw_config.dts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2020-2024, Arm Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ diff --git a/plat/arm/board/neoverse_rd/platform/rdn2/include/platform_def.h b/plat/arm/board/neoverse_rd/platform/rdn2/include/platform_def.h new file mode 100644 index 00000000..f6f2b862 --- /dev/null +++ b/plat/arm/board/neoverse_rd/platform/rdn2/include/platform_def.h @@ -0,0 +1,93 @@ +/* + * Copyright (c) 2020-2024, Arm Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef PLATFORM_DEF_H +#define PLATFORM_DEF_H + +#include <lib/utils_def.h> + +#include <nrd_css_fw_def2.h> +#include <nrd_plat_arm_def2.h> +#include <nrd_ros_fw_def2.h> +#include <nrd_sdei.h> + +/* Remote chip address offset */ +#define NRD_REMOTE_CHIP_MEM_OFFSET(n) \ + ((ULL(1) << NRD_ADDR_BITS_PER_CHIP) * (n)) + +#define NRD_MAX_CPUS_PER_CLUSTER U(1) +#define NRD_MAX_PE_PER_CPU U(1) + +/* Boot ROM */ +#define NRD_CSS_SECURE_ROM_SIZE UL(0x00080000) /* 512KB */ + +/* Secure SRAM */ +#define NRD_CSS_SECURE_SRAM_SIZE UL(0x00080000) /* 512KB */ + +/* NS SRAM */ +#define NRD_CSS_NS_SRAM_SIZE UL(0x00080000) /* 512KB */ + +/* DRAM2 */ +#define NRD_CSS_DRAM2_SIZE ULL(0x180000000) /* 6GB */ + +#define TZC400_OFFSET UL(0x1000000) + +#if (NRD_PLATFORM_VARIANT == 1) +#define TZC400_COUNT U(2) +#elif (NRD_PLATFORM_VARIANT == 2) +#define TZC400_COUNT U(4) +#else +#define TZC400_COUNT U(8) +#endif + +#define TZC400_BASE(n) (PLAT_ARM_TZC_BASE + \ + (n * TZC400_OFFSET)) + +#define TZC_NSAID_ALL_AP U(0) +#define TZC_NSAID_PCI U(1) +#define TZC_NSAID_HDLCD0 U(2) +#define TZC_NSAID_DMA U(5) +#define TZC_NSAID_DMA2 U(8) +#define TZC_NSAID_CLCD U(7) +#define TZC_NSAID_AP U(9) +#define TZC_NSAID_VIRTIO U(15) + +#define PLAT_ARM_TZC_NS_DEV_ACCESS \ + (TZC_REGION_ACCESS_RDWR(TZC_NSAID_ALL_AP)) | \ + (TZC_REGION_ACCESS_RDWR(TZC_NSAID_HDLCD0)) | \ + (TZC_REGION_ACCESS_RDWR(TZC_NSAID_PCI)) | \ + (TZC_REGION_ACCESS_RDWR(TZC_NSAID_DMA)) | \ + (TZC_REGION_ACCESS_RDWR(TZC_NSAID_DMA2)) | \ + (TZC_REGION_ACCESS_RDWR(TZC_NSAID_AP)) | \ + (TZC_REGION_ACCESS_RDWR(TZC_NSAID_CLCD)) | \ + (TZC_REGION_ACCESS_RDWR(TZC_NSAID_VIRTIO)) + +/* + * Physical and virtual address space limits for MMU in AARCH64 & AARCH32 modes + */ +#if (NRD_PLATFORM_VARIANT == 2) +#define NRD_ADDR_BITS_PER_CHIP U(46) /* 64TB */ +#else +#define NRD_ADDR_BITS_PER_CHIP U(42) /* 4TB */ +#endif + +/* GIC SPI range for multichip */ +#define NRD_CHIP0_SPI_MIN U(32) +#define NRD_CHIP0_SPI_MAX U(511) +#if NRD_CHIP_COUNT > 1 +#define NRD_CHIP1_SPI_MIN U(512) +#define NRD_CHIP1_SPI_MAX U(991) +#endif +#if NRD_CHIP_COUNT > 2 +#define NRD_CHIP2_SPI_MIN U(4096) +#define NRD_CHIP2_SPI_MAX U(4575) +#endif +#if NRD_CHIP_COUNT > 3 +#define NRD_CHIP3_SPI_MIN U(4576) +#define NRD_CHIP3_SPI_MAX U(5055) +#endif + +#endif /* PLATFORM_DEF_H */ diff --git a/plat/arm/board/neoverse_rd/platform/rdn2/include/rdn2_ras.h b/plat/arm/board/neoverse_rd/platform/rdn2/include/rdn2_ras.h new file mode 100644 index 00000000..c8a6f2d7 --- /dev/null +++ b/plat/arm/board/neoverse_rd/platform/rdn2/include/rdn2_ras.h @@ -0,0 +1,14 @@ +/* + * Copyright (c) 2023-2024, Arm Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef RDN2_RAS_H +#define RDN2_RAS_H + +#include <nrd_ras.h> + +extern struct plat_nrd_ras_config ras_config; + +#endif /* RDN2_RAS_H */ diff --git a/plat/arm/board/rdn2/platform.mk b/plat/arm/board/neoverse_rd/platform/rdn2/platform.mk similarity index 50% rename from plat/arm/board/rdn2/platform.mk rename to plat/arm/board/neoverse_rd/platform/rdn2/platform.mk index ef8f3d47..c2dfba6e 100644 --- a/plat/arm/board/rdn2/platform.mk +++ b/plat/arm/board/neoverse_rd/platform/rdn2/platform.mk @@ -1,19 +1,19 @@ -# Copyright (c) 2020-2023, Arm Limited and Contributors. All rights reserved. +# Copyright (c) 2020-2024, Arm Limited and Contributors. All rights reserved. # # SPDX-License-Identifier: BSD-3-Clause # RD_N2_VARIANTS := 0 1 2 3 -ifneq ($(CSS_SGI_PLATFORM_VARIANT),\ - $(filter $(CSS_SGI_PLATFORM_VARIANT),$(RD_N2_VARIANTS))) - $(error "CSS_SGI_PLATFORM_VARIANT for RD-N2 should be 0, 1, 2 or 3, currently \ - set to ${CSS_SGI_PLATFORM_VARIANT}.") +ifneq ($(NRD_PLATFORM_VARIANT),\ + $(filter $(NRD_PLATFORM_VARIANT),$(RD_N2_VARIANTS))) + $(error "NRD_PLATFORM_VARIANT for RD-N2 should be 0, 1, 2 or 3, currently \ + set to ${NRD_PLATFORM_VARIANT}.") endif $(eval $(call CREATE_SEQ,SEQ,4)) -ifneq ($(CSS_SGI_CHIP_COUNT),$(filter $(CSS_SGI_CHIP_COUNT),$(SEQ))) +ifneq ($(NRD_CHIP_COUNT),$(filter $(NRD_CHIP_COUNT),$(SEQ))) $(error "Chip count for RD-N2-MC should be either $(SEQ) \ - currently it is set to ${CSS_SGI_CHIP_COUNT}.") + currently it is set to ${NRD_CHIP_COUNT}.") endif # RD-N2 platform uses GIC-700 which is based on GICv4.1 @@ -21,25 +21,26 @@ GIC_ENABLE_V4_EXTN := 1 GIC_EXT_INTID := 1 #Enable GIC Multichip Extension only for Multichip Platforms -ifeq (${CSS_SGI_PLATFORM_VARIANT}, 2) +ifeq (${NRD_PLATFORM_VARIANT}, 2) GICV3_IMPL_GIC600_MULTICHIP := 1 endif override CSS_SYSTEM_GRACEFUL_RESET := 1 override EL3_EXCEPTION_HANDLING := 1 -include plat/arm/css/sgi/sgi-common.mk +include plat/arm/board/neoverse_rd/common/nrd-common.mk -RDN2_BASE = plat/arm/board/rdn2 +RDN2_BASE = plat/arm/board/neoverse_rd/platform/rdn2 -PLAT_INCLUDES += -I${RDN2_BASE}/include/ +PLAT_INCLUDES += -I${NRD_COMMON_BASE}/include/nrd2/ \ + -I${RDN2_BASE}/include/ -SGI_CPU_SOURCES := lib/cpus/aarch64/neoverse_n2.S \ +NRD_CPU_SOURCES := lib/cpus/aarch64/neoverse_n2.S \ lib/cpus/aarch64/neoverse_v2.S -PLAT_BL_COMMON_SOURCES += ${CSS_ENT_BASE}/sgi_plat_v2.c +PLAT_BL_COMMON_SOURCES += ${NRD_COMMON_BASE}/nrd_plat2.c -BL1_SOURCES += ${SGI_CPU_SOURCES} \ +BL1_SOURCES += ${NRD_CPU_SOURCES} \ ${RDN2_BASE}/rdn2_err.c BL2_SOURCES += ${RDN2_BASE}/rdn2_plat.c \ @@ -50,7 +51,7 @@ BL2_SOURCES += ${RDN2_BASE}/rdn2_plat.c \ plat/arm/common/arm_tzc400.c \ plat/arm/common/arm_nor_psci_mem_protect.c -BL31_SOURCES += ${SGI_CPU_SOURCES} \ +BL31_SOURCES += ${NRD_CPU_SOURCES} \ ${RDN2_BASE}/rdn2_plat.c \ ${RDN2_BASE}/rdn2_topology.c \ drivers/cfi/v2m/v2m_flash.c \ @@ -62,7 +63,7 @@ BL1_SOURCES += ${RDN2_BASE}/rdn2_trusted_boot.c BL2_SOURCES += ${RDN2_BASE}/rdn2_trusted_boot.c endif -ifeq (${CSS_SGI_PLATFORM_VARIANT}, 2) +ifeq (${NRD_PLATFORM_VARIANT}, 2) BL31_SOURCES += drivers/arm/gic/v3/gic600_multichip.c # Enable dynamic addition of MMAP regions in BL31 @@ -71,9 +72,9 @@ endif ifeq (${ENABLE_FEAT_RAS}-${HANDLE_EA_EL3_FIRST_NS},1-1) BL31_SOURCES += ${RDN2_BASE}/rdn2_ras.c \ - ${CSS_ENT_BASE}/ras/sgi_ras_common.c \ - ${CSS_ENT_BASE}/ras/sgi_ras_sram.c \ - ${CSS_ENT_BASE}/ras/sgi_ras_cpu.c + ${NRD_COMMON_BASE}/ras/nrd_ras_common.c \ + ${NRD_COMMON_BASE}/ras/nrd_ras_sram.c \ + ${NRD_COMMON_BASE}/ras/nrd_ras_cpu.c endif # Add the FDT_SOURCES and options for Dynamic Config @@ -93,5 +94,46 @@ NT_FW_CONFIG := ${BUILD_PLAT}/fdts/${PLAT}_nt_fw_config.dtb # Add the NT_FW_CONFIG to FIP and specify the same to certtool $(eval $(call TOOL_ADD_PAYLOAD,${NT_FW_CONFIG},--nt-fw-config)) +ifeq (${SPMC_AT_EL3}, 1) +STMM_CONFIG_DTS := ${RDN2_BASE}/fdts/${PLAT}_stmm_sel0_manifest.dts +FDT_SOURCES += ${STMM_CONFIG_DTS} +TOS_FW_CONFIG := ${BUILD_PLAT}/fdts/$(notdir $(basename ${STMM_CONFIG_DTS})).dtb + +# Add the TOS_FW_CONFIG to FIP and specify the same to certtool +$(eval $(call TOOL_ADD_PAYLOAD,${TOS_FW_CONFIG},--tos-fw-config,${TOS_FW_CONFIG})) +endif + +ifneq (${RESET_TO_BL31},0) + $(error "Using BL31 as the reset vector is not supported on ${PLAT} platform. \ + Please set RESET_TO_BL31 to 0.") +endif + override CTX_INCLUDE_AARCH32_REGS := 0 -override ENABLE_FEAT_AMU := 1 +override ENABLE_FEAT_AMU := 2 +override ENABLE_FEAT_MTE2 := 2 +override SPMD_SPM_AT_SEL2 := 0 + +# Enable the flag since RD-N2 has a system level cache +NEOVERSE_Nx_EXTERNAL_LLC := 1 + +# Enable N2 CPU errata workarounds +ERRATA_N2_2002655 := 1 +ERRATA_N2_2009478 := 1 +ERRATA_N2_2067956 := 1 +ERRATA_N2_2025414 := 1 +ERRATA_N2_2189731 := 1 +ERRATA_N2_2138956 := 1 +ERRATA_N2_2138953 := 1 +ERRATA_N2_2242415 := 1 +ERRATA_N2_2138958 := 1 +ERRATA_N2_2242400 := 1 +ERRATA_N2_2280757 := 1 +ERRATA_N2_2326639 := 1 +ERRATA_N2_2340933 := 1 +ERRATA_N2_2346952 := 1 +ERRATA_N2_2376738 := 1 +ERRATA_N2_2388450 := 1 +ERRATA_N2_2743014 := 1 +ERRATA_N2_2743089 := 1 +ERRATA_N2_2728475 := 1 +ERRATA_N2_2779511 := 1 diff --git a/plat/arm/board/rdn2/rdn2_err.c b/plat/arm/board/neoverse_rd/platform/rdn2/rdn2_err.c similarity index 71% rename from plat/arm/board/rdn2/rdn2_err.c rename to plat/arm/board/neoverse_rd/platform/rdn2/rdn2_err.c index 802ac21f..d7126454 100644 --- a/plat/arm/board/rdn2/rdn2_err.c +++ b/plat/arm/board/neoverse_rd/platform/rdn2/rdn2_err.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2020-2024, Arm Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ diff --git a/plat/arm/board/rdn2/rdn2_plat.c b/plat/arm/board/neoverse_rd/platform/rdn2/rdn2_plat.c similarity index 60% rename from plat/arm/board/rdn2/rdn2_plat.c rename to plat/arm/board/neoverse_rd/platform/rdn2/rdn2_plat.c index 2a6c658b..b1046d69 100644 --- a/plat/arm/board/rdn2/rdn2_plat.c +++ b/plat/arm/board/neoverse_rd/platform/rdn2/rdn2_plat.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020-2023, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2020-2024, Arm Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -9,93 +9,101 @@ #include <plat/arm/common/plat_arm.h> #include <plat/common/platform.h> #include <services/el3_spmc_ffa_memory.h> + +#include <nrd_plat.h> #include <rdn2_ras.h> -#include <sgi_soc_platform_def_v2.h> -#include <sgi_plat.h> #if defined(IMAGE_BL31) -#if (CSS_SGI_PLATFORM_VARIANT == 2) +#if (NRD_PLATFORM_VARIANT == 2) static const mmap_region_t rdn2mc_dynamic_mmap[] = { -#if CSS_SGI_CHIP_COUNT > 1 - ARM_MAP_SHARED_RAM_REMOTE_CHIP(1), - CSS_SGI_MAP_DEVICE_REMOTE_CHIP(1), +#if NRD_CHIP_COUNT > 1 + NRD_CSS_SHARED_RAM_MMAP(1), + NRD_CSS_PERIPH_MMAP(1), #endif -#if CSS_SGI_CHIP_COUNT > 2 - ARM_MAP_SHARED_RAM_REMOTE_CHIP(2), - CSS_SGI_MAP_DEVICE_REMOTE_CHIP(2), +#if NRD_CHIP_COUNT > 2 + NRD_CSS_SHARED_RAM_MMAP(2), + NRD_CSS_PERIPH_MMAP(2), #endif -#if CSS_SGI_CHIP_COUNT > 3 - ARM_MAP_SHARED_RAM_REMOTE_CHIP(3), - CSS_SGI_MAP_DEVICE_REMOTE_CHIP(3), +#if NRD_CHIP_COUNT > 3 + NRD_CSS_SHARED_RAM_MMAP(3), + NRD_CSS_PERIPH_MMAP(3), #endif }; #endif -#if (CSS_SGI_PLATFORM_VARIANT == 2) +#if (NRD_PLATFORM_VARIANT == 2) static struct gic600_multichip_data rdn2mc_multichip_data __init = { .rt_owner_base = PLAT_ARM_GICD_BASE, .rt_owner = 0, - .chip_count = CSS_SGI_CHIP_COUNT, + .chip_count = NRD_CHIP_COUNT, .chip_addrs = { PLAT_ARM_GICD_BASE >> 16, -#if CSS_SGI_CHIP_COUNT > 1 - (PLAT_ARM_GICD_BASE + CSS_SGI_REMOTE_CHIP_MEM_OFFSET(1)) >> 16, +#if NRD_CHIP_COUNT > 1 + (PLAT_ARM_GICD_BASE + NRD_REMOTE_CHIP_MEM_OFFSET(1)) >> 16, #endif -#if CSS_SGI_CHIP_COUNT > 2 - (PLAT_ARM_GICD_BASE + CSS_SGI_REMOTE_CHIP_MEM_OFFSET(2)) >> 16, +#if NRD_CHIP_COUNT > 2 + (PLAT_ARM_GICD_BASE + NRD_REMOTE_CHIP_MEM_OFFSET(2)) >> 16, #endif -#if CSS_SGI_CHIP_COUNT > 3 - (PLAT_ARM_GICD_BASE + CSS_SGI_REMOTE_CHIP_MEM_OFFSET(3)) >> 16, +#if NRD_CHIP_COUNT > 3 + (PLAT_ARM_GICD_BASE + NRD_REMOTE_CHIP_MEM_OFFSET(3)) >> 16, #endif }, .spi_ids = { - {PLAT_ARM_GICD_BASE, 32, 511}, - #if CSS_SGI_CHIP_COUNT > 1 - {PLAT_ARM_GICD_BASE, 512, 991}, + {PLAT_ARM_GICD_BASE, + NRD_CHIP0_SPI_MIN, + NRD_CHIP0_SPI_MAX}, + #if NRD_CHIP_COUNT > 1 + {PLAT_ARM_GICD_BASE, + NRD_CHIP1_SPI_MIN, + NRD_CHIP1_SPI_MAX}, #endif - #if CSS_SGI_CHIP_COUNT > 2 - {PLAT_ARM_GICD_BASE, 4096, 4575}, + #if NRD_CHIP_COUNT > 2 + {PLAT_ARM_GICD_BASE, + NRD_CHIP2_SPI_MIN, + NRD_CHIP2_SPI_MAX}, #endif - #if CSS_SGI_CHIP_COUNT > 3 - {PLAT_ARM_GICD_BASE, 4576, 5055}, + #if NRD_CHIP_COUNT > 3 + {PLAT_ARM_GICD_BASE, + NRD_CHIP3_SPI_MIN, + NRD_CHIP3_SPI_MAX}, #endif } }; #endif -#if (CSS_SGI_PLATFORM_VARIANT == 2) +#if (NRD_PLATFORM_VARIANT == 2) static uintptr_t rdn2mc_multichip_gicr_frames[] = { /* Chip 0's GICR Base */ PLAT_ARM_GICR_BASE, -#if CSS_SGI_CHIP_COUNT > 1 +#if NRD_CHIP_COUNT > 1 /* Chip 1's GICR BASE */ - PLAT_ARM_GICR_BASE + CSS_SGI_REMOTE_CHIP_MEM_OFFSET(1), + PLAT_ARM_GICR_BASE + NRD_REMOTE_CHIP_MEM_OFFSET(1), #endif -#if CSS_SGI_CHIP_COUNT > 2 +#if NRD_CHIP_COUNT > 2 /* Chip 2's GICR BASE */ - PLAT_ARM_GICR_BASE + CSS_SGI_REMOTE_CHIP_MEM_OFFSET(2), + PLAT_ARM_GICR_BASE + NRD_REMOTE_CHIP_MEM_OFFSET(2), #endif -#if CSS_SGI_CHIP_COUNT > 3 +#if NRD_CHIP_COUNT > 3 /* Chip 3's GICR BASE */ - PLAT_ARM_GICR_BASE + CSS_SGI_REMOTE_CHIP_MEM_OFFSET(3), + PLAT_ARM_GICR_BASE + NRD_REMOTE_CHIP_MEM_OFFSET(3), #endif UL(0) /* Zero Termination */ }; #endif #endif /* IMAGE_BL31 */ -unsigned int plat_arm_sgi_get_platform_id(void) +unsigned int plat_arm_nrd_get_platform_id(void) { return mmio_read_32(SID_REG_BASE + SID_SYSTEM_ID_OFFSET) & SID_SYSTEM_ID_PART_NUM_MASK; } -unsigned int plat_arm_sgi_get_config_id(void) +unsigned int plat_arm_nrd_get_config_id(void) { return mmio_read_32(SID_REG_BASE + SID_SYSTEM_CFG_OFFSET); } -unsigned int plat_arm_sgi_get_multi_chip_mode(void) +unsigned int plat_arm_nrd_get_multi_chip_mode(void) { return (mmio_read_32(SID_REG_BASE + SID_NODE_ID_OFFSET) & SID_MULTI_CHIP_MODE_MASK) >> @@ -105,13 +113,13 @@ unsigned int plat_arm_sgi_get_multi_chip_mode(void) #if defined(IMAGE_BL31) void bl31_platform_setup(void) { -#if (CSS_SGI_PLATFORM_VARIANT == 2) +#if (NRD_PLATFORM_VARIANT == 2) int ret; unsigned int i; - if (plat_arm_sgi_get_multi_chip_mode() == 0) { - ERROR("Chip Count is set to %u but multi-chip mode is not " - "enabled\n", CSS_SGI_CHIP_COUNT); + if (plat_arm_nrd_get_multi_chip_mode() == 0) { + ERROR("Chip Count is %u but multi-chip mode is not enabled\n", + NRD_CHIP_COUNT); panic(); } else { INFO("Enabling multi-chip support for RD-N2 variant\n"); @@ -135,10 +143,10 @@ void bl31_platform_setup(void) } #endif - sgi_bl31_common_platform_setup(); + nrd_bl31_common_platform_setup(); #if ENABLE_FEAT_RAS && FFH_SUPPORT - sgi_ras_platform_setup(&ras_config); + nrd_ras_platform_setup(&ras_config); #endif } #endif /* IMAGE_BL31 */ diff --git a/plat/arm/board/rdn2/rdn2_ras.c b/plat/arm/board/neoverse_rd/platform/rdn2/rdn2_ras.c similarity index 60% rename from plat/arm/board/rdn2/rdn2_ras.c rename to plat/arm/board/neoverse_rd/platform/rdn2/rdn2_ras.c index 3aed58e9..e3287644 100644 --- a/plat/arm/board/rdn2/rdn2_ras.c +++ b/plat/arm/board/neoverse_rd/platform/rdn2/rdn2_ras.c @@ -1,30 +1,31 @@ /* - * Copyright (c) 2023, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2023-2024, Arm Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ #include <platform_def.h> -#include <sgi_ras.h> -#include <sgi_sdei.h> -struct sgi_ras_ev_map plat_ras_map[] = { +#include <nrd_ras.h> +#include <nrd_sdei.h> + +struct nrd_ras_ev_map plat_ras_map[] = { /* Non Secure base RAM ECC CE interrupt */ - {SGI_SDEI_DS_EVENT_0, NS_RAM_ECC_CE_INT, SGI_RAS_INTR_TYPE_SPI}, + {NRD_SDEI_DS_EVENT_0, NRD_CSS_NS_RAM_ECC_CE_INT, NRD_RAS_INTR_TYPE_SPI}, /* Non Secure base RAM ECC UE interrupt */ - {SGI_SDEI_DS_EVENT_0, NS_RAM_ECC_UE_INT, SGI_RAS_INTR_TYPE_SPI}, + {NRD_SDEI_DS_EVENT_0, NRD_CSS_NS_RAM_ECC_UE_INT, NRD_RAS_INTR_TYPE_SPI}, /* CPU 1-bit ECC CE error interrupt */ - {SGI_SDEI_DS_EVENT_1, PLAT_CORE_FAULT_IRQ, SGI_RAS_INTR_TYPE_PPI} + {NRD_SDEI_DS_EVENT_1, PLAT_CORE_FAULT_IRQ, NRD_RAS_INTR_TYPE_PPI} }; /* RAS error record list definition, used by the common RAS framework. */ struct err_record_info plat_err_records[] = { /* Base element RAM Non-secure error record. */ - ERR_RECORD_MEMMAP_V1(SOC_NS_RAM_ERR_REC_BASE, 4, NULL, - &sgi_ras_sram_intr_handler, 0), - ERR_RECORD_SYSREG_V1(0, 1, NULL, &sgi_ras_cpu_intr_handler, 0), + ERR_RECORD_MEMMAP_V1(NRD_CSS_NS_RAM_ERR_REC_BASE, 4, NULL, + &nrd_ras_sram_intr_handler, 0), + ERR_RECORD_SYSREG_V1(0, 1, NULL, &nrd_ras_cpu_intr_handler, 0), }; /* RAS error interrupt list definition, used by the common RAS framework. */ @@ -33,10 +34,10 @@ struct ras_interrupt plat_ras_interrupts[] = { .intr_number = PLAT_CORE_FAULT_IRQ, .err_record = &plat_err_records[1], }, { - .intr_number = NS_RAM_ECC_CE_INT, + .intr_number = NRD_CSS_NS_RAM_ECC_CE_INT, .err_record = &plat_err_records[0], }, { - .intr_number = NS_RAM_ECC_UE_INT, + .intr_number = NRD_CSS_NS_RAM_ECC_UE_INT, .err_record = &plat_err_records[0], }, }; @@ -47,7 +48,7 @@ REGISTER_ERR_RECORD_INFO(plat_err_records); REGISTER_RAS_INTERRUPTS(plat_ras_interrupts); /* Platform RAS handling config data definition */ -struct plat_sgi_ras_config ras_config = { +struct plat_nrd_ras_config ras_config = { plat_ras_map, ARRAY_SIZE(plat_ras_map) }; diff --git a/plat/arm/board/rdn2/rdn2_security.c b/plat/arm/board/neoverse_rd/platform/rdn2/rdn2_security.c similarity index 55% rename from plat/arm/board/rdn2/rdn2_security.c rename to plat/arm/board/neoverse_rd/platform/rdn2/rdn2_security.c index 7cd4a1c8..7319d1a4 100644 --- a/plat/arm/board/rdn2/rdn2_security.c +++ b/plat/arm/board/neoverse_rd/platform/rdn2/rdn2_security.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020-2021, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2020-2024, Arm Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -8,9 +8,9 @@ #include <plat/arm/common/plat_arm.h> #include <platform_def.h> -#define RDN2_TZC_CPER_REGION \ - {CSS_SGI_SP_CPER_BUF_BASE, (CSS_SGI_SP_CPER_BUF_BASE + \ - CSS_SGI_SP_CPER_BUF_SIZE) - 1, TZC_REGION_S_NONE, \ +#define RDN2_TZC_CPER_REGION \ + {NRD_CSS_SP_CPER_BUF_BASE, (NRD_CSS_SP_CPER_BUF_BASE + \ + NRD_CSS_SP_CPER_BUF_SIZE) - 1, TZC_REGION_S_NONE, \ PLAT_ARM_TZC_NS_DEV_ACCESS} static const arm_tzc_regions_info_t tzc_regions[] = { @@ -21,29 +21,29 @@ static const arm_tzc_regions_info_t tzc_regions[] = { {} }; -#if (CSS_SGI_PLATFORM_VARIANT == 2 && CSS_SGI_CHIP_COUNT > 1) -static const arm_tzc_regions_info_t tzc_regions_mc[][CSS_SGI_CHIP_COUNT - 1] = { +#if (NRD_PLATFORM_VARIANT == 2 && NRD_CHIP_COUNT > 1) +static const arm_tzc_regions_info_t tzc_regions_mc[][NRD_CHIP_COUNT - 1] = { { /* TZC memory regions for second chip */ - SGI_PLAT_TZC_NS_REMOTE_REGIONS_DEF(1), + NRD_ROS_TZC_NS_REMOTE_REGIONS_DEF(1), {} }, -#if CSS_SGI_CHIP_COUNT > 2 +#if NRD_CHIP_COUNT > 2 { /* TZC memory regions for third chip */ - SGI_PLAT_TZC_NS_REMOTE_REGIONS_DEF(2), + NRD_ROS_TZC_NS_REMOTE_REGIONS_DEF(2), {} }, #endif -#if CSS_SGI_CHIP_COUNT > 3 +#if NRD_CHIP_COUNT > 3 { /* TZC memory regions for fourth chip */ - SGI_PLAT_TZC_NS_REMOTE_REGIONS_DEF(3), + NRD_ROS_TZC_NS_REMOTE_REGIONS_DEF(3), {} }, #endif }; -#endif /* CSS_SGI_PLATFORM_VARIANT && CSS_SGI_CHIP_COUNT */ +#endif /* NRD_PLATFORM_VARIANT && NRD_CHIP_COUNT */ /* Initialize the secure environment */ void plat_arm_security_setup(void) @@ -56,14 +56,14 @@ void plat_arm_security_setup(void) arm_tzc400_setup(TZC400_BASE(i), tzc_regions); } -#if (CSS_SGI_PLATFORM_VARIANT == 2 && CSS_SGI_CHIP_COUNT > 1) +#if (NRD_PLATFORM_VARIANT == 2 && NRD_CHIP_COUNT > 1) unsigned int j; - for (i = 1; i < CSS_SGI_CHIP_COUNT; i++) { + for (i = 1; i < NRD_CHIP_COUNT; i++) { INFO("Configuring TrustZone Controller for Chip %u\n", i); for (j = 0; j < TZC400_COUNT; j++) { - arm_tzc400_setup(CSS_SGI_REMOTE_CHIP_MEM_OFFSET(i) + arm_tzc400_setup(NRD_REMOTE_CHIP_MEM_OFFSET(i) + TZC400_BASE(j), tzc_regions_mc[i-1]); } } diff --git a/plat/arm/board/rdn2/rdn2_topology.c b/plat/arm/board/neoverse_rd/platform/rdn2/rdn2_topology.c similarity index 77% rename from plat/arm/board/rdn2/rdn2_topology.c rename to plat/arm/board/neoverse_rd/platform/rdn2/rdn2_topology.c index 24acc4d8..b8b6b7ad 100644 --- a/plat/arm/board/rdn2/rdn2_topology.c +++ b/plat/arm/board/neoverse_rd/platform/rdn2/rdn2_topology.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020-2023, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2020-2024, Arm Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -11,31 +11,31 @@ * The power domain tree descriptor. ******************************************************************************/ const unsigned char rd_n2_pd_tree_desc[] = { - (PLAT_ARM_CLUSTER_COUNT) * (CSS_SGI_CHIP_COUNT), - CSS_SGI_MAX_CPUS_PER_CLUSTER, - CSS_SGI_MAX_CPUS_PER_CLUSTER, - CSS_SGI_MAX_CPUS_PER_CLUSTER, - CSS_SGI_MAX_CPUS_PER_CLUSTER, + (PLAT_ARM_CLUSTER_COUNT) * (NRD_CHIP_COUNT), + NRD_MAX_CPUS_PER_CLUSTER, + NRD_MAX_CPUS_PER_CLUSTER, + NRD_MAX_CPUS_PER_CLUSTER, + NRD_MAX_CPUS_PER_CLUSTER, #if (PLAT_ARM_CLUSTER_COUNT > 4 || \ - (CSS_SGI_PLATFORM_VARIANT == 2 && CSS_SGI_CHIP_COUNT > 1)) - CSS_SGI_MAX_CPUS_PER_CLUSTER, - CSS_SGI_MAX_CPUS_PER_CLUSTER, - CSS_SGI_MAX_CPUS_PER_CLUSTER, - CSS_SGI_MAX_CPUS_PER_CLUSTER, + (NRD_PLATFORM_VARIANT == 2 && NRD_CHIP_COUNT > 1)) + NRD_MAX_CPUS_PER_CLUSTER, + NRD_MAX_CPUS_PER_CLUSTER, + NRD_MAX_CPUS_PER_CLUSTER, + NRD_MAX_CPUS_PER_CLUSTER, #endif #if (PLAT_ARM_CLUSTER_COUNT > 8 || \ - (CSS_SGI_PLATFORM_VARIANT == 2 && CSS_SGI_CHIP_COUNT > 2)) - CSS_SGI_MAX_CPUS_PER_CLUSTER, - CSS_SGI_MAX_CPUS_PER_CLUSTER, - CSS_SGI_MAX_CPUS_PER_CLUSTER, - CSS_SGI_MAX_CPUS_PER_CLUSTER, + (NRD_PLATFORM_VARIANT == 2 && NRD_CHIP_COUNT > 2)) + NRD_MAX_CPUS_PER_CLUSTER, + NRD_MAX_CPUS_PER_CLUSTER, + NRD_MAX_CPUS_PER_CLUSTER, + NRD_MAX_CPUS_PER_CLUSTER, #endif #if (PLAT_ARM_CLUSTER_COUNT > 8 || \ - (CSS_SGI_PLATFORM_VARIANT == 2 && CSS_SGI_CHIP_COUNT > 3)) - CSS_SGI_MAX_CPUS_PER_CLUSTER, - CSS_SGI_MAX_CPUS_PER_CLUSTER, - CSS_SGI_MAX_CPUS_PER_CLUSTER, - CSS_SGI_MAX_CPUS_PER_CLUSTER, + (NRD_PLATFORM_VARIANT == 2 && NRD_CHIP_COUNT > 3)) + NRD_MAX_CPUS_PER_CLUSTER, + NRD_MAX_CPUS_PER_CLUSTER, + NRD_MAX_CPUS_PER_CLUSTER, + NRD_MAX_CPUS_PER_CLUSTER, #endif }; @@ -51,25 +51,25 @@ const unsigned char *plat_get_power_domain_tree_desc(void) * The array mapping platform core position (implemented by plat_my_core_pos()) * to the SCMI power domain ID implemented by SCP. ******************************************************************************/ -#if (CSS_SGI_PLATFORM_VARIANT == 2) +#if (NRD_PLATFORM_VARIANT == 2) const uint32_t plat_css_core_pos_to_scmi_dmn_id_map[] = { (SET_SCMI_CHANNEL_ID(0x0) | SET_SCMI_DOMAIN_ID(0x0)), (SET_SCMI_CHANNEL_ID(0x0) | SET_SCMI_DOMAIN_ID(0x1)), (SET_SCMI_CHANNEL_ID(0x0) | SET_SCMI_DOMAIN_ID(0x2)), (SET_SCMI_CHANNEL_ID(0x0) | SET_SCMI_DOMAIN_ID(0x3)), -#if (CSS_SGI_CHIP_COUNT > 1) +#if (NRD_CHIP_COUNT > 1) (SET_SCMI_CHANNEL_ID(0x1) | SET_SCMI_DOMAIN_ID(0x0)), (SET_SCMI_CHANNEL_ID(0x1) | SET_SCMI_DOMAIN_ID(0x1)), (SET_SCMI_CHANNEL_ID(0x1) | SET_SCMI_DOMAIN_ID(0x2)), (SET_SCMI_CHANNEL_ID(0x1) | SET_SCMI_DOMAIN_ID(0x3)), #endif -#if (CSS_SGI_CHIP_COUNT > 2) +#if (NRD_CHIP_COUNT > 2) (SET_SCMI_CHANNEL_ID(0x2) | SET_SCMI_DOMAIN_ID(0x0)), (SET_SCMI_CHANNEL_ID(0x2) | SET_SCMI_DOMAIN_ID(0x1)), (SET_SCMI_CHANNEL_ID(0x2) | SET_SCMI_DOMAIN_ID(0x2)), (SET_SCMI_CHANNEL_ID(0x2) | SET_SCMI_DOMAIN_ID(0x3)), #endif -#if (CSS_SGI_CHIP_COUNT > 3) +#if (NRD_CHIP_COUNT > 3) (SET_SCMI_CHANNEL_ID(0x3) | SET_SCMI_DOMAIN_ID(0x0)), (SET_SCMI_CHANNEL_ID(0x3) | SET_SCMI_DOMAIN_ID(0x1)), (SET_SCMI_CHANNEL_ID(0x3) | SET_SCMI_DOMAIN_ID(0x2)), diff --git a/plat/arm/board/rdn2/rdn2_trusted_boot.c b/plat/arm/board/neoverse_rd/platform/rdn2/rdn2_trusted_boot.c similarity index 88% rename from plat/arm/board/rdn2/rdn2_trusted_boot.c rename to plat/arm/board/neoverse_rd/platform/rdn2/rdn2_trusted_boot.c index 4592b8fb..84622d00 100644 --- a/plat/arm/board/rdn2/rdn2_trusted_boot.c +++ b/plat/arm/board/neoverse_rd/platform/rdn2/rdn2_trusted_boot.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, Arm Limited. All rights reserved. + * Copyright (c) 2020-2024, Arm Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ diff --git a/plat/arm/board/rdv1/fdts/rdv1_fw_config.dts b/plat/arm/board/neoverse_rd/platform/rdv1/fdts/rdv1_fw_config.dts similarity index 83% rename from plat/arm/board/rdv1/fdts/rdv1_fw_config.dts rename to plat/arm/board/neoverse_rd/platform/rdv1/fdts/rdv1_fw_config.dts index 9c9cefe8..d4434437 100644 --- a/plat/arm/board/rdv1/fdts/rdv1_fw_config.dts +++ b/plat/arm/board/neoverse_rd/platform/rdv1/fdts/rdv1_fw_config.dts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2020-2024, Arm Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ diff --git a/plat/arm/board/rdv1/fdts/rdv1_nt_fw_config.dts b/plat/arm/board/neoverse_rd/platform/rdv1/fdts/rdv1_nt_fw_config.dts similarity index 83% rename from plat/arm/board/rdv1/fdts/rdv1_nt_fw_config.dts rename to plat/arm/board/neoverse_rd/platform/rdv1/fdts/rdv1_nt_fw_config.dts index 62ba2c3f..fb088854 100644 --- a/plat/arm/board/rdv1/fdts/rdv1_nt_fw_config.dts +++ b/plat/arm/board/neoverse_rd/platform/rdv1/fdts/rdv1_nt_fw_config.dts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2020-2024, Arm Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ diff --git a/plat/arm/board/rdv1/fdts/rdv1_tb_fw_config.dts b/plat/arm/board/neoverse_rd/platform/rdv1/fdts/rdv1_tb_fw_config.dts similarity index 89% rename from plat/arm/board/rdv1/fdts/rdv1_tb_fw_config.dts rename to plat/arm/board/neoverse_rd/platform/rdv1/fdts/rdv1_tb_fw_config.dts index 49eda273..c370623b 100644 --- a/plat/arm/board/rdv1/fdts/rdv1_tb_fw_config.dts +++ b/plat/arm/board/neoverse_rd/platform/rdv1/fdts/rdv1_tb_fw_config.dts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2020-2024, Arm Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ diff --git a/plat/arm/board/rdv1/include/platform_def.h b/plat/arm/board/neoverse_rd/platform/rdv1/include/platform_def.h similarity index 64% rename from plat/arm/board/rdv1/include/platform_def.h rename to plat/arm/board/neoverse_rd/platform/rdv1/include/platform_def.h index 620fa3e2..cd401174 100644 --- a/plat/arm/board/rdv1/include/platform_def.h +++ b/plat/arm/board/neoverse_rd/platform/rdv1/include/platform_def.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020-2022, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2020-2024, Arm Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -8,19 +8,21 @@ #define PLATFORM_DEF_H #include <lib/utils_def.h> +#include <nrd_css_fw_def1.h> +#include <nrd_plat_arm_def1.h> +#include <nrd_ros_fw_def1.h> -#include <sgi_soc_platform_def.h> +/* Remote chip address offset */ +#define NRD_REMOTE_CHIP_MEM_OFFSET(n) \ + ((ULL(1) << NRD_ADDR_BITS_PER_CHIP) * (n)) #define PLAT_ARM_CLUSTER_COUNT U(16) -#define CSS_SGI_MAX_CPUS_PER_CLUSTER U(1) -#define CSS_SGI_MAX_PE_PER_CPU U(1) +#define NRD_MAX_CPUS_PER_CLUSTER U(1) +#define NRD_MAX_PE_PER_CPU U(1) #define PLAT_CSS_MHU_BASE UL(0x45400000) #define PLAT_MHUV2_BASE PLAT_CSS_MHU_BASE -#define CSS_SYSTEM_PWR_DMN_LVL ARM_PWR_LVL2 -#define PLAT_MAX_PWR_LVL ARM_PWR_LVL1 - /* TZC Related Constants */ #define PLAT_ARM_TZC_BASE UL(0x21830000) #define PLAT_ARM_TZC_FILTERS TZC_400_REGION_ATTR_FILTER_BIT(0) @@ -47,22 +49,10 @@ (TZC_REGION_ACCESS_RDWR(TZC_NSAID_VIRTIO)) /* Maximum number of address bits used per chip */ -#define CSS_SGI_ADDR_BITS_PER_CHIP U(42) - -/* - * Physical and virtual address space limits for MMU in AARCH64 & AARCH32 modes - */ -#ifdef __aarch64__ -#define PLAT_PHY_ADDR_SPACE_SIZE (1ULL << CSS_SGI_ADDR_BITS_PER_CHIP) -#define PLAT_VIRT_ADDR_SPACE_SIZE (1ULL << CSS_SGI_ADDR_BITS_PER_CHIP) -#else -#define PLAT_PHY_ADDR_SPACE_SIZE (1ULL << 32) -#define PLAT_VIRT_ADDR_SPACE_SIZE (1ULL << 32) -#endif +#define NRD_ADDR_BITS_PER_CHIP U(42) /* GIC related constants */ #define PLAT_ARM_GICD_BASE UL(0x30000000) -#define PLAT_ARM_GICC_BASE UL(0x2C000000) #define PLAT_ARM_GICR_BASE UL(0x30140000) #endif /* PLATFORM_DEF_H */ diff --git a/plat/arm/board/rdv1/platform.mk b/plat/arm/board/neoverse_rd/platform/rdv1/platform.mk similarity index 64% rename from plat/arm/board/rdv1/platform.mk rename to plat/arm/board/neoverse_rd/platform/rdv1/platform.mk index a5fba671..db8efbbe 100644 --- a/plat/arm/board/rdv1/platform.mk +++ b/plat/arm/board/neoverse_rd/platform/rdv1/platform.mk @@ -1,4 +1,4 @@ -# Copyright (c) 2020-2021, ARM Limited and Contributors. All rights reserved. +# Copyright (c) 2020-2024, Arm Limited and Contributors. All rights reserved. # # SPDX-License-Identifier: BSD-3-Clause # @@ -6,17 +6,18 @@ # RD-V1 platform uses GIC-700 which is based on GICv4.1 GIC_ENABLE_V4_EXTN := 1 -include plat/arm/css/sgi/sgi-common.mk +include plat/arm/board/neoverse_rd/common/nrd-common.mk -RDV1_BASE = plat/arm/board/rdv1 +RDV1_BASE = plat/arm/board/neoverse_rd/platform/rdv1 -PLAT_INCLUDES += -I${RDV1_BASE}/include/ +PLAT_INCLUDES += -I${NRD_COMMON_BASE}/include/nrd1/ \ + -I${RDV1_BASE}/include/ -SGI_CPU_SOURCES := lib/cpus/aarch64/neoverse_v1.S +NRD_CPU_SOURCES := lib/cpus/aarch64/neoverse_v1.S -PLAT_BL_COMMON_SOURCES += ${CSS_ENT_BASE}/sgi_plat.c +PLAT_BL_COMMON_SOURCES += ${NRD_COMMON_BASE}/nrd_plat1.c -BL1_SOURCES += ${SGI_CPU_SOURCES} \ +BL1_SOURCES += ${NRD_CPU_SOURCES} \ ${RDV1_BASE}/rdv1_err.c BL2_SOURCES += ${RDV1_BASE}/rdv1_plat.c \ @@ -27,7 +28,7 @@ BL2_SOURCES += ${RDV1_BASE}/rdv1_plat.c \ plat/arm/common/arm_tzc400.c \ plat/arm/common/arm_nor_psci_mem_protect.c -BL31_SOURCES += ${SGI_CPU_SOURCES} \ +BL31_SOURCES += ${NRD_CPU_SOURCES} \ ${RDV1_BASE}/rdv1_plat.c \ ${RDV1_BASE}/rdv1_topology.c \ drivers/cfi/v2m/v2m_flash.c \ @@ -57,9 +58,18 @@ NT_FW_CONFIG := ${BUILD_PLAT}/fdts/${PLAT}_nt_fw_config.dtb $(eval $(call TOOL_ADD_PAYLOAD,${NT_FW_CONFIG},--nt-fw-config,${NT_FW_CONFIG})) override CTX_INCLUDE_AARCH32_REGS := 0 -override ENABLE_FEAT_AMU := 1 +override ENABLE_FEAT_AMU := 2 +override SPMD_SPM_AT_SEL2 := 0 -ifneq ($(CSS_SGI_PLATFORM_VARIANT),0) - $(error "CSS_SGI_PLATFORM_VARIANT for RD-V1 should always be 0, \ - currently set to ${CSS_SGI_PLATFORM_VARIANT}.") +ifneq ($(NRD_PLATFORM_VARIANT),0) + $(error "NRD_PLATFORM_VARIANT for RD-V1 should always be 0, \ + currently set to ${NRD_PLATFORM_VARIANT}.") endif + +ifneq (${RESET_TO_BL31},0) + $(error "Using BL31 as the reset vector is not supported on ${PLAT} platform. \ + Please set RESET_TO_BL31 to 0.") +endif + +# Enable the flag since RD-V1 has a system level cache +NEOVERSE_Nx_EXTERNAL_LLC := 1 diff --git a/plat/arm/board/rdv1/rdv1_err.c b/plat/arm/board/neoverse_rd/platform/rdv1/rdv1_err.c similarity index 71% rename from plat/arm/board/rdv1/rdv1_err.c rename to plat/arm/board/neoverse_rd/platform/rdv1/rdv1_err.c index 68f9a3ef..d75f525c 100644 --- a/plat/arm/board/rdv1/rdv1_err.c +++ b/plat/arm/board/neoverse_rd/platform/rdv1/rdv1_err.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2020-2024, Arm Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ diff --git a/plat/arm/board/rdv1/rdv1_plat.c b/plat/arm/board/neoverse_rd/platform/rdv1/rdv1_plat.c similarity index 59% rename from plat/arm/board/rdv1/rdv1_plat.c rename to plat/arm/board/neoverse_rd/platform/rdv1/rdv1_plat.c index ab5251e5..7cdc19a9 100644 --- a/plat/arm/board/rdv1/rdv1_plat.c +++ b/plat/arm/board/neoverse_rd/platform/rdv1/rdv1_plat.c @@ -1,24 +1,25 @@ /* - * Copyright (c) 2020, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2020-2024, Arm Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ #include <plat/common/platform.h> -#include <sgi_plat.h> -unsigned int plat_arm_sgi_get_platform_id(void) +#include <nrd_plat.h> + +unsigned int plat_arm_nrd_get_platform_id(void) { return mmio_read_32(SID_REG_BASE + SID_SYSTEM_ID_OFFSET) & SID_SYSTEM_ID_PART_NUM_MASK; } -unsigned int plat_arm_sgi_get_config_id(void) +unsigned int plat_arm_nrd_get_config_id(void) { return mmio_read_32(SID_REG_BASE + SID_SYSTEM_CFG_OFFSET); } -unsigned int plat_arm_sgi_get_multi_chip_mode(void) +unsigned int plat_arm_nrd_get_multi_chip_mode(void) { return (mmio_read_32(SID_REG_BASE + SID_NODE_ID_OFFSET) & SID_MULTI_CHIP_MODE_MASK) >> SID_MULTI_CHIP_MODE_SHIFT; @@ -26,5 +27,5 @@ unsigned int plat_arm_sgi_get_multi_chip_mode(void) void bl31_platform_setup(void) { - sgi_bl31_common_platform_setup(); + nrd_bl31_common_platform_setup(); } diff --git a/plat/arm/board/rdv1/rdv1_security.c b/plat/arm/board/neoverse_rd/platform/rdv1/rdv1_security.c similarity index 82% rename from plat/arm/board/rdv1/rdv1_security.c rename to plat/arm/board/neoverse_rd/platform/rdv1/rdv1_security.c index 1247db86..a936a71b 100644 --- a/plat/arm/board/rdv1/rdv1_security.c +++ b/plat/arm/board/neoverse_rd/platform/rdv1/rdv1_security.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2020-2024, Arm Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ diff --git a/plat/arm/board/rdv1/rdv1_topology.c b/plat/arm/board/neoverse_rd/platform/rdv1/rdv1_topology.c similarity index 77% rename from plat/arm/board/rdv1/rdv1_topology.c rename to plat/arm/board/neoverse_rd/platform/rdv1/rdv1_topology.c index ab64fd8d..20e4266b 100644 --- a/plat/arm/board/rdv1/rdv1_topology.c +++ b/plat/arm/board/neoverse_rd/platform/rdv1/rdv1_topology.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2020-2024, Arm Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -12,22 +12,22 @@ ******************************************************************************/ const unsigned char rd_v1_pd_tree_desc[] = { PLAT_ARM_CLUSTER_COUNT, - CSS_SGI_MAX_CPUS_PER_CLUSTER, - CSS_SGI_MAX_CPUS_PER_CLUSTER, - CSS_SGI_MAX_CPUS_PER_CLUSTER, - CSS_SGI_MAX_CPUS_PER_CLUSTER, - CSS_SGI_MAX_CPUS_PER_CLUSTER, - CSS_SGI_MAX_CPUS_PER_CLUSTER, - CSS_SGI_MAX_CPUS_PER_CLUSTER, - CSS_SGI_MAX_CPUS_PER_CLUSTER, - CSS_SGI_MAX_CPUS_PER_CLUSTER, - CSS_SGI_MAX_CPUS_PER_CLUSTER, - CSS_SGI_MAX_CPUS_PER_CLUSTER, - CSS_SGI_MAX_CPUS_PER_CLUSTER, - CSS_SGI_MAX_CPUS_PER_CLUSTER, - CSS_SGI_MAX_CPUS_PER_CLUSTER, - CSS_SGI_MAX_CPUS_PER_CLUSTER, - CSS_SGI_MAX_CPUS_PER_CLUSTER + NRD_MAX_CPUS_PER_CLUSTER, + NRD_MAX_CPUS_PER_CLUSTER, + NRD_MAX_CPUS_PER_CLUSTER, + NRD_MAX_CPUS_PER_CLUSTER, + NRD_MAX_CPUS_PER_CLUSTER, + NRD_MAX_CPUS_PER_CLUSTER, + NRD_MAX_CPUS_PER_CLUSTER, + NRD_MAX_CPUS_PER_CLUSTER, + NRD_MAX_CPUS_PER_CLUSTER, + NRD_MAX_CPUS_PER_CLUSTER, + NRD_MAX_CPUS_PER_CLUSTER, + NRD_MAX_CPUS_PER_CLUSTER, + NRD_MAX_CPUS_PER_CLUSTER, + NRD_MAX_CPUS_PER_CLUSTER, + NRD_MAX_CPUS_PER_CLUSTER, + NRD_MAX_CPUS_PER_CLUSTER }; /******************************************************************************* diff --git a/plat/arm/board/rdv1/rdv1_trusted_boot.c b/plat/arm/board/neoverse_rd/platform/rdv1/rdv1_trusted_boot.c similarity index 88% rename from plat/arm/board/rdv1/rdv1_trusted_boot.c rename to plat/arm/board/neoverse_rd/platform/rdv1/rdv1_trusted_boot.c index 4592b8fb..84622d00 100644 --- a/plat/arm/board/rdv1/rdv1_trusted_boot.c +++ b/plat/arm/board/neoverse_rd/platform/rdv1/rdv1_trusted_boot.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, Arm Limited. All rights reserved. + * Copyright (c) 2020-2024, Arm Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ diff --git a/plat/arm/board/rdv1mc/fdts/rdv1mc_fw_config.dts b/plat/arm/board/neoverse_rd/platform/rdv1mc/fdts/rdv1mc_fw_config.dts similarity index 83% rename from plat/arm/board/rdv1mc/fdts/rdv1mc_fw_config.dts rename to plat/arm/board/neoverse_rd/platform/rdv1mc/fdts/rdv1mc_fw_config.dts index 9c9cefe8..d4434437 100644 --- a/plat/arm/board/rdv1mc/fdts/rdv1mc_fw_config.dts +++ b/plat/arm/board/neoverse_rd/platform/rdv1mc/fdts/rdv1mc_fw_config.dts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2020-2024, Arm Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ diff --git a/plat/arm/board/rdv1mc/fdts/rdv1mc_nt_fw_config.dts b/plat/arm/board/neoverse_rd/platform/rdv1mc/fdts/rdv1mc_nt_fw_config.dts similarity index 83% rename from plat/arm/board/rdv1mc/fdts/rdv1mc_nt_fw_config.dts rename to plat/arm/board/neoverse_rd/platform/rdv1mc/fdts/rdv1mc_nt_fw_config.dts index 71c7db3c..78fa31e1 100644 --- a/plat/arm/board/rdv1mc/fdts/rdv1mc_nt_fw_config.dts +++ b/plat/arm/board/neoverse_rd/platform/rdv1mc/fdts/rdv1mc_nt_fw_config.dts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2020-2024, Arm Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ diff --git a/plat/arm/board/rdv1mc/fdts/rdv1mc_tb_fw_config.dts b/plat/arm/board/neoverse_rd/platform/rdv1mc/fdts/rdv1mc_tb_fw_config.dts similarity index 89% rename from plat/arm/board/rdv1mc/fdts/rdv1mc_tb_fw_config.dts rename to plat/arm/board/neoverse_rd/platform/rdv1mc/fdts/rdv1mc_tb_fw_config.dts index 49eda273..c370623b 100644 --- a/plat/arm/board/rdv1mc/fdts/rdv1mc_tb_fw_config.dts +++ b/plat/arm/board/neoverse_rd/platform/rdv1mc/fdts/rdv1mc_tb_fw_config.dts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2020-2024, Arm Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ diff --git a/plat/arm/board/rdv1mc/include/platform_def.h b/plat/arm/board/neoverse_rd/platform/rdv1mc/include/platform_def.h similarity index 69% rename from plat/arm/board/rdv1mc/include/platform_def.h rename to plat/arm/board/neoverse_rd/platform/rdv1mc/include/platform_def.h index 36709048..b4c5c0a2 100644 --- a/plat/arm/board/rdv1mc/include/platform_def.h +++ b/plat/arm/board/neoverse_rd/platform/rdv1mc/include/platform_def.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020-2022, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2020-2024, Arm Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -8,18 +8,21 @@ #define PLATFORM_DEF_H #include <lib/utils_def.h> -#include <sgi_soc_platform_def.h> +#include <nrd_css_fw_def1.h> +#include <nrd_plat_arm_def1.h> +#include <nrd_ros_fw_def1.h> + +/* Remote chip address offset */ +#define NRD_REMOTE_CHIP_MEM_OFFSET(n) \ + ((ULL(1) << NRD_ADDR_BITS_PER_CHIP) * (n)) #define PLAT_ARM_CLUSTER_COUNT U(4) -#define CSS_SGI_MAX_CPUS_PER_CLUSTER U(1) -#define CSS_SGI_MAX_PE_PER_CPU U(1) +#define NRD_MAX_CPUS_PER_CLUSTER U(1) +#define NRD_MAX_PE_PER_CPU U(1) #define PLAT_CSS_MHU_BASE UL(0x45400000) #define PLAT_MHUV2_BASE PLAT_CSS_MHU_BASE -#define CSS_SYSTEM_PWR_DMN_LVL ARM_PWR_LVL2 -#define PLAT_MAX_PWR_LVL ARM_PWR_LVL1 - /* TZC Related Constants */ #define PLAT_ARM_TZC_BASE UL(0x21830000) #define TZC400_BASE(n) (PLAT_ARM_TZC_BASE + \ @@ -47,17 +50,14 @@ #define PLAT_ARM_MEM_PROTEC_VA_FRAME UL(0xC0000000) /* Remote chip address offset (4TB per chip) */ -#define CSS_SGI_ADDR_BITS_PER_CHIP U(42) - -/* Physical and virtual address space limits for MMU in AARCH64 mode */ -#define PLAT_PHY_ADDR_SPACE_SIZE CSS_SGI_REMOTE_CHIP_MEM_OFFSET( \ - CSS_SGI_CHIP_COUNT) -#define PLAT_VIRT_ADDR_SPACE_SIZE CSS_SGI_REMOTE_CHIP_MEM_OFFSET( \ - CSS_SGI_CHIP_COUNT) +#define NRD_ADDR_BITS_PER_CHIP U(42) /* GIC related constants */ #define PLAT_ARM_GICD_BASE UL(0x30000000) -#define PLAT_ARM_GICC_BASE UL(0x2C000000) #define PLAT_ARM_GICR_BASE UL(0x30140000) +/* GIC SPI range for multichip */ +#define NRD_CHIP0_SPI_MIN U(32) +#define NRD_CHIP0_SPI_MAX U(991) + #endif /* PLATFORM_DEF_H */ diff --git a/plat/arm/board/rdv1mc/platform.mk b/plat/arm/board/neoverse_rd/platform/rdv1mc/platform.mk similarity index 65% rename from plat/arm/board/rdv1mc/platform.mk rename to plat/arm/board/neoverse_rd/platform/rdv1mc/platform.mk index 92f7c101..6d518d5c 100644 --- a/plat/arm/board/rdv1mc/platform.mk +++ b/plat/arm/board/neoverse_rd/platform/rdv1mc/platform.mk @@ -1,4 +1,4 @@ -# Copyright (c) 2020-2021, ARM Limited and Contributors. All rights reserved. +# Copyright (c) 2020-2024, Arm Limited and Contributors. All rights reserved. # # SPDX-License-Identifier: BSD-3-Clause # @@ -7,17 +7,18 @@ GIC_ENABLE_V4_EXTN := 1 GICV3_IMPL_GIC600_MULTICHIP := 1 -include plat/arm/css/sgi/sgi-common.mk +include plat/arm/board/neoverse_rd/common/nrd-common.mk -RDV1MC_BASE = plat/arm/board/rdv1mc +RDV1MC_BASE = plat/arm/board/neoverse_rd/platform/rdv1mc -PLAT_INCLUDES += -I${RDV1MC_BASE}/include/ +PLAT_INCLUDES += -I${NRD_COMMON_BASE}/include/nrd1/ \ + -I${RDV1MC_BASE}/include/ -SGI_CPU_SOURCES := lib/cpus/aarch64/neoverse_v1.S +NRD_CPU_SOURCES := lib/cpus/aarch64/neoverse_v1.S -PLAT_BL_COMMON_SOURCES += ${CSS_ENT_BASE}/sgi_plat.c +PLAT_BL_COMMON_SOURCES += ${NRD_COMMON_BASE}/nrd_plat1.c -BL1_SOURCES += ${SGI_CPU_SOURCES} \ +BL1_SOURCES += ${NRD_CPU_SOURCES} \ ${RDV1MC_BASE}/rdv1mc_err.c BL2_SOURCES += ${RDV1MC_BASE}/rdv1mc_plat.c \ @@ -28,7 +29,7 @@ BL2_SOURCES += ${RDV1MC_BASE}/rdv1mc_plat.c \ lib/utils/mem_region.c \ plat/arm/common/arm_nor_psci_mem_protect.c -BL31_SOURCES += ${SGI_CPU_SOURCES} \ +BL31_SOURCES += ${NRD_CPU_SOURCES} \ ${RDV1MC_BASE}/rdv1mc_plat.c \ ${RDV1MC_BASE}/rdv1mc_topology.c \ drivers/cfi/v2m/v2m_flash.c \ @@ -56,9 +57,9 @@ $(eval $(call TOOL_ADD_PAYLOAD,${FW_CONFIG},--fw-config,${FW_CONFIG})) $(eval $(call TOOL_ADD_PAYLOAD,${TB_FW_CONFIG},--tb-fw-config,${TB_FW_CONFIG})) $(eval $(call CREATE_SEQ,SEQ,4)) -ifneq ($(CSS_SGI_CHIP_COUNT),$(filter $(CSS_SGI_CHIP_COUNT),$(SEQ))) +ifneq ($(NRD_CHIP_COUNT),$(filter $(NRD_CHIP_COUNT),$(SEQ))) $(error "Chip count for RD-V1-MC should be either $(SEQ) \ - currently it is set to ${CSS_SGI_CHIP_COUNT}.") + currently it is set to ${NRD_CHIP_COUNT}.") endif FDT_SOURCES += ${RDV1MC_BASE}/fdts/${PLAT}_nt_fw_config.dts @@ -68,9 +69,18 @@ NT_FW_CONFIG := ${BUILD_PLAT}/fdts/${PLAT}_nt_fw_config.dtb $(eval $(call TOOL_ADD_PAYLOAD,${NT_FW_CONFIG},--nt-fw-config,${NT_FW_CONFIG})) override CTX_INCLUDE_AARCH32_REGS := 0 -override ENABLE_FEAT_AMU := 1 +override ENABLE_FEAT_AMU := 2 +override SPMD_SPM_AT_SEL2 := 0 -ifneq ($(CSS_SGI_PLATFORM_VARIANT),0) - $(error "CSS_SGI_PLATFORM_VARIANT for RD-V1-MC should always be 0, \ - currently set to ${CSS_SGI_PLATFORM_VARIANT}.") +ifneq ($(NRD_PLATFORM_VARIANT),0) + $(error "NRD_PLATFORM_VARIANT for RD-V1-MC should always be 0, \ + currently set to ${NRD_PLATFORM_VARIANT}.") endif + +ifneq (${RESET_TO_BL31},0) + $(error "Using BL31 as the reset vector is not supported on ${PLAT} platform. \ + Please set RESET_TO_BL31 to 0.") +endif + +# Enable the flag since RD-V1-MC has a system level cache +NEOVERSE_Nx_EXTERNAL_LLC := 1 diff --git a/plat/arm/board/rdv1mc/rdv1mc_err.c b/plat/arm/board/neoverse_rd/platform/rdv1mc/rdv1mc_err.c similarity index 71% rename from plat/arm/board/rdv1mc/rdv1mc_err.c rename to plat/arm/board/neoverse_rd/platform/rdv1mc/rdv1mc_err.c index 755a5034..b855edd7 100644 --- a/plat/arm/board/rdv1mc/rdv1mc_err.c +++ b/plat/arm/board/neoverse_rd/platform/rdv1mc/rdv1mc_err.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2020-2024, Arm Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ diff --git a/plat/arm/board/rdv1mc/rdv1mc_plat.c b/plat/arm/board/neoverse_rd/platform/rdv1mc/rdv1mc_plat.c similarity index 54% rename from plat/arm/board/rdv1mc/rdv1mc_plat.c rename to plat/arm/board/neoverse_rd/platform/rdv1mc/rdv1mc_plat.c index e4469dcc..5713cb9a 100644 --- a/plat/arm/board/rdv1mc/rdv1mc_plat.c +++ b/plat/arm/board/neoverse_rd/platform/rdv1mc/rdv1mc_plat.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2020-2024, Arm Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -8,47 +8,49 @@ #include <drivers/arm/gic600_multichip.h> #include <plat/arm/common/plat_arm.h> #include <plat/common/platform.h> -#include <sgi_soc_platform_def.h> -#include <sgi_plat.h> + +#include <nrd_plat.h> #if defined(IMAGE_BL31) static const mmap_region_t rdv1mc_dynamic_mmap[] = { - ARM_MAP_SHARED_RAM_REMOTE_CHIP(1), - CSS_SGI_MAP_DEVICE_REMOTE_CHIP(1), - SOC_CSS_MAP_DEVICE_REMOTE_CHIP(1), -#if (CSS_SGI_CHIP_COUNT > 2) - ARM_MAP_SHARED_RAM_REMOTE_CHIP(2), - CSS_SGI_MAP_DEVICE_REMOTE_CHIP(2), - SOC_CSS_MAP_DEVICE_REMOTE_CHIP(2), + NRD_CSS_SHARED_RAM_MMAP(1), + NRD_CSS_PERIPH_MMAP(1), + NRD_ROS_PERIPH_MMAP(1), +#if (NRD_CHIP_COUNT > 2) + NRD_CSS_SHARED_RAM_MMAP(2), + NRD_CSS_PERIPH_MMAP(2), + NRD_ROS_PERIPH_MMAP(2), #endif -#if (CSS_SGI_CHIP_COUNT > 3) - ARM_MAP_SHARED_RAM_REMOTE_CHIP(3), - CSS_SGI_MAP_DEVICE_REMOTE_CHIP(3), - SOC_CSS_MAP_DEVICE_REMOTE_CHIP(3) +#if (NRD_CHIP_COUNT > 3) + NRD_CSS_SHARED_RAM_MMAP(3), + NRD_CSS_PERIPH_MMAP(3), + NRD_ROS_PERIPH_MMAP(3) #endif }; static struct gic600_multichip_data rdv1mc_multichip_data __init = { .rt_owner_base = PLAT_ARM_GICD_BASE, .rt_owner = 0, - .chip_count = CSS_SGI_CHIP_COUNT, + .chip_count = NRD_CHIP_COUNT, .chip_addrs = { PLAT_ARM_GICD_BASE >> 16, - (PLAT_ARM_GICD_BASE + CSS_SGI_REMOTE_CHIP_MEM_OFFSET(1)) >> 16, -#if (CSS_SGI_CHIP_COUNT > 2) - (PLAT_ARM_GICD_BASE + CSS_SGI_REMOTE_CHIP_MEM_OFFSET(2)) >> 16, + (PLAT_ARM_GICD_BASE + NRD_REMOTE_CHIP_MEM_OFFSET(1)) >> 16, +#if (NRD_CHIP_COUNT > 2) + (PLAT_ARM_GICD_BASE + NRD_REMOTE_CHIP_MEM_OFFSET(2)) >> 16, #endif -#if (CSS_SGI_CHIP_COUNT > 3) - (PLAT_ARM_GICD_BASE + CSS_SGI_REMOTE_CHIP_MEM_OFFSET(3)) >> 16, +#if (NRD_CHIP_COUNT > 3) + (PLAT_ARM_GICD_BASE + NRD_REMOTE_CHIP_MEM_OFFSET(3)) >> 16, #endif }, .spi_ids = { - {PLAT_ARM_GICD_BASE, 32, 255}, + {PLAT_ARM_GICD_BASE, + NRD_CHIP0_SPI_MIN, + NRD_CHIP0_SPI_MAX}, {0, 0, 0}, -#if (CSS_SGI_CHIP_COUNT > 2) +#if (NRD_CHIP_COUNT > 2) {0, 0, 0}, #endif -#if (CSS_SGI_CHIP_COUNT > 3) +#if (NRD_CHIP_COUNT > 3) {0, 0, 0}, #endif } @@ -58,31 +60,31 @@ static uintptr_t rdv1mc_multichip_gicr_frames[] = { /* Chip 0's GICR Base */ PLAT_ARM_GICR_BASE, /* Chip 1's GICR BASE */ - PLAT_ARM_GICR_BASE + CSS_SGI_REMOTE_CHIP_MEM_OFFSET(1), -#if (CSS_SGI_CHIP_COUNT > 2) + PLAT_ARM_GICR_BASE + NRD_REMOTE_CHIP_MEM_OFFSET(1), +#if (NRD_CHIP_COUNT > 2) /* Chip 2's GICR BASE */ - PLAT_ARM_GICR_BASE + CSS_SGI_REMOTE_CHIP_MEM_OFFSET(2), + PLAT_ARM_GICR_BASE + NRD_REMOTE_CHIP_MEM_OFFSET(2), #endif -#if (CSS_SGI_CHIP_COUNT > 3) +#if (NRD_CHIP_COUNT > 3) /* Chip 3's GICR BASE */ - PLAT_ARM_GICR_BASE + CSS_SGI_REMOTE_CHIP_MEM_OFFSET(3), + PLAT_ARM_GICR_BASE + NRD_REMOTE_CHIP_MEM_OFFSET(3), #endif UL(0) /* Zero Termination */ }; #endif /* IMAGE_BL31 */ -unsigned int plat_arm_sgi_get_platform_id(void) +unsigned int plat_arm_nrd_get_platform_id(void) { return mmio_read_32(SID_REG_BASE + SID_SYSTEM_ID_OFFSET) & SID_SYSTEM_ID_PART_NUM_MASK; } -unsigned int plat_arm_sgi_get_config_id(void) +unsigned int plat_arm_nrd_get_config_id(void) { return mmio_read_32(SID_REG_BASE + SID_SYSTEM_CFG_OFFSET); } -unsigned int plat_arm_sgi_get_multi_chip_mode(void) +unsigned int plat_arm_nrd_get_multi_chip_mode(void) { return (mmio_read_32(SID_REG_BASE + SID_NODE_ID_OFFSET) & SID_MULTI_CHIP_MODE_MASK) >> SID_MULTI_CHIP_MODE_SHIFT; @@ -99,13 +101,13 @@ void bl31_platform_setup(void) int ret; unsigned int i; - if ((plat_arm_sgi_get_multi_chip_mode() == 0) && - (CSS_SGI_CHIP_COUNT > 1)) { - ERROR("Chip Count is set to %u but multi-chip mode is not " - "enabled\n", CSS_SGI_CHIP_COUNT); + if ((plat_arm_nrd_get_multi_chip_mode() == 0) && + (NRD_CHIP_COUNT > 1)) { + ERROR("Chip Count is %u but multi-chip mode is not enabled\n", + NRD_CHIP_COUNT); panic(); - } else if ((plat_arm_sgi_get_multi_chip_mode() == 1) && - (CSS_SGI_CHIP_COUNT > 1)) { + } else if ((plat_arm_nrd_get_multi_chip_mode() == 1) && + (NRD_CHIP_COUNT > 1)) { INFO("Enabling support for multi-chip in RD-V1-MC\n"); for (i = 0; i < ARRAY_SIZE(rdv1mc_dynamic_mmap); i++) { @@ -126,6 +128,6 @@ void bl31_platform_setup(void) gic600_multichip_init(&rdv1mc_multichip_data); } - sgi_bl31_common_platform_setup(); + nrd_bl31_common_platform_setup(); } #endif /* IMAGE_BL31 */ diff --git a/plat/arm/board/rdv1mc/rdv1mc_security.c b/plat/arm/board/neoverse_rd/platform/rdv1mc/rdv1mc_security.c similarity index 63% rename from plat/arm/board/rdv1mc/rdv1mc_security.c rename to plat/arm/board/neoverse_rd/platform/rdv1mc/rdv1mc_security.c index adc0bf81..1e59831b 100644 --- a/plat/arm/board/rdv1mc/rdv1mc_security.c +++ b/plat/arm/board/neoverse_rd/platform/rdv1mc/rdv1mc_security.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020-2021, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2020-2024, Arm Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -14,29 +14,29 @@ static const arm_tzc_regions_info_t tzc_regions[] = { {} }; -#if CSS_SGI_CHIP_COUNT > 1 -static const arm_tzc_regions_info_t tzc_regions_mc[][CSS_SGI_CHIP_COUNT - 1] = { +#if NRD_CHIP_COUNT > 1 +static const arm_tzc_regions_info_t tzc_regions_mc[][NRD_CHIP_COUNT - 1] = { { /* TZC memory regions for second chip */ - SGI_PLAT_TZC_NS_REMOTE_REGIONS_DEF(1), + NRD_ROS_TZC_NS_REMOTE_REGIONS_DEF(1), {} }, -#if CSS_SGI_CHIP_COUNT > 2 +#if NRD_CHIP_COUNT > 2 { /* TZC memory regions for third chip */ - SGI_PLAT_TZC_NS_REMOTE_REGIONS_DEF(2), + NRD_ROS_TZC_NS_REMOTE_REGIONS_DEF(2), {} }, #endif -#if CSS_SGI_CHIP_COUNT > 3 +#if NRD_CHIP_COUNT > 3 { /* TZC memory regions for fourth chip */ - SGI_PLAT_TZC_NS_REMOTE_REGIONS_DEF(3), + NRD_ROS_TZC_NS_REMOTE_REGIONS_DEF(3), {} }, #endif }; -#endif /* CSS_SGI_CHIP_COUNT */ +#endif /* NRD_CHIP_COUNT */ /* Initialize the secure environment */ void plat_arm_security_setup(void) @@ -49,14 +49,14 @@ void plat_arm_security_setup(void) arm_tzc400_setup(TZC400_BASE(i), tzc_regions); } -#if CSS_SGI_CHIP_COUNT > 1 +#if NRD_CHIP_COUNT > 1 unsigned int j; - for (i = 1; i < CSS_SGI_CHIP_COUNT; i++) { + for (i = 1; i < NRD_CHIP_COUNT; i++) { INFO("Configuring TrustZone Controller for Chip %u\n", i); for (j = 0; j < TZC400_COUNT; j++) { - arm_tzc400_setup(CSS_SGI_REMOTE_CHIP_MEM_OFFSET(i) + arm_tzc400_setup(NRD_REMOTE_CHIP_MEM_OFFSET(i) + TZC400_BASE(j), tzc_regions_mc[i-1]); } } diff --git a/plat/arm/board/rdv1mc/rdv1mc_topology.c b/plat/arm/board/neoverse_rd/platform/rdv1mc/rdv1mc_topology.c similarity index 70% rename from plat/arm/board/rdv1mc/rdv1mc_topology.c rename to plat/arm/board/neoverse_rd/platform/rdv1mc/rdv1mc_topology.c index 4486e5cf..52514ca3 100644 --- a/plat/arm/board/rdv1mc/rdv1mc_topology.c +++ b/plat/arm/board/neoverse_rd/platform/rdv1mc/rdv1mc_topology.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2020-2024, Arm Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -7,34 +7,35 @@ #include <common/debug.h> #include <plat/arm/common/plat_arm.h> #include <plat/arm/css/common/css_pm.h> -#include <sgi_variant.h> + +#include <nrd_variant.h> /****************************************************************************** * The power domain tree descriptor. ******************************************************************************/ const unsigned char rd_v1_mc_pd_tree_desc_multi_chip[] = { - ((PLAT_ARM_CLUSTER_COUNT) * (CSS_SGI_CHIP_COUNT)), - CSS_SGI_MAX_CPUS_PER_CLUSTER, - CSS_SGI_MAX_CPUS_PER_CLUSTER, - CSS_SGI_MAX_CPUS_PER_CLUSTER, - CSS_SGI_MAX_CPUS_PER_CLUSTER, -#if (CSS_SGI_CHIP_COUNT > 1) - CSS_SGI_MAX_CPUS_PER_CLUSTER, - CSS_SGI_MAX_CPUS_PER_CLUSTER, - CSS_SGI_MAX_CPUS_PER_CLUSTER, - CSS_SGI_MAX_CPUS_PER_CLUSTER, + ((PLAT_ARM_CLUSTER_COUNT) * (NRD_CHIP_COUNT)), + NRD_MAX_CPUS_PER_CLUSTER, + NRD_MAX_CPUS_PER_CLUSTER, + NRD_MAX_CPUS_PER_CLUSTER, + NRD_MAX_CPUS_PER_CLUSTER, +#if (NRD_CHIP_COUNT > 1) + NRD_MAX_CPUS_PER_CLUSTER, + NRD_MAX_CPUS_PER_CLUSTER, + NRD_MAX_CPUS_PER_CLUSTER, + NRD_MAX_CPUS_PER_CLUSTER, #endif -#if (CSS_SGI_CHIP_COUNT > 2) - CSS_SGI_MAX_CPUS_PER_CLUSTER, - CSS_SGI_MAX_CPUS_PER_CLUSTER, - CSS_SGI_MAX_CPUS_PER_CLUSTER, - CSS_SGI_MAX_CPUS_PER_CLUSTER, +#if (NRD_CHIP_COUNT > 2) + NRD_MAX_CPUS_PER_CLUSTER, + NRD_MAX_CPUS_PER_CLUSTER, + NRD_MAX_CPUS_PER_CLUSTER, + NRD_MAX_CPUS_PER_CLUSTER, #endif -#if (CSS_SGI_CHIP_COUNT > 3) - CSS_SGI_MAX_CPUS_PER_CLUSTER, - CSS_SGI_MAX_CPUS_PER_CLUSTER, - CSS_SGI_MAX_CPUS_PER_CLUSTER, - CSS_SGI_MAX_CPUS_PER_CLUSTER +#if (NRD_CHIP_COUNT > 3) + NRD_MAX_CPUS_PER_CLUSTER, + NRD_MAX_CPUS_PER_CLUSTER, + NRD_MAX_CPUS_PER_CLUSTER, + NRD_MAX_CPUS_PER_CLUSTER #endif }; @@ -43,7 +44,7 @@ const unsigned char rd_v1_mc_pd_tree_desc_multi_chip[] = { ******************************************************************************/ const unsigned char *plat_get_power_domain_tree_desc(void) { - if (plat_arm_sgi_get_multi_chip_mode() == 1) + if (plat_arm_nrd_get_multi_chip_mode() == 1) return rd_v1_mc_pd_tree_desc_multi_chip; panic(); } @@ -57,19 +58,19 @@ const uint32_t plat_css_core_pos_to_scmi_dmn_id_map[] = { (SET_SCMI_CHANNEL_ID(0x0) | SET_SCMI_DOMAIN_ID(0x1)), (SET_SCMI_CHANNEL_ID(0x0) | SET_SCMI_DOMAIN_ID(0x2)), (SET_SCMI_CHANNEL_ID(0x0) | SET_SCMI_DOMAIN_ID(0x3)), -#if (CSS_SGI_CHIP_COUNT > 1) +#if (NRD_CHIP_COUNT > 1) (SET_SCMI_CHANNEL_ID(0x1) | SET_SCMI_DOMAIN_ID(0x0)), (SET_SCMI_CHANNEL_ID(0x1) | SET_SCMI_DOMAIN_ID(0x1)), (SET_SCMI_CHANNEL_ID(0x1) | SET_SCMI_DOMAIN_ID(0x2)), (SET_SCMI_CHANNEL_ID(0x1) | SET_SCMI_DOMAIN_ID(0x3)), #endif -#if (CSS_SGI_CHIP_COUNT > 2) +#if (NRD_CHIP_COUNT > 2) (SET_SCMI_CHANNEL_ID(0x2) | SET_SCMI_DOMAIN_ID(0x0)), (SET_SCMI_CHANNEL_ID(0x2) | SET_SCMI_DOMAIN_ID(0x1)), (SET_SCMI_CHANNEL_ID(0x2) | SET_SCMI_DOMAIN_ID(0x2)), (SET_SCMI_CHANNEL_ID(0x2) | SET_SCMI_DOMAIN_ID(0x3)), #endif -#if (CSS_SGI_CHIP_COUNT > 3) +#if (NRD_CHIP_COUNT > 3) (SET_SCMI_CHANNEL_ID(0x3) | SET_SCMI_DOMAIN_ID(0x0)), (SET_SCMI_CHANNEL_ID(0x3) | SET_SCMI_DOMAIN_ID(0x1)), (SET_SCMI_CHANNEL_ID(0x3) | SET_SCMI_DOMAIN_ID(0x2)), diff --git a/plat/arm/board/rdv1mc/rdv1mc_trusted_boot.c b/plat/arm/board/neoverse_rd/platform/rdv1mc/rdv1mc_trusted_boot.c similarity index 88% rename from plat/arm/board/rdv1mc/rdv1mc_trusted_boot.c rename to plat/arm/board/neoverse_rd/platform/rdv1mc/rdv1mc_trusted_boot.c index 4592b8fb..84622d00 100644 --- a/plat/arm/board/rdv1mc/rdv1mc_trusted_boot.c +++ b/plat/arm/board/neoverse_rd/platform/rdv1mc/rdv1mc_trusted_boot.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, Arm Limited. All rights reserved. + * Copyright (c) 2020-2024, Arm Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ diff --git a/plat/arm/board/rdn2/fdts/rdn2_fw_config.dts b/plat/arm/board/neoverse_rd/platform/rdv3/fdts/rdv3_fw_config.dts similarity index 71% rename from plat/arm/board/rdn2/fdts/rdn2_fw_config.dts rename to plat/arm/board/neoverse_rd/platform/rdv3/fdts/rdv3_fw_config.dts index 9c9cefe8..62ba0fad 100644 --- a/plat/arm/board/rdn2/fdts/rdn2_fw_config.dts +++ b/plat/arm/board/neoverse_rd/platform/rdv3/fdts/rdv3_fw_config.dts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2024, Arm Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -13,13 +13,13 @@ compatible = "fconf,dyn_cfg-dtb_registry"; tb_fw-config { - load-address = <0x0 0x4001300>; + load-address = <0x0 0x01f300>; max-size = <0x200>; id = <TB_FW_CONFIG_ID>; }; nt_fw-config { - load-address = <0x0 0xFEF00000>; + load-address = <0x0 0xF3000000>; max-size = <0x0100000>; id = <NT_FW_CONFIG_ID>; }; diff --git a/plat/arm/board/rde1edge/fdts/rde1edge_nt_fw_config.dts b/plat/arm/board/neoverse_rd/platform/rdv3/fdts/rdv3_nt_fw_config.dts similarity index 78% rename from plat/arm/board/rde1edge/fdts/rde1edge_nt_fw_config.dts rename to plat/arm/board/neoverse_rd/platform/rdv3/fdts/rdv3_nt_fw_config.dts index 0af821e1..941d4a03 100644 --- a/plat/arm/board/rde1edge/fdts/rde1edge_nt_fw_config.dts +++ b/plat/arm/board/neoverse_rd/platform/rdv3/fdts/rdv3_nt_fw_config.dts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018-2020, Arm Limited. All rights reserved. + * Copyright (c) 2024, Arm Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -7,7 +7,7 @@ /dts-v1/; / { /* compatible string */ - compatible = "arm,rd-e1edge"; + compatible = "arm,rd-v3"; /* * Place holder for system-id node with default values. The @@ -19,5 +19,4 @@ config-id = <0x0>; multi-chip-mode = <0x0>; }; - }; diff --git a/plat/arm/board/rde1edge/fdts/rde1edge_tb_fw_config.dts b/plat/arm/board/neoverse_rd/platform/rdv3/fdts/rdv3_tb_fw_config.dts similarity index 90% rename from plat/arm/board/rde1edge/fdts/rde1edge_tb_fw_config.dts rename to plat/arm/board/neoverse_rd/platform/rdv3/fdts/rdv3_tb_fw_config.dts index dba91e53..a4c7c728 100644 --- a/plat/arm/board/rde1edge/fdts/rde1edge_tb_fw_config.dts +++ b/plat/arm/board/neoverse_rd/platform/rdv3/fdts/rdv3_tb_fw_config.dts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, ARM Limited. All rights reserved. + * Copyright (c) 2024, Arm Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ diff --git a/plat/arm/board/neoverse_rd/platform/rdv3/include/platform_def.h b/plat/arm/board/neoverse_rd/platform/rdv3/include/platform_def.h new file mode 100644 index 00000000..b55dbe80 --- /dev/null +++ b/plat/arm/board/neoverse_rd/platform/rdv3/include/platform_def.h @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2024, Arm Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef PLATFORM_DEF_H +#define PLATFORM_DEF_H + +#include <plat/arm/css/common/css_def.h> +#include <nrd_css_fw_def3.h> +#include <nrd_pas_def3.h> +#include <nrd_plat_arm_def3.h> +#include <nrd_ros_fw_def3.h> + +/* Remote chip address offset */ +#define NRD_REMOTE_CHIP_MEM_OFFSET(n) \ + ((ULL(1) << NRD_ADDR_BITS_PER_CHIP) * (n)) + +/* PE-Cluster count */ +#if (NRD_PLATFORM_VARIANT == 1) +#define PLAT_ARM_CLUSTER_COUNT U(8) +#elif (NRD_PLATFORM_VARIANT == 2) +#define PLAT_ARM_CLUSTER_COUNT U(4) +#else +#define PLAT_ARM_CLUSTER_COUNT U(16) +#endif +#define NRD_MAX_CPUS_PER_CLUSTER U(1) +#define NRD_MAX_PE_PER_CPU U(1) + +/* Shared RAM*/ +#define NRD_CSS_SHARED_SRAM_SIZE UL(0x000100000) + +/* DRAM1 */ +#define NRD_CSS_DRAM1_SIZE ULL(0x80000000) + +/* DRAM2 */ +#define NRD_CSS_DRAM2_SIZE ULL(0x180000000) + +/* Address bits */ +#define NRD_ADDR_BITS_PER_CHIP U(36) /* 64GB */ + +/* + * In the current implementation, the RoT Service request that requires the + * biggest message buffer is the RSE_DELEGATED_ATTEST_GET_PLATFORM_TOKEN. The + * maximum required buffer size is calculated based on the platform-specific + * needs of this request. + */ +#define PLAT_RSE_COMMS_PAYLOAD_MAX_SIZE UL(0x1000) + +#endif /* PLATFORM_DEF_H */ diff --git a/plat/arm/board/neoverse_rd/platform/rdv3/include/rdv3_mhuv3.h b/plat/arm/board/neoverse_rd/platform/rdv3/include/rdv3_mhuv3.h new file mode 100644 index 00000000..fa64963b --- /dev/null +++ b/plat/arm/board/neoverse_rd/platform/rdv3/include/rdv3_mhuv3.h @@ -0,0 +1,12 @@ +/* + * Copyright (c) 2024, Arm Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef RDV3_MHUV3_H +#define RDV3_MHUV3_H + +void mhu_v3_get_secure_device_base(uintptr_t *base, bool sender); + +#endif /* RDV3_MHUV3_H */ diff --git a/plat/arm/board/neoverse_rd/platform/rdv3/include/rdv3_rse_comms.h b/plat/arm/board/neoverse_rd/platform/rdv3/include/rdv3_rse_comms.h new file mode 100644 index 00000000..cb8e786b --- /dev/null +++ b/plat/arm/board/neoverse_rd/platform/rdv3/include/rdv3_rse_comms.h @@ -0,0 +1,12 @@ +/* + * Copyright (c) 2024, Arm Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef RDV3_RSE_COMMS_H +#define RDV3_RSE_COMMS_H + +int plat_rse_comms_init(void); + +#endif /* RDV3_RSE_COMMS_H */ diff --git a/plat/arm/board/neoverse_rd/platform/rdv3/platform.mk b/plat/arm/board/neoverse_rd/platform/rdv3/platform.mk new file mode 100644 index 00000000..f37d9038 --- /dev/null +++ b/plat/arm/board/neoverse_rd/platform/rdv3/platform.mk @@ -0,0 +1,156 @@ +# Copyright (c) 2024, Arm Limited and Contributors. All rights reserved. +# +# SPDX-License-Identifier: BSD-3-Clause +# + +RD_V3_VARIANTS := 0 1 2 +ifneq ($(NRD_PLATFORM_VARIANT), \ + $(filter $(NRD_PLATFORM_VARIANT),$(RD_V3_VARIANTS))) + $(error "NRD_PLATFORM_VARIANT for RD-V3 should be 0, 1, or 2," + "currently set to ${NRD_PLATFORM_VARIANT}.") +endif + +$(eval $(call CREATE_SEQ,SEQ,4)) +ifneq ($(NRD_CHIP_COUNT),$(filter $(NRD_CHIP_COUNT),$(SEQ))) + $(error "Chip count for RD-V3-MC should be either $(SEQ) \ + currently it is set to ${NRD_CHIP_COUNT}.") +endif + +# Build options +# Major and Minor versions +override ARM_ARCH_MAJOR := 8 +override ARM_ARCH_MINOR := 7 + +# Misc options +override CTX_INCLUDE_AARCH32_REGS := 0 + +ifeq (${PLAT_RESET_TO_BL31}, 1) +# Support for BL31 boot flow +override RESET_TO_BL31 := 1 + +# arm_common.mk sets ENABLE_PIE=1, but Makefile blocks PIE for RME +override ENABLE_PIE := 0 + +# Non Trusted Firmware parameters +override ARM_PRELOADED_DTB_BASE := 0xF3000000 +override ARM_LINUX_KERNEL_AS_BL33 := 1 +override PRELOADED_BL33_BASE := 0xE0000000 + +# These are internal build flags but as of now RESET_TO_BL31 won't work without defining them +override NEED_BL1 := no +override NEED_BL2 := no +override NEED_BL32 := no +endif + +# RD-V3 platform uses GIC-700 which is based on GICv4.1 +GIC_ENABLE_V4_EXTN := 1 + +# Enable GIC multichip extension only for multichip platforms +ifeq (${NRD_PLATFORM_VARIANT}, 2) +GICV3_IMPL_GIC600_MULTICHIP := 1 +endif + +# RD-V3 uses MHUv3 +PLAT_MHU_VERSION := 3 + +include plat/arm/board/neoverse_rd/common/nrd-common.mk +include drivers/arm/rse/rse_comms.mk +include drivers/auth/mbedtls/mbedtls_common.mk +ifeq (${MEASURED_BOOT},1) +include drivers/measured_boot/rse/rse_measured_boot.mk +endif + +RDV3_BASE = plat/arm/board/neoverse_rd/platform/rdv3 + +PLAT_INCLUDES += -I${NRD_COMMON_BASE}/include/nrd3/ \ + -I${RDV3_BASE}/include/ \ + -Iinclude/lib/psa + +NRD_CPU_SOURCES := lib/cpus/aarch64/neoverse_v3.S + +# Source files for RD-V3 variants +PLAT_BL_COMMON_SOURCES \ + += ${NRD_COMMON_BASE}/nrd_plat3.c \ + ${RDV3_BASE}/rdv3_common.c + +PLAT_MEASURED_BOOT_SOURCES \ + := ${MEASURED_BOOT_SOURCES} \ + ${RSE_COMMS_SOURCES} \ + ${RDV3_BASE}/rdv3_common_measured_boot.c \ + lib/psa/measured_boot.c + +BL1_SOURCES += ${NRD_CPU_SOURCES} \ + ${RDV3_BASE}/rdv3_err.c \ + ${RDV3_BASE}/rdv3_mhuv3.c +ifeq (${TRUSTED_BOARD_BOOT}, 1) +BL1_SOURCES += ${RDV3_BASE}/rdv3_trusted_boot.c +endif +ifeq (${MEASURED_BOOT},1) +BL1_SOURCES += ${PLAT_MEASURED_BOOT_SOURCES} \ + ${RDV3_BASE}/rdv3_bl1_measured_boot.c +endif + +BL2_SOURCES += ${RDV3_BASE}/rdv3_bl2_setup.c \ + ${RDV3_BASE}/rdv3_err.c \ + ${RDV3_BASE}/rdv3_mhuv3.c \ + ${RDV3_BASE}/rdv3_security.c \ + lib/utils/mem_region.c \ + plat/arm/common/arm_nor_psci_mem_protect.c +ifeq (${TRUSTED_BOARD_BOOT}, 1) +BL2_SOURCES += ${RDV3_BASE}/rdv3_trusted_boot.c +endif +ifeq (${MEASURED_BOOT},1) +BL2_SOURCES += ${PLAT_MEASURED_BOOT_SOURCES} \ + ${RDV3_BASE}/rdv3_bl2_measured_boot.c +endif + +ifeq (${PLAT_RESET_TO_BL31}, 1) +BL31_SOURCES += ${RDV3_BASE}/rdv3_security.c +endif + +BL31_SOURCES += ${NRD_CPU_SOURCES} \ + ${MBEDTLS_SOURCES} \ + ${RSE_COMMS_SOURCES} \ + ${RDV3_BASE}/rdv3_bl31_setup.c \ + ${RDV3_BASE}/rdv3_mhuv3.c \ + ${RDV3_BASE}/rdv3_topology.c \ + ${RDV3_BASE}/rdv3_plat_attest_token.c \ + ${RDV3_BASE}/rdv3_realm_attest_key.c \ + drivers/arm/smmu/smmu_v3.c \ + drivers/cfi/v2m/v2m_flash.c \ + lib/psa/cca_attestation.c \ + lib/psa/delegated_attestation.c \ + lib/utils/mem_region.c \ + plat/arm/common/arm_dyn_cfg.c \ + plat/arm/common/arm_nor_psci_mem_protect.c +ifeq (${NRD_PLATFORM_VARIANT}, 2) +BL31_SOURCES += drivers/arm/gic/v3/gic600_multichip.c +endif + +# XLAT options for RD-V3 variants +BL31_CFLAGS += -DPLAT_XLAT_TABLES_DYNAMIC +BL2_CFLAGS += -DPLAT_XLAT_TABLES_DYNAMIC + +# Add the FDT_SOURCES and options for Dynamic Config +FDT_SOURCES += ${RDV3_BASE}/fdts/${PLAT}_fw_config.dts \ + ${RDV3_BASE}/fdts/${PLAT}_tb_fw_config.dts \ + ${RDV3_BASE}/fdts/${PLAT}_nt_fw_config.dts + +FW_CONFIG := ${BUILD_PLAT}/fdts/${PLAT}_fw_config.dtb +TB_FW_CONFIG := ${BUILD_PLAT}/fdts/${PLAT}_tb_fw_config.dtb +NT_FW_CONFIG := ${BUILD_PLAT}/fdts/${PLAT}_nt_fw_config.dtb + +# Add the FW_CONFIG to FIP and specify the same to certtool +$(eval $(call TOOL_ADD_PAYLOAD,${FW_CONFIG},--fw-config,${FW_CONFIG})) +# Add the TB_FW_CONFIG to FIP and specify the same to certtool +$(eval $(call TOOL_ADD_PAYLOAD,${TB_FW_CONFIG},--tb-fw-config,${TB_FW_CONFIG})) +# Add the NT_FW_CONFIG to FIP and specify the same to certtool +$(eval $(call TOOL_ADD_PAYLOAD,${NT_FW_CONFIG},--nt-fw-config)) + +# Features for RD-V3 variants +override ENABLE_FEAT_MPAM := 2 +override ENABLE_FEAT_AMU := 2 +override ENABLE_SVE_FOR_SWD := 1 +override ENABLE_SVE_FOR_NS := 2 +override ENABLE_FEAT_MTE2 := 2 +override CTX_INCLUDE_SVE_REGS := 1 diff --git a/plat/arm/board/neoverse_rd/platform/rdv3/rdv3_bl1_measured_boot.c b/plat/arm/board/neoverse_rd/platform/rdv3/rdv3_bl1_measured_boot.c new file mode 100644 index 00000000..4db9a111 --- /dev/null +++ b/plat/arm/board/neoverse_rd/platform/rdv3/rdv3_bl1_measured_boot.c @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2024, Arm Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <stdint.h> + +#include <drivers/arm/rse_comms.h> +#include <drivers/measured_boot/rse/rse_measured_boot.h> +#include <lib/psa/measured_boot.h> +#include <plat/arm/common/plat_arm.h> +#include <platform_def.h> + +#include <nrd_plat.h> +#include <rdv3_rse_comms.h> + +/* + * Platform specific table with image IDs and metadata. Intentionally not a + * const struct, some members might set by bootloaders during trusted boot. + */ +struct rse_mboot_metadata rdv3_rse_mboot_metadata[] = { + { + .id = FW_CONFIG_ID, + .slot = U(8), + .signer_id_size = SIGNER_ID_MIN_SIZE, + .sw_type = MBOOT_FW_CONFIG_STRING, + .lock_measurement = false + }, + { + .id = TB_FW_CONFIG_ID, + .slot = U(9), + .signer_id_size = SIGNER_ID_MIN_SIZE, + .sw_type = MBOOT_TB_FW_CONFIG_STRING, + .lock_measurement = false + }, + { + .id = BL2_IMAGE_ID, + .slot = U(10), + .signer_id_size = SIGNER_ID_MIN_SIZE, + .sw_type = MBOOT_BL2_IMAGE_STRING, + .lock_measurement = false + }, + { + .id = RSE_MBOOT_INVALID_ID + } +}; + +void bl1_plat_mboot_init(void) +{ + /* Initialize the communication channel between AP and RSE */ + (void)plat_rse_comms_init(); + + rse_measured_boot_init(rdv3_rse_mboot_metadata); +} + +void bl1_plat_mboot_finish(void) +{ + /* Nothing to do. */ +} diff --git a/plat/arm/board/neoverse_rd/platform/rdv3/rdv3_bl2_measured_boot.c b/plat/arm/board/neoverse_rd/platform/rdv3/rdv3_bl2_measured_boot.c new file mode 100644 index 00000000..1b94427a --- /dev/null +++ b/plat/arm/board/neoverse_rd/platform/rdv3/rdv3_bl2_measured_boot.c @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2024, Arm Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <stdint.h> + +#include <drivers/arm/rse_comms.h> +#include <drivers/measured_boot/rse/rse_measured_boot.h> +#include <lib/psa/measured_boot.h> +#include <plat/common/common_def.h> +#include <platform_def.h> + +#include <nrd_plat.h> +#include <rdv3_rse_comms.h> + +/* + * Platform specific table with image IDs and metadata. Intentionally not a + * const struct, some members might set by bootloaders during trusted boot. + */ +struct rse_mboot_metadata rdv3_rse_mboot_metadata[] = { + { + .id = BL31_IMAGE_ID, + .slot = U(11), + .signer_id_size = SIGNER_ID_MIN_SIZE, + .sw_type = MBOOT_BL31_IMAGE_STRING, + .lock_measurement = false + }, + { + .id = HW_CONFIG_ID, + .slot = U(12), + .signer_id_size = SIGNER_ID_MIN_SIZE, + .sw_type = MBOOT_HW_CONFIG_STRING, + .lock_measurement = false + }, + { + .id = SOC_FW_CONFIG_ID, + .slot = U(13), + .signer_id_size = SIGNER_ID_MIN_SIZE, + .sw_type = MBOOT_SOC_FW_CONFIG_STRING, + .lock_measurement = false + }, +#if ENABLE_RME + { + .id = RMM_IMAGE_ID, + .slot = U(14), + .signer_id_size = SIGNER_ID_MIN_SIZE, + .sw_type = MBOOT_RMM_IMAGE_STRING, + .lock_measurement = false + }, +#endif /* ENABLE_RME */ + { + .id = RSE_MBOOT_INVALID_ID + } +}; + +void bl2_plat_mboot_init(void) +{ + /* Initialize the communication channel between AP and RSE */ + (void)plat_rse_comms_init(); + + rse_measured_boot_init(rdv3_rse_mboot_metadata); +} + +void bl2_plat_mboot_finish(void) +{ + /* Nothing to do. */ +} diff --git a/plat/arm/board/neoverse_rd/platform/rdv3/rdv3_bl2_setup.c b/plat/arm/board/neoverse_rd/platform/rdv3/rdv3_bl2_setup.c new file mode 100644 index 00000000..8dac8d3a --- /dev/null +++ b/plat/arm/board/neoverse_rd/platform/rdv3/rdv3_bl2_setup.c @@ -0,0 +1,97 @@ +/* + * Copyright (c) 2024, Arm Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <common/debug.h> +#include <lib/gpt_rme/gpt_rme.h> +#include <plat/arm/common/plat_arm.h> +#include <plat/common/platform.h> +#include <nrd_plat.h> + +/* + * The GPT library might modify the gpt regions structure to optimize + * the layout, so the array cannot be constant. + */ +static pas_region_t pas_regions[] = { + NRD_PAS_SHARED_SRAM, + NRD_PAS_SYSTEM_NCI, + NRD_PAS_DEBUG_NIC, + NRD_PAS_NS_UART, + NRD_PAS_REALM_UART, + NRD_PAS_AP_NS_WDOG, + NRD_PAS_AP_ROOT_WDOG, + NRD_PAS_AP_SECURE_WDOG, + NRD_PAS_SECURE_SRAM_ERB_AP, + NRD_PAS_NS_SRAM_ERB_AP, + NRD_PAS_ROOT_SRAM_ERB_AP, + NRD_PAS_REALM_SRAM_ERB_AP, + NRD_PAS_SECURE_SRAM_ERB_SCP, + NRD_PAS_NS_SRAM_ERB_SCP, + NRD_PAS_ROOT_SRAM_ERB_SCP, + NRD_PAS_REALM_SRAM_ERB_SCP, + NRD_PAS_SECURE_SRAM_ERB_MCP, + NRD_PAS_NS_SRAM_ERB_MCP, + NRD_PAS_ROOT_SRAM_ERB_MCP, + NRD_PAS_REALM_SRAM_ERB_MCP, + NRD_PAS_SECURE_SRAM_ERB_RSE, + NRD_PAS_NS_SRAM_ERB_RSE, + NRD_PAS_ROOT_SRAM_ERB_RSE, + NRD_PAS_REALM_SRAM_ERB_RSE, + NRD_PAS_RSE_SECURE_SRAM_ERB_RSM, + NRD_PAS_RSE_NS_SRAM_ERB_RSM, + NRD_PAS_SCP_SECURE_SRAM_ERB_RSM, + NRD_PAS_SCP_NS_SRAM_ERB_RSM, + NRD_PAS_MCP_SECURE_SRAM_ERB_RSM, + NRD_PAS_MCP_NS_SRAM_ERB_RSM, + NRD_PAS_AP_SCP_ROOT_MHU, + NRD_PAS_AP_MCP_NS_MHU, + NRD_PAS_AP_MCP_SECURE_MHU, + NRD_PAS_AP_MCP_ROOT_MHU, + NRD_PAS_AP_RSE_NS_MHU, + NRD_PAS_AP_RSE_SECURE_MHU, + NRD_PAS_AP_RSE_ROOT_MHU, + NRD_PAS_AP_RSE_REALM_MHU, + NRD_PAS_SCP_MCP_RSE_CROSS_CHIP_MHU, + NRD_PAS_SYNCNT_MSTUPDTVAL_ADDR, + NRD_PAS_STM_SYSTEM_ITS, + NRD_PAS_SCP_MCP_RSE_SHARED_SRAM, + NRD_PAS_GIC, + NRD_PAS_NS_DRAM, + NRD_PAS_RMM, + NRD_PAS_L1GPT, + NRD_PAS_CMN, + NRD_PAS_LCP_PERIPHERAL, + NRD_PAS_DDR_IO, + NRD_PAS_SMMU_NCI_IO, + NRD_PAS_DRAM2_CHIP0, +#if NRD_CHIP_COUNT > 1 + NRD_PAS_DRAM1_CHIP1, + NRD_PAS_DRAM2_CHIP1, +#endif +#if NRD_CHIP_COUNT > 2 + NRD_PAS_DRAM1_CHIP2, + NRD_PAS_DRAM2_CHIP2, +#endif +#if NRD_CHIP_COUNT > 3 + NRD_PAS_DRAM1_CHIP3, + NRD_PAS_DRAM2_CHIP3 +#endif +}; + +static const arm_gpt_info_t arm_gpt_info = { + .pas_region_base = pas_regions, + .pas_region_count = (unsigned int)ARRAY_SIZE(pas_regions), + .l0_base = (uintptr_t)ARM_L0_GPT_BASE, + .l1_base = (uintptr_t)ARM_L1_GPT_BASE, + .l0_size = (size_t)ARM_L0_GPT_SIZE, + .l1_size = (size_t)ARM_L1_GPT_SIZE, + .pps = GPCCR_PPS_256TB, + .pgs = GPCCR_PGS_4K +}; + +const arm_gpt_info_t *plat_arm_get_gpt_info(void) +{ + return &arm_gpt_info; +} diff --git a/plat/arm/board/neoverse_rd/platform/rdv3/rdv3_bl31_setup.c b/plat/arm/board/neoverse_rd/platform/rdv3/rdv3_bl31_setup.c new file mode 100644 index 00000000..a5d687e8 --- /dev/null +++ b/plat/arm/board/neoverse_rd/platform/rdv3/rdv3_bl31_setup.c @@ -0,0 +1,221 @@ +/* + * Copyright (c) 2024, Arm Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <common/debug.h> +#include <drivers/arm/gic600_multichip.h> +#include <drivers/arm/rse_comms.h> +#include <drivers/arm/smmu_v3.h> + +#include <plat/arm/common/plat_arm.h> +#include <plat/common/platform.h> +#include <nrd_plat.h> +#include <nrd_variant.h> +#include <rdv3_rse_comms.h> + +#if (NRD_PLATFORM_VARIANT == 2) +static const mmap_region_t rdv3mc_dynamic_mmap[] = { +#if NRD_CHIP_COUNT > 1 + NRD_CSS_SHARED_RAM_MMAP(1), + NRD_CSS_PERIPH_MMAP(1), +#endif +#if NRD_CHIP_COUNT > 2 + NRD_CSS_SHARED_RAM_MMAP(2), + NRD_CSS_PERIPH_MMAP(2), +#endif +#if NRD_CHIP_COUNT > 3 + NRD_CSS_SHARED_RAM_MMAP(3), + NRD_CSS_PERIPH_MMAP(3), +#endif +}; + +static struct gic600_multichip_data rdv3mc_multichip_data __init = { + .rt_owner_base = PLAT_ARM_GICD_BASE, + .rt_owner = 0, + .chip_count = NRD_CHIP_COUNT, + .chip_addrs = { + PLAT_ARM_GICD_BASE >> 16, +#if NRD_CHIP_COUNT > 1 + (PLAT_ARM_GICD_BASE + NRD_REMOTE_CHIP_MEM_OFFSET(1)) >> 16, +#endif +#if NRD_CHIP_COUNT > 2 + (PLAT_ARM_GICD_BASE + NRD_REMOTE_CHIP_MEM_OFFSET(2)) >> 16, +#endif +#if NRD_CHIP_COUNT > 3 + (PLAT_ARM_GICD_BASE + NRD_REMOTE_CHIP_MEM_OFFSET(3)) >> 16, +#endif + }, + .spi_ids = { + {PLAT_ARM_GICD_BASE, 32, 511}, +#if NRD_CHIP_COUNT > 1 + {PLAT_ARM_GICD_BASE, 512, 991}, +#endif +#if NRD_CHIP_COUNT > 2 + {PLAT_ARM_GICD_BASE, 4096, 4575}, +#endif +#if NRD_CHIP_COUNT > 3 + {PLAT_ARM_GICD_BASE, 4576, 5055}, +#endif + } +}; + +static uintptr_t rdv3mc_multichip_gicr_frames[] = { + /* Chip 0's GICR Base */ + PLAT_ARM_GICR_BASE, +#if NRD_CHIP_COUNT > 1 + /* Chip 1's GICR BASE */ + PLAT_ARM_GICR_BASE + NRD_REMOTE_CHIP_MEM_OFFSET(1), +#endif +#if NRD_CHIP_COUNT > 2 + /* Chip 2's GICR BASE */ + PLAT_ARM_GICR_BASE + NRD_REMOTE_CHIP_MEM_OFFSET(2), +#endif +#if NRD_CHIP_COUNT > 3 + /* Chip 3's GICR BASE */ + PLAT_ARM_GICR_BASE + NRD_REMOTE_CHIP_MEM_OFFSET(3), +#endif + UL(0) /* Zero Termination */ +}; +#endif /* NRD_PLATFORM_VARIANT == 2 */ + +void bl31_platform_setup(void) +{ + /* + * Perform SMMUv3 GPT configuration for the GPC SMMU present in system + * control block on RD-V3 platforms. This SMMUv3 initialization is + * not fatal. + * + * Don't perform smmuv3_security_init() for this instance of SMMUv3 as + * the global aborts need not be configured to allow the components in + * system control block send transations downstream to SMMUv3. + */ + if (smmuv3_init(NRD_CSS_GPC_SMMUV3_BASE) != 0) { + WARN("Failed initializing System SMMU.\n"); + } + +#if (NRD_PLATFORM_VARIANT == 2) + int ret; + unsigned int i; + + if (plat_arm_nrd_get_multi_chip_mode() == 0) { + ERROR("Chip Count is %u but multi-chip mode is not enabled\n", + NRD_CHIP_COUNT); + panic(); + } else { + INFO("Enabling multi-chip support for RD-V3 variant\n"); + + for (i = 0; i < ARRAY_SIZE(rdv3mc_dynamic_mmap); i++) { + ret = mmap_add_dynamic_region( + rdv3mc_dynamic_mmap[i].base_pa, + rdv3mc_dynamic_mmap[i].base_va, + rdv3mc_dynamic_mmap[i].size, + rdv3mc_dynamic_mmap[i].attr); + if (ret != 0) { + ERROR("Failed to add entry i: %d (ret=%d)\n", + i, ret); + panic(); + } + } + + plat_arm_override_gicr_frames( + rdv3mc_multichip_gicr_frames); + gic600_multichip_init(&rdv3mc_multichip_data); + } +#endif /* NRD_PLATFORM_VARIANT == 2 */ + nrd_bl31_common_platform_setup(); + + if (plat_rse_comms_init() != 0) { + WARN("Failed initializing AP-RSE comms.\n"); + } +} + +#if RESET_TO_BL31 +/* + * The GPT library might modify the gpt regions structure to optimize + * the layout, so the array cannot be constant. + */ +static pas_region_t pas_regions[] = { + NRD_PAS_SHARED_SRAM, + NRD_PAS_SYSTEM_NCI, + NRD_PAS_DEBUG_NIC, + NRD_PAS_NS_UART, + NRD_PAS_REALM_UART, + NRD_PAS_AP_NS_WDOG, + NRD_PAS_AP_ROOT_WDOG, + NRD_PAS_AP_SECURE_WDOG, + NRD_PAS_SECURE_SRAM_ERB_AP, + NRD_PAS_NS_SRAM_ERB_AP, + NRD_PAS_ROOT_SRAM_ERB_AP, + NRD_PAS_REALM_SRAM_ERB_AP, + NRD_PAS_SECURE_SRAM_ERB_SCP, + NRD_PAS_NS_SRAM_ERB_SCP, + NRD_PAS_ROOT_SRAM_ERB_SCP, + NRD_PAS_REALM_SRAM_ERB_SCP, + NRD_PAS_SECURE_SRAM_ERB_MCP, + NRD_PAS_NS_SRAM_ERB_MCP, + NRD_PAS_ROOT_SRAM_ERB_MCP, + NRD_PAS_REALM_SRAM_ERB_MCP, + NRD_PAS_SECURE_SRAM_ERB_RSE, + NRD_PAS_NS_SRAM_ERB_RSE, + NRD_PAS_ROOT_SRAM_ERB_RSE, + NRD_PAS_REALM_SRAM_ERB_RSE, + NRD_PAS_RSE_SECURE_SRAM_ERB_RSM, + NRD_PAS_RSE_NS_SRAM_ERB_RSM, + NRD_PAS_SCP_SECURE_SRAM_ERB_RSM, + NRD_PAS_SCP_NS_SRAM_ERB_RSM, + NRD_PAS_MCP_SECURE_SRAM_ERB_RSM, + NRD_PAS_MCP_NS_SRAM_ERB_RSM, + NRD_PAS_AP_SCP_ROOT_MHU, + NRD_PAS_AP_MCP_NS_MHU, + NRD_PAS_AP_MCP_SECURE_MHU, + NRD_PAS_AP_MCP_ROOT_MHU, + NRD_PAS_AP_RSE_NS_MHU, + NRD_PAS_AP_RSE_SECURE_MHU, + NRD_PAS_AP_RSE_ROOT_MHU, + NRD_PAS_AP_RSE_REALM_MHU, + NRD_PAS_SCP_MCP_RSE_CROSS_CHIP_MHU, + NRD_PAS_SYNCNT_MSTUPDTVAL_ADDR, + NRD_PAS_STM_SYSTEM_ITS, + NRD_PAS_SCP_MCP_RSE_SHARED_SRAM, + NRD_PAS_GIC, + NRD_PAS_NS_DRAM, + NRD_PAS_RMM, + NRD_PAS_L1GPT, + NRD_PAS_CMN, + NRD_PAS_LCP_PERIPHERAL, + NRD_PAS_DDR_IO, + NRD_PAS_SMMU_NCI_IO, + NRD_PAS_DRAM2_CHIP0, +#if NRD_CHIP_COUNT > 1 + NRD_PAS_DRAM1_CHIP1, + NRD_PAS_DRAM2_CHIP1, +#endif +#if NRD_CHIP_COUNT > 2 + NRD_PAS_DRAM1_CHIP2, + NRD_PAS_DRAM2_CHIP2, +#endif +#if NRD_CHIP_COUNT > 3 + NRD_PAS_DRAM1_CHIP3, + NRD_PAS_DRAM2_CHIP3 +#endif +}; + +static const arm_gpt_info_t arm_gpt_info = { + .pas_region_base = pas_regions, + .pas_region_count = (unsigned int)ARRAY_SIZE(pas_regions), + .l0_base = (uintptr_t)ARM_L0_GPT_BASE, + .l1_base = (uintptr_t)ARM_L1_GPT_BASE, + .l0_size = (size_t)ARM_L0_GPT_SIZE, + .l1_size = (size_t)ARM_L1_GPT_SIZE, + .pps = GPCCR_PPS_256TB, + .pgs = GPCCR_PGS_4K +}; + +const arm_gpt_info_t *plat_arm_get_gpt_info(void) +{ + return &arm_gpt_info; +} + +#endif /* RESET_TO_BL31 */ diff --git a/plat/arm/board/neoverse_rd/platform/rdv3/rdv3_common.c b/plat/arm/board/neoverse_rd/platform/rdv3/rdv3_common.c new file mode 100644 index 00000000..10fe666b --- /dev/null +++ b/plat/arm/board/neoverse_rd/platform/rdv3/rdv3_common.c @@ -0,0 +1,183 @@ +/* + * Copyright (c) 2024, Arm Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <common/debug.h> +#include <drivers/arm/gic600_multichip.h> +#include <drivers/arm/rse_comms.h> +#include <plat/arm/common/plat_arm.h> +#include <plat/common/platform.h> +#include <platform_def.h> +#include <nrd_plat.h> +#include <rdv3_mhuv3.h> +#include <rdv3_rse_comms.h> + +unsigned int plat_arm_nrd_get_platform_id(void) +{ + return mmio_read_32(SID_REG_BASE + SID_SYSTEM_ID_OFFSET) & + SID_SYSTEM_ID_PART_NUM_MASK; +} + +unsigned int plat_arm_nrd_get_config_id(void) +{ + return mmio_read_32(SID_REG_BASE + SID_SYSTEM_CFG_OFFSET); +} + +unsigned int plat_arm_nrd_get_multi_chip_mode(void) +{ + return (mmio_read_32(SID_REG_BASE + SID_NODE_ID_OFFSET) & + SID_MULTI_CHIP_MODE_MASK) >> SID_MULTI_CHIP_MODE_SHIFT; +} + +/* + * Get a pointer to the RMM-EL3 shared buffer and return it + * through the pointer passed as parameter. + * + * This function returns the size of the shared buffer. + */ +size_t plat_rmmd_get_el3_rmm_shared_mem(uintptr_t *shared) +{ + *shared = (uintptr_t)RMM_SHARED_BASE; + + return (size_t)RMM_SHARED_SIZE; +} + +int plat_rmmd_load_manifest(struct rmm_manifest *manifest) +{ + uint64_t checksum, num_banks, num_consoles; + struct ns_dram_bank *bank_ptr; + struct console_info *console_ptr; + + assert(manifest != NULL); + + /* DRAM Bank-1 and Bank-2 */ + num_banks = 2; + assert(num_banks <= ARM_DRAM_NUM_BANKS); + + /* Set number of consoles */ + num_consoles = NRD_CSS_RMM_CONSOLE_COUNT; + + manifest->version = RMMD_MANIFEST_VERSION; + manifest->padding = 0U; /* RES0 */ + manifest->plat_data = (uintptr_t)NULL; + manifest->plat_dram.num_banks = num_banks; + manifest->plat_console.num_consoles = num_consoles; + + /* + * Boot Manifest structure illustration, with two dram banks and + * a single console. + * + * +----------------------------------------+ + * | offset | field | comment | + * +--------+----------------+--------------+ + * | 0 | version | 0x00000003 | + * +--------+----------------+--------------+ + * | 4 | padding | 0x00000000 | + * +--------+----------------+--------------+ + * | 8 | plat_data | NULL | + * +--------+----------------+--------------+ + * | 16 | num_banks | | + * +--------+----------------+ | + * | 24 | banks | plat_dram | + * +--------+----------------+ | + * | 32 | checksum | | + * +--------+----------------+--------------+ + * | 40 | num_consoles | | + * +--------+----------------+ | + * | 48 | consoles | plat_console | + * +--------+----------------+ | + * | 56 | checksum | | + * +--------+----------------+--------------+ + * | 64 | base 0 | | + * +--------+----------------+ bank[0] | + * | 72 | size 0 | | + * +--------+----------------+--------------+ + * | 80 | base 1 | | + * +--------+----------------+ bank[1] | + * | 88 | size 1 | | + * +--------+----------------+--------------+ + * | 96 | base | | + * +--------+----------------+ | + * | 104 | map_pages | | + * +--------+----------------+ | + * | 112 | name | | + * +--------+----------------+ consoles[0] | + * | 120 | clk_in_hz | | + * +--------+----------------+ | + * | 128 | baud_rate | | + * +--------+----------------+ | + * | 136 | flags | | + * +--------+----------------+--------------+ + */ + + bank_ptr = (struct ns_dram_bank *) + (((uintptr_t)manifest) + sizeof(*manifest)); + console_ptr = (struct console_info *) + ((uintptr_t)bank_ptr + (num_banks * sizeof(*bank_ptr))); + + manifest->plat_dram.banks = bank_ptr; + manifest->plat_console.consoles = console_ptr; + + /* Ensure the manifest is not larger than the shared buffer */ + assert((sizeof(struct rmm_manifest) + + (sizeof(struct console_info) * + manifest->plat_console.num_consoles) + + (sizeof(struct ns_dram_bank) * manifest->plat_dram.num_banks)) + <= ARM_EL3_RMM_SHARED_SIZE); + + /* Calculate checksum of plat_dram structure */ + checksum = num_banks + (uint64_t)bank_ptr; + + /* Store FVP DRAM banks data in Boot Manifest */ + bank_ptr[0].base = ARM_NS_DRAM1_BASE; + bank_ptr[0].size = ARM_NS_DRAM1_SIZE; + + bank_ptr[1].base = ARM_DRAM2_BASE; + bank_ptr[1].size = ARM_DRAM2_SIZE; + + /* Update checksum */ + checksum += bank_ptr[0].base + bank_ptr[0].size + bank_ptr[1].base + + bank_ptr[1].size; + + /* Checksum must be 0 */ + manifest->plat_dram.checksum = ~checksum + 1UL; + + /* Calculate the checksum of the plat_consoles structure */ + checksum = num_consoles + (uint64_t)console_ptr; + + /* Zero out the console info struct */ + memset((void *)console_ptr, '\0', + sizeof(struct console_info) * num_consoles); + + console_ptr[0].map_pages = 1; + console_ptr[0].base = NRD_CSS_RMM_CONSOLE_BASE; + console_ptr[0].clk_in_hz = NRD_CSS_RMM_CONSOLE_CLK_IN_HZ; + console_ptr[0].baud_rate = NRD_CSS_RMM_CONSOLE_BAUD; + + strlcpy(console_ptr[0].name, NRD_CSS_RMM_CONSOLE_NAME, + sizeof(console_ptr[0].name)); + + /* Update checksum */ + checksum += console_ptr[0].base + console_ptr[0].map_pages + + console_ptr[0].clk_in_hz + console_ptr[0].baud_rate; + + /* Checksum must be 0 */ + manifest->plat_console.checksum = ~checksum + 1UL; + + return 0; +} + +int plat_rse_comms_init(void) +{ + uintptr_t snd_base, rcv_base; + + /* Get sender and receiver frames for AP-RSE communication */ + mhu_v3_get_secure_device_base(&snd_base, true); + mhu_v3_get_secure_device_base(&rcv_base, false); + + VERBOSE("Initializing the rse_comms now\n"); + /* Initialize the communication channel between AP and RSE */ + return rse_comms_init(snd_base, rcv_base); +} diff --git a/plat/arm/board/neoverse_rd/platform/rdv3/rdv3_common_measured_boot.c b/plat/arm/board/neoverse_rd/platform/rdv3/rdv3_common_measured_boot.c new file mode 100644 index 00000000..f5160ceb --- /dev/null +++ b/plat/arm/board/neoverse_rd/platform/rdv3/rdv3_common_measured_boot.c @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2024, Arm Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <assert.h> +#include <stdint.h> + +#include <common/desc_image_load.h> +#include <drivers/measured_boot/rse/rse_measured_boot.h> + +extern struct rse_mboot_metadata rdv3_rse_mboot_metadata[]; + +struct rse_mboot_metadata *plat_rse_mboot_get_metadata(void) +{ + return rdv3_rse_mboot_metadata; +} + +int plat_mboot_measure_image(unsigned int image_id, image_info_t *image_data) +{ + int err; + + /* Calculate image hash and record data in RSE */ + err = rse_mboot_measure_and_record(rdv3_rse_mboot_metadata, + image_data->image_base, + image_data->image_size, + image_id); + if (err != 0) { + ERROR("Measure and record failed for image id %u, err (%i)\n", + image_id, err); + } + + return err; +} + +int plat_mboot_measure_key(void *pk_oid, void *pk_ptr, unsigned int pk_len) +{ + return rse_mboot_set_signer_id(rdv3_rse_mboot_metadata, pk_oid, + pk_ptr, pk_len); +} diff --git a/plat/arm/board/neoverse_rd/platform/rdv3/rdv3_err.c b/plat/arm/board/neoverse_rd/platform/rdv3/rdv3_err.c new file mode 100644 index 00000000..270febf8 --- /dev/null +++ b/plat/arm/board/neoverse_rd/platform/rdv3/rdv3_err.c @@ -0,0 +1,17 @@ +/* + * Copyright (c) 2024, Arm Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <plat/arm/common/plat_arm.h> + +/* + * rdv3 error handler + */ +void __dead2 plat_arm_error_handler(int err) +{ + while (1) { + wfi(); + } +} diff --git a/plat/arm/board/neoverse_rd/platform/rdv3/rdv3_mhuv3.c b/plat/arm/board/neoverse_rd/platform/rdv3/rdv3_mhuv3.c new file mode 100644 index 00000000..738f753d --- /dev/null +++ b/plat/arm/board/neoverse_rd/platform/rdv3/rdv3_mhuv3.c @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2024, ARM Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <stdbool.h> +#include <stdint.h> + +#include <common/debug.h> + +#include <nrd_css_def3.h> +#include <nrd_plat.h> +#include <rdv3_mhuv3.h> + +void mhu_v3_get_secure_device_base(uintptr_t *base, bool sender) +{ + if (sender) { + *base = AP_RSE_ROOT_MHU_V3_PBX; + } else { + *base = AP_RSE_ROOT_MHU_V3_MBX; + } +} diff --git a/plat/arm/board/neoverse_rd/platform/rdv3/rdv3_plat_attest_token.c b/plat/arm/board/neoverse_rd/platform/rdv3/rdv3_plat_attest_token.c new file mode 100644 index 00000000..55846624 --- /dev/null +++ b/plat/arm/board/neoverse_rd/platform/rdv3/rdv3_plat_attest_token.c @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2024, Arm Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <assert.h> +#include <errno.h> +#include <stdint.h> + +#include <cca_attestation.h> +#include <common/debug.h> +#include <plat/common/common_def.h> +#include <psa/error.h> + +int plat_rmmd_get_cca_attest_token(uintptr_t buf, size_t *len, + uintptr_t hash, size_t hash_size, + size_t *remaining_len) +{ + psa_status_t ret; + + assert(*len == SZ_4K); + + ret = cca_attestation_get_plat_token(buf, len, hash, hash_size); + if (ret != PSA_SUCCESS) { + ERROR("Unable to fetch CCA attestation token\n"); + return -1; + } + + assert(*len <= SZ_4K); + + *remaining_len = 0; + + return 0; +} diff --git a/plat/arm/board/neoverse_rd/platform/rdv3/rdv3_realm_attest_key.c b/plat/arm/board/neoverse_rd/platform/rdv3/rdv3_realm_attest_key.c new file mode 100644 index 00000000..224c20b9 --- /dev/null +++ b/plat/arm/board/neoverse_rd/platform/rdv3/rdv3_realm_attest_key.c @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2024, Arm Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <errno.h> +#include <stdint.h> + +#include <cca_attestation.h> +#include <common/debug.h> +#include <psa/error.h> + +int plat_rmmd_get_cca_realm_attest_key(uintptr_t buf, size_t *len, + unsigned int type) +{ + psa_status_t ret; + + ret = cca_attestation_get_realm_key(buf, len, type); + if (ret != PSA_SUCCESS) { + ERROR("Unable to fetch CCA attestation key\n"); + return -1; + } + + return 0; +} diff --git a/plat/arm/board/neoverse_rd/platform/rdv3/rdv3_security.c b/plat/arm/board/neoverse_rd/platform/rdv3/rdv3_security.c new file mode 100644 index 00000000..a12d3e22 --- /dev/null +++ b/plat/arm/board/neoverse_rd/platform/rdv3/rdv3_security.c @@ -0,0 +1,10 @@ +/* + * Copyright (c) 2024, Arm Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +/* Placeholder function to resolve build dependency */ +void plat_arm_security_setup(void) +{ +} diff --git a/plat/arm/board/neoverse_rd/platform/rdv3/rdv3_topology.c b/plat/arm/board/neoverse_rd/platform/rdv3/rdv3_topology.c new file mode 100644 index 00000000..6eb5002a --- /dev/null +++ b/plat/arm/board/neoverse_rd/platform/rdv3/rdv3_topology.c @@ -0,0 +1,100 @@ +/* + * Copyright (c) 2024, Arm Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <plat/arm/common/plat_arm.h> +#include <plat/arm/css/common/css_pm.h> + +/****************************************************************************** + * The power domain tree descriptor. + ******************************************************************************/ +const unsigned char rd_v3_pd_tree_desc[] = { + (PLAT_ARM_CLUSTER_COUNT) * (NRD_CHIP_COUNT), + NRD_MAX_CPUS_PER_CLUSTER, + NRD_MAX_CPUS_PER_CLUSTER, + NRD_MAX_CPUS_PER_CLUSTER, + NRD_MAX_CPUS_PER_CLUSTER, +#if (PLAT_ARM_CLUSTER_COUNT > 4 || \ + (NRD_PLATFORM_VARIANT == 2 && NRD_CHIP_COUNT > 1)) + NRD_MAX_CPUS_PER_CLUSTER, + NRD_MAX_CPUS_PER_CLUSTER, + NRD_MAX_CPUS_PER_CLUSTER, + NRD_MAX_CPUS_PER_CLUSTER, +#endif +#if (PLAT_ARM_CLUSTER_COUNT > 8 || \ + (NRD_PLATFORM_VARIANT == 2 && NRD_CHIP_COUNT > 2)) + NRD_MAX_CPUS_PER_CLUSTER, + NRD_MAX_CPUS_PER_CLUSTER, + NRD_MAX_CPUS_PER_CLUSTER, + NRD_MAX_CPUS_PER_CLUSTER, +#endif +#if (PLAT_ARM_CLUSTER_COUNT > 12 || \ + (NRD_PLATFORM_VARIANT == 2 && NRD_CHIP_COUNT > 3)) + NRD_MAX_CPUS_PER_CLUSTER, + NRD_MAX_CPUS_PER_CLUSTER, + NRD_MAX_CPUS_PER_CLUSTER, + NRD_MAX_CPUS_PER_CLUSTER, +#endif +}; + +/******************************************************************************* + * This function returns the topology tree information. + ******************************************************************************/ +const unsigned char *plat_get_power_domain_tree_desc(void) +{ + return rd_v3_pd_tree_desc; +} + +/******************************************************************************* + * The array mapping platform core position (implemented by plat_my_core_pos()) + * to the SCMI power domain ID implemented by SCP. + ******************************************************************************/ +#if (NRD_PLATFORM_VARIANT == 2) +const uint32_t plat_css_core_pos_to_scmi_dmn_id_map[] = { + (SET_SCMI_CHANNEL_ID(0x0) | SET_SCMI_DOMAIN_ID(0x0)), + (SET_SCMI_CHANNEL_ID(0x0) | SET_SCMI_DOMAIN_ID(0x1)), + (SET_SCMI_CHANNEL_ID(0x0) | SET_SCMI_DOMAIN_ID(0x2)), + (SET_SCMI_CHANNEL_ID(0x0) | SET_SCMI_DOMAIN_ID(0x3)), +#if (NRD_CHIP_COUNT > 1) + (SET_SCMI_CHANNEL_ID(0x1) | SET_SCMI_DOMAIN_ID(0x0)), + (SET_SCMI_CHANNEL_ID(0x1) | SET_SCMI_DOMAIN_ID(0x1)), + (SET_SCMI_CHANNEL_ID(0x1) | SET_SCMI_DOMAIN_ID(0x2)), + (SET_SCMI_CHANNEL_ID(0x1) | SET_SCMI_DOMAIN_ID(0x3)), +#endif +#if (NRD_CHIP_COUNT > 2) + (SET_SCMI_CHANNEL_ID(0x2) | SET_SCMI_DOMAIN_ID(0x0)), + (SET_SCMI_CHANNEL_ID(0x2) | SET_SCMI_DOMAIN_ID(0x1)), + (SET_SCMI_CHANNEL_ID(0x2) | SET_SCMI_DOMAIN_ID(0x2)), + (SET_SCMI_CHANNEL_ID(0x2) | SET_SCMI_DOMAIN_ID(0x3)), +#endif +#if (NRD_CHIP_COUNT > 3) + (SET_SCMI_CHANNEL_ID(0x3) | SET_SCMI_DOMAIN_ID(0x0)), + (SET_SCMI_CHANNEL_ID(0x3) | SET_SCMI_DOMAIN_ID(0x1)), + (SET_SCMI_CHANNEL_ID(0x3) | SET_SCMI_DOMAIN_ID(0x2)), + (SET_SCMI_CHANNEL_ID(0x3) | SET_SCMI_DOMAIN_ID(0x3)), +#endif +}; +#else +const uint32_t plat_css_core_pos_to_scmi_dmn_id_map[] = { + (SET_SCMI_CHANNEL_ID(0x0) | SET_SCMI_DOMAIN_ID(0x0)), + (SET_SCMI_CHANNEL_ID(0x0) | SET_SCMI_DOMAIN_ID(0x1)), + (SET_SCMI_CHANNEL_ID(0x0) | SET_SCMI_DOMAIN_ID(0x2)), + (SET_SCMI_CHANNEL_ID(0x0) | SET_SCMI_DOMAIN_ID(0x3)), + (SET_SCMI_CHANNEL_ID(0x0) | SET_SCMI_DOMAIN_ID(0x4)), + (SET_SCMI_CHANNEL_ID(0x0) | SET_SCMI_DOMAIN_ID(0x5)), + (SET_SCMI_CHANNEL_ID(0x0) | SET_SCMI_DOMAIN_ID(0x6)), + (SET_SCMI_CHANNEL_ID(0x0) | SET_SCMI_DOMAIN_ID(0x7)), +#if (NRD_PLATFORM_VARIANT == 0) + (SET_SCMI_CHANNEL_ID(0x0) | SET_SCMI_DOMAIN_ID(0x8)), + (SET_SCMI_CHANNEL_ID(0x0) | SET_SCMI_DOMAIN_ID(0x9)), + (SET_SCMI_CHANNEL_ID(0x0) | SET_SCMI_DOMAIN_ID(0xA)), + (SET_SCMI_CHANNEL_ID(0x0) | SET_SCMI_DOMAIN_ID(0xB)), + (SET_SCMI_CHANNEL_ID(0x0) | SET_SCMI_DOMAIN_ID(0xC)), + (SET_SCMI_CHANNEL_ID(0x0) | SET_SCMI_DOMAIN_ID(0xD)), + (SET_SCMI_CHANNEL_ID(0x0) | SET_SCMI_DOMAIN_ID(0xE)), + (SET_SCMI_CHANNEL_ID(0x0) | SET_SCMI_DOMAIN_ID(0xF)), +#endif +}; +#endif diff --git a/plat/arm/board/rde1edge/rde1edge_trusted_boot.c b/plat/arm/board/neoverse_rd/platform/rdv3/rdv3_trusted_boot.c similarity index 89% rename from plat/arm/board/rde1edge/rde1edge_trusted_boot.c rename to plat/arm/board/neoverse_rd/platform/rdv3/rdv3_trusted_boot.c index 4592b8fb..6aec208d 100644 --- a/plat/arm/board/rde1edge/rde1edge_trusted_boot.c +++ b/plat/arm/board/neoverse_rd/platform/rdv3/rdv3_trusted_boot.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, Arm Limited. All rights reserved. + * Copyright (c) 2024, Arm Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ diff --git a/plat/arm/board/sgi575/fdts/sgi575_fw_config.dts b/plat/arm/board/neoverse_rd/platform/sgi575/fdts/sgi575_fw_config.dts similarity index 86% rename from plat/arm/board/sgi575/fdts/sgi575_fw_config.dts rename to plat/arm/board/neoverse_rd/platform/sgi575/fdts/sgi575_fw_config.dts index 84fc1ad5..fe62b6d1 100644 --- a/plat/arm/board/sgi575/fdts/sgi575_fw_config.dts +++ b/plat/arm/board/neoverse_rd/platform/sgi575/fdts/sgi575_fw_config.dts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018-2020, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2018-2024, Arm Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ diff --git a/plat/arm/board/sgi575/fdts/sgi575_nt_fw_config.dts b/plat/arm/board/neoverse_rd/platform/sgi575/fdts/sgi575_nt_fw_config.dts similarity index 86% rename from plat/arm/board/sgi575/fdts/sgi575_nt_fw_config.dts rename to plat/arm/board/neoverse_rd/platform/sgi575/fdts/sgi575_nt_fw_config.dts index 260247a0..05734883 100644 --- a/plat/arm/board/sgi575/fdts/sgi575_nt_fw_config.dts +++ b/plat/arm/board/neoverse_rd/platform/sgi575/fdts/sgi575_nt_fw_config.dts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018-2020, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2018-2024, Arm Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ diff --git a/plat/arm/board/sgi575/fdts/sgi575_tb_fw_config.dts b/plat/arm/board/neoverse_rd/platform/sgi575/fdts/sgi575_tb_fw_config.dts similarity index 89% rename from plat/arm/board/sgi575/fdts/sgi575_tb_fw_config.dts rename to plat/arm/board/neoverse_rd/platform/sgi575/fdts/sgi575_tb_fw_config.dts index 49eda273..c370623b 100644 --- a/plat/arm/board/sgi575/fdts/sgi575_tb_fw_config.dts +++ b/plat/arm/board/neoverse_rd/platform/sgi575/fdts/sgi575_tb_fw_config.dts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2020-2024, Arm Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ diff --git a/plat/arm/board/neoverse_rd/platform/sgi575/include/platform_def.h b/plat/arm/board/neoverse_rd/platform/sgi575/include/platform_def.h new file mode 100644 index 00000000..07970175 --- /dev/null +++ b/plat/arm/board/neoverse_rd/platform/sgi575/include/platform_def.h @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2018-2024, Arm Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef PLATFORM_DEF_H +#define PLATFORM_DEF_H + +#include <lib/utils_def.h> +#include <nrd_css_fw_def1.h> +#include <nrd_plat_arm_def1.h> +#include <nrd_ros_fw_def1.h> +#include <nrd_sdei.h> + +/* Remote chip address offset */ +#define NRD_REMOTE_CHIP_MEM_OFFSET(n) \ + ((ULL(1) << NRD_ADDR_BITS_PER_CHIP) * (n)) + +#define PLAT_ARM_CLUSTER_COUNT U(2) +#define NRD_MAX_CPUS_PER_CLUSTER U(4) +#define NRD_MAX_PE_PER_CPU U(1) + +#define PLAT_CSS_MHU_BASE UL(0x45000000) + +/* Base address of DMC-620 instances */ +#define SGI575_DMC620_BASE0 UL(0x4e000000) +#define SGI575_DMC620_BASE1 UL(0x4e100000) + +/* Maximum number of address bits used per chip */ +#define NRD_ADDR_BITS_PER_CHIP U(36) + +/* GIC related constants */ +#define PLAT_ARM_GICD_BASE UL(0x30000000) +#define PLAT_ARM_GICR_BASE UL(0x300C0000) + +#endif /* PLATFORM_DEF_H */ diff --git a/plat/arm/board/sgi575/platform.mk b/plat/arm/board/neoverse_rd/platform/sgi575/platform.mk similarity index 65% rename from plat/arm/board/sgi575/platform.mk rename to plat/arm/board/neoverse_rd/platform/sgi575/platform.mk index 2f2bf73b..1f401070 100644 --- a/plat/arm/board/sgi575/platform.mk +++ b/plat/arm/board/neoverse_rd/platform/sgi575/platform.mk @@ -1,22 +1,21 @@ # -# Copyright (c) 2018-2020, ARM Limited and Contributors. All rights reserved. +# Copyright (c) 2018-2024, Arm Limited and Contributors. All rights reserved. # # SPDX-License-Identifier: BSD-3-Clause # -$(warning Platform ${PLAT} is deprecated. Some of the features might not work as expected) +include plat/arm/board/neoverse_rd/common/nrd-common.mk -include plat/arm/css/sgi/sgi-common.mk +SGI575_BASE = plat/arm/board/neoverse_rd/platform/sgi575 -SGI575_BASE = plat/arm/board/sgi575 +PLAT_INCLUDES += -I${NRD_COMMON_BASE}/include/nrd1/ \ + -I${SGI575_BASE}/include/ -PLAT_INCLUDES += -I${SGI575_BASE}/include/ +NRD_CPU_SOURCES := lib/cpus/aarch64/cortex_a75.S -SGI_CPU_SOURCES := lib/cpus/aarch64/cortex_a75.S +PLAT_BL_COMMON_SOURCES += ${NRD_COMMON_BASE}/nrd_plat1.c -PLAT_BL_COMMON_SOURCES += ${CSS_ENT_BASE}/sgi_plat.c - -BL1_SOURCES += ${SGI_CPU_SOURCES} \ +BL1_SOURCES += ${NRD_CPU_SOURCES} \ ${SGI575_BASE}/sgi575_err.c BL2_SOURCES += ${SGI575_BASE}/sgi575_plat.c \ @@ -26,7 +25,7 @@ BL2_SOURCES += ${SGI575_BASE}/sgi575_plat.c \ lib/utils/mem_region.c \ plat/arm/common/arm_nor_psci_mem_protect.c -BL31_SOURCES += ${SGI_CPU_SOURCES} \ +BL31_SOURCES += ${NRD_CPU_SOURCES} \ ${SGI575_BASE}/sgi575_plat.c \ ${SGI575_BASE}/sgi575_topology.c \ drivers/cfi/v2m/v2m_flash.c \ @@ -56,12 +55,19 @@ NT_FW_CONFIG := ${BUILD_PLAT}/fdts/${PLAT}_nt_fw_config.dtb # Add the NT_FW_CONFIG to FIP and specify the same to certtool $(eval $(call TOOL_ADD_PAYLOAD,${NT_FW_CONFIG},--nt-fw-config,${NT_FW_CONFIG})) -ifneq ($(CSS_SGI_CHIP_COUNT),1) +ifneq ($(NRD_CHIP_COUNT),1) $(error "Chip count for SGI575 should be 1, currently set to \ - ${CSS_SGI_CHIP_COUNT}.") + ${NRD_CHIP_COUNT}.") +endif + +ifneq ($(NRD_PLATFORM_VARIANT),0) + $(error "NRD_PLATFORM_VARIANT for SGI575 should always be 0,\ + currently set to ${NRD_PLATFORM_VARIANT}.") endif -ifneq ($(CSS_SGI_PLATFORM_VARIANT),0) - $(error "CSS_SGI_PLATFORM_VARIANT for SGI575 should always be 0,\ - currently set to ${CSS_SGI_PLATFORM_VARIANT}.") +ifneq (${RESET_TO_BL31},0) + $(error "Using BL31 as the reset vector is not supported on ${PLAT} platform. \ + Please set RESET_TO_BL31 to 0.") endif + +override SPMD_SPM_AT_SEL2 := 0 diff --git a/plat/arm/board/sgi575/sgi575_err.c b/plat/arm/board/neoverse_rd/platform/sgi575/sgi575_err.c similarity index 71% rename from plat/arm/board/sgi575/sgi575_err.c rename to plat/arm/board/neoverse_rd/platform/sgi575/sgi575_err.c index 21bfcb73..7e656abb 100644 --- a/plat/arm/board/sgi575/sgi575_err.c +++ b/plat/arm/board/neoverse_rd/platform/sgi575/sgi575_err.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019-2020, Arm Limited. All rights reserved. + * Copyright (c) 2019-2024, Arm Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ diff --git a/plat/arm/board/sgi575/sgi575_plat.c b/plat/arm/board/neoverse_rd/platform/sgi575/sgi575_plat.c similarity index 52% rename from plat/arm/board/sgi575/sgi575_plat.c rename to plat/arm/board/neoverse_rd/platform/sgi575/sgi575_plat.c index dc294e6a..8b746160 100644 --- a/plat/arm/board/sgi575/sgi575_plat.c +++ b/plat/arm/board/neoverse_rd/platform/sgi575/sgi575_plat.c @@ -1,30 +1,31 @@ /* - * Copyright (c) 2018-2020, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2018-2024, Arm Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ #include <plat/common/platform.h> -#include <sgi_plat.h> -#include <sgi_variant.h> -unsigned int plat_arm_sgi_get_platform_id(void) +#include <nrd_plat.h> +#include <nrd_variant.h> + +unsigned int plat_arm_nrd_get_platform_id(void) { return mmio_read_32(SSC_VERSION) & SSC_VERSION_PART_NUM_MASK; } -unsigned int plat_arm_sgi_get_config_id(void) +unsigned int plat_arm_nrd_get_config_id(void) { return (mmio_read_32(SSC_VERSION) >> SSC_VERSION_CONFIG_SHIFT) & SSC_VERSION_CONFIG_MASK; } -unsigned int plat_arm_sgi_get_multi_chip_mode(void) +unsigned int plat_arm_nrd_get_multi_chip_mode(void) { return 0; } void bl31_platform_setup(void) { - sgi_bl31_common_platform_setup(); + nrd_bl31_common_platform_setup(); } diff --git a/plat/arm/board/sgi575/sgi575_security.c b/plat/arm/board/neoverse_rd/platform/sgi575/sgi575_security.c similarity index 85% rename from plat/arm/board/sgi575/sgi575_security.c rename to plat/arm/board/neoverse_rd/platform/sgi575/sgi575_security.c index 17d07d1a..8b8a3828 100644 --- a/plat/arm/board/sgi575/sgi575_security.c +++ b/plat/arm/board/neoverse_rd/platform/sgi575/sgi575_security.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018-2021, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2018-2024, Arm Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -7,7 +7,7 @@ #include <platform_def.h> #include <common/debug.h> -#include <sgi_dmc620_tzc_regions.h> +#include <nrd_dmc620_tzc_regions.h> uintptr_t sgi575_dmc_base[] = { SGI575_DMC620_BASE0, @@ -20,7 +20,7 @@ static const tzc_dmc620_driver_data_t sgi575_plat_driver_data = { }; static const tzc_dmc620_acc_addr_data_t sgi575_acc_addr_data[] = { - CSS_SGI_DMC620_TZC_REGIONS_DEF + NRD_DMC620_TZC_REGIONS_DEF }; static const tzc_dmc620_config_data_t sgi575_plat_config_data = { diff --git a/plat/arm/board/sgi575/sgi575_topology.c b/plat/arm/board/neoverse_rd/platform/sgi575/sgi575_topology.c similarity index 89% rename from plat/arm/board/sgi575/sgi575_topology.c rename to plat/arm/board/neoverse_rd/platform/sgi575/sgi575_topology.c index f7c38567..15ffc650 100644 --- a/plat/arm/board/sgi575/sgi575_topology.c +++ b/plat/arm/board/neoverse_rd/platform/sgi575/sgi575_topology.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019-2020, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2019-2024, Arm Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -11,8 +11,8 @@ ******************************************************************************/ static const unsigned char sgi575_pd_tree_desc[] = { PLAT_ARM_CLUSTER_COUNT, - CSS_SGI_MAX_CPUS_PER_CLUSTER, - CSS_SGI_MAX_CPUS_PER_CLUSTER + NRD_MAX_CPUS_PER_CLUSTER, + NRD_MAX_CPUS_PER_CLUSTER }; /******************************************************************************* diff --git a/plat/arm/board/sgi575/sgi575_trusted_boot.c b/plat/arm/board/neoverse_rd/platform/sgi575/sgi575_trusted_boot.c similarity index 88% rename from plat/arm/board/sgi575/sgi575_trusted_boot.c rename to plat/arm/board/neoverse_rd/platform/sgi575/sgi575_trusted_boot.c index 4592b8fb..84622d00 100644 --- a/plat/arm/board/sgi575/sgi575_trusted_boot.c +++ b/plat/arm/board/neoverse_rd/platform/sgi575/sgi575_trusted_boot.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, Arm Limited. All rights reserved. + * Copyright (c) 2020-2024, Arm Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ diff --git a/plat/arm/board/rde1edge/include/platform_def.h b/plat/arm/board/rde1edge/include/platform_def.h deleted file mode 100644 index 69bfd7bc..00000000 --- a/plat/arm/board/rde1edge/include/platform_def.h +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright (c) 2018-2022, Arm Limited. All rights reserved. - * - * SPDX-License-Identifier: BSD-3-Clause - */ - -#ifndef PLATFORM_DEF_H -#define PLATFORM_DEF_H - -#include <lib/utils_def.h> - -#include <sgi_sdei.h> -#include <sgi_soc_platform_def.h> - -#define PLAT_ARM_CLUSTER_COUNT U(2) -#define CSS_SGI_MAX_CPUS_PER_CLUSTER U(8) -#define CSS_SGI_MAX_PE_PER_CPU U(2) - -#define PLAT_CSS_MHU_BASE UL(0x45400000) - -/* Base address of DMC-620 instances */ -#define RDE1EDGE_DMC620_BASE0 UL(0x4e000000) -#define RDE1EDGE_DMC620_BASE1 UL(0x4e100000) - -#define PLAT_MAX_PWR_LVL ARM_PWR_LVL2 - -#define CSS_SYSTEM_PWR_DMN_LVL ARM_PWR_LVL3 - -/* Maximum number of address bits used per chip */ -#define CSS_SGI_ADDR_BITS_PER_CHIP U(36) - -/* - * Physical and virtual address space limits for MMU in AARCH64 & AARCH32 modes - */ -#ifdef __aarch64__ -#define PLAT_PHY_ADDR_SPACE_SIZE (1ULL << CSS_SGI_ADDR_BITS_PER_CHIP) -#define PLAT_VIRT_ADDR_SPACE_SIZE (1ULL << CSS_SGI_ADDR_BITS_PER_CHIP) -#else -#define PLAT_PHY_ADDR_SPACE_SIZE (1ULL << 32) -#define PLAT_VIRT_ADDR_SPACE_SIZE (1ULL << 32) -#endif - -/* GIC related constants */ -#define PLAT_ARM_GICD_BASE UL(0x30000000) -#define PLAT_ARM_GICC_BASE UL(0x2C000000) -#define PLAT_ARM_GICR_BASE UL(0x300C0000) - -#endif /* PLATFORM_DEF_H */ diff --git a/plat/arm/board/rde1edge/platform.mk b/plat/arm/board/rde1edge/platform.mk deleted file mode 100644 index 4a9a467b..00000000 --- a/plat/arm/board/rde1edge/platform.mk +++ /dev/null @@ -1,69 +0,0 @@ -# -# Copyright (c) 2018-2023, Arm Limited. All rights reserved. -# -# SPDX-License-Identifier: BSD-3-Clause -# - -$(warning Platform ${PLAT} is deprecated. \ - Some of the features might not work as expected) - -include plat/arm/css/sgi/sgi-common.mk - -RDE1EDGE_BASE = plat/arm/board/rde1edge - -PLAT_INCLUDES += -I${RDE1EDGE_BASE}/include/ - -SGI_CPU_SOURCES := lib/cpus/aarch64/neoverse_e1.S - -PLAT_BL_COMMON_SOURCES += ${CSS_ENT_BASE}/sgi_plat.c - -BL1_SOURCES += ${SGI_CPU_SOURCES} \ - ${RDE1EDGE_BASE}/rde1edge_err.c - -BL2_SOURCES += ${RDE1EDGE_BASE}/rde1edge_plat.c \ - ${RDE1EDGE_BASE}/rde1edge_security.c \ - ${RDE1EDGE_BASE}/rde1edge_err.c \ - drivers/arm/tzc/tzc_dmc620.c \ - lib/utils/mem_region.c \ - plat/arm/common/arm_nor_psci_mem_protect.c - -BL31_SOURCES += ${SGI_CPU_SOURCES} \ - ${RDE1EDGE_BASE}/rde1edge_plat.c \ - ${RDE1EDGE_BASE}/rde1edge_topology.c \ - drivers/cfi/v2m/v2m_flash.c \ - lib/utils/mem_region.c \ - plat/arm/common/arm_nor_psci_mem_protect.c - -ifeq (${TRUSTED_BOARD_BOOT}, 1) -BL1_SOURCES += ${RDE1EDGE_BASE}/rde1edge_trusted_boot.c -BL2_SOURCES += ${RDE1EDGE_BASE}/rde1edge_trusted_boot.c -endif - -# Add the FDT_SOURCES and options for Dynamic Config -FDT_SOURCES += ${RDE1EDGE_BASE}/fdts/${PLAT}_fw_config.dts \ - ${RDE1EDGE_BASE}/fdts/${PLAT}_tb_fw_config.dts -FW_CONFIG := ${BUILD_PLAT}/fdts/${PLAT}_fw_config.dtb -TB_FW_CONFIG := ${BUILD_PLAT}/fdts/${PLAT}_tb_fw_config.dtb - -# Add the FW_CONFIG to FIP and specify the same to certtool -$(eval $(call TOOL_ADD_PAYLOAD,${FW_CONFIG},--fw-config,${FW_CONFIG})) -# Add the TB_FW_CONFIG to FIP and specify the same to certtool -$(eval $(call TOOL_ADD_PAYLOAD,${TB_FW_CONFIG},--tb-fw-config,${TB_FW_CONFIG})) - -FDT_SOURCES += ${RDE1EDGE_BASE}/fdts/${PLAT}_nt_fw_config.dts -NT_FW_CONFIG := ${BUILD_PLAT}/fdts/${PLAT}_nt_fw_config.dtb - -# Add the NT_FW_CONFIG to FIP and specify the same to certtool -$(eval $(call TOOL_ADD_PAYLOAD,${NT_FW_CONFIG},--nt-fw-config,${NT_FW_CONFIG})) - -ifneq ($(CSS_SGI_CHIP_COUNT),1) - $(error "Chip count for RDE1Edge should be 1, currently set to \ - ${CSS_SGI_CHIP_COUNT}.") -endif - -ifneq ($(CSS_SGI_PLATFORM_VARIANT),0) - $(error "CSS_SGI_PLATFORM_VARIANT for RD-E1-Edge should always be 0, \ - currently set to ${CSS_SGI_PLATFORM_VARIANT}.") -endif - -override CTX_INCLUDE_AARCH32_REGS := 0 diff --git a/plat/arm/board/rde1edge/rde1edge_err.c b/plat/arm/board/rde1edge/rde1edge_err.c deleted file mode 100644 index c72c18ce..00000000 --- a/plat/arm/board/rde1edge/rde1edge_err.c +++ /dev/null @@ -1,17 +0,0 @@ -/* - * Copyright (c) 2019-2020, Arm Limited. All rights reserved. - * - * SPDX-License-Identifier: BSD-3-Clause - */ - -#include <plat/arm/common/plat_arm.h> - -/* - * rde1edge error handler - */ -void __dead2 plat_arm_error_handler(int err) -{ - while (true) { - wfi(); - } -} diff --git a/plat/arm/board/rde1edge/rde1edge_plat.c b/plat/arm/board/rde1edge/rde1edge_plat.c deleted file mode 100644 index 44d818ae..00000000 --- a/plat/arm/board/rde1edge/rde1edge_plat.c +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright (c) 2018-2020, Arm Limited. All rights reserved. - * - * SPDX-License-Identifier: BSD-3-Clause - */ - -#include <plat/common/platform.h> -#include <sgi_plat.h> - -unsigned int plat_arm_sgi_get_platform_id(void) -{ - return mmio_read_32(SID_REG_BASE + SID_SYSTEM_ID_OFFSET) - & SID_SYSTEM_ID_PART_NUM_MASK; -} - -unsigned int plat_arm_sgi_get_config_id(void) -{ - return mmio_read_32(SID_REG_BASE + SID_SYSTEM_CFG_OFFSET); -} - -unsigned int plat_arm_sgi_get_multi_chip_mode(void) -{ - return 0; -} - -void bl31_platform_setup(void) -{ - sgi_bl31_common_platform_setup(); -} diff --git a/plat/arm/board/rde1edge/rde1edge_security.c b/plat/arm/board/rde1edge/rde1edge_security.c deleted file mode 100644 index 35f81d19..00000000 --- a/plat/arm/board/rde1edge/rde1edge_security.c +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright (c) 2019-2021, Arm Limited. All rights reserved. - * - * SPDX-License-Identifier: BSD-3-Clause - */ - -#include <platform_def.h> - -#include <common/debug.h> -#include <sgi_dmc620_tzc_regions.h> - -uintptr_t rde1edge_dmc_base[] = { - RDE1EDGE_DMC620_BASE0, - RDE1EDGE_DMC620_BASE1 -}; - -static const tzc_dmc620_driver_data_t rde1edge_plat_driver_data = { - .dmc_base = rde1edge_dmc_base, - .dmc_count = ARRAY_SIZE(rde1edge_dmc_base) -}; - -static const tzc_dmc620_acc_addr_data_t rde1edge_acc_addr_data[] = { - CSS_SGI_DMC620_TZC_REGIONS_DEF -}; - -static const tzc_dmc620_config_data_t rde1edge_plat_config_data = { - .plat_drv_data = &rde1edge_plat_driver_data, - .plat_acc_addr_data = rde1edge_acc_addr_data, - .acc_addr_count = ARRAY_SIZE(rde1edge_acc_addr_data) -}; - -/* Initialize the secure environment */ -void plat_arm_security_setup(void) -{ - arm_tzc_dmc620_setup(&rde1edge_plat_config_data); -} diff --git a/plat/arm/board/rde1edge/rde1edge_topology.c b/plat/arm/board/rde1edge/rde1edge_topology.c deleted file mode 100644 index 91cc37e1..00000000 --- a/plat/arm/board/rde1edge/rde1edge_topology.c +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright (c) 2019-2020, ARM Limited and Contributors. All rights reserved. - * - * SPDX-License-Identifier: BSD-3-Clause - */ - -#include <plat/arm/common/plat_arm.h> - -/****************************************************************************** - * The power domain tree descriptor. RD-E1-Edge platform consists of two - * clusters with eight CPUs in each cluster. The CPUs are multi-threaded with - * two threads per CPU. - ******************************************************************************/ -static const unsigned char rde1edge_pd_tree_desc[] = { - CSS_SGI_CHIP_COUNT, - PLAT_ARM_CLUSTER_COUNT, - CSS_SGI_MAX_CPUS_PER_CLUSTER * CSS_SGI_MAX_PE_PER_CPU, - CSS_SGI_MAX_CPUS_PER_CLUSTER * CSS_SGI_MAX_PE_PER_CPU -}; - -/****************************************************************************** - * This function returns the topology tree information. - ******************************************************************************/ -const unsigned char *plat_get_power_domain_tree_desc(void) -{ - return rde1edge_pd_tree_desc; -} - -/******************************************************************************* - * The array mapping platform core position (implemented by plat_my_core_pos()) - * to the SCMI power domain ID implemented by SCP. - ******************************************************************************/ -const uint32_t plat_css_core_pos_to_scmi_dmn_id_map[] = { - 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, - 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31 -}; diff --git a/plat/arm/board/rdn1edge/include/platform_def.h b/plat/arm/board/rdn1edge/include/platform_def.h deleted file mode 100644 index de019027..00000000 --- a/plat/arm/board/rdn1edge/include/platform_def.h +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright (c) 2018-2022, ARM Limited and Contributors. All rights reserved. - * - * SPDX-License-Identifier: BSD-3-Clause - */ - -#ifndef PLATFORM_DEF_H -#define PLATFORM_DEF_H - -#include <lib/utils_def.h> - -#include <sgi_sdei.h> -#include <sgi_soc_platform_def.h> - -#define PLAT_ARM_CLUSTER_COUNT U(2) -#define CSS_SGI_MAX_CPUS_PER_CLUSTER U(4) -#define CSS_SGI_MAX_PE_PER_CPU U(1) - -#define PLAT_CSS_MHU_BASE UL(0x45400000) - -/* Base address of DMC-620 instances */ -#define RDN1EDGE_DMC620_BASE0 UL(0x4e000000) -#define RDN1EDGE_DMC620_BASE1 UL(0x4e100000) - -/* System power domain level */ -#define CSS_SYSTEM_PWR_DMN_LVL ARM_PWR_LVL2 - -#define PLAT_MAX_PWR_LVL ARM_PWR_LVL1 - -/* Virtual address used by dynamic mem_protect for chunk_base */ -#define PLAT_ARM_MEM_PROTEC_VA_FRAME UL(0xc0000000) - -/* Maximum number of address bits used per chip */ -#define CSS_SGI_ADDR_BITS_PER_CHIP U(42) - -/* - * Physical and virtual address space limits for MMU in AARCH64 & AARCH32 modes - */ -#ifdef __aarch64__ -#define PLAT_PHY_ADDR_SPACE_SIZE CSS_SGI_REMOTE_CHIP_MEM_OFFSET( \ - CSS_SGI_CHIP_COUNT) -#define PLAT_VIRT_ADDR_SPACE_SIZE CSS_SGI_REMOTE_CHIP_MEM_OFFSET( \ - CSS_SGI_CHIP_COUNT) -#else -#define PLAT_PHY_ADDR_SPACE_SIZE (1ULL << 32) -#define PLAT_VIRT_ADDR_SPACE_SIZE (1ULL << 32) -#endif - -/* GIC related constants */ -#define PLAT_ARM_GICD_BASE UL(0x30000000) -#define PLAT_ARM_GICC_BASE UL(0x2C000000) -#define PLAT_ARM_GICR_BASE UL(0x300C0000) - -#endif /* PLATFORM_DEF_H */ diff --git a/plat/arm/board/rdn2/include/platform_def.h b/plat/arm/board/rdn2/include/platform_def.h deleted file mode 100644 index 2391b725..00000000 --- a/plat/arm/board/rdn2/include/platform_def.h +++ /dev/null @@ -1,126 +0,0 @@ -/* - * Copyright (c) 2020-2023, ARM Limited and Contributors. All rights reserved. - * - * SPDX-License-Identifier: BSD-3-Clause - */ - -#ifndef PLATFORM_DEF_H -#define PLATFORM_DEF_H - -#include <lib/utils_def.h> -#include <sgi_sdei.h> -#include <sgi_soc_platform_def_v2.h> - -#if (CSS_SGI_PLATFORM_VARIANT == 1) -#define PLAT_ARM_CLUSTER_COUNT U(8) -#elif (CSS_SGI_PLATFORM_VARIANT == 2) -#define PLAT_ARM_CLUSTER_COUNT U(4) -#else -#define PLAT_ARM_CLUSTER_COUNT U(16) -#endif - -#define CSS_SGI_MAX_CPUS_PER_CLUSTER U(1) -#define CSS_SGI_MAX_PE_PER_CPU U(1) - -#define PLAT_CSS_MHU_BASE UL(0x2A920000) -#define PLAT_MHUV2_BASE PLAT_CSS_MHU_BASE - -#define CSS_SYSTEM_PWR_DMN_LVL ARM_PWR_LVL2 -#define PLAT_MAX_PWR_LVL ARM_PWR_LVL1 - -/* TZC Related Constants */ -#define PLAT_ARM_TZC_BASE UL(0x10720000) -#define PLAT_ARM_TZC_FILTERS TZC_400_REGION_ATTR_FILTER_BIT(0) - -#define TZC400_OFFSET UL(0x1000000) - -#if (CSS_SGI_PLATFORM_VARIANT == 1) -#define TZC400_COUNT U(2) -#elif (CSS_SGI_PLATFORM_VARIANT == 2) -#define TZC400_COUNT U(4) -#else -#define TZC400_COUNT U(8) -#endif - -#define TZC400_BASE(n) (PLAT_ARM_TZC_BASE + \ - (n * TZC400_OFFSET)) - -#define TZC_NSAID_ALL_AP U(0) -#define TZC_NSAID_PCI U(1) -#define TZC_NSAID_HDLCD0 U(2) -#define TZC_NSAID_DMA U(5) -#define TZC_NSAID_DMA2 U(8) -#define TZC_NSAID_CLCD U(7) -#define TZC_NSAID_AP U(9) -#define TZC_NSAID_VIRTIO U(15) - -#define PLAT_ARM_TZC_NS_DEV_ACCESS \ - (TZC_REGION_ACCESS_RDWR(TZC_NSAID_ALL_AP)) | \ - (TZC_REGION_ACCESS_RDWR(TZC_NSAID_HDLCD0)) | \ - (TZC_REGION_ACCESS_RDWR(TZC_NSAID_PCI)) | \ - (TZC_REGION_ACCESS_RDWR(TZC_NSAID_DMA)) | \ - (TZC_REGION_ACCESS_RDWR(TZC_NSAID_DMA2)) | \ - (TZC_REGION_ACCESS_RDWR(TZC_NSAID_AP)) | \ - (TZC_REGION_ACCESS_RDWR(TZC_NSAID_CLCD)) | \ - (TZC_REGION_ACCESS_RDWR(TZC_NSAID_VIRTIO)) - -/* - * Physical and virtual address space limits for MMU in AARCH64 & AARCH32 modes - */ -#ifdef __aarch64__ -#if (CSS_SGI_PLATFORM_VARIANT == 2) -#define CSS_SGI_ADDR_BITS_PER_CHIP U(46) /* 64TB */ -#else -#define CSS_SGI_ADDR_BITS_PER_CHIP U(42) /* 4TB */ -#endif - -#define PLAT_PHY_ADDR_SPACE_SIZE CSS_SGI_REMOTE_CHIP_MEM_OFFSET( \ - CSS_SGI_CHIP_COUNT) -#define PLAT_VIRT_ADDR_SPACE_SIZE CSS_SGI_REMOTE_CHIP_MEM_OFFSET( \ - CSS_SGI_CHIP_COUNT) -#else -#define PLAT_PHY_ADDR_SPACE_SIZE (1ULL << 32) -#define PLAT_VIRT_ADDR_SPACE_SIZE (1ULL << 32) -#endif - -/* GIC related constants */ -#define PLAT_ARM_GICD_BASE UL(0x30000000) -#define PLAT_ARM_GICC_BASE UL(0x2C000000) - -/* Virtual address used by dynamic mem_protect for chunk_base */ -#define PLAT_ARM_MEM_PROTEC_VA_FRAME UL(0xC0000000) - -#if (CSS_SGI_PLATFORM_VARIANT == 1) -#define PLAT_ARM_GICR_BASE UL(0x30100000) -#elif (CSS_SGI_PLATFORM_VARIANT == 3) -#define PLAT_ARM_GICR_BASE UL(0x30300000) -#else -#define PLAT_ARM_GICR_BASE UL(0x301C0000) -#endif - -/* Interrupt priority level for shutdown/reboot */ -#define PLAT_REBOOT_PRI GIC_HIGHEST_SEC_PRIORITY -#define PLAT_EHF_DESC EHF_PRI_DESC(PLAT_PRI_BITS, PLAT_REBOOT_PRI) - -/* - * Number of Secure Partitions supported. - * SPMC at EL3, uses this count to configure the maximum number of supported - * secure partitions. - */ -#define SECURE_PARTITION_COUNT 1 - -/* - * Number of NWd Partitions supported. - * SPMC at EL3, uses this count to configure the maximum number of supported - * nwld partitions. - */ -#define NS_PARTITION_COUNT 1 - -/* - * Number of Logical Partitions supported. - * SPMC at EL3, uses this count to configure the maximum number of supported - * logical partitions. - */ -#define MAX_EL3_LP_DESCS_COUNT 1 - -#endif /* PLATFORM_DEF_H */ diff --git a/plat/arm/board/rdn2/include/rdn2_ras.h b/plat/arm/board/rdn2/include/rdn2_ras.h deleted file mode 100644 index 1d9af609..00000000 --- a/plat/arm/board/rdn2/include/rdn2_ras.h +++ /dev/null @@ -1,14 +0,0 @@ -/* - * Copyright (c) 2023, ARM Limited and Contributors. All rights reserved. - * - * SPDX-License-Identifier: BSD-3-Clause - */ - -#ifndef RDN2_RAS_H -#define RDN2_RAS_H - -#include <sgi_ras.h> - -extern struct plat_sgi_ras_config ras_config; - -#endif /* RDN2_RAS_H */ diff --git a/plat/arm/board/sgi575/include/platform_def.h b/plat/arm/board/sgi575/include/platform_def.h deleted file mode 100644 index 82a38c54..00000000 --- a/plat/arm/board/sgi575/include/platform_def.h +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright (c) 2018-2022, ARM Limited and Contributors. All rights reserved. - * - * SPDX-License-Identifier: BSD-3-Clause - */ - -#ifndef PLATFORM_DEF_H -#define PLATFORM_DEF_H - -#include <lib/utils_def.h> - -#include <sgi_sdei.h> -#include <sgi_soc_platform_def.h> - -#define PLAT_ARM_CLUSTER_COUNT U(2) -#define CSS_SGI_MAX_CPUS_PER_CLUSTER U(4) -#define CSS_SGI_MAX_PE_PER_CPU U(1) - -#define PLAT_CSS_MHU_BASE UL(0x45000000) - -/* Base address of DMC-620 instances */ -#define SGI575_DMC620_BASE0 UL(0x4e000000) -#define SGI575_DMC620_BASE1 UL(0x4e100000) - -/* System power domain level */ -#define CSS_SYSTEM_PWR_DMN_LVL ARM_PWR_LVL2 - -#define PLAT_MAX_PWR_LVL ARM_PWR_LVL1 - -/* Maximum number of address bits used per chip */ -#define CSS_SGI_ADDR_BITS_PER_CHIP U(36) - -/* - * Physical and virtual address space limits for MMU in AARCH64 & AARCH32 modes - */ -#ifdef __aarch64__ -#define PLAT_PHY_ADDR_SPACE_SIZE (1ULL << CSS_SGI_ADDR_BITS_PER_CHIP) -#define PLAT_VIRT_ADDR_SPACE_SIZE (1ULL << CSS_SGI_ADDR_BITS_PER_CHIP) -#else -#define PLAT_PHY_ADDR_SPACE_SIZE (1ULL << 32) -#define PLAT_VIRT_ADDR_SPACE_SIZE (1ULL << 32) -#endif - -/* GIC related constants */ -#define PLAT_ARM_GICD_BASE UL(0x30000000) -#define PLAT_ARM_GICC_BASE UL(0x2C000000) -#define PLAT_ARM_GICR_BASE UL(0x300C0000) - -#endif /* PLATFORM_DEF_H */ diff --git a/plat/arm/board/tc/fdts/dice_prot_env.dtsi b/plat/arm/board/tc/fdts/dice_prot_env.dtsi new file mode 100644 index 00000000..118f995d --- /dev/null +++ b/plat/arm/board/tc/fdts/dice_prot_env.dtsi @@ -0,0 +1,11 @@ +/* + * Copyright (c) 2024, Arm Limited. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +/* DICE Protection Environment Client Config */ +dice_protection_environment: context_handle { + compatible = "arm,dpe_ctx_handle"; + dpe_ctx_handle = <0x0>; +}; diff --git a/plat/arm/board/tc/fdts/tc_fw_config.dts b/plat/arm/board/tc/fdts/tc_fw_config.dts index a84c7f85..de446eab 100644 --- a/plat/arm/board/tc/fdts/tc_fw_config.dts +++ b/plat/arm/board/tc/fdts/tc_fw_config.dts @@ -1,10 +1,11 @@ /* - * Copyright (c) 2020-2021, Arm Limited. All rights reserved. + * Copyright (c) 2020-2024, Arm Limited. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ #include <common/tbbr/tbbr_img_def.h> +#include <platform_def.h> /dts-v1/; @@ -25,9 +26,14 @@ }; hw-config { - load-address = <0x0 0x83000000>; - max-size = <0x8000>; + load-address = <0x0 PLAT_HW_CONFIG_DTB_BASE>; + max-size = <PLAT_ARM_HW_CONFIG_SIZE>; id = <HW_CONFIG_ID>; }; + nt_fw-config { + load-address = <0x0 (PLAT_HW_CONFIG_DTB_BASE + PLAT_ARM_HW_CONFIG_SIZE)>; + max-size = <0x1000>; + id = <NT_FW_CONFIG_ID>; + }; }; }; diff --git a/plat/arm/board/tc/fdts/tc_nt_fw_config.dts b/plat/arm/board/tc/fdts/tc_nt_fw_config.dts new file mode 100644 index 00000000..bb3086dd --- /dev/null +++ b/plat/arm/board/tc/fdts/tc_nt_fw_config.dts @@ -0,0 +1,13 @@ +/* + * Copyright (c) 2024, Arm Limited. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +/dts-v1/; + +/ { +#if DICE_PROTECTION_ENVIRONMENT + #include "dice_prot_env.dtsi" +#endif +}; diff --git a/plat/arm/board/tc/fdts/tc_spmc_common_sp_manifest.dtsi b/plat/arm/board/tc/fdts/tc_spmc_common_sp_manifest.dtsi new file mode 100644 index 00000000..06c29371 --- /dev/null +++ b/plat/arm/board/tc/fdts/tc_spmc_common_sp_manifest.dtsi @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2023-2024, Arm Limited. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +/* + * Secure world memory map. For a full view of the DRAM map, see platform_def.h + * + * 0xf900_c000 ------------------ + * | ... | + * 0xf901_c000 ------------------ + * | (63MB) | Trusty (=/=> OP-TEE) + * 0xfcf1_c000 ------------------ + * | ... | + * 0xfd00_0000 ------------------ + * | (512K) | Hafnium + * 0xfd08_0000 ------------------ + * | ... | Hafnium stack + * 0xfd28_0000 ------------------ + * | (11MB) | OP-TEE (=/=> Trusty) + * 0xfdd8_0000 ------------------ + * | ... | + * 0xfde0_0000 ------------------ + * | (2MB) | Firmware Upgrade + * 0xfec0_0000 ------------------ + * | (2MB) | Crypto + * 0xfee0_0000 ------------------ + * | (2MB) | Internal Truested Storage + * 0xff00_0000 ------------------ + */ +&hafnium { + vm1 { + is_ffa_partition; + vcpu_count = <8>; + /* partition information filled in separately */ + }; +#ifdef TS_SP_FW_CONFIG + vm2 { + is_ffa_partition; + debug_name = "internal-trusted-storage"; + load_address = <0xfee00000>; + vcpu_count = <1>; + mem_size = <0x200000>; /* 2MB TZC DRAM */ + }; + vm3 { + is_ffa_partition; + debug_name = "crypto"; + load_address = <0xfec00000>; + vcpu_count = <1>; + mem_size = <0x200000>; /* 2MB TZC DRAM */ + }; + vm4 { + is_ffa_partition; + debug_name = "firmware-update"; + load_address = <0xfde00000>; + vcpu_count = <1>; + mem_size = <0xe00000>; /* 14MB TZC DRAM */ + }; +#endif +}; diff --git a/plat/arm/board/tc/fdts/tc_spmc_manifest.dts b/plat/arm/board/tc/fdts/tc_spmc_manifest.dtsi similarity index 65% rename from plat/arm/board/tc/fdts/tc_spmc_manifest.dts rename to plat/arm/board/tc/fdts/tc_spmc_manifest.dtsi index b64e0762..737997d4 100644 --- a/plat/arm/board/tc/fdts/tc_spmc_manifest.dts +++ b/plat/arm/board/tc/fdts/tc_spmc_manifest.dtsi @@ -1,9 +1,9 @@ /* - * Copyright (c) 2020-2023, Arm Limited. All rights reserved. + * Copyright (c) 2020-2024, Arm Limited. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ -/dts-v1/; +#include <platform_def.h> / { compatible = "arm,ffa-core-manifest-1.0"; @@ -13,43 +13,16 @@ attribute { spmc_id = <0x8000>; maj_ver = <0x1>; - min_ver = <0x1>; + min_ver = <0x2>; exec_state = <0x0>; load_address = <0x0 0xfd000000>; entrypoint = <0x0 0xfd000000>; binary_size = <0x80000>; }; - hypervisor { + hafnium:hypervisor { compatible = "hafnium,hafnium"; - vm1 { - is_ffa_partition; - debug_name = "cactus-primary"; - load_address = <0xfe000000>; - vcpu_count = <8>; - mem_size = <1048576>; - }; - vm2 { - is_ffa_partition; - debug_name = "cactus-secondary"; - load_address = <0xfe100000>; - vcpu_count = <8>; - mem_size = <1048576>; - }; - vm3 { - is_ffa_partition; - debug_name = "cactus-tertiary"; - load_address = <0xfe200000>; - vcpu_count = <1>; - mem_size = <1048576>; - }; - vm4 { - is_ffa_partition; - debug_name = "ivy"; - load_address = <0xfe600000>; - vcpu_count = <1>; - mem_size = <1048576>; - }; + /* filled in in top level .dts */ }; cpus { @@ -117,16 +90,34 @@ }; }; + /* the full secure world range */ memory@0 { device_type = "memory"; - reg = <0x0 0xfd000000 0x0 0x2000000>, - <0x0 0x7000000 0x0 0x1000000>, + reg = <0x0 TC_TZC_DRAM1_BASE 0x0 TC_TZC_DRAM1_SIZE>, <0x0 0xff000000 0x0 0x1000000>; }; memory@1 { device_type = "ns-memory"; - reg = <0x00008800 0x80000000 0x0 0x7f000000>, - <0x0 0x88000000 0x1 0x00000000>; + reg = <0x0 TC_NS_DRAM1_BASE 0x0 TC_NS_DRAM1_SIZE>, + <HI(PLAT_ARM_DRAM2_BASE) LO(PLAT_ARM_DRAM2_BASE) + HI(TC_NS_DRAM2_SIZE) LO(TC_NS_DRAM2_SIZE)>; + }; + + memory@2 { + device_type = "device-memory"; + reg = <0x0 0x25000000 0x0 0x10000>; /* For cactus tertiary dummy device. */ + }; + + s_uart { + device_type = "device-memory"; + reg = <0x0 PLAT_ARM_BOOT_UART_BASE 0x0 0x01000>; + }; + +#ifdef TS_SP_FW_CONFIG + ns_flash { + device_type = "ns-device-memory"; + reg = <0x0 V2M_FLASH0_BASE 0x0 V2M_FLASH0_SIZE>; }; +#endif }; diff --git a/plat/arm/board/tc/fdts/tc_spmc_optee_sp_manifest.dts b/plat/arm/board/tc/fdts/tc_spmc_optee_sp_manifest.dts index 382f0e1b..840b80ff 100644 --- a/plat/arm/board/tc/fdts/tc_spmc_optee_sp_manifest.dts +++ b/plat/arm/board/tc/fdts/tc_spmc_optee_sp_manifest.dts @@ -1,129 +1,17 @@ /* - * Copyright (c) 2020-2023, Arm Limited. All rights reserved. + * Copyright (c) 2020-2024, Arm Limited. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ /dts-v1/; -/ { - compatible = "arm,ffa-core-manifest-1.0"; - #address-cells = <2>; - #size-cells = <2>; +#include <tc_spmc_manifest.dtsi> +#include <tc_spmc_common_sp_manifest.dtsi> - attribute { - spmc_id = <0x8000>; - maj_ver = <0x1>; - min_ver = <0x1>; - exec_state = <0x0>; - load_address = <0x0 0xfd000000>; - entrypoint = <0x0 0xfd000000>; - binary_size = <0x80000>; - }; - - hypervisor { - compatible = "hafnium,hafnium"; - vm1 { - is_ffa_partition; - debug_name = "op-tee"; - load_address = <0xfd280000>; - vcpu_count = <8>; -#ifdef TS_SP_FW_CONFIG - mem_size = <26738688>; /* 25MB TZC DRAM */ -#else - mem_size = <30928896>; /* 29MB TZC DRAM */ -#endif - }; -#ifdef TS_SP_FW_CONFIG - vm2 { - is_ffa_partition; - debug_name = "internal-trusted-storage"; - load_address = <0xfee00000>; - vcpu_count = <1>; - mem_size = <2097152>; /* 2MB TZC DRAM */ - }; - vm3 { - is_ffa_partition; - debug_name = "crypto"; - load_address = <0xfec00000>; - vcpu_count = <1>; - mem_size = <2097152>; /* 2MB TZC DRAM */ - }; -#endif - }; - - cpus { - #address-cells = <0x2>; - #size-cells = <0x0>; - - CPU0:cpu@0 { - device_type = "cpu"; - compatible = "arm,armv8"; - reg = <0x0 0x0>; - enable-method = "psci"; - }; - - /* - * SPMC (Hafnium) requires secondary cpu nodes are declared in - * descending order - */ - CPU7:cpu@700 { - device_type = "cpu"; - compatible = "arm,armv8"; - reg = <0x0 0x700>; - enable-method = "psci"; - }; - - CPU6:cpu@600 { - device_type = "cpu"; - compatible = "arm,armv8"; - reg = <0x0 0x600>; - enable-method = "psci"; - }; - - CPU5:cpu@500 { - device_type = "cpu"; - compatible = "arm,armv8"; - reg = <0x0 0x500>; - enable-method = "psci"; - }; - - CPU4:cpu@400 { - device_type = "cpu"; - compatible = "arm,armv8"; - reg = <0x0 0x400>; - enable-method = "psci"; - }; - - CPU3:cpu@300 { - device_type = "cpu"; - compatible = "arm,armv8"; - reg = <0x0 0x300>; - enable-method = "psci"; - }; - - CPU2:cpu@200 { - device_type = "cpu"; - compatible = "arm,armv8"; - reg = <0x0 0x200>; - enable-method = "psci"; - }; - - CPU1:cpu@100 { - device_type = "cpu"; - compatible = "arm,armv8"; - reg = <0x0 0x100>; - enable-method = "psci"; - }; - }; - - memory@0 { - device_type = "memory"; - reg = <0x0 0xfd000000 0x0 0x2000000>; - }; - - memory@1 { - device_type = "ns-memory"; - reg = <0x0 0x80000000 0x0 0x79000000>, - <0x80 0x80000000 0x1 0x80000000>; +&hafnium { + vm1 { + debug_name = "op-tee"; + load_address = <0xfd280000>; + mem_size = <0xb00000>; /* 11MB TZC DRAM */ }; }; diff --git a/plat/arm/board/tc/fdts/tc_spmc_test_manifest.dts b/plat/arm/board/tc/fdts/tc_spmc_test_manifest.dts new file mode 100644 index 00000000..2e35f82c --- /dev/null +++ b/plat/arm/board/tc/fdts/tc_spmc_test_manifest.dts @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2023-2024, Arm Limited. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ +/dts-v1/; + +#include <tc_spmc_manifest.dtsi> + +/ { + hypervisor { + vm1 { + is_ffa_partition; + debug_name = "cactus-primary"; + load_address = <0xfe000000>; + vcpu_count = <8>; + mem_size = <1048576>; + }; + vm2 { + is_ffa_partition; + debug_name = "cactus-secondary"; + load_address = <0xfe100000>; + vcpu_count = <8>; + mem_size = <1048576>; + }; + vm3 { + is_ffa_partition; + debug_name = "cactus-tertiary"; + load_address = <0xfe200000>; + vcpu_count = <1>; + mem_size = <1048576>; + }; + vm4 { + is_ffa_partition; + debug_name = "ivy"; + load_address = <0xfe600000>; + vcpu_count = <1>; + mem_size = <1048576>; + }; + }; +}; diff --git a/plat/arm/board/tc/fdts/tc_spmc_trusty_sp_manifest.dts b/plat/arm/board/tc/fdts/tc_spmc_trusty_sp_manifest.dts new file mode 100644 index 00000000..8233eda4 --- /dev/null +++ b/plat/arm/board/tc/fdts/tc_spmc_trusty_sp_manifest.dts @@ -0,0 +1,17 @@ +/* + * Copyright (c) 2023-2024, Arm Limited. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ +/dts-v1/; + +#include <tc_spmc_manifest.dtsi> +#include <tc_spmc_common_sp_manifest.dtsi> + +&hafnium { + vm1 { + debug_name = "trusty"; + load_address = <0xf901c000>; + mem_size = <0x3f00000>; /* 64MB TZC DRAM - 1MB align */ + }; +}; diff --git a/plat/arm/board/tc/fdts/tc_tb_fw_config.dts b/plat/arm/board/tc/fdts/tc_tb_fw_config.dts index 4c6ccef2..cb741a35 100644 --- a/plat/arm/board/tc/fdts/tc_tb_fw_config.dts +++ b/plat/arm/board/tc/fdts/tc_tb_fw_config.dts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020-2021, Arm Limited. All rights reserved. + * Copyright (c) 2020-2024, Arm Limited. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -41,12 +41,21 @@ uuid = "d9df52d5-16a2-4bb2-9aa4-d26d3b84e8c0"; load-address = <0xfec00000>; }; + firmware-update { + uuid = "6823a838-1b06-470e-9774-0cce8bfb53fd"; + load-address = <0xfde00000>; + }; #endif #if OPTEE_SP_FW_CONFIG op-tee { uuid = "486178e0-e7f8-11e3-bc5e-0002a5d5c51b"; load-address = <0xfd280000>; }; +#elif TRUSTY_SP_FW_CONFIG + trusty { + uuid = "40ee25f0-a2bc-304c-8c4c-a173c57d8af1"; + load-address = <0xf901c000>; + }; #else cactus-primary { uuid = "b4b5671e-4a90-4fe1-b81f-fb13dae1dacb"; @@ -73,4 +82,7 @@ #endif #endif /* ARM_BL2_SP_LIST_DTS */ }; +#if DICE_PROTECTION_ENVIRONMENT + #include "dice_prot_env.dtsi" +#endif }; diff --git a/plat/arm/board/tc/include/platform_def.h b/plat/arm/board/tc/include/platform_def.h index 59fff6e2..86fce0e3 100644 --- a/plat/arm/board/tc/include/platform_def.h +++ b/plat/arm/board/tc/include/platform_def.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020-2023, Arm Limited. All rights reserved. + * Copyright (c) 2020-2024, Arm Limited. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -7,18 +7,31 @@ #ifndef PLATFORM_DEF_H #define PLATFORM_DEF_H +#include <cortex_a520.h> #include <lib/utils_def.h> #include <lib/xlat_tables/xlat_tables_defs.h> #include <plat/arm/board/common/board_css_def.h> #include <plat/arm/board/common/v2m_def.h> + +/* + * arm_def.h depends on the platform system counter macros, so must define the + * platform macros before including arm_def.h. + */ +#if TARGET_PLATFORM == 4 +#ifdef ARM_SYS_CNTCTL_BASE +#error "error: ARM_SYS_CNTCTL_BASE is defined prior to the PLAT_ARM_SYS_CNTCTL_BASE definition" +#endif +#define PLAT_ARM_SYS_CNTCTL_BASE UL(0x47000000) +#define PLAT_ARM_SYS_CNTREAD_BASE UL(0x47010000) +#endif + #include <plat/arm/common/arm_def.h> + #include <plat/arm/common/arm_spm_def.h> #include <plat/arm/css/common/css_def.h> #include <plat/arm/soc/common/soc_css_def.h> #include <plat/common/common_def.h> -#define PLATFORM_CORE_COUNT 8 - #define PLAT_ARM_TRUSTED_SRAM_SIZE 0x00080000 /* 512 KB */ /* @@ -30,6 +43,17 @@ * - Region to load secure partitions * * + * 0x8000_0000 ------------------ TC_NS_DRAM1_BASE + * | DTB | + * | (32K) | + * 0x8000_8000 ------------------ + * | NT_FW_CONFIG | + * | (4KB) | + * 0x8000_9000 ------------------ + * | ... | + * 0xf8e0_0000 ------------------ TC_NS_OPTEE_BASE + * | OP-TEE shmem | + * | (2MB) | * 0xF900_0000 ------------------ TC_TZC_DRAM1_BASE * | | * | SPMC | @@ -46,7 +70,7 @@ */ #define TC_TZC_DRAM1_BASE (ARM_AP_TZC_DRAM1_BASE - \ TC_TZC_DRAM1_SIZE) -#define TC_TZC_DRAM1_SIZE 96 * SZ_1M /* 96 MB */ +#define TC_TZC_DRAM1_SIZE (96 * SZ_1M) /* 96 MB */ #define TC_TZC_DRAM1_END (TC_TZC_DRAM1_BASE + \ TC_TZC_DRAM1_SIZE - 1) @@ -54,8 +78,10 @@ #define TC_NS_DRAM1_SIZE (ARM_DRAM1_SIZE - \ ARM_TZC_DRAM1_SIZE - \ TC_TZC_DRAM1_SIZE) -#define TC_NS_DRAM1_END (TC_NS_DRAM1_BASE + \ - TC_NS_DRAM1_SIZE - 1) +#define TC_NS_DRAM1_END (TC_NS_DRAM1_BASE + TC_NS_DRAM1_SIZE - 1) + +#define TC_NS_OPTEE_SIZE (2 * SZ_1M) +#define TC_NS_OPTEE_BASE (TC_NS_DRAM1_BASE + TC_NS_DRAM1_SIZE - TC_NS_OPTEE_SIZE) /* * Mappings for TC DRAM1 (non-secure) and TC TZC DRAM1 (secure) @@ -71,12 +97,12 @@ TC_TZC_DRAM1_SIZE, \ MT_MEMORY | MT_RW | MT_SECURE) -#define PLAT_HW_CONFIG_DTB_BASE ULL(0x83000000) -#define PLAT_HW_CONFIG_DTB_SIZE ULL(0x8000) +#define PLAT_HW_CONFIG_DTB_BASE TC_NS_DRAM1_BASE +#define PLAT_ARM_HW_CONFIG_SIZE ULL(0x8000) #define PLAT_DTB_DRAM_NS MAP_REGION_FLAT( \ PLAT_HW_CONFIG_DTB_BASE, \ - PLAT_HW_CONFIG_DTB_SIZE, \ + PLAT_ARM_HW_CONFIG_SIZE, \ MT_MEMORY | MT_RO | MT_NS) /* * Max size of SPMC is 2MB for tc. With SPMD enabled this value corresponds to @@ -137,7 +163,7 @@ * little space for growth. Current size is considering that TRUSTED_BOARD_BOOT * and MEASURED_BOOT is enabled. */ -# define PLAT_ARM_MAX_BL2_SIZE 0x26000 +# define PLAT_ARM_MAX_BL2_SIZE 0x29000 /* @@ -152,24 +178,16 @@ * Size of cacheable stacks */ #if defined(IMAGE_BL1) -# if TRUSTED_BOARD_BOOT # define PLATFORM_STACK_SIZE 0x1000 -# else -# define PLATFORM_STACK_SIZE 0x440 -# endif #elif defined(IMAGE_BL2) -# if TRUSTED_BOARD_BOOT # define PLATFORM_STACK_SIZE 0x1000 -# else -# define PLATFORM_STACK_SIZE 0x400 -# endif #elif defined(IMAGE_BL2U) # define PLATFORM_STACK_SIZE 0x400 #elif defined(IMAGE_BL31) # if SPM_MM # define PLATFORM_STACK_SIZE 0x500 # else -# define PLATFORM_STACK_SIZE 0xa00 +# define PLATFORM_STACK_SIZE 0xb00 # endif #elif defined(IMAGE_BL32) # define PLATFORM_STACK_SIZE 0x440 @@ -177,15 +195,22 @@ /* * In the current implementation the RoT Service request that requires the - * biggest message buffer is the RSS_DELEGATED_ATTEST_GET_PLATFORM_TOKEN. The + * biggest message buffer is the RSE_DELEGATED_ATTEST_GET_PLATFORM_TOKEN. The * maximum required buffer size is calculated based on the platform-specific * needs of this request. */ -#define PLAT_RSS_COMMS_PAYLOAD_MAX_SIZE 0x500 +#define PLAT_RSE_COMMS_PAYLOAD_MAX_SIZE 0x500 #define TC_DEVICE_BASE 0x21000000 #define TC_DEVICE_SIZE 0x5f000000 +#if defined(TARGET_FLAVOUR_FPGA) +#undef V2M_FLASH0_BASE +#undef V2M_FLASH0_SIZE +#define V2M_FLASH0_BASE UL(0x0C000000) +#define V2M_FLASH0_SIZE UL(0x02000000) +#endif + // TC_MAP_DEVICE covers different peripherals // available to the platform #define TC_MAP_DEVICE MAP_REGION_FLAT( \ @@ -197,8 +222,11 @@ #define TC_FLASH0_RO MAP_REGION_FLAT(V2M_FLASH0_BASE,\ V2M_FLASH0_SIZE, \ MT_DEVICE | MT_RO | MT_SECURE) - -#define PLAT_ARM_NSTIMER_FRAME_ID 0 +#if TARGET_PLATFORM == 2 +#define PLAT_ARM_NSTIMER_FRAME_ID U(0) +#else +#define PLAT_ARM_NSTIMER_FRAME_ID U(1) +#endif #define PLAT_ARM_TRUSTED_ROM_BASE 0x0 @@ -206,12 +234,24 @@ #define PLAT_ARM_TRUSTED_ROM_SIZE (0x00080000 - PLAT_ARM_TRUSTED_ROM_BASE) #define PLAT_ARM_NSRAM_BASE 0x06000000 +#if TARGET_FLAVOUR_FVP #define PLAT_ARM_NSRAM_SIZE 0x00080000 /* 512KB */ +#else /* TARGET_FLAVOUR_FPGA */ +#define PLAT_ARM_NSRAM_SIZE 0x00008000 /* 64KB */ +#endif /* TARGET_FLAVOUR_FPGA */ +#if TARGET_PLATFORM <= 2 #define PLAT_ARM_DRAM2_BASE ULL(0x8080000000) +#elif TARGET_PLATFORM >= 3 +#define PLAT_ARM_DRAM2_BASE ULL(0x880000000) +#endif /* TARGET_PLATFORM >= 3 */ #define PLAT_ARM_DRAM2_SIZE ULL(0x180000000) #define PLAT_ARM_DRAM2_END (PLAT_ARM_DRAM2_BASE + PLAT_ARM_DRAM2_SIZE - 1ULL) +#define TC_NS_MTE_SIZE (256 * SZ_1M) +/* the SCP puts the carveout at the end of DRAM2 */ +#define TC_NS_DRAM2_SIZE (PLAT_ARM_DRAM2_SIZE - TC_NS_MTE_SIZE) + #define PLAT_ARM_G1S_IRQ_PROPS(grp) CSS_G1S_INT_PROPS(grp) #define PLAT_ARM_G0_IRQ_PROPS(grp) ARM_G0_IRQ_PROPS(grp), \ INTR_PROP_DESC(SBSA_SECURE_WDOG_INTID, \ @@ -221,6 +261,8 @@ #define PLAT_ARM_SP_IMAGE_STACK_BASE (PLAT_SP_IMAGE_NS_BUF_BASE + \ PLAT_SP_IMAGE_NS_BUF_SIZE) +#define PLAT_ARM_SP_MAX_SIZE U(0x2000000) + /******************************************************************************* * Memprotect definitions ******************************************************************************/ @@ -240,17 +282,46 @@ #define PLAT_ARM_SCMI_CHANNEL_COUNT 1 +/* Index of SDS region used in the communication with SCP */ +#define SDS_SCP_AP_REGION_ID U(0) +/* Index of SDS region used in the communication with RSE */ +#define SDS_RSE_AP_REGION_ID U(1) +/* + * Memory region for RSE's shared data storage (SDS) + * It is placed right after the SCMI payload area. + */ +#define PLAT_ARM_RSE_AP_SDS_MEM_BASE (CSS_SCMI_PAYLOAD_BASE + \ + CSS_SCMI_PAYLOAD_SIZE_MAX) + #define PLAT_ARM_CLUSTER_COUNT U(1) +#if TARGET_FLAVOUR_FPGA && TARGET_PLATFORM == 2 +#define PLAT_MAX_CPUS_PER_CLUSTER U(14) +#else /* TARGET_FLAVOUR_FPGA && TARGET_PLATFORM == 2 */ #define PLAT_MAX_CPUS_PER_CLUSTER U(8) +#endif /* TARGET_FLAVOUR_FPGA && TARGET_PLATFORM == 2 */ #define PLAT_MAX_PE_PER_CPU U(1) +#define PLATFORM_CORE_COUNT (PLAT_MAX_CPUS_PER_CLUSTER * PLAT_ARM_CLUSTER_COUNT) + /* Message Handling Unit (MHU) base addresses */ -#define PLAT_CSS_MHU_BASE UL(0x45400000) +#if TARGET_PLATFORM <= 2 + #define PLAT_CSS_MHU_BASE UL(0x45400000) +#elif TARGET_PLATFORM >= 3 + #define PLAT_CSS_MHU_BASE UL(0x46000000) +#endif /* TARGET_PLATFORM >= 3 */ #define PLAT_MHUV2_BASE PLAT_CSS_MHU_BASE -/* TC2: AP<->RSS MHUs */ -#define PLAT_RSS_AP_SND_MHU_BASE UL(0x2A840000) -#define PLAT_RSS_AP_RCV_MHU_BASE UL(0x2A850000) +/* AP<->RSS MHUs */ +#if TARGET_PLATFORM <= 2 +#define PLAT_RSE_AP_SND_MHU_BASE UL(0x2A840000) +#define PLAT_RSE_AP_RCV_MHU_BASE UL(0x2A850000) +#elif TARGET_PLATFORM == 3 +#define PLAT_RSE_AP_SND_MHU_BASE UL(0x49000000) +#define PLAT_RSE_AP_RCV_MHU_BASE UL(0x49100000) +#elif TARGET_PLATFORM == 4 +#define PLAT_RSE_AP_SND_MHU_BASE UL(0x49000000) +#define PLAT_RSE_AP_RCV_MHU_BASE UL(0x49010000) +#endif #define CSS_SYSTEM_PWR_DMN_LVL ARM_PWR_LVL2 #define PLAT_MAX_PWR_LVL ARM_PWR_LVL1 @@ -278,6 +349,7 @@ */ #define PLAT_CSS_MAX_SCP_BL2U_SIZE 0x20000 +#if TARGET_PLATFORM <= 2 /* TZC Related Constants */ #define PLAT_ARM_TZC_BASE UL(0x25000000) #define PLAT_ARM_TZC_FILTERS TZC_400_REGION_ATTR_FILTER_BIT(0) @@ -305,6 +377,7 @@ PLAT_ARM_TZC_NS_DEV_ACCESS}, \ {PLAT_ARM_DRAM2_BASE, PLAT_ARM_DRAM2_END, \ ARM_TZC_NS_DRAM_S_ACCESS, PLAT_ARM_TZC_NS_DEV_ACCESS} +#endif /* virtual address used by dynamic mem_protect for chunk_base */ #define PLAT_ARM_MEM_PROTEC_VA_FRAME UL(0xc0000000) @@ -322,4 +395,88 @@ #define PLAT_ARM_FIP_OFFSET_IN_GPT 0x6000 #endif /* ARM_GPT_SUPPORT */ +/* UART related constants */ + +#define TC_UART0 0x2a400000 +#define TC_UART1 0x2a410000 + +/* + * TODO: if any more undefs are needed, it's better to consider dropping the + * board_css_def.h include above + */ +#undef PLAT_ARM_BOOT_UART_BASE +#undef PLAT_ARM_RUN_UART_BASE + +#undef PLAT_ARM_CRASH_UART_BASE +#undef PLAT_ARM_BOOT_UART_CLK_IN_HZ +#undef PLAT_ARM_RUN_UART_CLK_IN_HZ + +#if TARGET_FLAVOUR_FVP +#define PLAT_ARM_BOOT_UART_BASE TC_UART1 +#define TC_UARTCLK 7372800 +#else /* TARGET_FLAVOUR_FPGA */ +#define PLAT_ARM_BOOT_UART_BASE TC_UART0 +#if TARGET_PLATFORM <= 2 +#define TC_UARTCLK 5000000 +#elif TARGET_PLATFORM >= 3 +#define TC_UARTCLK 3750000 +#endif /* TARGET_PLATFORM >= 3 */ +#undef ARM_CONSOLE_BAUDRATE +#define ARM_CONSOLE_BAUDRATE 38400 +#endif /* TARGET_FLAVOUR_FPGA */ + +#define PLAT_ARM_RUN_UART_BASE TC_UART0 +#define PLAT_ARM_CRASH_UART_BASE PLAT_ARM_RUN_UART_BASE + +#define PLAT_ARM_BOOT_UART_CLK_IN_HZ TC_UARTCLK +#define PLAT_ARM_RUN_UART_CLK_IN_HZ TC_UARTCLK + +#if TARGET_PLATFORM == 3 +#define NCI_BASE_ADDR UL(0x4F000000) +#ifdef TARGET_FLAVOUR_FPGA +#define MCN_ADDRESS_SPACE_SIZE 0x00120000 +#else +#define MCN_ADDRESS_SPACE_SIZE 0x00130000 +#endif /* TARGET_FLAVOUR_FPGA */ +#define MCN_OFFSET_IN_NCI 0x00C90000 +#define MCN_BASE_ADDR (NCI_BASE_ADDR + MCN_OFFSET_IN_NCI) +#define MCN_PMU_OFFSET 0x000C4000 +#define MCN_MICROARCH_OFFSET 0x000E4000 +#define MCN_MICROARCH_BASE_ADDR (MCN_BASE_ADDR + MCN_MICROARCH_OFFSET) +#define MCN_SCR_OFFSET 0x4 +#define MCN_SCR_PMU_BIT 10 +#define MCN_INSTANCES 4 +#define MCN_PMU_ADDR(n) (MCN_BASE_ADDR + \ + (n * MCN_ADDRESS_SPACE_SIZE) + \ + MCN_PMU_OFFSET) +#define MCN_MPAM_NS_OFFSET 0x000D0000 +#define MCN_MPAM_NS_BASE_ADDR (MCN_BASE_ADDR + MCN_MPAM_NS_OFFSET) +#define MCN_MPAM_S_OFFSET 0x000D4000 +#define MCN_MPAM_S_BASE_ADDR (MCN_BASE_ADDR + MCN_MPAM_S_OFFSET) +#define MPAM_SLCCFG_CTL_OFFSET 0x00003018 +#define SLC_RDALLOCMODE_SHIFT 8 +#define SLC_RDALLOCMODE_MASK (3 << SLC_RDALLOCMODE_SHIFT) +#define SLC_WRALLOCMODE_SHIFT 12 +#define SLC_WRALLOCMODE_MASK (3 << SLC_WRALLOCMODE_SHIFT) + +#define SLC_DONT_ALLOC 0 +#define SLC_ALWAYS_ALLOC 1 +#define SLC_ALLOC_BUS_SIGNAL_ATTR 2 + +#define MCN_CONFIG_OFFSET 0x204 +#define MCN_CONFIG_ADDR (MCN_BASE_ADDR + MCN_CONFIG_OFFSET) +#define MCN_CONFIG_SLC_PRESENT_BIT 3 + +/* + * TC3 CPUs have the same definitions for: + * CORTEX_{A520|A725|X925}_CPUECTLR_EL1 + * CORTEX_{A520|A725|X925}_CPUECTLR_EL1_EXTLLC_BIT + * Define the common macros for easier using. + */ +#define CPUECTLR_EL1 CORTEX_A520_CPUECTLR_EL1 +#define CPUECTLR_EL1_EXTLLC_BIT CORTEX_A520_CPUECTLR_EL1_EXTLLC_BIT +#endif /* TARGET_PLATFORM == 3 */ + +#define CPUACTLR_CLUSTERPMUEN (ULL(1) << 12) + #endif /* PLATFORM_DEF_H */ diff --git a/plat/arm/board/tc/include/tc_helpers.S b/plat/arm/board/tc/include/tc_helpers.S index 5f548566..9adf09af 100644 --- a/plat/arm/board/tc/include/tc_helpers.S +++ b/plat/arm/board/tc/include/tc_helpers.S @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2024, ARM Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -9,6 +9,9 @@ #include <platform_def.h> #include <cpu_macros.S> +#define TC_HANDLER(rev) plat_reset_handler_tc##rev +#define PLAT_RESET_HANDLER(rev) TC_HANDLER(rev) + .globl plat_arm_calc_core_pos .globl plat_reset_handler @@ -49,13 +52,46 @@ func plat_arm_calc_core_pos ret endfunc plat_arm_calc_core_pos +func mark_extllc_presence +#ifdef MCN_CONFIG_ADDR + mov_imm x0, (MCN_CONFIG_ADDR) + ldr w1, [x0] + ubfx x1, x1, #MCN_CONFIG_SLC_PRESENT_BIT, #1 + sysreg_bitfield_insert_from_gpr CPUECTLR_EL1, x1, \ + CPUECTLR_EL1_EXTLLC_BIT, 1 +#endif + ret +endfunc mark_extllc_presence + +func enable_dsu_pmu_el1_access + sysreg_bit_set actlr_el2, CPUACTLR_CLUSTERPMUEN + sysreg_bit_set actlr_el3, CPUACTLR_CLUSTERPMUEN + ret +endfunc enable_dsu_pmu_el1_access + +func TC_HANDLER(2) + ret +endfunc TC_HANDLER(2) + +func TC_HANDLER(3) + mov x9, lr + bl mark_extllc_presence + bl enable_dsu_pmu_el1_access + mov lr, x9 + ret +endfunc TC_HANDLER(3) + +func TC_HANDLER(4) + ret +endfunc TC_HANDLER(4) + /* ----------------------------------------------------- * void plat_reset_handler(void); - * - * Determine the CPU MIDR and disable power down bit for - * that CPU. * ----------------------------------------------------- */ func plat_reset_handler + mov x8, lr + bl PLAT_RESET_HANDLER(TARGET_PLATFORM) + mov lr, x8 ret endfunc plat_reset_handler diff --git a/plat/arm/board/tc/include/tc_plat.h b/plat/arm/board/tc/include/tc_plat.h index a6b2b0df..6ba4694f 100644 --- a/plat/arm/board/tc/include/tc_plat.h +++ b/plat/arm/board/tc/include/tc_plat.h @@ -8,7 +8,7 @@ #define TC_PLAT_H #ifdef PLATFORM_TEST_ROTPK -#include <rss_crypto_defs.h> +#include <rse_crypto_defs.h> #endif void tc_bl31_common_platform_setup(void); @@ -23,7 +23,7 @@ int nv_counter_test(void); #ifdef PLATFORM_TEST_ROTPK struct key_id_info { - enum rss_key_id_builtin_t key_id; + enum rse_key_id_builtin_t key_id; const char *key_id_name; }; diff --git a/plat/arm/board/tc/nv_counter_test.c b/plat/arm/board/tc/nv_counter_test.c index 179ec4b0..90255693 100644 --- a/plat/arm/board/tc/nv_counter_test.c +++ b/plat/arm/board/tc/nv_counter_test.c @@ -7,9 +7,9 @@ #include <stdint.h> #include <stdio.h> -#include <drivers/arm/rss_comms.h> +#include <drivers/arm/rse_comms.h> #include <plat/common/platform.h> -#include "rss_platform_api.h" +#include "rse_platform_api.h" #include <platform_def.h> @@ -20,30 +20,30 @@ int nv_counter_test(void) uint32_t new_val; uint32_t id; - status = rss_comms_init(PLAT_RSS_AP_SND_MHU_BASE, PLAT_RSS_AP_RCV_MHU_BASE); + status = rse_comms_init(PLAT_RSE_AP_SND_MHU_BASE, PLAT_RSE_AP_RCV_MHU_BASE); if (status != PSA_SUCCESS) { - printf("Failed to initialize RSS communication channel - psa_status = %d\n", status); + printf("Failed to initialize RSE communication channel - psa_status = %d\n", status); return -1; } for (id = 0; id < 3; id++) { - status = rss_platform_nv_counter_read(id, sizeof(old_val), (uint8_t *)&old_val); + status = rse_platform_nv_counter_read(id, sizeof(old_val), (uint8_t *)&old_val); if (status != PSA_SUCCESS) { - printf("Failed during first id=(%d) rss_platform_nv_counter_read - psa_status = %d\n", + printf("Failed during first id=(%d) rse_platform_nv_counter_read - psa_status = %d\n", id, status); return -1; } - status = rss_platform_nv_counter_increment(id); + status = rse_platform_nv_counter_increment(id); if (status != PSA_SUCCESS) { - printf("Failed during id=(%d) rss_platform_nv_counter_increment - psa_status = %d\n", + printf("Failed during id=(%d) rse_platform_nv_counter_increment - psa_status = %d\n", id, status); return -1; } - status = rss_platform_nv_counter_read(id, sizeof(new_val), (uint8_t *)&new_val); + status = rse_platform_nv_counter_read(id, sizeof(new_val), (uint8_t *)&new_val); if (status != PSA_SUCCESS) { - printf("Failed during second id=(%d) rss_platform_nv_counter_read - psa_status = %d\n", + printf("Failed during second id=(%d) rse_platform_nv_counter_read - psa_status = %d\n", id, status); return -1; } diff --git a/plat/arm/board/tc/plat_def_fip_uuid.h b/plat/arm/board/tc/plat_def_fip_uuid.h index 631f7c95..46a455cd 100644 --- a/plat/arm/board/tc/plat_def_fip_uuid.h +++ b/plat/arm/board/tc/plat_def_fip_uuid.h @@ -10,28 +10,28 @@ #include "uuid.h" -#define UUID_RSS_FIRMWARE_BL1_2 \ +#define UUID_RSE_FIRMWARE_BL1_2 \ {{0x0a, 0xa5, 0xb1, 0xbe}, {0xe7, 0x84}, {0x41, 0xc5}, 0x81, 0xb8, {0x4a, 0x41, 0xcb, 0x4a, 0xd2, 0xdf}} -#define UUID_RSS_FIRMWARE_BL2 \ +#define UUID_RSE_FIRMWARE_BL2 \ {{0xa3, 0xb3, 0xb3, 0x0d}, {0xeb, 0xc9}, {0x40, 0x48}, 0xb4, 0x80, {0x15, 0x53, 0x61, 0xc1, 0x70, 0x48}} -#define UUID_RSS_FIRMWARE_SCP_BL1 \ +#define UUID_RSE_FIRMWARE_SCP_BL1 \ {{0xbf, 0xd5, 0x09, 0x8d}, {0xa7, 0x07}, {0x4f, 0x15}, 0x89, 0x1c, {0x37, 0x22, 0x10, 0xcb, 0x51, 0xe2}} -#define UUID_RSS_FIRMWARE_AP_BL1 \ +#define UUID_RSE_FIRMWARE_AP_BL1 \ {{0x12, 0x4c, 0x50, 0xe0}, {0xf2, 0xda}, {0x45, 0xe9}, 0x85, 0xc8, {0xda, 0xd9, 0x60, 0x9b, 0x7a, 0x11}} -#define UUID_RSS_FIRMWARE_NS \ +#define UUID_RSE_FIRMWARE_NS \ {{0x8d, 0x95, 0x9f, 0x72}, {0xb8, 0xb1}, {0x42, 0x11}, 0x9a, 0xe6, {0x4b, 0x80, 0x97, 0x47, 0x5a, 0xd9}} -#define UUID_RSS_FIRMWARE_S \ +#define UUID_RSE_FIRMWARE_S \ {{0x22, 0xea, 0x33, 0x85}, {0xf8, 0x6e}, {0x47, 0x93}, 0x96, 0x8a, {0x2f, 0xe3, 0xdd, 0x50, 0x33, 0xcc}} -#define UUID_RSS_SIC_TABLES_NS \ +#define UUID_RSE_SIC_TABLES_NS \ {{0xd9, 0x10, 0x00, 0x72}, {0x6a, 0x28}, {0x4b, 0xec}, 0xb0, 0xd6, {0x8c, 0xed, 0xc4, 0x15, 0x7c, 0xe0}} -#define UUID_RSS_SIC_TABLES_S \ +#define UUID_RSE_SIC_TABLES_S \ {{0xc7, 0x38, 0xd0, 0xde}, {0x8c, 0x26}, {0x48, 0x51}, 0x93, 0x36, {0xf3, 0xdb, 0xe2, 0x96, 0x65, 0x18}} #endif /* __PLAT_DEF_FIP_UUID__ */ diff --git a/plat/arm/board/tc/plat_tc_mbedtls_config.h b/plat/arm/board/tc/plat_tc_mbedtls_config.h index f0aa60b5..4fd8b6be 100644 --- a/plat/arm/board/tc/plat_tc_mbedtls_config.h +++ b/plat/arm/board/tc/plat_tc_mbedtls_config.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022-2023, Arm Ltd. All rights reserved. + * Copyright (c) 2022-2024, Arm Ltd. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -19,15 +19,26 @@ #undef TF_MBEDTLS_HEAP_SIZE #define TF_MBEDTLS_HEAP_SIZE PLATFORM_TEST_MIN_MBEDTLS_HEAP_SIZE #endif -#endif +#endif /* TF_MBEDTLS_HEAP_SIZE */ + +/** + * On Arm TC platforms, the ROTPK is always hashed using the SHA-256 + * algorithm. + * TODO: Update to hash the ROTPK with the selected HASH_ALG to avoid + * the need for explicitly enabling the SHA-256 configuration in mbedTLS. + */ +#define MBEDTLS_SHA256_C + +/* + * Use an implementation of SHA-256 with a smaller memory footprint + * but reduced speed. + */ +#define MBEDTLS_SHA256_SMALLER #define MBEDTLS_PSA_CRYPTO_C -#define MBEDTLS_HMAC_DRBG_C -#define MBEDTLS_ENTROPY_C -#define MBEDTLS_NO_DEFAULT_ENTROPY_SOURCES -#define MBEDTLS_NO_PLATFORM_ENTROPY -#define MBEDTLS_TEST_NULL_ENTROPY +#define MBEDTLS_PSA_CRYPTO_EXTERNAL_RNG #define MBEDTLS_ECP_C #define MBEDTLS_ECP_DP_SECP384R1_ENABLED +#define MBEDTLS_ECP_NO_INTERNAL_RNG #endif /* PLAT_TC_MBEDTLS_CONFIG_H */ diff --git a/plat/arm/board/tc/platform.mk b/plat/arm/board/tc/platform.mk index 6874cfa5..9cd3011e 100644 --- a/plat/arm/board/tc/platform.mk +++ b/plat/arm/board/tc/platform.mk @@ -1,61 +1,101 @@ -# Copyright (c) 2021-2023, Arm Limited. All rights reserved. +# Copyright (c) 2021-2024, Arm Limited. All rights reserved. # # SPDX-License-Identifier: BSD-3-Clause # include common/fdt_wrappers.mk -ifeq ($(TARGET_PLATFORM), 0) - $(error Platform ${PLAT}$(TARGET_PLATFORM) is deprecated.) -endif - -ifeq ($(TARGET_PLATFORM), 1) - $(warning Platform ${PLAT}$(TARGET_PLATFORM) is deprecated. \ - Some of the features might not work as expected) -endif +TARGET_FLAVOUR := fvp +# DPU with SCMI may not necessarily work, so allow its independence +TC_DPU_USE_SCMI_CLK := 1 +# SCMI power domain control enable +TC_SCMI_PD_CTRL_EN := 1 + +# System setup +CSS_USE_SCMI_SDS_DRIVER := 1 +HW_ASSISTED_COHERENCY := 1 +USE_COHERENT_MEM := 0 +GIC_ENABLE_V4_EXTN := 1 +GICV3_SUPPORT_GIC600 := 1 +override NEED_BL2U := no +override ARM_PLAT_MT := 1 + +# CPU setup +ARM_ARCH_MINOR := 7 +BRANCH_PROTECTION := 1 +ENABLE_FEAT_MPAM := 1 # default is 2, optimise +ENABLE_SVE_FOR_NS := 2 # to show we use it +ENABLE_SVE_FOR_SWD := 1 +ENABLE_SME_FOR_NS := 2 +ENABLE_SME2_FOR_NS := 2 +ENABLE_SME_FOR_SWD := 1 +ENABLE_TRBE_FOR_NS := 1 +ENABLE_SYS_REG_TRACE_FOR_NS := 1 +ENABLE_FEAT_AMU := 1 +ENABLE_AMU_FCONF := 1 +ENABLE_AMU_AUXILIARY_COUNTERS := 1 +ENABLE_MPMM := 1 +ENABLE_MPMM_FCONF := 1 +ENABLE_FEAT_MTE2 := 2 +ENABLE_SPE_FOR_NS := 3 +ENABLE_FEAT_TCR2 := 3 + +CTX_INCLUDE_AARCH32_REGS := 0 -ifeq ($(shell expr $(TARGET_PLATFORM) \<= 2), 0) - $(error TARGET_PLATFORM must be less than or equal to 2) +ifeq (${SPD},spmd) + SPMD_SPM_AT_SEL2 := 1 + CTX_INCLUDE_PAUTH_REGS := 1 endif -$(eval $(call add_define,TARGET_PLATFORM)) - -CSS_LOAD_SCP_IMAGES := 1 - -CSS_USE_SCMI_SDS_DRIVER := 1 - -ENABLE_FEAT_RAS := 1 - -SDEI_SUPPORT := 0 +# TC RESOLUTION - LIST OF VALID OPTIONS (this impacts only FVP) +TC_RESOLUTION_OPTIONS := 640x480p60 \ + 1920x1080p60 +# Set default to the 640x480p60 resolution mode +TC_RESOLUTION ?= $(firstword $(TC_RESOLUTION_OPTIONS)) -EL3_EXCEPTION_HANDLING := 0 - -HANDLE_EA_EL3_FIRST_NS := 0 +# Check resolution option for FVP +ifneq ($(filter ${TARGET_FLAVOUR}, fvp),) +ifeq ($(filter ${TC_RESOLUTION}, ${TC_RESOLUTION_OPTIONS}),) + $(error TC_RESOLUTION is ${TC_RESOLUTION}, it must be: ${TC_RESOLUTION_OPTIONS}) +endif +endif -# System coherency is managed in hardware -HW_ASSISTED_COHERENCY := 1 +ifneq ($(shell expr $(TARGET_PLATFORM) \<= 1), 0) + $(error Platform ${PLAT}$(TARGET_PLATFORM) is no longer available.) +endif -# When building for systems with hardware-assisted coherency, there's no need to -# use USE_COHERENT_MEM. Require that USE_COHERENT_MEM must be set to 0 too. -USE_COHERENT_MEM := 0 +ifneq ($(shell expr $(TARGET_PLATFORM) = 2), 0) + $(warning Platform ${PLAT}$(TARGET_PLATFORM) is deprecated. \ + Some of the features might not work as expected) +endif -GIC_ENABLE_V4_EXTN := 1 +ifeq ($(shell expr $(TARGET_PLATFORM) \<= 4), 0) + $(error TARGET_PLATFORM must be less than or equal to 4) +endif -# GIC-600 configuration -GICV3_SUPPORT_GIC600 := 1 +ifeq ($(filter ${TARGET_FLAVOUR}, fvp fpga),) + $(error TARGET_FLAVOUR must be fvp or fpga) +endif -# Enable SVE -ENABLE_SVE_FOR_NS := 2 -ENABLE_SVE_FOR_SWD := 1 +$(eval $(call add_defines, \ + TARGET_PLATFORM \ + TARGET_FLAVOUR_$(call uppercase,${TARGET_FLAVOUR}) \ + TC_RESOLUTION_$(call uppercase,${TC_RESOLUTION}) \ + TC_DPU_USE_SCMI_CLK \ + TC_SCMI_PD_CTRL_EN \ +)) -# enable trace buffer control registers access to NS by default -ENABLE_TRBE_FOR_NS := 1 +CSS_LOAD_SCP_IMAGES := 1 -# enable trace system registers access to NS by default -ENABLE_SYS_REG_TRACE_FOR_NS := 1 +# Save DSU PMU registers on cluster off and restore them on cluster on +PRESERVE_DSU_PMU_REGS := 1 -# enable trace filter control registers access to NS by default -ENABLE_TRF_FOR_NS := 1 +# Specify MHU type based on platform +ifneq ($(filter ${TARGET_PLATFORM}, 2),) + PLAT_MHU_VERSION := 2 +else + PLAT_MHU_VERSION := 3 +endif # Include GICv3 driver files include drivers/arm/gic/v3/gicv3.mk @@ -64,13 +104,10 @@ ENT_GIC_SOURCES := ${GICV3_SOURCES} \ plat/common/plat_gicv3.c \ plat/arm/common/arm_gicv3.c -override NEED_BL2U := no - -override ARM_PLAT_MT := 1 - TC_BASE = plat/arm/board/tc -PLAT_INCLUDES += -I${TC_BASE}/include/ +PLAT_INCLUDES += -I${TC_BASE}/include/ \ + -I${TC_BASE}/fdts/ # CPU libraries for TARGET_PLATFORM=1 ifeq (${TARGET_PLATFORM}, 1) @@ -81,12 +118,32 @@ endif # CPU libraries for TARGET_PLATFORM=2 ifeq (${TARGET_PLATFORM}, 2) +ERRATA_A520_2938996 := 1 +ERRATA_X4_2726228 := 1 + TC_CPU_SOURCES += lib/cpus/aarch64/cortex_a520.S \ lib/cpus/aarch64/cortex_a720.S \ lib/cpus/aarch64/cortex_x4.S endif -INTERCONNECT_SOURCES := ${TC_BASE}/tc_interconnect.c +# CPU libraries for TARGET_PLATFORM=3 +ifeq (${TARGET_PLATFORM}, 3) +ERRATA_A520_2938996 := 1 + +TC_CPU_SOURCES += lib/cpus/aarch64/cortex_a520.S \ + lib/cpus/aarch64/cortex_a725.S \ + lib/cpus/aarch64/cortex_x925.S +endif + +# CPU libraries for TARGET_PLATFORM=4 +ifeq (${TARGET_PLATFORM}, 4) +TC_CPU_SOURCES += lib/cpus/aarch64/cortex_gelas.S \ + lib/cpus/aarch64/nevis.S \ + lib/cpus/aarch64/travis.S +endif + +INTERCONNECT_SOURCES := ${TC_BASE}/tc_interconnect.c \ + plat/arm/common/arm_ni.c PLAT_BL_COMMON_SOURCES += ${TC_BASE}/tc_plat.c \ ${TC_BASE}/include/tc_helpers.S @@ -94,6 +151,7 @@ PLAT_BL_COMMON_SOURCES += ${TC_BASE}/tc_plat.c \ BL1_SOURCES += ${INTERCONNECT_SOURCES} \ ${TC_CPU_SOURCES} \ ${TC_BASE}/tc_trusted_boot.c \ + ${TC_BASE}/tc_bl1_setup.c \ ${TC_BASE}/tc_err.c \ drivers/arm/sbsa/sbsa.c @@ -103,9 +161,12 @@ BL2_SOURCES += ${TC_BASE}/tc_security.c \ ${TC_BASE}/tc_bl2_setup.c \ lib/utils/mem_region.c \ drivers/arm/tzc/tzc400.c \ - plat/arm/common/arm_tzc400.c \ plat/arm/common/arm_nor_psci_mem_protect.c +ifeq ($(shell test $(TARGET_PLATFORM) -le 2; echo $$?),0) +BL2_SOURCES += plat/arm/common/arm_tzc400.c +endif + BL31_SOURCES += ${INTERCONNECT_SOURCES} \ ${TC_CPU_SOURCES} \ ${ENT_GIC_SOURCES} \ @@ -113,6 +174,7 @@ BL31_SOURCES += ${INTERCONNECT_SOURCES} \ ${TC_BASE}/tc_topology.c \ lib/fconf/fconf.c \ lib/fconf/fconf_dyn_cfg_getter.c \ + drivers/arm/css/dsu/dsu.c \ drivers/cfi/v2m/v2m_flash.c \ lib/utils/mem_region.c \ plat/arm/common/arm_nor_psci_mem_protect.c \ @@ -122,18 +184,22 @@ BL31_SOURCES += ${FDT_WRAPPERS_SOURCES} # Add the FDT_SOURCES and options for Dynamic Config FDT_SOURCES += ${TC_BASE}/fdts/${PLAT}_fw_config.dts \ - ${TC_BASE}/fdts/${PLAT}_tb_fw_config.dts + ${TC_BASE}/fdts/${PLAT}_tb_fw_config.dts \ + ${TC_BASE}/fdts/${PLAT}_nt_fw_config.dts FW_CONFIG := ${BUILD_PLAT}/fdts/${PLAT}_fw_config.dtb TB_FW_CONFIG := ${BUILD_PLAT}/fdts/${PLAT}_tb_fw_config.dtb +FVP_NT_FW_CONFIG := ${BUILD_PLAT}/fdts/${PLAT}_nt_fw_config.dtb # Add the FW_CONFIG to FIP and specify the same to certtool $(eval $(call TOOL_ADD_PAYLOAD,${FW_CONFIG},--fw-config,${FW_CONFIG})) # Add the TB_FW_CONFIG to FIP and specify the same to certtool $(eval $(call TOOL_ADD_PAYLOAD,${TB_FW_CONFIG},--tb-fw-config,${TB_FW_CONFIG})) +# Add the NT_FW_CONFIG to FIP and specify the same to certtool +$(eval $(call TOOL_ADD_PAYLOAD,${FVP_NT_FW_CONFIG},--nt-fw-config,${FVP_NT_FW_CONFIG})) ifeq (${SPD},spmd) ifeq ($(ARM_SPMC_MANIFEST_DTS),) -ARM_SPMC_MANIFEST_DTS := ${TC_BASE}/fdts/${PLAT}_spmc_manifest.dts +ARM_SPMC_MANIFEST_DTS := ${TC_BASE}/fdts/${PLAT}_spmc_test_manifest.dts endif FDT_SOURCES += ${ARM_SPMC_MANIFEST_DTS} @@ -144,7 +210,7 @@ $(eval $(call TOOL_ADD_PAYLOAD,${TC_TOS_FW_CONFIG},--tos-fw-config,${TC_TOS_FW_C endif #Device tree -TC_HW_CONFIG_DTS := fdts/tc.dts +TC_HW_CONFIG_DTS := fdts/${PLAT}${TARGET_PLATFORM}.dts TC_HW_CONFIG := ${BUILD_PLAT}/fdts/${PLAT}.dtb FDT_SOURCES += ${TC_HW_CONFIG_DTS} $(eval TC_HW_CONFIG := ${BUILD_PLAT}/$(patsubst %.dts,%.dtb,$(TC_HW_CONFIG_DTS))) @@ -152,43 +218,59 @@ $(eval TC_HW_CONFIG := ${BUILD_PLAT}/$(patsubst %.dts,%.dtb,$(TC_HW_CONFIG_DTS)) # Add the HW_CONFIG to FIP and specify the same to certtool $(eval $(call TOOL_ADD_PAYLOAD,${TC_HW_CONFIG},--hw-config,${TC_HW_CONFIG})) -override CTX_INCLUDE_AARCH32_REGS := 0 - -override CTX_INCLUDE_PAUTH_REGS := 1 - -override ENABLE_SPE_FOR_NS := 0 - -override ENABLE_FEAT_AMU := 1 -override ENABLE_AMU_AUXILIARY_COUNTERS := 1 -override ENABLE_AMU_FCONF := 1 - -override ENABLE_MPMM := 1 -override ENABLE_MPMM_FCONF := 1 - # Include Measured Boot makefile before any Crypto library makefile. # Crypto library makefile may need default definitions of Measured Boot build # flags present in Measured Boot makefile. +$(info Including rse_comms.mk) ifeq (${MEASURED_BOOT},1) - MEASURED_BOOT_MK := drivers/measured_boot/rss/rss_measured_boot.mk - $(info Including ${MEASURED_BOOT_MK}) - include ${MEASURED_BOOT_MK} - $(info Including rss_comms.mk) - include drivers/arm/rss/rss_comms.mk - - BL1_SOURCES += ${MEASURED_BOOT_SOURCES} \ + $(info Including rse_comms.mk) + include drivers/arm/rse/rse_comms.mk + + BL1_SOURCES += ${RSE_COMMS_SOURCES} + BL2_SOURCES += ${RSE_COMMS_SOURCES} + PLAT_INCLUDES += -Iinclude/lib/psa + + ifeq (${DICE_PROTECTION_ENVIRONMENT},1) + $(info Including qcbor.mk) + include drivers/measured_boot/rse/qcbor.mk + $(info Including dice_prot_env.mk) + include drivers/measured_boot/rse/dice_prot_env.mk + + BL1_SOURCES += ${QCBOR_SOURCES} \ + ${DPE_SOURCES} \ + plat/arm/board/tc/tc_common_dpe.c \ + plat/arm/board/tc/tc_bl1_dpe.c \ + lib/psa/dice_protection_environment.c \ + drivers/arm/css/sds/sds.c \ + drivers/delay_timer/delay_timer.c \ + drivers/delay_timer/generic_delay_timer.c + + BL2_SOURCES += ${QCBOR_SOURCES} \ + ${DPE_SOURCES} \ + plat/arm/board/tc/tc_common_dpe.c \ + plat/arm/board/tc/tc_bl2_dpe.c \ + lib/psa/dice_protection_environment.c + + PLAT_INCLUDES += -I${QCBOR_INCLUDES} \ + -Iinclude/lib/dice + else + $(info Including rse_measured_boot.mk) + include drivers/measured_boot/rse/rse_measured_boot.mk + + BL1_SOURCES += ${MEASURED_BOOT_SOURCES} \ plat/arm/board/tc/tc_common_measured_boot.c \ plat/arm/board/tc/tc_bl1_measured_boot.c \ - lib/psa/measured_boot.c \ - ${RSS_COMMS_SOURCES} + lib/psa/measured_boot.c - BL2_SOURCES += ${MEASURED_BOOT_SOURCES} \ + BL2_SOURCES += ${MEASURED_BOOT_SOURCES} \ plat/arm/board/tc/tc_common_measured_boot.c \ plat/arm/board/tc/tc_bl2_measured_boot.c \ - lib/psa/measured_boot.c \ - ${RSS_COMMS_SOURCES} - -PLAT_INCLUDES += -Iinclude/lib/psa + lib/psa/measured_boot.c + endif +endif +ifeq (${TRNG_SUPPORT},1) + BL31_SOURCES += plat/arm/board/tc/tc_trng.c endif ifneq (${PLATFORM_TEST},) @@ -202,5 +284,4 @@ endif include plat/arm/common/arm_common.mk include plat/arm/css/common/css_common.mk -include plat/arm/soc/common/soc_css.mk include plat/arm/board/common/board_common.mk diff --git a/plat/arm/board/tc/platform_test.mk b/plat/arm/board/tc/platform_test.mk index 2fd5ea0e..2ce66485 100644 --- a/plat/arm/board/tc/platform_test.mk +++ b/plat/arm/board/tc/platform_test.mk @@ -1,47 +1,47 @@ -# Copyright (c) 2022-2023, Arm Limited. All rights reserved. +# Copyright (c) 2022-2024, Arm Limited. All rights reserved. # # SPDX-License-Identifier: BSD-3-Clause # $(eval $(call add_define,PLATFORM_TESTS)) -ifeq (${PLATFORM_TEST},rss-nv-counters) - include drivers/arm/rss/rss_comms.mk +ifeq (${PLATFORM_TEST},rse-nv-counters) + include drivers/arm/rse/rse_comms.mk # Test code. BL31_SOURCES += plat/arm/board/tc/nv_counter_test.c # Code under testing. - BL31_SOURCES += lib/psa/rss_platform.c \ - drivers/arm/rss/rss_comms.c \ - ${RSS_COMMS_SOURCES} + BL31_SOURCES += lib/psa/rse_platform.c \ + ${RSE_COMMS_SOURCES} PLAT_INCLUDES += -Iinclude/lib/psa $(eval $(call add_define,PLATFORM_TEST_NV_COUNTERS)) -else ifeq (${PLATFORM_TEST},rss-rotpk) - include drivers/arm/rss/rss_comms.mk +else ifeq (${PLATFORM_TEST},rse-rotpk) + include drivers/arm/rse/rse_comms.mk # Test code. BL31_SOURCES += plat/arm/board/tc/rotpk_test.c # Code under testing. - BL31_SOURCES += lib/psa/rss_platform.c \ - drivers/arm/rss/rss_comms.c \ - ${RSS_COMMS_SOURCES} + BL31_SOURCES += lib/psa/rse_platform.c \ + ${RSE_COMMS_SOURCES} PLAT_INCLUDES += -Iinclude/lib/psa $(eval $(call add_define,PLATFORM_TEST_ROTPK)) else ifeq (${PLATFORM_TEST},tfm-testsuite) + include drivers/arm/rse/rse_comms.mk + include drivers/measured_boot/rse/qcbor.mk # The variables need to be set to compile the platform test: ifeq (${TF_M_TESTS_PATH},) - # Example: ../rss/tf-m-tests + # Example: ../rse/tf-m-tests $(error Error: TF_M_TESTS_PATH not set) endif ifeq (${TF_M_EXTRAS_PATH},) - # Example: ../rss/tf-m-extras + # Example: ../rse/tf-m-extras $(error Error: TF_M_EXTRAS_PATH not set) endif ifeq (${MEASUREMENT_VALUE_SIZE},) @@ -56,43 +56,47 @@ else ifeq (${PLATFORM_TEST},tfm-testsuite) MBEDTLS_CONFIG_FILE = "<plat_tc_mbedtls_config.h>" - LIBMBEDTLS_SRCS += $(addprefix ${MBEDTLS_DIR}/library/, \ + LIBMBEDTLS_SRCS += $(addprefix ${MBEDTLS_DIR}/library/, \ entropy.c \ entropy_poll.c \ hmac_drbg.c \ psa_crypto.c \ psa_crypto_client.c \ - psa_crypto_driver_wrappers.c \ + psa_crypto_driver_wrappers_no_static.c \ psa_crypto_hash.c \ psa_crypto_rsa.c \ psa_crypto_ecp.c \ psa_crypto_slot_management.c \ + psa_util.c \ ) - BL31_SOURCES += ${RSS_COMMS_SOURCES} \ - plat/arm/common/arm_dyn_cfg.c \ - ${TC_BASE}/rss_ap_tests.c \ - ${TC_BASE}/rss_ap_testsuites.c \ - ${TC_BASE}/rss_ap_test_stubs.c \ - $(TF_M_TESTS_PATH)/test/framework/test_framework.c \ + BL31_SOURCES += ${RSE_COMMS_SOURCES} \ + plat/arm/common/arm_dyn_cfg.c \ + ${TC_BASE}/rse_ap_tests.c \ + ${TC_BASE}/rse_ap_testsuites.c \ + ${TC_BASE}/rse_ap_test_stubs.c \ + $(TF_M_TESTS_PATH)/tests_reg/test/framework/test_framework.c \ $(MEASURED_BOOT_TESTS_PATH)/measured_boot_common.c \ $(MEASURED_BOOT_TESTS_PATH)/measured_boot_tests_common.c \ $(DELEGATED_ATTEST_TESTS_PATH)/delegated_attest_test.c \ - drivers/auth/mbedtls/mbedtls_common.c \ - lib/psa/measured_boot.c \ - lib/psa/delegated_attestation.c + drivers/auth/mbedtls/mbedtls_common.c \ + lib/psa/measured_boot.c \ + lib/psa/delegated_attestation.c \ + ${QCBOR_SOURCES} PLAT_INCLUDES += -I$(TF_M_EXTRAS_PATH)/partitions/measured_boot/interface/include \ -I$(TF_M_EXTRAS_PATH)/partitions/delegated_attestation/interface/include \ - -I$(TF_M_TESTS_PATH)/test/framework \ - -I$(TF_M_TESTS_PATH)/log \ - -I$(TF_M_TESTS_PATH)/test/secure_fw/suites/extra \ - -I$(MEASURED_BOOT_TESTS_PATH)/non_secure \ - -I$(DELEGATED_ATTEST_TESTS_PATH) \ - -I$(DELEGATED_ATTEST_TESTS_PATH)/non_secure \ - -Iplat/arm/board/tc \ - -Iinclude/drivers/auth/mbedtls \ - -Iinclude/drivers/arm + -I$(TF_M_TESTS_PATH)/tests_reg/test/framework \ + -I$(TF_M_TESTS_PATH)/tests_reg/test/secure_fw/suites/extra \ + -I$(TF_M_TESTS_PATH)/lib/log \ + -I$(MEASURED_BOOT_TESTS_PATH)/non_secure \ + -I$(DELEGATED_ATTEST_TESTS_PATH) \ + -I$(DELEGATED_ATTEST_TESTS_PATH)/non_secure \ + -Iplat/arm/board/tc \ + -Iinclude/drivers/auth/mbedtls \ + -Iinclude/drivers/arm \ + -Iinclude/lib/psa \ + -I${QCBOR_INCLUDES} # Some of the PSA functions are declared in multiple header files, that # triggers this warning. diff --git a/plat/arm/board/tc/rotpk_test.c b/plat/arm/board/tc/rotpk_test.c index ed56c31b..2178f697 100644 --- a/plat/arm/board/tc/rotpk_test.c +++ b/plat/arm/board/tc/rotpk_test.c @@ -7,9 +7,9 @@ #include <stdint.h> #include <stdio.h> -#include <drivers/arm/rss_comms.h> +#include <drivers/arm/rse_comms.h> #include <plat/common/platform.h> -#include <rss_platform_api.h> +#include <rse_platform_api.h> #include <tc_plat.h> static void print_hex(const char *key_id_name, size_t key_size, const uint8_t *key_buf) @@ -28,19 +28,19 @@ int rotpk_test(void) size_t key_size; struct key_id_info key_ids[3] = { - {.key_id = RSS_BUILTIN_KEY_ID_HOST_S_ROTPK, .key_id_name = "Secure-ROTPK"}, - {.key_id = RSS_BUILTIN_KEY_ID_HOST_NS_ROTPK, .key_id_name = "NS-ROTPK"}, - {.key_id = RSS_BUILTIN_KEY_ID_HOST_CCA_ROTPK, .key_id_name = "CCA-ROTPK"} + {.key_id = RSE_BUILTIN_KEY_ID_HOST_S_ROTPK, .key_id_name = "Secure-ROTPK"}, + {.key_id = RSE_BUILTIN_KEY_ID_HOST_NS_ROTPK, .key_id_name = "NS-ROTPK"}, + {.key_id = RSE_BUILTIN_KEY_ID_HOST_CCA_ROTPK, .key_id_name = "CCA-ROTPK"} }; - status = rss_comms_init(PLAT_RSS_AP_SND_MHU_BASE, PLAT_RSS_AP_RCV_MHU_BASE); + status = rse_comms_init(PLAT_RSE_AP_SND_MHU_BASE, PLAT_RSE_AP_RCV_MHU_BASE); if (status != PSA_SUCCESS) { - printf("Failed to initialize RSS communication channel - psa_status = %d\n", status); + printf("Failed to initialize RSE communication channel - psa_status = %d\n", status); return -1; } for (int i = 0; i < ARRAY_SIZE(key_ids); i++) { - status = rss_platform_key_read(key_ids[i].key_id, key_buf, + status = rse_platform_key_read(key_ids[i].key_id, key_buf, sizeof(key_buf), &key_size); if (status != PSA_SUCCESS) { printf("Failed to retrieve %s - psa_status = %d\n", key_ids[i].key_id_name, status); diff --git a/plat/arm/board/tc/rss_ap_test_stubs.c b/plat/arm/board/tc/rse_ap_test_stubs.c similarity index 92% rename from plat/arm/board/tc/rss_ap_test_stubs.c rename to plat/arm/board/tc/rse_ap_test_stubs.c index aa97476f..cf791814 100644 --- a/plat/arm/board/tc/rss_ap_test_stubs.c +++ b/plat/arm/board/tc/rse_ap_test_stubs.c @@ -26,7 +26,7 @@ tfm_measured_boot_extend_measurement(uint8_t index, size_t measurement_value_size, bool lock_measurement) { - return rss_measured_boot_extend_measurement(index, + return rse_measured_boot_extend_measurement(index, signer_id, signer_id_size, version, @@ -56,7 +56,7 @@ tfm_measured_boot_read_measurement(uint8_t index, size_t *measurement_value_len, bool *is_locked) { - return rss_measured_boot_read_measurement(index, + return rse_measured_boot_read_measurement(index, signer_id, signer_id_size, signer_id_len, @@ -80,7 +80,7 @@ tfm_delegated_attest_get_token(const uint8_t *dak_pub_hash, size_t token_buf_size, size_t *token_size) { - return rss_delegated_attest_get_token(dak_pub_hash, + return rse_delegated_attest_get_token(dak_pub_hash, dak_pub_hash_size, token_buf, token_buf_size, @@ -95,7 +95,7 @@ tfm_delegated_attest_get_delegated_key(uint8_t ecc_curve, size_t *key_size, uint32_t hash_algo) { - return rss_delegated_attest_get_delegated_key(ecc_curve, + return rse_delegated_attest_get_delegated_key(ecc_curve, key_bits, key_buf, key_buf_size, diff --git a/plat/arm/board/tc/rss_ap_tests.c b/plat/arm/board/tc/rse_ap_tests.c similarity index 94% rename from plat/arm/board/tc/rss_ap_tests.c rename to plat/arm/board/tc/rse_ap_tests.c index ea90ac33..3ca628a6 100644 --- a/plat/arm/board/tc/rss_ap_tests.c +++ b/plat/arm/board/tc/rse_ap_tests.c @@ -10,9 +10,9 @@ #include <mbedtls_common.h> #include <plat/common/platform.h> #include <psa/crypto.h> -#include <rss_comms.h> +#include <rse_comms.h> -#include "rss_ap_testsuites.h" +#include "rse_ap_testsuites.h" static struct test_suite_t test_suites[] = { {.freg = register_testsuite_delegated_attest}, @@ -32,7 +32,7 @@ static int run_tests(void) size_t i; /* Initialize test environment. */ - rss_comms_init(PLAT_RSS_AP_SND_MHU_BASE, PLAT_RSS_AP_RCV_MHU_BASE); + rse_comms_init(PLAT_RSE_AP_SND_MHU_BASE, PLAT_RSE_AP_RCV_MHU_BASE); mbedtls_init(); status = psa_crypto_init(); if (status != PSA_SUCCESS) { diff --git a/plat/arm/board/tc/rss_ap_testsuites.c b/plat/arm/board/tc/rse_ap_testsuites.c similarity index 93% rename from plat/arm/board/tc/rss_ap_testsuites.c rename to plat/arm/board/tc/rse_ap_testsuites.c index aa47d4c2..5f4dc165 100644 --- a/plat/arm/board/tc/rss_ap_testsuites.c +++ b/plat/arm/board/tc/rse_ap_testsuites.c @@ -11,7 +11,7 @@ * necessary because both files define the function `extra_tests_init`, so a * linker error occurs when both are linked to BL31. This file defines a macro * that renames the colliding function names to something unique. - * `plat/arm/board/tc/rss_ap_tests.c` can call the test init functions with + * `plat/arm/board/tc/rse_ap_tests.c` can call the test init functions with * their new name. */ diff --git a/plat/arm/board/tc/rss_ap_testsuites.h b/plat/arm/board/tc/rse_ap_testsuites.h similarity index 76% rename from plat/arm/board/tc/rss_ap_testsuites.h rename to plat/arm/board/tc/rse_ap_testsuites.h index 58502ab6..9bb42f58 100644 --- a/plat/arm/board/tc/rss_ap_testsuites.h +++ b/plat/arm/board/tc/rse_ap_testsuites.h @@ -5,12 +5,12 @@ * SPDX-License-Identifier: BSD-3-Clause */ -#ifndef RSS_AP_TESTSUITES_H -#define RSS_AP_TESTSUITES_H +#ifndef RSE_AP_TESTSUITES_H +#define RSE_AP_TESTSUITES_H #include <test_framework.h> void register_testsuite_measured_boot(struct test_suite_t *p_test_suite); void register_testsuite_delegated_attest(struct test_suite_t *p_test_suite); -#endif /* RSS_AP_TESTSUITES_H */ +#endif /* RSE_AP_TESTSUITES_H */ diff --git a/plat/arm/board/tc/tc_bl1_dpe.c b/plat/arm/board/tc/tc_bl1_dpe.c new file mode 100644 index 00000000..de5702a7 --- /dev/null +++ b/plat/arm/board/tc/tc_bl1_dpe.c @@ -0,0 +1,158 @@ +/* + * Copyright (c) 2024, Arm Limited. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <stdint.h> + +#include <common/debug.h> +#include <drivers/arm/css/sds.h> +#include <drivers/arm/rse_comms.h> +#include <drivers/delay_timer.h> +#include <drivers/generic_delay_timer.h> +#include <drivers/measured_boot/metadata.h> +#include <drivers/measured_boot/rse/dice_prot_env.h> +#include <plat/arm/common/plat_arm.h> +#include <plat/common/platform.h> +#include <platform_def.h> +#include <tools_share/zero_oid.h> + +#include "tc_dpe.h" + +struct dpe_metadata tc_dpe_metadata[] = { + { + .id = FW_CONFIG_ID, + .cert_id = DPE_AP_FW_CERT_ID, + .signer_id_size = SIGNER_ID_MIN_SIZE, + .sw_type = MBOOT_FW_CONFIG_STRING, + .allow_new_context_to_derive = false, + .retain_parent_context = true, + .create_certificate = false, + .target_locality = LOCALITY_NONE, /* won't derive don't care */ + .pk_oid = ZERO_OID }, + { + .id = TB_FW_CONFIG_ID, + .cert_id = DPE_AP_FW_CERT_ID, + .signer_id_size = SIGNER_ID_MIN_SIZE, + .sw_type = MBOOT_TB_FW_CONFIG_STRING, + .allow_new_context_to_derive = false, + .retain_parent_context = true, + .create_certificate = false, + .target_locality = LOCALITY_NONE, /* won't derive don't care */ + .pk_oid = ZERO_OID }, + { + .id = BL2_IMAGE_ID, + .cert_id = DPE_AP_FW_CERT_ID, + .signer_id_size = SIGNER_ID_MIN_SIZE, + .sw_type = MBOOT_BL2_IMAGE_STRING, + .allow_new_context_to_derive = true, + .retain_parent_context = true, /* To handle restart */ + .target_locality = LOCALITY_AP_S, + .create_certificate = false, + .pk_oid = ZERO_OID }, + { + .id = DPE_INVALID_ID } +}; + +/* Effective timeout of 10000 ms */ +#define RSE_DPE_BOOT_10US_RETRIES 1000000 +#define TC2_SDS_DPE_CTX_HANDLE_STRUCT_ID 0x0000000A + +/* Context handle is meant to be used by BL2. Sharing it via TB_FW_CONFIG */ +static int new_ctx_handle; +/* Save a valid parent context handle to be able to send commands to DPE service + * in case of an AP cold restart. + */ +static int new_parent_ctx_handle; + +void plat_dpe_share_context_handle(int *ctx_handle, int *parent_ctx_handle) +{ + new_ctx_handle = *ctx_handle; + new_parent_ctx_handle = *parent_ctx_handle; +} + +void plat_dpe_get_context_handle(int *ctx_handle) +{ + int retry = RSE_DPE_BOOT_10US_RETRIES; + int ret; + + /* Initialize System level generic or SP804 timer */ + generic_delay_timer_init(); + + /* Check the initialization of the Shared Data Storage area between RSE + * and AP. Since AP_BL1 is executed first then a bit later the RSE + * runtime, which initialize this area, therefore AP needs to check it + * in a loop until it gets written by RSE Secure Runtime. + */ + VERBOSE("Waiting for DPE service initialization in RSE Secure Runtime\n"); + while (retry > 0) { + ret = sds_init(SDS_RSE_AP_REGION_ID); + if (ret != SDS_OK) { + udelay(10); + retry--; + } else { + break; + } + } + + if (retry == 0) { + ERROR("DPE init timeout\n"); + plat_panic_handler(); + } else { + VERBOSE("DPE init succeeded in %dms.\n", + (RSE_DPE_BOOT_10US_RETRIES - retry) / 100); + } + + /* TODO: call this in a loop to avoid reading unfinished data */ + ret = sds_struct_read(SDS_RSE_AP_REGION_ID, + TC2_SDS_DPE_CTX_HANDLE_STRUCT_ID, + 0, + ctx_handle, + sizeof(*ctx_handle), + SDS_ACCESS_MODE_NON_CACHED); + if (ret != SDS_OK) { + ERROR("Unable to get DPE context handle from SDS area\n"); + plat_panic_handler(); + } + + VERBOSE("Received DPE context handle: 0x%x\n", *ctx_handle); +} + +void bl1_plat_mboot_init(void) +{ + /* Initialize the communication channel between AP and RSE */ + (void)rse_comms_init(PLAT_RSE_AP_SND_MHU_BASE, + PLAT_RSE_AP_RCV_MHU_BASE); + + dpe_init(tc_dpe_metadata); +} + +void bl1_plat_mboot_finish(void) +{ + int rc; + + VERBOSE("Share DPE context handle with BL2: 0x%x\n", new_ctx_handle); + rc = arm_set_tb_fw_info(&new_ctx_handle); + if (rc != 0) { + ERROR("Unable to set DPE context handle in TB_FW_CONFIG\n"); + /* + * It is a fatal error because on TC platform, BL2 software + * assumes that a valid DPE context_handle is passed through + * the DTB object by BL1. + */ + plat_panic_handler(); + } + + VERBOSE("Save parent context handle: 0x%x\n", new_parent_ctx_handle); + rc = sds_struct_write(SDS_RSE_AP_REGION_ID, + TC2_SDS_DPE_CTX_HANDLE_STRUCT_ID, + 0, + &new_parent_ctx_handle, + sizeof(new_parent_ctx_handle), + SDS_ACCESS_MODE_NON_CACHED); + if (rc != SDS_OK) { + ERROR("Unable to save DPE parent context handle to SDS area\n"); + plat_panic_handler(); + } +} diff --git a/plat/arm/board/tc/tc_bl1_measured_boot.c b/plat/arm/board/tc/tc_bl1_measured_boot.c index 6821a6ab..28a1e316 100644 --- a/plat/arm/board/tc/tc_bl1_measured_boot.c +++ b/plat/arm/board/tc/tc_bl1_measured_boot.c @@ -1,14 +1,14 @@ /* - * Copyright (c) 2022-2023, Arm Limited. All rights reserved. + * Copyright (c) 2022-2024, Arm Limited. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ #include <stdint.h> -#include <drivers/arm/rss_comms.h> -#include <drivers/measured_boot/rss/rss_measured_boot.h> -#include <lib/psa/measured_boot.h> +#include <drivers/arm/rse_comms.h> +#include <drivers/measured_boot/metadata.h> +#include <drivers/measured_boot/rse/rse_measured_boot.h> #include <tools_share/zero_oid.h> #include <plat/arm/common/plat_arm.h> @@ -17,40 +17,40 @@ /* Table with platform specific image IDs and metadata. Intentionally not a * const struct, some members might set by bootloaders during trusted boot. */ -struct rss_mboot_metadata tc_rss_mboot_metadata[] = { +struct rse_mboot_metadata tc_rse_mboot_metadata[] = { { .id = FW_CONFIG_ID, .slot = U(6), .signer_id_size = SIGNER_ID_MIN_SIZE, - .sw_type = RSS_MBOOT_FW_CONFIG_STRING, + .sw_type = MBOOT_FW_CONFIG_STRING, .pk_oid = ZERO_OID, .lock_measurement = true }, { .id = TB_FW_CONFIG_ID, .slot = U(7), .signer_id_size = SIGNER_ID_MIN_SIZE, - .sw_type = RSS_MBOOT_TB_FW_CONFIG_STRING, + .sw_type = MBOOT_TB_FW_CONFIG_STRING, .pk_oid = ZERO_OID, .lock_measurement = true }, { .id = BL2_IMAGE_ID, .slot = U(8), .signer_id_size = SIGNER_ID_MIN_SIZE, - .sw_type = RSS_MBOOT_BL2_STRING, + .sw_type = MBOOT_BL2_IMAGE_STRING, .pk_oid = ZERO_OID, .lock_measurement = true }, { - .id = RSS_MBOOT_INVALID_ID } + .id = RSE_MBOOT_INVALID_ID } }; void bl1_plat_mboot_init(void) { - /* Initialize the communication channel between AP and RSS */ - (void)rss_comms_init(PLAT_RSS_AP_SND_MHU_BASE, - PLAT_RSS_AP_RCV_MHU_BASE); + /* Initialize the communication channel between AP and RSE */ + (void)rse_comms_init(PLAT_RSE_AP_SND_MHU_BASE, + PLAT_RSE_AP_RCV_MHU_BASE); - rss_measured_boot_init(tc_rss_mboot_metadata); + rse_measured_boot_init(tc_rse_mboot_metadata); } void bl1_plat_mboot_finish(void) diff --git a/plat/arm/board/tc/tc_bl1_setup.c b/plat/arm/board/tc/tc_bl1_setup.c new file mode 100644 index 00000000..aedc94f9 --- /dev/null +++ b/plat/arm/board/tc/tc_bl1_setup.c @@ -0,0 +1,19 @@ +/* + * Copyright (c) 2024, Arm Limited. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <plat/arm/common/plat_arm.h> + +/******************************************************************************* + * Perform any BL1 specific platform actions. + ******************************************************************************/ + +void soc_css_init_nic400(void) +{ +} + +void soc_css_init_pcie(void) +{ +} diff --git a/plat/arm/board/tc/tc_bl2_dpe.c b/plat/arm/board/tc/tc_bl2_dpe.c new file mode 100644 index 00000000..c56612b4 --- /dev/null +++ b/plat/arm/board/tc/tc_bl2_dpe.c @@ -0,0 +1,255 @@ +/* + * Copyright (c) 2024, Arm Limited. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <stdint.h> + +#include <common/debug.h> +#include <drivers/arm/rse_comms.h> +#include <drivers/measured_boot/metadata.h> +#include <drivers/measured_boot/rse/dice_prot_env.h> +#include <plat/arm/common/plat_arm.h> +#include <plat/common/platform.h> +#include <platform_def.h> +#include <tools_share/tbbr_oid.h> + +#include "tc_dpe.h" + +/* + * The content and the values of this array depends on: + * - build config: Which components are loaded: SPMD, TOS, SPx, etc ? + * - boot order: the last element in a layer should be treated differently. + */ + +/* + * TODO: + * - The content of the array must be tailored according to the build + * config (TOS, SPMD, etc). All loaded components (executables and + * config blobs) must be present in this array. + * - Current content is according to the Trusty build config. + */ +struct dpe_metadata tc_dpe_metadata[] = { + { + .id = BL31_IMAGE_ID, + .cert_id = DPE_CERT_ID_SAME_AS_PARENT, /* AP_BL2: DPE_AP_FW_CERT_ID */ + .signer_id_size = SIGNER_ID_MIN_SIZE, + .sw_type = MBOOT_BL31_IMAGE_STRING, + .allow_new_context_to_derive = false, + .retain_parent_context = true, + .create_certificate = false, + .target_locality = LOCALITY_NONE, /* won't derive don't care */ + .pk_oid = BL31_IMAGE_KEY_OID }, + { + .id = BL32_IMAGE_ID, + .cert_id = DPE_CERT_ID_SAME_AS_PARENT, /* AP_BL2: DPE_AP_FW_CERT_ID */ + .signer_id_size = SIGNER_ID_MIN_SIZE, + .sw_type = MBOOT_BL32_IMAGE_STRING, + .allow_new_context_to_derive = false, + .retain_parent_context = true, + .create_certificate = false, + .target_locality = LOCALITY_NONE, /* won't derive don't care */ + .pk_oid = BL32_IMAGE_KEY_OID }, + { + .id = BL33_IMAGE_ID, + .cert_id = DPE_HYPERVISOR_CERT_ID, + .signer_id_size = SIGNER_ID_MIN_SIZE, + .sw_type = MBOOT_BL33_IMAGE_STRING, + .allow_new_context_to_derive = true, + .retain_parent_context = true, + .create_certificate = false, + .target_locality = LOCALITY_AP_NS, + .pk_oid = BL33_IMAGE_KEY_OID }, + + { + .id = HW_CONFIG_ID, + .cert_id = DPE_CERT_ID_SAME_AS_PARENT, /* AP_BL2: DPE_AP_FW_CERT_ID */ + .signer_id_size = SIGNER_ID_MIN_SIZE, + .sw_type = MBOOT_HW_CONFIG_STRING, + .allow_new_context_to_derive = false, + .retain_parent_context = true, + .create_certificate = false, + .target_locality = LOCALITY_NONE, /* won't derive don't care */ + .pk_oid = HW_CONFIG_KEY_OID }, + { + .id = NT_FW_CONFIG_ID, + .cert_id = DPE_HYPERVISOR_CERT_ID, + .signer_id_size = SIGNER_ID_MIN_SIZE, + .sw_type = MBOOT_NT_FW_CONFIG_STRING, + .allow_new_context_to_derive = false, + .retain_parent_context = true, + .create_certificate = false, + .target_locality = LOCALITY_NONE, /* won't derive don't care */ + .pk_oid = NT_FW_CONFIG_KEY_OID }, + { + .id = SCP_BL2_IMAGE_ID, + .cert_id = DPE_CERT_ID_SAME_AS_PARENT, /* AP_BL2: DPE_AP_FW_CERT_ID */ + .signer_id_size = SIGNER_ID_MIN_SIZE, + .sw_type = MBOOT_SCP_BL2_IMAGE_STRING, + .allow_new_context_to_derive = false, + .retain_parent_context = true, + .create_certificate = false, + .target_locality = LOCALITY_NONE, /* won't derive don't care */ + .pk_oid = SCP_BL2_IMAGE_KEY_OID }, + { + .id = SOC_FW_CONFIG_ID, + .cert_id = DPE_CERT_ID_SAME_AS_PARENT, /* AP_BL2: DPE_AP_FW_CERT_ID */ + .signer_id_size = SIGNER_ID_MIN_SIZE, + .sw_type = MBOOT_SOC_FW_CONFIG_STRING, + .allow_new_context_to_derive = false, + .retain_parent_context = true, + .create_certificate = false, + .target_locality = LOCALITY_NONE, /* won't derive don't care */ + .pk_oid = SOC_FW_CONFIG_KEY_OID }, + { + .id = TOS_FW_CONFIG_ID, + .cert_id = DPE_CERT_ID_SAME_AS_PARENT, /* AP_BL2: DPE_AP_FW_CERT_ID */ + .signer_id_size = SIGNER_ID_MIN_SIZE, + .sw_type = MBOOT_TOS_FW_CONFIG_STRING, + .allow_new_context_to_derive = false, + .retain_parent_context = true, + .create_certificate = false, + .target_locality = LOCALITY_NONE, /* won't derive don't care */ + .pk_oid = TOS_FW_CONFIG_KEY_OID }, +#if defined(SPD_spmd) + { + .id = SP_PKG1_ID, + .cert_id = DPE_CERT_ID_SAME_AS_PARENT, /* AP_BL2: DPE_AP_FW_CERT_ID */ + .signer_id_size = SIGNER_ID_MIN_SIZE, + .sw_type = MBOOT_SP1_STRING, + .allow_new_context_to_derive = false, + .retain_parent_context = true, + .create_certificate = true, /* With Trusty only one SP is loaded */ + .target_locality = LOCALITY_NONE, /* won't derive don't care */ + .pk_oid = NULL }, + { + .id = SP_PKG2_ID, + .cert_id = DPE_CERT_ID_SAME_AS_PARENT, /* AP_BL2: DPE_AP_FW_CERT_ID */ + .signer_id_size = SIGNER_ID_MIN_SIZE, + .sw_type = MBOOT_SP2_STRING, + .allow_new_context_to_derive = false, + .retain_parent_context = true, + .create_certificate = false, + .target_locality = LOCALITY_NONE, /* won't derive don't care */ + .pk_oid = NULL }, + { + .id = SP_PKG3_ID, + .cert_id = DPE_CERT_ID_SAME_AS_PARENT, /* AP_BL2: DPE_AP_FW_CERT_ID */ + .signer_id_size = SIGNER_ID_MIN_SIZE, + .sw_type = MBOOT_SP3_STRING, + .allow_new_context_to_derive = false, + .retain_parent_context = true, + .create_certificate = false, + .target_locality = LOCALITY_NONE, /* won't derive don't care */ + .pk_oid = NULL }, + { + .id = SP_PKG4_ID, + .cert_id = DPE_CERT_ID_SAME_AS_PARENT, /* AP_BL2: DPE_AP_FW_CERT_ID */ + .signer_id_size = SIGNER_ID_MIN_SIZE, + .sw_type = MBOOT_SP4_STRING, + .allow_new_context_to_derive = false, + .retain_parent_context = true, + .create_certificate = false, + .target_locality = LOCALITY_NONE, /* won't derive don't care */ + .pk_oid = NULL }, + { + .id = SP_PKG5_ID, + .cert_id = DPE_CERT_ID_SAME_AS_PARENT, /* AP_BL2: DPE_AP_FW_CERT_ID */ + .signer_id_size = SIGNER_ID_MIN_SIZE, + .sw_type = MBOOT_SP5_STRING, + .allow_new_context_to_derive = false, + .retain_parent_context = true, + .create_certificate = false, + .target_locality = LOCALITY_NONE, /* won't derive don't care */ + .pk_oid = NULL }, + { + .id = SP_PKG6_ID, + .cert_id = DPE_CERT_ID_SAME_AS_PARENT, /* AP_BL2: DPE_AP_FW_CERT_ID */ + .signer_id_size = SIGNER_ID_MIN_SIZE, + .sw_type = MBOOT_SP6_STRING, + .allow_new_context_to_derive = false, + .retain_parent_context = true, + .create_certificate = false, + .target_locality = LOCALITY_NONE, /* won't derive don't care */ + .pk_oid = NULL }, + { + .id = SP_PKG7_ID, + .cert_id = DPE_CERT_ID_SAME_AS_PARENT, /* AP_BL2: DPE_AP_FW_CERT_ID */ + .signer_id_size = SIGNER_ID_MIN_SIZE, + .sw_type = MBOOT_SP7_STRING, + .allow_new_context_to_derive = false, + .retain_parent_context = true, + .create_certificate = false, + .target_locality = LOCALITY_NONE, /* won't derive don't care */ + .pk_oid = NULL }, + { + .id = SP_PKG8_ID, + .cert_id = DPE_CERT_ID_SAME_AS_PARENT, /* AP_BL2: DPE_AP_FW_CERT_ID */ + .signer_id_size = SIGNER_ID_MIN_SIZE, + .sw_type = MBOOT_SP8_STRING, + .allow_new_context_to_derive = false, + .retain_parent_context = true, + .create_certificate = false, + .target_locality = LOCALITY_NONE, /* won't derive don't care */ + .pk_oid = NULL }, + +#endif + { + .id = DPE_INVALID_ID } +}; + +/* Context handle is meant to be used by BL33. Sharing it via NT_FW_CONFIG */ +static int new_ctx_handle; + +void plat_dpe_share_context_handle(int *ctx_handle, int *parent_ctx_handle) +{ + new_ctx_handle = *ctx_handle; + + /* Irrelevant in BL2 because cold restart resumes CPU in BL1 */ + (void)parent_ctx_handle; +} + +void plat_dpe_get_context_handle(int *ctx_handle) +{ + int rc; + + rc = arm_get_tb_fw_info(ctx_handle); + if (rc != 0) { + ERROR("Unable to get DPE context handle from TB_FW_CONFIG\n"); + /* + * It is a fatal error because on FVP platform, BL2 software + * assumes that a valid DPE context_handle is passed through + * the DTB object by BL1. + */ + plat_panic_handler(); + } + + VERBOSE("Received DPE context handle: 0x%x\n", *ctx_handle); +} + +void bl2_plat_mboot_init(void) +{ + /* Initialize the communication channel between AP and RSE */ + (void)rse_comms_init(PLAT_RSE_AP_SND_MHU_BASE, + PLAT_RSE_AP_RCV_MHU_BASE); + + dpe_init(tc_dpe_metadata); +} + +void bl2_plat_mboot_finish(void) +{ + int rc; + + VERBOSE("Share DPE context handle with BL33: 0x%x\n", new_ctx_handle); + rc = arm_set_nt_fw_info(&new_ctx_handle); + if (rc != 0) { + ERROR("Unable to set DPE context handle in NT_FW_CONFIG\n"); + /* + * It is a fatal error because on TC platform, BL33 software + * assumes that a valid DPE context_handle is passed through + * the DTB object by BL2. + */ + plat_panic_handler(); + } +} diff --git a/plat/arm/board/tc/tc_bl2_measured_boot.c b/plat/arm/board/tc/tc_bl2_measured_boot.c index 4b791708..3957c906 100644 --- a/plat/arm/board/tc/tc_bl2_measured_boot.c +++ b/plat/arm/board/tc/tc_bl2_measured_boot.c @@ -1,14 +1,14 @@ /* - * Copyright (c) 2022-2023, Arm Limited. All rights reserved. + * Copyright (c) 2022-2024, Arm Limited. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ #include <stdint.h> -#include <drivers/arm/rss_comms.h> -#include <drivers/measured_boot/rss/rss_measured_boot.h> -#include <lib/psa/measured_boot.h> +#include <drivers/arm/rse_comms.h> +#include <drivers/measured_boot/metadata.h> +#include <drivers/measured_boot/rse/rse_measured_boot.h> #include <tools_share/tbbr_oid.h> #include <plat/common/common_def.h> @@ -17,39 +17,46 @@ /* TC specific table with image IDs and metadata. Intentionally not a * const struct, some members might set by bootloaders during trusted boot. */ -struct rss_mboot_metadata tc_rss_mboot_metadata[] = { +struct rse_mboot_metadata tc_rse_mboot_metadata[] = { { .id = BL31_IMAGE_ID, .slot = U(9), .signer_id_size = SIGNER_ID_MIN_SIZE, - .sw_type = RSS_MBOOT_BL31_STRING, + .sw_type = MBOOT_BL31_IMAGE_STRING, .pk_oid = BL31_IMAGE_KEY_OID, .lock_measurement = true }, { .id = HW_CONFIG_ID, .slot = U(10), .signer_id_size = SIGNER_ID_MIN_SIZE, - .sw_type = RSS_MBOOT_HW_CONFIG_STRING, + .sw_type = MBOOT_HW_CONFIG_STRING, .pk_oid = HW_CONFIG_KEY_OID, .lock_measurement = true }, { .id = SOC_FW_CONFIG_ID, .slot = U(11), .signer_id_size = SIGNER_ID_MIN_SIZE, - .sw_type = RSS_MBOOT_SOC_FW_CONFIG_STRING, + .sw_type = MBOOT_SOC_FW_CONFIG_STRING, .pk_oid = SOC_FW_CONFIG_KEY_OID, .lock_measurement = true }, { - .id = RSS_MBOOT_INVALID_ID } + .id = SCP_BL2_IMAGE_ID, + .slot = U(12), + .signer_id_size = SIGNER_ID_MIN_SIZE, + .sw_type = MBOOT_SCP_BL2_IMAGE_STRING, + .pk_oid = SCP_BL2_IMAGE_KEY_OID, + .lock_measurement = true }, + { + .id = RSE_MBOOT_INVALID_ID } }; void bl2_plat_mboot_init(void) { - /* Initialize the communication channel between AP and RSS */ - (void)rss_comms_init(PLAT_RSS_AP_SND_MHU_BASE, - PLAT_RSS_AP_RCV_MHU_BASE); + /* Initialize the communication channel between AP and RSE */ + (void)rse_comms_init(PLAT_RSE_AP_SND_MHU_BASE, + PLAT_RSE_AP_RCV_MHU_BASE); - rss_measured_boot_init(tc_rss_mboot_metadata); + rse_measured_boot_init(tc_rse_mboot_metadata); } void bl2_plat_mboot_finish(void) diff --git a/plat/arm/board/tc/tc_bl31_setup.c b/plat/arm/board/tc/tc_bl31_setup.c index ff7809d2..801872aa 100644 --- a/plat/arm/board/tc/tc_bl31_setup.c +++ b/plat/arm/board/tc/tc_bl31_setup.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020-2023, Arm Limited and Contributors. All rights reserved. + * Copyright (c) 2020-2024, Arm Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -9,6 +9,7 @@ #include <libfdt.h> #include <tc_plat.h> +#include <arch_helpers.h> #include <common/bl_common.h> #include <common/debug.h> #include <drivers/arm/css/css_mhu_doorbell.h> @@ -19,25 +20,106 @@ #include <plat/arm/common/plat_arm.h> #include <plat/common/platform.h> -static scmi_channel_plat_info_t tc_scmi_plat_info[] = { - { - .scmi_mbx_mem = CSS_SCMI_PAYLOAD_BASE, - .db_reg_addr = PLAT_CSS_MHU_BASE + SENDER_REG_SET(0), - .db_preserve_mask = 0xfffffffe, - .db_modify_mask = 0x1, - .ring_doorbell = &mhuv2_ring_doorbell, +#ifdef PLATFORM_TEST_TFM_TESTSUITE +#include <psa/crypto_platform.h> +#include <psa/crypto_types.h> +#include <psa/crypto_values.h> +#endif /* PLATFORM_TEST_TFM_TESTSUITE */ + +#ifdef PLATFORM_TEST_TFM_TESTSUITE +/* + * We pretend using an external RNG (through MBEDTLS_PSA_CRYPTO_EXTERNAL_RNG + * mbedTLS config option) so we need to provide an implementation of + * mbedtls_psa_external_get_random(). Provide a fake one, since we do not + * actually use any of external RNG and this function is only needed during + * the execution of TF-M testsuite during exporting the public part of the + * delegated attestation key. + */ +psa_status_t mbedtls_psa_external_get_random( + mbedtls_psa_external_random_context_t *context, + uint8_t *output, size_t output_size, + size_t *output_length) +{ + for (size_t i = 0U; i < output_size; i++) { + output[i] = (uint8_t)(read_cntpct_el0() & 0xFFU); } + + *output_length = output_size; + + return PSA_SUCCESS; +} +#endif /* PLATFORM_TEST_TFM_TESTSUITE */ + +#if TARGET_PLATFORM <= 2 +static scmi_channel_plat_info_t tc_scmi_plat_info = { + .scmi_mbx_mem = CSS_SCMI_PAYLOAD_BASE, + .db_reg_addr = PLAT_CSS_MHU_BASE + SENDER_REG_SET(0), + .db_preserve_mask = 0xfffffffe, + .db_modify_mask = 0x1, + .ring_doorbell = &mhuv2_ring_doorbell, +}; +#elif TARGET_PLATFORM >= 3 +static scmi_channel_plat_info_t tc_scmi_plat_info = { + .scmi_mbx_mem = CSS_SCMI_PAYLOAD_BASE, + .db_reg_addr = PLAT_CSS_MHU_BASE + MHU_V3_SENDER_REG_SET(0), + .db_preserve_mask = 0xfffffffe, + .db_modify_mask = 0x1, + .ring_doorbell = &mhu_ring_doorbell, }; +#endif + +#if TARGET_PLATFORM == 3 +static void enable_ns_mcn_pmu(void) +{ + /* + * Enable non-secure access to MCN PMU registers + */ + for (int i = 0; i < MCN_INSTANCES; i++) { + uintptr_t mcn_scr = MCN_MICROARCH_BASE_ADDR + MCN_SCR_OFFSET + + (i * MCN_ADDRESS_SPACE_SIZE); + mmio_setbits_32(mcn_scr, 1 << MCN_SCR_PMU_BIT); + } +} + +static void set_mcn_slc_alloc_mode(void) +{ + /* + * SLC WRALLOCMODE and RDALLOCMODE are configured by default to + * 0b01 (always alloc), configure both to 0b10 (use bus signal + * attribute from interface). + */ + for (int i = 0; i < MCN_INSTANCES; i++) { + uintptr_t slccfg_ctl_ns = MCN_MPAM_NS_BASE_ADDR + + (i * MCN_ADDRESS_SPACE_SIZE) + MPAM_SLCCFG_CTL_OFFSET; + uintptr_t slccfg_ctl_s = MCN_MPAM_S_BASE_ADDR + + (i * MCN_ADDRESS_SPACE_SIZE) + MPAM_SLCCFG_CTL_OFFSET; + + mmio_clrsetbits_32(slccfg_ctl_ns, + (SLC_RDALLOCMODE_MASK | SLC_WRALLOCMODE_MASK), + (SLC_ALLOC_BUS_SIGNAL_ATTR << SLC_RDALLOCMODE_SHIFT) | + (SLC_ALLOC_BUS_SIGNAL_ATTR << SLC_WRALLOCMODE_SHIFT)); + mmio_clrsetbits_32(slccfg_ctl_s, + (SLC_RDALLOCMODE_MASK | SLC_WRALLOCMODE_MASK), + (SLC_ALLOC_BUS_SIGNAL_ATTR << SLC_RDALLOCMODE_SHIFT) | + (SLC_ALLOC_BUS_SIGNAL_ATTR << SLC_WRALLOCMODE_SHIFT)); + } +} +#endif void bl31_platform_setup(void) { tc_bl31_common_platform_setup(); +#if TARGET_PLATFORM == 3 + enable_ns_mcn_pmu(); + set_mcn_slc_alloc_mode(); + plat_arm_ni_setup(NCI_BASE_ADDR); +#endif } -scmi_channel_plat_info_t *plat_css_get_scmi_info(unsigned int channel_id) +scmi_channel_plat_info_t *plat_css_get_scmi_info(unsigned int channel_id __unused) { - return &tc_scmi_plat_info[channel_id]; + return &tc_scmi_plat_info; } @@ -104,10 +186,10 @@ void __init bl31_plat_arch_setup(void) #if defined(SPD_spmd) && (SPMC_AT_EL3 == 0) void tc_bl31_plat_runtime_setup(void) { - arm_bl31_plat_runtime_setup(); - /* Start secure watchdog timer. */ plat_arm_secure_wdt_start(); + + arm_bl31_plat_runtime_setup(); } void bl31_plat_runtime_setup(void) diff --git a/plat/arm/board/tc/tc_common_dpe.c b/plat/arm/board/tc/tc_common_dpe.c new file mode 100644 index 00000000..72ac6730 --- /dev/null +++ b/plat/arm/board/tc/tc_common_dpe.c @@ -0,0 +1,36 @@ + +/* + * Copyright (c) 2024, Arm Limited. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <assert.h> +#include <stdint.h> + +#include <common/desc_image_load.h> +#include <drivers/measured_boot/rse/dice_prot_env.h> + +extern struct dpe_metadata tc_dpe_metadata[]; + +int plat_mboot_measure_image(unsigned int image_id, image_info_t *image_data) +{ + int err; + + /* Calculate image hash and record it in the DPE service in RSE. */ + err = dpe_measure_and_record(tc_dpe_metadata, + image_data->image_base, + image_data->image_size, + image_id); + if (err != 0) { + ERROR("%s%s image id %u (%i)\n", + "Failed to ", "record in DPE", image_id, err); + } + + return err; +} + +int plat_mboot_measure_key(void *pk_oid, void *pk_ptr, unsigned int pk_len) +{ + return dpe_set_signer_id(tc_dpe_metadata, pk_oid, pk_ptr, pk_len); +} diff --git a/plat/arm/board/tc/tc_common_measured_boot.c b/plat/arm/board/tc/tc_common_measured_boot.c index 925a4114..6b8d41ac 100644 --- a/plat/arm/board/tc/tc_common_measured_boot.c +++ b/plat/arm/board/tc/tc_common_measured_boot.c @@ -8,22 +8,22 @@ #include <stdint.h> #include <common/desc_image_load.h> -#include <drivers/measured_boot/rss/rss_measured_boot.h> +#include <drivers/measured_boot/rse/rse_measured_boot.h> -extern struct rss_mboot_metadata tc_rss_mboot_metadata[]; +extern struct rse_mboot_metadata tc_rse_mboot_metadata[]; int plat_mboot_measure_image(unsigned int image_id, image_info_t *image_data) { int err; - /* Calculate image hash and record data in RSS */ - err = rss_mboot_measure_and_record(tc_rss_mboot_metadata, + /* Calculate image hash and record data in RSE */ + err = rse_mboot_measure_and_record(tc_rse_mboot_metadata, image_data->image_base, image_data->image_size, image_id); if (err != 0) { ERROR("%s%s image id %u (%i)\n", - "Failed to ", "record in RSS", image_id, err); + "Failed to ", "record in RSE", image_id, err); } return err; @@ -31,6 +31,6 @@ int plat_mboot_measure_image(unsigned int image_id, image_info_t *image_data) int plat_mboot_measure_key(void *pk_oid, void *pk_ptr, unsigned int pk_len) { - return rss_mboot_set_signer_id(tc_rss_mboot_metadata, pk_oid, pk_ptr, + return rse_mboot_set_signer_id(tc_rse_mboot_metadata, pk_oid, pk_ptr, pk_len); } diff --git a/plat/arm/board/tc/tc_dpe.h b/plat/arm/board/tc/tc_dpe.h new file mode 100644 index 00000000..3e1af5a8 --- /dev/null +++ b/plat/arm/board/tc/tc_dpe.h @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2024, Arm Limited. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef TC_DPE_H +#define TC_DPE_H + +/* + * The certificate structure on the TC platform: + * - The arrows indicate the parent/child relationships (who loads who). + * - The boxes indicate the certificates. + * + * AP FW Cert. + * +--------------------------------+ + * | | + * Plat Cert. | +->SPx | Hyper Cert. + * +--------------------------+ | +->SP1 | +--------------------+ + * RoT Cert. | | | +->TOS_FW_CONF | | | + * +------------+ | +->SCP_BL1 +-----+-----+-->FW_CONF +->AP_BL32 | | +->PVMFW | + * | | | | | | | | | | | | + * | RSE_BL1_2--+-----+-->RSE_BL2------->AP_BL1--+-----+------------->AP_BL2------------+-----+-->AP_BL33 | + * | | | | | | | | | | | | + * +------------+ | +->RSE_S +-----+-----+-->TB_FW_CONF +->AP_BL31 | | +->HYPERVISOR | + * | +->RSE_NS | | +->SCP_BL2 | | | + * | | | +->HW_CONF | | | + * +--------------------------+ | +---------------+-----+-->NT_FW_CONF | + * | | | | + * +--------------------------------+ +--------------------+ + */ + +#define DPE_AP_FW_CERT_ID 0x300 /* Includes: FW_CONF - SP1 */ +#define DPE_HYPERVISOR_CERT_ID 0x400 /* Includes: AP_BL33 - PVMFW */ + +/* Common definition */ +#define DPE_CERT_ID_SAME_AS_PARENT 0xFFFFFFFF + +/* + * Target Locality: + * The goal is to specify that a certain component is expected to run and + * thereby send DPE commands from a given security domain. RSE is capable of + * of distinguishing the client's locality based on the MHU channel used for + * communication. + * Defines here must match with RSE side: + */ +#define LOCALITY_NONE -1 +/* #define LOCALITY_RSE_S 0 */ /* Not applicable on AP side */ +/* #define LOCALITY_RSE_NS 1 */ /* Not applicable on AP side */ +#define LOCALITY_AP_S 2 +#define LOCALITY_AP_NS 3 + +#endif /* TC_DPE_H */ diff --git a/plat/arm/board/tc/tc_plat.c b/plat/arm/board/tc/tc_plat.c index 766bfb57..fed14f7d 100644 --- a/plat/arm/board/tc/tc_plat.c +++ b/plat/arm/board/tc/tc_plat.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020-2023, Arm Limited and Contributors. All rights reserved. + * Copyright (c) 2020-2024, Arm Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -12,6 +12,8 @@ #include <common/bl_common.h> #include <common/debug.h> #include <drivers/arm/ccn.h> +#include <drivers/arm/css/sds.h> +#include <lib/utils_def.h> #include <plat/arm/common/plat_arm.h> #include <plat/common/platform.h> #include <drivers/arm/sbsa.h> @@ -28,6 +30,7 @@ #if IMAGE_BL1 const mmap_region_t plat_arm_mmap[] = { ARM_MAP_SHARED_RAM, + TC_MAP_NS_DRAM1, TC_FLASH0_RO, TC_MAP_DEVICE, {0} @@ -159,3 +162,15 @@ void plat_arm_secure_wdt_refresh(void) { sbsa_wdog_refresh(SBSA_SECURE_WDOG_REFRESH_BASE); } + +static sds_region_desc_t tc_sds_regions[] = { + { .base = PLAT_ARM_SDS_MEM_BASE }, + { .base = PLAT_ARM_RSE_AP_SDS_MEM_BASE }, +}; + +sds_region_desc_t *plat_sds_get_regions(unsigned int *region_count) +{ + *region_count = ARRAY_SIZE(tc_sds_regions); + + return tc_sds_regions; +} diff --git a/plat/arm/board/tc/tc_security.c b/plat/arm/board/tc/tc_security.c index 6a345010..7c7a1a13 100644 --- a/plat/arm/board/tc/tc_security.c +++ b/plat/arm/board/tc/tc_security.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, Arm Limited. All rights reserved. + * Copyright (c) 2024, Arm Limited. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -7,17 +7,21 @@ #include <plat/arm/common/plat_arm.h> #include <platform_def.h> +#if (TARGET_PLATFORM <= 2) static const arm_tzc_regions_info_t tzc_regions[] = { TC_TZC_REGIONS_DEF, {} }; +#endif /* Initialize the secure environment */ void plat_arm_security_setup(void) { +#if (TARGET_PLATFORM <= 2) unsigned int i; for (i = 0U; i < TZC400_COUNT; i++) { arm_tzc400_setup(TZC400_BASE(i), tzc_regions); } +#endif } diff --git a/plat/arm/board/tc/tc_topology.c b/plat/arm/board/tc/tc_topology.c index 9e18da6d..76318738 100644 --- a/plat/arm/board/tc/tc_topology.c +++ b/plat/arm/board/tc/tc_topology.c @@ -1,11 +1,12 @@ /* - * Copyright (c) 2020, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2020-2024, Arm Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ #include <plat/arm/common/plat_arm.h> #include <plat/arm/css/common/css_pm.h> +#include <platform_def.h> /****************************************************************************** * The power domain tree descriptor. @@ -36,6 +37,14 @@ const uint32_t plat_css_core_pos_to_scmi_dmn_id_map[] = { (SET_SCMI_CHANNEL_ID(0x0) | SET_SCMI_DOMAIN_ID(0x5)), (SET_SCMI_CHANNEL_ID(0x0) | SET_SCMI_DOMAIN_ID(0x6)), (SET_SCMI_CHANNEL_ID(0x0) | SET_SCMI_DOMAIN_ID(0x7)), +#if PLATFORM_CORE_COUNT == 14 + (SET_SCMI_CHANNEL_ID(0x0) | SET_SCMI_DOMAIN_ID(0x8)), + (SET_SCMI_CHANNEL_ID(0x0) | SET_SCMI_DOMAIN_ID(0x9)), + (SET_SCMI_CHANNEL_ID(0x0) | SET_SCMI_DOMAIN_ID(0xA)), + (SET_SCMI_CHANNEL_ID(0x0) | SET_SCMI_DOMAIN_ID(0xB)), + (SET_SCMI_CHANNEL_ID(0x0) | SET_SCMI_DOMAIN_ID(0xC)), + (SET_SCMI_CHANNEL_ID(0x0) | SET_SCMI_DOMAIN_ID(0xD)), +#endif /* PLATFORM_CORE_COUNT == 14 */ }; /******************************************************************************* @@ -56,3 +65,11 @@ unsigned int plat_arm_get_cpu_pe_count(u_register_t mpidr) return PLAT_MAX_PE_PER_CPU; } #endif + +/****************************************************************************** + * Return the cluster ID of current CPU + *****************************************************************************/ +unsigned int plat_cluster_id_by_mpidr(u_register_t mpidr) +{ + return MPIDR_AFFLVL2_VAL(mpidr); +} diff --git a/plat/arm/board/tc/tc_trng.c b/plat/arm/board/tc/tc_trng.c new file mode 100644 index 00000000..e5ec48a1 --- /dev/null +++ b/plat/arm/board/tc/tc_trng.c @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2017-2024, ARM Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <arm_acle.h> +#include <assert.h> +#include <stdbool.h> +#include <stdint.h> +#include <string.h> + +#include <lib/mmio.h> +#include <lib/smccc.h> +#include <lib/utils_def.h> +#include <plat/common/platform.h> +#include <platform_def.h> +#include <services/trng_svc.h> +#include <smccc_helpers.h> + +DEFINE_SVC_UUID2(_plat_trng_uuid, + 0x23523c58, 0x7448, 0x4083, 0x9d, 0x16, + 0xe3, 0xfa, 0xb9, 0xf1, 0x73, 0xbc +); +uuid_t plat_trng_uuid; + +/* Dummy implementation */ +bool plat_get_entropy(uint64_t *out) +{ + *out = 0xABBAEDDAACDCDEAD; + + return true; +} + +void plat_entropy_setup(void) +{ + uint64_t dummy; + + plat_trng_uuid = _plat_trng_uuid; + + /* Initialise the entropy source and trigger RNG generation */ + plat_get_entropy(&dummy); +} diff --git a/plat/arm/common/arm_bl1_setup.c b/plat/arm/common/arm_bl1_setup.c index feff6913..f043f59d 100644 --- a/plat/arm/common/arm_bl1_setup.c +++ b/plat/arm/common/arm_bl1_setup.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015-2022, Arm Limited and Contributors. All rights reserved. + * Copyright (c) 2015-2024, Arm Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -14,6 +14,9 @@ #include <common/debug.h> #include <lib/fconf/fconf.h> #include <lib/fconf/fconf_dyn_cfg_getter.h> +#if TRANSFER_LIST +#include <lib/transfer_list.h> +#endif #include <lib/utils.h> #include <lib/xlat_tables/xlat_tables_compat.h> #include <plat/arm/common/plat_arm.h> @@ -61,6 +64,10 @@ static meminfo_t bl1_tzram_layout; /* Boolean variable to hold condition whether firmware update needed or not */ static bool is_fwu_needed; +#if TRANSFER_LIST +static struct transfer_list_header *secure_tl; +#endif + struct meminfo *bl1_plat_sec_mem_layout(void) { return &bl1_tzram_layout; @@ -144,9 +151,13 @@ void bl1_plat_arch_setup(void) */ void arm_bl1_platform_setup(void) { - const struct dyn_cfg_dtb_info_t *fw_config_info; + const struct dyn_cfg_dtb_info_t *config_info __unused; + uint32_t fw_config_max_size __unused; + image_info_t config_image_info __unused; + struct transfer_list_entry *te __unused; + image_desc_t *desc; - uint32_t fw_config_max_size; + int err = -1; /* Initialise the IO layer and register platform IO devices */ @@ -159,6 +170,37 @@ void arm_bl1_platform_setup(void) return; } +#if TRANSFER_LIST + secure_tl = transfer_list_init((void *)PLAT_ARM_EL3_FW_HANDOFF_BASE, + PLAT_ARM_FW_HANDOFF_SIZE); + + if (secure_tl == NULL) { + ERROR("Secure transfer list initialisation failed!\n"); + panic(); + } + + te = transfer_list_add(secure_tl, TL_TAG_TB_FW_CONFIG, + ARM_TB_FW_CONFIG_MAX_SIZE, NULL); + assert(te != NULL); + + /* + * Set the load address of TB_FW_CONFIG in the data section of the TE just + * allocated in the secure transfer list. + */ + SET_PARAM_HEAD(&config_image_info, PARAM_IMAGE_BINARY, VERSION_2, 0); + config_image_info.image_base = (uintptr_t)transfer_list_entry_data(te); + config_image_info.image_max_size = te->data_size; + + VERBOSE("FCONF: Loading config with image ID: %u\n", TB_FW_CONFIG_ID); + err = load_auth_image(TB_FW_CONFIG_ID, &config_image_info); + if (err != 0) { + VERBOSE("Failed to load config %u\n", TB_FW_CONFIG_ID); + plat_error_handler(err); + } + + transfer_list_update_checksum(secure_tl); + fconf_populate("TB_FW", (uintptr_t)transfer_list_entry_data(te)); +#else /* Set global DTB info for fixed fw_config information */ fw_config_max_size = ARM_FW_CONFIG_LIMIT - ARM_FW_CONFIG_BASE; set_config_info(ARM_FW_CONFIG_BASE, ~0UL, fw_config_max_size, FW_CONFIG_ID); @@ -174,13 +216,14 @@ void arm_bl1_platform_setup(void) * FW_CONFIG loaded successfully. If FW_CONFIG device tree parsing * is successful then load TB_FW_CONFIG device tree. */ - fw_config_info = FCONF_GET_PROPERTY(dyn_cfg, dtb, FW_CONFIG_ID); - if (fw_config_info != NULL) { - err = fconf_populate_dtb_registry(fw_config_info->config_addr); + config_info = FCONF_GET_PROPERTY(dyn_cfg, dtb, FW_CONFIG_ID); + if (config_info != NULL) { + err = fconf_populate_dtb_registry(config_info->config_addr); if (err < 0) { ERROR("Parsing of FW_CONFIG failed %d\n", err); plat_error_handler(err); } + /* load TB_FW_CONFIG */ err = fconf_load_config(TB_FW_CONFIG_ID); if (err < 0) { @@ -191,11 +234,17 @@ void arm_bl1_platform_setup(void) ERROR("Invalid FW_CONFIG address\n"); plat_error_handler(err); } +#endif /* TRANSFER_LIST */ - /* The BL2 ep_info arg0 is modified to point to FW_CONFIG */ desc = bl1_plat_get_image_desc(BL2_IMAGE_ID); + +#if TRANSFER_LIST + transfer_list_set_handoff_args(secure_tl, &desc->ep_info); +#else + /* The BL2 ep_info arg0 is modified to point to FW_CONFIG */ assert(desc != NULL); - desc->ep_info.args.arg0 = fw_config_info->config_addr; + desc->ep_info.args.arg0 = config_info->config_addr; +#endif /* TRANSFER_LIST */ #if CRYPTO_SUPPORT /* Share the Mbed TLS heap info with other images */ @@ -250,3 +299,32 @@ unsigned int bl1_plat_get_next_image_id(void) { return is_fwu_needed ? NS_BL1U_IMAGE_ID : BL2_IMAGE_ID; } + +// Use the default implementation of this function when Firmware Handoff is +// disabled to avoid duplicating its logic. +#if TRANSFER_LIST +int bl1_plat_handle_post_image_load(unsigned int image_id) +{ + image_desc_t *image_desc __unused; + + assert(image_id == BL2_IMAGE_ID); + struct transfer_list_entry *te; + + /* Convey this information to BL2 via its TL. */ + te = transfer_list_add(secure_tl, TL_TAG_SRAM_LAYOUT64, + sizeof(meminfo_t), NULL); + assert(te != NULL); + + bl1_plat_calc_bl2_layout(&bl1_tzram_layout, + (meminfo_t *)transfer_list_entry_data(te)); + + transfer_list_update_checksum(secure_tl); + + /** + * Before exiting make sure the contents of the TL are flushed in case there's no + * support for hardware cache coherency. + */ + flush_dcache_range((uintptr_t)secure_tl, secure_tl->size); + return 0; +} +#endif /* TRANSFER_LIST*/ diff --git a/plat/arm/common/arm_bl2_el3_setup.c b/plat/arm/common/arm_bl2_el3_setup.c index 01e0db0b..869830d4 100644 --- a/plat/arm/common/arm_bl2_el3_setup.c +++ b/plat/arm/common/arm_bl2_el3_setup.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2023, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2017-2024, ARM Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -8,6 +8,8 @@ #include <drivers/generic_delay_timer.h> #include <drivers/partition/partition.h> +#include <lib/fconf/fconf.h> +#include <lib/fconf/fconf_dyn_cfg_getter.h> #include <plat/arm/common/plat_arm.h> #include <plat/common/platform.h> #include <platform_def.h> @@ -64,6 +66,43 @@ void bl2_el3_early_platform_setup(u_register_t arg0 __unused, generic_delay_timer_init(); } +#if ARM_FW_CONFIG_LOAD_ENABLE +/************************************************************************************* + * FW CONFIG load function for BL2 when RESET_TO_BL2=1 && ARM_FW_CONFIG_LOAD_ENABLE=1 + *************************************************************************************/ +void arm_bl2_el3_plat_config_load(void) +{ + int ret; + const struct dyn_cfg_dtb_info_t *fw_config_info; + + /* Set global DTB info for fixed fw_config information */ + set_config_info(PLAT_FW_CONFIG_BASE, ~0UL, PLAT_FW_CONFIG_MAX_SIZE, FW_CONFIG_ID); + + /* Fill the device tree information struct with the info from the config dtb */ + ret = fconf_load_config(FW_CONFIG_ID); + if (ret < 0) { + ERROR("Loading of FW_CONFIG failed %d\n", ret); + plat_error_handler(ret); + } + + /* + * FW_CONFIG loaded successfully. Check the FW_CONFIG device tree parsing + * is successful. + */ + fw_config_info = FCONF_GET_PROPERTY(dyn_cfg, dtb, FW_CONFIG_ID); + if (fw_config_info == NULL) { + ret = -1; + ERROR("Invalid FW_CONFIG address\n"); + plat_error_handler(ret); + } + ret = fconf_populate_dtb_registry(fw_config_info->config_addr); + if (ret < 0) { + ERROR("Parsing of FW_CONFIG failed %d\n", ret); + plat_error_handler(ret); + } +} +#endif /* ARM_FW_CONFIG_LOAD_ENABLE */ + /******************************************************************************* * Perform the very early platform specific architectural setup here. At the * moment this is only initializes the mmu in a quick and dirty way. diff --git a/plat/arm/common/arm_bl2_setup.c b/plat/arm/common/arm_bl2_setup.c index 3e8109e6..90ee70cc 100644 --- a/plat/arm/common/arm_bl2_setup.c +++ b/plat/arm/common/arm_bl2_setup.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015-2023, Arm Limited and Contributors. All rights reserved. + * Copyright (c) 2015-2024, Arm Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -19,13 +19,13 @@ #include <lib/fconf/fconf.h> #include <lib/fconf/fconf_dyn_cfg_getter.h> #include <lib/gpt_rme/gpt_rme.h> +#if TRANSFER_LIST +#include <lib/transfer_list.h> +#endif #ifdef SPD_opteed #include <lib/optee_utils.h> #endif #include <lib/utils.h> -#if ENABLE_RME -#include <plat/arm/common/arm_pas_def.h> -#endif #include <plat/arm/common/plat_arm.h> #include <plat/common/platform.h> @@ -33,13 +33,18 @@ static meminfo_t bl2_tzram_layout __aligned(CACHE_WRITEBACK_GRANULE); /* Base address of fw_config received from BL1 */ -static uintptr_t config_base; +static uintptr_t config_base __unused; /* * Check that BL2_BASE is above ARM_FW_CONFIG_LIMIT. This reserved page is * for `meminfo_t` data structure and fw_configs passed from BL1. */ +#if TRANSFER_LIST +CASSERT(BL2_BASE >= PLAT_ARM_EL3_FW_HANDOFF_BASE + PLAT_ARM_FW_HANDOFF_SIZE, + assert_bl2_base_overflows); +#elif !RESET_TO_BL2 CASSERT(BL2_BASE >= ARM_FW_CONFIG_LIMIT, assert_bl2_base_overflows); +#endif /* TRANSFER_LIST */ /* Weak definitions may be overridden in specific ARM standard platform */ #pragma weak bl2_early_platform_setup2 @@ -61,6 +66,9 @@ CASSERT(BL2_BASE >= ARM_FW_CONFIG_LIMIT, assert_bl2_base_overflows); #pragma weak arm_bl2_plat_handle_post_image_load +static struct transfer_list_header *secure_tl __unused; +static struct transfer_list_header *ns_tl __unused; + /******************************************************************************* * BL1 has passed the extents of the trusted SRAM that should be visible to BL2 * in x0. This memory layout is sitting at the base of the free trusted SRAM. @@ -69,16 +77,28 @@ CASSERT(BL2_BASE >= ARM_FW_CONFIG_LIMIT, assert_bl2_base_overflows); void arm_bl2_early_platform_setup(uintptr_t fw_config, struct meminfo *mem_layout) { + struct transfer_list_entry *te __unused; int __maybe_unused ret; /* Initialize the console to provide early debug support */ arm_console_boot_init(); - /* Setup the BL2 memory layout */ - bl2_tzram_layout = *mem_layout; +#if TRANSFER_LIST + // TODO: modify the prototype of this function fw_config != bl2_tl + secure_tl = (struct transfer_list_header *)fw_config; + te = transfer_list_find(secure_tl, TL_TAG_SRAM_LAYOUT64); + assert(te != NULL); + + bl2_tzram_layout = *(meminfo_t *)transfer_list_entry_data(te); + transfer_list_rem(secure_tl, te); +#else config_base = fw_config; + /* Setup the BL2 memory layout */ + bl2_tzram_layout = *mem_layout; +#endif + /* Initialise the IO layer and register platform IO devices */ plat_arm_io_setup(); @@ -106,7 +126,25 @@ void bl2_early_platform_setup2(u_register_t arg0, u_register_t arg1, u_register_ */ void bl2_plat_preload_setup(void) { +#if TRANSFER_LIST +/* Assume the secure TL hasn't been initialised if BL2 is running at EL3. */ +#if RESET_TO_BL2 + secure_tl = transfer_list_init((void *)PLAT_ARM_EL3_FW_HANDOFF_BASE, + PLAT_ARM_FW_HANDOFF_SIZE); + + if (secure_tl == NULL) { + ERROR("Secure transfer list initialisation failed!\n"); + panic(); + } +#endif + + arm_transfer_list_dyn_cfg_init(secure_tl); +#else +#if ARM_FW_CONFIG_LOAD_ENABLE + arm_bl2_el3_plat_config_load(); +#endif /* ARM_FW_CONFIG_LOAD_ENABLE */ arm_bl2_dyn_cfg_init(); +#endif #if ARM_GPT_SUPPORT && !PSA_FWU_SUPPORT /* Always use the FIP from bank 0 */ @@ -134,48 +172,6 @@ void bl2_platform_setup(void) arm_bl2_platform_setup(); } -#if ENABLE_RME -static void arm_bl2_plat_gpt_setup(void) -{ - /* - * The GPT library might modify the gpt regions structure to optimize - * the layout, so the array cannot be constant. - */ - pas_region_t pas_regions[] = { - ARM_PAS_KERNEL, - ARM_PAS_SECURE, - ARM_PAS_REALM, - ARM_PAS_EL3_DRAM, - ARM_PAS_GPTS, - ARM_PAS_KERNEL_1 - }; - - /* Initialize entire protected space to GPT_GPI_ANY. */ - if (gpt_init_l0_tables(GPCCR_PPS_64GB, ARM_L0_GPT_ADDR_BASE, - ARM_L0_GPT_SIZE) < 0) { - ERROR("gpt_init_l0_tables() failed!\n"); - panic(); - } - - /* Carve out defined PAS ranges. */ - if (gpt_init_pas_l1_tables(GPCCR_PGS_4K, - ARM_L1_GPT_ADDR_BASE, - ARM_L1_GPT_SIZE, - pas_regions, - (unsigned int)(sizeof(pas_regions) / - sizeof(pas_region_t))) < 0) { - ERROR("gpt_init_pas_l1_tables() failed!\n"); - panic(); - } - - INFO("Enabling Granule Protection Checks\n"); - if (gpt_enable() < 0) { - ERROR("gpt_enable() failed!\n"); - panic(); - } -} -#endif /* ENABLE_RME */ - /******************************************************************************* * Perform the very early platform specific architectural setup here. * When RME is enabled the secure environment is initialised before @@ -196,11 +192,13 @@ void arm_bl2_plat_arch_setup(void) ARM_MAP_ROMLIB_CODE, ARM_MAP_ROMLIB_DATA, #endif +#if !TRANSFER_LIST ARM_MAP_BL_CONFIG_REGION, +#endif /* TRANSFER_LIST */ #if ENABLE_RME ARM_MAP_L0_GPT_REGION, #endif - {0} + { 0 } }; #if ENABLE_RME @@ -212,11 +210,11 @@ void arm_bl2_plat_arch_setup(void) #ifdef __aarch64__ #if ENABLE_RME /* BL2 runs in EL3 when RME enabled. */ - assert(get_armv9_2_feat_rme_support() != 0U); + assert(is_feat_rme_present()); enable_mmu_el3(0); /* Initialise and enable granule protection after MMU. */ - arm_bl2_plat_gpt_setup(); + arm_gpt_setup(); #else enable_mmu_el1(0); #endif @@ -229,10 +227,17 @@ void arm_bl2_plat_arch_setup(void) void bl2_plat_arch_setup(void) { - const struct dyn_cfg_dtb_info_t *tb_fw_config_info; - + const struct dyn_cfg_dtb_info_t *tb_fw_config_info __unused; + struct transfer_list_entry *te __unused; arm_bl2_plat_arch_setup(); +#if TRANSFER_LIST + te = transfer_list_find(secure_tl, TL_TAG_TB_FW_CONFIG); + assert(te != NULL); + + fconf_populate("TB_FW", (uintptr_t)transfer_list_entry_data(te)); + transfer_list_rem(secure_tl, te); +#else /* Fill the properties struct with the info from the config dtb */ fconf_populate("FW_CONFIG", config_base); @@ -241,6 +246,7 @@ void bl2_plat_arch_setup(void) assert(tb_fw_config_info != NULL); fconf_populate("TB_FW", tb_fw_config_info->config_addr); +#endif } int arm_bl2_handle_post_image_load(unsigned int image_id) @@ -310,5 +316,23 @@ int arm_bl2_plat_handle_post_image_load(unsigned int image_id) return 0; } #endif + +#if TRANSFER_LIST + if (image_id == HW_CONFIG_ID) { + /* Refresh the now stale checksum following loading of HW_CONFIG into the TL. */ + transfer_list_update_checksum(secure_tl); + } +#endif /* TRANSFER_LIST */ + return arm_bl2_handle_post_image_load(image_id); } + +void arm_bl2_setup_next_ep_info(bl_mem_params_node_t *next_param_node) +{ + entry_point_info_t *ep __unused; + ep = transfer_list_set_handoff_args(secure_tl, + &next_param_node->ep_info); + assert(ep != NULL); + + arm_transfer_list_populate_ep_info(next_param_node, secure_tl); +} diff --git a/plat/arm/common/arm_bl31_setup.c b/plat/arm/common/arm_bl31_setup.c index 8e90615b..0a8dd372 100644 --- a/plat/arm/common/arm_bl31_setup.c +++ b/plat/arm/common/arm_bl31_setup.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015-2023, Arm Limited and Contributors. All rights reserved. + * Copyright (c) 2015-2024, Arm Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -7,19 +7,27 @@ #include <assert.h> #include <arch.h> +#include <arch_features.h> #include <arch_helpers.h> #include <common/bl_common.h> #include <common/debug.h> #include <drivers/console.h> #include <lib/debugfs.h> #include <lib/extensions/ras.h> +#include <lib/fconf/fconf.h> #include <lib/gpt_rme/gpt_rme.h> #include <lib/mmio.h> +#if TRANSFER_LIST +#include <lib/transfer_list.h> +#endif #include <lib/xlat_tables/xlat_tables_compat.h> #include <plat/arm/common/plat_arm.h> #include <plat/common/platform.h> #include <platform_def.h> +static struct transfer_list_header *secure_tl __unused; +static struct transfer_list_header *ns_tl __unused; + /* * Placeholder variables for copying the arguments that have been passed to * BL31 from BL2. @@ -35,8 +43,12 @@ static entry_point_info_t rmm_image_ep_info; * Check that BL31_BASE is above ARM_FW_CONFIG_LIMIT. The reserved page * is required for SOC_FW_CONFIG/TOS_FW_CONFIG passed from BL2. */ +#if TRANSFER_LIST +CASSERT(BL31_BASE >= PLAT_ARM_EL3_FW_HANDOFF_LIMIT, assert_bl31_base_overflows); +#else CASSERT(BL31_BASE >= ARM_FW_CONFIG_LIMIT, assert_bl31_base_overflows); -#endif +#endif /* TRANSFER_LIST */ +#endif /* RESET_TO_BL31 */ /* Weak definitions may be overridden in specific ARM standard platform */ #pragma weak bl31_early_platform_setup2 @@ -86,7 +98,12 @@ struct entry_point_info *bl31_plat_get_next_image_ep_info(uint32_t type) assert(sec_state_is_valid(type)); if (type == NON_SECURE) { +#if TRANSFER_LIST && !RESET_TO_BL31 + next_image_info = transfer_list_set_handoff_args( + ns_tl, &bl33_image_ep_info); +#else next_image_info = &bl33_image_ep_info; +#endif } #if ENABLE_RME else if (type == REALM) { @@ -115,6 +132,62 @@ struct entry_point_info *bl31_plat_get_next_image_ep_info(uint32_t type) * while creating page tables. BL2 has flushed this information to memory, so * we are guaranteed to pick up good data. ******************************************************************************/ +#if TRANSFER_LIST +void __init arm_bl31_early_platform_setup(u_register_t arg0, u_register_t arg1, + u_register_t arg2, u_register_t arg3) +{ +#if RESET_TO_BL31 + /* Populate entry point information for BL33 */ + SET_PARAM_HEAD(&bl33_image_ep_info, PARAM_EP, VERSION_1, 0); + /* + * Tell BL31 where the non-trusted software image + * is located and the entry state information + */ + bl33_image_ep_info.pc = plat_get_ns_image_entrypoint(); + + bl33_image_ep_info.spsr = arm_get_spsr_for_bl33_entry(); + SET_SECURITY_STATE(bl33_image_ep_info.h.attr, NON_SECURE); + + bl33_image_ep_info.args.arg0 = PLAT_ARM_TRANSFER_LIST_DTB_OFFSET; + bl33_image_ep_info.args.arg1 = + TRANSFER_LIST_HANDOFF_X1_VALUE(REGISTER_CONVENTION_VERSION); + bl33_image_ep_info.args.arg3 = FW_NS_HANDOFF_BASE; +#else + struct transfer_list_entry *te = NULL; + struct entry_point_info *ep; + + secure_tl = (struct transfer_list_header *)arg3; + + /* + * Populate the global entry point structures used to execute subsequent + * images. + */ + while ((te = transfer_list_next(secure_tl, te)) != NULL) { + ep = transfer_list_entry_data(te); + + if (te->tag_id == TL_TAG_EXEC_EP_INFO64) { + switch (GET_SECURITY_STATE(ep->h.attr)) { + case NON_SECURE: + bl33_image_ep_info = *ep; + break; +#if ENABLE_RME + case REALM: + rmm_image_ep_info = *ep; + break; +#endif + case SECURE: + bl32_image_ep_info = *ep; + break; + default: + ERROR("Unrecognized Image Security State %lu\n", + GET_SECURITY_STATE(ep->h.attr)); + panic(); + } + } + } +#endif /* RESET_TO_BL31 */ +} +#else void __init arm_bl31_early_platform_setup(void *from_bl2, uintptr_t soc_fw_config, uintptr_t hw_config, void *plat_params_from_bl2) { @@ -258,11 +331,16 @@ void __init arm_bl31_early_platform_setup(void *from_bl2, uintptr_t soc_fw_confi bl33_image_ep_info.args.arg3 = 0U; # endif } +#endif void bl31_early_platform_setup2(u_register_t arg0, u_register_t arg1, u_register_t arg2, u_register_t arg3) { +#if TRANSFER_LIST + arm_bl31_early_platform_setup(arg0, arg1, arg2, arg3); +#else arm_bl31_early_platform_setup((void *)arg0, arg1, arg2, (void *)arg3); +#endif /* * Initialize Interconnect for this cluster during cold boot. @@ -286,6 +364,36 @@ void bl31_early_platform_setup2(u_register_t arg0, u_register_t arg1, ******************************************************************************/ void arm_bl31_platform_setup(void) { + struct transfer_list_entry *te __unused; + +#if TRANSFER_LIST && !RESET_TO_BL31 + ns_tl = transfer_list_init((void *)FW_NS_HANDOFF_BASE, + PLAT_ARM_FW_HANDOFF_SIZE); + + if (ns_tl == NULL) { + ERROR("Non-secure transfer list initialisation failed!"); + panic(); + } + + te = transfer_list_find(secure_tl, TL_TAG_FDT); + assert(te != NULL); + + /* + * A pre-existing assumption is that FCONF is unsupported w/ RESET_TO_BL2 and + * RESET_TO_BL31. In the case of RESET_TO_BL31 this makes sense because there + * isn't a prior stage to load the device tree, but the reasoning for RESET_TO_BL2 is + * less clear. For the moment hardware properties that would normally be + * derived from the DT are statically defined. + */ +#if !RESET_TO_BL2 + fconf_populate("HW_CONFIG", (uintptr_t)transfer_list_entry_data(te)); +#endif + + te = transfer_list_add(ns_tl, TL_TAG_FDT, te->data_size, + transfer_list_entry_data(te)); + assert(te != NULL); +#endif /* TRANSFER_LIST */ + /* Initialize the GIC driver, cpu and distributor interfaces */ plat_arm_gic_driver_init(); plat_arm_gic_init(); @@ -325,15 +433,22 @@ void arm_bl31_platform_setup(void) /******************************************************************************* * Perform any BL31 platform runtime setup prior to BL31 exit common to ARM * standard platforms - * Perform BL31 platform setup ******************************************************************************/ void arm_bl31_plat_runtime_setup(void) { - console_switch_state(CONSOLE_FLAG_RUNTIME); - + struct transfer_list_entry *te __unused; /* Initialize the runtime console */ arm_console_runtime_init(); +#if TRANSFER_LIST && !RESET_TO_BL31 + /* + * We assume BL31 has added all TE's required by BL33 at this stage, ensure + * that data is visible to all observers by performing a flush operation, so + * they can access the updated data even if caching is not enabled. + */ + flush_dcache_range((uintptr_t)ns_tl, ns_tl->size); +#endif /* TRANSFER_LIST && !(RESET_TO_BL2 || RESET_TO_BL31) */ + #if RECLAIM_INIT_CODE arm_free_init_memory(); #endif @@ -431,6 +546,13 @@ void __init arm_bl31_plat_arch_setup(void) enable_mmu_el3(0); #if ENABLE_RME +#if RESET_TO_BL31 + /* initialize GPT only when RME is enabled. */ + assert(is_feat_rme_present()); + + /* Initialise and enable granule protection after MMU. */ + arm_gpt_setup(); +#endif /* RESET_TO_BL31 */ /* * Initialise Granule Protection library and enable GPC for the primary * processor. The tables have already been initialized by a previous BL diff --git a/plat/arm/common/arm_common.c b/plat/arm/common/arm_common.c index fc681149..2d4165c4 100644 --- a/plat/arm/common/arm_common.c +++ b/plat/arm/common/arm_common.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015-2021, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2015-2024, ARM Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -12,6 +12,8 @@ #include <arch_helpers.h> #include <common/debug.h> #include <common/romlib.h> +#include <common/par.h> +#include <lib/extensions/sysreg128.h> #include <lib/mmio.h> #include <lib/smccc.h> #include <lib/xlat_tables/xlat_tables_compat.h> @@ -196,7 +198,8 @@ unsigned int plat_get_syscnt_freq2(void) */ int plat_sdei_validate_entry_point(uintptr_t ep, unsigned int client_mode) { - uint64_t par, pa; + uint64_t pa; + sysreg_t par; u_register_t scr_el3; /* Doing Non-secure address translation requires SCR_EL3.NS set */ @@ -230,7 +233,7 @@ int plat_sdei_validate_entry_point(uintptr_t ep, unsigned int client_mode) return -1; /* Extract Physical Address from PAR */ - pa = (par & (PAR_ADDR_MASK << PAR_ADDR_SHIFT)); + pa = get_par_el1_pa(par); /* Perform NS entry point validation on the physical address */ return arm_validate_ns_entrypoint(pa); @@ -241,3 +244,43 @@ const mmap_region_t *plat_get_addr_mmap(void) { return plat_arm_mmap; } + +#if ENABLE_RME +void arm_gpt_setup(void) +{ + /* + * It is to be noted that any Arm platform that reuses arm_gpt_setup + * must implement plat_arm_get_gpt_info within its platform code + */ + const arm_gpt_info_t *arm_gpt_info = + plat_arm_get_gpt_info(); + + if (arm_gpt_info == NULL) { + ERROR("arm_gpt_info not initialized!!\n"); + panic(); + } + + /* Initialize entire protected space to GPT_GPI_ANY. */ + if (gpt_init_l0_tables(arm_gpt_info->pps, arm_gpt_info->l0_base, + arm_gpt_info->l0_size) < 0) { + ERROR("gpt_init_l0_tables() failed!\n"); + panic(); + } + + /* Carve out defined PAS ranges. */ + if (gpt_init_pas_l1_tables(arm_gpt_info->pgs, + arm_gpt_info->l1_base, + arm_gpt_info->l1_size, + arm_gpt_info->pas_region_base, + arm_gpt_info->pas_region_count) < 0) { + ERROR("gpt_init_pas_l1_tables() failed!\n"); + panic(); + } + + INFO("Enabling Granule Protection Checks\n"); + if (gpt_enable() < 0) { + ERROR("gpt_enable() failed!\n"); + panic(); + } +} +#endif /* ENABLE_RME */ diff --git a/plat/arm/common/arm_common.mk b/plat/arm/common/arm_common.mk index ae0d85da..2fd993c4 100644 --- a/plat/arm/common/arm_common.mk +++ b/plat/arm/common/arm_common.mk @@ -1,5 +1,5 @@ # -# Copyright (c) 2015-2023, Arm Limited and Contributors. All rights reserved. +# Copyright (c) 2015-2024, Arm Limited and Contributors. All rights reserved. # # SPDX-License-Identifier: BSD-3-Clause # @@ -26,7 +26,7 @@ ifeq (${ARCH}, aarch64) else ifeq (${ARM_TSP_RAM_LOCATION}, dram) ARM_TSP_RAM_LOCATION_ID = ARM_DRAM_ID else - $(error "Unsupported ARM_TSP_RAM_LOCATION value") + $(error Unsupported ARM_TSP_RAM_LOCATION value) endif # Process flags @@ -83,7 +83,7 @@ $(eval $(call add_define,ARM_BL31_IN_DRAM)) # memory. This means we must not run BL31 from TZC-protected DRAM. ifeq (${ARM_BL31_IN_DRAM},1) ifeq (${ENABLE_RME},1) - $(error "BL31 must not run from DRAM on RME-systems. Please set ARM_BL31_IN_DRAM to 0") + $(error BL31 must not run from DRAM on RME-systems. Please set ARM_BL31_IN_DRAM to 0) endif endif @@ -105,25 +105,20 @@ $(eval $(call add_define,ARM_LINUX_KERNEL_AS_BL33)) ifeq (${ARM_LINUX_KERNEL_AS_BL33},1) ifneq (${ARCH},aarch64) ifneq (${RESET_TO_SP_MIN},1) - $(error "ARM_LINUX_KERNEL_AS_BL33 is only available if RESET_TO_SP_MIN=1.") + $(error ARM_LINUX_KERNEL_AS_BL33 is only available if RESET_TO_SP_MIN=1.) endif endif ifndef PRELOADED_BL33_BASE - $(error "PRELOADED_BL33_BASE must be set if ARM_LINUX_KERNEL_AS_BL33 is used.") + $(error PRELOADED_BL33_BASE must be set if ARM_LINUX_KERNEL_AS_BL33 is used.) endif ifeq (${RESET_TO_BL31},1) ifndef ARM_PRELOADED_DTB_BASE - $(error "ARM_PRELOADED_DTB_BASE must be set if ARM_LINUX_KERNEL_AS_BL33 is - used with RESET_TO_BL31.") + $(error ARM_PRELOADED_DTB_BASE must be set if ARM_LINUX_KERNEL_AS_BL33 is used with RESET_TO_BL31.) endif $(eval $(call add_define,ARM_PRELOADED_DTB_BASE)) endif endif -# Use an implementation of SHA-256 with a smaller memory footprint but reduced -# speed. -$(eval $(call add_define,MBEDTLS_SHA256_SMALLER)) - # Add the build options to pack Trusted OS Extra1 and Trusted OS Extra2 images # in the FIP if the platform requires. ifneq ($(BL32_EXTRA1),) @@ -169,6 +164,25 @@ ifneq ($(filter 1,${RESET_TO_BL31} ${RESET_TO_SP_MIN}),) ENABLE_PIE := 1 endif +# On Arm platform, disable ARM_FW_CONFIG_LOAD_ENABLE by default. +ARM_FW_CONFIG_LOAD_ENABLE := 0 +$(eval $(call assert_boolean,ARM_FW_CONFIG_LOAD_ENABLE)) +$(eval $(call add_define,ARM_FW_CONFIG_LOAD_ENABLE)) + +# In order to enable ARM_FW_CONFIG_LOAD_ENABLE for the Arm platform, the +# platform should be reset to BL2 (RESET_TO_BL2=1), and FW_CONFIG must be +# specified. +ifeq (${ARM_FW_CONFIG_LOAD_ENABLE},1) + ifneq (${RESET_TO_BL2},1) + $(error RESET_TO_BL2 must be enabled when ARM_FW_CONFIG_LOAD_ENABLE \ + is enabled) + endif + ifeq (${FW_CONFIG},) + $(error FW_CONFIG must be specified when ARM_FW_CONFIG_LOAD_ENABLE \ + is enabled) + endif +endif + # Disable GPT parser support, use FIP image by default ARM_GPT_SUPPORT := 0 $(eval $(call assert_boolean,ARM_GPT_SUPPORT)) @@ -280,7 +294,7 @@ endif ifeq (${JUNO_AARCH32_EL3_RUNTIME},1) BL2_SOURCES += plat/arm/common/aarch32/arm_bl2_mem_params_desc.c else -ifneq (${PLAT}, corstone1000) +ifeq ($(filter $(PLAT), corstone1000 rd1ae),) BL2_SOURCES += plat/arm/common/${ARCH}/arm_bl2_mem_params_desc.c endif endif @@ -299,6 +313,10 @@ BL31_SOURCES += plat/arm/common/arm_bl31_setup.c \ plat/arm/common/arm_topology.c \ plat/common/plat_psci_common.c +ifeq (${TRANSFER_LIST}, 1) + TRANSFER_LIST_SOURCES += plat/arm/common/arm_transfer_list.c +endif + ifneq ($(filter 1,${ENABLE_PMF} ${ETHOSN_NPU_DRIVER}),) ARM_SVC_HANDLER_SRCS := @@ -361,6 +379,17 @@ ifeq (${DRTM_SUPPORT},1) BL31_SOURCES += plat/arm/common/arm_err.c endif +ifneq ($(filter 1,${MEASURED_BOOT} ${TRUSTED_BOARD_BOOT} ${DRTM_SUPPORT}),) + PLAT_INCLUDES += -Iplat/arm/common \ + -Iinclude/drivers/auth/mbedtls + # Specify mbed TLS configuration file + ifeq (${PSA_CRYPTO},1) + MBEDTLS_CONFIG_FILE ?= "<plat_arm_psa_mbedtls_config.h>" + else + MBEDTLS_CONFIG_FILE ?= "<plat_arm_mbedtls_config.h>" + endif +endif + ifneq (${TRUSTED_BOARD_BOOT},0) # Include common TBB sources @@ -374,20 +403,37 @@ ifneq (${TRUSTED_BOARD_BOOT},0) ifneq (${COT_DESC_IN_DTB},0) BL2_SOURCES += lib/fconf/fconf_cot_getter.c else - BL2_SOURCES += drivers/auth/tbbr/tbbr_cot_common.c # Juno has its own TBBR CoT file for BL2 - ifneq (${PLAT},juno) - BL2_SOURCES += drivers/auth/tbbr/tbbr_cot_bl2.c + ifeq (${PLAT},juno) + BL2_SOURCES += drivers/auth/tbbr/tbbr_cot_common.c endif endif else ifeq (${COT},dualroot) - AUTH_SOURCES += drivers/auth/dualroot/cot.c + BL1_SOURCES += drivers/auth/dualroot/bl1_cot.c + ifneq (${COT_DESC_IN_DTB},0) + BL2_SOURCES += lib/fconf/fconf_cot_getter.c + endif else ifeq (${COT},cca) - AUTH_SOURCES += drivers/auth/cca/cot.c + BL1_SOURCES += drivers/auth/cca/bl1_cot.c + ifneq (${COT_DESC_IN_DTB},0) + BL2_SOURCES += lib/fconf/fconf_cot_getter.c + endif else $(error Unknown chain of trust ${COT}) endif + ifeq (${COT_DESC_IN_DTB},0) + ifeq (${COT},dualroot) + COTDTPATH := fdts/dualroot_cot_descriptors.dtsi + else ifeq (${COT},cca) + COTDTPATH := fdts/cca_cot_descriptors.dtsi + else ifeq (${COT},tbbr) + ifneq (${PLAT},juno) + COTDTPATH := fdts/tbbr_cot_descriptors.dtsi + endif + endif + endif + BL1_SOURCES += ${AUTH_SOURCES} \ bl1/tbbr/tbbr_img_desc.c \ plat/arm/common/arm_bl1_fwu.c \ @@ -412,10 +458,6 @@ ifneq ($(filter 1,${MEASURED_BOOT} ${DRTM_SUPPORT}),) $(info Including ${MEASURED_BOOT_MK}) include ${MEASURED_BOOT_MK} - ifneq (${MBOOT_EL_HASH_ALG}, sha256) - $(eval $(call add_define,TF_MBEDTLS_MBOOT_USE_SHA512)) - endif - ifeq (${MEASURED_BOOT},1) BL1_SOURCES += ${EVENT_LOG_SOURCES} BL2_SOURCES += ${EVENT_LOG_SOURCES} @@ -442,6 +484,27 @@ endif ifeq (${RECLAIM_INIT_CODE}, 1) ifeq (${ARM_XLAT_TABLES_LIB_V1}, 1) - $(error "To reclaim init code xlat tables v2 must be used") + $(error To reclaim init code xlat tables v2 must be used) endif endif + +ifneq ($(COTDTPATH),) + cot-dt-defines = IMAGE_BL2 $(BL2_DEFINES) $(PLAT_BL_COMMON_DEFINES) + cot-dt-include-dirs = $(BL2_INCLUDE_DIRS) $(PLAT_BL_COMMON_INCLUDE_DIRS) + + cot-dt-cpp-flags = $(cot-dt-defines:%=-D%) + cot-dt-cpp-flags += $(cot-dt-include-dirs:%=-I%) + + cot-dt-cpp-flags += $(BL2_CPPFLAGS) $(PLAT_BL_COMMON_CPPFLAGS) + cot-dt-cpp-flags += $(CPPFLAGS) $(BL_CPPFLAGS) $(TF_CFLAGS_$(ARCH)) + cot-dt-cpp-flags += -c -x assembler-with-cpp -E -P -o $@ $< + + $(BUILD_PLAT)/$(COTDTPATH:.dtsi=.dts): $(COTDTPATH) | $$(@D)/ + $(q)$($(ARCH)-cpp) $(cot-dt-cpp-flags) + + $(BUILD_PLAT)/$(COTDTPATH:.dtsi=.c): $(BUILD_PLAT)/$(COTDTPATH:.dtsi=.dts) | $$(@D)/ + $(if $(host-poetry),$(q)poetry -q install) + $(q)$(if $(host-poetry),poetry run )cot-dt2c convert-to-c $< $@ + + BL2_SOURCES += $(BUILD_PLAT)/$(COTDTPATH:.dtsi=.c) +endif diff --git a/plat/arm/common/arm_dyn_cfg.c b/plat/arm/common/arm_dyn_cfg.c index 99e28098..18ab5be8 100644 --- a/plat/arm/common/arm_dyn_cfg.c +++ b/plat/arm/common/arm_dyn_cfg.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018-2023, Arm Limited and Contributors. All rights reserved. + * Copyright (c) 2018-2024, Arm Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -120,6 +120,7 @@ void arm_bl1_set_mbedtls_heap(void) } #endif /* CRYPTO_SUPPORT */ +#if IMAGE_BL2 /* * BL2 utility function to initialize dynamic configuration specified by * FW_CONFIG. Populate the bl_mem_params_node_t of other FW_CONFIGs if @@ -229,3 +230,4 @@ void arm_bl2_dyn_cfg_init(void) panic(); } } +#endif /* IMAGE_BL2 */ diff --git a/plat/arm/common/arm_dyn_cfg_helpers.c b/plat/arm/common/arm_dyn_cfg_helpers.c index 5dc11151..d13be993 100644 --- a/plat/arm/common/arm_dyn_cfg_helpers.c +++ b/plat/arm/common/arm_dyn_cfg_helpers.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018-2023, Arm Limited and Contributors. All rights reserved. + * Copyright (c) 2018-2024, Arm Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -124,6 +124,150 @@ int arm_set_dtb_mbedtls_heap_info(void *dtb, void *heap_addr, size_t heap_size) } #if MEASURED_BOOT +#if DICE_PROTECTION_ENVIRONMENT + +#include <common/desc_image_load.h> + +#define DTB_PROP_DPE_CTX_HANDLE "dpe_ctx_handle" + +static int arm_set_dpe_context_handle(uintptr_t config_base, + int *ctx_handle) +{ + /* As libfdt uses void *, we can't avoid this cast */ + void *dtb = (void *)config_base; + const char *compatible = "arm,dpe_ctx_handle"; + int err, node; + + /* + * Verify that the DTB is valid, before attempting to write to it, + * and get the DTB root node. + */ + + /* Check if the pointer to DT is correct */ + err = fdt_check_header(dtb); + if (err < 0) { + WARN("Invalid DTB file passed\n"); + return err; + } + + /* Assert the node offset point to compatible property */ + node = fdt_node_offset_by_compatible(dtb, -1, compatible); + if (node < 0) { + WARN("The compatible property '%s' not%s", compatible, + " found in the config\n"); + return node; + } + + VERBOSE("Dyn cfg: '%s'%s", compatible, " found in the config\n"); + + err = fdtw_write_inplace_cells(dtb, node, + DTB_PROP_DPE_CTX_HANDLE, 1, ctx_handle); + if (err < 0) { + ERROR("%sDTB property '%s'\n", + "Unable to write ", DTB_PROP_DPE_CTX_HANDLE); + } else { + /* + * Ensure that the info written to the DTB is visible + * to other images. + */ + flush_dcache_range(config_base, fdt_totalsize(dtb)); + } + + return err; +} + +/* + * This function writes the DPE context handle value to the NT_FW_CONFIG DTB. + * + * This function is supposed to be called only by BL2. + * + * Returns: + * 0 = success + * < 0 = error + */ +int arm_set_nt_fw_info(int *ctx_handle) +{ + uintptr_t config_base; + const bl_mem_params_node_t *cfg_mem_params; + + /* Get the config load address and size from NT_FW_CONFIG */ + cfg_mem_params = get_bl_mem_params_node(NT_FW_CONFIG_ID); + assert(cfg_mem_params != NULL); + + config_base = cfg_mem_params->image_info.image_base; + + /* Write the context handle value in the DTB */ + return arm_set_dpe_context_handle(config_base, ctx_handle); +} + +/* + * This function writes the DPE context handle value to the TB_FW_CONFIG DTB. + * + * This function is supposed to be called only by BL1. + * + * Returns: + * 0 = success + * < 0 = error + */ +int arm_set_tb_fw_info(int *ctx_handle) +{ + /* + * Read tb_fw_config device tree for Event Log properties + * and write the Event Log address and its size in the DTB + */ + const struct dyn_cfg_dtb_info_t *tb_fw_config_info; + uintptr_t tb_fw_cfg_dtb; + + tb_fw_config_info = FCONF_GET_PROPERTY(dyn_cfg, dtb, TB_FW_CONFIG_ID); + assert(tb_fw_config_info != NULL); + + tb_fw_cfg_dtb = tb_fw_config_info->config_addr; + + /* Write the context handle value in the DTB */ + return arm_set_dpe_context_handle(tb_fw_cfg_dtb, ctx_handle); +} + +/* + * This function reads the initial DPE context handle from TB_FW_CONFIG DTB. + * + * This function is supposed to be called only by BL2. + * + * Returns: + * 0 = success + * < 0 = error + */ + +int arm_get_tb_fw_info(int *ctx_handle) +{ + /* As libfdt uses void *, we can't avoid this cast */ + const struct dyn_cfg_dtb_info_t *tb_fw_config_info; + int node, rc; + + tb_fw_config_info = FCONF_GET_PROPERTY(dyn_cfg, dtb, TB_FW_CONFIG_ID); + assert(tb_fw_config_info != NULL); + + void *dtb = (void *)tb_fw_config_info->config_addr; + const char *compatible = "arm,dpe_ctx_handle"; + + /* Assert the node offset point to compatible property */ + node = fdt_node_offset_by_compatible(dtb, -1, compatible); + if (node < 0) { + WARN("The compatible property '%s'%s", compatible, + " not specified in TB_FW config.\n"); + return node; + } + + VERBOSE("Dyn cfg: '%s'%s", compatible, " found in the config\n"); + + rc = fdt_read_uint32(dtb, node, DTB_PROP_DPE_CTX_HANDLE, (uint32_t *)ctx_handle); + if (rc != 0) { + ERROR("%s%s", DTB_PROP_DPE_CTX_HANDLE, + " not specified in TB_FW config.\n"); + } + + return rc; +} +#else /* * Write the Event Log address and its size in the DTB. * @@ -393,4 +537,5 @@ int arm_get_tb_fw_info(uint64_t *log_addr, size_t *log_size, return rc; } +#endif /* DICE_PROTECTION_ENVIRONMENT */ #endif /* MEASURED_BOOT */ diff --git a/plat/arm/common/arm_image_load.c b/plat/arm/common/arm_image_load.c index c411c6cb..25252666 100644 --- a/plat/arm/common/arm_image_load.c +++ b/plat/arm/common/arm_image_load.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016-2021, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2016-2024, Arm Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -17,7 +17,10 @@ #pragma weak plat_get_bl_image_load_info #pragma weak plat_get_next_bl_params -static bl_params_t *next_bl_params_cpy_ptr; +#if TRANSFER_LIST +static bl_params_t next_bl_params_cpy; +#endif +bl_params_t *next_bl_params_cpy_ptr; /******************************************************************************* * This function flushes the data structures so that they are visible @@ -96,9 +99,15 @@ struct bl_load_info *plat_get_bl_image_load_info(void) ******************************************************************************/ struct bl_params *arm_get_next_bl_params(void) { - bl_mem_params_node_t *bl2_mem_params_descs_cpy - = (bl_mem_params_node_t *)ARM_BL2_MEM_DESC_BASE; - const bl_params_t *next_bl_params; + bl_mem_params_node_t *bl2_mem_params_descs_cpy __unused; + const bl_params_t *next_bl_params __unused; + +#if TRANSFER_LIST + next_bl_params_cpy_ptr = &next_bl_params_cpy; + SET_PARAM_HEAD(next_bl_params_cpy_ptr, PARAM_BL_PARAMS, VERSION_2, 0U); +#else + bl2_mem_params_descs_cpy = + (bl_mem_params_node_t *)ARM_BL2_MEM_DESC_BASE; next_bl_params_cpy_ptr = (bl_params_t *)(ARM_BL2_MEM_DESC_BASE + @@ -127,6 +136,7 @@ struct bl_params *arm_get_next_bl_params(void) (sizeof(bl_params_t))); populate_next_bl_params_config(next_bl_params_cpy_ptr); +#endif /* TRANSFER_LIST */ return next_bl_params_cpy_ptr; } diff --git a/plat/arm/common/arm_io_storage.c b/plat/arm/common/arm_io_storage.c index 19ee1b0b..5a3d27ab 100644 --- a/plat/arm/common/arm_io_storage.c +++ b/plat/arm/common/arm_io_storage.c @@ -124,6 +124,7 @@ int plat_get_image_source(unsigned int image_id, uintptr_t *dev_handle, const struct plat_io_policy *policy; policy = FCONF_GET_PROPERTY(arm, io_policies, image_id); + assert(policy->check != NULL); result = policy->check(policy->image_spec); if (result == 0) { *image_spec = policy->image_spec; diff --git a/plat/arm/common/arm_ni.c b/plat/arm/common/arm_ni.c new file mode 100644 index 00000000..b3ad8b38 --- /dev/null +++ b/plat/arm/common/arm_ni.c @@ -0,0 +1,167 @@ +/* + * Copyright (c) 2024, ARM Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <errno.h> + +#include <common/debug.h> +#include <plat/arm/common/plat_arm.h> +#include <platform_def.h> + +#define NI_CHILD_NODE_COUNT 4 +#define NI_CHILD_POINTERS_START 8 + +#define NI_PMU_SECURE_CTRL 0x100 +#define NI_PMU_SECURE_EVENT_OBSERVATION 0x108 +#define NI_PMU_DEBUG_ENABLE 0x110 +#define NI_COMP_NUM_SUBFEATURES 0x100 +#define NI_COMP_SUBFEATURE_TYPE_START 0x108 +#define NI_COMP_SUBFEATURE_SECURE_CTRL_START 0x308 + +#define SECURE_OVERRIDE_DEFAULT BIT(0) +#define SECURE_EVENT_ENABLE BIT(2) +#define NA_EVENT_ENABLE BIT(3) +#define PMU_ENABLE BIT(0) + +#define NI_NODE_MASK 0x0000ffff +#define NI_NODE_TYPE(node_info) (node_info & NI_NODE_MASK) +#define NI_CHILD_POINTER(i) (NI_CHILD_POINTERS_START + (i * 4)) +#define NI_COMP_SUBFEATURE_TYPE(i) (NI_COMP_SUBFEATURE_TYPE_START + (i * 8)) +#define NI_COMP_SUBFEATURE_SECURE_CTRL(i) (NI_COMP_SUBFEATURE_SECURE_CTRL_START + (i * 8)) + +#define NI_PERIPHERAL_ID0 0xfe0 +#define NI_PIDR0_PART_MASK 0xff +#define NI_PERIPHERAL_ID1 0xfe4 +#define NI_PIDR1_PART_MASK 0xf +#define NI_PIDR1_PART_SHIFT 8 + +enum ni_part { + NI_700 = 0x43b, + NI_710AE = 0x43d, + NI_TOWER = 0x43f, +}; + +enum ni_node_type { + NI_INVALID_NODE = 0, + NI_VOLTAGE_DOMAIN = 1, + NI_POWER_DOMAIN = 2, + NI_CLOCK_DOMAIN = 3, + NI_ASNI = 4, + NI_AMNI = 5, + NI_PMU = 6, + NI_HSNI = 7, + NI_HMNI = 8, + NI_PMNI = 9, + NI_CMNI = 14, + NI_CFGNI = 15 +}; + +enum ni_subfeature_type { + NI_SUBFEATURE_APU = 0, + NI_SUBFEATURE_ADDR_MAP = 1, + NI_SUBFEATURE_FCU = 2, + NI_SUBFEATURE_IDM = 3 +}; + +static void ni_enable_pmu(uintptr_t pmu_addr) +{ + mmio_setbits_32(pmu_addr + NI_PMU_DEBUG_ENABLE, PMU_ENABLE); +} + +static void ni_enable_fcu_ns_access(uintptr_t comp_addr) +{ + uint32_t subfeature_type; + uint32_t subfeature_count; + uint32_t subfeature_secure_ctrl; + + subfeature_count = mmio_read_32(comp_addr + NI_COMP_NUM_SUBFEATURES); + for (uint32_t i = 0U; i < subfeature_count; i++) { + subfeature_type = + NI_NODE_TYPE(mmio_read_32(comp_addr + NI_COMP_SUBFEATURE_TYPE(i))); + if (subfeature_type == NI_SUBFEATURE_FCU) { + subfeature_secure_ctrl = comp_addr + NI_COMP_SUBFEATURE_SECURE_CTRL(i); + mmio_setbits_32(subfeature_secure_ctrl, SECURE_OVERRIDE_DEFAULT); + } + } +} + +static void ni_enable_pmu_ns_access(uintptr_t comp_addr) +{ + mmio_setbits_32(comp_addr + NI_PMU_SECURE_CTRL, SECURE_OVERRIDE_DEFAULT); + mmio_setbits_32(comp_addr + NI_PMU_SECURE_EVENT_OBSERVATION, + SECURE_EVENT_ENABLE | NA_EVENT_ENABLE); +} + +static void ni_setup_component(uintptr_t comp_addr) +{ + uint32_t node_info; + + node_info = mmio_read_32(comp_addr); + + switch (NI_NODE_TYPE(node_info)) { + case NI_ASNI: + case NI_AMNI: + case NI_HSNI: + case NI_HMNI: + case NI_PMNI: + ni_enable_fcu_ns_access(comp_addr); + break; + case NI_PMU: + ni_enable_pmu_ns_access(comp_addr); + ni_enable_pmu(comp_addr); + break; + default: + return; + } +} + +int plat_arm_ni_setup(uintptr_t global_cfg) +{ + uintptr_t vd_addr; + uintptr_t pd_addr; + uintptr_t cd_addr; + uintptr_t comp_addr; + uint32_t vd_count; + uint32_t pd_count; + uint32_t cd_count; + uint32_t comp_count; + uint32_t part; + uint32_t reg; + + reg = mmio_read_32(global_cfg + NI_PERIPHERAL_ID0); + part = reg & NI_PIDR0_PART_MASK; + reg = mmio_read_32(global_cfg + NI_PERIPHERAL_ID1); + part |= ((reg & NI_PIDR1_PART_MASK) << NI_PIDR1_PART_SHIFT); + + if (part != NI_TOWER) { + ERROR("0x%x is not supported\n", part); + return -EINVAL; + } + + vd_count = mmio_read_32(global_cfg + NI_CHILD_NODE_COUNT); + + for (uint32_t i = 0U; i < vd_count; i++) { + vd_addr = global_cfg + mmio_read_32(global_cfg + NI_CHILD_POINTER(i)); + pd_count = mmio_read_32(vd_addr + NI_CHILD_NODE_COUNT); + + for (uint32_t j = 0U; j < pd_count; j++) { + pd_addr = global_cfg + mmio_read_32(vd_addr + NI_CHILD_POINTER(j)); + cd_count = mmio_read_32(pd_addr + NI_CHILD_NODE_COUNT); + + for (uint32_t k = 0U; k < cd_count; k++) { + cd_addr = global_cfg + mmio_read_32(pd_addr + NI_CHILD_POINTER(k)); + comp_count = mmio_read_32(cd_addr + NI_CHILD_NODE_COUNT); + + for (uint32_t l = 0U; l < comp_count; l++) { + comp_addr = global_cfg + + mmio_read_32(cd_addr + NI_CHILD_POINTER(l)); + ni_setup_component(comp_addr); + } + } + } + } + + return 0; +} diff --git a/plat/arm/common/arm_pm.c b/plat/arm/common/arm_pm.c index 055ab361..498dedf3 100644 --- a/plat/arm/common/arm_pm.c +++ b/plat/arm/common/arm_pm.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015-2020, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2015-2024, Arm Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -79,12 +79,8 @@ int arm_validate_power_state(unsigned int power_state, * search if the number of entries justify the additional complexity. */ for (i = 0; !!arm_pm_idle_states[i]; i++) { -#if PSCI_OS_INIT_MODE if ((power_state & ~ARM_LAST_AT_PLVL_MASK) == arm_pm_idle_states[i]) -#else - if (power_state == arm_pm_idle_states[i]) -#endif /* __PSCI_OS_INIT_MODE__ */ break; } diff --git a/plat/arm/common/arm_sip_svc.c b/plat/arm/common/arm_sip_svc.c index 09226f4b..18e9381a 100644 --- a/plat/arm/common/arm_sip_svc.c +++ b/plat/arm/common/arm_sip_svc.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016-2023, Arm Limited and Contributors. All rights reserved. + * Copyright (c) 2016-2024, Arm Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -22,9 +22,11 @@ DEFINE_SVC_UUID2(arm_sip_svc_uid, static int arm_sip_setup(void) { +#if ENABLE_PMF if (pmf_setup() != 0) { return 1; } +#endif /* ENABLE_PMF */ #if USE_DEBUGFS @@ -60,12 +62,13 @@ static uintptr_t arm_sip_handler(unsigned int smc_fid, int call_count = 0; #if ENABLE_PMF - /* * Dispatch PMF calls to PMF SMC handler and return its return * value */ - if (is_pmf_fid(smc_fid)) { + if (is_pmf_fid_deprecated(smc_fid)) { + NOTICE("PMF Interface usage from arm-sip range is deprecated. \ + Please migrate smc call to Vendor-specific el3 range.\n"); return pmf_smc_handler(smc_fid, x1, x2, x3, x4, cookie, handle, flags); } @@ -73,8 +76,9 @@ static uintptr_t arm_sip_handler(unsigned int smc_fid, #endif /* ENABLE_PMF */ #if USE_DEBUGFS - - if (is_debugfs_fid(smc_fid)) { + if (is_debugfs_fid_deprecated(smc_fid)) { + NOTICE("Debugfs Interface usage from arm-sip range is deprecated. \ + Please migrate smc call to vendor-specific el3 range.\n"); return debugfs_smc_handler(smc_fid, x1, x2, x3, x4, cookie, handle, flags); } diff --git a/plat/arm/common/arm_transfer_list.c b/plat/arm/common/arm_transfer_list.c new file mode 100644 index 00000000..59fb0398 --- /dev/null +++ b/plat/arm/common/arm_transfer_list.c @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2024, Arm Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <plat/arm/common/plat_arm.h> +#include <platform_def.h> + +void arm_transfer_list_dyn_cfg_init(struct transfer_list_header *secure_tl) +{ + struct transfer_list_entry *te; + bl_mem_params_node_t *next_param_node = + get_bl_mem_params_node(HW_CONFIG_ID); + assert(next_param_node != NULL); + + /* + * The HW_CONFIG needs to be authenticated via the normal loading + * mechanism. Pre-allocate a TE for the configuration and update the + * load information so the configuration is loaded directly into the TE. + */ + te = transfer_list_add(secure_tl, TL_TAG_FDT, PLAT_ARM_HW_CONFIG_SIZE, + NULL); + assert(te != NULL); + + next_param_node->image_info.h.attr &= ~IMAGE_ATTRIB_SKIP_LOADING; + next_param_node->image_info.image_max_size = PLAT_ARM_HW_CONFIG_SIZE; + next_param_node->image_info.image_base = + (uintptr_t)transfer_list_entry_data(te); +} + +void arm_transfer_list_populate_ep_info(bl_mem_params_node_t *next_param_node, + struct transfer_list_header *secure_tl) +{ + uint32_t next_exe_img_id; + entry_point_info_t *ep; + struct transfer_list_entry *te; + + assert(next_param_node != NULL); + + while ((next_exe_img_id = next_param_node->next_handoff_image_id) != + INVALID_IMAGE_ID) { + next_param_node = + &bl_mem_params_desc_ptr[get_bl_params_node_index( + next_exe_img_id)]; + assert(next_param_node != NULL); + + te = transfer_list_add(secure_tl, TL_TAG_EXEC_EP_INFO64, + sizeof(entry_point_info_t), + &next_param_node->ep_info); + assert(te != NULL); + + ep = transfer_list_entry_data(te); + + if ((next_exe_img_id == BL32_IMAGE_ID) && SPMC_AT_EL3) { + /* + * Populate the BL32 image base, size and max limit in + * the entry point information, since there is no + * platform function to retrieve them in generic + * code. We choose arg2, arg3 and arg4 since the generic + * code uses arg1 for stashing the SP manifest size. The + * SPMC setup uses these arguments to update SP manifest + * with actual SP's base address and it size. + */ + ep->args.arg2 = next_param_node->image_info.image_base; + ep->args.arg3 = next_param_node->image_info.image_size; + ep->args.arg4 = + next_param_node->image_info.image_base + + next_param_node->image_info.image_max_size; + } + + next_exe_img_id = next_param_node->next_handoff_image_id; + } + + flush_dcache_range((uintptr_t)secure_tl, secure_tl->size); +} diff --git a/plat/arm/common/fconf/arm_fconf_sp.c b/plat/arm/common/fconf/arm_fconf_sp.c index 18c83c79..8655156c 100644 --- a/plat/arm/common/fconf/arm_fconf_sp.c +++ b/plat/arm/common/fconf/arm_fconf_sp.c @@ -1,10 +1,11 @@ /* - * Copyright (c) 2020-2022, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2020-2024, ARM Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ #include <assert.h> +#include <string.h> #include <common/debug.h> #include <common/desc_image_load.h> @@ -27,7 +28,7 @@ struct arm_sp_t arm_sp; int fconf_populate_arm_sp(uintptr_t config) { int sp_node, node, err; - union uuid_helper_t uuid_helper; + struct uuid uuid; unsigned int index = 0; uint32_t val32; const unsigned int sip_start = SP_PKG1_ID; @@ -68,13 +69,14 @@ int fconf_populate_arm_sp(uintptr_t config) /* Read UUID */ err = fdtw_read_uuid(dtb, sp_node, "uuid", 16, - (uint8_t *)&uuid_helper); + (uint8_t *)&uuid); if (err < 0) { ERROR("FCONF: cannot read SP uuid\n"); return -1; } - arm_sp.uuids[index] = uuid_helper; + memcpy_s(&arm_sp.uuids[index].uuid_struct, sizeof(struct uuid), + &uuid, sizeof(struct uuid)); /* Read Load address */ err = fdt_read_uint32(dtb, sp_node, "load-address", &val32); @@ -88,16 +90,16 @@ int fconf_populate_arm_sp(uintptr_t config) " %02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x" " load_addr=%lx\n", __func__, - uuid_helper.uuid_struct.time_low[0], uuid_helper.uuid_struct.time_low[1], - uuid_helper.uuid_struct.time_low[2], uuid_helper.uuid_struct.time_low[3], - uuid_helper.uuid_struct.time_mid[0], uuid_helper.uuid_struct.time_mid[1], - uuid_helper.uuid_struct.time_hi_and_version[0], - uuid_helper.uuid_struct.time_hi_and_version[1], - uuid_helper.uuid_struct.clock_seq_hi_and_reserved, - uuid_helper.uuid_struct.clock_seq_low, - uuid_helper.uuid_struct.node[0], uuid_helper.uuid_struct.node[1], - uuid_helper.uuid_struct.node[2], uuid_helper.uuid_struct.node[3], - uuid_helper.uuid_struct.node[4], uuid_helper.uuid_struct.node[5], + uuid.time_low[0], uuid.time_low[1], + uuid.time_low[2], uuid.time_low[3], + uuid.time_mid[0], uuid.time_mid[1], + uuid.time_hi_and_version[0], + uuid.time_hi_and_version[1], + uuid.clock_seq_hi_and_reserved, + uuid.clock_seq_low, + uuid.node[0], uuid.node[1], + uuid.node[2], uuid.node[3], + uuid.node[4], uuid.node[5], arm_sp.load_addr[index]); /* Read owner field only for dualroot CoT */ diff --git a/plat/arm/common/plat_arm_mbedtls_config.h b/plat/arm/common/plat_arm_mbedtls_config.h new file mode 100644 index 00000000..a5d0ec40 --- /dev/null +++ b/plat/arm/common/plat_arm_mbedtls_config.h @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2024, Arm Ltd. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef PLAT_ARM_MBEDTLS_CONFIG_H +#define PLAT_ARM_MBEDTLS_CONFIG_H + +#include <mbedtls_config-3.h> + +/** + * On Arm platforms, the ROTPK is always hashed using the SHA-256 + * algorithm. + * TODO: Update to hash the ROTPK with the selected HASH_ALG to avoid + * the need for explicitly enabling the SHA-256 configuration in mbedTLS. + */ +#define MBEDTLS_SHA256_C + +/* + * Use an implementation of SHA-256 with a smaller memory footprint + * but reduced speed. + */ +#define MBEDTLS_SHA256_SMALLER + +#endif /* PLAT_ARM_MBEDTLS_CONFIG_H */ diff --git a/plat/arm/common/plat_arm_psa_mbedtls_config.h b/plat/arm/common/plat_arm_psa_mbedtls_config.h new file mode 100644 index 00000000..fd434c98 --- /dev/null +++ b/plat/arm/common/plat_arm_psa_mbedtls_config.h @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2024, Arm Ltd. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef PLAT_ARM_PSA_MBEDTLS_CONFIG_H +#define PLAT_ARM_PSA_MBEDTLS_CONFIG_H + +#include "plat_arm_mbedtls_config.h" + +#define MBEDTLS_PSA_CRYPTO_C +#define MBEDTLS_PSA_ASSUME_EXCLUSIVE_BUFFERS + +/* + * Using PSA crypto API requires an RNG right now. If we don't define the macro + * below then we get build errors. + * + * This is a functionality gap in mbedTLS. The technical limitation is that + * psa_crypto_init() is all-or-nothing, and fixing that would require separate + * initialization of the keystore, the RNG, etc. + * + * By defining MBEDTLS_PSA_CRYPTO_EXTERNAL_RNG, we pretend using an external + * RNG. As a result, the PSA crypto init code does nothing when it comes to + * initializing the RNG, as we are supposed to take care of that ourselves. + */ +#define MBEDTLS_PSA_CRYPTO_EXTERNAL_RNG + +#endif /* PLAT_ARM_PSA_MBEDTLS_CONFIG_H */ diff --git a/plat/arm/common/plat_arm_sip_svc.c b/plat/arm/common/plat_arm_sip_svc.c index b1dab165..d6341e2c 100644 --- a/plat/arm/common/plat_arm_sip_svc.c +++ b/plat/arm/common/plat_arm_sip_svc.c @@ -1,10 +1,11 @@ /* - * Copyright (c) 2023, Arm Limited. All rights reserved. + * Copyright (c) 2023-2024, Arm Limited. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ #include <stdint.h> +#include <errno.h> #include <common/debug.h> #include <common/runtime_svc.h> @@ -12,10 +13,73 @@ #include <plat/arm/common/arm_sip_svc.h> #include <plat/common/platform.h> +#if ENABLE_RME && SPMD_SPM_AT_SEL2 +#include <lib/gpt_rme/gpt_rme.h> +#endif + #if ENABLE_SPMD_LP #include <services/el3_spmd_logical_sp.h> #endif +#if (ENABLE_RME == 1) && (defined(SPD_spmd) && SPMD_SPM_AT_SEL2 == 1) +static uint64_t plat_protect_memory(bool protect, + bool secure_origin, + const uint64_t base, + const size_t size, + void *handle) +{ + uint64_t ret = SMC_INVALID_PARAM; + uint64_t last_updated = 0; + + if (!secure_origin) { + SMC_RET1(handle, SMC_UNK); + /* Shall not be reached. */ + } + + if ((base % PAGE_SIZE_4KB) != 0U && + (size % PAGE_SIZE_4KB) != 0U) { + VERBOSE("Base address must be aligned to 4k.\n"); + SMC_RET1(handle, SMC_INVALID_PARAM); + /* Shall not be reached. */ + } + + if ((ULONG_MAX - base) < size) { + VERBOSE("Base + Size results in overflow.\n"); + SMC_RET1(handle, SMC_INVALID_PARAM); + /* Shall not be reached. */ + } + + for (uint64_t it = base; it < (base + size); it += PAGE_SIZE_4KB) { + /* + * If protect is true, add memory to secure PAS. + * Else unprotect it, making part of non-secure PAS. + */ + ret = protect + ? gpt_delegate_pas(it, PAGE_SIZE_4KB, + SMC_FROM_SECURE) + : gpt_undelegate_pas(it, PAGE_SIZE_4KB, + SMC_FROM_SECURE); + + switch (ret) { + case 0: + last_updated = it; + break; + case -EINVAL: + SMC_RET2(handle, SMC_INVALID_PARAM, last_updated); + break; /* Shall not be reached. */ + case -EPERM: + SMC_RET2(handle, SMC_DENIED, last_updated); + break; /* Shall not be reached. */ + default: + ERROR("Unexpected return\n"); + panic(); + } + } + + SMC_RET1(handle, SMC_OK); +} +#endif /* ENABLE_RME && SPMD_SPM_AT_SEL2 */ + uintptr_t plat_arm_sip_handler(uint32_t smc_fid, u_register_t x1, u_register_t x2, @@ -25,13 +89,14 @@ uintptr_t plat_arm_sip_handler(uint32_t smc_fid, void *handle, u_register_t flags) { -#if PLAT_TEST_SPM bool secure_origin; /* Determine which security state this SMC originated from */ secure_origin = is_caller_secure(flags); + (void) secure_origin; switch (smc_fid) { +#if PLAT_TEST_SPM case ARM_SIP_SET_INTERRUPT_PENDING: if (!secure_origin) { SMC_RET1(handle, SMC_UNK); @@ -42,10 +107,19 @@ uintptr_t plat_arm_sip_handler(uint32_t smc_fid, SMC_RET1(handle, SMC_OK); break; /* Not reached */ - default: +#endif + +#if (ENABLE_RME == 1) && (defined(SPD_spmd) && SPMD_SPM_AT_SEL2 == 1) + case PLAT_PROTECT_MEM_SMC64: + VERBOSE("Sip Call - Protect memory\n"); + return plat_protect_memory(true, secure_origin, x1, x2, handle); + break; + case PLAT_UNPROTECT_MEM_SMC64: + VERBOSE("Sip Call - Unprotect memory\n"); + return plat_protect_memory(false, secure_origin, x1, x2, handle); break; - } #endif + } #if ENABLE_SPMD_LP return plat_spmd_logical_sp_smc_handler(smc_fid, x1, x2, x3, x4, diff --git a/plat/arm/common/sp_min/arm_sp_min_setup.c b/plat/arm/common/sp_min/arm_sp_min_setup.c index f15c1379..4cd514bf 100644 --- a/plat/arm/common/sp_min/arm_sp_min_setup.c +++ b/plat/arm/common/sp_min/arm_sp_min_setup.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016-2020, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2016-2024, ARM Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -68,10 +68,6 @@ void arm_sp_min_early_platform_setup(void *from_bl2, uintptr_t tos_fw_config, arm_console_boot_init(); #if RESET_TO_SP_MIN - /* There are no parameters from BL2 if SP_MIN is a reset vector */ - assert(from_bl2 == NULL); - assert(plat_params_from_bl2 == NULL); - /* Populate entry point information for BL33 */ SET_PARAM_HEAD(&bl33_image_ep_info, PARAM_EP, diff --git a/plat/arm/css/common/css_pm.c b/plat/arm/css/common/css_pm.c index bb64e734..db4a1691 100644 --- a/plat/arm/css/common/css_pm.c +++ b/plat/arm/css/common/css_pm.c @@ -12,6 +12,7 @@ #include <bl31/interrupt_mgmt.h> #include <common/debug.h> #include <drivers/arm/css/css_scp.h> +#include <drivers/arm/css/dsu.h> #include <lib/cassert.h> #include <plat/arm/common/plat_arm.h> @@ -82,8 +83,12 @@ static void css_pwr_domain_on_finisher_common( * Perform the common cluster specific operations i.e enable coherency * if this cluster was off. */ - if (CSS_CLUSTER_PWR_STATE(target_state) == ARM_LOCAL_STATE_OFF) + if (CSS_CLUSTER_PWR_STATE(target_state) == ARM_LOCAL_STATE_OFF) { +#if PRESERVE_DSU_PMU_REGS + cluster_on_dsu_pmu_context_restore(); +#endif plat_arm_interconnect_enter_coherency(); + } } /******************************************************************************* @@ -131,8 +136,12 @@ static void css_power_down_common(const psci_power_state_t *target_state) plat_arm_gic_cpuif_disable(); /* Cluster is to be turned off, so disable coherency */ - if (CSS_CLUSTER_PWR_STATE(target_state) == ARM_LOCAL_STATE_OFF) + if (CSS_CLUSTER_PWR_STATE(target_state) == ARM_LOCAL_STATE_OFF) { +#if PRESERVE_DSU_PMU_REGS + cluster_off_dsu_pmu_context_save(); +#endif plat_arm_interconnect_exit_coherency(); + } } /******************************************************************************* diff --git a/plat/arm/css/sgi/include/sgi_base_platform_def.h b/plat/arm/css/sgi/include/sgi_base_platform_def.h deleted file mode 100644 index 610f1fcb..00000000 --- a/plat/arm/css/sgi/include/sgi_base_platform_def.h +++ /dev/null @@ -1,304 +0,0 @@ -/* - * Copyright (c) 2018-2023, Arm Limited and Contributors. All rights reserved. - * - * SPDX-License-Identifier: BSD-3-Clause - */ - -#ifndef SGI_BASE_PLATFORM_DEF_H -#define SGI_BASE_PLATFORM_DEF_H - -#include <lib/utils_def.h> -#include <lib/xlat_tables/xlat_tables_defs.h> -#include <plat/arm/common/arm_def.h> -#include <plat/arm/common/arm_spm_def.h> -#include <plat/arm/css/common/css_def.h> -#include <plat/common/common_def.h> - -#define PLATFORM_CORE_COUNT (CSS_SGI_CHIP_COUNT * \ - PLAT_ARM_CLUSTER_COUNT * \ - CSS_SGI_MAX_CPUS_PER_CLUSTER * \ - CSS_SGI_MAX_PE_PER_CPU) - -#define PLAT_ARM_TRUSTED_SRAM_SIZE 0x00080000 /* 512 KB */ - -/* Remote chip address offset */ -#define CSS_SGI_REMOTE_CHIP_MEM_OFFSET(n) \ - ((ULL(1) << CSS_SGI_ADDR_BITS_PER_CHIP) * (n)) - -/* - * PLAT_ARM_MMAP_ENTRIES depends on the number of entries in the - * plat_arm_mmap array defined for each BL stage. In addition to that, on - * multi-chip platforms, address regions on each of the remote chips are - * also mapped. In BL31, for instance, three address regions on the remote - * chips are accessed - secure ram, css device and soc device regions. - */ -#if defined(IMAGE_BL31) -# if SPM_MM || (SPMC_AT_EL3 && SPMC_AT_EL3_SEL0_SP) -# define PLAT_ARM_MMAP_ENTRIES (10 + ((CSS_SGI_CHIP_COUNT - 1) * 3)) -# define MAX_XLAT_TABLES (8 + ((CSS_SGI_CHIP_COUNT - 1) * 3)) -# define PLAT_SP_IMAGE_MMAP_REGIONS 12 -# define PLAT_SP_IMAGE_MAX_XLAT_TABLES 14 -# else -# define PLAT_ARM_MMAP_ENTRIES (5 + ((CSS_SGI_CHIP_COUNT - 1) * 3)) -# define MAX_XLAT_TABLES (6 + ((CSS_SGI_CHIP_COUNT - 1) * 3)) -# endif -#elif defined(IMAGE_BL32) -# define PLAT_ARM_MMAP_ENTRIES 8 -# define MAX_XLAT_TABLES 5 -#elif defined(IMAGE_BL2) -# define PLAT_ARM_MMAP_ENTRIES (11 + (CSS_SGI_CHIP_COUNT - 1)) - -/* - * MAX_XLAT_TABLES entries need to be doubled because when the address width - * exceeds 40 bits an additional level of translation is required. In case of - * multichip platforms peripherals also fall into address space with width - * > 40 bits - * - */ -# define MAX_XLAT_TABLES (7 + ((CSS_SGI_CHIP_COUNT - 1) * 2)) -#elif !USE_ROMLIB -# define PLAT_ARM_MMAP_ENTRIES 11 -# define MAX_XLAT_TABLES 7 -#else -# define PLAT_ARM_MMAP_ENTRIES 12 -# define MAX_XLAT_TABLES 6 -#endif - -/* - * PLAT_ARM_MAX_BL1_RW_SIZE is calculated using the current BL1 RW debug size - * plus a little space for growth. - */ -#define PLAT_ARM_MAX_BL1_RW_SIZE (64 * 1024) /* 64 KB */ - -/* - * PLAT_ARM_MAX_ROMLIB_RW_SIZE is define to use a full page - */ - -#if USE_ROMLIB -#define PLAT_ARM_MAX_ROMLIB_RW_SIZE 0x1000 -#define PLAT_ARM_MAX_ROMLIB_RO_SIZE 0xe000 -#else -#define PLAT_ARM_MAX_ROMLIB_RW_SIZE 0 -#define PLAT_ARM_MAX_ROMLIB_RO_SIZE 0 -#endif - -/* - * PLAT_ARM_MAX_BL2_SIZE is calculated using the current BL2 debug size plus a - * little space for growth. Additional 8KiB space is added per chip in - * order to accommodate the additional level of translation required for "TZC" - * peripheral access which lies in >4TB address space. - * - */ -#if TRUSTED_BOARD_BOOT -# define PLAT_ARM_MAX_BL2_SIZE (0x20000 + ((CSS_SGI_CHIP_COUNT - 1) * \ - 0x2000)) -#else -# define PLAT_ARM_MAX_BL2_SIZE (0x14000 + ((CSS_SGI_CHIP_COUNT - 1) * \ - 0x2000)) -#endif - -/* - * Since BL31 NOBITS overlays BL2 and BL1-RW, PLAT_ARM_MAX_BL31_SIZE is - * calculated using the current BL31 PROGBITS debug size plus the sizes of BL2 - * and BL1-RW. CSS_SGI_BL31_SIZE - is tuned with respect to the actual BL31 - * PROGBITS size which is around 64-68KB at the time this change is being made. - * A buffer of ~35KB is added to account for future expansion of the image, - * making it a total of 100KB. - */ -#define CSS_SGI_BL31_SIZE (100 * 1024) /* 100 KB */ -#define PLAT_ARM_MAX_BL31_SIZE (CSS_SGI_BL31_SIZE + \ - PLAT_ARM_MAX_BL2_SIZE + \ - PLAT_ARM_MAX_BL1_RW_SIZE) - -/* - * Size of cacheable stacks - */ -#if defined(IMAGE_BL1) -# if TRUSTED_BOARD_BOOT -# define PLATFORM_STACK_SIZE 0x1000 -# else -# define PLATFORM_STACK_SIZE 0x440 -# endif -#elif defined(IMAGE_BL2) -# if TRUSTED_BOARD_BOOT -# define PLATFORM_STACK_SIZE 0x1000 -# else -# define PLATFORM_STACK_SIZE 0x400 -# endif -#elif defined(IMAGE_BL2U) -# define PLATFORM_STACK_SIZE 0x400 -#elif defined(IMAGE_BL31) -# if SPM_MM -# define PLATFORM_STACK_SIZE 0x500 -# else -# define PLATFORM_STACK_SIZE 0x400 -# endif -#elif defined(IMAGE_BL32) -# define PLATFORM_STACK_SIZE 0x440 -#endif - -/* PL011 UART related constants */ -#define SOC_CSS_SEC_UART_BASE UL(0x2A410000) -#define SOC_CSS_NSEC_UART_BASE UL(0x2A400000) -#define SOC_CSS_UART_SIZE UL(0x10000) -#define SOC_CSS_UART_CLK_IN_HZ UL(7372800) - -/* UART related constants */ -#define PLAT_ARM_BOOT_UART_BASE SOC_CSS_SEC_UART_BASE -#define PLAT_ARM_BOOT_UART_CLK_IN_HZ SOC_CSS_UART_CLK_IN_HZ - -#define PLAT_ARM_RUN_UART_BASE SOC_CSS_SEC_UART_BASE -#define PLAT_ARM_RUN_UART_CLK_IN_HZ SOC_CSS_UART_CLK_IN_HZ - -#define PLAT_ARM_CRASH_UART_BASE SOC_CSS_SEC_UART_BASE -#define PLAT_ARM_CRASH_UART_CLK_IN_HZ SOC_CSS_UART_CLK_IN_HZ - -#define PLAT_ARM_NSTIMER_FRAME_ID 0 - -#define PLAT_ARM_TRUSTED_ROM_BASE 0x0 -#define PLAT_ARM_TRUSTED_ROM_SIZE 0x00080000 /* 512KB */ - -#define PLAT_ARM_NSRAM_BASE 0x06000000 -#define PLAT_ARM_NSRAM_SIZE 0x00080000 /* 512KB */ - -#define PLAT_ARM_DRAM2_BASE ULL(0x8080000000) -#define PLAT_ARM_DRAM2_SIZE ULL(0x180000000) - -#define PLAT_ARM_G1S_IRQ_PROPS(grp) CSS_G1S_IRQ_PROPS(grp) -#define PLAT_ARM_G0_IRQ_PROPS(grp) ARM_G0_IRQ_PROPS(grp) - -#define CSS_SGI_DEVICE_BASE (0x20000000) -#define CSS_SGI_DEVICE_SIZE (0x20000000) -#define CSS_SGI_MAP_DEVICE MAP_REGION_FLAT( \ - CSS_SGI_DEVICE_BASE, \ - CSS_SGI_DEVICE_SIZE, \ - MT_DEVICE | MT_RW | MT_SECURE) - -#define ARM_MAP_SHARED_RAM_REMOTE_CHIP(n) \ - MAP_REGION_FLAT( \ - CSS_SGI_REMOTE_CHIP_MEM_OFFSET(n) + \ - ARM_SHARED_RAM_BASE, \ - ARM_SHARED_RAM_SIZE, \ - MT_NON_CACHEABLE | MT_RW | MT_SECURE \ - ) - -#define CSS_SGI_MAP_DEVICE_REMOTE_CHIP(n) \ - MAP_REGION_FLAT( \ - CSS_SGI_REMOTE_CHIP_MEM_OFFSET(n) + \ - CSS_SGI_DEVICE_BASE, \ - CSS_SGI_DEVICE_SIZE, \ - MT_DEVICE | MT_RW | MT_SECURE \ - ) - -#define SOC_CSS_MAP_DEVICE_REMOTE_CHIP(n) \ - MAP_REGION_FLAT( \ - CSS_SGI_REMOTE_CHIP_MEM_OFFSET(n) + \ - SOC_CSS_DEVICE_BASE, \ - SOC_CSS_DEVICE_SIZE, \ - MT_DEVICE | MT_RW | MT_SECURE \ - ) - -/* Map the secure region for access from S-EL0 */ -#define PLAT_ARM_SECURE_MAP_DEVICE MAP_REGION_FLAT( \ - SOC_CSS_DEVICE_BASE, \ - SOC_CSS_DEVICE_SIZE, \ - MT_DEVICE | MT_RW | MT_SECURE | MT_USER) - -#if ENABLE_FEAT_RAS && FFH_SUPPORT -#define PLAT_SP_PRI PLAT_RAS_PRI -#else -#define PLAT_SP_PRI 0x10 -#endif - -#if (SPM_MM || (SPMC_AT_EL3 && SPMC_AT_EL3_SEL0_SP)) && ENABLE_FEAT_RAS && FFH_SUPPORT -/* - * CPER buffer memory of 128KB is reserved and it is placed adjacent to the - * memory shared between EL3 and S-EL0. - */ -#define CSS_SGI_SP_CPER_BUF_BASE (PLAT_SP_IMAGE_NS_BUF_BASE + \ - PLAT_SP_IMAGE_NS_BUF_SIZE) -#define CSS_SGI_SP_CPER_BUF_SIZE ULL(0x20000) -#define CSS_SGI_SP_CPER_BUF_MMAP MAP_REGION2( \ - CSS_SGI_SP_CPER_BUF_BASE, \ - CSS_SGI_SP_CPER_BUF_BASE, \ - CSS_SGI_SP_CPER_BUF_SIZE, \ - MT_RW_DATA | MT_NS | MT_USER, \ - PAGE_SIZE) - -/* - * Secure partition stack follows right after the memory space reserved for - * CPER buffer memory. - */ -#define PLAT_ARM_SP_IMAGE_STACK_BASE (PLAT_SP_IMAGE_NS_BUF_BASE + \ - PLAT_SP_IMAGE_NS_BUF_SIZE + \ - CSS_SGI_SP_CPER_BUF_SIZE) -#elif (SPM_MM || (SPMC_AT_EL3 && SPMC_AT_EL3_SEL0_SP)) -/* - * Secure partition stack follows right after the memory region that is shared - * between EL3 and S-EL0. - */ -#define PLAT_ARM_SP_IMAGE_STACK_BASE (PLAT_SP_IMAGE_NS_BUF_BASE + \ - PLAT_SP_IMAGE_NS_BUF_SIZE) -#endif /* SPM_MM && ENABLE_FEAT_RAS && FFH_SUPPORT */ - -/* Platform ID address */ -#define SSC_VERSION (SSC_REG_BASE + SSC_VERSION_OFFSET) -#ifndef __ASSEMBLER__ -/* SSC_VERSION related accessors */ -/* Returns the part number of the platform */ -#define GET_SGI_PART_NUM \ - GET_SSC_VERSION_PART_NUM(mmio_read_32(SSC_VERSION)) -/* Returns the configuration number of the platform */ -#define GET_SGI_CONFIG_NUM \ - GET_SSC_VERSION_CONFIG(mmio_read_32(SSC_VERSION)) -#endif /* __ASSEMBLER__ */ - -/******************************************************************************* - * Memprotect definitions - ******************************************************************************/ -/* PSCI memory protect definitions: - * This variable is stored in a non-secure flash because some ARM reference - * platforms do not have secure NVRAM. Real systems that provided MEM_PROTECT - * support must use a secure NVRAM to store the PSCI MEM_PROTECT definitions. - */ -#define PLAT_ARM_MEM_PROT_ADDR (V2M_FLASH0_BASE + \ - V2M_FLASH0_SIZE - V2M_FLASH_BLOCK_SIZE) - -/*Secure Watchdog Constants */ -#define SBSA_SECURE_WDOG_BASE UL(0x2A480000) -#define SBSA_SECURE_WDOG_TIMEOUT UL(100) - -/* Number of SCMI channels on the platform */ -#define PLAT_ARM_SCMI_CHANNEL_COUNT CSS_SGI_CHIP_COUNT - -/* - * Mapping definition of the TrustZone Controller for ARM SGI/RD platforms - * where both the DRAM regions are marked for non-secure access. This applies - * to multi-chip platforms. - */ -#define SGI_PLAT_TZC_NS_REMOTE_REGIONS_DEF(n) \ - {CSS_SGI_REMOTE_CHIP_MEM_OFFSET(n) + ARM_DRAM1_BASE, \ - CSS_SGI_REMOTE_CHIP_MEM_OFFSET(n) + ARM_DRAM1_END, \ - ARM_TZC_NS_DRAM_S_ACCESS, PLAT_ARM_TZC_NS_DEV_ACCESS}, \ - {CSS_SGI_REMOTE_CHIP_MEM_OFFSET(n) + ARM_DRAM2_BASE, \ - CSS_SGI_REMOTE_CHIP_MEM_OFFSET(n) + ARM_DRAM2_END, \ - ARM_TZC_NS_DRAM_S_ACCESS, PLAT_ARM_TZC_NS_DEV_ACCESS} - -#if SPM_MM - -/* - * Stand-alone MM logs would be routed via secure UART. Define page table - * entry for secure UART which would be common to all platforms. - */ -#define SOC_PLATFORM_SECURE_UART MAP_REGION_FLAT( \ - SOC_CSS_SEC_UART_BASE, \ - SOC_CSS_UART_SIZE, \ - MT_DEVICE | MT_RW | \ - MT_SECURE | MT_USER) - -#endif - -/* SDS ID for unusable CPU MPID list structure */ -#define SDS_ISOLATED_CPU_LIST_ID U(128) - -#endif /* SGI_BASE_PLATFORM_DEF_H */ diff --git a/plat/arm/css/sgi/include/sgi_plat.h b/plat/arm/css/sgi/include/sgi_plat.h deleted file mode 100644 index a5fbded3..00000000 --- a/plat/arm/css/sgi/include/sgi_plat.h +++ /dev/null @@ -1,13 +0,0 @@ -/* - * Copyright (c) 2020, ARM Limited and Contributors. All rights reserved. - * - * SPDX-License-Identifier: BSD-3-Clause - */ - -#ifndef SGI_PLAT_H -#define SGI_PLAT_H - -/* BL31 platform setup common to all SGI based platforms */ -void sgi_bl31_common_platform_setup(void); - -#endif /* SGI_PLAT_H */ diff --git a/plat/arm/css/sgi/include/sgi_sdei.h b/plat/arm/css/sgi/include/sgi_sdei.h deleted file mode 100644 index f380122b..00000000 --- a/plat/arm/css/sgi/include/sgi_sdei.h +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright (c) 2021, ARM Limited and Contributors. All rights reserved. - * - * SPDX-License-Identifier: BSD-3-Clause - */ - -#ifndef SGI_SDEI_H -#define SGI_SDEI_H - -#if SDEI_SUPPORT - -/* ARM SDEI dynamic shared event numbers */ -#define SGI_SDEI_DS_EVENT_0 U(804) -#define SGI_SDEI_DS_EVENT_1 U(805) - -#define PLAT_ARM_PRIVATE_SDEI_EVENTS \ - SDEI_DEFINE_EVENT_0(ARM_SDEI_SGI), \ - SDEI_EXPLICIT_EVENT(SGI_SDEI_DS_EVENT_0, SDEI_MAPF_CRITICAL), \ - SDEI_EXPLICIT_EVENT(SGI_SDEI_DS_EVENT_1, SDEI_MAPF_CRITICAL), - -#define PLAT_ARM_SHARED_SDEI_EVENTS - -#endif /* SDEI_SUPPORT */ - -#endif /* SGI_SDEI_H */ diff --git a/plat/arm/css/sgi/include/sgi_soc_css_def.h b/plat/arm/css/sgi/include/sgi_soc_css_def.h deleted file mode 100644 index f78b45a2..00000000 --- a/plat/arm/css/sgi/include/sgi_soc_css_def.h +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright (c) 2022, ARM Limited and Contributors. All rights reserved. - * - * SPDX-License-Identifier: BSD-3-Clause - */ - -#ifndef SGI_SOC_CSS_DEF_H -#define SGI_SOC_CSS_DEF_H - -#include <lib/utils_def.h> -#include <plat/arm/board/common/v2m_def.h> -#include <plat/arm/soc/common/soc_css_def.h> -#include <plat/common/common_def.h> - -/* - * Definitions common to all ARM CSSv1-based development platforms - */ - -/* Platform ID address */ -#define BOARD_CSS_PLAT_ID_REG_ADDR UL(0x7ffe00e0) - -/* Platform ID related accessors */ -#define BOARD_CSS_PLAT_ID_REG_ID_MASK 0x0f -#define BOARD_CSS_PLAT_ID_REG_ID_SHIFT 0x0 -#define BOARD_CSS_PLAT_TYPE_EMULATOR 0x02 - -#ifndef __ASSEMBLER__ - -#include <lib/mmio.h> - -#define BOARD_CSS_GET_PLAT_TYPE(addr) \ - ((mmio_read_32(addr) & BOARD_CSS_PLAT_ID_REG_ID_MASK) \ - >> BOARD_CSS_PLAT_ID_REG_ID_SHIFT) - -#endif /* __ASSEMBLER__ */ - -#define MAX_IO_DEVICES 3 -#define MAX_IO_HANDLES 4 - -/* Reserve the last block of flash for PSCI MEM PROTECT flag */ -#define PLAT_ARM_FLASH_IMAGE_BASE V2M_FLASH0_BASE -#define PLAT_ARM_FLASH_IMAGE_MAX_SIZE (V2M_FLASH0_SIZE - V2M_FLASH_BLOCK_SIZE) - -#define PLAT_ARM_NVM_BASE V2M_FLASH0_BASE -#define PLAT_ARM_NVM_SIZE (V2M_FLASH0_SIZE - V2M_FLASH_BLOCK_SIZE) - -#endif /* SGI_SOC_CSS_DEF_H */ diff --git a/plat/arm/css/sgi/include/sgi_soc_css_def_v2.h b/plat/arm/css/sgi/include/sgi_soc_css_def_v2.h deleted file mode 100644 index d659ae51..00000000 --- a/plat/arm/css/sgi/include/sgi_soc_css_def_v2.h +++ /dev/null @@ -1,207 +0,0 @@ -/* - * Copyright (c) 2021-2022, ARM Limited and Contributors. All rights reserved. - * - * SPDX-License-Identifier: BSD-3-Clause - */ - -#ifndef SGI_SOC_CSS_DEF_V2_H -#define SGI_SOC_CSS_DEF_V2_H - -#include <lib/utils_def.h> -#include <plat/common/common_def.h> - -/* - * Definitions common to all ARM CSS SoCs - */ - -/* Following covers ARM CSS SoC Peripherals */ - -#define SOC_SYSTEM_PERIPH_BASE UL(0x0C000000) -#define SOC_SYSTEM_PERIPH_SIZE UL(0x02000000) - -#define SOC_PLATFORM_PERIPH_BASE UL(0x0E000000) -#define SOC_PLATFORM_PERIPH_SIZE UL(0x02000000) - -#define SOC_CSS_PCIE_CONTROL_BASE UL(0x0ef20000) - -/* Memory controller */ -#define SOC_MEMCNTRL_BASE UL(0x10000000) -#define SOC_MEMCNTRL_SIZE UL(0x10000000) - -/* SoC NIC-400 Global Programmers View (GPV) */ -#define SOC_CSS_NIC400_BASE UL(0x0ED00000) - -#define SOC_CSS_NIC400_USB_EHCI U(0) -#define SOC_CSS_NIC400_TLX_MASTER U(1) -#define SOC_CSS_NIC400_USB_OHCI U(2) -#define SOC_CSS_NIC400_PL354_SMC U(3) -/* - * The apb4_bridge controls access to: - * - the PCIe configuration registers - * - the MMU units for USB, HDLCD and DMA - */ -#define SOC_CSS_NIC400_APB4_BRIDGE U(4) - -/* Non-volatile counters */ -#define SOC_TRUSTED_NVCTR_BASE UL(0x0EE70000) -#define TFW_NVCTR_BASE (SOC_TRUSTED_NVCTR_BASE + 0x0000) -#define TFW_NVCTR_SIZE U(4) -#define NTFW_CTR_BASE (SOC_TRUSTED_NVCTR_BASE + 0x0004) -#define NTFW_CTR_SIZE U(4) - -/* Keys */ -#define SOC_KEYS_BASE UL(0x0EE80000) -#define TZ_PUB_KEY_HASH_BASE (SOC_KEYS_BASE + 0x0000) -#define TZ_PUB_KEY_HASH_SIZE U(32) -#define HU_KEY_BASE (SOC_KEYS_BASE + 0x0020) -#define HU_KEY_SIZE U(16) -#define END_KEY_BASE (SOC_KEYS_BASE + 0x0044) -#define END_KEY_SIZE U(32) - -/* Base Element RAM error definitions */ -#define SOC_NS_RAM_ERR_REC_BASE UL(0x2A4C0000) -#define NS_RAM_ECC_CE_INT U(87) -#define NS_RAM_ECC_UE_INT U(88) - -#define SOC_PLATFORM_PERIPH_MAP_DEVICE MAP_REGION_FLAT( \ - SOC_PLATFORM_PERIPH_BASE, \ - SOC_PLATFORM_PERIPH_SIZE, \ - MT_DEVICE | MT_RW | MT_SECURE) - -#if SPM_MM -/* - * Memory map definition for the platform peripheral memory region that is - * accessible from S-EL0 (with secure user mode access). - */ -#define SOC_PLATFORM_PERIPH_MAP_DEVICE_USER \ - MAP_REGION_FLAT( \ - SOC_PLATFORM_PERIPH_BASE, \ - SOC_PLATFORM_PERIPH_SIZE, \ - MT_DEVICE | MT_RW | MT_SECURE | MT_USER) -#endif - -#define SOC_SYSTEM_PERIPH_MAP_DEVICE MAP_REGION_FLAT( \ - SOC_SYSTEM_PERIPH_BASE, \ - SOC_SYSTEM_PERIPH_SIZE, \ - MT_DEVICE | MT_RW | MT_SECURE) - -#define SOC_MEMCNTRL_MAP_DEVICE MAP_REGION_FLAT( \ - SOC_MEMCNTRL_BASE, \ - SOC_MEMCNTRL_SIZE, \ - MT_DEVICE | MT_RW | MT_SECURE) - -#define SOC_MEMCNTRL_MAP_DEVICE_REMOTE_CHIP(n) \ - MAP_REGION_FLAT( \ - CSS_SGI_REMOTE_CHIP_MEM_OFFSET(n) + SOC_MEMCNTRL_BASE, \ - SOC_MEMCNTRL_SIZE, \ - MT_DEVICE | MT_RW | MT_SECURE) - -/* - * The bootsec_bridge controls access to a bunch of peripherals, e.g. the UARTs. - */ -#define SOC_CSS_NIC400_BOOTSEC_BRIDGE U(5) -#define SOC_CSS_NIC400_BOOTSEC_BRIDGE_UART1 UL(1 << 12) - -/* - * Required platform porting definitions common to all ARM CSS SoCs - */ -/* 2MB used for SCP DDR retraining */ -#define PLAT_ARM_SCP_TZC_DRAM1_SIZE UL(0x00200000) - -/* V2M motherboard system registers & offsets */ -#define V2M_SYSREGS_BASE UL(0x0C010000) -#define V2M_SYS_LED U(0x8) - -/* - * V2M sysled bit definitions. The values written to this - * register are defined in arch.h & runtime_svc.h. Only - * used by the primary cpu to diagnose any cold boot issues. - * - * SYS_LED[0] - Security state (S=0/NS=1) - * SYS_LED[2:1] - Exception Level (EL3-EL0) - * SYS_LED[7:3] - Exception Class (Sync/Async & origin) - * - */ -#define V2M_SYS_LED_SS_SHIFT U(0) -#define V2M_SYS_LED_EL_SHIFT U(1) -#define V2M_SYS_LED_EC_SHIFT U(3) - -#define V2M_SYS_LED_SS_MASK U(0x01) -#define V2M_SYS_LED_EL_MASK U(0x03) -#define V2M_SYS_LED_EC_MASK U(0x1f) - -/* NOR Flash */ -#define V2M_FLASH0_BASE UL(0x08000000) -#define V2M_FLASH0_SIZE UL(0x04000000) -#define V2M_FLASH_BLOCK_SIZE UL(0x00040000) /* 256 KB */ - -/* - * The flash can be mapped either as read-only or read-write. - * - * If it is read-write then it should also be mapped as device memory because - * NOR flash programming involves sending a fixed, ordered sequence of commands. - * - * If it is read-only then it should also be mapped as: - * - Normal memory, because reading from NOR flash is transparent, it is like - * reading from RAM. - * - Non-executable by default. If some parts of the flash need to be executable - * then platform code is responsible for re-mapping the appropriate portion - * of it as executable. - */ -#define V2M_MAP_FLASH0_RW MAP_REGION_FLAT(V2M_FLASH0_BASE,\ - V2M_FLASH0_SIZE, \ - MT_DEVICE | MT_RW | MT_SECURE) - -#define V2M_MAP_FLASH0_RO MAP_REGION_FLAT(V2M_FLASH0_BASE,\ - V2M_FLASH0_SIZE, \ - MT_RO_DATA | MT_SECURE) - -#define SGI_MAP_FLASH0_RO MAP_REGION_FLAT(V2M_FLASH0_BASE,\ - V2M_FLASH0_SIZE, \ - MT_DEVICE | MT_RO | MT_SECURE) - -/* Platform ID address */ -#define BOARD_CSS_PLAT_ID_REG_ADDR UL(0x0EFE00E0) - -/* Platform ID related accessors */ -#define BOARD_CSS_PLAT_ID_REG_ID_MASK U(0x0F) -#define BOARD_CSS_PLAT_ID_REG_ID_SHIFT U(0x00) -#define BOARD_CSS_PLAT_ID_REG_VERSION_MASK U(0xF00) -#define BOARD_CSS_PLAT_ID_REG_VERSION_SHIFT U(0x08) -#define BOARD_CSS_PLAT_TYPE_RTL U(0x00) -#define BOARD_CSS_PLAT_TYPE_FPGA U(0x01) -#define BOARD_CSS_PLAT_TYPE_EMULATOR U(0x02) -#define BOARD_CSS_PLAT_TYPE_FVP U(0x03) - -#ifndef __ASSEMBLER__ - -#include <lib/mmio.h> - -#define BOARD_CSS_GET_PLAT_TYPE(addr) \ - ((mmio_read_32(addr) & BOARD_CSS_PLAT_ID_REG_ID_MASK) \ - >> BOARD_CSS_PLAT_ID_REG_ID_SHIFT) - -#endif /* __ASSEMBLER__ */ - - -#define MAX_IO_DEVICES U(3) -#define MAX_IO_HANDLES U(4) - -/* Reserve the last block of flash for PSCI MEM PROTECT flag */ -#define PLAT_ARM_FLASH_IMAGE_BASE V2M_FLASH0_BASE -#define PLAT_ARM_FLASH_IMAGE_MAX_SIZE (V2M_FLASH0_SIZE - V2M_FLASH_BLOCK_SIZE) - -#if ARM_GPT_SUPPORT -/* - * Offset of the FIP in the GPT image. BL1 component uses this option - * as it does not load the partition table to get the FIP base - * address. At sector 34 by default (i.e. after reserved sectors 0-33) - * Offset = 34 * 512(sector size) = 17408 i.e. 0x4400 - */ -#define PLAT_ARM_FIP_OFFSET_IN_GPT 0x4400 -#endif /* ARM_GPT_SUPPORT */ - -#define PLAT_ARM_NVM_BASE V2M_FLASH0_BASE -#define PLAT_ARM_NVM_SIZE (V2M_FLASH0_SIZE - V2M_FLASH_BLOCK_SIZE) - -#endif /* SGI_SOC_CSS_DEF_V2_H */ diff --git a/plat/arm/css/sgi/include/sgi_soc_platform_def.h b/plat/arm/css/sgi/include/sgi_soc_platform_def.h deleted file mode 100644 index 3b8d9c66..00000000 --- a/plat/arm/css/sgi/include/sgi_soc_platform_def.h +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright (c) 2020-2022, ARM Limited and Contributors. All rights reserved. - * - * SPDX-License-Identifier: BSD-3-Clause - */ - -#ifndef SGI_SOC_PLATFORM_DEF_H -#define SGI_SOC_PLATFORM_DEF_H - -#include <plat/arm/board/common/v2m_def.h> -#include <plat/arm/soc/common/soc_css_def.h> -#include <sgi_base_platform_def.h> -#include <sgi_soc_css_def.h> - -/* Map the System registers to access from S-EL0 */ -#define CSS_SYSTEMREG_DEVICE_BASE (0x1C010000) -#define CSS_SYSTEMREG_DEVICE_SIZE (0x00010000) -#define PLAT_ARM_SECURE_MAP_SYSTEMREG MAP_REGION_FLAT( \ - CSS_SYSTEMREG_DEVICE_BASE, \ - CSS_SYSTEMREG_DEVICE_SIZE, \ - (MT_DEVICE | MT_RW | \ - MT_SECURE | MT_USER)) - -/* Map the NOR2 Flash to access from S-EL0 */ -#define CSS_NOR2_FLASH_DEVICE_BASE (0x10000000) -#define CSS_NOR2_FLASH_DEVICE_SIZE (0x04000000) -#define PLAT_ARM_SECURE_MAP_NOR2 MAP_REGION_FLAT( \ - CSS_NOR2_FLASH_DEVICE_BASE, \ - CSS_NOR2_FLASH_DEVICE_SIZE, \ - (MT_DEVICE | MT_RW | \ - MT_SECURE | MT_USER)) - -#endif /* SGI_SOC_PLATFORM_DEF_H */ diff --git a/plat/arm/css/sgi/include/sgi_soc_platform_def_v2.h b/plat/arm/css/sgi/include/sgi_soc_platform_def_v2.h deleted file mode 100644 index 20dd6825..00000000 --- a/plat/arm/css/sgi/include/sgi_soc_platform_def_v2.h +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright (c) 2020-2021, ARM Limited and Contributors. All rights reserved. - * - * SPDX-License-Identifier: BSD-3-Clause - */ - -#ifndef SGI_SOC_PLATFORM_DEF_V2_H -#define SGI_SOC_PLATFORM_DEF_V2_H - -#include <sgi_base_platform_def.h> -#include <sgi_soc_css_def_v2.h> - -/* Map the System registers to access from S-EL0 */ -#define CSS_SYSTEMREG_DEVICE_BASE (0x0C010000) -#define CSS_SYSTEMREG_DEVICE_SIZE (0x00010000) -#define PLAT_ARM_SECURE_MAP_SYSTEMREG MAP_REGION_FLAT( \ - CSS_SYSTEMREG_DEVICE_BASE, \ - CSS_SYSTEMREG_DEVICE_SIZE, \ - (MT_DEVICE | MT_RW | \ - MT_SECURE | MT_USER)) - -/* Map the NOR2 Flash to access from S-EL0 */ -#define CSS_NOR2_FLASH_DEVICE_BASE (0x001054000000) -#define CSS_NOR2_FLASH_DEVICE_SIZE (0x000004000000) -#define PLAT_ARM_SECURE_MAP_NOR2 MAP_REGION_FLAT( \ - CSS_NOR2_FLASH_DEVICE_BASE, \ - CSS_NOR2_FLASH_DEVICE_SIZE, \ - (MT_DEVICE | MT_RW | \ - MT_SECURE | MT_USER)) - -#endif /* SGI_SOC_PLATFORM_DEF_V2_H */ diff --git a/plat/arm/css/sgi/sgi_bl31_setup.c b/plat/arm/css/sgi/sgi_bl31_setup.c deleted file mode 100644 index 7aa7b34a..00000000 --- a/plat/arm/css/sgi/sgi_bl31_setup.c +++ /dev/null @@ -1,136 +0,0 @@ -/* - * Copyright (c) 2018-2023, Arm Limited and Contributors. All rights reserved. - * - * SPDX-License-Identifier: BSD-3-Clause - */ - -#include <assert.h> - -#include <libfdt.h> - -#include <common/bl_common.h> -#include <common/debug.h> -#include <drivers/arm/css/css_mhu_doorbell.h> -#include <drivers/arm/css/scmi.h> -#include <plat/arm/common/plat_arm.h> - -#include <plat/common/platform.h> - -#include <plat/arm/css/common/css_pm.h> - -#include <sgi_ras.h> -#include <sgi_variant.h> - -sgi_platform_info_t sgi_plat_info; - -static scmi_channel_plat_info_t sgi575_scmi_plat_info = { - .scmi_mbx_mem = CSS_SCMI_PAYLOAD_BASE, - .db_reg_addr = PLAT_CSS_MHU_BASE + CSS_SCMI_MHU_DB_REG_OFF, - .db_preserve_mask = 0xfffffffe, - .db_modify_mask = 0x1, - .ring_doorbell = &mhu_ring_doorbell, -}; - -static scmi_channel_plat_info_t plat_rd_scmi_info[] = { - { - .scmi_mbx_mem = CSS_SCMI_PAYLOAD_BASE, - .db_reg_addr = PLAT_CSS_MHU_BASE + SENDER_REG_SET(0), - .db_preserve_mask = 0xfffffffe, - .db_modify_mask = 0x1, - .ring_doorbell = &mhuv2_ring_doorbell, - }, - #if (CSS_SGI_CHIP_COUNT > 1) - { - .scmi_mbx_mem = CSS_SCMI_PAYLOAD_BASE + - CSS_SGI_REMOTE_CHIP_MEM_OFFSET(1), - .db_reg_addr = PLAT_CSS_MHU_BASE - + CSS_SGI_REMOTE_CHIP_MEM_OFFSET(1) + SENDER_REG_SET(0), - .db_preserve_mask = 0xfffffffe, - .db_modify_mask = 0x1, - .ring_doorbell = &mhuv2_ring_doorbell, - }, - #endif - #if (CSS_SGI_CHIP_COUNT > 2) - { - .scmi_mbx_mem = CSS_SCMI_PAYLOAD_BASE + - CSS_SGI_REMOTE_CHIP_MEM_OFFSET(2), - .db_reg_addr = PLAT_CSS_MHU_BASE + - CSS_SGI_REMOTE_CHIP_MEM_OFFSET(2) + SENDER_REG_SET(0), - .db_preserve_mask = 0xfffffffe, - .db_modify_mask = 0x1, - .ring_doorbell = &mhuv2_ring_doorbell, - }, - #endif - #if (CSS_SGI_CHIP_COUNT > 3) - { - .scmi_mbx_mem = CSS_SCMI_PAYLOAD_BASE + - CSS_SGI_REMOTE_CHIP_MEM_OFFSET(3), - .db_reg_addr = PLAT_CSS_MHU_BASE + - CSS_SGI_REMOTE_CHIP_MEM_OFFSET(3) + SENDER_REG_SET(0), - .db_preserve_mask = 0xfffffffe, - .db_modify_mask = 0x1, - .ring_doorbell = &mhuv2_ring_doorbell, - }, - #endif -}; - -scmi_channel_plat_info_t *plat_css_get_scmi_info(unsigned int channel_id) -{ - if (sgi_plat_info.platform_id == RD_N1E1_EDGE_SID_VER_PART_NUM || - sgi_plat_info.platform_id == RD_V1_SID_VER_PART_NUM || - sgi_plat_info.platform_id == RD_N2_SID_VER_PART_NUM || - sgi_plat_info.platform_id == RD_V2_SID_VER_PART_NUM || - sgi_plat_info.platform_id == RD_N2_CFG1_SID_VER_PART_NUM || - sgi_plat_info.platform_id == RD_N2_CFG3_SID_VER_PART_NUM) { - if (channel_id >= ARRAY_SIZE(plat_rd_scmi_info)) - panic(); - return &plat_rd_scmi_info[channel_id]; - } - else if (sgi_plat_info.platform_id == SGI575_SSC_VER_PART_NUM) - return &sgi575_scmi_plat_info; - else - panic(); -} - -void bl31_early_platform_setup2(u_register_t arg0, u_register_t arg1, - u_register_t arg2, u_register_t arg3) -{ - sgi_plat_info.platform_id = plat_arm_sgi_get_platform_id(); - sgi_plat_info.config_id = plat_arm_sgi_get_config_id(); - sgi_plat_info.multi_chip_mode = plat_arm_sgi_get_multi_chip_mode(); - - arm_bl31_early_platform_setup((void *)arg0, arg1, arg2, (void *)arg3); -} - -void sgi_bl31_common_platform_setup(void) -{ - arm_bl31_platform_setup(); - - /* Configure the warm reboot SGI for primary core */ - css_setup_cpu_pwr_down_intr(); - -#if CSS_SYSTEM_GRACEFUL_RESET - /* Register priority level handlers for reboot */ - ehf_register_priority_handler(PLAT_REBOOT_PRI, - css_reboot_interrupt_handler); -#endif -} - -const plat_psci_ops_t *plat_arm_psci_override_pm_ops(plat_psci_ops_t *ops) -{ - /* - * For RD-E1-Edge, only CPU power ON/OFF, PSCI platform callbacks are - * supported. - */ - if (((sgi_plat_info.platform_id == RD_N1E1_EDGE_SID_VER_PART_NUM) && - (sgi_plat_info.config_id == RD_E1_EDGE_CONFIG_ID))) { - ops->cpu_standby = NULL; - ops->system_off = NULL; - ops->system_reset = NULL; - ops->get_sys_suspend_power_state = NULL; - ops->pwr_domain_suspend = NULL; - ops->pwr_domain_suspend_finish = NULL; - } - - return css_scmi_override_pm_ops(ops); -} diff --git a/plat/aspeed/ast2700/include/platform_def.h b/plat/aspeed/ast2700/include/platform_def.h index 8be26c37..e6681152 100644 --- a/plat/aspeed/ast2700/include/platform_def.h +++ b/plat/aspeed/ast2700/include/platform_def.h @@ -21,9 +21,6 @@ #define PLATFORM_CORE_COUNT (PLATFORM_CLUSTER_COUNT * \ PLATFORM_CORE_COUNT_PER_CLUSTER) -/* arch timer */ -#define PLAT_SYSCNT_CLKIN_HZ U(1600000000) - /* power domain */ #define PLAT_MAX_PWR_LVL U(1) #define PLAT_NUM_PWR_DOMAINS U(5) @@ -55,4 +52,12 @@ #define CONSOLE_UART_CLKIN_HZ U(1846153) #define CONSOLE_UART_BAUDRATE U(115200) +/* CLK information */ +#define CLKIN_25M UL(25000000) + +#define PLAT_CLK_GATE_NUM U(29) +#define PLAT_CLK_HPLL (PLAT_CLK_GATE_NUM + 5) +#define PLAT_CLK_DPLL (PLAT_CLK_GATE_NUM + 6) +#define PLAT_CLK_MPLL (PLAT_CLK_GATE_NUM + 7) + #endif /* PLATFORM_DEF_H */ diff --git a/plat/aspeed/ast2700/include/platform_reg.h b/plat/aspeed/ast2700/include/platform_reg.h index 7f268654..3c164a4c 100644 --- a/plat/aspeed/ast2700/include/platform_reg.h +++ b/plat/aspeed/ast2700/include/platform_reg.h @@ -19,6 +19,10 @@ /* CPU-die SCU */ #define SCU_CPU_BASE U(0x12c02000) +#define SCU_CPU_HW_STRAP1 (SCU_CPU_BASE + 0x010) +#define SCU_CPU_HPLL (SCU_CPU_BASE + 0x300) +#define SCU_CPU_DPLL (SCU_CPU_BASE + 0x308) +#define SCU_CPU_MPLL (SCU_CPU_BASE + 0x310) #define SCU_CPU_SMP_EP0 (SCU_CPU_BASE + 0x780) #define SCU_CPU_SMP_EP1 (SCU_CPU_BASE + 0x788) #define SCU_CPU_SMP_EP2 (SCU_CPU_BASE + 0x790) diff --git a/plat/aspeed/ast2700/plat_bl31_setup.c b/plat/aspeed/ast2700/plat_bl31_setup.c index 92a48ff8..087b4795 100644 --- a/plat/aspeed/ast2700/plat_bl31_setup.c +++ b/plat/aspeed/ast2700/plat_bl31_setup.c @@ -4,6 +4,7 @@ * SPDX-License-Identifier: BSD-3-Clause */ +#include <errno.h> #include <arch.h> #include <common/debug.h> #include <common/desc_image_load.h> @@ -112,3 +113,90 @@ entry_point_info_t *bl31_plat_get_next_image_ep_info(uint32_t type) return ep_info; } + +/* + * Clock divider/multiplier configuration struct. + * For H-PLL and M-PLL the formula is + * (Output Frequency) = CLKIN * ((M + 1) / (N + 1)) / (P + 1) + * M - Numerator + * N - Denumerator + * P - Post Divider + * They have the same layout in their control register. + * + */ +union plat_pll_reg { + uint32_t w; + struct { + uint16_t m : 13; /* bit[12:0] */ + uint8_t n : 6; /* bit[18:13] */ + uint8_t p : 4; /* bit[22:19] */ + uint8_t off : 1; /* bit[23] */ + uint8_t bypass : 1; /* bit[24] */ + uint8_t reset : 1; /* bit[25] */ + uint8_t reserved : 6; /* bit[31:26] */ + } b; +}; + +static uint32_t plat_get_pll_rate(int pll_idx) +{ + union plat_pll_reg pll_reg; + uint32_t mul = 1, div = 1; + uint32_t rate = 0; + + switch (pll_idx) { + case PLAT_CLK_HPLL: + pll_reg.w = mmio_read_32(SCU_CPU_HPLL); + break; + case PLAT_CLK_DPLL: + pll_reg.w = mmio_read_32(SCU_CPU_DPLL); + break; + case PLAT_CLK_MPLL: + pll_reg.w = mmio_read_32(SCU_CPU_MPLL); + break; + default: + ERROR("%s: invalid PSP clock source (%d)\n", __func__, pll_idx); + return -EINVAL; + } + + if (pll_idx == PLAT_CLK_HPLL && ((mmio_read_32(SCU_CPU_HW_STRAP1) & GENMASK(3, 2)) != 0U)) { + switch ((mmio_read_32(SCU_CPU_HW_STRAP1) & GENMASK(3, 2)) >> 2) { + case 1U: + rate = 1900000000; + break; + case 2U: + rate = 1800000000; + break; + case 3U: + rate = 1700000000; + break; + default: + rate = 2000000000; + break; + } + } else { + if (pll_reg.b.bypass == 0U) { + if (pll_idx == PLAT_CLK_MPLL) { + /* F = 25Mhz * [M / (n + 1)] / (p + 1) */ + mul = (pll_reg.b.m) / ((pll_reg.b.n + 1)); + div = (pll_reg.b.p + 1); + } else { + /* F = 25Mhz * [(M + 2) / 2 * (n + 1)] / (p + 1) */ + mul = (pll_reg.b.m + 1) / ((pll_reg.b.n + 1) * 2); + div = (pll_reg.b.p + 1); + } + } + + rate = ((CLKIN_25M * mul) / div); + } + + return rate; +} + +unsigned int plat_get_syscnt_freq2(void) +{ + if (mmio_read_32(SCU_CPU_HW_STRAP1) & BIT(4)) { + return plat_get_pll_rate(PLAT_CLK_HPLL); + } else { + return plat_get_pll_rate(PLAT_CLK_MPLL); + } +} diff --git a/plat/aspeed/ast2700/plat_helpers.S b/plat/aspeed/ast2700/plat_helpers.S index c6d987e6..e4a283c0 100644 --- a/plat/aspeed/ast2700/plat_helpers.S +++ b/plat/aspeed/ast2700/plat_helpers.S @@ -59,12 +59,6 @@ poll_smp_mbox_go: br x0 endfunc plat_secondary_cold_boot_setup -/* unsigned int plat_get_syscnt_freq2(void); */ -func plat_get_syscnt_freq2 - mov_imm w0, PLAT_SYSCNT_CLKIN_HZ - ret -endfunc plat_get_syscnt_freq2 - /* int plat_crash_console_init(void); */ func plat_crash_console_init mov_imm x0, CONSOLE_UART_BASE diff --git a/plat/brcm/common/brcm_bl31_setup.c b/plat/brcm/common/brcm_bl31_setup.c index d3fa83da..6eef1d4c 100644 --- a/plat/brcm/common/brcm_bl31_setup.c +++ b/plat/brcm/common/brcm_bl31_setup.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015-2020, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2015-2024, ARM Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -235,8 +235,6 @@ void brcm_bl31_platform_setup(void) ******************************************************************************/ void brcm_bl31_plat_runtime_setup(void) { - console_switch_state(CONSOLE_FLAG_RUNTIME); - /* Initialize the runtime console */ bcm_console_runtime_init(); } diff --git a/plat/common/aarch32/plat_common.c b/plat/common/aarch32/plat_common.c index 2c1a8fa0..89791712 100644 --- a/plat/common/aarch32/plat_common.c +++ b/plat/common/aarch32/plat_common.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016-2018, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2016-2024, ARM Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -7,6 +7,14 @@ #include <lib/xlat_tables/xlat_mmu_helpers.h> #include <plat/common/platform.h> +/* Pointer and function to register platform function to load alernate images */ +const struct plat_try_images_ops *plat_try_img_ops; + +void plat_setup_try_img_ops(const struct plat_try_images_ops *plat_try_ops) +{ + plat_try_img_ops = plat_try_ops; +} + /* * The following platform setup functions are weakly defined. They * provide typical implementations that may be re-used by multiple @@ -14,7 +22,6 @@ */ #pragma weak bl32_plat_enable_mmu - void bl32_plat_enable_mmu(uint32_t flags) { enable_mmu_svc_mon(flags); diff --git a/plat/common/aarch64/crash_console_helpers.S b/plat/common/aarch64/crash_console_helpers.S index 75b42089..1a500919 100644 --- a/plat/common/aarch64/crash_console_helpers.S +++ b/plat/common/aarch64/crash_console_helpers.S @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018-2019, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2018-2024, ARM Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -100,7 +100,7 @@ endfunc plat_crash_console_init * int plat_crash_console_putc(char c) * Prints the character on all consoles registered with the console * framework that have CONSOLE_FLAG_CRASH set. Note that this is only - * helpful for crashes that occur after the platform intialization code + * helpful for crashes that occur after the platform initialization code * has registered a console. Platforms using this implementation need to * ensure that all console drivers they use that have the CRASH flag set * support this (i.e. are written in assembly and comply to the register diff --git a/plat/common/aarch64/plat_common.c b/plat/common/aarch64/plat_common.c index ab99b158..7a228b97 100644 --- a/plat/common/aarch64/plat_common.c +++ b/plat/common/aarch64/plat_common.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2023, Arm Limited and Contributors. All rights reserved. + * Copyright (c) 2014-2024, Arm Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -17,6 +17,14 @@ #include <lib/xlat_tables/xlat_mmu_helpers.h> #include <plat/common/platform.h> +/* Pointer and function to register platform function to load alernate images */ +const struct plat_try_images_ops *plat_try_img_ops; + +void plat_setup_try_img_ops(const struct plat_try_images_ops *plat_try_ops) +{ + plat_try_img_ops = plat_try_ops; +} + /* * The following platform setup functions are weakly defined. They * provide typical implementations that may be re-used by multiple @@ -35,7 +43,6 @@ void bl31_plat_runtime_setup(void) { - console_switch_state(CONSOLE_FLAG_RUNTIME); } /* @@ -71,12 +78,19 @@ int plat_sdei_validate_entry_point(uintptr_t ep, unsigned int client_mode) const char *get_el_str(unsigned int el) { - if (el == MODE_EL3) { + switch (el) { + case MODE_EL3: return "EL3"; - } else if (el == MODE_EL2) { + case MODE_EL2: return "EL2"; + case MODE_EL1: + return "EL1"; + case MODE_EL0: + return "EL0"; + default: + assert(false); + return NULL; } - return "EL1"; } #if FFH_SUPPORT diff --git a/plat/common/plat_bl1_common.c b/plat/common/plat_bl1_common.c index bcf9f895..ff0e0828 100644 --- a/plat/common/plat_bl1_common.c +++ b/plat/common/plat_bl1_common.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015-2021, Arm Limited and Contributors. All rights reserved. + * Copyright (c) 2015-2024, Arm Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -80,10 +80,8 @@ int bl1_plat_mem_check(uintptr_t mem_base, unsigned int mem_size, */ int bl1_plat_handle_post_image_load(unsigned int image_id) { - meminfo_t *bl2_secram_layout; - meminfo_t *bl1_secram_layout; + meminfo_t *bl1_tzram_layout; image_desc_t *image_desc; - entry_point_info_t *ep_info; if (image_id != BL2_IMAGE_ID) return 0; @@ -92,26 +90,41 @@ int bl1_plat_handle_post_image_load(unsigned int image_id) image_desc = bl1_plat_get_image_desc(BL2_IMAGE_ID); assert(image_desc != NULL); - /* Get the entry point info */ - ep_info = &image_desc->ep_info; - /* Find out how much free trusted ram remains after BL1 load */ - bl1_secram_layout = bl1_plat_sec_mem_layout(); + bl1_tzram_layout = bl1_plat_sec_mem_layout(); /* - * Create a new layout of memory for BL2 as seen by BL1 i.e. - * tell it the amount of total and free memory available. - * This layout is created at the first free address visible - * to BL2. BL2 will read the memory layout before using its - * memory for other purposes. + * Convey this information to BL2 by storing the layout at the first free + * address visible to BL2. */ - bl2_secram_layout = (meminfo_t *) bl1_secram_layout->total_base; - - bl1_calc_bl2_mem_layout(bl1_secram_layout, bl2_secram_layout); + bl1_plat_calc_bl2_layout(bl1_tzram_layout, + (meminfo_t *)bl1_tzram_layout->total_base); - ep_info->args.arg1 = (uintptr_t)bl2_secram_layout; + image_desc->ep_info.args.arg1 = (uintptr_t)bl1_tzram_layout->total_base; VERBOSE("BL1: BL2 memory layout address = %p\n", - (void *) bl2_secram_layout); + (void *)image_desc->ep_info.args.arg1); + return 0; } + +/******************************************************************************* + * Helper utility to calculate the BL2 memory layout taking into consideration + * the BL1 RW data assuming that it is at the top of the memory layout. + ******************************************************************************/ +void bl1_plat_calc_bl2_layout(const meminfo_t *bl1_mem_layout, + meminfo_t *bl2_mem_layout) +{ + assert(bl1_mem_layout != NULL); + assert(bl2_mem_layout != NULL); + + /* + * Remove BL1 RW data from the scope of memory visible to BL2. + * This is assuming BL1 RW data is at the top of bl1_mem_layout. + */ + assert(BL1_RW_BASE > bl1_mem_layout->total_base); + bl2_mem_layout->total_base = bl1_mem_layout->total_base; + bl2_mem_layout->total_size = BL1_RW_BASE - bl1_mem_layout->total_base; + + flush_dcache_range((uintptr_t)bl2_mem_layout, sizeof(meminfo_t)); +} diff --git a/plat/common/plat_bl_common.c b/plat/common/plat_bl_common.c index 89b77ba6..a603f2b1 100644 --- a/plat/common/plat_bl_common.c +++ b/plat/common/plat_bl_common.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018-2020, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2018-2024, Arm Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -24,7 +24,6 @@ #pragma weak bl2_plat_preload_setup #pragma weak bl2_plat_handle_pre_image_load #pragma weak bl2_plat_handle_post_image_load -#pragma weak plat_try_next_boot_source #pragma weak plat_get_enc_key_info #pragma weak plat_is_smccc_feature_available #pragma weak plat_get_soc_version @@ -69,11 +68,6 @@ int bl2_plat_handle_post_image_load(unsigned int image_id) return 0; } -int plat_try_next_boot_source(void) -{ - return 0; -} - /* * Weak implementation to provide dummy decryption key only for test purposes, * platforms must override this API for any real world firmware encryption diff --git a/plat/common/plat_gicv3.c b/plat/common/plat_gicv3.c index baa70e0b..d0c7a31e 100644 --- a/plat/common/plat_gicv3.c +++ b/plat/common/plat_gicv3.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015-2023, Arm Limited and Contributors. All rights reserved. + * Copyright (c) 2015-2024, Arm Limited and Contributors. All rights reserved. * Portions copyright (c) 2021-2022, ProvenRun S.A.S. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause @@ -344,6 +344,11 @@ unsigned int plat_ic_set_priority_mask(unsigned int mask) return gicv3_set_pmr(mask); } +unsigned int plat_ic_deactivate_priority(unsigned int mask) +{ + return gicv3_deactivate_priority(mask); +} + unsigned int plat_ic_get_interrupt_id(unsigned int raw) { unsigned int id = raw & INT_ID_MASK; diff --git a/plat/hisilicon/hikey/hikey_bl31_setup.c b/plat/hisilicon/hikey/hikey_bl31_setup.c index 7d008e74..55b425c4 100644 --- a/plat/hisilicon/hikey/hikey_bl31_setup.c +++ b/plat/hisilicon/hikey/hikey_bl31_setup.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2018, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2017-2024, Arm Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -149,7 +149,3 @@ void bl31_platform_setup(void) hisi_ipc_init(); hisi_pwrc_setup(); } - -void bl31_plat_runtime_setup(void) -{ -} diff --git a/plat/hisilicon/hikey/platform.mk b/plat/hisilicon/hikey/platform.mk index 807a9159..b67d3ff9 100644 --- a/plat/hisilicon/hikey/platform.mk +++ b/plat/hisilicon/hikey/platform.mk @@ -1,5 +1,5 @@ # -# Copyright (c) 2017-2023, ARM Limited and Contributors. All rights reserved. +# Copyright (c) 2017-2024, Arm Limited and Contributors. All rights reserved. # # SPDX-License-Identifier: BSD-3-Clause # @@ -152,13 +152,13 @@ $(BUILD_PLAT)/bl1/hikey_rotpk.o: $(ROTPK_HASH) $(BUILD_PLAT)/bl2/hikey_rotpk.o: $(ROTPK_HASH) certificates: $(ROT_KEY) -$(ROT_KEY): | $(BUILD_PLAT) - @echo " OPENSSL $@" - $(Q)${OPENSSL_BIN_PATH}/openssl genrsa 2048 > $@ 2>/dev/null +$(ROT_KEY): | $$(@D)/ + $(s)echo " OPENSSL $@" + $(q)${OPENSSL_BIN_PATH}/openssl genrsa 2048 > $@ 2>/dev/null -$(ROTPK_HASH): $(ROT_KEY) - @echo " OPENSSL $@" - $(Q)${OPENSSL_BIN_PATH}/openssl rsa -in $< -pubout -outform DER 2>/dev/null |\ +$(ROTPK_HASH): $(ROT_KEY) | $$(@D)/ + $(s)echo " OPENSSL $@" + $(q)${OPENSSL_BIN_PATH}/openssl rsa -in $< -pubout -outform DER 2>/dev/null |\ ${OPENSSL_BIN_PATH}/openssl dgst -sha256 -binary > $@ 2>/dev/null endif diff --git a/plat/hisilicon/hikey960/hikey960_bl31_setup.c b/plat/hisilicon/hikey960/hikey960_bl31_setup.c index 159eee9e..6e80347a 100644 --- a/plat/hisilicon/hikey960/hikey960_bl31_setup.c +++ b/plat/hisilicon/hikey960/hikey960_bl31_setup.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2023, Arm Limited and Contributors. All rights reserved. + * Copyright (c) 2017-2024, Arm Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ diff --git a/plat/hisilicon/hikey960/platform.mk b/plat/hisilicon/hikey960/platform.mk index fd11a4da..c278d8ed 100644 --- a/plat/hisilicon/hikey960/platform.mk +++ b/plat/hisilicon/hikey960/platform.mk @@ -1,5 +1,5 @@ # -# Copyright (c) 2017-2023, ARM Limited and Contributors. All rights reserved. +# Copyright (c) 2017-2024, Arm Limited and Contributors. All rights reserved. # # SPDX-License-Identifier: BSD-3-Clause # @@ -145,13 +145,13 @@ $(BUILD_PLAT)/bl1/hikey960_rotpk.o: $(ROTPK_HASH) $(BUILD_PLAT)/bl2/hikey960_rotpk.o: $(ROTPK_HASH) certificates: $(ROT_KEY) -$(ROT_KEY): | $(BUILD_PLAT) - @echo " OPENSSL $@" - $(Q)${OPENSSL_BIN_PATH}/openssl genrsa 2048 > $@ 2>/dev/null +$(ROT_KEY): | $$(@D)/ + $(s)echo " OPENSSL $@" + $(q)${OPENSSL_BIN_PATH}/openssl genrsa 2048 > $@ 2>/dev/null -$(ROTPK_HASH): $(ROT_KEY) - @echo " OPENSSL $@" - $(Q)${OPENSSL_BIN_PATH}/openssl rsa -in $< -pubout -outform DER 2>/dev/null |\ +$(ROTPK_HASH): $(ROT_KEY) | $$(@D)/ + $(s)echo " OPENSSL $@" + $(q)${OPENSSL_BIN_PATH}/openssl rsa -in $< -pubout -outform DER 2>/dev/null |\ ${OPENSSL_BIN_PATH}/openssl dgst -sha256 -binary > $@ 2>/dev/null endif diff --git a/plat/hisilicon/poplar/bl31_plat_setup.c b/plat/hisilicon/poplar/bl31_plat_setup.c index fe60ddcb..5f4a18a4 100644 --- a/plat/hisilicon/poplar/bl31_plat_setup.c +++ b/plat/hisilicon/poplar/bl31_plat_setup.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2019, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2017-2024, Arm Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -118,11 +118,6 @@ void bl31_platform_setup(void) hisi_tzpc_sec_init(); } -void bl31_plat_runtime_setup(void) -{ - /* do nothing */ -} - void bl31_plat_arch_setup(void) { plat_configure_mmu_el3(BL31_BASE, diff --git a/plat/hisilicon/poplar/include/hi3798cv200.h b/plat/hisilicon/poplar/include/hi3798cv200.h index e31f4b3e..802dec53 100644 --- a/plat/hisilicon/poplar/include/hi3798cv200.h +++ b/plat/hisilicon/poplar/include/hi3798cv200.h @@ -38,7 +38,11 @@ /* SCTL */ #define REG_BASE_SCTL (0xF8000000) +#define REG_SC_SYSRES (0x0004) #define REG_SC_GEN12 (0x00B0) +#define REG_SC_LOCKEN (0x020C) + +#define SC_UNLOCK_MAGIC (0x4F50454E) /* CRG */ #define REG_BASE_CRG (0xF8A22000) diff --git a/plat/hisilicon/poplar/plat_pm.c b/plat/hisilicon/poplar/plat_pm.c index 67ebca1c..77fc5327 100644 --- a/plat/hisilicon/poplar/plat_pm.c +++ b/plat/hisilicon/poplar/plat_pm.c @@ -92,14 +92,18 @@ static void poplar_pwr_domain_suspend_finish( static void __dead2 poplar_system_off(void) { ERROR("Poplar System Off: operation not handled.\n"); + /* Turn off watchdog0 before panic() */ + mmio_write_32((uintptr_t)(HISI_WDG0_BASE + 0xc00), 0x1ACCE551); + mmio_write_32((uintptr_t)(HISI_WDG0_BASE + 0x8), 0x00000000); panic(); } static void __dead2 poplar_system_reset(void) { - mmio_write_32((uintptr_t)(HISI_WDG0_BASE + 0xc00), 0x1ACCE551); - mmio_write_32((uintptr_t)(HISI_WDG0_BASE + 0x0), 0x00000100); - mmio_write_32((uintptr_t)(HISI_WDG0_BASE + 0x8), 0x00000003); + /* Unlock Sysctrl critical registers */ + mmio_write_32((uintptr_t)(REG_BASE_SCTL + REG_SC_LOCKEN), SC_UNLOCK_MAGIC); + /* Assert system reset */ + mmio_write_32((uintptr_t)(REG_BASE_SCTL + REG_SC_SYSRES), 0xfee1dead); wfi(); ERROR("Poplar System Reset: operation not handled.\n"); diff --git a/plat/imx/common/imx8_helpers.S b/plat/imx/common/imx8_helpers.S index 19293bfe..eb938336 100644 --- a/plat/imx/common/imx8_helpers.S +++ b/plat/imx/common/imx8_helpers.S @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015-2018, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2015-2024, ARM Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -86,6 +86,51 @@ func plat_calc_core_pos ret endfunc plat_calc_core_pos + /* ---------------------------------------------- + * function to handle platform specific reset. + * ---------------------------------------------- + */ +func plat_reset_handler +#if defined(PLAT_imx8ulp) + /* enable the 512KB cache by default */ + mov x0, #IMX_SIM1_BASE + /* + * if the RVBADDR is ROM entry, that means we did + * NOT switch the L2 cache to 512KB. default is 256K config, + * so skip + */ + ldr w1, [x0, #0x5c] + cmp w1, #0x1000 + b.eq 1f + add x0, x0, #0x30 + ldr w1, [x0] + /* if already 512KB config, skip */ + tbnz w1, #4, 1f + ldr w1, [x0] + orr w1, w1, #0x10 + str w1, [x0] + orr w1, w1, #0x10000 + str w1, [x0] + b . +1: mrs x0, CORTEX_A35_CPUECTLR_EL1 + orr x0, x0, #(0x1 << 0) + orr x0, x0, #(0x1 << 3) + msr CORTEX_A35_CPUECTLR_EL1, x0 + + mrs x0, CORTEX_A35_L2ECTLR_EL1 + orr x0, x0, #(0x1 << 0) + msr CORTEX_A35_L2ECTLR_EL1, x0 + isb +#endif + /* enable EL2 cpuectlr RW access */ + mov x0, #0x73 + msr actlr_el3, x0 + msr actlr_el2, x0 + isb + + ret +endfunc plat_reset_handler + /* --------------------------------------------- * function to get the entrypoint. * --------------------------------------------- diff --git a/plat/imx/common/imx_bl31_common.c b/plat/imx/common/imx_bl31_common.c new file mode 100644 index 00000000..f6d7e248 --- /dev/null +++ b/plat/imx/common/imx_bl31_common.c @@ -0,0 +1,23 @@ +/* + * Copyright 2023-2024 NXP + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <imx_plat_common.h> + +uint32_t plat_get_spsr_for_bl33_entry(void) +{ + unsigned long el_status; + unsigned long mode; + uint32_t spsr; + + /* figure out what mode we enter the non-secure world */ + el_status = read_id_aa64pfr0_el1() >> ID_AA64PFR0_EL2_SHIFT; + el_status &= ID_AA64PFR0_ELX_MASK; + + mode = (el_status) ? MODE_EL2 : MODE_EL1; + + spsr = SPSR_64(mode, MODE_SP_ELX, DISABLE_ALL_EXCEPTIONS); + return spsr; +} diff --git a/plat/imx/common/imx_common.c b/plat/imx/common/imx_common.c new file mode 100644 index 00000000..01f354ac --- /dev/null +++ b/plat/imx/common/imx_common.c @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2024, Pengutronix, Inc. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ +#include <errno.h> +#include <stdint.h> + +#include <common/bl_common.h> +#include <common/desc_image_load.h> + +#include <plat_common.h> + +/* + * This function checks if @arg0 can safely be accessed as a pointer + * and if it does, it fills in @bl32_info and @bl33_info with data + * found in @arg0. + * + * Returns 0 when @arg0 can be used as entry point info and a negative + * error code otherwise. + */ +int imx_bl31_params_parse(uintptr_t arg0, uintptr_t ocram_base, + uintptr_t ocram_size, + entry_point_info_t *bl32_info, + entry_point_info_t *bl33_info) +{ + bl_params_t *v2 = (void *)(uintptr_t)arg0; + + if (arg0 & 0x3) { + return -EINVAL; + } + + if (arg0 < ocram_base || arg0 >= ocram_base + ocram_size) { + return -EINVAL; + } + + if (v2->h.version != PARAM_VERSION_2) { + return -EINVAL; + } + + if (v2->h.type != PARAM_BL_PARAMS) { + return -EINVAL; + } + + bl31_params_parse_helper(arg0, bl32_info, bl33_info); + + return 0; +} diff --git a/plat/imx/common/imx_sip_handler.c b/plat/imx/common/imx_sip_handler.c index ec8631a4..49fdacf0 100644 --- a/plat/imx/common/imx_sip_handler.c +++ b/plat/imx/common/imx_sip_handler.c @@ -9,14 +9,24 @@ #include <stdint.h> #include <services/std_svc.h> #include <string.h> -#include <platform_def.h> +#include <common/build_message.h> #include <common/debug.h> #include <common/runtime_svc.h> +#include <platform_def.h> #include <imx_sip_svc.h> #include <lib/el3_runtime/context_mgmt.h> #include <lib/mmio.h> #include <sci/sci.h> +#if defined(PLAT_imx8mn) || defined(PLAT_imx8mp) +/* + * Defined in + * table 11. ROM event log buffer address location + * AN12853 "i.MX ROMs Log Events" + */ +#define ROM_LOG_BUFFER_ADDR 0x9E0 +#endif + #if defined(PLAT_imx8qm) || defined(PLAT_imx8qx) #ifdef PLAT_imx8qm @@ -177,12 +187,82 @@ int imx_src_handler(uint32_t smc_fid, } #endif /* defined(PLAT_imx8mm) || defined(PLAT_imx8mq) */ +#if defined(PLAT_imx8mn) || defined(PLAT_imx8mp) +static bool is_secondary_boot(void) +{ + uint32_t *rom_log_addr = (uint32_t *)ROM_LOG_BUFFER_ADDR; + bool is_secondary = false; + uint32_t *rom_log; + uint8_t event_id; + + /* If the ROM event log pointer is not valid. */ + if (*rom_log_addr < 0x900000 || *rom_log_addr >= 0xB00000 || + *rom_log_addr & 0x3) { + return false; + } + + /* Parse the ROM event ID version 2 log */ + rom_log = (uint32_t *)(uintptr_t)(*rom_log_addr); + for (size_t i = 0; i < 128; i++) { + event_id = rom_log[i] >> 24; + switch (event_id) { + case 0x00: /* End of list */ + return is_secondary; + /* Log entries with 1 parameter, skip 1 */ + case 0x80: /* Perform the device initialization */ + case 0x81: /* The boot device initialization completes */ + case 0x82: /* Execute boot device driver pre-config */ + case 0x8F: /* The boot device initialization fails */ + case 0x90: /* Start to read data from boot device */ + case 0x91: /* Reading data from boot device completes */ + case 0x9F: /* Reading data from boot device fails */ + i += 1; + continue; + /* Log entries with 2 parameters, skip 2 */ + case 0xA0: /* Image authentication result */ + case 0xC0: /* Jump to the boot image soon */ + i += 2; + continue; + /* Booted the primary boot image */ + case 0x50: + is_secondary = false; + continue; + /* Booted the secondary boot image */ + case 0x51: + is_secondary = true; + continue; + } + } + + return is_secondary; +} + +int imx_src_handler(uint32_t smc_fid, + u_register_t x1, + u_register_t x2, + u_register_t x3, + void *handle) +{ + switch (x1) { + case IMX_SIP_SRC_SET_SECONDARY_BOOT: + /* we do support that on these SoCs */ + break; + case IMX_SIP_SRC_IS_SECONDARY_BOOT: + return is_secondary_boot(); + default: + return SMC_UNK; + }; + + return 0; +} +#endif /* defined(PLAT_imx8mn) || defined(PLAT_imx8mp) */ + static uint64_t imx_get_commit_hash(u_register_t x2, u_register_t x3, u_register_t x4) { /* Parse the version_string */ - char *parse = (char *)version_string; + char *parse = (char *)build_version_string; uint64_t hash = 0; do { @@ -253,3 +333,16 @@ int imx_kernel_entry_handler(uint32_t smc_fid, return 0; } + +#if defined(PLAT_imx8ulp) +int imx_hifi_xrdc(uint32_t smc_fid) +{ + mmio_setbits_32(IMX_SIM2_BASE + 0x8, BIT_32(19) | BIT_32(17) | BIT_32(18)); + mmio_clrbits_32(IMX_SIM2_BASE + 0x8, BIT_32(16)); + + extern int xrdc_apply_hifi_config(void); + xrdc_apply_hifi_config(); + + return 0; +} +#endif diff --git a/plat/imx/common/imx_sip_svc.c b/plat/imx/common/imx_sip_svc.c index 6d6633cf..75b709ad 100644 --- a/plat/imx/common/imx_sip_svc.c +++ b/plat/imx/common/imx_sip_svc.c @@ -1,14 +1,17 @@ /* - * Copyright (c) 2015-2023, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2015-2024, ARM Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ #include <stdint.h> + #include <common/debug.h> #include <common/runtime_svc.h> +#include <drivers/scmi-msg.h> #include <lib/pmf/pmf.h> #include <tools_share/uuid.h> + #include <imx_sip_svc.h> static int32_t imx_sip_setup(void) @@ -29,6 +32,17 @@ static uintptr_t imx_sip_handler(unsigned int smc_fid, case IMX_SIP_AARCH32: SMC_RET1(handle, imx_kernel_entry_handler(smc_fid, x1, x2, x3, x4)); break; +#if defined(PLAT_imx8ulp) + case IMX_SIP_SCMI: + scmi_smt_fastcall_smc_entry(0); + SMC_RET1(handle, 0); + break; + case IMX_SIP_HIFI_XRDC: + SMC_RET1(handle, imx_hifi_xrdc(smc_fid)); + break; + case IMX_SIP_DDR_DVFS: + return dram_dvfs_handler(smc_fid, handle, x1, x2, x3); +#endif #if defined(PLAT_imx8mq) case IMX_SIP_GET_SOC_INFO: SMC_RET1(handle, imx_soc_info_handler(smc_fid, x1, x2, x3)); @@ -60,12 +74,14 @@ static uintptr_t imx_sip_handler(unsigned int smc_fid, case IMX_SIP_MISC_SET_TEMP: SMC_RET1(handle, imx_misc_set_temp_handler(smc_fid, x1, x2, x3, x4)); #endif -#if defined(PLAT_imx8mm) || defined(PLAT_imx8mq) +#if defined(PLAT_imx8mm) || defined(PLAT_imx8mq) || defined(PLAT_imx8mn) || \ + defined(PLAT_imx8mp) case IMX_SIP_SRC: SMC_RET1(handle, imx_src_handler(smc_fid, x1, x2, x3, handle)); break; #endif -#if defined(PLAT_imx8mm) || defined(PLAT_imx8mn) || defined(PLAT_imx8mp) +#if defined(PLAT_imx8mm) || defined(PLAT_imx8mn) || defined(PLAT_imx8mp) || \ + defined(PLAT_imx8mq) case IMX_SIP_HAB: SMC_RET1(handle, imx_hab_handler(smc_fid, x1, x2, x3, x4)); break; diff --git a/plat/imx/common/include/imx_plat_common.h b/plat/imx/common/include/imx_plat_common.h new file mode 100644 index 00000000..8ec9481d --- /dev/null +++ b/plat/imx/common/include/imx_plat_common.h @@ -0,0 +1,16 @@ +/* + * Copyright 2023-2024 NXP + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef IMX_PLAT_COMMON_H +#define IMX_PLAT_COMMON_H + +#include <stdint.h> + +#include <arch_helpers.h> + +uint32_t plat_get_spsr_for_bl33_entry(void); + +#endif /*IMX_PLAT_COMMON_H */ diff --git a/plat/imx/common/include/imx_sip_svc.h b/plat/imx/common/include/imx_sip_svc.h index 0e91c717..404a8295 100644 --- a/plat/imx/common/include/imx_sip_svc.h +++ b/plat/imx/common/include/imx_sip_svc.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015-2023, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2015-2024, ARM Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -52,13 +52,26 @@ int imx_kernel_entry_handler(uint32_t smc_fid, u_register_t x1, u_register_t x2, u_register_t x3, u_register_t x4); + +#define IMX_SIP_SCMI 0xC20000FE + +#define IMX_SIP_HIFI_XRDC 0xC200000E + #if defined(PLAT_imx8mq) int imx_soc_info_handler(uint32_t smc_fid, u_register_t x1, u_register_t x2, u_register_t x3); int imx_gpc_handler(uint32_t smc_fid, u_register_t x1, u_register_t x2, u_register_t x3); +#if IMX_DRAM_RETENTION int dram_dvfs_handler(uint32_t smc_fid, void *handle, u_register_t x1, u_register_t x2, u_register_t x3); +#else +static inline int dram_dvfs_handler(uint32_t smc_fid, void *handle, + u_register_t x1, u_register_t x2, u_register_t x3) +{ + SMC_RET1(handle, SMC_UNK); +} +#endif #endif #if defined(PLAT_imx8mm) || defined(PLAT_imx8mn) || defined(PLAT_imx8mp) int dram_dvfs_handler(uint32_t smc_fid, void *handle, @@ -68,7 +81,9 @@ int imx_gpc_handler(uint32_t smc_fid, u_register_t x1, u_register_t x2, u_register_t x3); #endif -#if defined(PLAT_imx8mm) || defined(PLAT_imx8mq) +#if defined(PLAT_imx8mm) || defined(PLAT_imx8mq) || defined(PLAT_imx8mn) || \ + defined(PLAT_imx8mp) + int imx_src_handler(uint32_t smc_fid, u_register_t x1, u_register_t x2, u_register_t x3, void *handle); #endif @@ -94,5 +109,12 @@ int imx_misc_set_temp_handler(uint32_t smc_fid, u_register_t x1, uint64_t imx_buildinfo_handler(uint32_t smc_fid, u_register_t x1, u_register_t x2, u_register_t x3, u_register_t x4); +int scmi_handler(uint32_t smc_fid, u_register_t x1, u_register_t x2, u_register_t x3); +int imx_hifi_xrdc(uint32_t smc_fid); + +#if defined(PLAT_imx8ulp) +int dram_dvfs_handler(uint32_t smc_fid, void *handle, + u_register_t x1, u_register_t x2, u_register_t x3); +#endif #endif /* __IMX_SIP_SVC_H__ */ diff --git a/plat/imx/common/include/plat_common.h b/plat/imx/common/include/plat_common.h new file mode 100644 index 00000000..6f412225 --- /dev/null +++ b/plat/imx/common/include/plat_common.h @@ -0,0 +1,17 @@ +/* + * Copyright (c) 2024, Pengutronix, Inc. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ +#ifndef PLAT_COMMON_H +#define PLAT_COMMON_H + +#include <stdint.h> +#include <common/bl_common.h> + +int imx_bl31_params_parse(uintptr_t arg0, uintptr_t ocram_base, + uintptr_t ocram_size, + entry_point_info_t *bl32_info, + entry_point_info_t *bl33_info); + +#endif /* PLAT_COMMON_H */ diff --git a/plat/imx/imx7/common/imx7.mk b/plat/imx/imx7/common/imx7.mk index 156c55dd..a7e8fe81 100644 --- a/plat/imx/imx7/common/imx7.mk +++ b/plat/imx/imx7/common/imx7.mk @@ -1,5 +1,5 @@ # -# Copyright (c) 2018-2023, Arm Limited and Contributors. All rights reserved. +# Copyright (c) 2018-2024, Arm Limited and Contributors. All rights reserved. # # SPDX-License-Identifier: BSD-3-Clause # @@ -71,21 +71,20 @@ ROT_KEY = $(BUILD_PLAT)/rot_key.pem ROTPK_HASH = $(BUILD_PLAT)/rotpk_sha256.bin $(eval $(call add_define_val,ROTPK_HASH,'"$(ROTPK_HASH)"')) -$(eval $(call MAKE_LIB_DIRS)) $(BUILD_PLAT)/bl2/imx7_rotpk.o: $(ROTPK_HASH) certificates: $(ROT_KEY) -$(ROT_KEY): | $(BUILD_PLAT) - @echo " OPENSSL $@" - @if [ ! -f $(ROT_KEY) ]; then \ +$(ROT_KEY): | $$(@D)/ + $(s)echo " OPENSSL $@" + $(q)if [ ! -f $(ROT_KEY) ]; then \ ${OPENSSL_BIN_PATH}/openssl genrsa 2048 > $@ 2>/dev/null; \ fi -$(ROTPK_HASH): $(ROT_KEY) - @echo " OPENSSL $@" - $(Q)${OPENSSL_BIN_PATH}/openssl rsa -in $< -pubout -outform DER 2>/dev/null |\ +$(ROTPK_HASH): $(ROT_KEY) | $$(@D)/ + $(s)echo " OPENSSL $@" + $(q)${OPENSSL_BIN_PATH}/openssl rsa -in $< -pubout -outform DER 2>/dev/null |\ ${OPENSSL_BIN_PATH}/openssl dgst -sha256 -binary > $@ 2>/dev/null endif diff --git a/plat/imx/imx8m/ddr/clock.c b/plat/imx/imx8m/ddr/clock.c index 31f2f560..21a1b684 100644 --- a/plat/imx/imx8m/ddr/clock.c +++ b/plat/imx/imx8m/ddr/clock.c @@ -91,12 +91,16 @@ void dram_pll_init(unsigned int drate) case 4000: mmio_write_32(DRAM_PLL_CTRL + 0x4, (250 << 12) | (3 << 4) | 1); break; + case 3734: case 3733: case 3732: mmio_write_32(DRAM_PLL_CTRL + 0x4, (311 << 12) | (4 << 4) | 1); break; + case 3600: + mmio_write_32(DRAM_PLL_CTRL + 0x4, (300 << 12) | (8 << 4) | 0); + break; case 3200: - mmio_write_32(DRAM_PLL_CTRL + 0x4, (200 << 12) | (3 << 4) | 1); + mmio_write_32(DRAM_PLL_CTRL + 0x4, (300 << 12) | (9 << 4) | 0); break; case 2400: mmio_write_32(DRAM_PLL_CTRL + 0x4, (300 << 12) | (3 << 4) | 2); diff --git a/plat/imx/imx8m/imx8m_ccm.c b/plat/imx/imx8m/imx8m_ccm.c index 10a00c99..6b14446d 100644 --- a/plat/imx/imx8m/imx8m_ccm.c +++ b/plat/imx/imx8m/imx8m_ccm.c @@ -17,16 +17,16 @@ static struct imx_uart { } imx8m_uart_info[] = { { /* UART 1 */ .ccm_reg = 0x4490, - .uart_base = 0x30860000, + .uart_base = IMX_UART1_BASE, }, { /* UART 2 */ .ccm_reg = 0x44a0, - .uart_base = 0x30890000, + .uart_base = IMX_UART2_BASE, }, { /* UART 3 */ .ccm_reg = 0x44b0, - .uart_base = 0x30880000, + .uart_base = IMX_UART3_BASE, }, { /* UART 4 */ .ccm_reg = 0x44c0, - .uart_base = 0x30a60000, + .uart_base = IMX_UART4_BASE, } }; diff --git a/plat/imx/imx8m/imx8m_measured_boot.c b/plat/imx/imx8m/imx8m_measured_boot.c index bfcd6ceb..159be00a 100644 --- a/plat/imx/imx8m/imx8m_measured_boot.c +++ b/plat/imx/imx8m/imx8m_measured_boot.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022-2023, Arm Limited. All rights reserved. + * Copyright (c) 2022-2024, Arm Limited. All rights reserved. * Copyright (c) 2022, Linaro. * * SPDX-License-Identifier: BSD-3-Clause @@ -9,6 +9,7 @@ #include "./include/imx8m_measured_boot.h" #include <drivers/measured_boot/event_log/event_log.h> +#include <drivers/measured_boot/metadata.h> #include <plat/arm/common/plat_arm.h> /* Event Log data */ @@ -16,11 +17,11 @@ static uint8_t event_log[PLAT_IMX_EVENT_LOG_MAX_SIZE]; /* FVP table with platform specific image IDs, names and PCRs */ static const event_log_metadata_t imx8m_event_log_metadata[] = { - { BL31_IMAGE_ID, EVLOG_BL31_STRING, PCR_0 }, - { BL32_IMAGE_ID, EVLOG_BL32_STRING, PCR_0 }, - { BL32_EXTRA1_IMAGE_ID, EVLOG_BL32_EXTRA1_STRING, PCR_0 }, - { BL32_EXTRA2_IMAGE_ID, EVLOG_BL32_EXTRA2_STRING, PCR_0 }, - { BL33_IMAGE_ID, EVLOG_BL33_STRING, PCR_0 }, + { BL31_IMAGE_ID, MBOOT_BL31_IMAGE_STRING, PCR_0 }, + { BL32_IMAGE_ID, MBOOT_BL32_IMAGE_STRING, PCR_0 }, + { BL32_EXTRA1_IMAGE_ID, MBOOT_BL32_EXTRA1_IMAGE_STRING, PCR_0 }, + { BL32_EXTRA2_IMAGE_ID, MBOOT_BL32_EXTRA2_IMAGE_STRING, PCR_0 }, + { BL33_IMAGE_ID, MBOOT_BL33_IMAGE_STRING, PCR_0 }, { EVLOG_INVALID_ID, NULL, (unsigned int)(-1) } /* Terminator */ }; diff --git a/plat/imx/imx8m/imx8mm/imx8mm_bl31_setup.c b/plat/imx/imx8m/imx8mm/imx8mm_bl31_setup.c index dc9dd594..03edc6e4 100644 --- a/plat/imx/imx8m/imx8mm/imx8mm_bl31_setup.c +++ b/plat/imx/imx8m/imx8mm/imx8mm_bl31_setup.c @@ -30,6 +30,7 @@ #include <imx8m_ccm.h> #include <imx8m_csu.h> #include <imx8m_snvs.h> +#include <plat_common.h> #include <plat_imx8.h> #define TRUSTY_PARAMS_LEN_BYTES (4096*2) @@ -61,7 +62,7 @@ static const struct aipstz_cfg aipstz[] = { {0}, }; -static const struct imx_rdc_cfg rdc[] = { +static struct imx_rdc_cfg rdc[] = { /* Master domain assignment */ RDC_MDAn(RDC_MDA_M4, DID1), @@ -77,11 +78,31 @@ static const struct imx_rdc_cfg rdc[] = { static const struct imx_csu_cfg csu_cfg[] = { /* peripherals csl setting */ - CSU_CSLx(0x1, CSU_SEC_LEVEL_0, UNLOCKED), + CSU_CSLx(CSU_CSL_RDC, CSU_SEC_LEVEL_3, LOCKED), + CSU_CSLx(CSU_CSL_TZASC, CSU_SEC_LEVEL_5, LOCKED), + CSU_CSLx(CSU_CSL_CSU, CSU_SEC_LEVEL_5, LOCKED), /* master HP0~1 */ /* SA setting */ + CSU_SA(CSU_SA_M4, NON_SEC_ACCESS, LOCKED), + CSU_SA(CSU_SA_SDMA1, NON_SEC_ACCESS, LOCKED), + CSU_SA(CSU_SA_PCIE_CTRL1, NON_SEC_ACCESS, LOCKED), + CSU_SA(CSU_SA_USB1, NON_SEC_ACCESS, LOCKED), + CSU_SA(CSU_SA_USB2, NON_SEC_ACCESS, LOCKED), + CSU_SA(CSU_SA_VPU, NON_SEC_ACCESS, LOCKED), + CSU_SA(CSU_SA_GPU, NON_SEC_ACCESS, LOCKED), + CSU_SA(CSU_SA_APBHDMA, NON_SEC_ACCESS, LOCKED), + CSU_SA(CSU_SA_ENET, NON_SEC_ACCESS, LOCKED), + CSU_SA(CSU_SA_USDHC1, NON_SEC_ACCESS, LOCKED), + CSU_SA(CSU_SA_USDHC2, NON_SEC_ACCESS, LOCKED), + CSU_SA(CSU_SA_USDHC3, NON_SEC_ACCESS, LOCKED), + CSU_SA(CSU_SA_HUGO, NON_SEC_ACCESS, LOCKED), + CSU_SA(CSU_SA_DAP, NON_SEC_ACCESS, LOCKED), + CSU_SA(CSU_SA_SDMA2, NON_SEC_ACCESS, LOCKED), + CSU_SA(CSU_SA_SDMA3, NON_SEC_ACCESS, LOCKED), + CSU_SA(CSU_SA_LCDIF, NON_SEC_ACCESS, LOCKED), + CSU_SA(CSU_SA_CSI, NON_SEC_ACCESS, LOCKED), /* HP control setting */ @@ -134,7 +155,7 @@ void bl31_early_platform_setup2(u_register_t arg0, u_register_t arg1, { unsigned int console_base = IMX_BOOT_UART_BASE; static console_t console; - int i; + int i, ret; /* Enable CSU NS access permission */ for (i = 0; i < 64; i++) { @@ -143,14 +164,14 @@ void bl31_early_platform_setup2(u_register_t arg0, u_register_t arg1, imx_aipstz_init(aipstz); - imx_rdc_init(rdc); - - imx_csu_init(csu_cfg); - if (console_base == 0U) { console_base = imx8m_uart_get_base(); } + imx_rdc_init(rdc, console_base); + + imx_csu_init(csu_cfg); + console_imx_uart_register(console_base, IMX_BOOT_UART_CLK_IN_HZ, IMX_CONSOLE_BAUDRATE, &console); /* This console is only used for boot stage */ @@ -187,6 +208,13 @@ void bl31_early_platform_setup2(u_register_t arg0, u_register_t arg1, bl32_image_ep_info.args.arg3 = BL32_FDT_OVERLAY_ADDR; #endif #endif + ret = imx_bl31_params_parse(arg0, IMX_NS_OCRAM_SIZE, IMX_NS_OCRAM_BASE, + &bl32_image_ep_info, &bl33_image_ep_info); + if (ret != 0) { + ret = imx_bl31_params_parse(arg0, IMX_TCM_BASE, IMX_TCM_SIZE, + &bl32_image_ep_info, + &bl33_image_ep_info); + } #if !defined(SPD_opteed) && !defined(SPD_trusty) enable_snvs_privileged_access(); diff --git a/plat/imx/imx8m/imx8mm/include/imx_sec_def.h b/plat/imx/imx8m/imx8mm/include/imx_sec_def.h index 62159837..d53c922d 100644 --- a/plat/imx/imx8m/imx8mm/include/imx_sec_def.h +++ b/plat/imx/imx8m/imx8mm/include/imx_sec_def.h @@ -213,4 +213,26 @@ enum csu_csl_idx { CSU_CSL_CAAM = 114, }; +enum csu_sa_idx { + CSU_SA_M4 = 1, + CSU_SA_SDMA1 = 2, + CSU_SA_PCIE_CTRL1 = 3, + CSU_SA_USB1 = 4, + CSU_SA_USB2 = 5, + CSU_SA_VPU = 6, + CSU_SA_GPU = 7, + CSU_SA_APBHDMA = 8, + CSU_SA_ENET = 9, + CSU_SA_USDHC1 = 10, + CSU_SA_USDHC2 = 11, + CSU_SA_USDHC3 = 12, + CSU_SA_HUGO = 13, + CSU_SA_DAP = 14, + CSU_SA_SDMA2 = 15, + CSU_SA_CAAM = 16, + CSU_SA_SDMA3 = 17, + CSU_SA_LCDIF = 18, + CSU_SA_CSI = 19, +}; + #endif /* IMX_SEC_DEF_H */ diff --git a/plat/imx/imx8m/imx8mm/include/platform_def.h b/plat/imx/imx8m/imx8mm/include/platform_def.h index 65749f34..e6ad8fec 100644 --- a/plat/imx/imx8m/imx8mm/include/platform_def.h +++ b/plat/imx/imx8m/imx8mm/include/platform_def.h @@ -60,7 +60,9 @@ #define BL31_LIMIT (BL31_BASE + BL31_SIZE) /* non-secure uboot base */ +#ifndef PLAT_NS_IMAGE_OFFSET #define PLAT_NS_IMAGE_OFFSET U(0x40200000) +#endif #define PLAT_NS_IMAGE_SIZE U(0x00200000) #define BL32_FDT_OVERLAY_ADDR (PLAT_NS_IMAGE_OFFSET + 0x3000000) @@ -83,6 +85,11 @@ #define PLAT_CRASH_UART_CLK_IN_HZ 24000000 #define IMX_CONSOLE_BAUDRATE 115200 +#define IMX_UART1_BASE U(0x30860000) +#define IMX_UART2_BASE U(0x30890000) +#define IMX_UART3_BASE U(0x30880000) +#define IMX_UART4_BASE U(0x30a60000) + #define IMX_AIPSTZ1 U(0x301f0000) #define IMX_AIPSTZ2 U(0x305f0000) #define IMX_AIPSTZ3 U(0x309f0000) @@ -116,6 +123,8 @@ #define IMX_ROM_SIZE U(0x40000) #define IMX_NS_OCRAM_BASE U(0x900000) #define IMX_NS_OCRAM_SIZE U(0x20000) +#define IMX_TCM_BASE U(0x7E0000) +#define IMX_TCM_SIZE U(0x40000) #define IMX_CAAM_RAM_BASE U(0x100000) #define IMX_CAAM_RAM_SIZE U(0x10000) #define IMX_DRAM_BASE U(0x40000000) diff --git a/plat/imx/imx8m/imx8mm/platform.mk b/plat/imx/imx8m/imx8mm/platform.mk index 97f4f248..e54256c1 100644 --- a/plat/imx/imx8m/imx8mm/platform.mk +++ b/plat/imx/imx8m/imx8mm/platform.mk @@ -1,5 +1,5 @@ # -# Copyright (c) 2019-2023, ARM Limited and Contributors. All rights reserved. +# Copyright (c) 2019-2024, Arm Limited and Contributors. All rights reserved. # # SPDX-License-Identifier: BSD-3-Clause # @@ -30,7 +30,8 @@ IMX_GIC_SOURCES := ${GICV3_SOURCES} \ plat/common/plat_psci_common.c \ plat/imx/common/plat_imx8_gic.c -BL31_SOURCES += plat/imx/common/imx8_helpers.S \ +BL31_SOURCES += common/desc_image_load.c \ + plat/imx/common/imx8_helpers.S \ plat/imx/imx8m/gpc_common.c \ plat/imx/imx8m/imx_hab.c \ plat/imx/imx8m/imx_aipstz.c \ @@ -46,13 +47,13 @@ BL31_SOURCES += plat/imx/common/imx8_helpers.S \ plat/imx/common/imx8_topology.c \ plat/imx/common/imx_sip_handler.c \ plat/imx/common/imx_sip_svc.c \ + plat/imx/common/imx_common.c \ plat/imx/common/imx_uart_console.S \ lib/cpus/aarch64/cortex_a53.S \ drivers/arm/tzc/tzc380.c \ drivers/delay_timer/delay_timer.c \ drivers/delay_timer/generic_delay_timer.c \ ${XLAT_TABLES_LIB_SRCS} \ - ${IMX_DRAM_SOURCES} \ ${IMX_GIC_SOURCES} ifeq (${NEED_BL2},yes) @@ -126,21 +127,20 @@ ROT_KEY = $(BUILD_PLAT)/rot_key.pem ROTPK_HASH = $(BUILD_PLAT)/rotpk_sha256.bin $(eval $(call add_define_val,ROTPK_HASH,'"$(ROTPK_HASH)"')) -$(eval $(call MAKE_LIB_DIRS)) $(BUILD_PLAT)/bl2/imx8mm_rotpk.o: $(ROTPK_HASH) certificates: $(ROT_KEY) -$(ROT_KEY): | $(BUILD_PLAT) - @echo " OPENSSL $@" - @if [ ! -f $(ROT_KEY) ]; then \ +$(ROT_KEY): | $$(@D)/ + $(s)echo " OPENSSL $@" + $(q)if [ ! -f $(ROT_KEY) ]; then \ ${OPENSSL_BIN_PATH}/openssl genrsa 2048 > $@ 2>/dev/null; \ fi -$(ROTPK_HASH): $(ROT_KEY) - @echo " OPENSSL $@" - $(Q)${OPENSSL_BIN_PATH}/openssl rsa -in $< -pubout -outform DER 2>/dev/null |\ +$(ROTPK_HASH): $(ROT_KEY) | $$(@D)/ + $(s)echo " OPENSSL $@" + $(q)${OPENSSL_BIN_PATH}/openssl rsa -in $< -pubout -outform DER 2>/dev/null |\ ${OPENSSL_BIN_PATH}/openssl dgst -sha256 -binary > $@ 2>/dev/null endif @@ -153,6 +153,18 @@ ERRATA_A53_835769 := 1 ERRATA_A53_843419 := 1 ERRATA_A53_855873 := 1 +IMX_DRAM_RETENTION ?= 1 +$(eval $(call assert_boolean,IMX_DRAM_RETENTION)) +$(eval $(call add_define,IMX_DRAM_RETENTION)) + +ifeq (${IMX_DRAM_RETENTION},1) +BL31_SOURCES += ${IMX_DRAM_SOURCES} +endif + +ifneq (${PRELOADED_BL33_BASE},) +$(eval $(call add_define_val,PLAT_NS_IMAGE_OFFSET,${PRELOADED_BL33_BASE})) +endif + BL32_BASE ?= 0xbe000000 $(eval $(call add_define,BL32_BASE)) @@ -176,10 +188,6 @@ ifeq (${MEASURED_BOOT},1) $(info Including ${MEASURED_BOOT_MK}) include ${MEASURED_BOOT_MK} -ifneq (${MBOOT_EL_HASH_ALG}, sha256) - $(eval $(call add_define,TF_MBEDTLS_MBOOT_USE_SHA512)) -endif - BL2_SOURCES += plat/imx/imx8m/imx8m_measured_boot.c \ plat/imx/imx8m/imx8m_dyn_cfg_helpers.c \ ${EVENT_LOG_SOURCES} diff --git a/plat/imx/imx8m/imx8mn/imx8mn_bl31_setup.c b/plat/imx/imx8m/imx8mn/imx8mn_bl31_setup.c index f9e430bf..42d173e7 100644 --- a/plat/imx/imx8m/imx8mn/imx8mn_bl31_setup.c +++ b/plat/imx/imx8m/imx8mn/imx8mn_bl31_setup.c @@ -29,6 +29,7 @@ #include <imx8m_csu.h> #include <imx8m_snvs.h> #include <platform_def.h> +#include <plat_common.h> #include <plat_imx8.h> #define TRUSTY_PARAMS_LEN_BYTES (4096*2) @@ -47,7 +48,7 @@ static const struct aipstz_cfg aipstz[] = { {0}, }; -static const struct imx_rdc_cfg rdc[] = { +static struct imx_rdc_cfg rdc[] = { /* Master domain assignment */ RDC_MDAn(RDC_MDA_M7, DID1), @@ -126,7 +127,7 @@ void bl31_early_platform_setup2(u_register_t arg0, u_register_t arg1, unsigned int console_base = IMX_BOOT_UART_BASE; static console_t console; unsigned int val; - int i; + int i, ret; /* Enable CSU NS access permission */ for (i = 0; i < 64; i++) { @@ -135,7 +136,11 @@ void bl31_early_platform_setup2(u_register_t arg0, u_register_t arg1, imx_aipstz_init(aipstz); - imx_rdc_init(rdc); + if (console_base == 0U) { + console_base = imx8m_uart_get_base(); + } + + imx_rdc_init(rdc, console_base); imx_csu_init(csu_cfg); @@ -151,10 +156,6 @@ void bl31_early_platform_setup2(u_register_t arg0, u_register_t arg1, val = mmio_read_32(IMX_IOMUX_GPR_BASE + 0x2c); mmio_write_32(IMX_IOMUX_GPR_BASE + 0x2c, val | 0x3DFF0000); - if (console_base == 0U) { - console_base = imx8m_uart_get_base(); - } - console_imx_uart_register(console_base, IMX_BOOT_UART_CLK_IN_HZ, IMX_CONSOLE_BAUDRATE, &console); /* This console is only used for boot stage */ @@ -192,6 +193,13 @@ void bl31_early_platform_setup2(u_register_t arg0, u_register_t arg1, #endif #endif + ret = imx_bl31_params_parse(arg0, IMX_NS_OCRAM_SIZE, IMX_NS_OCRAM_BASE, + &bl32_image_ep_info, &bl33_image_ep_info); + if (ret != 0) { + imx_bl31_params_parse(arg0, IMX_TCM_BASE, IMX_TCM_SIZE, + &bl32_image_ep_info, &bl33_image_ep_info); + } + #if !defined(SPD_opteed) && !defined(SPD_trusty) enable_snvs_privileged_access(); #endif diff --git a/plat/imx/imx8m/imx8mn/include/imx_sec_def.h b/plat/imx/imx8m/imx8mn/include/imx_sec_def.h index 0ef14a90..83c5fa95 100644 --- a/plat/imx/imx8m/imx8mn/include/imx_sec_def.h +++ b/plat/imx/imx8m/imx8mn/include/imx_sec_def.h @@ -207,4 +207,23 @@ enum csu_csl_idx { CSU_CSL_OCRAM_S = 119, }; +enum csu_sa_idx { + CSU_SA_M7 = 1, + CSU_SA_SDMA1 = 2, + CSU_SA_USB1 = 4, + CSU_SA_GPU = 7, + CSU_SA_APBHDMA = 8, + CSU_SA_ENET1 = 9, + CSU_SA_USDHC1 = 10, + CSU_SA_USDHC2 = 11, + CSU_SA_USDHC3 = 12, + CSU_SA_HUGO = 13, + CSU_SA_DAP = 14, + CSU_SA_SDMA2 = 15, + CSU_SA_CAAM = 16, + CSU_SA_SDMA3 = 17, + CSU_SA_LCDIF = 18, + CSU_SA_ISI = 19, +}; + #endif /* IMX_SEC_DEF_H */ diff --git a/plat/imx/imx8m/imx8mn/include/platform_def.h b/plat/imx/imx8m/imx8mn/include/platform_def.h index d5176dd1..b76bdbfd 100644 --- a/plat/imx/imx8m/imx8mn/include/platform_def.h +++ b/plat/imx/imx8m/imx8mn/include/platform_def.h @@ -45,7 +45,9 @@ #define BL31_LIMIT (BL31_BASE + BL31_SIZE) /* non-secure uboot base */ +#ifndef PLAT_NS_IMAGE_OFFSET #define PLAT_NS_IMAGE_OFFSET U(0x40200000) +#endif #define BL32_FDT_OVERLAY_ADDR (PLAT_NS_IMAGE_OFFSET + 0x3000000) @@ -66,6 +68,11 @@ #define PLAT_CRASH_UART_CLK_IN_HZ 24000000 #define IMX_CONSOLE_BAUDRATE 115200 +#define IMX_UART1_BASE U(0x30860000) +#define IMX_UART2_BASE U(0x30890000) +#define IMX_UART3_BASE U(0x30880000) +#define IMX_UART4_BASE U(0x30a60000) + #define IMX_AIPSTZ1 U(0x301f0000) #define IMX_AIPSTZ2 U(0x305f0000) #define IMX_AIPSTZ3 U(0x309f0000) @@ -138,6 +145,8 @@ #define OCRAM_S_SIZE U(0x8000) #define OCRAM_S_LIMIT (OCRAM_S_BASE + OCRAM_S_SIZE) #define SAVED_DRAM_TIMING_BASE OCRAM_S_BASE +#define IMX_TCM_BASE U(0x7E0000) +#define IMX_TCM_SIZE U(0x40000) #define COUNTER_FREQUENCY 8000000 /* 8MHz */ diff --git a/plat/imx/imx8m/imx8mn/platform.mk b/plat/imx/imx8m/imx8mn/platform.mk index e0826e29..6a9d22e1 100644 --- a/plat/imx/imx8m/imx8mn/platform.mk +++ b/plat/imx/imx8m/imx8mn/platform.mk @@ -25,7 +25,8 @@ IMX_GIC_SOURCES := ${GICV3_SOURCES} \ plat/common/plat_psci_common.c \ plat/imx/common/plat_imx8_gic.c -BL31_SOURCES += plat/imx/common/imx8_helpers.S \ +BL31_SOURCES += common/desc_image_load.c \ + plat/imx/common/imx8_helpers.S \ plat/imx/imx8m/gpc_common.c \ plat/imx/imx8m/imx_hab.c \ plat/imx/imx8m/imx_aipstz.c \ @@ -41,12 +42,12 @@ BL31_SOURCES += plat/imx/common/imx8_helpers.S \ plat/imx/common/imx8_topology.c \ plat/imx/common/imx_sip_handler.c \ plat/imx/common/imx_sip_svc.c \ + plat/imx/common/imx_common.c \ plat/imx/common/imx_uart_console.S \ lib/cpus/aarch64/cortex_a53.S \ drivers/arm/tzc/tzc380.c \ drivers/delay_timer/delay_timer.c \ drivers/delay_timer/generic_delay_timer.c \ - ${IMX_DRAM_SOURCES} \ ${IMX_GIC_SOURCES} \ ${XLAT_TABLES_LIB_SRCS} @@ -59,6 +60,18 @@ ERRATA_A53_835769 := 1 ERRATA_A53_843419 := 1 ERRATA_A53_855873 := 1 +IMX_DRAM_RETENTION ?= 1 +$(eval $(call assert_boolean,IMX_DRAM_RETENTION)) +$(eval $(call add_define,IMX_DRAM_RETENTION)) + +ifeq (${IMX_DRAM_RETENTION},1) +BL31_SOURCES += ${IMX_DRAM_SOURCES} +endif + +ifneq (${PRELOADED_BL33_BASE},) +$(eval $(call add_define_val,PLAT_NS_IMAGE_OFFSET,${PRELOADED_BL33_BASE})) +endif + BL32_BASE ?= 0xbe000000 $(eval $(call add_define,BL32_BASE)) diff --git a/plat/imx/imx8m/imx8mp/gpc.c b/plat/imx/imx8m/imx8mp/gpc.c index 956b5081..a95eb365 100644 --- a/plat/imx/imx8m/imx8mp/gpc.c +++ b/plat/imx/imx8m/imx8mp/gpc.c @@ -374,12 +374,20 @@ void imx_gpc_init(void) mmio_clrbits_32(IMX_SRC_BASE + SRC_OTG1PHY_SCR, 0x1); mmio_clrbits_32(IMX_SRC_BASE + SRC_OTG2PHY_SCR, 0x1); - /* enable all the power domain by default */ + /* enable all clocks by default */ for (i = 0; i < 101; i++) { mmio_write_32(IMX_CCM_BASE + CCGR(i), 0x3); } - for (i = 0; i < 20; i++) { - imx_gpc_pm_domain_enable(i, true); - } + /* Depending on SKU, we may be lacking e.g. a VPU and shouldn't + * access that domain here, because that would lockup the SoC. + * Other i.MX8M variants don't initialize any power domains, but + * for 8MP we have been enabling the USB power domains since the + * beginning and stopping to do this now may render systems + * unrecoverable. So we'll keep initializing just the USB power + * domains instead of all of them like before. + */ + imx_gpc_pm_domain_enable(HSIOMIX, true); + imx_gpc_pm_domain_enable(USB1_PHY, true); + imx_gpc_pm_domain_enable(USB2_PHY, true); } diff --git a/plat/imx/imx8m/imx8mp/imx8mp_bl31_setup.c b/plat/imx/imx8m/imx8mp/imx8mp_bl31_setup.c index 43fa0646..141c94bb 100644 --- a/plat/imx/imx8m/imx8mp/imx8mp_bl31_setup.c +++ b/plat/imx/imx8m/imx8mp/imx8mp_bl31_setup.c @@ -29,6 +29,7 @@ #include <imx8m_csu.h> #include <imx8m_snvs.h> #include <platform_def.h> +#include <plat_common.h> #include <plat_imx8.h> #define TRUSTY_PARAMS_LEN_BYTES (4096*2) @@ -48,7 +49,7 @@ static const struct aipstz_cfg aipstz[] = { {0}, }; -static const struct imx_rdc_cfg rdc[] = { +static struct imx_rdc_cfg rdc[] = { /* Master domain assignment */ RDC_MDAn(RDC_MDA_M7, DID1), @@ -63,12 +64,45 @@ static const struct imx_rdc_cfg rdc[] = { static const struct imx_csu_cfg csu_cfg[] = { /* peripherals csl setting */ - CSU_CSLx(CSU_CSL_OCRAM, CSU_SEC_LEVEL_2, UNLOCKED), - CSU_CSLx(CSU_CSL_OCRAM_S, CSU_SEC_LEVEL_2, UNLOCKED), + CSU_CSLx(CSU_CSL_OCRAM, CSU_SEC_LEVEL_2, LOCKED), + CSU_CSLx(CSU_CSL_OCRAM_S, CSU_SEC_LEVEL_2, LOCKED), + CSU_CSLx(CSU_CSL_RDC, CSU_SEC_LEVEL_3, LOCKED), + CSU_CSLx(CSU_CSL_TZASC, CSU_SEC_LEVEL_5, LOCKED), + CSU_CSLx(CSU_CSL_CSU, CSU_SEC_LEVEL_5, LOCKED), /* master HP0~1 */ /* SA setting */ + CSU_SA(CSU_SA_M7, NON_SEC_ACCESS, LOCKED), + CSU_SA(CSU_SA_SDMA1, NON_SEC_ACCESS, LOCKED), + CSU_SA(CSU_SA_PCIE_CTRL1, NON_SEC_ACCESS, LOCKED), + CSU_SA(CSU_SA_USB1, NON_SEC_ACCESS, LOCKED), + CSU_SA(CSU_SA_USB2, NON_SEC_ACCESS, LOCKED), + CSU_SA(CSU_SA_APB_HDMA, NON_SEC_ACCESS, LOCKED), + CSU_SA(CSU_SA_ENET1, NON_SEC_ACCESS, LOCKED), + CSU_SA(CSU_SA_USDHC1, NON_SEC_ACCESS, LOCKED), + CSU_SA(CSU_SA_USDHC2, NON_SEC_ACCESS, LOCKED), + CSU_SA(CSU_SA_USDHC3, NON_SEC_ACCESS, LOCKED), + CSU_SA(CSU_SA_HUGO, NON_SEC_ACCESS, LOCKED), + CSU_SA(CSU_SA_DAP, NON_SEC_ACCESS, LOCKED), + CSU_SA(CSU_SA_SDMA2, NON_SEC_ACCESS, LOCKED), + CSU_SA(CSU_SA_SDMA3, NON_SEC_ACCESS, LOCKED), + CSU_SA(CSU_SA_LCDIF1, NON_SEC_ACCESS, LOCKED), + CSU_SA(CSU_SA_ISI, NON_SEC_ACCESS, LOCKED), + CSU_SA(CSU_SA_NPU, NON_SEC_ACCESS, LOCKED), + CSU_SA(CSU_SA_LCDIF2, NON_SEC_ACCESS, LOCKED), + CSU_SA(CSU_SA_HDMI_TX, NON_SEC_ACCESS, LOCKED), + CSU_SA(CSU_SA_ENET2, NON_SEC_ACCESS, LOCKED), + CSU_SA(CSU_SA_GPU3D, NON_SEC_ACCESS, LOCKED), + CSU_SA(CSU_SA_GPU2D, NON_SEC_ACCESS, LOCKED), + CSU_SA(CSU_SA_VPU_G1, NON_SEC_ACCESS, LOCKED), + CSU_SA(CSU_SA_VPU_G2, NON_SEC_ACCESS, LOCKED), + CSU_SA(CSU_SA_VPU_VC8000E, NON_SEC_ACCESS, LOCKED), + CSU_SA(CSU_SA_AUDIO_EDMA, NON_SEC_ACCESS, LOCKED), + CSU_SA(CSU_SA_ISP1, NON_SEC_ACCESS, LOCKED), + CSU_SA(CSU_SA_ISP2, NON_SEC_ACCESS, LOCKED), + CSU_SA(CSU_SA_DEWARP, NON_SEC_ACCESS, LOCKED), + CSU_SA(CSU_SA_GIC500, NON_SEC_ACCESS, LOCKED), /* HP control setting */ @@ -123,6 +157,7 @@ void bl31_early_platform_setup2(u_register_t arg0, u_register_t arg1, static console_t console; unsigned int val; unsigned int i; + int ret; /* Enable CSU NS access permission */ for (i = 0; i < 64; i++) { @@ -131,7 +166,11 @@ void bl31_early_platform_setup2(u_register_t arg0, u_register_t arg1, imx_aipstz_init(aipstz); - imx_rdc_init(rdc); + if (console_base == 0U) { + console_base = imx8m_uart_get_base(); + } + + imx_rdc_init(rdc, console_base); imx_csu_init(csu_cfg); @@ -140,10 +179,6 @@ void bl31_early_platform_setup2(u_register_t arg0, u_register_t arg1, val = mmio_read_32(IMX_IOMUX_GPR_BASE + 0x2c); mmio_write_32(IMX_IOMUX_GPR_BASE + 0x2c, val | 0x3DFF0000); - if (console_base == 0U) { - console_base = imx8m_uart_get_base(); - } - console_imx_uart_register(console_base, IMX_BOOT_UART_CLK_IN_HZ, IMX_CONSOLE_BAUDRATE, &console); /* This console is only used for boot stage */ @@ -180,6 +215,13 @@ void bl31_early_platform_setup2(u_register_t arg0, u_register_t arg1, bl32_image_ep_info.args.arg3 = BL32_FDT_OVERLAY_ADDR; #endif #endif + ret = imx_bl31_params_parse(arg0, IMX_NS_OCRAM_SIZE, IMX_NS_OCRAM_BASE, + &bl32_image_ep_info, &bl33_image_ep_info); + if (ret != 0) { + ret = imx_bl31_params_parse(arg0, IMX_TCM_BASE, IMX_TCM_SIZE, + &bl32_image_ep_info, + &bl33_image_ep_info); + } #if !defined(SPD_opteed) && !defined(SPD_trusty) enable_snvs_privileged_access(); diff --git a/plat/imx/imx8m/imx8mp/include/imx_sec_def.h b/plat/imx/imx8m/imx8mp/include/imx_sec_def.h index ba248b59..1ba30339 100644 --- a/plat/imx/imx8m/imx8mp/include/imx_sec_def.h +++ b/plat/imx/imx8m/imx8mp/include/imx_sec_def.h @@ -269,6 +269,41 @@ enum csu_csl_idx { CSU_CSL_OCRAM_A = 113, CSU_CSL_OCRAM = 118, CSU_CSL_OCRAM_S = 119, + CSU_CSL_VPU = 120, +}; + +enum csu_sa_idx { + CSU_SA_M7 = 1, + CSU_SA_SDMA1 = 2, + CSU_SA_PCIE_CTRL1 = 3, + CSU_SA_USB1 = 4, + CSU_SA_USB2 = 6, + CSU_SA_APB_HDMA = 8, + CSU_SA_ENET1 = 9, + CSU_SA_USDHC1 = 10, + CSU_SA_USDHC2 = 11, + CSU_SA_USDHC3 = 12, + CSU_SA_HUGO = 13, + CSU_SA_DAP = 14, + CSU_SA_SDMA2 = 15, + CSU_SA_CAAM = 16, + CSU_SA_SDMA3 = 17, + CSU_SA_LCDIF1 = 18, + CSU_SA_ISI = 19, + CSU_SA_NPU = 20, + CSU_SA_LCDIF2 = 21, + CSU_SA_HDMI_TX = 22, + CSU_SA_ENET2 = 23, + CSU_SA_GPU3D = 24, + CSU_SA_GPU2D = 25, + CSU_SA_VPU_G1 = 26, + CSU_SA_VPU_G2 = 27, + CSU_SA_VPU_VC8000E = 28, + CSU_SA_AUDIO_EDMA = 29, + CSU_SA_ISP1 = 30, + CSU_SA_ISP2 = 31, + CSU_SA_DEWARP = 32, + CSU_SA_GIC500 = 33, }; #endif /* IMX_SEC_DEF_H */ diff --git a/plat/imx/imx8m/imx8mp/include/platform_def.h b/plat/imx/imx8m/imx8mp/include/platform_def.h index 12812705..78f3d5b8 100644 --- a/plat/imx/imx8m/imx8mp/include/platform_def.h +++ b/plat/imx/imx8m/imx8mp/include/platform_def.h @@ -62,7 +62,9 @@ #define PLAT_SDEI_SGI_PRIVATE U(9) /* non-secure uboot base */ +#ifndef PLAT_NS_IMAGE_OFFSET #define PLAT_NS_IMAGE_OFFSET U(0x40200000) +#endif #define PLAT_NS_IMAGE_SIZE U(0x00200000) #define BL32_FDT_OVERLAY_ADDR (PLAT_NS_IMAGE_OFFSET + 0x3000000) @@ -84,6 +86,11 @@ #define PLAT_CRASH_UART_CLK_IN_HZ 24000000 #define IMX_CONSOLE_BAUDRATE 115200 +#define IMX_UART1_BASE U(0x30860000) +#define IMX_UART2_BASE U(0x30890000) +#define IMX_UART3_BASE U(0x30880000) +#define IMX_UART4_BASE U(0x30a60000) + #define IMX_AIPSTZ1 U(0x301f0000) #define IMX_AIPSTZ2 U(0x305f0000) #define IMX_AIPSTZ3 U(0x309f0000) @@ -170,6 +177,9 @@ #define MAX_CSU_NUM U(64) +#define IMX_TCM_BASE U(0x7E0000) +#define IMX_TCM_SIZE U(0x40000) + #define OCRAM_S_BASE U(0x00180000) #define OCRAM_S_SIZE U(0x8000) #define OCRAM_S_LIMIT (OCRAM_S_BASE + OCRAM_S_SIZE) diff --git a/plat/imx/imx8m/imx8mp/platform.mk b/plat/imx/imx8m/imx8mp/platform.mk index ce690719..98b99d11 100644 --- a/plat/imx/imx8m/imx8mp/platform.mk +++ b/plat/imx/imx8m/imx8mp/platform.mk @@ -26,7 +26,8 @@ IMX_GIC_SOURCES := ${GICV3_SOURCES} \ plat/common/plat_psci_common.c \ plat/imx/common/plat_imx8_gic.c -BL31_SOURCES += plat/imx/common/imx8_helpers.S \ +BL31_SOURCES += common/desc_image_load.c \ + plat/imx/common/imx8_helpers.S \ plat/imx/imx8m/gpc_common.c \ plat/imx/imx8m/imx_hab.c \ plat/imx/imx8m/imx_aipstz.c \ @@ -42,12 +43,12 @@ BL31_SOURCES += plat/imx/common/imx8_helpers.S \ plat/imx/common/imx8_topology.c \ plat/imx/common/imx_sip_handler.c \ plat/imx/common/imx_sip_svc.c \ + plat/imx/common/imx_common.c \ plat/imx/common/imx_uart_console.S \ lib/cpus/aarch64/cortex_a53.S \ drivers/arm/tzc/tzc380.c \ drivers/delay_timer/delay_timer.c \ drivers/delay_timer/generic_delay_timer.c \ - ${IMX_DRAM_SOURCES} \ ${IMX_GIC_SOURCES} \ ${XLAT_TABLES_LIB_SRCS} @@ -123,21 +124,20 @@ ROT_KEY = $(BUILD_PLAT)/rot_key.pem ROTPK_HASH = $(BUILD_PLAT)/rotpk_sha256.bin $(eval $(call add_define_val,ROTPK_HASH,'"$(ROTPK_HASH)"')) -$(eval $(call MAKE_LIB_DIRS)) $(BUILD_PLAT)/bl2/imx8mp_rotpk.o: $(ROTPK_HASH) certificates: $(ROT_KEY) -$(ROT_KEY): | $(BUILD_PLAT) - @echo " OPENSSL $@" - @if [ ! -f $(ROT_KEY) ]; then \ +$(ROT_KEY): | $$(@D)/ + $(s)echo " OPENSSL $@" + $(q)if [ ! -f $(ROT_KEY) ]; then \ ${OPENSSL_BIN_PATH}/openssl genrsa 2048 > $@ 2>/dev/null; \ fi -$(ROTPK_HASH): $(ROT_KEY) - @echo " OPENSSL $@" - $(Q)${OPENSSL_BIN_PATH}/openssl rsa -in $< -pubout -outform DER 2>/dev/null |\ +$(ROTPK_HASH): $(ROT_KEY) | $$(@D)/ + $(s)echo " OPENSSL $@" + $(q)${OPENSSL_BIN_PATH}/openssl rsa -in $< -pubout -outform DER 2>/dev/null |\ ${OPENSSL_BIN_PATH}/openssl dgst -sha256 -binary > $@ 2>/dev/null endif @@ -150,6 +150,18 @@ ERRATA_A53_835769 := 1 ERRATA_A53_843419 := 1 ERRATA_A53_855873 := 1 +IMX_DRAM_RETENTION ?= 1 +$(eval $(call assert_boolean,IMX_DRAM_RETENTION)) +$(eval $(call add_define,IMX_DRAM_RETENTION)) + +ifeq (${IMX_DRAM_RETENTION},1) +BL31_SOURCES += ${IMX_DRAM_SOURCES} +endif + +ifneq (${PRELOADED_BL33_BASE},) +$(eval $(call add_define_val,PLAT_NS_IMAGE_OFFSET,${PRELOADED_BL33_BASE})) +endif + BL32_BASE ?= 0x56000000 $(eval $(call add_define,BL32_BASE)) diff --git a/plat/imx/imx8m/imx8mq/imx8mq_bl31_setup.c b/plat/imx/imx8m/imx8mq/imx8mq_bl31_setup.c index 7065a658..70c2def7 100644 --- a/plat/imx/imx8m/imx8mq/imx8mq_bl31_setup.c +++ b/plat/imx/imx8m/imx8mq/imx8mq_bl31_setup.c @@ -26,6 +26,7 @@ #include <imx_aipstz.h> #include <imx_uart.h> #include <imx8m_caam.h> +#include <imx8m_ccm.h> #include <plat_imx8.h> #define TRUSTY_PARAMS_LEN_BYTES (4096*2) @@ -145,6 +146,7 @@ static void bl31_tz380_setup(void) void bl31_early_platform_setup2(u_register_t arg0, u_register_t arg1, u_register_t arg2, u_register_t arg3) { + unsigned int console_base = IMX_BOOT_UART_BASE; static console_t console; int i; /* enable CSU NS access permission */ @@ -154,7 +156,11 @@ void bl31_early_platform_setup2(u_register_t arg0, u_register_t arg1, imx_aipstz_init(aipstz); - console_imx_uart_register(IMX_BOOT_UART_BASE, IMX_BOOT_UART_CLK_IN_HZ, + if (console_base == 0U) { + console_base = imx8m_uart_get_base(); + } + + console_imx_uart_register(console_base, IMX_BOOT_UART_CLK_IN_HZ, IMX_CONSOLE_BAUDRATE, &console); /* This console is only used for boot stage */ console_set_scope(&console, CONSOLE_FLAG_BOOT); diff --git a/plat/imx/imx8m/imx8mq/include/platform_def.h b/plat/imx/imx8m/imx8mq/include/platform_def.h index 2526a02d..61c0e8e4 100644 --- a/plat/imx/imx8m/imx8mq/include/platform_def.h +++ b/plat/imx/imx8m/imx8mq/include/platform_def.h @@ -38,7 +38,9 @@ #define BL31_LIMIT (BL31_BASE + BL31_SIZE) /* non-secure uboot base */ +#ifndef PLAT_NS_IMAGE_OFFSET #define PLAT_NS_IMAGE_OFFSET U(0x40200000) +#endif #define BL32_FDT_OVERLAY_ADDR (PLAT_NS_IMAGE_OFFSET + 0x3000000) /* GICv3 base address */ @@ -63,6 +65,11 @@ #define PLAT_CRASH_UART_CLK_IN_HZ 25000000 #define IMX_CONSOLE_BAUDRATE 115200 +#define IMX_UART1_BASE U(0x30860000) +#define IMX_UART2_BASE U(0x30890000) +#define IMX_UART3_BASE U(0x30880000) +#define IMX_UART4_BASE U(0x30a60000) + #define IMX_AIPS_BASE U(0x30200000) #define IMX_AIPS_SIZE U(0xC00000) #define IMX_AIPS1_BASE U(0x30200000) diff --git a/plat/imx/imx8m/imx8mq/platform.mk b/plat/imx/imx8m/imx8mq/platform.mk index b1c189fa..73179dd0 100644 --- a/plat/imx/imx8m/imx8mq/platform.mk +++ b/plat/imx/imx8m/imx8mq/platform.mk @@ -31,6 +31,7 @@ BL31_SOURCES += plat/imx/common/imx8_helpers.S \ plat/imx/imx8m/gpc_common.c \ plat/imx/imx8m/imx_aipstz.c \ plat/imx/imx8m/imx8m_caam.c \ + plat/imx/imx8m/imx8m_ccm.c \ plat/imx/imx8m/imx8m_psci_common.c \ plat/imx/imx8m/imx8mq/gpc.c \ plat/imx/common/imx8_topology.c \ @@ -42,7 +43,6 @@ BL31_SOURCES += plat/imx/common/imx8_helpers.S \ drivers/delay_timer/delay_timer.c \ drivers/delay_timer/generic_delay_timer.c \ ${XLAT_TABLES_LIB_SRCS} \ - ${IMX_DRAM_SOURCES} \ ${IMX_GIC_SOURCES} ENABLE_PIE := 1 @@ -55,6 +55,18 @@ ERRATA_A53_835769 := 1 ERRATA_A53_843419 := 1 ERRATA_A53_855873 := 1 +IMX_DRAM_RETENTION ?= 0 +$(eval $(call assert_boolean,IMX_DRAM_RETENTION)) +$(eval $(call add_define,IMX_DRAM_RETENTION)) + +ifeq (${IMX_DRAM_RETENTION},1) +BL31_SOURCES += ${IMX_DRAM_SOURCES} +endif + +ifneq (${PRELOADED_BL33_BASE},) +$(eval $(call add_define_val,PLAT_NS_IMAGE_OFFSET,${PRELOADED_BL33_BASE})) +endif + BL32_BASE ?= 0xfe000000 $(eval $(call add_define,BL32_BASE)) @@ -62,6 +74,9 @@ BL32_SIZE ?= 0x2000000 $(eval $(call add_define,BL32_SIZE)) IMX_BOOT_UART_BASE ?= 0x30860000 +ifeq (${IMX_BOOT_UART_BASE},auto) + override IMX_BOOT_UART_BASE := 0 +endif $(eval $(call add_define,IMX_BOOT_UART_BASE)) ifeq (${SPD},trusty) diff --git a/plat/imx/imx8m/imx_rdc.c b/plat/imx/imx8m/imx_rdc.c index 85de1911..de159565 100644 --- a/plat/imx/imx8m/imx_rdc.c +++ b/plat/imx/imx8m/imx_rdc.c @@ -4,13 +4,78 @@ * SPDX-License-Identifier: BSD-3-Clause */ +#include <errno.h> + #include <lib/mmio.h> #include <imx_rdc.h> -void imx_rdc_init(const struct imx_rdc_cfg *rdc_cfg) +struct imx_uart { + int index; + unsigned int uart_base; +}; + +static const struct imx_uart imx8m_uart_info[] = { + { /* UART 1 */ + .index = RDC_PDAP_UART1, + .uart_base = IMX_UART1_BASE, + }, { /* UART 2 */ + .index = RDC_PDAP_UART2, + .uart_base = IMX_UART2_BASE, + }, { /* UART 3 */ + .index = RDC_PDAP_UART3, + .uart_base = IMX_UART3_BASE, + }, { /* UART 4 */ + .index = RDC_PDAP_UART4, + .uart_base = IMX_UART4_BASE, + } +}; + +static int imx_rdc_uart_get_pdap_index(unsigned int uart_base) +{ + size_t i; + + for (i = 0; i < ARRAY_SIZE(imx8m_uart_info); i++) { + if (imx8m_uart_info[i].uart_base == uart_base) { + return imx8m_uart_info[i].index; + } + } + + return -ENODEV; +} + +static void imx_rdc_console_access_enable(struct imx_rdc_cfg *rdc_cfg, + unsigned int console_base) +{ + struct imx_rdc_cfg *rdc; + int console_pdap_index; + + console_pdap_index = imx_rdc_uart_get_pdap_index(console_base); + if (console_pdap_index < 0) { + return; + } + + for (rdc = rdc_cfg; rdc->type != RDC_INVALID; rdc++) { + if (rdc->type != RDC_PDAP || rdc->index != console_pdap_index) { + continue; + } + + if (rdc->index == console_pdap_index && + rdc->setting.rdc_pdap == (D0R | D0W)) { + return; + } + + if (rdc->index == console_pdap_index) { + rdc->setting.rdc_pdap = D0R | D0W; + } + } +} + +void imx_rdc_init(struct imx_rdc_cfg *rdc_cfg, unsigned int console_base) { - const struct imx_rdc_cfg *rdc = rdc_cfg; + struct imx_rdc_cfg *rdc = rdc_cfg; + + imx_rdc_console_access_enable(rdc, console_base); while (rdc->type != RDC_INVALID) { switch (rdc->type) { diff --git a/plat/imx/imx8m/include/dram.h b/plat/imx/imx8m/include/dram.h index 719c3906..1cf0666e 100644 --- a/plat/imx/imx8m/include/dram.h +++ b/plat/imx/imx8m/include/dram.h @@ -70,13 +70,19 @@ struct dram_info { extern struct dram_info dram_info; -void dram_info_init(unsigned long dram_timing_base); void dram_umctl2_init(struct dram_timing_info *timing); void dram_phy_init(struct dram_timing_info *timing); /* dram retention */ +#if IMX_DRAM_RETENTION +void dram_info_init(unsigned long dram_timing_base); void dram_enter_retention(void); void dram_exit_retention(void); +#else +static inline void dram_info_init(unsigned long dram_timing_base) {} +static inline void dram_enter_retention(void) {} +static inline void dram_exit_retention(void) {} +#endif void dram_clock_switch(unsigned int target_drate, bool bypass_mode); diff --git a/plat/imx/imx8m/include/imx8m_csu.h b/plat/imx/imx8m/include/imx8m_csu.h index dc634ed9..3851e913 100644 --- a/plat/imx/imx8m/include/imx8m_csu.h +++ b/plat/imx/imx8m/include/imx8m_csu.h @@ -20,6 +20,9 @@ #define CSU_SEC_LEVEL_6 0x03 #define CSU_SEC_LEVEL_7 0x0 +#define SEC_ACCESS 0x0 +#define NON_SEC_ACCESS 0x1 + #define LOCKED 0x1 #define UNLOCKED 0x0 @@ -27,11 +30,11 @@ #define CSLx_LOCK(x) ((0x1 << (((x) % 2) * 16 + 8))) #define CSLx_CFG(x, n) ((x) << (((n) % 2) * 16)) -#define CSU_HP_REG(x) (IMX_CSU_BASE + ((x) / 16) * 4 + 0x200) +#define CSU_HP_REG(x) (IMX_CSU_BASE + (((x) / 16) * 4) + 0x200) #define CSU_HP_LOCK(x) ((0x1 << (((x) % 16) * 2 + 1))) #define CSU_HP_CFG(x, n) ((x) << (((n) % 16) * 2)) -#define CSU_SA_REG(x) (IMX_CSU_BASE + 0x218) +#define CSU_SA_REG(x) (IMX_CSU_BASE + (((x) / 16) * 4) + 0x218) #define CSU_SA_LOCK(x) ((0x1 << (((x) % 16) * 2 + 1))) #define CSU_SA_CFG(x, n) ((x) << (((n) % 16) * 2)) diff --git a/plat/imx/imx8m/include/imx_rdc.h b/plat/imx/imx8m/include/imx_rdc.h index a6e10a7b..fbdcbf29 100644 --- a/plat/imx/imx8m/include/imx_rdc.h +++ b/plat/imx/imx8m/include/imx_rdc.h @@ -67,7 +67,7 @@ struct imx_rdc_cfg { .setting.rdc_mem_region[2] = (mrc), \ } -void imx_rdc_init(const struct imx_rdc_cfg *cfg); +void imx_rdc_init(struct imx_rdc_cfg *cfg, unsigned int console_base); #endif /* IMX_RDC_H */ diff --git a/plat/imx/imx8qm/imx8qm_bl31_setup.c b/plat/imx/imx8qm/imx8qm_bl31_setup.c index bd7896a9..4c637402 100644 --- a/plat/imx/imx8qm/imx8qm_bl31_setup.c +++ b/plat/imx/imx8qm/imx8qm_bl31_setup.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015-2022, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2015-2024, ARM Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -408,8 +408,3 @@ unsigned int plat_get_syscnt_freq2(void) { return COUNTER_FREQUENCY; } - -void bl31_plat_runtime_setup(void) -{ - return; -} diff --git a/plat/imx/imx8qx/imx8qx_bl31_setup.c b/plat/imx/imx8qx/imx8qx_bl31_setup.c index 13e80fb3..08bf8f39 100644 --- a/plat/imx/imx8qx/imx8qx_bl31_setup.c +++ b/plat/imx/imx8qx/imx8qx_bl31_setup.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015-2022, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2015-2024, ARM Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -386,8 +386,3 @@ unsigned int plat_get_syscnt_freq2(void) { return COUNTER_FREQUENCY; } - -void bl31_plat_runtime_setup(void) -{ - return; -} diff --git a/plat/imx/imx8ulp/apd_context.c b/plat/imx/imx8ulp/apd_context.c new file mode 100644 index 00000000..54b87958 --- /dev/null +++ b/plat/imx/imx8ulp/apd_context.c @@ -0,0 +1,657 @@ +/* + * Copyright 2021-2024 NXP + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <assert.h> +#include <stdbool.h> + +#include <drivers/delay_timer.h> +#include <lib/mmio.h> + +#include <plat_imx8.h> +#include <xrdc.h> + +#define PCC_PR BIT(31) +#define PFD_VALID_MASK U(0x40404040) + +#define S400_MU_BASE U(0x27020000) +#define S400_MU_RSR (S400_MU_BASE + 0x12c) +#define S400_MU_TRx(i) (S400_MU_BASE + 0x200 + (i) * 4) +#define S400_MU_RRx(i) (S400_MU_BASE + 0x280 + (i) * 4) + +/* + * need to re-init the PLL, CGC1, PCC, CMC, XRDC, SIM, GPIO etc. + * init the PLL &PFD first, then switch the CA35 clock to PLL for + * performance consideration, restore other bus fabric clock. + */ + +extern void imx8ulp_caam_init(void); +extern void upower_wait_resp(void); +extern void dram_enter_retention(void); +extern void dram_exit_retention(void); + +struct plat_gic_ctx imx_gicv3_ctx; +static uint32_t cmc1_pmprot; +static uint32_t cmc1_srie; + +/* TPM5: global timer */ +static uint32_t tpm5[3]; + +static uint32_t wdog3[2]; + +/* CGC1 PLL2 */ +uint32_t pll2[][2] = { + {0x292c0510, 0x0}, {0x292c0518, 0x0}, {0x292c051c, 0x0}, + {0x292c0520, 0x0}, {0x292c0500, 0x0}, +}; + +/* CGC1 PLL3 */ +uint32_t pll3[][2] = { + {0x292c0604, 0x0}, {0x292c0608, 0x0}, {0x292c060c, 0x0}, + {0x292c0610, 0x0}, {0x292c0618, 0x0}, {0x292c061c, 0x0}, + {0x292c0620, 0x0}, {0x292c0624, 0x0}, {0x292c0600, 0x0}, + {0x292c0614, 0x0}, +}; + +/* CGC1 others */ +uint32_t cgc1[][2] = { + {0x292c0014, 0x0}, {0x292c0034, 0x0}, {0x292c0038, 0x0}, + {0x292c0108, 0x0}, {0x292c0208, 0x0}, {0x292c0700, 0x0}, + {0x292c0810, 0x0}, {0x292c0900, 0x0}, {0x292c0904, 0x0}, + {0x292c0908, 0x0}, {0x292c090c, 0x0}, {0x292c0a00, 0x0}, +}; + +static uint32_t pcc3[61]; +static uint32_t pcc4[32]; + +static uint32_t pcc5_0[33]; +static uint32_t pcc5_1[][2] = { + {0x2da70084, 0x0}, {0x2da70088, 0x0}, {0x2da7008c, 0x0}, + {0x2da700a0, 0x0}, {0x2da700a4, 0x0}, {0x2da700a8, 0x0}, + {0x2da700ac, 0x0}, {0x2da700b0, 0x0}, {0x2da700b4, 0x0}, + {0x2da700bc, 0x0}, {0x2da700c0, 0x0}, {0x2da700c8, 0x0}, + {0x2da700cc, 0x0}, {0x2da700d0, 0x0}, {0x2da700f0, 0x0}, + {0x2da700f4, 0x0}, {0x2da700f8, 0x0}, {0x2da70108, 0x0}, + {0x2da7010c, 0x0}, {0x2da70110, 0x0}, {0x2da70114, 0x0}, +}; + +static uint32_t cgc2[][2] = { + {0x2da60014, 0x0}, {0x2da60020, 0x0}, {0x2da6003c, 0x0}, + {0x2da60040, 0x0}, {0x2da60108, 0x0}, {0x2da60208, 0x0}, + {0x2da60900, 0x0}, {0x2da60904, 0x0}, {0x2da60908, 0x0}, + {0x2da60910, 0x0}, {0x2da60a00, 0x0}, +}; + +static uint32_t pll4[][2] = { + {0x2da60604, 0x0}, {0x2da60608, 0x0}, {0x2da6060c, 0x0}, + {0x2da60610, 0x0}, {0x2da60618, 0x0}, {0x2da6061c, 0x0}, + {0x2da60620, 0x0}, {0x2da60624, 0x0}, {0x2da60600, 0x0}, + {0x2da60614, 0x0}, +}; + +static uint32_t lpav_sim[][2] = { + {0x2da50000, 0x0}, {0x2da50004, 0x0}, {0x2da50008, 0x0}, + {0x2da5001c, 0x0}, {0x2da50020, 0x0}, {0x2da50024, 0x0}, + {0x2da50034, 0x0}, +}; + +#define APD_GPIO_CTRL_NUM 2 +#define LPAV_GPIO_CTRL_NUM 1 +#define GPIO_CTRL_REG_NUM 8 +#define GPIO_PIN_MAX_NUM 32 +#define GPIO_CTX(addr, num) \ + {.base = (addr), .pin_num = (num), } + +struct gpio_ctx { + /* gpio base */ + uintptr_t base; + /* port control */ + uint32_t port_ctrl[GPIO_CTRL_REG_NUM]; + /* GPIO ICR, Max 32 */ + uint32_t pin_num; + uint32_t gpio_icr[GPIO_PIN_MAX_NUM]; +}; + +static uint32_t gpio_ctrl_offset[GPIO_CTRL_REG_NUM] = { + 0xc, 0x10, 0x14, 0x18, 0x1c, 0x40, 0x54, 0x58 +}; +static struct gpio_ctx apd_gpio_ctx[APD_GPIO_CTRL_NUM] = { + GPIO_CTX(IMX_GPIOE_BASE, 24), + GPIO_CTX(IMX_GPIOF_BASE, 32), +}; + +static struct gpio_ctx lpav_gpio_ctx = GPIO_CTX(IMX_GPIOD_BASE, 24); +/* iomuxc setting */ +#define IOMUXC_SECTION_NUM 8 +struct iomuxc_section { + uint32_t offset; + uint32_t reg_num; +}; + +struct iomuxc_section iomuxc_sections[IOMUXC_SECTION_NUM] = { + {.offset = IOMUXC_PTD_PCR_BASE, .reg_num = 24}, + {.offset = IOMUXC_PTE_PCR_BASE, .reg_num = 24}, + {.offset = IOMUXC_PTF_PCR_BASE, .reg_num = 32}, + {.offset = IOMUXC_PSMI_BASE0, .reg_num = 10}, + {.offset = IOMUXC_PSMI_BASE1, .reg_num = 61}, + {.offset = IOMUXC_PSMI_BASE2, .reg_num = 12}, + {.offset = IOMUXC_PSMI_BASE3, .reg_num = 20}, + {.offset = IOMUXC_PSMI_BASE4, .reg_num = 75}, +}; +static uint32_t iomuxc_ctx[258]; + +#define PORTS_NUM 3U +void apd_io_pad_off(void) +{ + unsigned int i, j; + + /* off the PTD/E/F, need to be customized based on actual user case */ + for (i = 0; i < PORTS_NUM; i++) { + for (j = 0; j < iomuxc_sections[i].reg_num; j++) { + mmio_write_32(iomuxc_sections[i].offset + j * 4, 0); + } + } + + /* disable the PTD compensation */ + mmio_write_32(IMX_SIM1_BASE + 0x48, 0x800); +} + +void iomuxc_save(void) +{ + unsigned int i, j; + unsigned int index = 0U; + + for (i = 0U; i < IOMUXC_SECTION_NUM; i++) { + for (j = 0U; j < iomuxc_sections[i].reg_num; j++) { + iomuxc_ctx[index++] = mmio_read_32(iomuxc_sections[i].offset + j * 4); + } + } + + apd_io_pad_off(); +} + +void iomuxc_restore(void) +{ + unsigned int i, j; + unsigned int index = 0U; + + for (i = 0U; i < IOMUXC_SECTION_NUM; i++) { + for (j = 0U; j < iomuxc_sections[i].reg_num; j++) { + mmio_write_32(iomuxc_sections[i].offset + j * 4, iomuxc_ctx[index++]); + } + } +} + +void gpio_save(struct gpio_ctx *ctx, int port_num) +{ + unsigned int i, j; + + for (i = 0U; i < port_num; i++) { + /* save the port control setting */ + for (j = 0U; j < GPIO_CTRL_REG_NUM; j++) { + if (j < 4U) { + ctx->port_ctrl[j] = mmio_read_32(ctx->base + gpio_ctrl_offset[j]); + /* + * clear the permission setting to read the GPIO + * non-secure world setting. + */ + mmio_write_32(ctx->base + gpio_ctrl_offset[j], 0x0); + } else { + ctx->port_ctrl[j] = mmio_read_32(ctx->base + gpio_ctrl_offset[j]); + } + } + /* save the gpio icr setting */ + for (j = 0U; j < ctx->pin_num; j++) { + ctx->gpio_icr[j] = mmio_read_32(ctx->base + 0x80 + j * 4); + } + + ctx++; + } +} + +void gpio_restore(struct gpio_ctx *ctx, int port_num) +{ + unsigned int i, j; + + for (i = 0U; i < port_num; i++) { + for (j = 0U; j < ctx->pin_num; j++) + mmio_write_32(ctx->base + 0x80 + j * 4, ctx->gpio_icr[j]); + + for (j = 4U; j < GPIO_CTRL_REG_NUM; j++) { + mmio_write_32(ctx->base + gpio_ctrl_offset[j], ctx->port_ctrl[j]); + } + + /* permission config retore last */ + for (j = 0U; j < 4; j++) { + mmio_write_32(ctx->base + gpio_ctrl_offset[j], ctx->port_ctrl[j]); + } + + ctx++; + } +} + +void cgc1_save(void) +{ + unsigned int i; + + /* PLL2 */ + for (i = 0U; i < ARRAY_SIZE(pll2); i++) { + pll2[i][1] = mmio_read_32(pll2[i][0]); + } + + /* PLL3 */ + for (i = 0U; i < ARRAY_SIZE(pll3); i++) { + pll3[i][1] = mmio_read_32(pll3[i][0]); + } + + /* CGC1 others */ + for (i = 0U; i < ARRAY_SIZE(cgc1); i++) { + cgc1[i][1] = mmio_read_32(cgc1[i][0]); + } +} + +void cgc1_restore(void) +{ + unsigned int i; + + /* PLL2 */ + for (i = 0U; i < ARRAY_SIZE(pll2); i++) { + mmio_write_32(pll2[i][0], pll2[i][1]); + } + /* wait for PLL2 lock */ + while (!(mmio_read_32(pll2[4][0]) & BIT(24))) { + ; + } + + /* PLL3 */ + for (i = 0U; i < 9U; i++) { + mmio_write_32(pll3[i][0], pll3[i][1]); + } + + /* wait for PLL3 lock */ + while (!(mmio_read_32(pll3[4][0]) & BIT(24))) { + ; + } + + /* restore the PFDs */ + mmio_write_32(pll3[9][0], pll3[9][1] & ~(BIT(31) | BIT(23) | BIT(15) | BIT(7))); + mmio_write_32(pll3[9][0], pll3[9][1]); + + /* wait for the PFD is stable, only need to check the enabled PFDs */ + while (!(mmio_read_32(pll3[9][0]) & PFD_VALID_MASK)) { + ; + } + + /* CGC1 others */ + for (i = 0U; i < ARRAY_SIZE(cgc1); i++) { + mmio_write_32(cgc1[i][0], cgc1[i][1]); + } +} + +void tpm5_save(void) +{ + tpm5[0] = mmio_read_32(IMX_TPM5_BASE + 0x10); + tpm5[1] = mmio_read_32(IMX_TPM5_BASE + 0x18); + tpm5[2] = mmio_read_32(IMX_TPM5_BASE + 0x20); +} + +void tpm5_restore(void) +{ + mmio_write_32(IMX_TPM5_BASE + 0x10, tpm5[0]); + mmio_write_32(IMX_TPM5_BASE + 0x18, tpm5[1]); + mmio_write_32(IMX_TPM5_BASE + 0x20, tpm5[2]); +} + +void wdog3_save(void) +{ + /* enable wdog3 clock */ + mmio_write_32(IMX_PCC3_BASE + 0xa8, 0xd2800000); + + /* save the CS & TOVAL regiter */ + wdog3[0] = mmio_read_32(IMX_WDOG3_BASE); + wdog3[1] = mmio_read_32(IMX_WDOG3_BASE + 0x8); +} + +void wdog3_restore(void) +{ + /* enable wdog3 clock */ + mmio_write_32(IMX_PCC3_BASE + 0xa8, 0xd2800000); + + /* reconfig the CS */ + mmio_write_32(IMX_WDOG3_BASE, wdog3[0]); + /* set the tiemout value */ + mmio_write_32(IMX_WDOG3_BASE + 0x8, wdog3[1]); + + /* wait for the lock status */ + while ((mmio_read_32(IMX_WDOG3_BASE) & BIT(11))) { + ; + } + + /* wait for the config done */ + while (!(mmio_read_32(IMX_WDOG3_BASE) & BIT(10))) { + ; + } +} + +static uint32_t lpuart_regs[4]; +#define LPUART_BAUD 0x10 +#define LPUART_CTRL 0x18 +#define LPUART_FIFO 0x28 +#define LPUART_WATER 0x2c + +void lpuart_save(void) +{ + lpuart_regs[0] = mmio_read_32(IMX_LPUART5_BASE + LPUART_BAUD); + lpuart_regs[1] = mmio_read_32(IMX_LPUART5_BASE + LPUART_FIFO); + lpuart_regs[2] = mmio_read_32(IMX_LPUART5_BASE + LPUART_WATER); + lpuart_regs[3] = mmio_read_32(IMX_LPUART5_BASE + LPUART_CTRL); +} + +void lpuart_restore(void) +{ + mmio_write_32(IMX_LPUART5_BASE + LPUART_BAUD, lpuart_regs[0]); + mmio_write_32(IMX_LPUART5_BASE + LPUART_FIFO, lpuart_regs[1]); + mmio_write_32(IMX_LPUART5_BASE + LPUART_WATER, lpuart_regs[2]); + mmio_write_32(IMX_LPUART5_BASE + LPUART_CTRL, lpuart_regs[3]); +} + +bool is_lpav_owned_by_apd(void) +{ + return (mmio_read_32(0x2802b044) & BIT(7)) ? true : false; +} + +void lpav_ctx_save(void) +{ + unsigned int i; + uint32_t val; + + /* CGC2 save */ + for (i = 0U; i < ARRAY_SIZE(cgc2); i++) { + cgc2[i][1] = mmio_read_32(cgc2[i][0]); + } + + /* PLL4 */ + for (i = 0U; i < ARRAY_SIZE(pll4); i++) { + pll4[i][1] = mmio_read_32(pll4[i][0]); + } + + /* PCC5 save */ + for (i = 0U; i < ARRAY_SIZE(pcc5_0); i++) { + val = mmio_read_32(IMX_PCC5_BASE + i * 4); + if (val & PCC_PR) { + pcc5_0[i] = val; + } + } + + for (i = 0U; i < ARRAY_SIZE(pcc5_1); i++) { + val = mmio_read_32(pcc5_1[i][0]); + if (val & PCC_PR) { + pcc5_1[i][1] = val; + } + } + + /* LPAV SIM save */ + for (i = 0U; i < ARRAY_SIZE(lpav_sim); i++) { + lpav_sim[i][1] = mmio_read_32(lpav_sim[i][0]); + } + + /* Save GPIO port D */ + gpio_save(&lpav_gpio_ctx, LPAV_GPIO_CTRL_NUM); + + /* put DDR into retention */ + dram_enter_retention(); +} + +void lpav_ctx_restore(void) +{ + unsigned int i; + + /* PLL4 */ + for (i = 0U; i < 9U; i++) { + mmio_write_32(pll4[i][0], pll4[i][1]); + } + + /* wait for PLL4 lock */ + while (!(mmio_read_32(pll4[8][0]) & BIT(24))) { + ; + } + + /* restore the PLL4 PFDs */ + mmio_write_32(pll4[9][0], pll4[9][1] & ~(BIT(31) | BIT(23) | BIT(15) | BIT(7))); + mmio_write_32(pll4[9][0], pll4[9][1]); + + /* wait for the PFD is stable */ + while (!(mmio_read_32(pll4[9][0]) & PFD_VALID_MASK)) { + ; + } + + /* CGC2 restore */ + for (i = 0U; i < ARRAY_SIZE(cgc2); i++) { + mmio_write_32(cgc2[i][0], cgc2[i][1]); + } + + /* PCC5 restore */ + for (i = 0U; i < ARRAY_SIZE(pcc5_0); i++) { + if (pcc5_0[i] & PCC_PR) { + mmio_write_32(IMX_PCC5_BASE + i * 4, pcc5_0[i]); + } + } + + for (i = 0U; i < ARRAY_SIZE(pcc5_1); i++) { + if (pcc5_1[i][1] & PCC_PR) { + mmio_write_32(pcc5_1[i][0], pcc5_1[i][1]); + } + } + + /* LPAV_SIM */ + for (i = 0U; i < ARRAY_SIZE(lpav_sim); i++) { + mmio_write_32(lpav_sim[i][0], lpav_sim[i][1]); + } + + gpio_restore(&lpav_gpio_ctx, LPAV_GPIO_CTRL_NUM); + /* DDR retention exit */ + dram_exit_retention(); +} + +void imx_apd_ctx_save(unsigned int proc_num) +{ + unsigned int i; + uint32_t val; + + /* enable LPUART5's clock by default */ + mmio_setbits_32(IMX_PCC3_BASE + 0xe8, BIT(30)); + + /* save the gic config */ + plat_gic_save(proc_num, &imx_gicv3_ctx); + + cmc1_pmprot = mmio_read_32(IMX_CMC1_BASE + 0x18); + cmc1_srie = mmio_read_32(IMX_CMC1_BASE + 0x8c); + + /* save the PCC3 */ + for (i = 0U; i < ARRAY_SIZE(pcc3); i++) { + /* save the pcc if it is exist */ + val = mmio_read_32(IMX_PCC3_BASE + i * 4); + if (val & PCC_PR) { + pcc3[i] = val; + } + } + + /* save the PCC4 */ + for (i = 0U; i < ARRAY_SIZE(pcc4); i++) { + /* save the pcc if it is exist */ + val = mmio_read_32(IMX_PCC4_BASE + i * 4); + if (val & PCC_PR) { + pcc4[i] = val; + } + } + + /* save the CGC1 */ + cgc1_save(); + + wdog3_save(); + + gpio_save(apd_gpio_ctx, APD_GPIO_CTRL_NUM); + + iomuxc_save(); + + tpm5_save(); + + lpuart_save(); + + /* + * save the lpav ctx & put the ddr into retention + * if lpav master is assigned to APD domain. + */ + if (is_lpav_owned_by_apd()) { + lpav_ctx_save(); + } +} + +void xrdc_reinit(void) +{ + xrdc_apply_apd_config(); + xrdc_apply_lpav_config(); + + xrdc_enable(); +} + +void s400_release_caam(void) +{ + uint32_t msg, resp; + + mmio_write_32(S400_MU_TRx(0), 0x17d70206); + mmio_write_32(S400_MU_TRx(1), 0x7); + + do { + resp = mmio_read_32(S400_MU_RSR); + } while ((resp & 0x3) != 0x3); + + msg = mmio_read_32(S400_MU_RRx(0)); + resp = mmio_read_32(S400_MU_RRx(1)); + + VERBOSE("resp %x; %x", msg, resp); +} + +void imx_apd_ctx_restore(unsigned int proc_num) +{ + unsigned int i; + + /* restore the CCG1 */ + cgc1_restore(); + + for (i = 0U; i < ARRAY_SIZE(pcc3); i++) { + /* save the pcc if it is exist */ + if (pcc3[i] & PCC_PR) { + mmio_write_32(IMX_PCC3_BASE + i * 4, pcc3[i]); + } + } + + for (i = 0U; i < ARRAY_SIZE(pcc4); i++) { + if (pcc4[i] & PCC_PR) { + mmio_write_32(IMX_PCC4_BASE + i * 4, pcc4[i]); + } + } + + wdog3_restore(); + + iomuxc_restore(); + + tpm5_restore(); + + xrdc_reinit(); + + /* Restore GPIO after xrdc_reinit, otherwise MSCs are invalid */ + gpio_restore(apd_gpio_ctx, APD_GPIO_CTRL_NUM); + + /* restore the gic config */ + plat_gic_restore(proc_num, &imx_gicv3_ctx); + + mmio_write_32(IMX_CMC1_BASE + 0x18, cmc1_pmprot); + mmio_write_32(IMX_CMC1_BASE + 0x8c, cmc1_srie); + + /* enable LPUART5's clock by default */ + mmio_setbits_32(IMX_PCC3_BASE + 0xe8, BIT(30)); + + /* restore the console lpuart */ + lpuart_restore(); + + /* FIXME: make uart work for ATF */ + mmio_write_32(IMX_LPUART_BASE + 0x18, 0xc0000); + + /* Allow M core to reset A core */ + mmio_clrbits_32(IMX_MU0B_BASE + 0x10, BIT(2)); + /* + * Ask S400 to release caam to APD as it is owned by s400 + */ + s400_release_caam(); + + /* re-init the caam */ + imx8ulp_caam_init(); + + /* + * ack the upower, seems a necessary steps, otherwise the upower can + * not response to the new API service call. put this just before the + * ddr retention exit because that the dram retention exit flow need to + * communicate with upower. + */ + upower_wait_resp(); + + /* + * restore the lpav ctx & make ddr out of retention + * if lpav master is assigned to APD domain. + */ + if (is_lpav_owned_by_apd()) { + lpav_ctx_restore(); + } +} + +#define DGO_CTRL1 U(0xc) +#define USB_WAKEUP U(0x44) +#define USB1_PHY_DPD_WAKEUP_EN BIT_32(5) +#define USB0_PHY_DPD_WAKEUP_EN BIT_32(4) +#define USB1_PHY_WAKEUP_ISO_DISABLE BIT_32(1) +#define USB0_PHY_WAKEUP_ISO_DISABLE BIT_32(0) + +void usb_wakeup_enable(bool enable) +{ + if (enable) { + mmio_setbits_32(IMX_SIM1_BASE + USB_WAKEUP, + USB1_PHY_WAKEUP_ISO_DISABLE | USB0_PHY_WAKEUP_ISO_DISABLE); + mmio_setbits_32(IMX_SIM1_BASE + DGO_CTRL1, BIT(0)); + while (!(mmio_read_32(IMX_SIM1_BASE + DGO_CTRL1) & BIT(1))) { + ; + } + + mmio_clrbits_32(IMX_SIM1_BASE + DGO_CTRL1, BIT(0)); + mmio_write_32(IMX_SIM1_BASE + DGO_CTRL1, BIT(1)); + + /* Need to delay for a while to make sure the wakeup logic can work */ + udelay(500); + + mmio_setbits_32(IMX_SIM1_BASE + USB_WAKEUP, + USB1_PHY_DPD_WAKEUP_EN | USB0_PHY_DPD_WAKEUP_EN); + mmio_setbits_32(IMX_SIM1_BASE + DGO_CTRL1, BIT(0)); + while (!(mmio_read_32(IMX_SIM1_BASE + DGO_CTRL1) & BIT(1))) { + ; + } + + mmio_clrbits_32(IMX_SIM1_BASE + DGO_CTRL1, BIT(0)); + mmio_write_32(IMX_SIM1_BASE + DGO_CTRL1, BIT(1)); + } else { + /* + * USBx_PHY_DPD_WAKEUP_EN should be cleared before USB0_PHY_WAKEUP_ISO_DISABLE + * to provide the correct the wake-up functionality. + */ + mmio_write_32(IMX_SIM1_BASE + USB_WAKEUP, USB1_PHY_WAKEUP_ISO_DISABLE | + USB0_PHY_WAKEUP_ISO_DISABLE); + mmio_write_32(IMX_SIM1_BASE + DGO_CTRL1, BIT(0)); + while (!(mmio_read_32(IMX_SIM1_BASE + DGO_CTRL1) & BIT(1))) { + ; + } + + mmio_clrbits_32(IMX_SIM1_BASE + DGO_CTRL1, BIT(0)); + mmio_write_32(IMX_SIM1_BASE + DGO_CTRL1, BIT(1)); + } +} diff --git a/plat/imx/imx8ulp/dram.c b/plat/imx/imx8ulp/dram.c new file mode 100644 index 00000000..00a52208 --- /dev/null +++ b/plat/imx/imx8ulp/dram.c @@ -0,0 +1,798 @@ +/* + * Copyright 2021-2024 NXP + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <assert.h> +#include <stdbool.h> + +#include <arch_helpers.h> +#include <bl31/interrupt_mgmt.h> +#include <common/runtime_svc.h> +#include <lib/mmio.h> +#include <lib/spinlock.h> +#include <plat/common/platform.h> + +#include <platform_def.h> + +#include <dram.h> +#include <upower_api.h> + +#define PHY_FREQ_SEL_INDEX(x) ((x) << 16) +#define PHY_FREQ_MULTICAST_EN(x) ((x) << 8) +#define DENALI_PHY_1537 U(0x5804) + +#define IMX_DDRC_BASE U(0x2E060000) +#define SAVED_DRAM_DATA_BASE U(0x20055000) +#define DENALI_CTL_143 U(0x23C) +#define DENALI_CTL_144 U(0x240) +#define DENALI_CTL_146 U(0x248) +#define LP_STATE_CS_IDLE U(0x404000) +#define LP_STATE_CS_PD_CG U(0x4F4F00) +#define LPI_WAKEUP_EN_SHIFT U(8) +#define IMX_LPAV_SIM_BASE 0x2DA50000 +#define LPDDR_CTRL 0x14 +#define LPDDR_AUTO_LP_MODE_DISABLE BIT(24) +#define SOC_LP_CMD_SHIFT U(15) +#define LPDDR_CTRL2 0x18 +#define LPDDR_EN_CLKGATE (0x1<<17) +#define LPDDR_MAX_CLKDIV_EN (0x1 << 16) +#define LP_AUTO_ENTRY_EN 0x4 +#define LP_AUTO_EXIT_EN 0xF + +#define DENALI_CTL_00 U(0x0) +#define DENALI_CTL_23 U(0x5c) +#define DFIBUS_FREQ_INIT_SHIFT U(24) +#define TSREF2PHYMSTR_SHIFT U(8) +#define TSREF2PHYMSTR_MASK GENMASK(13, 8) + +#define DENALI_CTL_24 U(0x60) +#define DENALI_CTL_25 U(0x64) + +#define DENALI_CTL_93 U(0x174) +#define PWRUP_SREFRESH_EXIT BIT(0) + +#define DENALI_CTL_127 U(0x1fc) +#define PHYMSTR_TRAIN_AFTER_INIT_COMPLETE BIT(16) + +#define DENALI_CTL_147 U(0x24c) +#define DENALI_CTL_153 U(0x264) +#define PCPCS_PD_EN BIT(8) + +#define DENALI_CTL_249 U(0x3E4) +#define DENALI_CTL_266 U(0x428) + +#define DENALI_PHY_1547 U(0x582c) +#define PHY_LP4_BOOT_DISABLE BIT(8) + +#define DENALI_PHY_1559 U(0x585c) +#define DENALI_PHY_1590 U(0x58D8) + +#define DENALI_PI_00 U(0x2000) +#define DENALI_PI_04 U(0x2010) +#define DENALI_PI_52 U(0x20D0) +#define DENALI_PI_26 U(0x2068) +#define DENALI_PI_33 U(0x2084) +#define DENALI_PI_65 U(0x2104) +#define DENALI_PI_77 U(0x2134) +#define DENALI_PI_134 U(0x2218) +#define DENALI_PI_131 U(0x220C) +#define DENALI_PI_132 U(0x2210) +#define DENALI_PI_134 U(0x2218) +#define DENALI_PI_137 U(0x2224) +#define DENALI_PI_174 U(0x22B8) +#define DENALI_PI_175 U(0x22BC) +#define DENALI_PI_181 U(0x22D4) +#define DENALI_PI_182 U(0x22D8) +#define DENALI_PI_191 U(0x22FC) +#define DENALI_PI_192 U(0x2300) +#define DENALI_PI_212 U(0x2350) +#define DENALI_PI_214 U(0x2358) +#define DENALI_PI_217 U(0x2364) + +#define LPDDR3_TYPE U(0x7) +#define LPDDR4_TYPE U(0xB) + +extern void upower_wait_resp(void); + +struct dram_cfg_param { + uint32_t reg; + uint32_t val; +}; + +struct dram_timing_info { + /* ddr controller config */ + struct dram_cfg_param *ctl_cfg; + unsigned int ctl_cfg_num; + /* pi config */ + struct dram_cfg_param *pi_cfg; + unsigned int pi_cfg_num; + /* phy freq1 config */ + struct dram_cfg_param *phy_f1_cfg; + unsigned int phy_f1_cfg_num; + /* phy freq2 config */ + struct dram_cfg_param *phy_f2_cfg; + unsigned int phy_f2_cfg_num; + /* automatic low power config */ + struct dram_cfg_param *auto_lp_cfg; + unsigned int auto_lp_cfg_num; + /* initialized drate table */ + unsigned int fsp_table[3]; +}; + +#define CTL_NUM U(680) +#define PI_NUM U(298) +#define PHY_NUM U(1654) +#define PHY_DIFF_NUM U(49) +#define AUTO_LP_NUM U(3) +struct dram_cfg { + uint32_t ctl_cfg[CTL_NUM]; + uint32_t pi_cfg[PI_NUM]; + uint32_t phy_full[PHY_NUM]; + uint32_t phy_diff[PHY_DIFF_NUM]; + uint32_t auto_lp_cfg[AUTO_LP_NUM]; +}; + +struct dram_timing_info *info; +struct dram_cfg *dram_timing_cfg; + +/* mark if dram cfg is already saved */ +static bool dram_cfg_saved; +static bool dram_auto_lp_true; +static uint32_t dram_class, dram_ctl_143; + +/* PHY register index for frequency diff */ +uint32_t freq_specific_reg_array[PHY_DIFF_NUM] = { +90, 92, 93, 96, 97, 100, 101, 102, 103, 104, 114, +346, 348, 349, 352, 353, 356, 357, 358, 359, 360, +370, 602, 604, 605, 608, 609, 612, 613, 614, 615, +616, 626, 858, 860, 861, 864, 865, 868, 869, 870, +871, 872, 882, 1063, 1319, 1566, 1624, 1625 +}; + +/* lock used for DDR DVFS */ +spinlock_t dfs_lock; +static volatile uint32_t core_count; +static volatile bool in_progress; +static volatile bool sys_dvfs; +static int num_fsp; + +static void ddr_init(void) +{ + unsigned int i; + + /* restore the ddr ctl config */ + for (i = 0U; i < CTL_NUM; i++) { + mmio_write_32(IMX_DDRC_BASE + i * 4, dram_timing_cfg->ctl_cfg[i]); + } + + /* load the PI registers */ + for (i = 0U; i < PI_NUM; i++) { + mmio_write_32(IMX_DDRC_BASE + 0x2000 + i * 4, dram_timing_cfg->pi_cfg[i]); + } + + + /* restore all PHY registers for all the fsp. */ + mmio_write_32(IMX_DDRC_BASE + DENALI_PHY_1537, 0x100); + /* restore all the phy configs */ + for (i = 0U; i < PHY_NUM; i++) { + /* skip the reserved registers space */ + if (i >= 121U && i <= 255U) { + continue; + } + if (i >= 377U && i <= 511U) { + continue; + } + if (i >= 633U && i <= 767U) { + continue; + } + if (i >= 889U && i <= 1023U) { + continue; + } + if (i >= 1065U && i <= 1279U) { + continue; + } + if (i >= 1321U && i <= 1535U) { + continue; + } + mmio_write_32(IMX_DDRC_BASE + 0x4000 + i * 4, dram_timing_cfg->phy_full[i]); + } + + if (dram_class == LPDDR4_TYPE) { + /* restore only the diff. */ + mmio_write_32(IMX_DDRC_BASE + DENALI_PHY_1537, 0x0); + for (i = 0U; i < PHY_DIFF_NUM; i++) { + mmio_write_32(IMX_DDRC_BASE + 0x4000 + freq_specific_reg_array[i] * 4, + dram_timing_cfg->phy_diff[i]); + } + } + + /* Re-enable MULTICAST mode */ + mmio_write_32(IMX_DDRC_BASE + DENALI_PHY_1537, PHY_FREQ_MULTICAST_EN(1)); +} + +void dram_lp_auto_disable(void) +{ + uint32_t lp_auto_en; + + dram_timing_cfg = (struct dram_cfg *)(SAVED_DRAM_DATA_BASE + + sizeof(struct dram_timing_info)); + lp_auto_en = (mmio_read_32(IMX_DDRC_BASE + DENALI_CTL_146) & (LP_AUTO_ENTRY_EN << 24)); + /* Save initial config */ + dram_ctl_143 = mmio_read_32(IMX_DDRC_BASE + DENALI_CTL_143); + + if (lp_auto_en && !dram_auto_lp_true) { + /* 0.a Save DDRC auto low-power mode parameter */ + dram_timing_cfg->auto_lp_cfg[0] = mmio_read_32(IMX_DDRC_BASE + DENALI_CTL_144); + dram_timing_cfg->auto_lp_cfg[1] = mmio_read_32(IMX_DDRC_BASE + DENALI_CTL_147); + dram_timing_cfg->auto_lp_cfg[2] = mmio_read_32(IMX_DDRC_BASE + DENALI_CTL_146); + /* Set LPI_SRPD_LONG_MCCLK_GATE_WAKEUP_F2 to Maximum */ + mmio_setbits_32(IMX_DDRC_BASE + DENALI_CTL_143, 0xF << 24); + /* 0.b Disable DDRC auto low-power mode interface */ + mmio_clrbits_32(IMX_DDRC_BASE + DENALI_CTL_146, LP_AUTO_ENTRY_EN << 24); + /* 0.c Read any location to get DRAM out of Self-refresh */ + mmio_read_32(DEVICE2_BASE); + /* 0.d Confirm DRAM is out of Self-refresh */ + while ((mmio_read_32(IMX_DDRC_BASE + DENALI_CTL_146) & + LP_STATE_CS_PD_CG) != LP_STATE_CS_IDLE) { + ; + } + /* 0.e Disable DDRC auto low-power exit */ + mmio_clrbits_32(IMX_DDRC_BASE + DENALI_CTL_147, LP_AUTO_EXIT_EN); + /* dram low power mode flag */ + dram_auto_lp_true = true; + } +} + +void dram_lp_auto_enable(void) +{ + /* Switch back to Auto Low-power mode */ + if (dram_auto_lp_true) { + /* 12.a Confirm DRAM is out of Self-refresh */ + while ((mmio_read_32(IMX_DDRC_BASE + DENALI_CTL_146) & + LP_STATE_CS_PD_CG) != LP_STATE_CS_IDLE) { + ; + } + /* 12.b Enable DDRC auto low-power exit */ + /* + * 12.c TBC! : Set DENALI_CTL_144 [LPI_CTRL_REQ_EN[24]] and + * [DFI_LP_VERSION[16]] back to default settings = 1b'1. + */ + /* + * 12.d Reconfigure DENALI_CTL_144 [LPI_WAKEUP_EN[5:0]] bit + * LPI_WAKEUP_EN[3] = 1b'1. + */ + mmio_write_32(IMX_DDRC_BASE + DENALI_CTL_144, dram_timing_cfg->auto_lp_cfg[0]); + mmio_write_32(IMX_DDRC_BASE + DENALI_CTL_147, dram_timing_cfg->auto_lp_cfg[1]); + /* 12.e Re-enable DDRC auto low-power mode interface */ + mmio_write_32(IMX_DDRC_BASE + DENALI_CTL_146, dram_timing_cfg->auto_lp_cfg[2]); + /* restore ctl config */ + mmio_write_32(IMX_DDRC_BASE + DENALI_CTL_143, dram_ctl_143); + /* dram low power mode flag */ + dram_auto_lp_true = false; + } +} + +void dram_enter_self_refresh(void) +{ + /* disable auto low power interface */ + dram_lp_auto_disable(); + /* 1. config the PCC_LPDDR4[SSADO] to 2b'11 for ACK domain 0/1's STOP */ + mmio_setbits_32(IMX_PCC5_BASE + 0x108, 0x2 << 22); + /* 1.a Clock gate PCC_LPDDR4[CGC] and no software reset PCC_LPDDR4[SWRST] */ + mmio_setbits_32(IMX_PCC5_BASE + 0x108, (BIT(30) | BIT(28))); + + /* + * 2. Make sure the DENALI_CTL_144[LPI_WAKEUP_EN[5:0]] has the bit + * LPI_WAKEUP_EN[3] = 1b'1. This enables the option 'self-refresh + * long with mem and ctlr clk gating or self-refresh power-down long + * with mem and ctlr clk gating' + */ + mmio_setbits_32(IMX_DDRC_BASE + DENALI_CTL_144, BIT(3) << LPI_WAKEUP_EN_SHIFT); + /* TODO: Needed ? 2.a DENALI_CTL_144[LPI_TIMER_WAKEUP_F2] */ + //mmio_setbits_32(IMX_DDRC_BASE + DENALI_CTL_144, BIT(0)); + + /* + * 3a. Config SIM_LPAV LPDDR_CTRL[LPDDR_AUTO_LP_MODE_DISABLE] to 1b'0(enable + * the logic to automatic handles low power entry/exit. This is the recommended + * option over handling through software. + * 3b. Config the SIM_LPAV LPDDR_CTRL[SOC_LP_CMD] to 6b'101001(encoding for + * self_refresh with both DDR controller and DRAM clock gate. THis is mandatory + * since LPPDR logic will be power gated). + */ + mmio_clrbits_32(IMX_LPAV_SIM_BASE + LPDDR_CTRL, LPDDR_AUTO_LP_MODE_DISABLE); + mmio_clrsetbits_32(IMX_LPAV_SIM_BASE + LPDDR_CTRL, + 0x3f << SOC_LP_CMD_SHIFT, 0x29 << SOC_LP_CMD_SHIFT); + /* 3.c clock gate ddr controller */ + mmio_setbits_32(IMX_LPAV_SIM_BASE + LPDDR_CTRL2, LPDDR_EN_CLKGATE); + /* 3.d lpddr max clk div en */ + mmio_clrbits_32(IMX_LPAV_SIM_BASE + LPDDR_CTRL2, LPDDR_MAX_CLKDIV_EN); +} + +void dram_exit_self_refresh(void) +{ + dram_lp_auto_enable(); +} + +void dram_enter_retention(void) +{ + unsigned int i; + + dram_lp_auto_disable(); + + /* 1. config the PCC_LPDDR4[SSADO] to 2b'11 for ACK domain 0/1's STOP */ + mmio_setbits_32(IMX_PCC5_BASE + 0x108, 0x2 << 22); + + /* + * 2. Make sure the DENALI_CTL_144[LPI_WAKEUP_EN[5:0]] has the bit + * LPI_WAKEUP_EN[3] = 1b'1. This enables the option 'self-refresh + * long with mem and ctlr clk gating or self-refresh power-down + * long with mem and ctlr clk gating' + */ + mmio_setbits_32(IMX_DDRC_BASE + DENALI_CTL_144, BIT(3) << LPI_WAKEUP_EN_SHIFT); + + /* + * 3a. Config SIM_LPAV LPDDR_CTRL[LPDDR_AUTO_LP_MODE_DISABLE] to 1b'0(enable + * the logic to automatic handles low power entry/exit. This is the recommended + * option over handling through software. + * 3b. Config the SIM_LPAV LPDDR_CTRL[SOC_LP_CMD] to 6b'101001(encoding for + * self_refresh with both DDR controller and DRAM clock gate. THis is mandatory + * since LPPDR logic will be power gated). + */ + mmio_clrbits_32(IMX_LPAV_SIM_BASE + LPDDR_CTRL, LPDDR_AUTO_LP_MODE_DISABLE); + mmio_clrsetbits_32(IMX_LPAV_SIM_BASE + LPDDR_CTRL, + 0x3f << SOC_LP_CMD_SHIFT, 0x29 << SOC_LP_CMD_SHIFT); + + /* Save DDR Controller & PHY config. + * Set PHY_FREQ_SEL_MULTICAST_EN=0 & PHY_FREQ_SEL_INDEX=1. Read and store all + * the PHY registers for F2 into phy_f1_cfg, then read/store the diff between + * F1 & F2 into phy_f2_cfg. + */ + if (!dram_cfg_saved) { + info = (struct dram_timing_info *)SAVED_DRAM_DATA_BASE; + dram_timing_cfg = (struct dram_cfg *)(SAVED_DRAM_DATA_BASE + + sizeof(struct dram_timing_info)); + + /* get the dram type */ + dram_class = mmio_read_32(IMX_DDRC_BASE + DENALI_CTL_00); + dram_class = (dram_class >> 8) & 0xf; + + /* save the ctl registers */ + for (i = 0U; i < CTL_NUM; i++) { + dram_timing_cfg->ctl_cfg[i] = mmio_read_32(IMX_DDRC_BASE + i * 4); + } + dram_timing_cfg->ctl_cfg[0] = dram_timing_cfg->ctl_cfg[0] & 0xFFFFFFFE; + + /* save the PI registers */ + for (i = 0U; i < PI_NUM; i++) { + dram_timing_cfg->pi_cfg[i] = mmio_read_32(IMX_DDRC_BASE + 0x2000 + i * 4); + } + dram_timing_cfg->pi_cfg[0] = dram_timing_cfg->pi_cfg[0] & 0xFFFFFFFE; + + /* + * Read and store all PHY registers. full array is a full + * copy for all the setpoint + */ + if (dram_class == LPDDR4_TYPE) { + mmio_write_32(IMX_DDRC_BASE + DENALI_PHY_1537, 0x10000); + for (i = 0U; i < PHY_NUM; i++) { + /* Make sure MULTICASE is enabled */ + if (i == 1537U) { + dram_timing_cfg->phy_full[i] = 0x100; + } else { + dram_timing_cfg->phy_full[i] = mmio_read_32(IMX_DDRC_BASE + 0x4000 + i * 4); + } + } + + /* + * set PHY_FREQ_SEL_MULTICAST_EN=0 & PHY_FREQ_SEL_INDEX=0. + * Read and store only the diff. + */ + mmio_write_32(IMX_DDRC_BASE + DENALI_PHY_1537, 0x0); + /* save only the frequency based diff config to save memory */ + for (i = 0U; i < PHY_DIFF_NUM; i++) { + dram_timing_cfg->phy_diff[i] = mmio_read_32(IMX_DDRC_BASE + 0x4000 + + freq_specific_reg_array[i] * 4); + } + } else { + /* LPDDR3, only f1 need to save */ + for (i = 0U; i < info->phy_f1_cfg_num; i++) { + info->phy_f1_cfg[i].val = mmio_read_32(info->phy_f1_cfg[i].reg); + } + } + + dram_cfg_saved = true; + } +} + +void dram_exit_retention(void) +{ + uint32_t val; + + /* 1. Config the LPAV PLL4 and DDR clock for the desired LPDDR operating frequency. */ + mmio_setbits_32(IMX_PCC5_BASE + 0x108, BIT(30)); + + /* 2. Write PCC5.PCC_LPDDR4[SWRST] to 1b'1 to release LPDDR from reset. */ + mmio_setbits_32(IMX_PCC5_BASE + 0x108, BIT(28)); + + /* 3. Reload the LPDDR CTL/PI/PHY register */ + ddr_init(); + + if (dram_class == LPDDR4_TYPE) { + /* 4a. FIXME Set PHY_SET_DFI_INPUT_N parameters to 4'h1. LPDDR4 only */ + mmio_write_32(IMX_DDRC_BASE + DENALI_PHY_1559, 0x01010101); + + /* + * 4b. CTL PWRUP_SREFRESH_EXIT=1'b0 for disabling self refresh exit + * from controller. + */ + /* + * 4c. PI_PWRUP_SELF_REF_EXIT=1, PI_MC_PWRUP_SELF_REF_EXIT=0 for enabling + * self refresh exit from PI + */ + /* 4c. PI_INT_LVL_EN=0 to skip Initialization trainings. */ + /* + * 4d. PI_WRLVL_EN_F0/1/2= PI_CALVL_EN_F0/1/2= PI_RDLVL_EN_F0/1/2= + * PI_RDLVL_GATE_EN_F0/1/2= PI_WDQLVL_EN_F0/1/2=0x2. + * Enable non initialization trainings. + */ + /* 4e. PI_PWRUP_SREFRESH_EXIT_CS=0xF */ + /* 4f. PI_DLL_RESET=0x1 */ + mmio_setbits_32(IMX_DDRC_BASE + DENALI_PI_137, 0x1); + /* PI_PWRUP_SELF_REF_EXIT = 1 */ + mmio_setbits_32(IMX_DDRC_BASE + DENALI_PI_132, 0x01000000); + /* PI_MC_PWRUP_SELF_REF_EXIT = 0 */ + mmio_clrbits_32(IMX_DDRC_BASE + DENALI_PI_132, BIT(16)); + /* PI_INT_LVL_EN = 0 */ + mmio_clrbits_32(IMX_DDRC_BASE + DENALI_PI_04, BIT(0)); + /* PI_WRLVL_EN_F0 = 3, PI_WRLVL_EN_F1 = 3 */ + mmio_setbits_32(IMX_DDRC_BASE + DENALI_PI_174, 0x03030000); + /* PI_WRLVL_EN_F2 = 3 */ + mmio_setbits_32(IMX_DDRC_BASE + DENALI_PI_175, 0x03); + /* PI_CALVL_EN_F0 = 3, PI_CALVL_EN_F1 = 3 */ + mmio_setbits_32(IMX_DDRC_BASE + DENALI_PI_191, 0x03030000); + /* PI_CALVL_EN_F2 = 3 */ + mmio_setbits_32(IMX_DDRC_BASE + DENALI_PI_192, 0x03); + /* PI_WDQLVL_EN_F0 = 3 */ + mmio_setbits_32(IMX_DDRC_BASE + DENALI_PI_212, 0x300); + /* PI_WDQLVL_EN_F1 = 3 */ + mmio_setbits_32(IMX_DDRC_BASE + DENALI_PI_214, 0x03000000); + /* PI_WDQLVL_EN_F2 = 3 */ + mmio_setbits_32(IMX_DDRC_BASE + DENALI_PI_217, 0x300); + /* PI_EDLVL_EN_F0 = 3, PI_EDLVL_GATE_EN_F0 = 3 */ + mmio_setbits_32(IMX_DDRC_BASE + DENALI_PI_181, 0x03030000); + /* + * PI_RDLVL_EN_F1 = 3, PI_RDLVL_GATE_EN_F1 = 3, + * PI_RDLVL_EN_F2 = 3, PI_RDLVL_GATE_EN_F2 = 3 + */ + mmio_setbits_32(IMX_DDRC_BASE + DENALI_PI_182, 0x03030303); + /* PI_PWRUP_SREFRESH_EXIT_CS = 0xF */ + mmio_setbits_32(IMX_DDRC_BASE + DENALI_PI_134, 0x000F0000); + } else { + /* PI_DLL_RESET=1 */ + mmio_setbits_32(IMX_DDRC_BASE + DENALI_PI_137, 0x1); + /* PI_PWRUP_SELF_REF_EXIT=1 */ + mmio_setbits_32(IMX_DDRC_BASE + DENALI_PI_132, 0x01000000); + /* PI_MC_PWRUP_SELF_REF_EXIT=0 */ + mmio_clrbits_32(IMX_DDRC_BASE + DENALI_PI_132, BIT(16)); + /* PI_INT_LVL_EN=0 */ + mmio_clrbits_32(IMX_DDRC_BASE + DENALI_PI_04, BIT(0)); + /* PI_WRLVL_EN_F0=3 */ + mmio_setbits_32(IMX_DDRC_BASE + DENALI_PI_174, 0x00030000); + /* PI_CALVL_EN_F0=3 */ + mmio_setbits_32(IMX_DDRC_BASE + DENALI_PI_191, 0x00030000); + /* PI_RDLVL_EN_F0=3,PI_RDLVL_GATE_EN_F0=3 */ + mmio_setbits_32(IMX_DDRC_BASE + DENALI_PI_181, 0x03030000); + /* PI_PWRUP_SREFRESH_EXIT_CS=0xF */ + mmio_setbits_32(IMX_DDRC_BASE + DENALI_PI_134, 0x000F0000); + } + + mmio_write_32(IMX_DDRC_BASE + DENALI_CTL_144, 0x00002D00); + + /* Force in-order AXI read data */ + mmio_write_32(IMX_DDRC_BASE + DENALI_CTL_144, 0x1); + + /* + * Disable special R/W group switches so that R/W group placement + * is always at END of R/W group. + */ + mmio_write_32(IMX_DDRC_BASE + DENALI_CTL_249, 0x0); + + /* Reduce time for IO pad calibration */ + mmio_write_32(IMX_DDRC_BASE + DENALI_PHY_1590, 0x01000000); + + mmio_write_32(IMX_DDRC_BASE + DENALI_CTL_25, 0x00020100); + + /* PD disable */ + mmio_write_32(IMX_DDRC_BASE + DENALI_CTL_153, 0x04040000); + /* + * 5. Disable automatic LP entry and PCPCS modes LP_AUTO_ENTRY_EN + * to 1b'0, PCPCS_PD_EN to 1b'0 + */ + + upwr_xcp_set_ddr_retention(APD_DOMAIN, 0, NULL); + upower_wait_resp(); + + if (dram_class == LPDDR4_TYPE) { + /* 7. Write PI START parameter to 1'b1 */ + mmio_write_32(IMX_DDRC_BASE + DENALI_PI_00, 0x00000b01); + + /* 8. Write CTL START parameter to 1'b1 */ + mmio_write_32(IMX_DDRC_BASE + DENALI_CTL_00, 0x00000b01); + } else { + /* 7. Write PI START parameter to 1'b1 */ + mmio_write_32(IMX_DDRC_BASE + DENALI_PI_00, 0x00000701); + + /* 8. Write CTL START parameter to 1'b1 */ + mmio_write_32(IMX_DDRC_BASE + DENALI_CTL_00, 0x00000701); + } + + /* 9. DENALI_CTL_266: Wait for INT_STATUS_INIT=0x2 */ + do { + val = (mmio_read_32(IMX_DDRC_BASE + DENALI_CTL_266) >> 8) & 0xFF; + } while (val != 0x2); + + /* + * 10. Run SW trainings by setting PI_CALVL_REQ,PI_WRLVL_REQ,PI_RDLVL_GATE_REQ, + * PI_RDLVL_REQ,PI_WDQLVL_REQ(NA for LPDDR3) in same order. + */ + if (dram_class == LPDDR4_TYPE) { + mmio_setbits_32(IMX_DDRC_BASE + DENALI_PI_52, 0x10000); /* CALVL */ + mmio_setbits_32(IMX_DDRC_BASE + DENALI_PI_26, 0x100); /* WRLVL */ + mmio_setbits_32(IMX_DDRC_BASE + DENALI_PI_33, 0x10000); /* RDGATE */ + mmio_setbits_32(IMX_DDRC_BASE + DENALI_PI_33, 0x100); /* RDQLVL */ + mmio_setbits_32(IMX_DDRC_BASE + DENALI_PI_65, 0x10000); /* WDQLVL */ + + /* 11. Wait for trainings to get complete by polling PI_INT_STATUS */ + while ((mmio_read_32(IMX_DDRC_BASE + DENALI_PI_77) & 0x07E00000) != 0x07E00000) { + ; + } + } else { + mmio_setbits_32(IMX_DDRC_BASE + DENALI_PI_52, 0x10000); /* CALVL */ + mmio_setbits_32(IMX_DDRC_BASE + DENALI_PI_26, 0x100); /* WRLVL */ + mmio_setbits_32(IMX_DDRC_BASE + DENALI_PI_33, 0x10000); /* RDGATE */ + mmio_setbits_32(IMX_DDRC_BASE + DENALI_PI_33, 0x100); /* RDQLVL */ + while ((mmio_read_32(IMX_DDRC_BASE + DENALI_PI_77) & 0x05E00000) != 0x05E00000) { + ; + } + } + + dram_lp_auto_enable(); +} + +#define LPDDR_DONE (0x1<<4) +#define SOC_FREQ_CHG_ACK (0x1<<6) +#define SOC_FREQ_CHG_REQ (0x1<<7) +#define LPI_WAKEUP_EN (0x4<<8) +#define SOC_FREQ_REQ (0x1<<11) + +static void set_cgc2_ddrclk(uint8_t src, uint8_t div) +{ + + /* Wait until the reg is unlocked for writing */ + while (mmio_read_32(IMX_CGC2_BASE + 0x40) & BIT(31)) + ; + + mmio_write_32(IMX_CGC2_BASE + 0x40, (src << 28) | (div << 21)); + /* Wait for the clock switching done */ + while (!(mmio_read_32(IMX_CGC2_BASE + 0x40) & BIT(27))) + ; +} +static void set_ddr_clk(uint32_t ddr_freq) +{ + /* Disable DDR clock */ + mmio_clrbits_32(IMX_PCC5_BASE + 0x108, BIT(30)); + switch (ddr_freq) { + /* boot frequency ? */ + case 48: + set_cgc2_ddrclk(2, 0); + break; + /* default bypass frequency for fsp 1 */ + case 192: + set_cgc2_ddrclk(0, 1); + break; + case 384: + set_cgc2_ddrclk(0, 0); + break; + case 264: + set_cgc2_ddrclk(4, 3); + break; + case 528: + set_cgc2_ddrclk(4, 1); + break; + default: + break; + } + /* Enable DDR clock */ + mmio_setbits_32(IMX_PCC5_BASE + 0x108, BIT(30)); + + /* Wait until the reg is unlocked for writing */ + while (mmio_read_32(IMX_CGC2_BASE + 0x40) & BIT(31)) { + ; + } +} + +#define AVD_SIM_LPDDR_CTRL (IMX_LPAV_SIM_BASE + 0x14) +#define AVD_SIM_LPDDR_CTRL2 (IMX_LPAV_SIM_BASE + 0x18) +#define MAX_FSP_NUM U(3) +#define DDR_DFS_GET_FSP_COUNT 0x10 +#define DDR_BYPASS_DRATE U(400) + +extern int upower_pmic_i2c_write(uint32_t reg_addr, uint32_t reg_val); + +/* Normally, we only switch frequency between 1(bypass) and 2(highest) */ +int lpddr4_dfs(uint32_t freq_index) +{ + uint32_t lpddr_ctrl, lpddr_ctrl2; + uint32_t ddr_ctl_144; + + /* + * Valid index: 0 to 2 + * index 0: boot frequency + * index 1: bypass frequency + * index 2: highest frequency + */ + if (freq_index > 2U) { + return -1; + } + + /* + * increase the voltage to 1.1V firstly before increase frequency + * and APD enter OD mode + */ + if (freq_index == 2U && sys_dvfs) { + upower_pmic_i2c_write(0x22, 0x28); + } + + /* Enable LPI_WAKEUP_EN */ + ddr_ctl_144 = mmio_read_32(IMX_DDRC_BASE + DENALI_CTL_144); + mmio_setbits_32(IMX_DDRC_BASE + DENALI_CTL_144, LPI_WAKEUP_EN); + + /* put DRAM into long self-refresh & clock gating */ + lpddr_ctrl = mmio_read_32(AVD_SIM_LPDDR_CTRL); + lpddr_ctrl = (lpddr_ctrl & ~((0x3f << 15) | (0x3 << 9))) | (0x28 << 15) | (freq_index << 9); + mmio_write_32(AVD_SIM_LPDDR_CTRL, lpddr_ctrl); + + /* Gating the clock */ + lpddr_ctrl2 = mmio_read_32(AVD_SIM_LPDDR_CTRL2); + mmio_setbits_32(AVD_SIM_LPDDR_CTRL2, LPDDR_EN_CLKGATE); + + /* Request frequency change */ + mmio_setbits_32(AVD_SIM_LPDDR_CTRL, SOC_FREQ_REQ); + + do { + lpddr_ctrl = mmio_read_32(AVD_SIM_LPDDR_CTRL); + if (lpddr_ctrl & SOC_FREQ_CHG_REQ) { + /* Bypass mode */ + if (info->fsp_table[freq_index] < DDR_BYPASS_DRATE) { + /* Change to PLL bypass mode */ + mmio_write_32(IMX_LPAV_SIM_BASE, 0x1); + /* change the ddr clock source & frequency */ + set_ddr_clk(info->fsp_table[freq_index]); + } else { + /* Change to PLL unbypass mode */ + mmio_write_32(IMX_LPAV_SIM_BASE, 0x0); + /* change the ddr clock source & frequency */ + set_ddr_clk(info->fsp_table[freq_index] >> 1); + } + + mmio_clrsetbits_32(AVD_SIM_LPDDR_CTRL, SOC_FREQ_CHG_REQ, SOC_FREQ_CHG_ACK); + continue; + } + } while ((lpddr_ctrl & LPDDR_DONE) != 0); /* several try? */ + + /* restore the original setting */ + mmio_write_32(IMX_DDRC_BASE + DENALI_CTL_144, ddr_ctl_144); + mmio_write_32(AVD_SIM_LPDDR_CTRL2, lpddr_ctrl2); + + /* Check the DFS result */ + lpddr_ctrl = mmio_read_32(AVD_SIM_LPDDR_CTRL) & 0xF; + if (lpddr_ctrl != 0U) { + /* Must be something wrong, return failure */ + return -1; + } + + /* decrease the BUCK3 voltage after frequency changed to lower + * and APD in ND_MODE + */ + if (freq_index == 1U && sys_dvfs) { + upower_pmic_i2c_write(0x22, 0x20); + } + + /* DFS done successfully */ + return 0; +} + +/* for the non-primary core, waiting for DFS done */ +static uint64_t waiting_dvfs(uint32_t id, uint32_t flags, + void *handle, void *cookie) +{ + uint32_t irq; + + irq = plat_ic_acknowledge_interrupt(); + if (irq < 1022U) { + plat_ic_end_of_interrupt(irq); + } + + /* set the WFE done status */ + spin_lock(&dfs_lock); + core_count++; + dsb(); + spin_unlock(&dfs_lock); + + while (in_progress) { + wfe(); + } + + return 0; +} + +int dram_dvfs_handler(uint32_t smc_fid, void *handle, + u_register_t x1, u_register_t x2, u_register_t x3) +{ + unsigned int fsp_index = x1; + uint32_t online_cpus = x2 - 1; + uint64_t mpidr = read_mpidr_el1(); + unsigned int cpu_id = MPIDR_AFFLVL0_VAL(mpidr); + + /* Get the number of FSPs */ + if (x1 == DDR_DFS_GET_FSP_COUNT) { + SMC_RET2(handle, num_fsp, info->fsp_table[1]); + } + + /* start lpddr frequency scaling */ + in_progress = true; + sys_dvfs = x3 ? true : false; + dsb(); + + /* notify other core wait for scaling done */ + for (unsigned int i = 0; i < PLATFORM_CORE_COUNT; i++) + /* Skip raise SGI for current CPU */ + if (i != cpu_id) { + plat_ic_raise_el3_sgi(0x8, i); + } + + /* Make sure all the cpu in WFE */ + while (online_cpus != core_count) { + ; + } + + /* Flush the L1/L2 cache */ + dcsw_op_all(DCCSW); + + lpddr4_dfs(fsp_index); + + in_progress = false; + core_count = 0; + dsb(); + sev(); + isb(); + + SMC_RET1(handle, 0); +} + +void dram_init(void) +{ + uint32_t flags = 0; + uint32_t rc; + unsigned int i; + + /* Register the EL3 handler for DDR DVFS */ + set_interrupt_rm_flag(flags, NON_SECURE); + rc = register_interrupt_type_handler(INTR_TYPE_EL3, waiting_dvfs, flags); + if (rc) { + panic(); + } + + info = (struct dram_timing_info *)SAVED_DRAM_DATA_BASE; + + /* Get the num of the supported Fsp */ + for (i = 0; i < MAX_FSP_NUM; i++) { + if (!info->fsp_table[i]) { + break; + } + } + + num_fsp = (i > MAX_FSP_NUM) ? MAX_FSP_NUM : i; +} diff --git a/plat/imx/imx8ulp/imx8ulp_bl31_setup.c b/plat/imx/imx8ulp/imx8ulp_bl31_setup.c new file mode 100644 index 00000000..696f4b65 --- /dev/null +++ b/plat/imx/imx8ulp/imx8ulp_bl31_setup.c @@ -0,0 +1,186 @@ +/* + * Copyright 2021-2024 NXP + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <assert.h> +#include <stdbool.h> + +#include <arch_helpers.h> +#include <common/bl_common.h> +#include <common/debug.h> +#include <context.h> +#include <drivers/console.h> +#include <drivers/generic_delay_timer.h> +#include <lib/el3_runtime/context_mgmt.h> +#include <lib/mmio.h> +#include <lib/xlat_tables/xlat_tables_v2.h> +#include <plat/common/platform.h> +#include <platform_def.h> + +#include <dram.h> +#include <imx8_lpuart.h> +#include <imx8ulp_caam.h> +#include <imx_plat_common.h> +#include <plat_imx8.h> +#include <upower_api.h> +#include <xrdc.h> + +#define MAP_BL31_TOTAL \ + MAP_REGION_FLAT(BL31_BASE, BL31_LIMIT - BL31_BASE, MT_MEMORY | MT_RW | MT_SECURE) +#define MAP_BL31_RO \ + MAP_REGION_FLAT(BL_CODE_BASE, BL_CODE_END - BL_CODE_BASE, MT_MEMORY | MT_RO | MT_SECURE) +#define MAP_BL32_TOTAL MAP_REGION_FLAT(BL32_BASE, BL32_SIZE, MT_MEMORY | MT_RW) +#define MAP_COHERENT_MEM \ + MAP_REGION_FLAT(BL_COHERENT_RAM_BASE, (BL_COHERENT_RAM_END - BL_COHERENT_RAM_BASE), \ + MT_DEVICE | MT_RW | MT_SECURE) + +#define TRUSTY_PARAMS_LEN_BYTES (4096*2) + +static const mmap_region_t imx_mmap[] = { + DEVICE0_MAP, DEVICE1_MAP, DEVICE2_MAP, + ELE_MAP, SEC_SIM_MAP, SRAM0_MAP, + {0} +}; + +extern uint32_t upower_init(void); +extern void imx8ulp_init_scmi_server(void); + +static entry_point_info_t bl32_image_ep_info; +static entry_point_info_t bl33_image_ep_info; + +void bl31_early_platform_setup2(u_register_t arg0, u_register_t arg1, + u_register_t arg2, u_register_t arg3) +{ + static console_t console; + + /* config the TPM5 clock */ + mmio_write_32(IMX_PCC3_BASE + 0xd0, 0x92000000); + mmio_write_32(IMX_PCC3_BASE + 0xd0, 0xd2000000); + + /* enable the GPIO D,E,F non-secure access by default */ + mmio_write_32(IMX_PCC4_BASE + 0x78, 0xc0000000); + mmio_write_32(IMX_PCC4_BASE + 0x7c, 0xc0000000); + mmio_write_32(IMX_PCC5_BASE + 0x114, 0xc0000000); + + mmio_write_32(IMX_GPIOE_BASE + 0x10, 0xffffffff); + mmio_write_32(IMX_GPIOE_BASE + 0x14, 0x3); + mmio_write_32(IMX_GPIOE_BASE + 0x18, 0xffffffff); + mmio_write_32(IMX_GPIOE_BASE + 0x1c, 0x3); + + mmio_write_32(IMX_GPIOF_BASE + 0x10, 0xffffffff); + mmio_write_32(IMX_GPIOF_BASE + 0x14, 0x3); + mmio_write_32(IMX_GPIOF_BASE + 0x18, 0xffffffff); + mmio_write_32(IMX_GPIOF_BASE + 0x1c, 0x3); + + mmio_write_32(IMX_GPIOD_BASE + 0x10, 0xffffffff); + mmio_write_32(IMX_GPIOD_BASE + 0x14, 0x3); + mmio_write_32(IMX_GPIOD_BASE + 0x18, 0xffffffff); + mmio_write_32(IMX_GPIOD_BASE + 0x1c, 0x3); + + console_lpuart_register(IMX_LPUART_BASE, IMX_BOOT_UART_CLK_IN_HZ, + IMX_CONSOLE_BAUDRATE, &console); + + /* This console is only used for boot stage */ + console_set_scope(&console, CONSOLE_FLAG_BOOT | CONSOLE_FLAG_RUNTIME); + + bl33_image_ep_info.pc = PLAT_NS_IMAGE_OFFSET; + bl33_image_ep_info.spsr = plat_get_spsr_for_bl33_entry(); + SET_SECURITY_STATE(bl33_image_ep_info.h.attr, NON_SECURE); + +#if defined(SPD_opteed) || defined(SPD_trusty) + /* Populate entry point information for BL32 */ + SET_PARAM_HEAD(&bl32_image_ep_info, PARAM_EP, VERSION_1, 0); + SET_SECURITY_STATE(bl32_image_ep_info.h.attr, SECURE); + bl32_image_ep_info.pc = BL32_BASE; + bl32_image_ep_info.spsr = 0; + + /* Pass TEE base and size to bl33 */ + bl33_image_ep_info.args.arg1 = BL32_BASE; + bl33_image_ep_info.args.arg2 = BL32_SIZE; + +#ifdef SPD_trusty + bl32_image_ep_info.args.arg0 = BL32_SIZE; + bl32_image_ep_info.args.arg1 = BL32_BASE; +#else + /* Make sure memory is clean */ + mmio_write_32(BL32_FDT_OVERLAY_ADDR, 0); + bl33_image_ep_info.args.arg3 = BL32_FDT_OVERLAY_ADDR; + bl32_image_ep_info.args.arg3 = BL32_FDT_OVERLAY_ADDR; +#endif +#endif +} + +void bl31_plat_arch_setup(void) +{ + const mmap_region_t bl_regions[] = { + MAP_BL31_TOTAL, + MAP_BL31_RO, +#if USE_COHERENT_MEM + MAP_COHERENT_MEM, +#endif +#if defined(SPD_opteed) || defined(SPD_trusty) + MAP_BL32_TOTAL, +#endif + {0}, + }; + + setup_page_tables(bl_regions, imx_mmap); + enable_mmu_el3(0); + + /* TODO: Hack, refine this piece, scmi channel free */ + mmio_write_32(SRAM0_BASE + 0x4, 1); + + /* Allow M core to reset A core */ + mmio_clrbits_32(IMX_MU0B_BASE + 0x10, BIT(2)); +} + +void bl31_platform_setup(void) +{ + /* select the arch timer source */ + mmio_setbits_32(IMX_SIM1_BASE + 0x30, 0x8000000); + + generic_delay_timer_init(); + + plat_gic_driver_init(); + plat_gic_init(); + + imx8ulp_init_scmi_server(); + upower_init(); + + xrdc_apply_apd_config(); + xrdc_apply_lpav_config(); + xrdc_enable(); + + imx8ulp_caam_init(); + + dram_init(); +} + +entry_point_info_t *bl31_plat_get_next_image_ep_info(unsigned int type) +{ + if (type == NON_SECURE) { + return &bl33_image_ep_info; + } else { + return &bl32_image_ep_info; + } +} + +unsigned int plat_get_syscnt_freq2(void) +{ + return COUNTER_FREQUENCY; +} + +void bl31_plat_runtime_setup(void) +{ +} + +#ifdef SPD_trusty +void plat_trusty_set_boot_args(aapcs64_params_t *args) +{ + args->arg0 = BL32_SIZE; + args->arg1 = BL32_BASE; + args->arg2 = TRUSTY_PARAMS_LEN_BYTES; +} +#endif diff --git a/plat/imx/imx8ulp/imx8ulp_caam.c b/plat/imx/imx8ulp/imx8ulp_caam.c new file mode 100644 index 00000000..d150fe2c --- /dev/null +++ b/plat/imx/imx8ulp/imx8ulp_caam.c @@ -0,0 +1,18 @@ +/* + * Copyright 2021-2024 NXP. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <lib/mmio.h> + +#include <imx8ulp_caam.h> + +void imx8ulp_caam_init(void) +{ + /* config CAAM JRaMID set MID to Cortex A */ + mmio_write_32(CAAM_JR0MID, CAAM_NS_MID); + mmio_write_32(CAAM_JR1MID, CAAM_NS_MID); + mmio_write_32(CAAM_JR2MID, CAAM_NS_MID); + mmio_write_32(CAAM_JR3MID, CAAM_NS_MID); +} diff --git a/plat/imx/imx8ulp/imx8ulp_psci.c b/plat/imx/imx8ulp/imx8ulp_psci.c new file mode 100644 index 00000000..628aceab --- /dev/null +++ b/plat/imx/imx8ulp/imx8ulp_psci.c @@ -0,0 +1,555 @@ +/* + * Copyright 2021-2024 NXP + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <stdbool.h> + +#include <arch.h> +#include <arch_helpers.h> +#include <common/debug.h> +#include <drivers/arm/gicv3.h> +#include <drivers/delay_timer.h> +#include <lib/mmio.h> +#include <lib/psci/psci.h> + +#include <plat_imx8.h> +#include <upower_api.h> + +extern void cgc1_save(void); +extern void cgc1_restore(void); +extern void imx_apd_ctx_save(unsigned int cpu); +extern void imx_apd_ctx_restore(unsigned int cpu); +extern void usb_wakeup_enable(bool enable); +extern void upower_wait_resp(void); +extern bool is_lpav_owned_by_apd(void); +extern void apd_io_pad_off(void); +extern int upower_pmic_i2c_read(uint32_t reg_addr, uint32_t *reg_val); +extern void imx8ulp_init_scmi_server(void); + +static uintptr_t secure_entrypoint; + +#define CORE_PWR_STATE(state) ((state)->pwr_domain_state[MPIDR_AFFLVL0]) +#define CLUSTER_PWR_STATE(state) ((state)->pwr_domain_state[MPIDR_AFFLVL1]) +#define SYSTEM_PWR_STATE(state) ((state)->pwr_domain_state[PLAT_MAX_PWR_LVL]) + +#define RVBARADDRx(c) (IMX_SIM1_BASE + 0x5c + 0x4 * (c)) +#define WKPUx(c) (IMX_SIM1_BASE + 0x3c + 0x4 * (c)) +#define AD_COREx_LPMODE(c) (IMX_CMC1_BASE + 0x50 + 0x4 * (c)) + +#define PMIC_CFG(v, m, msk) \ + { \ + .volt = (v), \ + .mode = (m), \ + .mode_msk = (msk), \ + } + +#define PAD_CFG(c, r, t) \ + { \ + .pad_close = (c), \ + .pad_reset = (r), \ + .pad_tqsleep = (t) \ + } + +#define BIAS_CFG(m, n, p, mbias) \ + { \ + .dombias_cfg = { \ + .mode = (m), \ + .rbbn = (n), \ + .rbbp = (p), \ + }, \ + .membias_cfg = {mbias}, \ + } + +#define SWT_BOARD(swt_on, msk) \ + { \ + .on = (swt_on), \ + .mask = (msk), \ + } + +#define SWT_MEM(a, p, m) \ + { \ + .array = (a), \ + .perif = (p), \ + .mask = (m), \ + } + +static int imx_pwr_set_cpu_entry(unsigned int cpu, unsigned int entry) +{ + mmio_write_32(RVBARADDRx(cpu), entry); + + /* set update bit */ + mmio_write_32(IMX_SIM1_BASE + 0x8, mmio_read_32(IMX_SIM1_BASE + 0x8) | BIT_32(24 + cpu)); + /* wait for ack */ + while (!(mmio_read_32(IMX_SIM1_BASE + 0x8) & BIT_32(26 + cpu))) { + } + + /* clear update bit */ + mmio_write_32(IMX_SIM1_BASE + 0x8, mmio_read_32(IMX_SIM1_BASE + 0x8) & ~BIT_32(24 + cpu)); + /* clear ack bit */ + mmio_write_32(IMX_SIM1_BASE + 0x8, mmio_read_32(IMX_SIM1_BASE + 0x8) | BIT_32(26 + cpu)); + + return 0; +} + +static volatile uint32_t cgc1_nicclk; +int imx_pwr_domain_on(u_register_t mpidr) +{ + unsigned int cpu = MPIDR_AFFLVL0_VAL(mpidr); + + imx_pwr_set_cpu_entry(cpu, secure_entrypoint); + + /* slow down the APD NIC bus clock */ + cgc1_nicclk = mmio_read_32(IMX_CGC1_BASE + 0x34); + mmio_clrbits_32(IMX_CGC1_BASE + 0x34, GENMASK_32(29, 28)); + + mmio_write_32(IMX_CMC1_BASE + 0x18, 0x3f); + mmio_write_32(IMX_CMC1_BASE + 0x50 + 0x4 * cpu, 0); + + /* enable wku wakeup for idle */ + mmio_write_32(IMX_SIM1_BASE + 0x3c + 0x4 * cpu, 0xffffffff); + + return PSCI_E_SUCCESS; +} + +void imx_pwr_domain_on_finish(const psci_power_state_t *target_state) +{ + imx_pwr_set_cpu_entry(0, IMX_ROM_ENTRY); + plat_gic_pcpu_init(); + plat_gic_cpuif_enable(); + + /* set APD NIC back to orignally setting */ + mmio_write_32(IMX_CGC1_BASE + 0x34, cgc1_nicclk); +} + +int imx_validate_ns_entrypoint(uintptr_t ns_entrypoint) +{ + return PSCI_E_SUCCESS; +} + +void imx_pwr_domain_off(const psci_power_state_t *target_state) +{ + unsigned int cpu = MPIDR_AFFLVL0_VAL(read_mpidr_el1()); + + plat_gic_cpuif_disable(); + + /* disable wakeup */ + mmio_write_32(WKPUx(cpu), 0); + + /* set core power mode to PD */ + mmio_write_32(AD_COREx_LPMODE(cpu), 0x3); +} + +/* APD power mode config */ +ps_apd_pwr_mode_cfgs_t apd_pwr_mode_cfgs = { + [DPD_PWR_MODE] = { + .swt_board_offs = 0x180, + .swt_mem_offs = 0x188, + .pmic_cfg = PMIC_CFG(0x23, 0x0, 0x2), + .pad_cfg = PAD_CFG(0x0, 0xc, 0x01e80a02), + .bias_cfg = BIAS_CFG(0x0, 0x2, 0x2, 0x0), + }, + + /* PD */ + [PD_PWR_MODE] = { + .swt_board_offs = 0x170, + .swt_mem_offs = 0x178, + .pmic_cfg = PMIC_CFG(0x23, 0x0, 0x2), + .pad_cfg = PAD_CFG(0x0, 0xc, 0x01e80a00), + .bias_cfg = BIAS_CFG(0x0, 0x2, 0x2, 0x0), + }, + + [ADMA_PWR_MODE] = { + .swt_board_offs = 0x120, + .swt_mem_offs = 0x128, + .pmic_cfg = PMIC_CFG(0x23, 0x0, 0x2), + .pad_cfg = PAD_CFG(0x0, 0x0, 0x0deb7a00), + .bias_cfg = BIAS_CFG(0x2, 0x2, 0x2, 0x0), + }, + + [ACT_PWR_MODE] = { + .swt_board_offs = 0x110, + .swt_mem_offs = 0x118, + .pmic_cfg = PMIC_CFG(0x23, 0x0, 0x2), + .pad_cfg = PAD_CFG(0x0, 0x0, 0x0deb7a00), + .bias_cfg = BIAS_CFG(0x2, 0x2, 0x2, 0x0), + }, +}; + +/* APD power switch config */ +ps_apd_swt_cfgs_t apd_swt_cfgs = { + [DPD_PWR_MODE] = { + .swt_board[0] = SWT_BOARD(0x0, 0x1fffc), + .swt_mem[0] = SWT_MEM(0x0, 0x0, 0x1ffff), + .swt_mem[1] = SWT_MEM(0x003fffff, 0x003fffff, 0x0), + }, + + [PD_PWR_MODE] = { + .swt_board[0] = SWT_BOARD(0x0, 0x00001fffc), + .swt_mem[0] = SWT_MEM(0x00010c00, 0x0, 0x1ffff), + .swt_mem[1] = SWT_MEM(0x003fffff, 0x003f0000, 0x0), + }, + + [ADMA_PWR_MODE] = { + .swt_board[0] = SWT_BOARD(0x15f74, 0x15f74), + .swt_mem[0] = SWT_MEM(0x0001fffd, 0x0001fffd, 0x1ffff), + .swt_mem[1] = SWT_MEM(0x003fffff, 0x003fffff, 0x0), + }, + + [ACT_PWR_MODE] = { + .swt_board[0] = SWT_BOARD(0x15f74, 0x15f74), + .swt_mem[0] = SWT_MEM(0x0001fffd, 0x0001fffd, 0x1ffff), + .swt_mem[1] = SWT_MEM(0x003fffff, 0x003fffff, 0x0), + }, +}; + +/* PMIC config for power down, LDO1 should be OFF */ +ps_apd_pmic_reg_data_cfgs_t pd_pmic_reg_cfgs = { + [0] = { + .tag = PMIC_REG_VALID_TAG, + .power_mode = PD_PWR_MODE, + .i2c_addr = 0x30, + .i2c_data = 0x9c, + }, + [1] = { + .tag = PMIC_REG_VALID_TAG, + .power_mode = PD_PWR_MODE, + .i2c_addr = 0x22, + .i2c_data = 0xb, + }, + [2] = { + .tag = PMIC_REG_VALID_TAG, + .power_mode = ACT_PWR_MODE, + .i2c_addr = 0x30, + .i2c_data = 0x9d, + }, + [3] = { + .tag = PMIC_REG_VALID_TAG, + .power_mode = ACT_PWR_MODE, + .i2c_addr = 0x22, + .i2c_data = 0x28, + }, +}; + +/* PMIC config for deep power down, BUCK3 should be OFF */ +ps_apd_pmic_reg_data_cfgs_t dpd_pmic_reg_cfgs = { + [0] = { + .tag = PMIC_REG_VALID_TAG, + .power_mode = DPD_PWR_MODE, + .i2c_addr = 0x21, + .i2c_data = 0x78, + }, + [1] = { + .tag = PMIC_REG_VALID_TAG, + .power_mode = DPD_PWR_MODE, + .i2c_addr = 0x30, + .i2c_data = 0x9c, + }, + [2] = { + .tag = PMIC_REG_VALID_TAG, + .power_mode = ACT_PWR_MODE, + .i2c_addr = 0x21, + .i2c_data = 0x79, + }, + [3] = { + .tag = PMIC_REG_VALID_TAG, + .power_mode = ACT_PWR_MODE, + .i2c_addr = 0x30, + .i2c_data = 0x9d, + }, +}; + +struct ps_pwr_mode_cfg_t *pwr_sys_cfg = (struct ps_pwr_mode_cfg_t *)UPWR_DRAM_SHARED_BASE_ADDR; + +void imx_set_pwr_mode_cfg(abs_pwr_mode_t mode) +{ + uint32_t volt; + + if (mode >= NUM_PWR_MODES) { + return; + } + + /* apd power mode config */ + memcpy(&pwr_sys_cfg->ps_apd_pwr_mode_cfg[mode], &apd_pwr_mode_cfgs[mode], + sizeof(struct ps_apd_pwr_mode_cfg_t)); + + /* apd power switch config */ + memcpy(&pwr_sys_cfg->ps_apd_swt_cfg[mode], &apd_swt_cfgs[mode], sizeof(swt_config_t)); + + /* + * BUCK3 & LDO1 can only be shutdown when LPAV is owned by APD side + * otherwise RTD side is responsible to control them in low power mode. + */ + if (is_lpav_owned_by_apd()) { + /* power off the BUCK3 in DPD mode */ + if (mode == DPD_PWR_MODE) { + memcpy(&pwr_sys_cfg->ps_apd_pmic_reg_data_cfg, &dpd_pmic_reg_cfgs, + sizeof(ps_apd_pmic_reg_data_cfgs_t)); + /* LDO1 should be power off in PD mode */ + } else if (mode == PD_PWR_MODE) { + /* overwrite the buck3 voltage setting in active mode */ + upower_pmic_i2c_read(0x22, &volt); + pd_pmic_reg_cfgs[3].i2c_data = volt; + memcpy(&pwr_sys_cfg->ps_apd_pmic_reg_data_cfg, &pd_pmic_reg_cfgs, + sizeof(ps_apd_pmic_reg_data_cfgs_t)); + } + } +} + +void imx_domain_suspend(const psci_power_state_t *target_state) +{ + unsigned int cpu = MPIDR_AFFLVL0_VAL(read_mpidr_el1()); + + if (is_local_state_off(CORE_PWR_STATE(target_state))) { + plat_gic_cpuif_disable(); + imx_pwr_set_cpu_entry(cpu, secure_entrypoint); + /* core put into power down */ + mmio_write_32(IMX_CMC1_BASE + 0x50 + 0x4 * cpu, 0x3); + /* FIXME config wakeup interrupt in WKPU */ + mmio_write_32(IMX_SIM1_BASE + 0x3c + 0x4 * cpu, 0x7fffffe3); + } else { + /* for core standby/retention mode */ + mmio_write_32(IMX_CMC1_BASE + 0x50 + 0x4 * cpu, 0x1); + mmio_write_32(IMX_SIM1_BASE + 0x3c + 0x4 * cpu, 0x7fffffe3); + dsb(); + write_scr_el3(read_scr_el3() | SCR_FIQ_BIT); + isb(); + } + + if (is_local_state_retn(CLUSTER_PWR_STATE(target_state))) { + /* + * just for sleep mode for now, need to update to + * support more modes, same for suspend finish call back. + */ + mmio_write_32(IMX_CMC1_BASE + 0x10, 0x1); + mmio_write_32(IMX_CMC1_BASE + 0x20, 0x1); + + } else if (is_local_state_off(CLUSTER_PWR_STATE(target_state))) { + /* + * for cluster off state, put cluster into power down mode, + * config the cluster clock to be off. + */ + mmio_write_32(IMX_CMC1_BASE + 0x10, 0x7); + mmio_write_32(IMX_CMC1_BASE + 0x20, 0xf); + } + + if (is_local_state_off(SYSTEM_PWR_STATE(target_state))) { + /* + * low power mode config info used by upower + * to do low power mode transition. + */ + imx_set_pwr_mode_cfg(ADMA_PWR_MODE); + imx_set_pwr_mode_cfg(ACT_PWR_MODE); + imx_set_pwr_mode_cfg(PD_PWR_MODE); + + /* clear the upower wakeup */ + upwr_xcp_set_rtd_apd_llwu(APD_DOMAIN, 0, NULL); + upower_wait_resp(); + + /* enable the USB wakeup */ + usb_wakeup_enable(true); + + /* config the WUU to enabled the wakeup source */ + mmio_write_32(IMX_PCC3_BASE + 0x98, 0xc0800000); + + /* !!! clear all the pad wakeup pending event */ + mmio_write_32(IMX_WUU1_BASE + 0x20, 0xffffffff); + + /* enable upower usb phy wakeup by default */ + mmio_setbits_32(IMX_WUU1_BASE + 0x18, BIT(4) | BIT(1) | BIT(0)); + + /* enabled all pad wakeup by default */ + mmio_write_32(IMX_WUU1_BASE + 0x8, 0xffffffff); + + /* save the AD domain context before entering PD mode */ + imx_apd_ctx_save(cpu); + } +} + +#define DRAM_LPM_STATUS U(0x2802b004) +void imx_domain_suspend_finish(const psci_power_state_t *target_state) +{ + unsigned int cpu = MPIDR_AFFLVL0_VAL(read_mpidr_el1()); + + if (is_local_state_off(SYSTEM_PWR_STATE(target_state))) { + /* restore the ap domain context */ + imx_apd_ctx_restore(cpu); + + /* clear the upower wakeup */ + upwr_xcp_set_rtd_apd_llwu(APD_DOMAIN, 0, NULL); + upower_wait_resp(); + + /* disable all pad wakeup */ + mmio_write_32(IMX_WUU1_BASE + 0x8, 0x0); + + /* clear all the pad wakeup pending event */ + mmio_write_32(IMX_WUU1_BASE + 0x20, 0xffffffff); + + /* + * disable the usb wakeup after resume to make sure the pending + * usb wakeup in WUU can be cleared successfully, otherwise, + * APD will resume failed in next PD mode. + */ + usb_wakeup_enable(false); + + /* re-init the SCMI channel */ + imx8ulp_init_scmi_server(); + } + + /* + * wait for DDR is ready when DDR is under the RTD + * side control for power saving + */ + while (mmio_read_32(DRAM_LPM_STATUS) != 0) { + ; + } + + /* + * when resume from low power mode, need to delay for a while + * before access the CMC register. + */ + udelay(5); + + /* clear cluster's LPM setting. */ + mmio_write_32(IMX_CMC1_BASE + 0x20, 0x0); + mmio_write_32(IMX_CMC1_BASE + 0x10, 0x0); + + /* clear core's LPM setting */ + mmio_write_32(IMX_CMC1_BASE + 0x50 + 0x4 * cpu, 0x0); + mmio_write_32(IMX_SIM1_BASE + 0x3c + 0x4 * cpu, 0x0); + + if (is_local_state_off(CORE_PWR_STATE(target_state))) { + imx_pwr_set_cpu_entry(0, IMX_ROM_ENTRY); + plat_gic_cpuif_enable(); + } else { + dsb(); + write_scr_el3(read_scr_el3() & (~SCR_FIQ_BIT)); + isb(); + } +} + +void __dead2 imx8ulp_pwr_domain_pwr_down_wfi(const psci_power_state_t *target_state) +{ + while (1) { + wfi(); + } +} + +void __dead2 imx8ulp_system_reset(void) +{ + imx_pwr_set_cpu_entry(0, IMX_ROM_ENTRY); + + /* Write invalid command to WDOG CNT to trigger reset */ + mmio_write_32(IMX_WDOG3_BASE + 0x4, 0x12345678); + + while (true) { + wfi(); + } +} + +int imx_validate_power_state(unsigned int power_state, + psci_power_state_t *req_state) +{ + int pwr_lvl = psci_get_pstate_pwrlvl(power_state); + int pwr_type = psci_get_pstate_type(power_state); + + if (pwr_lvl > PLAT_MAX_PWR_LVL) { + return PSCI_E_INVALID_PARAMS; + } + + if (pwr_type == PSTATE_TYPE_STANDBY) { + CORE_PWR_STATE(req_state) = PLAT_MAX_RET_STATE; + CLUSTER_PWR_STATE(req_state) = PLAT_MAX_RET_STATE; + } + + /* No power down state support */ + if (pwr_type == PSTATE_TYPE_POWERDOWN) { + return PSCI_E_INVALID_PARAMS; + } + + return PSCI_E_SUCCESS; +} + +void imx_get_sys_suspend_power_state(psci_power_state_t *req_state) +{ + unsigned int i; + + for (i = IMX_PWR_LVL0; i <= PLAT_MAX_PWR_LVL; i++) { + req_state->pwr_domain_state[i] = PLAT_POWER_DOWN_OFF_STATE; + } +} + +void __dead2 imx_system_off(void) +{ + unsigned int i; + + /* config the all the core into OFF mode and IRQ masked. */ + for (i = 0U; i < PLATFORM_CORE_COUNT; i++) { + /* disable wakeup from wkpu */ + mmio_write_32(WKPUx(i), 0x0); + + /* reset the core reset entry to 0x1000 */ + imx_pwr_set_cpu_entry(i, 0x1000); + + /* config the core power mode to off */ + mmio_write_32(AD_COREx_LPMODE(i), 0x3); + } + + plat_gic_cpuif_disable(); + + /* power off all the pad */ + apd_io_pad_off(); + + /* Config the power mode info for entering DPD mode and ACT mode */ + imx_set_pwr_mode_cfg(ADMA_PWR_MODE); + imx_set_pwr_mode_cfg(ACT_PWR_MODE); + imx_set_pwr_mode_cfg(DPD_PWR_MODE); + + /* Set the APD domain into DPD mode */ + mmio_write_32(IMX_CMC1_BASE + 0x10, 0x7); + mmio_write_32(IMX_CMC1_BASE + 0x20, 0x1f); + + /* make sure no pending upower wakeup */ + upwr_xcp_set_rtd_apd_llwu(APD_DOMAIN, 0, NULL); + upower_wait_resp(); + + /* enable the upower wakeup from wuu, act as APD boot up method */ + mmio_write_32(IMX_PCC3_BASE + 0x98, 0xc0800000); + mmio_setbits_32(IMX_WUU1_BASE + 0x18, BIT(4)); + + /* make sure no pad wakeup event is pending */ + mmio_write_32(IMX_WUU1_BASE + 0x20, 0xffffffff); + + wfi(); + + ERROR("power off failed.\n"); + panic(); +} + +static const plat_psci_ops_t imx_plat_psci_ops = { + .pwr_domain_on = imx_pwr_domain_on, + .pwr_domain_on_finish = imx_pwr_domain_on_finish, + .validate_ns_entrypoint = imx_validate_ns_entrypoint, + .system_off = imx_system_off, + .system_reset = imx8ulp_system_reset, + .pwr_domain_off = imx_pwr_domain_off, + .pwr_domain_suspend = imx_domain_suspend, + .pwr_domain_suspend_finish = imx_domain_suspend_finish, + .get_sys_suspend_power_state = imx_get_sys_suspend_power_state, + .validate_power_state = imx_validate_power_state, + .pwr_domain_pwr_down_wfi = imx8ulp_pwr_domain_pwr_down_wfi, +}; + +int plat_setup_psci_ops(uintptr_t sec_entrypoint, + const plat_psci_ops_t **psci_ops) +{ + secure_entrypoint = sec_entrypoint; + imx_pwr_set_cpu_entry(0, sec_entrypoint); + *psci_ops = &imx_plat_psci_ops; + + mmio_write_32(IMX_CMC1_BASE + 0x18, 0x3f); + mmio_write_32(IMX_SIM1_BASE + 0x3c, 0xffffffff); + + return 0; +} diff --git a/plat/imx/imx8ulp/include/dram.h b/plat/imx/imx8ulp/include/dram.h new file mode 100644 index 00000000..9ed8969a --- /dev/null +++ b/plat/imx/imx8ulp/include/dram.h @@ -0,0 +1,13 @@ +/* + * Copyright 2024 NXP + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef DRAM_H +#define DRAM_H + +void dram_init(void); + +#endif /* DRAM_H */ + diff --git a/plat/imx/imx8ulp/include/imx8ulp_caam.h b/plat/imx/imx8ulp/include/imx8ulp_caam.h new file mode 100644 index 00000000..1b93d7dd --- /dev/null +++ b/plat/imx/imx8ulp/include/imx8ulp_caam.h @@ -0,0 +1,24 @@ +/* + * Copyright 2021-2024 NXP. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef IMX8ULP_CAAM_H +#define IMX8ULP_CAAM_H + +#include <lib/utils_def.h> + +#include <platform_def.h> + +#define CAAM_JR0MID (IMX_CAAM_BASE + 0x10) +#define CAAM_JR1MID (IMX_CAAM_BASE + 0x18) +#define CAAM_JR2MID (IMX_CAAM_BASE + 0x20) +#define CAAM_JR3MID (IMX_CAAM_BASE + 0x28) +#define CAAM_NS_MID (0x7) + +#define JR0_BASE (IMX_CAAM_BASE + 0x1000) + +void imx8ulp_caam_init(void); + +#endif /* IMX8ULP_CAAM_H */ diff --git a/plat/imx/imx8ulp/include/platform_def.h b/plat/imx/imx8ulp/include/platform_def.h new file mode 100644 index 00000000..20c5851b --- /dev/null +++ b/plat/imx/imx8ulp/include/platform_def.h @@ -0,0 +1,124 @@ +/* + * Copyright 2021-2024 NXP + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef PLATFORM_DEF_H +#define PLATFORM_DEF_H + +#include <lib/utils_def.h> + +#define PLATFORM_LINKER_FORMAT "elf64-littleaarch64" +#define PLATFORM_LINKER_ARCH aarch64 + +#define PLATFORM_STACK_SIZE 0x400 +#define CACHE_WRITEBACK_GRANULE 64 + +#define PLAT_PRIMARY_CPU 0x0 +#define PLATFORM_MAX_CPU_PER_CLUSTER 2 +#define PLATFORM_CLUSTER_COUNT 1 +#define PLATFORM_CORE_COUNT 2 +#define PLATFORM_CLUSTER0_CORE_COUNT 2 +#define PLATFORM_CLUSTER1_CORE_COUNT 0 + +#define IMX_PWR_LVL0 MPIDR_AFFLVL0 +#define IMX_PWR_LVL1 MPIDR_AFFLVL1 +#define IMX_PWR_LVL2 MPIDR_AFFLVL2 + +#define PWR_DOMAIN_AT_MAX_LVL U(1) +#define PLAT_MAX_PWR_LVL U(2) + +#define PLAT_SLEEP_RET_STATE U(1) +#define PLAT_DEEP_SLEEP_RET_STATE U(2) +#define PLAT_MAX_RET_STATE U(3) + +#define PLAT_POWER_DOWN_OFF_STATE U(4) +#define PLAT_DEEP_POWER_DOWN_STATE U(5) +#define PLAT_MAX_OFF_STATE U(6) + +#define BL31_BASE 0x20040000 +#define BL31_LIMIT 0x20070000 + +#define PLAT_VIRT_ADDR_SPACE_SIZE (1ull << 32) +#define PLAT_PHY_ADDR_SPACE_SIZE (1ull << 32) + +#ifdef SPD_trusty +#define MAX_XLAT_TABLES 11 +#define MAX_MMAP_REGIONS 12 +#else +#define MAX_XLAT_TABLES 10 +#define MAX_MMAP_REGIONS 11 +#endif + +#define PLAT_GICD_BASE U(0x2d400000) +#define PLAT_GICR_BASE U(0x2d440000) +#define DEVICE0_BASE U(0x20000000) +#define DEVICE0_SIZE U(0x10000000) +#define DEVICE1_BASE U(0x30000000) +#define DEVICE1_SIZE U(0x10000000) +#define DEVICE2_BASE U(0x8ff00000) +#define DEVICE2_SIZE U(0x00001000) +#define IMX_LPUART4_BASE U(0x29390000) +#define IMX_LPUART5_BASE U(0x293a0000) +#define IMX_LPUART_BASE IMX_LPUART5_BASE +#define IMX_CAAM_BASE U(0x292e0000) +#define IMX_BOOT_UART_CLK_IN_HZ 24000000 +#define IMX_CONSOLE_BAUDRATE 115200 + +#define IMX_CGC1_BASE U(0x292c0000) +#define IMX_PCC3_BASE U(0x292d0000) +#define IMX_PCC4_BASE U(0x29800000) +#define IMX_SIM2_BASE U(0x2da50000) +#define IMX_CGC2_BASE U(0x2da60000) +#define IMX_PCC5_BASE U(0x2da70000) +#define IMX_MU0B_BASE U(0x29220000) +#define IMX_CMC1_BASE U(0x29240000) +#define IMX_WUU1_BASE U(0x29260000) +#define IMX_SIM1_BASE U(0x29290000) +#define IMX_GPIOD_BASE U(0x2e200000) +#define IMX_GPIOE_BASE U(0x2d000000) +#define IMX_GPIOF_BASE U(0x2d010000) +#define IMX_WDOG3_BASE U(0x292a0000) +#define IMX_TPM5_BASE U(0x29340000) + +#define SRAM0_BASE U(0x2201F000) + +#define IOMUXC_PTD_PCR_BASE U(0x298c0000) +#define IOMUXC_PTE_PCR_BASE U(0x298c0080) +#define IOMUXC_PTF_PCR_BASE U(0x298c0100) +#define IOMUXC_PSMI_BASE0 U(0x298c0800) +#define IOMUXC_PSMI_BASE1 U(0x298c0838) +#define IOMUXC_PSMI_BASE2 U(0x298c0954) +#define IOMUXC_PSMI_BASE3 U(0x298c0994) +#define IOMUXC_PSMI_BASE4 U(0x298c0a58) + +#define IMX_ROM_ENTRY U(0x1000) +#define COUNTER_FREQUENCY 1000000 + +#define PLAT_NS_IMAGE_OFFSET 0x80200000 + +#define BL31_NOBITS_BASE 0x20058000 +#define BL31_NOBITS_LIMIT 0x2006d000 + +#define BL31_RWDATA_BASE 0x2006d000 +#define BL31_RWDATA_LIMIT 0x20070000 + +#define BL32_FDT_OVERLAY_ADDR 0x9d000000 + +#ifdef SPD_trusty +#define IMX_TRUSTY_STACK_SIZE 0x100 +#endif + +/* system memory map define */ +#define DEVICE0_MAP MAP_REGION_FLAT(DEVICE0_BASE, DEVICE0_SIZE, MT_DEVICE | MT_RW) +#define DEVICE1_MAP MAP_REGION_FLAT(DEVICE1_BASE, DEVICE1_SIZE, MT_DEVICE | MT_RW) +/* Map partial DRAM space for DRAM low-power mode control */ +#define DEVICE2_MAP MAP_REGION_FLAT(DEVICE2_BASE, DEVICE2_SIZE, MT_DEVICE | MT_RW) + /* MU and FSB */ +#define ELE_MAP MAP_REGION_FLAT(0x27010000, 0x20000, MT_DEVICE | MT_RW | MT_NS) +#define SEC_SIM_MAP MAP_REGION_FLAT(0x2802B000, 0x1000, MT_DEVICE | MT_RW | MT_NS) /* SEC SIM */ +/* For SCMI shared memory region */ +#define SRAM0_MAP MAP_REGION_FLAT(SRAM0_BASE, 0x1000, MT_RW | MT_DEVICE) + +#endif /* PLATFORM_DEF_H */ diff --git a/plat/imx/imx8ulp/include/scmi.h b/plat/imx/imx8ulp/include/scmi.h new file mode 100644 index 00000000..03e16f5e --- /dev/null +++ b/plat/imx/imx8ulp/include/scmi.h @@ -0,0 +1,100 @@ +/* + * Copyright 2021-2024 NXP + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef IMX8_SCMI_H +#define IMX8_SCMI_H + +#include <stdint.h> + +#define SCMI_SHMEM_CHANNEL_ERROR BIT_32(1) +#define SCMI_SHMEM_CHANNEL_FREE BIT_32(0) + +#define SCMI_SHMEM_FLAG_INTR_ENABLED BIT_32(0) + +enum scmi_std_protocol { + SCMI_PROTOCOL_BASE = 0x10, + SCMI_PROTOCOL_POWER_DOMAIN = 0x11, + SCMI_PROTOCOL_SYS_POWER = 0x12, + SCMI_PROTOCOL_PERF_DOMAIN = 0x13, + SCMI_PROTOCOL_CLK = 0x14, + SCMI_PROTOCOL_SENSOR = 0x15, + SCMI_PROTOCOL_RESET_DOMAIN = 0x16, +}; + +#define MSG_ID(m) ((m) & 0xff) +#define MSG_TYPE(m) (((m) >> 8) & 0x3) +#define MSG_PRO_ID(m) (((m) >> 10) & 0xff) +#define MSG_TOKEN(m) (((m) >> 18) & 0x3ff) + +enum { + SCMI_POWER_DOMAIN_PROTOCOL = 0x11, + SCMI_SYS_PWR_DOMAIN_PROTOCOL = 0x12, + SCMI_PER_DOMAIN_PROTOCOL = 0x13, + SCMI_CLK_DOMAIN_PROTOCOL = 0x14, + SCMI_SENSOR_PROTOCOL = 0x15, +}; + +#define PROTOCOL_VERSION 0 +#define PROTOCOL_ATTRIBUTES 1 +#define PROTOCOL_MESSAGE_ATTRIBUTES 2 +#define BASE_DISCOVER_VENDOR 3 +#define BASE_DISCOVER_SUB_VENDOR 4 +#define BASE_DISCOVER_IMPLEMENTATION_VERSION 5 +#define BASE_DISCOVER_LIST_PROTOCOLS 6 +#define BASE_DISCOVER_AGENT 7 +#define BASE_NOTIFY_ERRORS 8 +#define BASE_SET_DEVICE_PERMISSIONS 9 +#define BASE_SET_PROTOCOL_PERMISSIONS 0xA +#define BASE_RESET_AGENT_CONFIGURATION 0xB + +enum { + SCMI_RET_SUCCESS = 0, + SCMI_RET_NOT_SUPPORTED = -1, + SCMI_RET_INVALID_PARAMETERS = -2, + SCMI_RET_DENIED = -3, + SCMI_RET_NOT_FOUND = -4, + SCMI_RET_OUT_OF_RANGE = -5, + SCMI_RET_BUSY = -6, + SCMI_RET_COMMS_ERROR = -7, + SCMI_RET_GENERIC_ERROR = -8, + SCMI_RET_HARDWARE_ERROR = -9, + SCMI_RET_PROTOCOL_ERROR = -10, +}; + +#define POWER_DOMAIN_ATTRIBUTES 3 +#define POWER_DOMAIN_SUPPORT_NOTIFICATION BIT(31) +#define POWER_DOMAIN_SUPPORT_ASYNCHRONOUS BIT(30) +#define POWER_DOMAIN_SUPPORT_SYNCHRONOUS BIT(29) + +#define POWER_STATE_SET 4 +#define POWER_STATE_GET 5 +#define POWER_STATE_NOTIFY 6 +#define POWER_STATE_CHANGE_REQUESTED_NOTIFY 7 + +int scmi_power_domain_handler(uint32_t msg_id, void *shmem); + +#define PERFORMANCE_DOMAIN_ATTRIBUTES 3 +#define PERFORMANCE_DESCRIBE_LEVELS 4 +#define PERFORMANCE_LIMITS_SET 5 +#define PERFORMANCE_LIMITS_GET 6 +#define PERFORMANCE_LEVEL_SET 7 +#define PERFORMANCE_LEVEL_GET 8 +#define PERFORMANCE_NOTIFY_LIMITS 9 +#define PERFORMANCE_NOTIFY_LEVEL 0xA +#define PERFORMANCE_DESCRIBE_FAST_CHANNEL 0xB + +int scmi_perf_domain_handler(uint32_t msg_id, void *shmem); + +#define SENSOR_DESCRIPTION_GET 0x003 +#define SENSOR_CONFIG_SET 0x004 +#define SENSOR_TRIP_POINT_SET 0x005 +#define SENSOR_READING_GET 0x006 + +int scmi_sensor_handler(uint32_t msg_id, void *shmem); + +#define SMC_SHMEM_BASE 0x2201f000 + +#endif /* IMX8_SCMI_H */ diff --git a/plat/imx/imx8ulp/include/scmi_sensor.h b/plat/imx/imx8ulp/include/scmi_sensor.h new file mode 100644 index 00000000..5dab8986 --- /dev/null +++ b/plat/imx/imx8ulp/include/scmi_sensor.h @@ -0,0 +1,139 @@ +/* + * Copyright (c) 2015-2024, Arm Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + * + * Description: + * System Control and Management Interface (SCMI) support. + */ + +#ifndef INTERNAL_SCMI_SENSOR_H +#define INTERNAL_SCMI_SENSOR_H + +#include <stdint.h> + +#define SCMI_PROTOCOL_VERSION_SENSOR UINT32_C(0x10000) + +/* + * PROTOCOL_ATTRIBUTES + */ +struct scmi_sensor_protocol_attributes_p2a { + int32_t status; + uint32_t attributes; + uint32_t sensor_reg_address_low; + uint32_t sensor_reg_address_high; + uint32_t sensor_reg_len; +}; + +/* + * SENSOR_READING_GET + */ +#define SCMI_SENSOR_PROTOCOL_READING_GET_ASYNC_FLAG_MASK (1 << 0) + +struct scmi_sensor_protocol_reading_get_a2p { + uint32_t sensor_id; + uint32_t flags; +}; + +struct scmi_sensor_protocol_reading_get_p2a { + int32_t status; + uint32_t sensor_value_low; + uint32_t sensor_value_high; +}; + +/* + * SENSOR_DESCRIPTION_GET + */ + #define SCMI_SENSOR_DESCS_MAX(MAILBOX_SIZE) \ + ((sizeof(struct scmi_sensor_protocol_description_get_p2a) < MAILBOX_SIZE) \ + ? ((MAILBOX_SIZE - \ + sizeof(struct scmi_sensor_protocol_description_get_p2a)) \ + / sizeof(struct scmi_sensor_desc)) \ + : 0) + +#define SCMI_SENSOR_DESC_ATTRS_HIGH_SENSOR_TYPE_POS 0 +#define SCMI_SENSOR_DESC_ATTRS_HIGH_SENSOR_UNIT_MULTIPLIER_POS 11 +#define SCMI_SENSOR_DESC_ATTRS_HIGH_SENSOR_UPDATE_MULTIPLIER_POS 22 +#define SCMI_SENSOR_DESC_ATTRS_HIGH_SENSOR_UPDATE_INTERVAL_POS 27 + +#define SCMI_SENSOR_DESC_ATTRS_HIGH_SENSOR_TYPE_MASK \ + (UINT32_C(0xFF) << SCMI_SENSOR_DESC_ATTRS_HIGH_SENSOR_TYPE_POS) +#define SCMI_SENSOR_DESC_ATTRS_HIGH_SENSOR_UNIT_MULTIPLIER_MASK \ + (UINT32_C(0x1F) << SCMI_SENSOR_DESC_ATTRS_HIGH_SENSOR_UNIT_MULTIPLIER_POS) +#define SCMI_SENSOR_DESC_ATTRS_HIGH_SENSOR_UPDATE_MULTIPLIER_MASK \ + (UINT32_C(0x1F) << \ + SCMI_SENSOR_DESC_ATTRS_HIGH_SENSOR_UPDATE_MULTIPLIER_POS) +#define SCMI_SENSOR_DESC_ATTRS_HIGH_SENSOR_UPDATE_INTERVAL_MASK \ + (UINT32_C(0x1F) << SCMI_SENSOR_DESC_ATTRS_HIGH_SENSOR_UPDATE_INTERVAL_POS) + +#define SCMI_SENSOR_DESC_ATTRS_HIGH_SENSOR_UNIT_MULTIPLIER_MAX \ + (int32_t)(SCMI_SENSOR_DESC_ATTRS_HIGH_SENSOR_UNIT_MULTIPLIER_MASK >> 1) +#define SCMI_SENSOR_DESC_ATTRS_HIGH_SENSOR_UNIT_MULTIPLIER_MIN \ + (-(SCMI_SENSOR_DESC_ATTRS_HIGH_SENSOR_UNIT_MULTIPLIER_MAX + 1)) + +#define SCMI_SENSOR_DESC_ATTRS_HIGH_SENSOR_UPDATE_MULTIPLIER_MAX \ + (int32_t)(SCMI_SENSOR_DESC_ATTRS_HIGH_SENSOR_UPDATE_INTERVAL_MASK >> 1) +#define SCMI_SENSOR_DESC_ATTRS_HIGH_SENSOR_UPDATE_MULTIPLIER_MIN \ + (-(SCMI_SENSOR_DESC_ATTRS_HIGH_SENSOR_UPDATE_MULTIPLIER_MAX + 1)) + +#define SCMI_SENSOR_DESC_ATTRIBUTES_HIGH(SENSOR_TYPE, UNIT_MULTIPLIER, \ + UPDATE_MULTIPLIER, UPDATE_INTERVAL) \ + ( \ + (((SENSOR_TYPE) << \ + SCMI_SENSOR_DESC_ATTRS_HIGH_SENSOR_TYPE_POS) & \ + SCMI_SENSOR_DESC_ATTRS_HIGH_SENSOR_TYPE_MASK) | \ + (((UNIT_MULTIPLIER) << \ + SCMI_SENSOR_DESC_ATTRS_HIGH_SENSOR_UNIT_MULTIPLIER_POS) & \ + SCMI_SENSOR_DESC_ATTRS_HIGH_SENSOR_UNIT_MULTIPLIER_MASK) | \ + (((UPDATE_MULTIPLIER) << \ + SCMI_SENSOR_DESC_ATTRS_HIGH_SENSOR_UPDATE_MULTIPLIER_POS) & \ + SCMI_SENSOR_DESC_ATTRS_HIGH_SENSOR_UPDATE_MULTIPLIER_MASK) | \ + (((UPDATE_INTERVAL) << \ + SCMI_SENSOR_DESC_ATTRS_HIGH_SENSOR_UPDATE_INTERVAL_POS) & \ + SCMI_SENSOR_DESC_ATTRS_HIGH_SENSOR_UPDATE_INTERVAL_MASK) \ + ) + +#define SCMI_SENSOR_NUM_SENSOR_FLAGS_NUM_DESCS_POS 0 +#define SCMI_SENSOR_NUM_SENSOR_FLAGS_NUM_REMAINING_DESCS_POS 16 + +#define SCMI_SENSOR_NUM_SENSOR_FLAGS_NUM_DESCS_MASK \ + (UINT32_C(0xFFF) << SCMI_SENSOR_NUM_SENSOR_FLAGS_NUM_DESCS_POS) +#define SCMI_SENSOR_NUM_SENSOR_FLAGS_NUM_REMAINING_DESCS_MASK \ + (UINT32_C(0xFFFF) << SCMI_SENSOR_NUM_SENSOR_FLAGS_NUM_REMAINING_DESCS_POS) + +#define SCMI_SENSOR_NUM_SENSOR_FLAGS(NUM_DESCS, NUM_REMAINING_DESCS) \ + ( \ + (((NUM_DESCS) << \ + SCMI_SENSOR_NUM_SENSOR_FLAGS_NUM_DESCS_POS) & \ + SCMI_SENSOR_NUM_SENSOR_FLAGS_NUM_DESCS_MASK) | \ + (((NUM_REMAINING_DESCS) << \ + SCMI_SENSOR_NUM_SENSOR_FLAGS_NUM_REMAINING_DESCS_POS) & \ + SCMI_SENSOR_NUM_SENSOR_FLAGS_NUM_REMAINING_DESCS_MASK) \ + ) + +#define SCMI_SENSOR_NAME_LEN 16 + +struct scmi_sensor_desc { + uint32_t sensor_id; + uint32_t sensor_attributes_low; + uint32_t sensor_attributes_high; + char sensor_name[SCMI_SENSOR_NAME_LEN]; +}; + +struct scmi_sensor_protocol_description_get_a2p { + uint32_t desc_index; +}; + +struct scmi_sensor_protocol_description_get_p2a { + int32_t status; + uint32_t num_sensor_flags; + struct scmi_sensor_desc sensor_desc[]; +}; + +/* Event indices */ +enum scmi_sensor_api_idx { + SCMI_SENSOR_EVENT_IDX_REQUEST, + SCMI_SENSOR_EVENT_IDX_COUNT, +}; + +#endif /* INTERNAL_SCMI_SENSOR_H */ diff --git a/plat/imx/imx8ulp/include/xrdc.h b/plat/imx/imx8ulp/include/xrdc.h new file mode 100644 index 00000000..15250f03 --- /dev/null +++ b/plat/imx/imx8ulp/include/xrdc.h @@ -0,0 +1,47 @@ +/* + * Copyright 2021-2024 NXP + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef IMX8ULP_XRDC_H +#define IMX8ULP_XRDC_H + +#define DID_MAX 8 +#define PAC_SLOT_ALL 128 +#define MSC_SLOT_ALL 8 + +enum xrdc_mda_sa { + MDA_SA_S, + MDA_SA_NS, + MDA_SA_PT, /* pass through master's secure/nonsecure attribute */ +}; + +struct xrdc_mda_config { + uint16_t mda_id; + uint16_t did; + enum xrdc_mda_sa sa; +}; + +struct xrdc_pac_msc_config { + uint16_t pac_msc_id; + uint16_t slot_id; + uint8_t dsel[DID_MAX]; +}; + +struct xrdc_mrc_config { + uint16_t mrc_id; + uint16_t region_id; + uint32_t region_start; + uint32_t region_size; + uint8_t dsel[DID_MAX]; + uint16_t accset[2]; +}; + +/* APIs to apply and enable XRDC */ +int xrdc_apply_lpav_config(void); +int xrdc_apply_hifi_config(void); +int xrdc_apply_apd_config(void); +void xrdc_enable(void); + +#endif diff --git a/plat/imx/imx8ulp/platform.mk b/plat/imx/imx8ulp/platform.mk new file mode 100644 index 00000000..f1e53ca9 --- /dev/null +++ b/plat/imx/imx8ulp/platform.mk @@ -0,0 +1,69 @@ +# +# Copyright 2021-2024 NXP +# +# SPDX-License-Identifier: BSD-3-Clause +# + +# Translation tables library +include lib/xlat_tables_v2/xlat_tables.mk + +# Include GICv3 driver files +include drivers/arm/gic/v3/gicv3.mk + +PLAT_INCLUDES := -Iplat/imx/imx8ulp/include \ + -Iplat/imx/common/include \ + -Iplat/imx/imx8ulp/upower + +IMX_GIC_SOURCES := ${GICV3_SOURCES} \ + plat/common/plat_gicv3.c \ + plat/common/plat_psci_common.c \ + plat/imx/common/plat_imx8_gic.c + +BL31_SOURCES += plat/imx/common/lpuart_console.S \ + plat/imx/common/imx8_helpers.S \ + plat/imx/imx8ulp/imx8ulp_bl31_setup.c \ + plat/imx/imx8ulp/imx8ulp_psci.c \ + plat/imx/imx8ulp/apd_context.c \ + plat/imx/common/imx8_topology.c \ + plat/imx/common/imx_sip_svc.c \ + plat/imx/common/imx_sip_handler.c \ + plat/imx/common/imx_bl31_common.c \ + plat/common/plat_psci_common.c \ + lib/cpus/aarch64/cortex_a35.S \ + drivers/delay_timer/delay_timer.c \ + drivers/delay_timer/generic_delay_timer.c \ + plat/imx/imx8ulp/xrdc/xrdc_core.c \ + plat/imx/imx8ulp/imx8ulp_caam.c \ + plat/imx/imx8ulp/dram.c \ + drivers/scmi-msg/base.c \ + drivers/scmi-msg/entry.c \ + drivers/scmi-msg/smt.c \ + drivers/scmi-msg/power_domain.c \ + drivers/scmi-msg/sensor.c \ + plat/imx/imx8ulp/scmi/scmi.c \ + plat/imx/imx8ulp/scmi/scmi_pd.c \ + plat/imx/imx8ulp/scmi/scmi_sensor.c \ + plat/imx/imx8ulp/upower/upower_api.c \ + plat/imx/imx8ulp/upower/upower_hal.c \ + ${XLAT_TABLES_LIB_SRCS} \ + ${IMX_GIC_SOURCES} + +ifeq ($(findstring clang,$(notdir $(CC))),) + TF_CFLAGS_aarch64 += -fno-strict-aliasing +endif + +USE_COHERENT_MEM := 1 +RESET_TO_BL31 := 1 +SEPARATE_NOBITS_REGION := 1 +SEPARATE_RWDATA_REGION := 1 +PROGRAMMABLE_RESET_ADDRESS := 1 +COLD_BOOT_SINGLE_CPU := 1 +WARMBOOT_ENABLE_DCACHE_EARLY := 1 +BL32_BASE ?= 0xa6000000 +BL32_SIZE ?= 0x2000000 +$(eval $(call add_define,BL32_BASE)) +$(eval $(call add_define,BL32_SIZE)) + +ifeq (${SPD},trusty) + BL31_CFLAGS += -DPLAT_XLAT_TABLES_DYNAMIC=1 +endif diff --git a/plat/imx/imx8ulp/scmi/scmi.c b/plat/imx/imx8ulp/scmi/scmi.c new file mode 100644 index 00000000..5d3e7d72 --- /dev/null +++ b/plat/imx/imx8ulp/scmi/scmi.c @@ -0,0 +1,69 @@ +/* + * Copyright 2021-2024 NXP + * + * SPDX-License-Identifier: BSD-3-Clause + */ +#include <assert.h> +#include <stdint.h> + +#include <drivers/scmi-msg.h> +#include <drivers/scmi.h> + +#include <platform_def.h> + +#define SMT_BUFFER_BASE 0x2201f000 +#define SMT_BUFFER0_BASE SMT_BUFFER_BASE +#define SMT_BUFFER1_BASE (SMT_BUFFER_BASE + 0x200) + +static struct scmi_msg_channel scmi_channel[] = { + [0] = { + .shm_addr = SMT_BUFFER0_BASE, + .shm_size = SMT_BUF_SLOT_SIZE, + }, +}; + +struct scmi_msg_channel *plat_scmi_get_channel(unsigned int agent_id) +{ + assert(agent_id < ARRAY_SIZE(scmi_channel)); + + return &scmi_channel[agent_id]; +} + +static const char vendor[] = "NXP"; +static const char sub_vendor[] = ""; + +const char *plat_scmi_vendor_name(void) +{ + return vendor; +} + +const char *plat_scmi_sub_vendor_name(void) +{ + return sub_vendor; +} + +/* Currently supporting Clocks and Reset Domains */ +static const uint8_t plat_protocol_list[] = { + SCMI_PROTOCOL_ID_POWER_DOMAIN, + SCMI_PROTOCOL_ID_SENSOR, + 0U /* Null termination */ +}; + +size_t plat_scmi_protocol_count(void) +{ + return ARRAY_SIZE(plat_protocol_list) - 1U; +} + +const uint8_t *plat_scmi_protocol_list(unsigned int agent_id __unused) +{ + return plat_protocol_list; +} + +void imx8ulp_init_scmi_server(void) +{ + size_t i; + + for (i = 0U; i < ARRAY_SIZE(scmi_channel); i++) { + scmi_smt_init_agent_channel(&scmi_channel[i]); + } +} diff --git a/plat/imx/imx8ulp/scmi/scmi_pd.c b/plat/imx/imx8ulp/scmi/scmi_pd.c new file mode 100644 index 00000000..8e7e5d66 --- /dev/null +++ b/plat/imx/imx8ulp/scmi/scmi_pd.c @@ -0,0 +1,371 @@ +/* + * Copyright 2021-2024 NXP + * + * SPDX-License-Identifier: BSD-3-Clause + */ +#include <inttypes.h> +#include <lib/libc/errno.h> +#include <stdint.h> +#include <stdlib.h> +#include <string.h> + +#include <common/debug.h> +#include <drivers/scmi.h> +#include <lib/mmio.h> +#include <lib/utils_def.h> +#include <platform_def.h> +#include <scmi.h> + +#include <upower_api.h> + +#define POWER_STATE_ON (0 << 30) +#define POWER_STATE_OFF (1 << 30) + +extern bool is_lpav_owned_by_apd(void); + +enum { + PS0 = 0, + PS1 = 1, + PS2 = 2, + PS3 = 3, + PS4 = 4, + PS5 = 5, + PS6 = 6, + PS7 = 7, + PS8 = 8, + PS9 = 9, + PS10 = 10, + PS11 = 11, + PS12 = 12, + PS13 = 13, + PS14 = 14, + PS15 = 15, + PS16 = 16, + PS17 = 17, + PS18 = 18, + PS19 = 19, +}; + +#define SRAM_DMA1 BIT(6) +#define SRAM_FLEXSPI2 BIT(7) +#define SRAM_USB0 BIT(10) +#define SRAM_USDHC0 BIT(11) +#define SRAM_USDHC1 BIT(12) +#define SRAM_USDHC2_USB1 BIT(13) +#define SRAM_DCNANO GENMASK_32(18, 17) +#define SRAM_EPDC GENMASK_32(20, 19) +#define SRAM_DMA2 BIT(21) +#define SRAM_GPU2D GENMASK_32(23, 22) +#define SRAM_GPU3D GENMASK_32(25, 24) +#define SRAM_HIFI4 BIT(26) +#define SRAM_ISI_BUFFER BIT(27) +#define SRAM_MIPI_CSI_FIFO BIT(28) +#define SRAM_MIPI_DSI_FIFO BIT(29) +#define SRAM_PXP BIT(30) + +#define SRAM_DMA0 BIT_64(33) +#define SRAM_FLEXCAN BIT_64(34) +#define SRAM_FLEXSPI0 BIT_64(35) +#define SRAM_FLEXSPI1 BIT_64(36) + +struct psw { + char *name; + uint32_t reg; + int power_state; + uint32_t count; + int flags; +}; + +#define ALWAYS_ON BIT(0) + +static struct psw imx8ulp_psw[] = { + [PS6] = { .name = "PS6", .reg = PS6, .flags = ALWAYS_ON, .power_state = POWER_STATE_ON }, + [PS7] = { .name = "PS7", .reg = PS7, .power_state = POWER_STATE_OFF }, + [PS8] = { .name = "PS8", .reg = PS8, .power_state = POWER_STATE_OFF }, + [PS13] = { .name = "PS13", .reg = PS13, .power_state = POWER_STATE_OFF }, + [PS14] = { .name = "PS14", .reg = PS14, .flags = ALWAYS_ON, .power_state = POWER_STATE_OFF }, + [PS15] = { .name = "PS15", .reg = PS15, .power_state = POWER_STATE_OFF }, + [PS16] = { .name = "PS16", .reg = PS16, .flags = ALWAYS_ON, .power_state = POWER_STATE_ON }, +}; + +struct power_domain { + char *name; + uint32_t reg; + uint32_t psw_parent; + uint32_t sram_parent; + uint64_t bits; + uint32_t power_state; + bool lpav; /* belong to lpav domain */ + uint32_t sw_rst_reg; /* pcc sw reset reg offset */ +}; + +/* The Rich OS need flow the macro */ +#define IMX8ULP_PD_DMA1 0 +#define IMX8ULP_PD_FLEXSPI2 1 +#define IMX8ULP_PD_USB0 2 +#define IMX8ULP_PD_USDHC0 3 +#define IMX8ULP_PD_USDHC1 4 +#define IMX8ULP_PD_USDHC2_USB1 5 +#define IMX8ULP_PD_DCNANO 6 +#define IMX8ULP_PD_EPDC 7 +#define IMX8ULP_PD_DMA2 8 +#define IMX8ULP_PD_GPU2D 9 +#define IMX8ULP_PD_GPU3D 10 +#define IMX8ULP_PD_HIFI4 11 +#define IMX8ULP_PD_ISI 12 +#define IMX8ULP_PD_MIPI_CSI 13 +#define IMX8ULP_PD_MIPI_DSI 14 +#define IMX8ULP_PD_PXP 15 + +#define IMX8ULP_PD_PS6 16 +#define IMX8ULP_PD_PS7 17 +#define IMX8ULP_PD_PS8 18 +#define IMX8ULP_PD_PS13 19 +#define IMX8ULP_PD_PS14 20 +#define IMX8ULP_PD_PS15 21 +#define IMX8ULP_PD_PS16 22 +#define IMX8ULP_PD_MAX 23 + +/* LPAV peripheral PCC */ +#define PCC_GPU2D (IMX_PCC5_BASE + 0xf0) +#define PCC_GPU3D (IMX_PCC5_BASE + 0xf4) +#define PCC_EPDC (IMX_PCC5_BASE + 0xcc) +#define PCC_CSI (IMX_PCC5_BASE + 0xbc) +#define PCC_PXP (IMX_PCC5_BASE + 0xd0) + +#define PCC_SW_RST BIT(28) + +#define PWR_DOMAIN(_name, _reg, _psw_parent, _sram_parent, \ + _bits, _state, _lpav, _rst_reg) \ + { \ + .name = _name, \ + .reg = _reg, \ + .psw_parent = _psw_parent, \ + .sram_parent = _sram_parent, \ + .bits = _bits, \ + .power_state = _state, \ + .lpav = _lpav, \ + .sw_rst_reg = _rst_reg, \ + } + +static struct power_domain scmi_power_domains[] = { + PWR_DOMAIN("DMA1", IMX8ULP_PD_DMA1, PS6, PS6, SRAM_DMA1, POWER_STATE_OFF, false, 0U), + PWR_DOMAIN("FLEXSPI2", IMX8ULP_PD_FLEXSPI2, PS6, PS6, SRAM_FLEXSPI2, POWER_STATE_OFF, false, 0U), + PWR_DOMAIN("USB0", IMX8ULP_PD_USB0, PS6, PS6, SRAM_USB0, POWER_STATE_OFF, false, 0U), + PWR_DOMAIN("USDHC0", IMX8ULP_PD_USDHC0, PS6, PS6, SRAM_USDHC0, POWER_STATE_OFF, false, 0U), + PWR_DOMAIN("USDHC1", IMX8ULP_PD_USDHC1, PS6, PS6, SRAM_USDHC1, POWER_STATE_OFF, false, 0U), + PWR_DOMAIN("USDHC2_USB1", IMX8ULP_PD_USDHC2_USB1, PS6, PS6, SRAM_USDHC2_USB1, POWER_STATE_OFF, false, 0U), + PWR_DOMAIN("DCNano", IMX8ULP_PD_DCNANO, PS16, PS16, SRAM_DCNANO, POWER_STATE_OFF, true, 0U), + PWR_DOMAIN("EPDC", IMX8ULP_PD_EPDC, PS13, PS13, SRAM_EPDC, POWER_STATE_OFF, true, PCC_EPDC), + PWR_DOMAIN("DMA2", IMX8ULP_PD_DMA2, PS16, PS16, SRAM_DMA2, POWER_STATE_OFF, true, 0U), + PWR_DOMAIN("GPU2D", IMX8ULP_PD_GPU2D, PS16, PS16, SRAM_GPU2D, POWER_STATE_OFF, true, PCC_GPU2D), + PWR_DOMAIN("GPU3D", IMX8ULP_PD_GPU3D, PS7, PS7, SRAM_GPU3D, POWER_STATE_OFF, true, PCC_GPU3D), + PWR_DOMAIN("HIFI4", IMX8ULP_PD_HIFI4, PS8, PS8, SRAM_HIFI4, POWER_STATE_OFF, true, 0U), + PWR_DOMAIN("ISI", IMX8ULP_PD_ISI, PS16, PS16, SRAM_ISI_BUFFER, POWER_STATE_OFF, true, 0U), + PWR_DOMAIN("MIPI_CSI", IMX8ULP_PD_MIPI_CSI, PS15, PS16, SRAM_MIPI_CSI_FIFO, POWER_STATE_OFF, true, PCC_CSI), + PWR_DOMAIN("MIPI_DSI", IMX8ULP_PD_MIPI_DSI, PS14, PS16, SRAM_MIPI_DSI_FIFO, POWER_STATE_OFF, true, 0U), + PWR_DOMAIN("PXP", IMX8ULP_PD_PXP, PS13, PS13, SRAM_PXP | SRAM_EPDC, POWER_STATE_OFF, true, PCC_PXP) +}; + +size_t plat_scmi_pd_count(unsigned int agent_id __unused) +{ + return ARRAY_SIZE(scmi_power_domains); +} + +const char *plat_scmi_pd_get_name(unsigned int agent_id __unused, + unsigned int pd_id) +{ + if (pd_id >= IMX8ULP_PD_PS6) { + return imx8ulp_psw[pd_id - IMX8ULP_PD_PS6].name; + } + + return scmi_power_domains[pd_id].name; +} + +unsigned int plat_scmi_pd_get_state(unsigned int agent_id __unused, + unsigned int pd_id __unused) +{ + if (pd_id >= IMX8ULP_PD_PS6) { + return imx8ulp_psw[pd_id - IMX8ULP_PD_PS6].power_state; + } + + return scmi_power_domains[pd_id].power_state; +} + +extern void upower_wait_resp(void); +int upwr_pwm_power(const uint32_t swton[], const uint32_t memon[], bool on) +{ + int ret_val; + int ret; + + if (on == true) { + ret = upwr_pwm_power_on(swton, memon, NULL); + } else { + ret = upwr_pwm_power_off(swton, memon, NULL); + } + + if (ret != 0U) { + WARN("%s failed: ret: %d, state: %x\n", __func__, ret, on); + return ret; + } + + upower_wait_resp(); + + ret = upwr_poll_req_status(UPWR_SG_PWRMGMT, NULL, NULL, &ret_val, 1000); + if (ret != UPWR_REQ_OK) { + WARN("Failure %d, %s\n", ret, __func__); + if (ret == UPWR_REQ_BUSY) { + return -EBUSY; + } else { + return -EINVAL; + } + } + + return 0; +} + +int32_t plat_scmi_pd_psw(unsigned int index, unsigned int state) +{ + uint32_t psw_parent = scmi_power_domains[index].psw_parent; + uint32_t sram_parent = scmi_power_domains[index].sram_parent; + uint64_t swt; + bool on; + int ret = 0; + + if ((imx8ulp_psw[psw_parent].flags & ALWAYS_ON) != 0U && + (imx8ulp_psw[sram_parent].flags & ALWAYS_ON) != 0U) { + return 0; + } + + on = (state == POWER_STATE_ON) ? true : false; + + if ((imx8ulp_psw[psw_parent].flags & ALWAYS_ON) == 0U) { + swt = 1 << imx8ulp_psw[psw_parent].reg; + if (imx8ulp_psw[psw_parent].count == 0U) { + if (on == false) { + WARN("off PSW[%d] that already in off state\n", psw_parent); + ret = -EACCES; + } else { + ret = upwr_pwm_power((const uint32_t *)&swt, NULL, on); + imx8ulp_psw[psw_parent].count++; + } + } else { + if (on == true) { + imx8ulp_psw[psw_parent].count++; + } else { + imx8ulp_psw[psw_parent].count--; + } + + if (imx8ulp_psw[psw_parent].count == 0U) { + ret = upwr_pwm_power((const uint32_t *)&swt, NULL, on); + } + } + } + + if (!(imx8ulp_psw[sram_parent].flags & ALWAYS_ON) && (psw_parent != sram_parent)) { + swt = 1 << imx8ulp_psw[sram_parent].reg; + if (imx8ulp_psw[sram_parent].count == 0U) { + if (on == false) { + WARN("off PSW[%d] that already in off state\n", sram_parent); + ret = -EACCES; + } else { + ret = upwr_pwm_power((const uint32_t *)&swt, NULL, on); + imx8ulp_psw[sram_parent].count++; + } + } else { + if (on == true) { + imx8ulp_psw[sram_parent].count++; + } else { + imx8ulp_psw[sram_parent].count--; + } + + if (imx8ulp_psw[sram_parent].count == 0U) { + ret = upwr_pwm_power((const uint32_t *)&swt, NULL, on); + } + } + } + + return ret; +} + +bool pd_allow_power_off(unsigned int pd_id) +{ + if (scmi_power_domains[pd_id].lpav) { + if (!is_lpav_owned_by_apd()) { + return false; + } + } + + return true; +} + +void assert_pcc_reset(unsigned int pcc) +{ + /* if sw_rst_reg is valid, assert the pcc reset */ + if (pcc != 0U) { + mmio_clrbits_32(pcc, PCC_SW_RST); + } +} + +int32_t plat_scmi_pd_set_state(unsigned int agent_id __unused, + unsigned int flags, + unsigned int pd_id, + unsigned int state) +{ + unsigned int ps_idx; + uint64_t mem; + bool on; + int ret; + + if (flags != 0U || pd_id >= IMX8ULP_PD_PS6) { + return SCMI_NOT_SUPPORTED; + } + + ps_idx = 0; + while (ps_idx < IMX8ULP_PD_PS6 && scmi_power_domains[ps_idx].reg != pd_id) { + ps_idx++; + } + + if (ps_idx == IMX8ULP_PD_PS6) { + return SCMI_NOT_FOUND; + } + + if (state == scmi_power_domains[ps_idx].power_state) { + return SCMI_SUCCESS; + } + + mem = scmi_power_domains[ps_idx].bits; + on = (state == POWER_STATE_ON ? true : false); + if (on == true) { + /* Assert pcc sw reset if necessary */ + assert_pcc_reset(scmi_power_domains[ps_idx].sw_rst_reg); + + ret = plat_scmi_pd_psw(ps_idx, state); + if (ret != 0U) { + return SCMI_DENIED; + } + + ret = upwr_pwm_power(NULL, (const uint32_t *)&mem, on); + if (ret != 0U) { + return SCMI_DENIED; + } + } else { + if (!pd_allow_power_off(ps_idx)) { + return SCMI_DENIED; + } + + ret = upwr_pwm_power(NULL, (const uint32_t *)&mem, on); + if (ret != 0U) { + return SCMI_DENIED; + } + + ret = plat_scmi_pd_psw(ps_idx, state); + if (ret != 0U) { + return SCMI_DENIED; + } + } + + scmi_power_domains[pd_id].power_state = state; + + return SCMI_SUCCESS; +} diff --git a/plat/imx/imx8ulp/scmi/scmi_sensor.c b/plat/imx/imx8ulp/scmi/scmi_sensor.c new file mode 100644 index 00000000..6976b2e2 --- /dev/null +++ b/plat/imx/imx8ulp/scmi/scmi_sensor.c @@ -0,0 +1,85 @@ +/* + * Copyright 2021-2024 NXP + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <lib/libc/errno.h> +#include <stdint.h> +#include <stdlib.h> +#include <string.h> + +#include "../../../drivers/scmi-msg/sensor.h" + +#include <common/debug.h> +#include <drivers/scmi.h> +#include <lib/mmio.h> +#include <lib/utils_def.h> +#include <scmi.h> + +#include <upower_api.h> + +/* Only Temperature now */ +static uint16_t imx_scmi_sensor_count(unsigned int agent_id __unused) +{ + return 1U; +} + +uint8_t imx_scmi_sensor_max_requests(unsigned int agent_id __unused) +{ + return 1U; +} + +extern int upower_read_temperature(uint32_t sensor_id, int32_t *temperature); +int imx_scmi_sensor_reading_get(uint32_t agent_id __unused, uint16_t sensor_id __unused, + uint32_t *val) +{ + int32_t temperature; + int ret; + + ret = upower_read_temperature(1, &temperature); + if (ret != 0U) { + val[0] = 0xFFFFFFFF; + } else { + val[0] = temperature; + } + + val[1] = 0; + val[2] = 0; + val[3] = 0; + + return ret; +} + +#define SCMI_SENSOR_NAME_LENGTH_MAX 16U + +uint32_t imx_scmi_sensor_state(uint32_t agent_id __unused, uint16_t sensor_id __unused) +{ + return 1U; +} + +uint32_t imx_scmi_sensor_description_get(uint32_t agent_id __unused, uint16_t desc_index __unused, + struct scmi_sensor_desc *desc __unused) +{ + desc->id = 0; + desc->attr_low = 0; + desc->attr_high = 2; + strlcpy((char *)desc->name, "UPOWER-TEMP", 12); + desc->power = 0; + desc->resolution = 0; + desc->min_range_low = 0; + desc->min_range_high = 0x80000000; + desc->max_range_low = 0xffffffff; + desc->max_range_high = 0x7fffffff; + + return 1U; +} + +REGISTER_SCMI_SENSOR_OPS(imx_scmi_sensor_count, + imx_scmi_sensor_max_requests, + NULL, + imx_scmi_sensor_reading_get, + imx_scmi_sensor_description_get, + NULL, + imx_scmi_sensor_state, + NULL); diff --git a/plat/imx/imx8ulp/upower/upmu.h b/plat/imx/imx8ulp/upower/upmu.h new file mode 100644 index 00000000..ce4f47ed --- /dev/null +++ b/plat/imx/imx8ulp/upower/upmu.h @@ -0,0 +1,279 @@ +/* + * Copyright 2021-2024 NXP + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef MU_H +#define MU_H + +#include <stdint.h> + +typedef volatile unsigned int vuint32_t; + +/****************************************************************************/ +/* MODULE: Message Unit */ +/****************************************************************************/ +/* VER Register */ +typedef union { + vuint32_t R; + struct { + vuint32_t FEATURE : 16; + vuint32_t MINOR : 8; + vuint32_t MAJOR : 8; + } B; +} MU_VER_t; + +/* PAR Register */ +typedef union { + vuint32_t R; + struct { + vuint32_t TR_NUM : 8; + vuint32_t RR_NUM : 8; + vuint32_t GIR_NUM : 8; + vuint32_t FLAG_WIDTH : 8; + } B; +} MU_PAR_t; + +/* CR Register */ +typedef union { + vuint32_t R; + struct { + vuint32_t MUR : 1; + vuint32_t MURIE : 1; + vuint32_t rsrv_1 : 30; + } B; +} MU_CR_t; + +/* SR Register */ +typedef union { + vuint32_t R; + struct { + vuint32_t MURS : 1; + vuint32_t MURIP : 1; + vuint32_t EP : 1; + vuint32_t FUP : 1; + vuint32_t GIRP : 1; + vuint32_t TEP : 1; + vuint32_t RFP : 1; + vuint32_t CEP : 1; + vuint32_t rsrv_1 : 24; + + } B; +} MU_SR_t; + +/* CCR0 Register */ +typedef union { + vuint32_t R; + struct { + vuint32_t NMI : 1; + vuint32_t HR : 1; + vuint32_t HRM : 1; + vuint32_t CLKE : 1; + vuint32_t RSTH : 1; + vuint32_t BOOT : 2; + vuint32_t rsrv_1 : 25; + + } B; +} MU_CCR0_t; + +/* CIER0 Register */ +typedef union { + vuint32_t R; + struct { + vuint32_t rsrv_1 : 1; + vuint32_t HRIE : 1; + vuint32_t RUNIE : 1; + vuint32_t RAIE : 1; + vuint32_t HALTIE : 1; + vuint32_t WAITIE : 1; + vuint32_t STOPIE : 1; + vuint32_t PDIE : 1; + vuint32_t rsrv_2 : 24; + } B; +} MU_CIER0_t; + +/* CSSR0 Register */ +typedef union { + vuint32_t R; + struct { + vuint32_t NMIC : 1; + vuint32_t HRIP : 1; + vuint32_t RUN : 1; + vuint32_t RAIP : 1; + vuint32_t HALT : 1; + vuint32_t WAIT : 1; + vuint32_t STOP : 1; + vuint32_t PD : 1; + vuint32_t rsrv_1 : 24; + } B; +} MU_CSSR0_t; + +/* CSR0 Register */ +typedef union { + vuint32_t R; + struct { + vuint32_t rsrv_1 : 1; + vuint32_t HRIP : 1; + vuint32_t RUN : 1; + vuint32_t RAIP : 1; + vuint32_t HALT : 1; + vuint32_t WAIT : 1; + vuint32_t STOP : 1; + vuint32_t PD : 1; + vuint32_t rsrv_2 : 24; + } B; +} MU_CSR0_t; + +/* FCR Register */ +typedef union { + vuint32_t R; + struct { + vuint32_t F0 : 1; + vuint32_t F1 : 1; + vuint32_t F2 : 1; + vuint32_t rsrv_1 : 29; + } B; +} MU_FCR_t; + +/* FSR Register */ +typedef union { + vuint32_t R; + struct { + vuint32_t F0 : 1; + vuint32_t F1 : 1; + vuint32_t F2 : 1; + vuint32_t rsrv_1 : 29; + } B; +} MU_FSR_t; + +/* GIER Register */ +typedef union { + vuint32_t R; + struct { + vuint32_t GIE0 : 1; + vuint32_t GIE1 : 1; + vuint32_t rsrv_1 : 30; + } B; +} MU_GIER_t; + +/* GCR Register */ +typedef union { + vuint32_t R; + struct { + vuint32_t GIR0 : 1; + vuint32_t GIR1 : 1; + vuint32_t rsrv_1 : 30; + } B; +} MU_GCR_t; + +/* GSR Register */ +typedef union { + vuint32_t R; + struct { + vuint32_t GIP0 : 1; + vuint32_t GIP1 : 1; + vuint32_t rsrv_1 : 30; + } B; +} MU_GSR_t; + +/* TCR Register */ +typedef union{ + vuint32_t R; + struct { + vuint32_t TIE0 : 1; + vuint32_t TIE1 : 1; + vuint32_t rsrv_1 : 30; + } B; +} MU_TCR_t; + +/* TSR Register */ +typedef union { + vuint32_t R; + struct { + vuint32_t TE0 : 1; + vuint32_t TE1 : 1; + vuint32_t rsrv_1 : 30; + } B; +} MU_TSR_t; + +/* RCR Register */ +typedef union { + vuint32_t R; + struct { + vuint32_t RIE0 : 1; + vuint32_t RIE1 : 1; + vuint32_t rsrv_1 : 30; + } B; +} MU_RCR_t; + +/* RSR Register */ +typedef union { + vuint32_t R; + struct { + vuint32_t RF0 : 1; + vuint32_t RF1 : 1; + vuint32_t rsrv_1 : 30; + } B; +} MU_RSR_t; + +/* TR0 Register */ +typedef union { + vuint32_t R; + struct { + vuint32_t TR_DATA : 32; + } B; +} MU_TR0_t; + +/* TR1 Register */ +typedef union { + vuint32_t R; + struct { + vuint32_t TR_DATA : 32; + } B; +} MU_TR1_t; + +/* RR0 Register */ +typedef union { + vuint32_t R; + struct { + vuint32_t RR_DATA : 32; + } B; +} MU_RR0_t; + +/* RR1 Register */ +typedef union { + vuint32_t R; + struct { + vuint32_t RR_DATA : 32; + } B; +} MU_RR1_t; + +struct MU_t { + MU_VER_t VER; + MU_PAR_t PAR; + MU_CR_t CR; + MU_SR_t SR; + MU_CCR0_t CCR0; + MU_CIER0_t CIER0; + MU_CSSR0_t CSSR0; + MU_CSR0_t CSR0; + uint8_t MU_reserved0[224]; + MU_FCR_t FCR; + MU_FSR_t FSR; + uint8_t MU_reserved1[8]; + MU_GIER_t GIER; + MU_GCR_t GCR; + MU_GSR_t GSR; + uint8_t MU_reserved2[4]; + MU_TCR_t TCR; + MU_TSR_t TSR; + MU_RCR_t RCR; + MU_RSR_t RSR; + uint8_t MU_reserved3[208]; + MU_TR0_t TR[2]; + uint8_t MU_reserved4[120]; + MU_RR0_t RR[2]; +}; + +#endif /* MU_H */ diff --git a/plat/imx/imx8ulp/upower/upower_api.c b/plat/imx/imx8ulp/upower/upower_api.c new file mode 100644 index 00000000..ce8c1c84 --- /dev/null +++ b/plat/imx/imx8ulp/upower/upower_api.c @@ -0,0 +1,3095 @@ +/* SPDX-License-Identifier: BSD-3-Clause */ +/** + * Copyright 2019-2024 NXP + * + * KEYWORDS: micro-power uPower driver API + */ + +#include <string.h> + +#include "upower_api.h" +#include "upower_soc_defs.h" + +/* --------------------------------------------------------------- + * Common Macros + * --------------------------------------------------------------- + */ + +/* tests Service Group busy */ +#define UPWR_SG_BUSY(sg) ((sg_busy & (1U << (sg))) == 1U) + +/* install user callback for the Service Group */ +#define UPWR_USR_CALLB(sg, cb) { user_callback[(sg)] = (cb); } + +/* fills up common message header info */ +#define UPWR_MSG_HDR(hdr, sg, fn) { \ + (hdr).domain = (uint32_t)pwr_domain; \ + (hdr).srvgrp = (sg); \ + (hdr).function = (fn); } + +/* --------------------------------------------------------------- + * Common Data Structures + * --------------------------------------------------------------- + */ +static soc_domain_t pwr_domain; + +static upwr_code_vers_t fw_rom_version; +static upwr_code_vers_t fw_ram_version; +static uint32_t fw_launch_option; + +/* shared memory buffers */ +#define UPWR_API_BUFFER_SIZE (MAX_SG_EXCEPT_MEM_SIZE + \ + MAX_SG_PWRMGMT_MEM_SIZE + MAX_SG_VOLTM_MEM_SIZE) + +/* service group shared mem buffer pointers */ +static void *sh_buffer[UPWR_SG_COUNT]; + +/* Callbacks registered for each service group : + * + * NULL means no callback is registered; + * for sgrp_callback, it also means the service group is + * free to receive a new request. + */ +static upwr_callb user_callback[UPWR_SG_COUNT]; +static UPWR_RX_CALLB_FUNC_T sgrp_callback[UPWR_SG_COUNT]; + +/* request data structures for each service group */ +/* message waiting for TX */ +static upwr_down_max_msg sg_req_msg[UPWR_SG_COUNT]; +/* waiting message size */ +static unsigned int sg_req_siz[UPWR_SG_COUNT]; +/* response msg */ +static upwr_up_max_msg sg_rsp_msg[UPWR_SG_COUNT]; +/* response msg size */ +static unsigned int sg_rsp_siz[UPWR_SG_COUNT]; + +/* tx pending status for each (1 bit per service group) */ +static volatile uint32_t sg_tx_pend; +/* serv.group of current ongoing Tx, if any */ +static volatile upwr_sg_t sg_tx_curr; + +/* service group busy status, only for this domain (MU index 0) */ +/* SG bit = 1 if group is busy with a request */ +static volatile uint32_t sg_busy; + +/* OS-dependent memory allocation function */ +static upwr_malloc_ptr_t os_malloc; +/* OS-dependent pointer->physical address conversion function */ +static upwr_phyadr_ptr_t os_ptr2phy; +/* OS-dependent function to lock critical code */ +static upwr_lock_ptr_t os_lock; + +/* pointer to MU structure */ +static struct MU_t *mu; + +/* + * indicates that a transmission was done and is pending; this + * bit is necessary because the Tx and Rx interrupts are ORed + * together, and there is no way of telling if only Rx interrupt + * or both occurred just by looking at the MU status registers + */ +static uint32_t mu_tx_pend; + +static UPWR_TX_CALLB_FUNC_T mu_tx_callb; +static UPWR_RX_CALLB_FUNC_T mu_rx_callb; + +#define UPWR_API_INIT_WAIT (0U) /* waiting for ROM firmware initialization */ +#define UPWR_API_INITLZED (1U) /* ROM firmware initialized */ +#define UPWR_API_START_WAIT (2U) /* waiting for start services */ +#define UPWR_API_SHUTDOWN_WAIT (3U) /* waiting for shutdown */ +#define UPWR_API_READY (4U) /* ready to receive service requests */ + +volatile upwr_api_state_t api_state; + +/* default pointer->physical address conversion, returns the same address */ +static void *ptr2phys(const void *ptr) +{ + return (void *)ptr; +} + +/* --------------------------------------------------------------- + * SHARED MEMORY MANAGEMENT + * -------------------------------------------------------------- + */ + +/* + * upwr_ptr2offset() - converts a pointer (casted to uint64_t) to an + * address offset from the shared memory start. If it does not point + * to a shared memory location, the structure pointed is copied to a + * buffer in the shared memory, and the buffer offset is returned. + * The 2nd argument is the service group to which the buffer belongs; + * The 3rd argument is the size of structure to be copied. The 4th argument + * is an offset to apply to the copy destination address. The 5th argument + * is ptr before the conversion to physical address. 2nd, 3rd. 4th and 5th + * arguments are not used if the 1st one points to a location inside the + * shared memory. + */ + +static uint32_t upwr_ptr2offset(unsigned long ptr, + upwr_sg_t sg, + size_t siz, + size_t offset, + const void *vptr) +{ + if ((ptr >= UPWR_DRAM_SHARED_BASE_ADDR) && + ((ptr - UPWR_DRAM_SHARED_BASE_ADDR) < UPWR_DRAM_SHARED_SIZE)) { + return (uint32_t)(ptr - UPWR_DRAM_SHARED_BASE_ADDR); + } + + /* pointer is outside the shared memory, copy the struct to buffer */ + (void)memcpy((void *)(offset + (char *)sh_buffer[sg]), (void *)vptr, siz); + return (uint32_t)((unsigned long)sh_buffer[sg] + offset - UPWR_DRAM_SHARED_BASE_ADDR); +} + +/* + * --------------------------------------------------------------- + * INTERRUPTS AND CALLBACKS + * Service-group specific callbacks are in their own sections + * -------------------------------------------------------------- + */ + +/* + * upwr_lock()- locks (lock=1) or unlocks (lock=0) a critical code section; + * for now it only needs to protect a portion of the code from being + * interrupted by the MU. + */ +static void upwr_lock(int lock) +{ + if (os_lock != NULL) { + os_lock(lock); + } +} + +/* upwr_exp_isr()- handles the exception interrupt from uPower */ +static void upwr_exp_isr(void) +{ +} + +/* upwr_copy2tr prototype; function definition in auxiliary function section */ +void upwr_copy2tr(struct MU_t *local_mu, const uint32_t *msg, unsigned int size); + +#define UPWR_MU_TSR_EMPTY ((uint32_t)((1UL << UPWR_MU_MSG_SIZE) - 1UL)) + +/* upwr_txrx_isr()- handles both the Tx and Rx MU interrupts */ +void upwr_txrx_isr(void) +{ + /* Tx pending and TX register empty */ + if ((mu_tx_pend != 0UL) && (mu->TSR.R == UPWR_MU_TSR_EMPTY)) { + mu_tx_pend = 0UL; + /* disable the tx interrupts */ + mu->TCR.R = 0U; + /* urgency flag off, in case it was set */ + mu->FCR.B.F0 = 0U; + + if (mu_tx_callb != NULL) { + mu_tx_callb(); + } + } + + /* RX ISR occurred */ + if (mu->RSR.R != 0UL) { + /* disable the interrupt until data is read */ + mu->RCR.R = 0U; + + if (mu_rx_callb != NULL) { + mu_rx_callb(); + } + } +} + +/** + * upwr_next_req() - sends the next pending service request message, if any. + * + * Called upon MU Tx interrupts, it checks if there is any service request + * pending amongst the service groups, and sends the request if needed. + * + * Context: no sleep, no locks taken/released. + * Return: none (void). + */ +static void upwr_next_req(void) +{ + upwr_sg_t sg = (upwr_sg_t)0U; + + /* no lock needed here, this is called from an MU ISR */ + sg_tx_pend &= ~((uint32_t)1UL << sg_tx_curr); /* no longer pending */ + + if (sg_tx_pend == 0U) { + return; /* no other pending */ + } + + /* find the next one pending */ + for (uint32_t mask = 1UL; mask < (1UL << UPWR_SG_COUNT); mask = mask << 1UL) { + if ((sg_tx_pend & mask) != 0U) { + break; + } + + sg = (upwr_sg_t)(sg + 1U); + } + + sg_tx_curr = sg; + if (upwr_tx((uint32_t *)&sg_req_msg[sg], sg_req_siz[sg], upwr_next_req) < 0) { + return; /* leave the Tx pending */ + } +} + +/** + * upwr_mu_int_callback() - general MU interrupt callback. + * + * Called upon MU Rx interrupts, it calls the Service Group-specific callback, + * if any registered, based on the service group field in the received message. + * Otherwise, calls the user callback, if any registered. + * + * Context: no sleep, no locks taken/released. + * Return: none (void). + */ +static void upwr_mu_int_callback(void) +{ + upwr_sg_t sg; /* service group number */ + UPWR_RX_CALLB_FUNC_T sg_callb; /* service group callback */ + upwr_up_max_msg rxmsg = {0}; + unsigned int size; /* in words */ + + if (upwr_rx((char *)&rxmsg, &size) < 0) { + return; + } + + sg = (upwr_sg_t)rxmsg.hdr.srvgrp; + + /* copy msg to the service group buffer */ + msg_copy((char *)&sg_rsp_msg[sg], (char *)&rxmsg, size); + sg_rsp_siz[sg] = size; + + /* clear the service group busy status */ + sg_busy &= ~(1UL << sg); /* no lock needed here, we're in the MU ISR */ + + sg_callb = sgrp_callback[sg]; + if (sg_callb == NULL) { + upwr_callb user_callb = user_callback[sg]; + /* no service group callback; call the user callback if any */ + if (user_callb == NULL) { + goto done; /* no user callback */ + } + + /* make the user callback */ + user_callb(sg, rxmsg.hdr.function, + (upwr_resp_t)rxmsg.hdr.errcode, + (size == 2U) ? rxmsg.word2 : rxmsg.hdr.ret); + goto done; + } + + /* + * finally make the group callback. don't uninstall the group + * callback, it is permanent. + */ + sg_callb(); +done: + if (rxmsg.hdr.errcode == UPWR_RESP_SHUTDOWN) { /* shutdown error: */ + /* + * change the API state automatically. so new requests + * are rejected by the API immediately + */ + api_state = UPWR_API_INITLZED; + } +} + +/** + * upwr_srv_req() - sends a service request message. + * @sg: message service group. + * @msg: pointer to the message + * @size: message size in 32-bit words. + * + * The message is sent right away if possible, or gets pending to be sent later. + * If pending, the message is stored in sg_req_msg and will be sent when the + * MU transmission buffer is clear and there are no other pending messages + * from higher priority service groups. + * + * This is an auxiliary function used by the rest of the API calls. + * It is normally not called by the driver code, unless maybe for test purposes. + * + * Context: no sleep, no locks taken/released. + * Return: none (void) + */ +static void upwr_srv_req(upwr_sg_t sg, + uint32_t *msg, + unsigned int size) +{ + int rc; + + upwr_lock(1); + sg_busy |= (uint32_t)1U << sg; + upwr_lock(0); + + rc = upwr_tx(msg, size, upwr_next_req); + if (rc < 0) { + /* queue full, make the transmission pending */ + msg_copy((char *)&sg_req_msg[sg], (char *)msg, size); + sg_req_siz[sg] = size; + + upwr_lock(1); + sg_tx_curr = sg; + sg_tx_pend |= (uint32_t)1U << sg; + upwr_lock(0); + + return; + } +} + +/**--------------------------------------------------------------- + * INITIALIZATION, CONFIGURATION + * + * A reference uPower initialization sequence goes as follows: + * + * 1. host CPU calls upwr_init. + * 2. (optional) host checks the ROM version and SoC code calling upwr_vers(...) + * and optionally performs any configuration or workaround accordingly. + * 3. host CPU calls upwr_start to start the uPower services, passing a + * service option number. + * If no RAM code is loaded or it has no service options, the launch option + * number passed must be 0, which will start the services available in ROM. + * upwr_start also receives a pointer to a callback called by the API + * when the firmware is ready to receive service requests. + * The callback may be replaced by polling, calling upwr_req_status in a loop + * or upwr_poll_req_status; in this case the callback pointer may be NULL. + * A host may call upwr_start even if the services were already started by + * any host: if the launch option is the same, the response will be ok, + * but will indicate error if the services were already started with a + * different launch option. + * 4. host waits for the callback calling, or polling finishing; + * if no error is returned, it can start making service calls using the API. + * + * Variations on that reference sequence are possible: + * - the uPower services can be started using the ROM code only, which includes + * the basic Power Management services, among others, with launch option + * number = 0. + * The code RAM can be loaded while these services are running and, + * when the loading is done, the services can be re-started with these 2 + * requests executed in order: upwr_xcp_shutdown and upwr_start, + * using the newly loaded RAM code (launch option > 0). + * + * NOTE: the initialization call upwr_init is not effective and + * returns error when called after the uPower services are started. + */ + +/** + * upwr_start_callb() - internal callback for the Rx message from uPower + * that indicates the firmware is ready to receive the start commands. + * It calls the user callbacks registered in the upwr_start_boot and upwr_start + * call. + */ +void upwr_start_callb(void) +{ + switch (api_state) { + case UPWR_API_START_WAIT: { + upwr_rdy_callb start_callb = (upwr_rdy_callb)user_callback[UPWR_SG_EXCEPT]; + upwr_ready_msg *msg = (upwr_ready_msg *)&sg_rsp_msg[UPWR_SG_EXCEPT]; + + fw_ram_version.soc_id = fw_rom_version.soc_id; + fw_ram_version.vmajor = msg->args.vmajor; + fw_ram_version.vminor = msg->args.vminor; + fw_ram_version.vfixes = msg->args.vfixes; + + /* + * vmajor == vminor == vfixes == 0 indicates start error + * in this case, go back to the INITLZED state + */ + if ((fw_ram_version.vmajor != 0U) || + (fw_ram_version.vminor != 0U) || + (fw_ram_version.vfixes != 0U)) { + api_state = UPWR_API_READY; + + /* + * initialization is over: + * uninstall the user callback just in case + */ + UPWR_USR_CALLB(UPWR_SG_EXCEPT, NULL); + + if (fw_launch_option == 0U) { + /* + * launched ROM firmware: + * RAM fw versions must be all 0s + */ + fw_ram_version.vmajor = 0U; + fw_ram_version.vminor = 0U; + fw_ram_version.vfixes = 0U; + } + } else { + api_state = UPWR_API_INITLZED; + } + + start_callb(msg->args.vmajor, msg->args.vminor, msg->args.vfixes); + } + break; + + case UPWR_API_SHUTDOWN_WAIT: { + upwr_callb user_callb = (upwr_callb)user_callback[UPWR_SG_EXCEPT]; + upwr_shutdown_msg *msg = (upwr_shutdown_msg *)&sg_rsp_msg[UPWR_SG_EXCEPT]; + + if ((upwr_resp_t)msg->hdr.errcode == UPWR_RESP_OK) { + api_state = UPWR_API_INITLZED; + } + + if (user_callb != NULL) { + user_callb(UPWR_SG_EXCEPT, UPWR_XCP_SHUTDOWN, + (upwr_resp_t)msg->hdr.errcode, 0U); + } + } + break; + + case UPWR_API_READY: + { + upwr_callb user_callb = (upwr_callb)user_callback[UPWR_SG_EXCEPT]; + upwr_up_max_msg *msg = (upwr_up_max_msg *)&sg_rsp_msg[UPWR_SG_EXCEPT]; + + if (user_callb != NULL) { + user_callb(UPWR_SG_EXCEPT, msg->hdr.function, + (upwr_resp_t)msg->hdr.errcode, + (int)((sg_rsp_siz[UPWR_SG_EXCEPT] == 2U) ? + msg->word2 : msg->hdr.ret)); + } + } + break; + + default: + break; + } +} + +/** + * upwr_init() - API initialization; must be the first API call after reset. + * @domain: SoC-dependent CPU domain id; identifier used by the firmware in + * many services. Defined by SoC-dependent type soc_domain_t found in + * upower_soc_defs.h. + * @muptr: pointer to the MU instance. + * @mallocptr: pointer to the memory allocation function + * @physaddrptr: pointer to the function to convert pointers to + * physical addresses. If NULL, no conversion is made (pointer=physical address) + * @isrinstptr: pointer to the function to install the uPower ISR callbacks; + * the function receives the pointers to the MU tx/rx and Exception ISRs + * callbacks, which must be called from the actual system ISRs. + * The function pointed by isrinstptr must also enable the interrupt at the + * core/interrupt controller, but must not enable the interrupt at the MU IP. + * The system ISRs are responsible for dealing with the interrupt controller, + * performing any other context save/restore, and any other housekeeping. + * @lockptr: pointer to a function that prevents MU interrupts (if argrument=1) + * or allows it (if argument=0). The API calls this function to make small + * specific code portions thread safe. Only MU interrupts must be avoided, + * the code may be suspended for other reasons. + * If no MU interrupts can happen during the execution of an API call or + * callback, even if enabled, for some other reason (e.g. interrupt priority), + * then this argument may be NULL. + * + * Context: no sleep, no locks taken/released. + * Return: 0 if ok, + * -1 if failed to allocate memory, or use some other resource. + * -2 if any argument is invalid. + * -3 if failed to send the ping message. + * -4 if failed to receive the initialization message, or was invalid + */ +int upwr_init(soc_domain_t domain, struct MU_t *muptr, + const upwr_malloc_ptr_t mallocptr, + const upwr_phyadr_ptr_t phyadrptr, + const upwr_inst_isr_ptr_t isrinstptr, + const upwr_lock_ptr_t lockptr) +{ + uint32_t j; + + upwr_sg_t sg; /* service group number */ + unsigned int size; + unsigned long dom_buffer_base = (domain == RTD_DOMAIN) ? UPWR_API_BUFFER_BASE : + ((UPWR_API_BUFFER_ENDPLUS + UPWR_API_BUFFER_BASE) / 2U); + + upwr_init_msg *msg = (upwr_init_msg *)&sg_rsp_msg[UPWR_SG_EXCEPT]; + + mu = muptr; + /* + * Disable tx and rx interrupts in case not called + * 1st time after reset + */ + mu->TCR.R = mu->RCR.R = 0U; + + os_malloc = mallocptr; + os_ptr2phy = (phyadrptr == (upwr_phyadr_ptr_t)NULL) ? ptr2phys : phyadrptr; + + os_lock = lockptr; + api_state = UPWR_API_INIT_WAIT; + sg_busy = 0UL; + pwr_domain = domain; + + /* initialize the versions, in case they are polled */ + fw_rom_version.soc_id = 0U; + fw_rom_version.vmajor = 0U; + fw_rom_version.vminor = 0U; + fw_rom_version.vfixes = 0U; + + fw_ram_version.soc_id = 0U; + fw_ram_version.vmajor = 0U; + fw_ram_version.vminor = 0U; + fw_ram_version.vfixes = 0U; + + mu_tx_pend = (uint32_t)0U; + sg_tx_pend = (uint32_t)0U; + + sg_tx_curr = UPWR_SG_COUNT; /* means none here */ + + sh_buffer[UPWR_SG_EXCEPT] = (void *)(unsigned long)dom_buffer_base; + sh_buffer[UPWR_SG_PWRMGMT] = (void *)(unsigned long)(dom_buffer_base + + MAX_SG_EXCEPT_MEM_SIZE); + sh_buffer[UPWR_SG_DELAYM] = NULL; + sh_buffer[UPWR_SG_VOLTM] = (void *)(unsigned long)(dom_buffer_base + + MAX_SG_EXCEPT_MEM_SIZE + MAX_SG_PWRMGMT_MEM_SIZE); + sh_buffer[UPWR_SG_CURRM] = NULL; + sh_buffer[UPWR_SG_TEMPM] = NULL; + sh_buffer[UPWR_SG_DIAG] = NULL; + + /* (no buffers service groups other than xcp and pwm for now) */ + for (j = 0; j < UPWR_SG_COUNT; j++) { + user_callback[j] = NULL; + /* service group Exception gets the initialization callbacks */ + sgrp_callback[j] = (j == UPWR_SG_EXCEPT) ? upwr_start_callb : NULL; + /* response messages with an initial consistent content */ + sg_rsp_msg[j].hdr.errcode = UPWR_RESP_SHUTDOWN; + } + + /* init message already received, assume takss are running on upower */ + if (mu->FSR.B.F0 != 0U) { + /* send a ping message down to get the ROM version back */ + upwr_xcp_ping_msg ping_msg = {0}; + + ping_msg.hdr.domain = pwr_domain; + ping_msg.hdr.srvgrp = UPWR_SG_EXCEPT; + ping_msg.hdr.function = UPWR_XCP_PING; + + if (mu->RSR.B.RF0 != 0U) { /* first clean any Rx message left over */ + (void)upwr_rx((char *)msg, &size); + } + + /* wait any TX left over to be sent */ + while (mu->TSR.R != UPWR_MU_TSR_EMPTY) { + } + + /* + * now send the ping message; + * do not use upwr_tx, which needs API initialized; + * just write to the MU TR register(s) + */ + mu->FCR.B.F0 = 1U; /* flag urgency status */ + upwr_copy2tr(mu, (uint32_t *)&ping_msg, sizeof(ping_msg) / 4U); + } + + do { + /* + * poll for the MU Rx status: wait for an init message, either + * 1st sent from uPower after reset or as a response to a ping + */ + while (mu->RSR.B.RF0 == 0U) { + } + + /* urgency status off, in case it was set */ + mu->FCR.B.F0 = 0U; + + if (upwr_rx((char *)msg, &size) < 0) { + return -4; + } + + if (size != (sizeof(upwr_init_msg) / 4U)) { + if (mu->FSR.B.F0 != 0U) { + continue; /* discard left over msg */ + } else { + return -4; + } + } + + sg = (upwr_sg_t)msg->hdr.srvgrp; + if (sg != UPWR_SG_EXCEPT) { + if (mu->FSR.B.F0 != 0U) { + continue; /* discard left over msg */ + } else { + return -4; + } + } + + if ((upwr_xcp_f_t)msg->hdr.function != UPWR_XCP_INIT) { + if (mu->FSR.B.F0 != 0U) { + continue; /* discard left over msg */ + } else { + return -4; + } + } + + break; + } while (true); + + fw_rom_version.soc_id = msg->args.soc; + fw_rom_version.vmajor = msg->args.vmajor; + fw_rom_version.vminor = msg->args.vminor; + fw_rom_version.vfixes = msg->args.vfixes; + + if (upwr_rx_callback(upwr_mu_int_callback) < 0) { + /* catastrophic error, but is it possible to happen? */ + return -1; + } + + mu_tx_callb = NULL; /* assigned on upwr_tx */ + + /* install the ISRs and enable the interrupts */ + isrinstptr(upwr_txrx_isr, upwr_exp_isr); + + /* enable only RR[0] receive interrupt */ + mu->RCR.R = 1U; + + api_state = UPWR_API_INITLZED; + + return 0; +} + +/** + * upwr_start() - Starts the uPower services. + * @launchopt: a number to select between multiple launch options, + * that may define, among other things, which services will be started, + * or which services implementations, features etc. + * launchopt = 0 selects a subset of services implemented in ROM; + * any other number selects service sets implemented in RAM, launched + * by the firmware function ram_launch; if an invalid launchopt value is passed, + * no services are started, and the callback returns error (see below). + * @rdycallb: pointer to the callback to be called when the uPower is ready + * to receive service requests. NULL if no callback needed. + * The callback receives as arguments the RAM firmware version numbers. + * If all 3 numbers (vmajor, vminor, vfixes) are 0, that means the + * service launching failed. + * Firmware version numbers will be the same as ROM if launchopt = 0, + * selecting the ROM services. + * + * upwr_start can be called by any domain even if the services are already + * started: it has no effect, returning success, if the launch option is the + * same as the one that actually started the service, and returns error if + * called with a different option. + * + * A callback can be optionally registered, and will be called upon the arrival + * of the request response from the uPower firmware, telling if it succeeded or + * not. + * A callback may not be registered (NULL pointer), in which case polling has + * to be used to check the response, by calling upwr_req_status or + * upwr_poll_req_status, using UPWR_SG_EXCEPT as the service group argument. + * + * Context: no sleep, no locks taken/released. + * Return: 0 if ok, + * -1 if a resource failed, + * -2 if the domain passed is the same as the caller, + * -3 if called in an invalid API state + */ +int upwr_start(uint32_t launchopt, const upwr_rdy_callb rdycallb) +{ + upwr_start_msg txmsg = {0}; + + if (api_state != UPWR_API_INITLZED) { + return -3; + } + + UPWR_USR_CALLB(UPWR_SG_EXCEPT, (upwr_callb)rdycallb); + + UPWR_MSG_HDR(txmsg.hdr, UPWR_SG_EXCEPT, UPWR_XCP_START); + + txmsg.hdr.arg = fw_launch_option = launchopt; + + if (upwr_tx((uint32_t *)&txmsg, sizeof(txmsg) / 4U, NULL) < 0) { + /* catastrophic error, but is it possible to happen? */ + return -1; + } + + api_state = UPWR_API_START_WAIT; + + return 0; +} + +/**--------------------------------------------------------------- + * EXCEPTION SERVICE GROUP + */ + +/** + * upwr_xcp_config() - Applies general uPower configurations. + * @config: pointer to the uPower SoC-dependent configuration struct + * upwr_xcp_config_t defined in upower_soc_defs.h. NULL may be passed, meaning + * a request to read the configuration, in which case it appears in the callback + * argument ret, or can be pointed by argument retptr in the upwr_req_status and + * upwr_poll_req_status calls, casted to upwr_xcp_config_t. + * @callb: pointer to the callback to be called when the uPower has finished + * the configuration, or NULL if no callback needed (polling used instead). + * + * Some configurations are targeted for a specific domain (see the struct + * upwr_xcp_config_t definition in upower_soc_defs.h); this call has implicit + * domain target (the same domain from which is called). + * + * The return value is always the current configuration value, either in a + * read-only request (config = NULL) or after setting a new configuration + * (non-NULL config). + * + * A callback can be optionally registered, and will be called upon the arrival + * of the request response from the uPower firmware, telling if it succeeded or + * not. + * A callback may not be registered (NULL pointer), in which case polling has + * to be used to check the response, by calling upwr_req_status or + * upwr_poll_req_status, using UPWR_SG_EXCEPT as the service group argument. + * + * Context: no sleep, no locks taken/released. + * Return: 0 if ok, + * -1 if service group is busy, + * -3 if called in an invalid API state + */ +int upwr_xcp_config(const upwr_xcp_config_t *config, const upwr_callb callb) +{ + upwr_xcp_config_msg txmsg = {0}; + + if (api_state != UPWR_API_READY) { + return -3; + } + + if (UPWR_SG_BUSY(UPWR_SG_EXCEPT)) { + return -1; + } + + UPWR_USR_CALLB(UPWR_SG_EXCEPT, callb); + + if (config == NULL) { + txmsg.hdr.arg = 1U; /* 1= read, txmsg.word2 ignored */ + } else { + txmsg.hdr.arg = 0U; /* 1= write */ + txmsg.word2 = config->R; + } + + UPWR_MSG_HDR(txmsg.hdr, UPWR_SG_EXCEPT, UPWR_XCP_CONFIG); + + upwr_srv_req(UPWR_SG_EXCEPT, (uint32_t *)&txmsg, sizeof(txmsg) / 4U); + + return 0; +} + +/** + * upwr_xcp_sw_alarm() - Makes uPower issue an alarm interrupt to given domain. + * @domain: identifier of the domain to alarm. Defined by SoC-dependent type + * soc_domain_t found in upower_soc_defs.h. + * @code: alarm code. Defined by SoC-dependent type upwr_alarm_t found in + * upower_soc_defs.h. + * @callb: pointer to the callback to be called when the uPower has finished + * the alarm, or NULL if no callback needed (polling used instead). + * + * The function requests the uPower to issue an alarm of the given code as if + * it had originated internally. This service is useful mainly to test the + * system response to such alarms, or to make the system handle a similar alarm + * situation detected externally to uPower. + * + * The system ISR/code handling the alarm may retrieve the alarm code by calling + * the auxiliary function upwr_alarm_code. + * + * A callback can be optionally registered, and will be called upon the arrival + * of the request response from the uPower firmware, telling if it succeeded or + * not. + * A callback may not be registered (NULL pointer), in which case polling has + * to be used to check the response, by calling upwr_req_status or + * upwr_poll_req_status, using UPWR_SG_EXCEPT as the service group argument. + * + * Context: no sleep, no locks taken/released. + * Return: 0 if ok, + * -1 if service group is busy, + * -3 if called in an invalid API state + */ +int upwr_xcp_sw_alarm(soc_domain_t domain, + upwr_alarm_t code, + const upwr_callb callb) +{ + upwr_xcp_swalarm_msg txmsg = {0}; + + if (api_state != UPWR_API_READY) { + return -3; + } + + if (UPWR_SG_BUSY(UPWR_SG_EXCEPT)) { + return -1; + } + + UPWR_USR_CALLB(UPWR_SG_EXCEPT, callb); + + UPWR_MSG_HDR(txmsg.hdr, UPWR_SG_EXCEPT, UPWR_XCP_SW_ALARM); + txmsg.hdr.domain = (uint32_t)domain; + txmsg.hdr.arg = (uint32_t)code; + + upwr_srv_req(UPWR_SG_EXCEPT, (uint32_t *)&txmsg, sizeof(txmsg) / 4U); + + return 0; +} + +/** + * upwr_xcp_set_ddr_retention() - M33/A35 can use this API to set/clear ddr retention + * @domain: identifier of the caller domain. + * soc_domain_t found in upower_soc_defs.h. + * @enable: true, means that set ddr retention, false clear ddr retention. + * @callb: NULL + * + * A callback may not be registered (NULL pointer), in which case polling has + * to be used to check the response, by calling upwr_req_status or + * upwr_poll_req_status, using UPWR_SG_EXCEPT as the service group argument. + * + * Context: no sleep, no locks taken/released. + * Return: 0 if ok, + * -1 if service group is busy, + * -3 if called in an invalid API state + */ +int upwr_xcp_set_ddr_retention(soc_domain_t domain, + uint32_t enable, + const upwr_callb callb) +{ + upwr_xcp_ddr_retn_msg txmsg = {0}; + + if (api_state != UPWR_API_READY) { + return -3; + } + + if (UPWR_SG_BUSY(UPWR_SG_EXCEPT)) { + return -1; + } + + UPWR_USR_CALLB(UPWR_SG_EXCEPT, callb); + + UPWR_MSG_HDR(txmsg.hdr, UPWR_SG_EXCEPT, UPWR_XCP_SET_DDR_RETN); + txmsg.hdr.domain = (uint32_t)domain; + txmsg.hdr.arg = (uint32_t)enable; + + upwr_srv_req(UPWR_SG_EXCEPT, (uint32_t *)&txmsg, sizeof(txmsg) / 4U); + + return 0; +} + +/** + * upwr_xcp_set_mipi_dsi_ena() - M33/A35 can use this API to set/clear mipi dsi ena + * @domain: identifier of the caller domain. + * soc_domain_t found in upower_soc_defs.h. + * @enable: true, means that set ddr retention, false clear ddr retention. + * @callb: NULL + * + * A callback may not be registered (NULL pointer), in which case polling has + * to be used to check the response, by calling upwr_req_status or + * upwr_poll_req_status, using UPWR_SG_EXCEPT as the service group argument. + * + * Context: no sleep, no locks taken/released. + * Return: 0 if ok, + * -1 if service group is busy, + * -3 if called in an invalid API state + */ +int upwr_xcp_set_mipi_dsi_ena(soc_domain_t domain, + uint32_t enable, + const upwr_callb callb) +{ + upwr_xcp_set_mipi_dsi_ena_msg txmsg = {0}; + + if (api_state != UPWR_API_READY) { + return -3; + } + + if (UPWR_SG_BUSY(UPWR_SG_EXCEPT)) { + return -1; + } + + UPWR_USR_CALLB(UPWR_SG_EXCEPT, callb); + + UPWR_MSG_HDR(txmsg.hdr, UPWR_SG_EXCEPT, UPWR_XCP_SET_MIPI_DSI_ENA); + txmsg.hdr.domain = (uint32_t)domain; + txmsg.hdr.arg = (uint32_t)enable; + + upwr_srv_req(UPWR_SG_EXCEPT, (uint32_t *)&txmsg, sizeof(txmsg) / 4U); + + return 0; +} + +/** + * upwr_xcp_get_mipi_dsi_ena() - M33/A35 can use this API to get mipi dsi ena status + * @domain: identifier of the caller domain. + * soc_domain_t found in upower_soc_defs.h. + * @callb: NULL + * + * A callback may not be registered (NULL pointer), in which case polling has + * to be used to check the response, by calling upwr_req_status or + * upwr_poll_req_status, using UPWR_SG_EXCEPT as the service group argument. + * + * Context: no sleep, no locks taken/released. + * Return: 0 if ok, + * -1 if service group is busy, + * -3 if called in an invalid API state + */ +int upwr_xcp_get_mipi_dsi_ena(soc_domain_t domain, const upwr_callb callb) +{ + upwr_xcp_get_mipi_dsi_ena_msg txmsg = {0}; + + if (api_state != UPWR_API_READY) { + return -3; + } + + if (UPWR_SG_BUSY(UPWR_SG_EXCEPT)) { + return -1; + } + + UPWR_USR_CALLB(UPWR_SG_EXCEPT, callb); + + UPWR_MSG_HDR(txmsg.hdr, UPWR_SG_EXCEPT, UPWR_XCP_GET_MIPI_DSI_ENA); + txmsg.hdr.domain = (uint32_t)domain; + + upwr_srv_req(UPWR_SG_EXCEPT, (uint32_t *)&txmsg, sizeof(txmsg) / 4U); + + return 0; +} + +/** + * upwr_xcp_set_osc_mode() - M33/A35 can use this API to set uPower OSC mode + * @domain: identifier of the caller domain. + * soc_domain_t found in upower_soc_defs.h. + * @osc_mode, 0 means low frequency, not 0 means high frequency. + * @callb: NULL + * + * A callback may not be registered (NULL pointer), in which case polling has + * to be used to check the response, by calling upwr_req_status or + * upwr_poll_req_status, using UPWR_SG_EXCEPT as the service group argument. + * + * Context: no sleep, no locks taken/released. + * Return: 0 if ok, + * -1 if service group is busy, + * -3 if called in an invalid API state + */ +int upwr_xcp_set_osc_mode(soc_domain_t domain, + uint32_t osc_mode, + const upwr_callb callb) +{ + upwr_xcp_set_osc_mode_msg txmsg = {0}; + + if (api_state != UPWR_API_READY) { + return -3; + } + + if (UPWR_SG_BUSY(UPWR_SG_EXCEPT)) { + return -1; + } + + UPWR_USR_CALLB(UPWR_SG_EXCEPT, callb); + + UPWR_MSG_HDR(txmsg.hdr, UPWR_SG_EXCEPT, UPWR_XCP_SET_OSC_MODE); + txmsg.hdr.domain = (uint32_t)domain; + txmsg.hdr.arg = (uint32_t)osc_mode; + + upwr_srv_req(UPWR_SG_EXCEPT, (uint32_t *)&txmsg, sizeof(txmsg) / 4U); + + return 0; +} + +/** + * upwr_xcp_set_rtd_use_ddr() - M33 call this API to inform uPower, M33 is using ddr + * @domain: identifier of the caller domain. + * soc_domain_t found in upower_soc_defs.h. + * @is_use_ddr: not 0, true, means that RTD is using ddr. 0, false, means that, RTD + * is not using ddr. + * @callb: NULL + * + * A callback may not be registered (NULL pointer), in which case polling has + * to be used to check the response, by calling upwr_req_status or + * upwr_poll_req_status, using UPWR_SG_EXCEPT as the service group argument. + * + * Context: no sleep, no locks taken/released. + * Return: 0 if ok, + * -1 if service group is busy, + * -3 if called in an invalid API state + */ +int upwr_xcp_set_rtd_use_ddr(soc_domain_t domain, + uint32_t is_use_ddr, + const upwr_callb callb) +{ + upwr_xcp_rtd_use_ddr_msg txmsg = {0}; + + if (api_state != UPWR_API_READY) { + return -3; + } + + if (UPWR_SG_BUSY(UPWR_SG_EXCEPT)) { + return -1; + } + + UPWR_USR_CALLB(UPWR_SG_EXCEPT, callb); + + UPWR_MSG_HDR(txmsg.hdr, UPWR_SG_EXCEPT, UPWR_XCP_SET_RTD_USE_DDR); + txmsg.hdr.domain = (uint32_t)domain; + txmsg.hdr.arg = (uint32_t)is_use_ddr; + + upwr_srv_req(UPWR_SG_EXCEPT, (uint32_t *)&txmsg, sizeof(txmsg) / 4U); + + return 0; +} + +/** + * upwr_xcp_set_rtd_apd_llwu() - M33/A35 can use this API to set/clear rtd_llwu apd_llwu + * @domain: set which domain (RTD_DOMAIN, APD_DOMAIN) LLWU. + * soc_domain_t found in upower_soc_defs.h. + * @enable: true, means that set rtd_llwu or apd_llwu, false clear rtd_llwu or apd_llwu. + * @callb: NULL + * + * A callback may not be registered (NULL pointer), in which case polling has + * to be used to check the response, by calling upwr_req_status or + * upwr_poll_req_status, using UPWR_SG_EXCEPT as the service group argument. + * + * Context: no sleep, no locks taken/released. + * Return: 0 if ok, + * -1 if service group is busy, + * -3 if called in an invalid API state + */ +int upwr_xcp_set_rtd_apd_llwu(soc_domain_t domain, + uint32_t enable, + const upwr_callb callb) +{ + upwr_xcp_rtd_apd_llwu_msg txmsg = {0}; + + if (api_state != UPWR_API_READY) { + return -3; + } + + if (UPWR_SG_BUSY(UPWR_SG_EXCEPT)) { + return -1; + } + + UPWR_USR_CALLB(UPWR_SG_EXCEPT, callb); + + UPWR_MSG_HDR(txmsg.hdr, UPWR_SG_EXCEPT, UPWR_XCP_SET_RTD_APD_LLWU); + txmsg.hdr.domain = (uint32_t)domain; + txmsg.hdr.arg = (uint32_t)enable; + + upwr_srv_req(UPWR_SG_EXCEPT, (uint32_t *)&txmsg, sizeof(txmsg) / 4U); + + return 0; +} + +/** + * upwr_xcp_shutdown() - Shuts down all uPower services and power mode tasks. + * @callb: pointer to the callback to be called when the uPower has finished + * the shutdown, or NULL if no callback needed + * (polling used instead). + * + * A callback can be optionally registered, and will be called upon the arrival + * of the request response from the uPower firmware, telling if it succeeded or + * not. + * A callback may not be registered (NULL pointer), in which case polling has + * to be used to check the response, by calling upwr_req_status or + * upwr_poll_req_status, using UPWR_SG_EXCEPT as the service group argument. + * + * At the callback the uPower/API is back to initialization/start-up phase, + * so service request calls return error. + * + * Context: no sleep, no locks taken/released. + * Return: 0 if ok, + * -1 if service group is busy, + * -3 if called in an invalid API state + */ +int upwr_xcp_shutdown(const upwr_callb callb) +{ + upwr_xcp_shutdown_msg txmsg = {0}; + + if (api_state != UPWR_API_READY) { + return -3; + } + + if (UPWR_SG_BUSY(UPWR_SG_EXCEPT)) { + return -1; + } + + UPWR_USR_CALLB(UPWR_SG_EXCEPT, callb); + + UPWR_MSG_HDR(txmsg.hdr, UPWR_SG_EXCEPT, UPWR_XCP_SHUTDOWN); + + upwr_srv_req(UPWR_SG_EXCEPT, (uint32_t *)&txmsg, sizeof(txmsg) / 4U); + + api_state = UPWR_API_SHUTDOWN_WAIT; + + return 0; +} + +/** + * upwr_xcp_i2c_access() - Performs an access through the uPower I2C interface. + * @addr: I2C slave address, up to 10 bits. + * @data_size: determines the access direction and data size in bytes, up to 4; + * negetive data_size determines a read access with size -data_size; + * positive data_size determines a write access with size data_size; + * data_size=0 is invalid, making the service return error UPWR_RESP_BAD_REQ. + * @subaddr_size: size of the sub-address in bytes, up to 4; if subaddr_size=0, + * no subaddress is used. + * @subaddr: sub-address, only used if subaddr_size > 0. + * @wdata: write data, up to 4 bytes; ignored if data_size < 0 (read) + * @callb: pointer to the callback to be called when the uPower has finished + * the access, or NULL if no callback needed + * (polling used instead). + * + * A callback can be optionally registered, and will be called upon the arrival + * of the request response from the uPower firmware, telling if it succeeded or + * not. + * A callback may not be registered (NULL pointer), in which case polling has + * to be used to check the response, by calling upwr_req_status or + * upwr_poll_req_status, using UPWR_SG_EXCEPT as the service group argument. + * + * The service performs a read (data_size < 0) or a write (data_size > 0) of + * up to 4 bytes on the uPower I2C interface. The data read from I2C comes via + * the callback argument ret, or written to the variable pointed by retptr, + * if polling is used (calls upwr_req_status or upwr_poll_req_status). + * ret (or *retptr) also returns the data written on writes. + * + * Sub-addressing is supported, with sub-address size determined by the argument + * subaddr_size, up to 4 bytes. Sub-addressing is not used if subaddr_size=0. + * + * Context: no sleep, no locks taken/released. + * Return: 0 if ok, + * -1 if service group is busy, + * -3 if called in an invalid API state + */ + +int upwr_xcp_i2c_access(uint16_t addr, + int8_t data_size, + uint8_t subaddr_size, + uint32_t subaddr, + uint32_t wdata, + const upwr_callb callb) +{ + unsigned long ptrval = (unsigned long)sh_buffer[UPWR_SG_EXCEPT]; + upwr_i2c_access *i2c_acc_ptr = (upwr_i2c_access *)ptrval; + upwr_pwm_pmiccfg_msg txmsg = {0}; + + if (api_state != UPWR_API_READY) { + return -3; + } + + if (UPWR_SG_BUSY(UPWR_SG_EXCEPT)) { + return -1; + } + + UPWR_USR_CALLB(UPWR_SG_EXCEPT, callb); + + UPWR_MSG_HDR(txmsg.hdr, UPWR_SG_EXCEPT, UPWR_XCP_I2C); + + i2c_acc_ptr->addr = addr; + i2c_acc_ptr->subaddr = subaddr; + i2c_acc_ptr->subaddr_size = subaddr_size; + i2c_acc_ptr->data = wdata; + i2c_acc_ptr->data_size = data_size; + + txmsg.ptr = upwr_ptr2offset(ptrval, + UPWR_SG_EXCEPT, + (size_t)sizeof(upwr_i2c_access), + 0U, + i2c_acc_ptr); + + upwr_srv_req(UPWR_SG_EXCEPT, (uint32_t *)&txmsg, sizeof(txmsg) / 4U); + + return 0; +} + +/**--------------------------------------------------------------- + * VOLTAGE MANAGERMENT SERVICE GROUP + */ + +/** + * upwr_vtm_pmic_cold_reset() -request cold reset the pmic. + * pmic will power cycle all the regulators + * @callb: response callback pointer; NULL if no callback needed. + * + * The function requests uPower to cold reset the pmic. + * The request is executed if arguments are within range, with no protections + * regarding the adequate voltage value for the given domain process, + * temperature and frequency. + * + * A callback can be optionally registered, and will be called upon the arrival + * of the request response from the uPower firmware, telling if it succeeded + * or not. + * + * A callback may not be registered (NULL pointer), in which case polling has + * to be used to check the response, by calling upwr_req_status or + * upwr_poll_req_status, using UPWR_SG_VOLTM as the service group argument. + * + * Context: no sleep, no locks taken/released. + * Return: 0 if ok, + * -1 if service group is busy, + * -3 if called in an invalid API state + * Note that this is not the error response from the request itself: + * it only tells if the request was successfully sent to the uPower. + */ +int upwr_vtm_pmic_cold_reset(upwr_callb callb) +{ + upwr_volt_pmic_cold_reset_msg txmsg = {0}; + + if (api_state != UPWR_API_READY) { + return -3; + } + + if (UPWR_SG_BUSY(UPWR_SG_VOLTM)) { + return -1; + } + + UPWR_USR_CALLB(UPWR_SG_VOLTM, callb); + + UPWR_MSG_HDR(txmsg.hdr, UPWR_SG_VOLTM, UPWR_VTM_PMIC_COLD_RESET); + + upwr_srv_req(UPWR_SG_VOLTM, (uint32_t *)&txmsg, sizeof(txmsg) / 4U); + + return 0; +} + +/** + * upwr_vtm_set_pmic_mode() -request uPower set pmic mode + * @pmic_mode: the target mode need to be set + * @callb: response callback pointer; NULL if no callback needed. + * + * The function requests uPower to set pmic mode + * The request is executed if arguments are within range, with no protections + * regarding the adequate voltage value for the given domain process, + * temperature and frequency. + * + * A callback can be optionally registered, and will be called upon the arrival + * of the request response from the uPower firmware, telling if it succeeded + * or not. + * + * A callback may not be registered (NULL pointer), in which case polling has + * to be used to check the response, by calling upwr_req_status or + * upwr_poll_req_status, using UPWR_SG_VOLTM as the service group argument. + * + * Context: no sleep, no locks taken/released. + * Return: 0 if ok, + * -1 if service group is busy, + * -3 if called in an invalid API state + * Note that this is not the error response from the request itself: + * it only tells if the request was successfully sent to the uPower. + */ +int upwr_vtm_set_pmic_mode(uint32_t pmic_mode, upwr_callb callb) +{ + upwr_volt_pmic_set_mode_msg txmsg = {0}; + + if (api_state != UPWR_API_READY) { + return -3; + } + + if (UPWR_SG_BUSY(UPWR_SG_VOLTM)) { + return -1; + } + + UPWR_USR_CALLB(UPWR_SG_VOLTM, callb); + + UPWR_MSG_HDR(txmsg.hdr, UPWR_SG_VOLTM, UPWR_VTM_SET_PMIC_MODE); + + txmsg.hdr.arg = pmic_mode; + + upwr_srv_req(UPWR_SG_VOLTM, (uint32_t *)&txmsg, sizeof(txmsg) / 4U); + + return 0; +} + +/** + * upwr_vtm_chng_pmic_voltage() - Changes the voltage of a given rail. + * @rail: pmic rail id. + * @volt: the target voltage of the given rail, accurate to uV + * If pass volt value 0, means that power off this rail. + * @callb: response callback pointer; NULL if no callback needed. + * + * The function requests uPower to change the voltage of the given rail. + * The request is executed if arguments are within range, with no protections + * regarding the adequate voltage value for the given domain process, + * temperature and frequency. + * + * A callback can be optionally registered, and will be called upon the arrival + * of the request response from the uPower firmware, telling if it succeeded + * or not. + * + * A callback may not be registered (NULL pointer), in which case polling has + * to be used to check the response, by calling upwr_req_status or + * upwr_poll_req_status, using UPWR_SG_VOLTM as the service group argument. + * + * Context: no sleep, no locks taken/released. + * Return: 0 if ok, + * -1 if service group is busy, + * -3 if called in an invalid API state + * Note that this is not the error response from the request itself: + * it only tells if the request was successfully sent to the uPower. + */ +int upwr_vtm_chng_pmic_voltage(uint32_t rail, uint32_t volt, upwr_callb callb) +{ + upwr_volt_pmic_set_volt_msg txmsg = {0}; + + if (api_state != UPWR_API_READY) { + return -3; + } + + if (UPWR_SG_BUSY(UPWR_SG_VOLTM)) { + return -1; + } + + UPWR_USR_CALLB(UPWR_SG_VOLTM, callb); + + UPWR_MSG_HDR(txmsg.hdr, UPWR_SG_VOLTM, UPWR_VTM_CHNG_PMIC_RAIL_VOLT); + + txmsg.args.rail = rail; + + txmsg.args.volt = (volt + PMIC_VOLTAGE_MIN_STEP - 1U) / PMIC_VOLTAGE_MIN_STEP; + + upwr_srv_req(UPWR_SG_VOLTM, (uint32_t *)&txmsg, sizeof(txmsg) / 4U); + + return 0; +} + +/** + * upwr_vtm_get_pmic_voltage() - Get the voltage of a given rail. + * @rail: pmic rail id. + * @callb: response callback pointer; NULL if no callback needed. + * (polling used instead) + * + * The function requests uPower to get the voltage of the given rail. + * The request is executed if arguments are within range, with no protections + * regarding the adequate voltage value for the given domain process, + * temperature and frequency. + * + * A callback can be optionally registered, and will be called upon the arrival + * of the request response from the uPower firmware, telling if it succeeded + * or not. + * + * A callback may not be registered (NULL pointer), in which case polling has + * to be used to check the response, by calling upwr_req_status or + * upwr_poll_req_status, using UPWR_SG_VOLTM as the service group argument. + * + * The voltage data read from uPower via + * the callback argument ret, or written to the variable pointed by retptr, + * if polling is used (calls upwr_req_status or upwr_poll_req_status). + * ret (or *retptr) also returns the data written on writes. + * + * Context: no sleep, no locks taken/released. + * Return: 0 if ok, + * -1 if service group is busy, + * -3 if called in an invalid API state + * Note that this is not the error response from the request itself: + * it only tells if the request was successfully sent to the uPower. + */ +int upwr_vtm_get_pmic_voltage(uint32_t rail, upwr_callb callb) +{ + upwr_volt_pmic_get_volt_msg txmsg = {0}; + + if (api_state != UPWR_API_READY) { + return -3; + } + + if (UPWR_SG_BUSY(UPWR_SG_VOLTM)) { + return -1; + } + + UPWR_USR_CALLB(UPWR_SG_VOLTM, callb); + + UPWR_MSG_HDR(txmsg.hdr, UPWR_SG_VOLTM, UPWR_VTM_GET_PMIC_RAIL_VOLT); + + txmsg.args.rail = rail; + + upwr_srv_req(UPWR_SG_VOLTM, (uint32_t *)&txmsg, sizeof(txmsg) / 4U); + + return 0; +} + +/** + * upwr_vtm_power_measure() - request uPower to measure power consumption + * @ssel: This field determines which power switches will have their currents + * sampled to be accounted for a + * current/power measurement. Support 0~7 + + * SSEL bit # Power Switch + * 0 M33 core complex/platform/peripherals + * 1 Fusion Core and Peripherals + * 2 A35[0] core complex + * 3 A35[1] core complex + * 4 3DGPU + * 5 HiFi4 + * 6 DDR Controller (PHY and PLL NOT included) + * 7 PXP, EPDC + * + * @callb: response callback pointer; NULL if no callback needed. + * (polling used instead) + * + * The function requests uPower to measure power consumption + * The request is executed if arguments are within range, with no protections + * regarding the adequate voltage value for the given domain process, + * temperature and frequency. + * + * A callback can be optionally registered, and will be called upon the arrival + * of the request response from the uPower firmware, telling if it succeeded + * or not. + * + * A callback may not be registered (NULL pointer), in which case polling has + * to be used to check the response, by calling upwr_req_status or + * upwr_poll_req_status, using UPWR_SG_VOLTM as the service group argument. + * + * The power consumption data read from uPower via + * the callback argument ret, or written to the variable pointed by retptr, + * if polling is used (calls upwr_req_status or upwr_poll_req_status). + * ret (or *retptr) also returns the data written on writes. + * upower fw needs support cocurrent request from M33 and A35. + * + * Accurate to uA + * + * Context: no sleep, no locks taken/released. + * Return: 0 if ok, + * -1 if service group is busy, + * -3 if called in an invalid API state + * Note that this is not the error response from the request itself: + * it only tells if the request was successfully sent to the uPower. + */ +int upwr_vtm_power_measure(uint32_t ssel, upwr_callb callb) +{ + upwr_volt_pmeter_meas_msg txmsg = {0}; + + if (api_state != UPWR_API_READY) { + return -3; + } + + if (UPWR_SG_BUSY(UPWR_SG_VOLTM)) { + return -1; + } + + UPWR_USR_CALLB(UPWR_SG_VOLTM, callb); + + UPWR_MSG_HDR(txmsg.hdr, UPWR_SG_VOLTM, UPWR_VTM_PMETER_MEAS); + + txmsg.hdr.arg = ssel; + + upwr_srv_req(UPWR_SG_VOLTM, (uint32_t *)&txmsg, sizeof(txmsg) / 4U); + + return 0; +} + +/** + * upwr_vtm_vmeter_measure() - request uPower to measure voltage + * @vdetsel: Voltage Detector Selector, support 0~3 + * 00b - RTD sense point + 01b - LDO output + 10b - APD domain sense point + 11b - AVD domain sense point + Refer to upower_defs.h + * @callb: response callback pointer; NULL if no callback needed. + * (polling used instead) + * + * The function requests uPower to use vmeter to measure voltage + * The request is executed if arguments are within range, with no protections + * regarding the adequate voltage value for the given domain process, + * temperature and frequency. + * + * A callback can be optionally registered, and will be called upon the arrival + * of the request response from the uPower firmware, telling if it succeeded + * or not. + * + * A callback may not be registered (NULL pointer), in which case polling has + * to be used to check the response, by calling upwr_req_status or + * upwr_poll_req_status, using UPWR_SG_VOLTM as the service group argument. + * + * The voltage data read from uPower via + * the callback argument ret, or written to the variable pointed by retptr, + * if polling is used (calls upwr_req_status or upwr_poll_req_status). + * ret (or *retptr) also returns the data written on writes. + * upower fw needs support cocurrent request from M33 and A35. + * + * Refer to RM COREREGVL (Core Regulator Voltage Level) + * uPower return VDETLVL to user, user can calculate the real voltage: + * +0b000000(0x00) - 0.595833V +0b100110(0x26) - 1.007498V +<value> - 0.595833V + <value>x10.8333mV +0b110010(0x32) - 1.138V + * + * Context: no sleep, no locks taken/released. + * Return: 0 if ok, + * -1 if service group is busy, + * -3 if called in an invalid API state + * Note that this is not the error response from the request itself: + * it only tells if the request was successfully sent to the uPower. + */ +int upwr_vtm_vmeter_measure(uint32_t vdetsel, upwr_callb callb) +{ + upwr_volt_vmeter_meas_msg txmsg = {0}; + + if (api_state != UPWR_API_READY) { + return -3; + } + + if (UPWR_SG_BUSY(UPWR_SG_VOLTM)) { + return -1; + } + + UPWR_USR_CALLB(UPWR_SG_VOLTM, callb); + + UPWR_MSG_HDR(txmsg.hdr, UPWR_SG_VOLTM, UPWR_VTM_VMETER_MEAS); + + txmsg.hdr.arg = vdetsel; + + upwr_srv_req(UPWR_SG_VOLTM, (uint32_t *)&txmsg, sizeof(txmsg) / 4U); + + return 0; +} + +/** + * upwr_vtm_pmic_config() - Configures the SoC PMIC (Power Management IC). + * @config: pointer to a PMIC-dependent struct defining the PMIC configuration. + * @size: size of the struct pointed by config, in bytes. + * @callb: pointer to the callback called when configurations are applied. + * NULL if no callback is required. + * + * The function requests uPower to change/define the PMIC configuration. + * + * A callback can be optionally registered, and will be called upon the arrival + * of the request response from the uPower firmware, telling if it succeeded + * or not. + * + * A callback may not be registered (NULL pointer), in which case polling has + * to be used to check the response, by calling upwr_req_status or + * upwr_poll_req_status, using UPWR_SG_PWRMGMT as the service group argument. + * + * Context: no sleep, no locks taken/released. + * Return: 0 if ok, -1 if service group is busy, + * -2 if the pointer conversion to physical address failed, + * -3 if called in an invalid API state. + * Note that this is not the error response from the request itself: + * it only tells if the request was successfully sent to the uPower. + */ +int upwr_vtm_pmic_config(const void *config, uint32_t size, upwr_callb callb) +{ + upwr_pwm_pmiccfg_msg txmsg = {0}; + unsigned long ptrval = 0UL; /* needed for X86, ARM64 */ + + if (api_state != UPWR_API_READY) { + return -3; + } + + if (UPWR_SG_BUSY(UPWR_SG_VOLTM)) { + return -1; + } + + UPWR_USR_CALLB(UPWR_SG_VOLTM, callb); + + UPWR_MSG_HDR(txmsg.hdr, UPWR_SG_VOLTM, UPWR_VTM_PMIC_CONFIG); + + ptrval = (unsigned long)os_ptr2phy(config); + if (ptrval == 0UL) { + return -2; /* pointer conversion failed */ + } + + txmsg.ptr = upwr_ptr2offset(ptrval, + UPWR_SG_VOLTM, + (size_t)size, + 0U, + config); + + upwr_srv_req(UPWR_SG_VOLTM, (uint32_t *)&txmsg, sizeof(txmsg) / 4U); + + return 0; +} + +/**--------------------------------------------------------------- + * TEMPERATURE MANAGEMENT SERVICE GROUP + */ + +/** + * upwr_tpm_get_temperature() - request uPower to get temperature of one temperature sensor + * @sensor_id: temperature sensor ID, support 0~2 + * @callb: response callback pointer; NULL if no callback needed. + * (polling used instead) + * + * The function requests uPower to measure temperature + * The request is executed if arguments are within range, with no protections + * regarding the adequate voltage value for the given domain process, + * temperature and frequency. + * + * A callback can be optionally registered, and will be called upon the arrival + * of the request response from the uPower firmware, telling if it succeeded + * or not. + * + * A callback may not be registered (NULL pointer), in which case polling has + * to be used to check the response, by calling upwr_req_status or + * upwr_poll_req_status, using UPWR_SG_TEMPM as the service group argument. + * + * The temperature data read from uPower via + * the callback argument ret, or written to the variable pointed by retptr, + * if polling is used (calls upwr_req_status or upwr_poll_req_status). + * ret (or *retptr) also returns the data written on writes. + * + * uPower return TSEL to the caller (M33 or A35), caller calculate the real temperature + * Tsh = 0.000002673049*TSEL[7:0]^3 + 0.0003734262*TSEL[7:0]^2 + +0.4487042*TSEL[7:0] - 46.98694 + * + * upower fw needs support cocurrent request from M33 and A35. + * + * Context: no sleep, no locks taken/released. + * Return: 0 if ok, + * -1 if service group is busy, + * -3 if called in an invalid API state + * Note that this is not the error response from the request itself: + * it only tells if the request was successfully sent to the uPower. + */ +int upwr_tpm_get_temperature(uint32_t sensor_id, upwr_callb callb) +{ + upwr_temp_get_cur_temp_msg txmsg = {0}; + + if (api_state != UPWR_API_READY) { + return -3; + } + + if (UPWR_SG_BUSY(UPWR_SG_TEMPM)) { + return -1; + } + + UPWR_USR_CALLB(UPWR_SG_TEMPM, callb); + + UPWR_MSG_HDR(txmsg.hdr, UPWR_SG_TEMPM, UPWR_TEMP_GET_CUR_TEMP); + + txmsg.args.sensor_id = sensor_id; + + upwr_srv_req(UPWR_SG_TEMPM, (uint32_t *)&txmsg, sizeof(txmsg) / 4U); + + return 0; +} + +/**--------------------------------------------------------------- + * DELAY MANAGEMENT SERVICE GROUP + */ + +/** + * upwr_dlm_get_delay_margin() - request uPower to get delay margin + * @path: The critical path + * @index: Use whitch delay meter + * @callb: response callback pointer; NULL if no callback needed. + * (polling used instead) + * + * The function requests uPower to get delay margin + * The request is executed if arguments are within range, with no protections + * regarding the adequate voltage value for the given domain process, + * temperature and frequency. + * + * A callback can be optionally registered, and will be called upon the arrival + * of the request response from the uPower firmware, telling if it succeeded + * or not. + * + * A callback may not be registered (NULL pointer), in which case polling has + * to be used to check the response, by calling upwr_req_status or + * upwr_poll_req_status, using UPWR_SG_DELAYM as the service group argument. + * + * The delay margin data read from uPower via + * the callback argument ret, or written to the variable pointed by retptr, + * if polling is used (calls upwr_req_status or upwr_poll_req_status). + * ret (or *retptr) also returns the data written on writes. + * upower fw needs support cocurrent request from M33 and A35. + * + * Context: no sleep, no locks taken/released. + * Return: 0 if ok, + * -1 if service group is busy, + * -3 if called in an invalid API state + * Note that this is not the error response from the request itself: + * it only tells if the request was successfully sent to the uPower. + */ +int upwr_dlm_get_delay_margin(uint32_t path, uint32_t index, upwr_callb callb) +{ + upwr_dmeter_get_delay_margin_msg txmsg = {0}; + + if (api_state != UPWR_API_READY) { + return -3; + } + + if (UPWR_SG_BUSY(UPWR_SG_DELAYM)) { + return -1; + } + + UPWR_USR_CALLB(UPWR_SG_DELAYM, callb); + + UPWR_MSG_HDR(txmsg.hdr, UPWR_SG_DELAYM, UPWR_DMETER_GET_DELAY_MARGIN); + + txmsg.args.path = path; + txmsg.args.index = index; + + upwr_srv_req(UPWR_SG_DELAYM, (uint32_t *)&txmsg, sizeof(txmsg) / 4U); + + return 0; +} + +/** + * upwr_dlm_set_delay_margin() - request uPower to set delay margin + * @path: The critical path + * @index: Use whitch delay meter + * @delay_margin: the value of delay margin + * @callb: response callback pointer; NULL if no callback needed. + * (polling used instead) + * + * The function requests uPower to set delay margin + * The request is executed if arguments are within range, with no protections + * regarding the adequate voltage value for the given domain process, + * temperature and frequency. + * + * A callback can be optionally registered, and will be called upon the arrival + * of the request response from the uPower firmware, telling if it succeeded + * or not. + * + * A callback may not be registered (NULL pointer), in which case polling has + * to be used to check the response, by calling upwr_req_status or + * upwr_poll_req_status, using UPWR_SG_DELAYM as the service group argument. + * + * The result of the corresponding critical path, failed or not read from uPower via + * the callback argument ret, or written to the variable pointed by retptr, + * if polling is used (calls upwr_req_status or upwr_poll_req_status). + * ret (or *retptr) also returns the data written on writes. + * upower fw needs support cocurrent request from M33 and A35. + * + * Context: no sleep, no locks taken/released. + * Return: 0 if ok, + * -1 if service group is busy, + * -3 if called in an invalid API state + * Note that this is not the error response from the request itself: + * it only tells if the request was successfully sent to the uPower. + */ +int upwr_dlm_set_delay_margin(uint32_t path, uint32_t index, uint32_t delay_margin, + upwr_callb callb) +{ + upwr_dmeter_set_delay_margin_msg txmsg = {0}; + + if (api_state != UPWR_API_READY) { + return -3; + } + + if (UPWR_SG_BUSY(UPWR_SG_DELAYM)) { + return -1; + } + + UPWR_USR_CALLB(UPWR_SG_DELAYM, callb); + + UPWR_MSG_HDR(txmsg.hdr, UPWR_SG_DELAYM, UPWR_DMETER_SET_DELAY_MARGIN); + + txmsg.args.path = path; + txmsg.args.index = index; + txmsg.args.dm = delay_margin; + + upwr_srv_req(UPWR_SG_DELAYM, (uint32_t *)&txmsg, sizeof(txmsg) / 4U); + + return 0; +} + +/** + * upwr_dlm_process_monitor() - request uPower to do process monitor + * @chain_sel: Chain Cell Type Selection + * Select the chain to be used for the clock signal generation. + * Support two types chain cell, 0~1 +0b - P4 type delay cells selected +1b - P16 type delay cells selected + * @callb: response callback pointer; NULL if no callback needed. + * (polling used instead) + * + * The function requests uPower to do process monitor + * The request is executed if arguments are within range, with no protections + * regarding the adequate voltage value for the given domain process, + * temperature and frequency. + * + * A callback can be optionally registered, and will be called upon the arrival + * of the request response from the uPower firmware, telling if it succeeded + * or not. + * + * A callback may not be registered (NULL pointer), in which case polling has + * to be used to check the response, by calling upwr_req_status or + * upwr_poll_req_status, using UPWR_SG_DELAYM as the service group argument. + * + * The result of process monitor, failed or not read from uPower via + * the callback argument ret, or written to the variable pointed by retptr, + * if polling is used (calls upwr_req_status or upwr_poll_req_status). + * ret (or *retptr) also returns the data written on writes. + * upower fw needs support cocurrent request from M33 and A35. + * + * Context: no sleep, no locks taken/released. + * Return: 0 if ok, + * -1 if service group is busy, + * -3 if called in an invalid API state + * Note that this is not the error response from the request itself: + * it only tells if the request was successfully sent to the uPower. + */ +int upwr_dlm_process_monitor(uint32_t chain_sel, upwr_callb callb) +{ + upwr_pmon_msg txmsg = {0}; + + if (api_state != UPWR_API_READY) { + return -3; + } + + if (UPWR_SG_BUSY(UPWR_SG_DELAYM)) { + return -1; + } + + UPWR_USR_CALLB(UPWR_SG_DELAYM, callb); + + UPWR_MSG_HDR(txmsg.hdr, UPWR_SG_DELAYM, UPWR_PMON_REQ); + + txmsg.args.chain_sel = chain_sel; + + upwr_srv_req(UPWR_SG_DELAYM, (uint32_t *)&txmsg, sizeof(txmsg) / 4U); + + return 0; +} + +/**--------------------------------------------------------------- + * POWER MANAGEMENT SERVICE GROUP + */ + +/** + * upwr_pwm_dom_power_on() - Commands uPower to power on the platform of other + * domain (not necessarily its core(s)); does not release the core reset. + * @domain: identifier of the domain to power on. Defined by SoC-dependent type + * soc_domain_t found in upower_soc_defs.h. + * @boot_start: must be 1 to start the domain core(s) boot(s), releasing + * its (their) resets, or 0 otherwise. + * @pwroncallb: pointer to the callback to be called when the uPower has + * finished the power on procedure, or NULL if no callback needed + * (polling used instead). + * + * A callback can be optionally registered, and will be called upon the arrival + * of the request response from the uPower firmware, telling if it succeeded or + * not. + * A callback may not be registered (NULL pointer), in which case polling has + * to be used to check the response, by calling upwr_req_status or + * upwr_poll_req_status, using UPWR_SG_PWRMGMT as the service group argument. + * + * Context: no sleep, no locks taken/released. + * Return: 0 if ok, + * -1 if service group is busy, + * -2 if the domain passed is the same as the caller, + * -3 if called in an invalid API state + */ +int upwr_pwm_dom_power_on(soc_domain_t domain, + int boot_start, + const upwr_callb pwroncallb) +{ + upwr_pwm_dom_pwron_msg txmsg = {0}; + + if (pwr_domain == domain) { + return -2; + } + + if (api_state != UPWR_API_READY) { + return -3; + } + + if (UPWR_SG_BUSY(UPWR_SG_PWRMGMT)) { + return -1; + } + + UPWR_USR_CALLB(UPWR_SG_PWRMGMT, (upwr_callb)pwroncallb); + + UPWR_MSG_HDR(txmsg.hdr, UPWR_SG_PWRMGMT, UPWR_PWM_DOM_PWRON); + txmsg.hdr.domain = (uint32_t)domain; + txmsg.hdr.arg = (uint32_t)boot_start; + + upwr_srv_req(UPWR_SG_PWRMGMT, (uint32_t *)&txmsg, sizeof(txmsg) / 4U); + + return 0; +} + +/** + * upwr_pwm_boot_start() - Commands uPower to release the reset of other CPU(s), + * starting their boots. + * @domain: identifier of the domain to release the reset. Defined by + * SoC-dependent type soc_domain_t found in upower_soc_defs.h. + * @bootcallb: pointer to the callback to be called when the uPower has finished + * the boot start procedure, or NULL if no callback needed + * (polling used instead). + * + * A callback can be optionally registered, and will be called upon the arrival + * of the request response from the uPower firmware, telling if it succeeded or + * not. + * A callback may not be registered (NULL pointer), in which case polling has + * to be used to check the response, by calling upwr_req_status or + * upwr_poll_req_status, using UPWR_SG_PWRMGMT as the service group argument. + * + * The callback calling doesn't mean the CPUs boots have finished: + * it only indicates that uPower released the CPUs resets, and can receive + * other power management service group requests. + * + * Context: no sleep, no locks taken/released. + * Return: 0 if ok, + * -1 if service group is busy, + * -2 if the domain passed is the same as the caller, + * -3 if called in an invalid API state + */ +int upwr_pwm_boot_start(soc_domain_t domain, const upwr_callb bootcallb) +{ + upwr_pwm_boot_start_msg txmsg = {0}; + + if (pwr_domain == domain) { + return -2; + } + + if (api_state != UPWR_API_READY) { + return -3; + } + + if (UPWR_SG_BUSY(UPWR_SG_PWRMGMT)) { + return -1; + } + + UPWR_USR_CALLB(UPWR_SG_PWRMGMT, (upwr_callb)bootcallb); + + UPWR_MSG_HDR(txmsg.hdr, UPWR_SG_PWRMGMT, UPWR_PWM_BOOT); + txmsg.hdr.domain = (uint32_t)domain; + + upwr_srv_req(UPWR_SG_PWRMGMT, (uint32_t *)&txmsg, sizeof(txmsg) / 4U); + + return 0; +} + +/** + * upwr_pwm_param() - Changes Power Management parameters. + * @param: pointer to a parameter structure upwr_pwm_param_t, SoC-dependent, + * defined in upwr_soc_defines.h. NULL may be passed, meaning + * a request to read the parameter set, in which case it appears in the callback + * argument ret, or can be pointed by argument retptr in the upwr_req_status and + * upwr_poll_req_status calls, casted to upwr_pwm_param_t. + * @callb: response callback pointer; NULL if no callback needed. + * + * The return value is always the current parameter set value, either in a + * read-only request (param = NULL) or after setting a new parameter + * (non-NULL param). + * + * Some parameters may be targeted for a specific domain (see the struct + * upwr_pwm_param_t definition in upower_soc_defs.h); this call has implicit + * domain target (the same domain from which is called). + * + * A callback can be optionally registered, and will be called upon the arrival + * of the request response from the uPower firmware, telling if it succeeded or + * not. + * A callback may not be registered (NULL pointer), in which case polling has + * to be used to check the response, by calling upwr_req_status or + * upwr_poll_req_status, using UPWR_SG_PWRMGMT as the service group argument. + * + * Context: no sleep, no locks taken/released. + * Return: 0 if ok, + * -1 if service group is busy, + * -3 if called in an invalid API state + */ +int upwr_pwm_param(upwr_pwm_param_t *param, const upwr_callb callb) +{ + upwr_pwm_param_msg txmsg = {0}; + + if (api_state != UPWR_API_READY) { + return -3; + } + + if (UPWR_SG_BUSY(UPWR_SG_PWRMGMT)) { + return -1; + } + + UPWR_USR_CALLB(UPWR_SG_PWRMGMT, callb); + + UPWR_MSG_HDR(txmsg.hdr, UPWR_SG_PWRMGMT, UPWR_PWM_PARAM); + + if (param == NULL) { + txmsg.hdr.arg = 1U; /* 1= read, txmsg.word2 ignored */ + } else { + txmsg.hdr.arg = 0U; /* 1= write */ + txmsg.word2 = param->R; /* just 1 word, so that's ok */ + } + + upwr_srv_req(UPWR_SG_PWRMGMT, (uint32_t *)&txmsg, sizeof(txmsg) / 4U); + + return 0; +} + +/** + * upwr_pwm_chng_reg_voltage() - Changes the voltage at a given regulator. + * @reg: regulator id. + * @volt: voltage value; value unit is SoC-dependent, converted from mV by the + * macro UPWR_VTM_MILIV, or from micro-Volts by the macro UPWR_VTM_MICROV, + * both macros in upower_soc_defs.h + * @callb: response callback pointer; NULL if no callback needed. + * + * The function requests uPower to change the voltage of the given regulator. + * The request is executed if arguments are within range, with no protections + * regarding the adequate voltage value for the given domain process, + * temperature and frequency. + * + * A callback can be optionally registered, and will be called upon the arrival + * of the request response from the uPower firmware, telling if it succeeded + * or not. + * + * A callback may not be registered (NULL pointer), in which case polling has + * to be used to check the response, by calling upwr_req_status or + * upwr_poll_req_status, using UPWR_SG_PWRMGMT as the service group argument. + * + * Context: no sleep, no locks taken/released. + * Return: 0 if ok, + * -1 if service group is busy, + * -3 if called in an invalid API state + * Note that this is not the error response from the request itself: + * it only tells if the request was successfully sent to the uPower. + */ +int upwr_pwm_chng_reg_voltage(uint32_t reg, uint32_t volt, upwr_callb callb) +{ + upwr_pwm_volt_msg txmsg = {0}; + + if (api_state != UPWR_API_READY) { + return -3; + } + + if (UPWR_SG_BUSY(UPWR_SG_PWRMGMT)) { + return -1; + } + + UPWR_USR_CALLB(UPWR_SG_PWRMGMT, callb); + + UPWR_MSG_HDR(txmsg.hdr, UPWR_SG_PWRMGMT, UPWR_PWM_VOLT); + + txmsg.args.reg = reg; + txmsg.args.volt = volt; + + upwr_srv_req(UPWR_SG_PWRMGMT, (uint32_t *)&txmsg, sizeof(txmsg) / 4U); + + return 0; +} + +/** + * upwr_pwm_freq_setup() - Determines the next frequency target for a given + * domain and current frequency. + * @domain: identifier of the domain to change frequency. Defined by + * SoC-dependent type soc_domain_t found in upower_soc_defs.h. + * @rail: the pmic regulator number for the target domain. + * @stage: DVA adjust stage + * refer to upower_defs.h "DVA adjust stage" + * @target_freq: the target adjust frequency, accurate to MHz + * + * refer to upower_defs.h structure definition upwr_pwm_freq_msg + * + * @callb: response callback pointer; NULL if no callback needed. + * + * The DVA algorithm is broken down into two phases. + * The first phase uses a look up table to get a safe operating voltage + * for the requested frequency. + * This voltage is guaranteed to work over process and temperature. + * + * The second step of the second phase is to measure the temperature + * using the uPower Temperature Sensor module. + * This is accomplished by doing a binary search of the TSEL bit field + * in the Temperature Measurement Register (TMR). + * The search is repeated until the THIGH bit fields in the same register change value. + * There are 3 temperature sensors in 8ULP (APD, AVD, and RTD). + * + * + * The second phase is the fine adjust of the voltage. + * This stage is entered only when the new frequency requested + * by application was already set as well as the voltage for that frequency. + * The first step of the fine adjust is to find what is the current margins + * for the monitored critical paths, or, in other words, + * how many delay cells will be necessary to generate a setup-timing violation. + * The function informs uPower that the given domain frequency has changed or + * will change to the given value. uPower firmware will then adjust voltage and + * bias to cope with the new frequency (if decreasing) or prepare for it + * (if increasing). The function must be called after decreasing the frequency, + * and before increasing it. The actual increase in frequency must not occur + * before the service returns its response. + * + * So, for increase clock frequency case, user need to call this API twice, + * the first stage gross adjust and the second stage fine adjust. + * + * for reduce clock frequency case, user can only call this API once, + * full stage (combine gross stage and fine adjust) + * + * The request is executed if arguments are within range. + * + * A callback can be optionally registered, and will be called upon the arrival + * of the request response from the uPower firmware, telling if it succeeded + * or not. + * + * A callback may not be registered (NULL pointer), in which case polling has + * to be used to check the response, by calling upwr_req_status or + * upwr_poll_req_status, using UPWR_SG_PWRMGMT as the service group argument. + * + * Context: no sleep, no locks taken/released. + * Return: 0 if ok, + * -1 if service group is busy, + * -3 if called in an invalid API state + * Note that this is not the error response from the request itself: + * it only tells if the request was successfully sent to the uPower. + */ +int upwr_pwm_freq_setup(soc_domain_t domain, uint32_t rail, uint32_t stage, uint32_t target_freq, + upwr_callb callb) +{ + upwr_pwm_freq_msg txmsg = {0}; + + if (api_state != UPWR_API_READY) { + return -3; + } + + if (UPWR_SG_BUSY(UPWR_SG_PWRMGMT)) { + return -1; + } + + UPWR_USR_CALLB(UPWR_SG_PWRMGMT, callb); + + UPWR_MSG_HDR(txmsg.hdr, UPWR_SG_PWRMGMT, UPWR_PWM_FREQ); + + txmsg.hdr.domain = (uint32_t)domain; + txmsg.args.rail = rail; + txmsg.args.stage = stage; + txmsg.args.target_freq = target_freq; + + upwr_srv_req(UPWR_SG_PWRMGMT, (uint32_t *)&txmsg, sizeof(txmsg) / 4U); + + return 0; +} + +/** + * upwr_pwm_power_on()- Powers on (not off) one or more switches and ROM/RAMs. + * @swton: pointer to an array of words that tells which power switches to + * turn on. Each word in the array has 1 bit for each switch. + * A bit=1 means the respective switch must be turned on, + * bit = 0 means it will stay unchanged (on or off). + * The pointer may be set to NULL, in which case no switch will be changed, + * unless a memory that it feeds must be turned on. + * WARNING: swton must not point to the first shared memory address. + * @memon: pointer to an array of words that tells which memories to turn on. + * Each word in the array has 1 bit for each switch. + * A bit=1 means the respective memory must be turned on, both array and + * periphery logic; + * bit = 0 means it will stay unchanged (on or off). + * The pointer may be set to NULL, in which case no memory will be changed. + * WARNING: memon must not point to the first shared memory address. + * @callb: pointer to the callback called when configurations are applyed. + * NULL if no callback is required. + * + * The function requests uPower to turn on the PMC and memory array/peripheral + * switches that control their power, as specified above. + * The request is executed if arguments are within range, with no protections + * regarding the adequate memory power state related to overall system state. + * + * If a memory is requested to turn on, but the power switch that feeds that + * memory is not, the power switch will be turned on anyway, if the pwron + * array is not provided (that is, if pwron is NULL). + * + * A callback can be optionally registered, and will be called upon the arrival + * of the request response from the uPower firmware, telling if it succeeded + * or not. + * + * A callback may not be registered (NULL pointer), in which case polling has + * to be used to check the response, by calling upwr_req_status or + * upwr_poll_req_status, using UPWR_SG_PWRMGMT as the service group argument. + * + * Callback or polling may return error if the service contends for a resource + * already being used by a power mode transition or an ongoing service in + * another domain. + * + * Context: no sleep, no locks taken/released. + * Return: 0 if ok, -1 if service group is busy, + * -2 if a pointer conversion to physical address failed, + * -3 if called in an invalid API state. + * Note that this is not the error response from the request itself: + * it only tells if the request was successfully sent to the uPower. + */ + +int upwr_pwm_power_on(const uint32_t swton[], + const uint32_t memon[], + upwr_callb callb) +{ + upwr_pwm_pwron_msg txmsg = {0}; + unsigned long ptrval = 0UL; /* needed for X86, ARM64 */ + size_t stsize = 0U; + + if (api_state != UPWR_API_READY) { + return -3; + } + + if (UPWR_SG_BUSY(UPWR_SG_PWRMGMT)) { + return -1; + } + + UPWR_USR_CALLB(UPWR_SG_PWRMGMT, callb); + + UPWR_MSG_HDR(txmsg.hdr, UPWR_SG_PWRMGMT, UPWR_PWM_PWR_ON); + + ptrval = (unsigned long)os_ptr2phy((void *)swton); + if (swton == NULL) { + txmsg.ptrs.ptr0 = 0; /* NULL pointer -> 0 offset */ + } else if (ptrval == 0U) { + return -2; /* pointer conversion failed */ + } else { + txmsg.ptrs.ptr0 = upwr_ptr2offset(ptrval, + UPWR_SG_PWRMGMT, + (stsize = UPWR_PMC_SWT_WORDS * 4U), + 0U, + swton); + } + + ptrval = (unsigned long)os_ptr2phy((void *)memon); + if (memon == NULL) { + txmsg.ptrs.ptr1 = 0; /* NULL pointer -> 0 offset */ + + } else if (ptrval == 0U) { + return -2; /* pointer conversion failed */ + } else { + txmsg.ptrs.ptr1 = upwr_ptr2offset(ptrval, + UPWR_SG_PWRMGMT, + UPWR_PMC_MEM_WORDS * 4U, + stsize, + memon); + } + + upwr_srv_req(UPWR_SG_PWRMGMT, (uint32_t *)&txmsg, sizeof(txmsg) / 4U); + + return 0; +} + +/** + * upwr_pwm_power_off()- Powers off (not on) one or more switches and ROM/RAMs. + * @swtoff: pointer to an array of words that tells which power switches to + * turn off. Each word in the array has 1 bit for each switch. + * A bit=1 means the respective switch must be turned off, + * bit = 0 means it will stay unchanged (on or off). + * The pointer may be set to NULL, in which case no switch will be changed. + * WARNING: swtoff must not point to the first shared memory address. + * @memoff: pointer to an array of words that tells which memories to turn off. + * Each word in the array has 1 bit for each switch. + * A bit=1 means the respective memory must be turned off, both array and + * periphery logic; + * bit = 0 means it will stay unchanged (on or off). + * The pointer may be set to NULL, in which case no memory will be changed, + * but notice it may be turned off if the switch that feeds it is powered off. + * WARNING: memoff must not point to the first shared memory address. + * @callb: pointer to the callback called when configurations are applyed. + * NULL if no callback is required. + * + * The function requests uPower to turn off the PMC and memory array/peripheral + * switches that control their power, as specified above. + * The request is executed if arguments are within range, with no protections + * regarding the adequate memory power state related to overall system state. + * + * A callback can be optionally registered, and will be called upon the arrival + * of the request response from the uPower firmware, telling if it succeeded + * or not. + * + * A callback may not be registered (NULL pointer), in which case polling has + * to be used to check the response, by calling upwr_req_status or + * upwr_poll_req_status, using UPWR_SG_PWRMGMT as the service group argument. + * + * Callback or polling may return error if the service contends for a resource + * already being used by a power mode transition or an ongoing service in + * another domain. + * + * Context: no sleep, no locks taken/released. + * Return: 0 if ok, -1 if service group is busy, + * -2 if a pointer conversion to physical address failed, + * -3 if called in an invalid API state. + * Note that this is not the error response from the request itself: + * it only tells if the request was successfully sent to the uPower. + */ +int upwr_pwm_power_off(const uint32_t swtoff[], + const uint32_t memoff[], + upwr_callb callb) +{ + upwr_pwm_pwroff_msg txmsg = {0}; + unsigned long ptrval = 0UL; /* needed for X86, ARM64 */ + size_t stsize = 0; + + if (api_state != UPWR_API_READY) { + return -3; + } + + if (UPWR_SG_BUSY(UPWR_SG_PWRMGMT)) { + return -1; + } + + UPWR_USR_CALLB(UPWR_SG_PWRMGMT, callb); + + UPWR_MSG_HDR(txmsg.hdr, UPWR_SG_PWRMGMT, UPWR_PWM_PWR_OFF); + + ptrval = (unsigned long)os_ptr2phy((void *)swtoff); + if (swtoff == NULL) { + txmsg.ptrs.ptr0 = 0; /* NULL pointer -> 0 offset */ + } else if (ptrval == 0U) { + return -2; /* pointer conversion failed */ + } else { + txmsg.ptrs.ptr0 = upwr_ptr2offset(ptrval, + UPWR_SG_PWRMGMT, + (stsize = UPWR_PMC_SWT_WORDS * 4U), + 0U, + swtoff); + } + + ptrval = (unsigned long)os_ptr2phy((void *)memoff); + if (memoff == NULL) { + txmsg.ptrs.ptr1 = 0; /* NULL pointer -> 0 offset */ + } else if (ptrval == 0U) { + return -2; /* pointer conversion failed */ + } else { + txmsg.ptrs.ptr1 = upwr_ptr2offset(ptrval, + UPWR_SG_PWRMGMT, + UPWR_PMC_MEM_WORDS * 4U, + stsize, + memoff); + } + + upwr_srv_req(UPWR_SG_PWRMGMT, (uint32_t *)&txmsg, sizeof(txmsg) / 4U); + + return 0; +} + +/** + * upwr_pwm_mem_retain()- Configures one or more memory power switches to + * retain its contents, having the power array on, while its peripheral logic + * is turned off. + * @mem: pointer to an array of words that tells which memories to put in a + * retention state. Each word in the array has 1 bit for each memory. + * A bit=1 means the respective memory must be put in retention state, + * bit = 0 means it will stay unchanged (retention, fully on or off). + * @callb: pointer to the callback called when configurations are applyed. + * NULL if no callback is required. + * + * The function requests uPower to turn off the memory peripheral and leave + * its array on, as specified above. + * The request is executed if arguments are within range. + * + * A callback can be optionally registered, and will be called upon the arrival + * of the request response from the uPower firmware, telling if it succeeded + * or not. + * + * A callback may not be registered (NULL pointer), in which case polling has + * to be used to check the response, by calling upwr_req_status or + * upwr_poll_req_status, using UPWR_SG_PWRMGMT as the service group argument. + * + * Callback or polling may return error if the service contends for a resource + * already being used by a power mode transition or an ongoing service in + * another domain. + * + * Context: no sleep, no locks taken/released. + * Return: 0 if ok, -1 if service group is busy, + * -2 if a pointer conversion to physical address failed, + * -3 if called in an invalid API state. + * Note that this is not the error response from the request itself: + * it only tells if the request was successfully sent to the uPower. + */ +int upwr_pwm_mem_retain(const uint32_t mem[], upwr_callb callb) +{ + upwr_pwm_retain_msg txmsg = {0}; + unsigned long ptrval = 0UL; /* needed for X86, ARM64 */ + + if (api_state != UPWR_API_READY) { + return -3; + } + + if (UPWR_SG_BUSY(UPWR_SG_PWRMGMT)) { + return -1; + } + + UPWR_USR_CALLB(UPWR_SG_PWRMGMT, callb); + + UPWR_MSG_HDR(txmsg.hdr, UPWR_SG_PWRMGMT, UPWR_PWM_RETAIN); + + ptrval = (unsigned long)os_ptr2phy((void *)mem); + if (ptrval == 0U) { + return -2; /* pointer conversion failed */ + } + + txmsg.ptr = upwr_ptr2offset(ptrval, + UPWR_SG_PWRMGMT, + UPWR_PMC_MEM_WORDS * 4U, + 0U, + mem); + + upwr_srv_req(UPWR_SG_PWRMGMT, (uint32_t *)&txmsg, sizeof(txmsg) / 4U); + + return 0; +} + +/** + * upwr_pwm_chng_switch_mem() - Turns on/off power on one or more PMC switches + * and memories, including their array and peripheral logic. + * @swt: pointer to a list of PMC switches to be opened/closed. + * The list is structured as an array of struct upwr_switch_board_t + * (see upower_defs.h), each one containing a word for up to 32 switches, + * one per bit. A bit = 1 means switch closed, bit = 0 means switch open. + * struct upwr_switch_board_t also specifies a mask with 1 bit for each + * respective switch: mask bit = 1 means the open/close action is applied, + * mask bit = 0 means the switch stays unchanged. + * The pointer may be set to NULL, in which case no switch will be changed, + * unless a memory that it feeds must be turned on. + * WARNING: swt must not point to the first shared memory address. + * @mem: pointer to a list of switches to be turned on/off. + * The list is structured as an array of struct upwr_mem_switches_t + * (see upower_defs.h), each one containing 2 word for up to 32 switches, + * one per bit, one word for the RAM array power switch, other for the + * RAM peripheral logic power switch. A bit = 1 means switch closed, + * bit = 0 means switch open. + * struct upwr_mem_switches_t also specifies a mask with 1 bit for each + * respective switch: mask bit = 1 means the open/close action is applied, + * mask bit = 0 means the switch stays unchanged. + * The pointer may be set to NULL, in which case no memory switch will be + * changed, but notice it may be turned off if the switch that feeds it is + * powered off. + * WARNING: mem must not point to the first shared memory address. + * @callb: pointer to the callback called when the configurations are applied. + * NULL if no callback is required. + * + * The function requests uPower to change the PMC switches and/or memory power + * as specified above. + * The request is executed if arguments are within range, with no protections + * regarding the adequate switch combinations and overall system state. + * + * If a memory is requested to turn on, but the power switch that feeds that + * memory is not, the power switch will be turned on anyway, if the swt + * array is not provided (that is, if swt is NULL). + * + * A callback can be optionally registered, and will be called upon the arrival + * of the request response from the uPower firmware, telling if it succeeded + * or not. + * + * A callback may not be registered (NULL pointer), in which case polling has + * to be used to check the response, by calling upwr_req_status or + * upwr_poll_req_status, using UPWR_SG_PWRMGMT as the service group argument. + * + * Callback or polling may return error if the service contends for a resource + * already being used by a power mode transition or an ongoing service in + * another domain. + * + * Context: no sleep, no locks taken/released. + * Return: 0 if ok, -1 if service group is busy. + * -2 if a pointer conversion to physical address failed, + * -3 if called in an invalid API state. + * Note that this is not the error response from the request itself: + * it only tells if the request was successfully sent to the uPower. + */ + +int upwr_pwm_chng_switch_mem(const struct upwr_switch_board_t swt[], + const struct upwr_mem_switches_t mem[], + upwr_callb callb) +{ + upwr_pwm_switch_msg txmsg = {0}; + unsigned long ptrval = 0UL; /* needed for X86, ARM64 */ + size_t stsize = 0U; + + if (api_state != UPWR_API_READY) { + return -3; + } + + if (UPWR_SG_BUSY(UPWR_SG_PWRMGMT)) { + return -1; + } + + UPWR_USR_CALLB(UPWR_SG_PWRMGMT, callb); + + UPWR_MSG_HDR(txmsg.hdr, UPWR_SG_PWRMGMT, UPWR_PWM_SWITCH); + + ptrval = (unsigned long)os_ptr2phy((void *)swt); + if (swt == NULL) { + txmsg.ptrs.ptr0 = 0; /* NULL pointer -> 0 offset */ + } else if (ptrval == 0U) { + return -2; /* pointer conversion failed */ + } else { + txmsg.ptrs.ptr0 = upwr_ptr2offset(ptrval, + UPWR_SG_PWRMGMT, + (stsize = UPWR_PMC_SWT_WORDS * sizeof(struct upwr_switch_board_t)), + 0U, + swt); + } + + ptrval = (unsigned long)os_ptr2phy((void *)mem); + if (mem == NULL) { + txmsg.ptrs.ptr1 = 0; /* NULL pointer -> 0 offset */ + } else if (ptrval == 0U) { + return -2; /* pointer conversion failed */ + } else { + txmsg.ptrs.ptr1 = upwr_ptr2offset(ptrval, + UPWR_SG_PWRMGMT, + UPWR_PMC_MEM_WORDS * sizeof(struct upwr_mem_switches_t), + stsize, + mem); + } + + upwr_srv_req(UPWR_SG_PWRMGMT, (uint32_t *)&txmsg, sizeof(txmsg) / 4U); + + return 0; +} + +/** + * upwr_pwm_pmode_config() - Configures a given power mode in a given domain. + * @domain: identifier of the domain to which the power mode belongs. + * Defined by SoC-dependent type soc_domain_t found in upower_soc_defs.h. + * @pmode: SoC-dependent power mode identifier defined by type abs_pwr_mode_t + * found in upower_soc_defs.h. + * @config: pointer to an SoC-dependent struct defining the power mode + * configuration, found in upower_soc_defs.h. + * @callb: pointer to the callback called when configurations are applied. + * NULL if no callback is required. + * + * The function requests uPower to change the power mode configuration as + * specified above. The request is executed if arguments are within range, + * and complies with SoC-dependent restrictions on value combinations. + * + * A callback can be optionally registered, and will be called upon the arrival + * of the request response from the uPower firmware, telling if it succeeded + * or not. + * + * A callback may not be registered (NULL pointer), in which case polling has + * to be used to check the response, by calling upwr_req_status or + * upwr_poll_req_status, using UPWR_SG_PWRMGMT as the service group argument. + * + * Context: no sleep, no locks taken/released. + * Return: 0 if ok, -1 if service group is busy, + * -2 if the pointer conversion to physical address failed, + * -3 if called in an invalid API state. + * Note that this is not the error response from the request itself: + * it only tells if the request was successfully sent to the uPower. + */ +int upwr_pwm_pmode_config(soc_domain_t domain, + abs_pwr_mode_t pmode, + const void *config, + upwr_callb callb) +{ + upwr_pwm_pmode_cfg_msg txmsg = {0}; + unsigned long ptrval = 0UL; /* needed for X86, ARM64 */ + + if (api_state != UPWR_API_READY) { + return -3; + } + + if (UPWR_SG_BUSY(UPWR_SG_PWRMGMT)) { + return -1; + } + + UPWR_USR_CALLB(UPWR_SG_PWRMGMT, callb); + + UPWR_MSG_HDR(txmsg.hdr, UPWR_SG_PWRMGMT, UPWR_PWM_CONFIG); + txmsg.hdr.domain = (uint32_t)domain; + txmsg.hdr.arg = pmode; + + ptrval = (unsigned long)os_ptr2phy(config); + if (ptrval == 0U) { + return -2; /* pointer conversion failed */ + } + + /* + * upwr_pwm_pmode_config is an exception: use the pointer + * (physical addr) as is + */ + + txmsg.ptr = (uint32_t)ptrval; + + upwr_srv_req(UPWR_SG_PWRMGMT, (uint32_t *)&txmsg, sizeof(txmsg) / 4U); + + return 0; +} + +/** + * upwr_pwm_reg_config() - Configures the uPower internal regulators. + * @config: pointer to the struct defining the regulator configuration; + * the struct upwr_reg_config_t is defined in the file upower_defs.h. + * @callb: pointer to the callback called when configurations are applied. + * NULL if no callback is required. + * + * The function requests uPower to change/define the configurations of the + * internal regulators. + * + * A callback can be optionally registered, and will be called upon the arrival + * of the request response from the uPower firmware, telling if it succeeded + * or not. + * + * A callback may not be registered (NULL pointer), in which case polling has + * to be used to check the response, by calling upwr_req_status or + * upwr_poll_req_status, using UPWR_SG_PWRMGMT as the service group argument. + * + * The service may fail with error UPWR_RESP_RESOURCE if a power mode transition + * or the same service (called from another domain) is executing simultaneously. + * This error should be interpreted as a "try later" response, as the service + * will succeed once those concurrent executions are done, and no other is + * started. + * + * Context: no sleep, no locks taken/released. + * Return: 0 if ok, -1 if service group is busy, + * -2 if the pointer conversion to physical address failed, + * -3 if called in an invalid API state. + * Note that this is not the error response from the request itself: + * it only tells if the request was successfully sent to the uPower. + */ + +int upwr_pwm_reg_config(const struct upwr_reg_config_t *config, + upwr_callb callb) +{ + upwr_pwm_regcfg_msg txmsg = {0}; + unsigned long ptrval = 0UL; /* needed for X86, ARM64 */ + + if (api_state != UPWR_API_READY) { + return -3; + } + + if (UPWR_SG_BUSY(UPWR_SG_PWRMGMT)) { + return -1; + } + + UPWR_USR_CALLB(UPWR_SG_PWRMGMT, callb); + + UPWR_MSG_HDR(txmsg.hdr, UPWR_SG_PWRMGMT, UPWR_PWM_REGCFG); + + ptrval = (unsigned long)os_ptr2phy(config); + if (ptrval == 0U) { + return -2; /* pointer conversion failed */ + } + + txmsg.ptr = upwr_ptr2offset(ptrval, + UPWR_SG_PWRMGMT, + sizeof(struct upwr_reg_config_t), + 0U, + config); + + upwr_srv_req(UPWR_SG_PWRMGMT, (uint32_t *)&txmsg, sizeof(txmsg) / 4U); + + return 0; +} + +/** + * upwr_pwm_chng_dom_bias() - Changes the domain bias. + * @bias: pointer to a domain bias configuration struct (see upower_soc_defs.h). + * @callb: pointer to the callback called when configurations are applied. + * NULL if no callback is required. + * + * The function requests uPower to change the domain bias configuration as + * specified above. The request is executed if arguments are within range, + * with no protections regarding the adequate value combinations and + * overall system state. + * + * A callback can be optionally registered, and will be called upon the arrival + * of the request response from the uPower firmware, telling if it succeeded + * or not. + * + * A callback may not be registered (NULL pointer), in which case polling has + * to be used to check the response, by calling upwr_req_status or + * upwr_poll_req_status, using UPWR_SG_PWRMGMT as the service group argument. + * + * Context: no sleep, no locks taken/released. + * Return: 0 if ok, -1 if service group is busy, + * -3 if called in an invalid API state. + * Note that this is not the error response from the request itself: + * it only tells if the request was successfully sent to the uPower. + */ +int upwr_pwm_chng_dom_bias(const struct upwr_dom_bias_cfg_t *bias, + upwr_callb callb) +{ + upwr_pwm_dom_bias_msg txmsg = {0}; + + if (api_state != UPWR_API_READY) { + return -3; + } + + if (UPWR_SG_BUSY(UPWR_SG_PWRMGMT)) { + return -1; + } + + UPWR_USR_CALLB(UPWR_SG_PWRMGMT, callb); + + UPWR_MSG_HDR(txmsg.hdr, UPWR_SG_PWRMGMT, UPWR_PWM_DOM_BIAS); + + /* SoC-dependent argument filling, defined in upower_soc_defs.h */ + UPWR_FILL_DOMBIAS_ARGS(txmsg.hdr.domain, bias, txmsg.args); + + upwr_srv_req(UPWR_SG_PWRMGMT, (uint32_t *)&txmsg, sizeof(txmsg) / 4U); + + return 0; +} + +/** + * upwr_pwm_chng_mem_bias()- Changes a ROM/RAM power bias. + * @domain: identifier of the domain upon which the bias is applied. + * Defined by SoC-dependent type soc_domain_t found in upower_soc_defs.h. + * @bias: pointer to a memory bias configuration struct (see upower_soc_defs.h). + * @callb: pointer to the callback called when configurations are applied. + * NULL if no callback is required. + * + * The function requests uPower to change the memory bias configuration as + * specified above. The request is executed if arguments are within range, + * with no protections regarding the adequate value combinations and + * overall system state. + * + * A callback can be optionally registered, and will be called upon the arrival + * of the request response from the uPower firmware, telling if it succeeded + * or not. + * + * A callback may not be registered (NULL pointer), in which case polling has + * to be used to check the response, by calling upwr_req_status or + * upwr_poll_req_status, using UPWR_SG_PWRMGMT as the service group argument. + * + * Context: no sleep, no locks taken/released. + * Return: 0 if ok, -1 if service group is busy, + * -3 if called in an invalid API state. + * Note that this is not the error response from the request itself: + * it only tells if the request was successfully sent to the uPower. + */ +int upwr_pwm_chng_mem_bias(soc_domain_t domain, + const struct upwr_mem_bias_cfg_t *bias, + upwr_callb callb) +{ + upwr_pwm_mem_bias_msg txmsg = {0}; + + if (api_state != UPWR_API_READY) { + return -3; + } + + if (UPWR_SG_BUSY(UPWR_SG_PWRMGMT)) { + return -1; + } + + UPWR_USR_CALLB(UPWR_SG_PWRMGMT, callb); + + UPWR_MSG_HDR(txmsg.hdr, UPWR_SG_PWRMGMT, UPWR_PWM_MEM_BIAS); + + txmsg.hdr.domain = (uint32_t)domain; + + /* SoC-dependent argument filling, defined in upower_soc_defs.h */ + UPWR_FILL_MEMBIAS_ARGS(bias, txmsg.args); + + upwr_srv_req(UPWR_SG_PWRMGMT, (uint32_t *)&txmsg, sizeof(txmsg) / 4U); + + return 0; +} + +/**--------------------------------------------------------------- + * DIAGNOSE SERVICE GROUP + */ + +/** + * upwr_dgn_mode() - Sets the diagnostic mode. + * @mode: diagnostic mode, which can be: + * - UPWR_DGN_NONE: no diagnostic recorded + * - UPWR_DGN_TRACE: warnings, errors, service, internal activity recorded + * - UPWR_DGN_SRVREQ: warnings, errors, service activity recorded + * - UPWR_DGN_WARN: warnings and errors recorded + * - UPWR_DGN_ALL: trace, service, warnings, errors, task state recorded + * - UPWR_DGN_ERROR: only errors recorded + * - UPWR_DGN_ALL2ERR: record all until an error occurs, + * freeze recording on error + * - UPWR_DGN_ALL2HLT: record all until an error occurs, + * executes an ebreak on error, which halts the core if enabled through + * the debug interface + * @callb: pointer to the callback called when mode is changed. + * NULL if no callback is required. + * + * Context: no sleep, no locks taken/released. + * Return: 0 if ok, + * -1 if service group is busy, + * -3 if called in an invalid API state + */ +int upwr_dgn_mode(upwr_dgn_mode_t mode, const upwr_callb callb) +{ + upwr_dgn_mode_msg txmsg = {0}; + + if (UPWR_SG_BUSY(UPWR_SG_DIAG)) { + return -1; + } + + UPWR_USR_CALLB(UPWR_SG_DIAG, callb); + + UPWR_MSG_HDR(txmsg.hdr, UPWR_SG_DIAG, UPWR_DGN_MODE); + + txmsg.hdr.arg = mode; + + upwr_srv_req(UPWR_SG_DIAG, (uint32_t *)&txmsg, sizeof(txmsg) / 4U); + + return 0; +} + +/**--------------------------------------------------------------- + * AUXILIARY CALLS + */ + +/** + * upwr_rom_version() - informs the ROM firwmware version. + * @vmajor: pointer to the variable to get the firmware major version number. + * @vminor: pointer to the variable to get the firmware minor version number. + * @vfixes: pointer to the variable to get the firmware fixes number. + * + * Context: no sleep, no locks taken/released. + * Return: SoC id. + */ +uint32_t upwr_rom_version(uint32_t *vmajor, uint32_t *vminor, uint32_t *vfixes) +{ + uint32_t soc; + + upwr_lock(1); + soc = fw_rom_version.soc_id; + *vmajor = fw_rom_version.vmajor; + *vminor = fw_rom_version.vminor; + *vfixes = fw_rom_version.vfixes; + upwr_lock(0); + return soc; +} + +/** + * upwr_ram_version() - informs the RAM firwmware version. + * @vminor: pointer to the variable to get the firmware minor version number. + * @vfixes: pointer to the variable to get the firmware fixes number. + * + * The 3 values returned are 0 if no RAM firmwmare was loaded and initialized. + * + * Context: no sleep, no locks taken/released. + * Return: firmware major version number. + */ +uint32_t upwr_ram_version(uint32_t *vminor, uint32_t *vfixes) +{ + uint32_t vmajor; + + upwr_lock(1); + vmajor = fw_ram_version.vmajor; + *vminor = fw_ram_version.vminor; + *vfixes = fw_ram_version.vfixes; + upwr_lock(0); + + return vmajor; +} + +/** + * upwr_req_status() - tells the status of the service group request, and + * returns a request return value, if any. + * @sg: service group of the request + * @sgfptr: pointer to the variable that will hold the function id of + * the last request completed; can be NULL, in which case it is not used. + * @errptr: pointer to the variable that will hold the error code; + * can be NULL, in which case it is not used. + * @retptr: pointer to the variable that will hold the value returned + * by the last request completed (invalid if the last request completed didn't + * return any value); can be NULL, in which case it is not used. + * Note that a request may return a value even if service error is returned + * (*errptr != UPWR_RESP_OK): that is dependent on the specific service. + * + * This call can be used in a poll loop of a service request completion in case + * a callback was not registered. + * + * Context: no sleep, no locks taken/released. + * Return: service request status: succeeded, failed, or ongoing (busy) + */ +upwr_req_status_t upwr_req_status(upwr_sg_t sg, + uint32_t *sgfptr, + upwr_resp_t *errptr, + int *retptr) +{ + upwr_req_status_t status; + + upwr_lock(1); + if (sgfptr != NULL) { + *sgfptr = (uint32_t)sg_rsp_msg[sg].hdr.function; + } + + if (errptr != NULL) { + *errptr = (upwr_resp_t)sg_rsp_msg[sg].hdr.errcode; + } + + if (retptr != NULL) { + *retptr = (int)((sg_rsp_siz[sg] == 2U) ? + sg_rsp_msg[sg].word2 : sg_rsp_msg[sg].hdr.ret); + } + + status = ((sg_busy & (1UL << sg)) == 1U) ? UPWR_REQ_BUSY : + (sg_rsp_msg[sg].hdr.errcode == UPWR_RESP_OK) ? UPWR_REQ_OK : + UPWR_REQ_ERR; + upwr_lock(0); + return status; +} + +/** + * upwr_poll_req_status() - polls the status of the service group request, and + * returns a request return value, if any. + * @sg: service group of the request + * @sgfptr: pointer to the variable that will hold the function id of + * the last request completed; can be NULL, in which case it is not used. + * @errptr: pointer to the variable that will hold the error code; + * can be NULL, in which case it is not used. + * @retptr: pointer to the variable that will hold the value returned + * by the last request completed (invalid if the last request completed didn't + * return any value); can be NULL, in which case it is not used. + * Note that a request may return a value even if service error is returned + * (*errptr != UPWR_RESP_OK): that is dependent on the specific service. + * @attempts: maximum number of polling attempts; if attempts > 0 and is + * reached with no service response received, upwr_poll_req_status returns + * UPWR_REQ_BUSY and variables pointed by sgfptr, retptr and errptr are not + * updated; if attempts = 0, upwr_poll_req_status waits "forever". + * + * This call can be used to poll a service request completion in case a + * callback was not registered. + * + * Context: no sleep, no locks taken/released. + * Return: service request status: succeeded, failed, or ongoing (busy) + */ +upwr_req_status_t upwr_poll_req_status(upwr_sg_t sg, + uint32_t *sgfptr, + upwr_resp_t *errptr, + int *retptr, + uint32_t attempts) +{ + uint32_t i; + upwr_req_status_t ret; + + if (attempts == 0U) { + while ((ret = upwr_req_status(sg, sgfptr, errptr, retptr)) == UPWR_REQ_BUSY) { + }; + + return ret; + } + + for (i = 0U; i < attempts; i++) { + ret = upwr_req_status(sg, sgfptr, errptr, retptr); + if (ret != UPWR_REQ_BUSY) { + break; + } + } + + return ret; +} + +/** + * upwr_alarm_code() - returns the alarm code of the last alarm occurrence. + * + * The value returned is not meaningful if no alarm was issued by uPower. + * + * Context: no sleep, no locks taken/released. + * Return: alarm code, as defined by the type upwr_alarm_t in upwr_soc_defines.h + */ +upwr_alarm_t upwr_alarm_code(void) +{ + return (upwr_alarm_t)(3U & (mu->FSR.R >> 1U)); /* FSR[2:1] */ +} + +/**--------------------------------------------------------------- + * TRANSMIT/RECEIVE PRIMITIVES + * --------------------------------------------------------------- + */ + +/* + * upwr_copy2tr() - copies a message to the MU TR registers; + * fill the TR registers before writing TIEN to avoid early interrupts; + * also, fill them from the higher index to the lowest, so the receive + * interrupt flag RF[0] will be the last to set, regardless of message size; + */ +void upwr_copy2tr(struct MU_t *local_mu, const uint32_t *msg, unsigned int size) +{ + for (int i = (int)size - 1; i > -1; i--) { + local_mu->TR[i].R = msg[i]; + } +} + +/** + * upwr_tx() - queues a message for transmission. + * @msg : pointer to the message sent. + * @size: message size in 32-bit words + * @callback: pointer to a function to be called when transmission done; + * can be NULL, in which case no callback is done. + * + * This is an auxiliary function used by the rest of the API calls. + * It is normally not called by the driver code, unless maybe for test purposes. + * + * Context: no sleep, no locks taken/released. + * Return: number of vacant positions left in the transmission queue, or + * -1 if the queue was already full when upwr_tx was called, or + * -2 if any argument is invalid (like size off-range) + */ +int upwr_tx(const uint32_t *msg, + unsigned int size, + UPWR_TX_CALLB_FUNC_T callback) +{ + if (size > UPWR_MU_MSG_SIZE) { + return -2; + } + + if (size == 0U) { + return -2; + } + + if (mu->TSR.R != UPWR_MU_TSR_EMPTY) { + return -1; /* not all TE bits in 1: some data to send still */ + } + + mu_tx_callb = callback; + + upwr_copy2tr(mu, msg, size); + mu->TCR.R = 1UL << (size - 1UL); + + mu_tx_pend = 1UL; + + return 0; +} + +/** + * upwr_rx() - unqueues a received message from the reception queue. + * @msg: pointer to the message destination buffer. + * @size: pointer to variable to hold message size in 32-bit words. + * + * This is an auxiliary function used by the rest of the API calls. + * It is normally not called by the driver code, unless maybe for test purposes. + * + * Context: no sleep, no locks taken/released. + * Return: number of messages remaining in the reception queue, or + * -1 if the queue was already empty when upwr_rx was called, or + * -2 if any argument is invalid (like mu off-range) + */ +int upwr_rx(char *msg, unsigned int *size) +{ + unsigned int len = mu->RSR.R; + + len = (len == 0x0U) ? 0U : + (len == 0x1U) ? 1U : + #if UPWR_MU_MSG_SIZE > 1 + (len == 0x3U) ? 2U : + #if UPWR_MU_MSG_SIZE > 2 + (len == 0x7U) ? 3U : + #if UPWR_MU_MSG_SIZE > 3 + (len == 0xFU) ? 4U : + #endif + #endif + #endif + 0xFFFFFFFFU; /* something wrong */ + + if (len == 0xFFFFFFFFU) { + return -3; + } + + if (len == 0U) { + return -1; + } + + *size = len; + + /* + * copy the received message to the rx queue, + * so the interrupts are cleared. + */ + msg_copy(msg, (char *)&mu->RR[0], len); + + mu->RCR.R = 1U; /* enable only RR[0] receive interrupt */ + + return 0; +} + +/** + * upwr_rx_callback() - sets up a callback for a message receiving event. + * @callback: pointer to a function to be called when a message arrives; + * can be NULL, in which case no callback is done. + * + * This is an auxiliary function used by the rest of the API calls. + * It is normally not called by the driver code, unless maybe for test purposes. + * + * Context: no sleep, no locks taken/released. + * Return: 0 if ok; -2 if any argument is invalid (mu off-range). + */ +int upwr_rx_callback(UPWR_RX_CALLB_FUNC_T callback) +{ + mu_rx_callb = callback; + + return 0; +} + +/** + * msg_copy() - copies a message. + * @dest: pointer to the destination message. + * @src : pointer to the source message. + * @size: message size in words. + * + * This is an auxiliary function used by the rest of the API calls. + * It is normally not called by the driver code, unless maybe for test purposes. + * + * Context: no sleep, no locks taken/released. + * Return: none (void) + */ +void msg_copy(char *dest, char *src, unsigned int size) +{ + for (uint32_t i = 0U; i < size * sizeof(uint32_t); i++) { + dest[i] = src[i]; + } +} diff --git a/plat/imx/imx8ulp/upower/upower_api.h b/plat/imx/imx8ulp/upower/upower_api.h new file mode 100644 index 00000000..0069f5f0 --- /dev/null +++ b/plat/imx/imx8ulp/upower/upower_api.h @@ -0,0 +1,1629 @@ +/* SPDX-License-Identifier: BSD-3-Clause */ +/** + * Copyright 2019-2024 NXP + * + * KEYWORDS: micro-power uPower driver API + * ----------------------------------------------------------------------------- + * PURPOSE: uPower driver API + * ----------------------------------------------------------------------------- + * PARAMETERS: + * PARAM NAME RANGE:DESCRIPTION: DEFAULTS: UNITS + * ----------------------------------------------------------------------------- + * REUSE ISSUES: no reuse issues + */ +#ifndef UPWR_API_H +#define UPWR_API_H + +#include "upmu.h" +#include "upower_soc_defs.h" +/****************************************************************************** + * uPower API Overview and Concepts + * + * This API is intended to be used by the OS drivers (Linux, FreeRTOS etc) + * as well as bare metal drivers to command and use services from the uPower. + * It aims to be OS-independent. + * + * The API functions fall in 3 categories: + * - initialization/start-up + * - service requests + * - auxiliary + * + * The communication with the uPower is mostly made through the Message Unit + * (MU) IP. uPower provides one MU for each CPU cluster in a different + * power domain. An API instance runs on each CPU cluster. + * + * The API assumes each SoC power domain/CPU cluster receives 2 interrupts + * from the uPower MU: + * 1. Tx/Rx, which is issued on both transmission and reception + * 2. Exception interrupt, to handle critical alams, catastrophic errors, etc. + * This interrupt should have a high priority, preferably an NMI. + * + * The normal uPower operation is done by service requests. There is an API + * function for each service request, and all service requests send back a + * response, at least to indicate success/failure. + * The service request functions are non-blocking, and their completion can be + * tracked in two ways: + * 1. by a callback, registered when the service request call is made by + * passing the callback function pointer; a NULL pointer may be passed, + * in which case no callback is made. + * 2. by polling, using the auxiliary functions upwr_req_status or + * upwr_poll_req_status; + * polling must be used if no callback is registered, but callbacks and + * polling are completely independent. + * + * Note: a service request must not be started from a callback. + * + * uPower service requests are classified in Service Groups. + * Each Service Group has a set of related functions, named upwr_XXX_, + * where XXX is a 3-letter service group mnemonic. The service groups are: + * - Exception Service Group - upwr_xcp_* + * ~ gathers functions that deal with errors and other processes outside + * the functional scope. + * - Power Management Service Group - upwr_pwm_* + * ~ functions to control switches, configure power modes, set internal voltage etc + * - Delay Measurement Service Group - upwr_dlm_* + * ~ delay measurements function using the process monitor and delay meter + * - Voltage Measurement Service Group - upwr_vtm_* + * ~ functions for voltage measurements, comparisons, alarms, power meter, set PMIC rail voltage + * - Temperature Measurement Service Group - upwr_tpm_* + * ~ functions for temperature measurements, comparisons, alarms + * - Current Measurement Service Group - upwr_crm_* + * ~ functions for current and charge measurement + * - Diagnostic Service Group - upwr_dgn_* + * ~ functions for log configuration and statistics collecting + * + * Service requests follow this "golden rule": + * *** No two requests run simultaneously for the same service group, + * on the same domain *** + * They can run simultaneously on different domains (RTD/APD), and can also run + * simultaneously if belong to different service groups (even on same domain). + * Therefore, requests to the same service group on the same domain must be + * serialized. A service request call returns error if there is another request + * on the same service group pending, waiting a response (on the same domain). + * + * A request for continuous service does not block the service group. + * For instance, a request to "measure the temperature each 10 miliseconds" + * responds quickly, unlocks the service group, and the temperature + * continues to be measured as requested, every 10 miliseconds from then on. + * + * Service Groups have a fixed priority in the API, from higher to lower: + * 1. Exception + * 2. Power Management + * 3. Delay Measurement + * 4. Voltage Measurement + * 5. Current Measurement + * 6. Temperature Measurement + * 7. Diagnostics + * + * The priority above only affects the order in which requests are sent to the + * uPower firmware: request to the higher priority Service Group is sent first, + * even if the call was made later, if there is an MU transmission pending, + * blocking it. The service priorities in the firmware depend on other factors. + * + * Services are requested using API functions. A service function returns with + * no error if a request was successfully made, but it doesn't mean the service + * was completed. The service is executed asynchronously, and returns a result + * (at least success/fail) via a callback or polling for service status. + * The possible service response codes are: + * - UPWR_RESP_OK = 0, : no error + * - UPWR_RESP_SG_BUSY : service group is busy + * - UPWR_RESP_SHUTDOWN : services not up or shutting down + * - UPWR_RESP_BAD_REQ : invalid request (usually invalid argumnents) + * - UPWR_RESP_BAD_STATE : system state doesn't allow perform the request + * - UPWR_RESP_UNINSTALLD : service or function not installed + * - UPWR_RESP_UNINSTALLED : service or function not installed (alias) + * - UPWR_RESP_RESOURCE : resource not available + * - UPWR_RESP_TIMEOUT : service timeout + */ + +/** + * upwr_callb()-generic function pointer for a request return callback; + * @sg: request service group + * @func: service request function id. + * @errcode: error code. + * @ret: return value, if any. Note that a request may return a value even if + * service error is returned (errcode != UPWR_RESP_OK); that is dependent on + * the specific service. + * + * Context: no sleep, no locks taken/released. + * Return: none (void) + */ +typedef void (*upwr_callb)(upwr_sg_t sg, uint32_t func, + upwr_resp_t errcode, ...); + +/**--------------------------------------------------------------- + * INITIALIZATION, CONFIGURATION + * + * A reference uPower initialization sequence goes as follows: + * + * 1. host CPU calls upwr_init. + * 2. (optional) host checks the ROM version and SoC code calling upwr_vers(...) + * and optionally performs any configuration or workaround accordingly. + * 3. host CPU calls upwr_start to start the uPower services, passing a + * service option number. + * If no RAM code is loaded or it has no service options, the launch option + * number passed must be 0, which will start the services available in ROM. + * upwr_start also receives a pointer to a callback called by the API + * when the firmware is ready to receive service requests. + * The callback may be replaced by polling, calling upwr_req_status in a loop + * or upwr_poll_req_status; in this case the callback pointer may be NULL. + * A host may call upwr_start even if the services were already started by + * any host: if the launch option is the same, the response will be ok, + * but will indicate error if the services were already started with a + * different launch option. + * 4. host waits for the callback calling, or polling finishing; + * if no error is returned, it can start making service calls using the API. + * + * Variations on that reference sequence are possible: + * - the uPower services can be started using the ROM code only, which includes + * the basic Power Management services, among others, with launch option + * number = 0. + * The code RAM can be loaded while these services are running and, + * when the loading is done, the services can be re-started with these 2 + * requests executed in order: upwr_xcp_shutdown and upwr_start, + * using the newly loaded RAM code (launch option > 0). + * + * NOTE: the initialization call upwr_init is not effective and + * returns error when called after the uPower services are started. + */ + +/** + * upwr_init() - API initialization; must be the first API call after reset. + * @domain: SoC-dependent CPU domain id; identifier used by the firmware in + * many services. Defined by SoC-dependent type soc_domain_t found in + * upower_soc_defs.h. + * @muptr: pointer to the MU instance. + * @mallocptr: pointer to the memory allocation function + * @physaddrptr: pointer to the function to convert pointers to + * physical addresses. If NULL, no conversion is made (pointer=physical address) + * @isrinstptr: pointer to the function to install the uPower ISR callbacks; + * the function receives the pointers to the MU tx/rx and Exception ISRs + * callbacks, which must be called from the actual system ISRs. + * The function pointed by isrinstptr must also enable the interrupt at the + * core/interrupt controller, but must not enable the interrupt at the MU IP. + * The system ISRs are responsible for dealing with the interrupt controller, + * performing any other context save/restore, and any other housekeeping. + * @lockptr: pointer to a function that prevents MU interrupts (if argrument=1) + * or allows it (if argument=0). The API calls this function to make small + * specific code portions thread safe. Only MU interrupts must be avoided, + * the code may be suspended for other reasons. + * If no MU interrupts can happen during the execution of an API call or + * callback, even if enabled, for some other reason (e.g. interrupt priority), + * then this argument may be NULL. + * + * Context: no sleep, no locks taken/released. + * Return: 0 if ok, + * -1 if failed to allocate memory, or use some other resource. + * -2 if any argument is invalid. + * -3 if failed to send the ping message. + * -4 if failed to receive the initialization message, or was invalid + */ + +/* malloc function ptr */ +typedef void* (*upwr_malloc_ptr_t)(unsigned int size); + +/* pointer->physical address conversion function ptr */ +typedef void* (*upwr_phyadr_ptr_t)(const void *addr); + +typedef uint32_t upwr_api_state_t; + +extern volatile upwr_api_state_t api_state; + +/* + * upwr_lock_ptr_t: pointer to a function that prevents MU interrupts + * (if argrument lock=1) or allows it (if argument lock=0). + * The API calls this function to make small specific code portions thread safe. + * Only MU interrupts must be avoided, the code may be suspended for other + * reasons. + */ +typedef void (*upwr_lock_ptr_t)(int lock); + +typedef void (*upwr_isr_callb)(void); + +typedef void (*upwr_inst_isr_ptr_t)(upwr_isr_callb txrx_isr, + upwr_isr_callb excp_isr); +void upwr_start_callb(void); + +int upwr_init(soc_domain_t domain, struct MU_t *muptr, + const upwr_malloc_ptr_t mallocptr, + const upwr_phyadr_ptr_t phyadrptr, + const upwr_inst_isr_ptr_t isrinstptr, + const upwr_lock_ptr_t lockptr); + +/** + * upwr_start() - Starts the uPower services. + * @launchopt: a number to select between multiple launch options, + * that may define, among other things, which services will be started, + * or which services implementations, features etc. + * launchopt = 0 selects a subset of services implemented in ROM; + * any other number selects service sets implemented in RAM, launched + * by the firmware function ram_launch; if an invalid launchopt value is passed, + * no services are started, and the callback returns error (see below). + * @rdycallb: pointer to the callback to be called when the uPower is ready + * to receive service requests. NULL if no callback needed. + * The callback receives as arguments the RAM firmware version numbers. + * If all 3 numbers (vmajor, vminor, vfixes) are 0, that means the + * service launching failed. + * Firmware version numbers will be the same as ROM if launchopt = 0, + * selecting the ROM services. + * + * upwr_start can be called by any domain even if the services are already + * started: it has no effect, returning success, if the launch option is the + * same as the one that actually started the service, and returns error if + * called with a different option. + * + * A callback can be optionally registered, and will be called upon the arrival + * of the request response from the uPower firmware, telling if it succeeded or + * not. + * A callback may not be registered (NULL pointer), in which case polling has + * to be used to check the response, by calling upwr_req_status or + * upwr_poll_req_status, using UPWR_SG_EXCEPT as the service group argument. + * + * Context: no sleep, no locks taken/released. + * Return: 0 if ok, + * -1 if a resource failed, + * -2 if the domain passed is the same as the caller, + * -3 if called in an invalid API state + */ + +extern void upwr_txrx_isr(void); + +typedef void (*upwr_rdy_callb)(uint32_t vmajor, uint32_t vminor, uint32_t vfixes); + +int upwr_start(uint32_t launchopt, const upwr_rdy_callb rdycallb); + + +/**--------------------------------------------------------------- + * EXCEPTION SERVICE GROUP + */ + +/** + * upwr_xcp_config() - Applies general uPower configurations. + * @config: pointer to the uPower SoC-dependent configuration struct + * upwr_xcp_config_t defined in upower_soc_defs.h. NULL may be passed, meaning + * a request to read the configuration, in which case it appears in the callback + * argument ret, or can be pointed by argument retptr in the upwr_req_status and + * upwr_poll_req_status calls, casted to upwr_xcp_config_t. + * @callb: pointer to the callback to be called when the uPower has finished + * the configuration, or NULL if no callback needed (polling used instead). + * + * Some configurations are targeted for a specific domain (see the struct + * upwr_xcp_config_t definition in upower_soc_defs.h); this call has implicit + * domain target (the same domain from which is called). + * + * The return value is always the current configuration value, either in a + * read-only request (config = NULL) or after setting a new configuration + * (non-NULL config). + * + * A callback can be optionally registered, and will be called upon the arrival + * of the request response from the uPower firmware, telling if it succeeded or + * not. + * A callback may not be registered (NULL pointer), in which case polling has + * to be used to check the response, by calling upwr_req_status or + * upwr_poll_req_status, using UPWR_SG_EXCEPT as the service group argument. + * + * Context: no sleep, no locks taken/released. + * Return: 0 if ok, + * -1 if service group is busy, + * -3 if called in an invalid API state + */ + +int upwr_xcp_config(const upwr_xcp_config_t *config, const upwr_callb callb); + +/** + * upwr_xcp_sw_alarm() - Makes uPower issue an alarm interrupt to given domain. + * @domain: identifier of the domain to alarm. Defined by SoC-dependent type + * soc_domain_t found in upower_soc_defs.h. + * @code: alarm code. Defined by SoC-dependent type upwr_alarm_t found in + * upower_soc_defs.h. + * @callb: pointer to the callback to be called when the uPower has finished + * the alarm, or NULL if no callback needed (polling used instead). + * + * The function requests the uPower to issue an alarm of the given code as if + * it had originated internally. This service is useful mainly to test the + * system response to such alarms, or to make the system handle a similar alarm + * situation detected externally to uPower. + * + * The system ISR/code handling the alarm may retrieve the alarm code by calling + * the auxiliary function upwr_alarm_code. + * + * A callback can be optionally registered, and will be called upon the arrival + * of the request response from the uPower firmware, telling if it succeeded or + * not. + * A callback may not be registered (NULL pointer), in which case polling has + * to be used to check the response, by calling upwr_req_status or + * upwr_poll_req_status, using UPWR_SG_EXCEPT as the service group argument. + * + * Context: no sleep, no locks taken/released. + * Return: 0 if ok, + * -1 if service group is busy, + * -3 if called in an invalid API state + */ + +int upwr_xcp_sw_alarm(soc_domain_t domain, upwr_alarm_t code, + const upwr_callb callb); + +/** + * upwr_xcp_set_ddr_retention() - M33/A35 can use this API to set/clear ddr retention + * @domain: identifier of the caller domain. + * soc_domain_t found in upower_soc_defs.h. + * @enable: true, means that set ddr retention, false clear ddr retention. + * @callb: NULL + * + * A callback may not be registered (NULL pointer), in which case polling has + * to be used to check the response, by calling upwr_req_status or + * upwr_poll_req_status, using UPWR_SG_EXCEPT as the service group argument. + * + * Context: no sleep, no locks taken/released. + * Return: 0 if ok, + * -1 if service group is busy, + * -3 if called in an invalid API state + */ + +int upwr_xcp_set_ddr_retention(soc_domain_t domain, uint32_t enable, + const upwr_callb callb); + +/** + * upwr_xcp_set_mipi_dsi_ena() - M33/A35 can use this API to set/clear mipi dsi ena + * @domain: identifier of the caller domain. + * soc_domain_t found in upower_soc_defs.h. + * @enable: true, means that set ddr retention, false clear ddr retention. + * @callb: NULL + * + * A callback may not be registered (NULL pointer), in which case polling has + * to be used to check the response, by calling upwr_req_status or + * upwr_poll_req_status, using UPWR_SG_EXCEPT as the service group argument. + * + * Context: no sleep, no locks taken/released. + * Return: 0 if ok, + * -1 if service group is busy, + * -3 if called in an invalid API state + */ + +int upwr_xcp_set_mipi_dsi_ena(soc_domain_t domain, uint32_t enable, + const upwr_callb callb); + +/** + * upwr_xcp_get_mipi_dsi_ena() - M33/A35 can use this API to get mipi dsi ena status + * @domain: identifier of the caller domain. + * soc_domain_t found in upower_soc_defs.h. + * @callb: NULL + * + * A callback may not be registered (NULL pointer), in which case polling has + * to be used to check the response, by calling upwr_req_status or + * upwr_poll_req_status, using UPWR_SG_EXCEPT as the service group argument. + * + * Context: no sleep, no locks taken/released. + * Return: 0 if ok, + * -1 if service group is busy, + * -3 if called in an invalid API state + */ + +int upwr_xcp_get_mipi_dsi_ena(soc_domain_t domain, const upwr_callb callb); + +/** + * upwr_xcp_set_osc_mode() - M33/A35 can use this API to set uPower OSC mode + * @domain: identifier of the caller domain. + * soc_domain_t found in upower_soc_defs.h. + * @osc_mode, 0 means low frequency, not 0 means high frequency. + * @callb: NULL + * + * A callback may not be registered (NULL pointer), in which case polling has + * to be used to check the response, by calling upwr_req_status or + * upwr_poll_req_status, using UPWR_SG_EXCEPT as the service group argument. + * + * Context: no sleep, no locks taken/released. + * Return: 0 if ok, + * -1 if service group is busy, + * -3 if called in an invalid API state + */ +int upwr_xcp_set_osc_mode(soc_domain_t domain, uint32_t osc_mode, + const upwr_callb callb); + +/** + * upwr_xcp_set_rtd_use_ddr() - M33 call this API to inform uPower, M33 is using ddr + * @domain: identifier of the caller domain. + * soc_domain_t found in upower_soc_defs.h. + * @is_use_ddr: not 0, true, means that RTD is using ddr. 0, false, means that, RTD + * is not using ddr. + * @callb: NULL + * + * A callback may not be registered (NULL pointer), in which case polling has + * to be used to check the response, by calling upwr_req_status or + * upwr_poll_req_status, using UPWR_SG_EXCEPT as the service group argument. + * + * Context: no sleep, no locks taken/released. + * Return: 0 if ok, + * -1 if service group is busy, + * -3 if called in an invalid API state + */ +int upwr_xcp_set_rtd_use_ddr(soc_domain_t domain, uint32_t is_use_ddr, + const upwr_callb callb); + +/** + * upwr_xcp_set_rtd_apd_llwu() - M33/A35 can use this API to set/clear rtd_llwu apd_llwu + * @domain: set which domain (RTD_DOMAIN, APD_DOMAIN) LLWU. + * soc_domain_t found in upower_soc_defs.h. + * @enable: true, means that set rtd_llwu or apd_llwu, false clear rtd_llwu or apd_llwu. + * @callb: NULL + * + * A callback may not be registered (NULL pointer), in which case polling has + * to be used to check the response, by calling upwr_req_status or + * upwr_poll_req_status, using UPWR_SG_EXCEPT as the service group argument. + * + * Context: no sleep, no locks taken/released. + * Return: 0 if ok, + * -1 if service group is busy, + * -3 if called in an invalid API state + */ +int upwr_xcp_set_rtd_apd_llwu(soc_domain_t domain, uint32_t enable, + const upwr_callb callb); +/** + * upwr_xcp_shutdown() - Shuts down all uPower services and power mode tasks. + * @callb: pointer to the callback to be called when the uPower has finished + * the shutdown, or NULL if no callback needed + * (polling used instead). + * + * A callback can be optionally registered, and will be called upon the arrival + * of the request response from the uPower firmware, telling if it succeeded or + * not. + * A callback may not be registered (NULL pointer), in which case polling has + * to be used to check the response, by calling upwr_req_status or + * upwr_poll_req_status, using UPWR_SG_EXCEPT as the service group argument. + * + * At the callback the uPower/API is back to initialization/start-up phase, + * so service request calls return error. + * + * Context: no sleep, no locks taken/released. + * Return: 0 if ok, + * -1 if service group is busy, + * -3 if called in an invalid API state + */ +int upwr_xcp_shutdown(const upwr_callb callb); + +/** + * upwr_xcp_i2c_access() - Performs an access through the uPower I2C interface. + * @addr: I2C slave address, up to 10 bits. + * @data_size: determines the access direction and data size in bytes, up to 4; + * negetive data_size determines a read access with size -data_size; + * positive data_size determines a write access with size data_size; + * data_size=0 is invalid, making the service return error UPWR_RESP_BAD_REQ. + * @subaddr_size: size of the sub-address in bytes, up to 4; if subaddr_size=0, + * no subaddress is used. + * @subaddr: sub-address, only used if subaddr_size > 0. + * @wdata: write data, up to 4 bytes; ignored if data_size < 0 (read) + * @callb: pointer to the callback to be called when the uPower has finished + * the access, or NULL if no callback needed + * (polling used instead). + * + * A callback can be optionally registered, and will be called upon the arrival + * of the request response from the uPower firmware, telling if it succeeded or + * not. + * A callback may not be registered (NULL pointer), in which case polling has + * to be used to check the response, by calling upwr_req_status or + * upwr_poll_req_status, using UPWR_SG_EXCEPT as the service group argument. + * + * The service performs a read (data_size < 0) or a write (data_size > 0) of + * up to 4 bytes on the uPower I2C interface. The data read from I2C comes via + * the callback argument ret, or written to the variable pointed by retptr, + * if polling is used (calls upwr_req_status or upwr_poll_req_status). + * ret (or *retptr) also returns the data written on writes. + * + * Sub-addressing is supported, with sub-address size determined by the argument + * subaddr_size, up to 4 bytes. Sub-addressing is not used if subaddr_size=0. + * + * Context: no sleep, no locks taken/released. + * Return: 0 if ok, + * -1 if service group is busy, + * -3 if called in an invalid API state + */ +int upwr_xcp_i2c_access(uint16_t addr, int8_t data_size, uint8_t subaddr_size, + uint32_t subaddr, uint32_t wdata, + const upwr_callb callb); + + +/**--------------------------------------------------------------- + * POWER MANAGEMENT SERVICE GROUP + */ + +/** + * upwr_pwm_dom_power_on() - Commands uPower to power on the platform of other + * domain (not necessarily its core(s)); does not release the core reset. + * @domain: identifier of the domain to power on. Defined by SoC-dependent type + * soc_domain_t found in upower_soc_defs.h. + * @boot_start: must be 1 to start the domain core(s) boot(s), releasing + * its (their) resets, or 0 otherwise. + * @pwroncallb: pointer to the callback to be called when the uPower has + * finished the power on procedure, or NULL if no callback needed + * (polling used instead). + * + * A callback can be optionally registered, and will be called upon the arrival + * of the request response from the uPower firmware, telling if it succeeded or + * not. + * A callback may not be registered (NULL pointer), in which case polling has + * to be used to check the response, by calling upwr_req_status or + * upwr_poll_req_status, using UPWR_SG_PWRMGMT as the service group argument. + * + * Context: no sleep, no locks taken/released. + * Return: 0 if ok, + * -1 if service group is busy, + * -2 if the domain passed is the same as the caller, + * -3 if called in an invalid API state + */ +int upwr_pwm_dom_power_on(soc_domain_t domain, int boot_start, + const upwr_callb pwroncallb); + +/** + * upwr_pwm_boot_start() - Commands uPower to release the reset of other CPU(s), + * starting their boots. + * @domain: identifier of the domain to release the reset. Defined by + * SoC-dependent type soc_domain_t found in upower_soc_defs.h. + * @bootcallb: pointer to the callback to be called when the uPower has finished + * the boot start procedure, or NULL if no callback needed + * (polling used instead). + * + * A callback can be optionally registered, and will be called upon the arrival + * of the request response from the uPower firmware, telling if it succeeded or + * not. + * A callback may not be registered (NULL pointer), in which case polling has + * to be used to check the response, by calling upwr_req_status or + * upwr_poll_req_status, using UPWR_SG_PWRMGMT as the service group argument. + * + * The callback calling doesn't mean the CPUs boots have finished: + * it only indicates that uPower released the CPUs resets, and can receive + * other power management service group requests. + * + * Context: no sleep, no locks taken/released. + * Return: 0 if ok, + * -1 if service group is busy, + * -2 if the domain passed is the same as the caller, + * -3 if called in an invalid API state + */ +int upwr_pwm_boot_start(soc_domain_t domain, const upwr_callb bootcallb); + +/** + * upwr_pwm_param() - Changes Power Management parameters. + * @param: pointer to a parameter structure upwr_pwm_param_t, SoC-dependent, + * defined in upwr_soc_defines.h. NULL may be passed, meaning + * a request to read the parameter set, in which case it appears in the callback + * argument ret, or can be pointed by argument retptr in the upwr_req_status and + * upwr_poll_req_status calls, casted to upwr_pwm_param_t. + * @callb: response callback pointer; NULL if no callback needed. + * + * The return value is always the current parameter set value, either in a + * read-only request (param = NULL) or after setting a new parameter + * (non-NULL param). + * + * Some parameters may be targeted for a specific domain (see the struct + * upwr_pwm_param_t definition in upower_soc_defs.h); this call has implicit + * domain target (the same domain from which is called). + * + * A callback can be optionally registered, and will be called upon the arrival + * of the request response from the uPower firmware, telling if it succeeded or + * not. + * A callback may not be registered (NULL pointer), in which case polling has + * to be used to check the response, by calling upwr_req_status or + * upwr_poll_req_status, using UPWR_SG_PWRMGMT as the service group argument. + * + * Context: no sleep, no locks taken/released. + * Return: 0 if ok, + * -1 if service group is busy, + * -3 if called in an invalid API state + */ +int upwr_pwm_param(upwr_pwm_param_t *param, const upwr_callb callb); + +/** + * upwr_pwm_chng_reg_voltage() - Changes the voltage at a given regulator. + * @reg: regulator id. + * @volt: voltage value; value unit is SoC-dependent, converted from mV by the + * macro UPWR_VOLT_MILIV, or from micro-Volts by the macro UPWR_VOLT_MICROV, + * both macros in upower_soc_defs.h + * @callb: response callback pointer; NULL if no callback needed. + * + * The function requests uPower to change the voltage of the given regulator. + * The request is executed if arguments are within range, with no protections + * regarding the adequate voltage value for the given domain process, + * temperature and frequency. + * + * A callback can be optionally registered, and will be called upon the arrival + * of the request response from the uPower firmware, telling if it succeeded + * or not. + * + * A callback may not be registered (NULL pointer), in which case polling has + * to be used to check the response, by calling upwr_req_status or + * upwr_poll_req_status, using UPWR_SG_PWRMGMT as the service group argument. + * + * Context: no sleep, no locks taken/released. + * Return: 0 if ok, + * -1 if service group is busy, + * -3 if called in an invalid API state + * Note that this is not the error response from the request itself: + * it only tells if the request was successfully sent to the uPower. + */ +int upwr_pwm_chng_reg_voltage(uint32_t reg, uint32_t volt, upwr_callb callb); + +/** + * upwr_pwm_freq_setup() - Determines the next frequency target for a given + * domain and current frequency. + * @domain: identifier of the domain to change frequency. Defined by + * SoC-dependent type soc_domain_t found in upower_soc_defs.h. + * @rail: the pmic regulator number for the target domain. + * @stage: DVA adjust stage + * refer to upower_defs.h "DVA adjust stage" + * @target_freq: the target adjust frequency, accurate to MHz + * + * refer to upower_defs.h structure definition upwr_pwm_freq_msg + * + * @callb: response callback pointer; NULL if no callback needed. + * + * The DVA algorithm is broken down into two phases. + * The first phase uses a look up table to get a safe operating voltage + * for the requested frequency. + * This voltage is guaranteed to work over process and temperature. + * + * The second step of the second phase is to measure the temperature + * using the uPower Temperature Sensor module. + * This is accomplished by doing a binary search of the TSEL bit field + * in the Temperature Measurement Register (TMR). + * The search is repeated until the THIGH bit fields in the same register change value. + * There are 3 temperature sensors in 8ULP (APD, AVD, and RTD). + * + * + * The second phase is the fine adjust of the voltage. + * This stage is entered only when the new frequency requested + * by application was already set as well as the voltage for that frequency. + * The first step of the fine adjust is to find what is the current margins + * for the monitored critical paths, or, in other words, + * how many delay cells will be necessary to generate a setup-timing violation. + * The function informs uPower that the given domain frequency has changed or + * will change to the given value. uPower firmware will then adjust voltage and + * bias to cope with the new frequency (if decreasing) or prepare for it + * (if increasing). The function must be called after decreasing the frequency, + * and before increasing it. The actual increase in frequency must not occur + * before the service returns its response. + * + * So, for increase clock frequency case, user need to call this API twice, + * the first stage gross adjust and the second stage fine adjust. + * + * for reduce clock frequency case, user can only call this API once, + * full stage (combine gross stage and fine adjust) + * + * The request is executed if arguments are within range. + * + * A callback can be optionally registered, and will be called upon the arrival + * of the request response from the uPower firmware, telling if it succeeded + * or not. + * + * A callback may not be registered (NULL pointer), in which case polling has + * to be used to check the response, by calling upwr_req_status or + * upwr_poll_req_status, using UPWR_SG_PWRMGMT as the service group argument. + * + * Context: no sleep, no locks taken/released. + * Return: 0 if ok, + * -1 if service group is busy, + * -3 if called in an invalid API state + * Note that this is not the error response from the request itself: + * it only tells if the request was successfully sent to the uPower. + */ +int upwr_pwm_freq_setup(soc_domain_t domain, uint32_t rail, uint32_t stage, + uint32_t target_freq, upwr_callb callb); + +/** + * upwr_pwm_power_on()- Powers on (not off) one or more switches and ROM/RAMs. + * @swton: pointer to an array of words that tells which power switches to + * turn on. Each word in the array has 1 bit for each switch. + * A bit=1 means the respective switch must be turned on, + * bit = 0 means it will stay unchanged (on or off). + * The pointer may be set to NULL, in which case no switch will be changed, + * unless a memory that it feeds must be turned on. + * WARNING: swton must not point to the first shared memory address. + * @memon: pointer to an array of words that tells which memories to turn on. + * Each word in the array has 1 bit for each switch. + * A bit=1 means the respective memory must be turned on, both array and + * periphery logic; + * bit = 0 means it will stay unchanged (on or off). + * The pointer may be set to NULL, in which case no memory will be changed. + * WARNING: memon must not point to the first shared memory address. + * @callb: pointer to the callback called when configurations are applyed. + * NULL if no callback is required. + * + * The function requests uPower to turn on the PMC and memory array/peripheral + * switches that control their power, as specified above. + * The request is executed if arguments are within range, with no protections + * regarding the adequate memory power state related to overall system state. + * + * If a memory is requested to turn on, but the power switch that feeds that + * memory is not, the power switch will be turned on anyway, if the pwron + * array is not provided (that is, if pwron is NULL). + * + * A callback can be optionally registered, and will be called upon the arrival + * of the request response from the uPower firmware, telling if it succeeded + * or not. + * + * A callback may not be registered (NULL pointer), in which case polling has + * to be used to check the response, by calling upwr_req_status or + * upwr_poll_req_status, using UPWR_SG_PWRMGMT as the service group argument. + * + * Callback or polling may return error if the service contends for a resource + * already being used by a power mode transition or an ongoing service in + * another domain. + * + * Context: no sleep, no locks taken/released. + * Return: 0 if ok, -1 if service group is busy, + * -2 if a pointer conversion to physical address failed, + * -3 if called in an invalid API state. + * Note that this is not the error response from the request itself: + * it only tells if the request was successfully sent to the uPower. + */ +int upwr_pwm_power_on(const uint32_t swton[], const uint32_t memon[], + upwr_callb callb); + +/** + * upwr_pwm_power_off()- Powers off (not on) one or more switches and ROM/RAMs. + * @swtoff: pointer to an array of words that tells which power switches to + * turn off. Each word in the array has 1 bit for each switch. + * A bit=1 means the respective switch must be turned off, + * bit = 0 means it will stay unchanged (on or off). + * The pointer may be set to NULL, in which case no switch will be changed. + * WARNING: swtoff must not point to the first shared memory address. + * @memoff: pointer to an array of words that tells which memories to turn off. + * Each word in the array has 1 bit for each switch. + * A bit=1 means the respective memory must be turned off, both array and + * periphery logic; + * bit = 0 means it will stay unchanged (on or off). + * The pointer may be set to NULL, in which case no memory will be changed, + * but notice it may be turned off if the switch that feeds it is powered off. + * WARNING: memoff must not point to the first shared memory address. + * @callb: pointer to the callback called when configurations are applyed. + * NULL if no callback is required. + * + * The function requests uPower to turn off the PMC and memory array/peripheral + * switches that control their power, as specified above. + * The request is executed if arguments are within range, with no protections + * regarding the adequate memory power state related to overall system state. + * + * A callback can be optionally registered, and will be called upon the arrival + * of the request response from the uPower firmware, telling if it succeeded + * or not. + * + * A callback may not be registered (NULL pointer), in which case polling has + * to be used to check the response, by calling upwr_req_status or + * upwr_poll_req_status, using UPWR_SG_PWRMGMT as the service group argument. + * + * Callback or polling may return error if the service contends for a resource + * already being used by a power mode transition or an ongoing service in + * another domain. + * + * Context: no sleep, no locks taken/released. + * Return: 0 if ok, -1 if service group is busy, + * -2 if a pointer conversion to physical address failed, + * -3 if called in an invalid API state. + * Note that this is not the error response from the request itself: + * it only tells if the request was successfully sent to the uPower. + */ +int upwr_pwm_power_off(const uint32_t swtoff[], const uint32_t memoff[], + upwr_callb callb); + +/** + * upwr_pwm_mem_retain()- Configures one or more memory power switches to + * retain its contents, having the power array on, while its peripheral logic + * is turned off. + * @mem: pointer to an array of words that tells which memories to put in a + * retention state. Each word in the array has 1 bit for each memory. + * A bit=1 means the respective memory must be put in retention state, + * bit = 0 means it will stay unchanged (retention, fully on or off). + * @callb: pointer to the callback called when configurations are applyed. + * NULL if no callback is required. + * + * The function requests uPower to turn off the memory peripheral and leave + * its array on, as specified above. + * The request is executed if arguments are within range. + * + * A callback can be optionally registered, and will be called upon the arrival + * of the request response from the uPower firmware, telling if it succeeded + * or not. + * + * A callback may not be registered (NULL pointer), in which case polling has + * to be used to check the response, by calling upwr_req_status or + * upwr_poll_req_status, using UPWR_SG_PWRMGMT as the service group argument. + * + * Callback or polling may return error if the service contends for a resource + * already being used by a power mode transition or an ongoing service in + * another domain. + * + * Context: no sleep, no locks taken/released. + * Return: 0 if ok, -1 if service group is busy, + * -2 if a pointer conversion to physical address failed, + * -3 if called in an invalid API state. + * Note that this is not the error response from the request itself: + * it only tells if the request was successfully sent to the uPower. + */ +int upwr_pwm_mem_retain(const uint32_t mem[], upwr_callb callb); + +/** + * upwr_pwm_chng_switch_mem() - Turns on/off power on one or more PMC switches + * and memories, including their array and peripheral logic. + * @swt: pointer to a list of PMC switches to be opened/closed. + * The list is structured as an array of struct upwr_switch_board_t + * (see upower_defs.h), each one containing a word for up to 32 switches, + * one per bit. A bit = 1 means switch closed, bit = 0 means switch open. + * struct upwr_switch_board_t also specifies a mask with 1 bit for each + * respective switch: mask bit = 1 means the open/close action is applied, + * mask bit = 0 means the switch stays unchanged. + * The pointer may be set to NULL, in which case no switch will be changed, + * unless a memory that it feeds must be turned on. + * WARNING: swt must not point to the first shared memory address. + * @mem: pointer to a list of switches to be turned on/off. + * The list is structured as an array of struct upwr_mem_switches_t + * (see upower_defs.h), each one containing 2 word for up to 32 switches, + * one per bit, one word for the RAM array power switch, other for the + * RAM peripheral logic power switch. A bit = 1 means switch closed, + * bit = 0 means switch open. + * struct upwr_mem_switches_t also specifies a mask with 1 bit for each + * respective switch: mask bit = 1 means the open/close action is applied, + * mask bit = 0 means the switch stays unchanged. + * The pointer may be set to NULL, in which case no memory switch will be + * changed, but notice it may be turned off if the switch that feeds it is + * powered off. + * WARNING: mem must not point to the first shared memory address. + * @callb: pointer to the callback called when the configurations are applied. + * NULL if no callback is required. + * + * The function requests uPower to change the PMC switches and/or memory power + * as specified above. + * The request is executed if arguments are within range, with no protections + * regarding the adequate switch combinations and overall system state. + * + * If a memory is requested to turn on, but the power switch that feeds that + * memory is not, the power switch will be turned on anyway, if the swt + * array is not provided (that is, if swt is NULL). + * + * A callback can be optionally registered, and will be called upon the arrival + * of the request response from the uPower firmware, telling if it succeeded + * or not. + * + * A callback may not be registered (NULL pointer), in which case polling has + * to be used to check the response, by calling upwr_req_status or + * upwr_poll_req_status, using UPWR_SG_PWRMGMT as the service group argument. + * + * Callback or polling may return error if the service contends for a resource + * already being used by a power mode transition or an ongoing service in + * another domain. + * + * Context: no sleep, no locks taken/released. + * Return: 0 if ok, -1 if service group is busy. + * -2 if a pointer conversion to physical address failed, + * -3 if called in an invalid API state. + * Note that this is not the error response from the request itself: + * it only tells if the request was successfully sent to the uPower. + */ +int upwr_pwm_chng_switch_mem(const struct upwr_switch_board_t swt[], + const struct upwr_mem_switches_t mem[], + upwr_callb callb); + +/** + * upwr_pwm_pmode_config() - Configures a given power mode in a given domain. + * @domain: identifier of the domain to which the power mode belongs. + * Defined by SoC-dependent type soc_domain_t found in upower_soc_defs.h. + * @pmode: SoC-dependent power mode identifier defined by type abs_pwr_mode_t + * found in upower_soc_defs.h. + * @config: pointer to an SoC-dependent struct defining the power mode + * configuration, found in upower_soc_defs.h. + * @callb: pointer to the callback called when configurations are applied. + * NULL if no callback is required. + * + * The function requests uPower to change the power mode configuration as + * specified above. The request is executed if arguments are within range, + * and complies with SoC-dependent restrictions on value combinations. + * + * A callback can be optionally registered, and will be called upon the arrival + * of the request response from the uPower firmware, telling if it succeeded + * or not. + * + * A callback may not be registered (NULL pointer), in which case polling has + * to be used to check the response, by calling upwr_req_status or + * upwr_poll_req_status, using UPWR_SG_PWRMGMT as the service group argument. + * + * Context: no sleep, no locks taken/released. + * Return: 0 if ok, -1 if service group is busy, + * -2 if the pointer conversion to physical address failed, + * -3 if called in an invalid API state. + * Note that this is not the error response from the request itself: + * it only tells if the request was successfully sent to the uPower. + */ +int upwr_pwm_pmode_config(soc_domain_t domain, abs_pwr_mode_t pmode, + const void *config, upwr_callb callb); + + + +/** + * upwr_pwm_reg_config() - Configures the uPower internal regulators. + * @config: pointer to the struct defining the regulator configuration; + * the struct upwr_reg_config_t is defined in the file upower_defs.h. + * @callb: pointer to the callback called when configurations are applied. + * NULL if no callback is required. + * + * The function requests uPower to change/define the configurations of the + * internal regulators. + * + * A callback can be optionally registered, and will be called upon the arrival + * of the request response from the uPower firmware, telling if it succeeded + * or not. + * + * A callback may not be registered (NULL pointer), in which case polling has + * to be used to check the response, by calling upwr_req_status or + * upwr_poll_req_status, using UPWR_SG_PWRMGMT as the service group argument. + * + * The service may fail with error UPWR_RESP_RESOURCE if a power mode transition + * or the same service (called from another domain) is executing simultaneously. + * This error should be interpreted as a "try later" response, as the service + * will succeed once those concurrent executions are done, and no other is + * started. + * + * Context: no sleep, no locks taken/released. + * Return: 0 if ok, -1 if service group is busy, + * -2 if the pointer conversion to physical address failed, + * -3 if called in an invalid API state. + * Note that this is not the error response from the request itself: + * it only tells if the request was successfully sent to the uPower. + */ +int upwr_pwm_reg_config(const struct upwr_reg_config_t *config, + upwr_callb callb); + +/** + * upwr_pwm_chng_dom_bias() - Changes the domain bias. + * @bias: pointer to a domain bias configuration struct (see upower_soc_defs.h). + * @callb: pointer to the callback called when configurations are applied. + * NULL if no callback is required. + * + * The function requests uPower to change the domain bias configuration as + * specified above. The request is executed if arguments are within range, + * with no protections regarding the adequate value combinations and + * overall system state. + * + * A callback can be optionally registered, and will be called upon the arrival + * of the request response from the uPower firmware, telling if it succeeded + * or not. + * + * A callback may not be registered (NULL pointer), in which case polling has + * to be used to check the response, by calling upwr_req_status or + * upwr_poll_req_status, using UPWR_SG_PWRMGMT as the service group argument. + * + * Context: no sleep, no locks taken/released. + * Return: 0 if ok, -1 if service group is busy, + * -3 if called in an invalid API state. + * Note that this is not the error response from the request itself: + * it only tells if the request was successfully sent to the uPower. + */ + +int upwr_pwm_chng_dom_bias(const struct upwr_dom_bias_cfg_t *bias, + upwr_callb callb); + +/** + * upwr_pwm_chng_mem_bias()- Changes a ROM/RAM power bias. + * @domain: identifier of the domain upon which the bias is applied. + * Defined by SoC-dependent type soc_domain_t found in upower_soc_defs.h. + * @bias: pointer to a memory bias configuration struct (see upower_soc_defs.h). + * @callb: pointer to the callback called when configurations are applied. + * NULL if no callback is required. + * + * The function requests uPower to change the memory bias configuration as + * specified above. The request is executed if arguments are within range, + * with no protections regarding the adequate value combinations and + * overall system state. + * + * A callback can be optionally registered, and will be called upon the arrival + * of the request response from the uPower firmware, telling if it succeeded + * or not. + * + * A callback may not be registered (NULL pointer), in which case polling has + * to be used to check the response, by calling upwr_req_status or + * upwr_poll_req_status, using UPWR_SG_PWRMGMT as the service group argument. + * + * Context: no sleep, no locks taken/released. + * Return: 0 if ok, -1 if service group is busy, + * -3 if called in an invalid API state. + * Note that this is not the error response from the request itself: + * it only tells if the request was successfully sent to the uPower. + */ + +int upwr_pwm_chng_mem_bias(soc_domain_t domain, + const struct upwr_mem_bias_cfg_t *bias, + upwr_callb callb); + +/**--------------------------------------------------------------- + * VOLTAGE MANAGEMENT SERVICE GROUP + */ + +/** + * upwr_vtm_pmic_cold_reset() -request cold reset the pmic. + * pmic will power cycle all the regulators + * @callb: response callback pointer; NULL if no callback needed. + * + * The function requests uPower to cold reset the pmic. + * The request is executed if arguments are within range, with no protections + * regarding the adequate voltage value for the given domain process, + * temperature and frequency. + * + * A callback can be optionally registered, and will be called upon the arrival + * of the request response from the uPower firmware, telling if it succeeded + * or not. + * + * A callback may not be registered (NULL pointer), in which case polling has + * to be used to check the response, by calling upwr_req_status or + * upwr_poll_req_status, using UPWR_SG_VOLTM as the service group argument. + * + * Context: no sleep, no locks taken/released. + * Return: 0 if ok, + * -1 if service group is busy, + * -3 if called in an invalid API state + * Note that this is not the error response from the request itself: + * it only tells if the request was successfully sent to the uPower. + */ +int upwr_vtm_pmic_cold_reset(upwr_callb callb); + +/** + * upwr_vtm_set_pmic_mode() -request uPower set pmic mode + * @pmic_mode: the target mode need to be set + * @callb: response callback pointer; NULL if no callback needed. + * + * The function requests uPower to set pmic mode + * The request is executed if arguments are within range, with no protections + * regarding the adequate voltage value for the given domain process, + * temperature and frequency. + * + * A callback can be optionally registered, and will be called upon the arrival + * of the request response from the uPower firmware, telling if it succeeded + * or not. + * + * A callback may not be registered (NULL pointer), in which case polling has + * to be used to check the response, by calling upwr_req_status or + * upwr_poll_req_status, using UPWR_SG_VOLTM as the service group argument. + * + * Context: no sleep, no locks taken/released. + * Return: 0 if ok, + * -1 if service group is busy, + * -3 if called in an invalid API state + * Note that this is not the error response from the request itself: + * it only tells if the request was successfully sent to the uPower. + */ +int upwr_vtm_set_pmic_mode(uint32_t pmic_mode, upwr_callb callb); + +/** + * upwr_vtm_chng_pmic_voltage() - Changes the voltage of a given rail. + * @rail: pmic rail id. + * @volt: the target voltage of the given rail, accurate to uV + * If pass volt value 0, means that power off this rail. + * @callb: response callback pointer; NULL if no callback needed. + * + * The function requests uPower to change the voltage of the given rail. + * The request is executed if arguments are within range, with no protections + * regarding the adequate voltage value for the given domain process, + * temperature and frequency. + * + * A callback can be optionally registered, and will be called upon the arrival + * of the request response from the uPower firmware, telling if it succeeded + * or not. + * + * A callback may not be registered (NULL pointer), in which case polling has + * to be used to check the response, by calling upwr_req_status or + * upwr_poll_req_status, using UPWR_SG_VOLTM as the service group argument. + * + * Context: no sleep, no locks taken/released. + * Return: 0 if ok, + * -1 if service group is busy, + * -3 if called in an invalid API state + * Note that this is not the error response from the request itself: + * it only tells if the request was successfully sent to the uPower. + */ +int upwr_vtm_chng_pmic_voltage(uint32_t rail, uint32_t volt, upwr_callb callb); + +/** + * upwr_vtm_get_pmic_voltage() - Get the voltage of a given ral. + * @rail: pmic rail id. + * @callb: response callback pointer; NULL if no callback needed. + * (polling used instead) + * + * The function requests uPower to get the voltage of the given rail. + * The request is executed if arguments are within range, with no protections + * regarding the adequate voltage value for the given domain process, + * temperature and frequency. + * + * A callback can be optionally registered, and will be called upon the arrival + * of the request response from the uPower firmware, telling if it succeeded + * or not. + * + * A callback may not be registered (NULL pointer), in which case polling has + * to be used to check the response, by calling upwr_req_status or + * upwr_poll_req_status, using UPWR_SG_VOLTM as the service group argument. + * + * The voltage data read from uPower via + * the callback argument ret, or written to the variable pointed by retptr, + * if polling is used (calls upwr_req_status or upwr_poll_req_status). + * ret (or *retptr) also returns the data written on writes. + * + * Context: no sleep, no locks taken/released. + * Return: 0 if ok, + * -1 if service group is busy, + * -3 if called in an invalid API state + * Note that this is not the error response from the request itself: + * it only tells if the request was successfully sent to the uPower. + */ +int upwr_vtm_get_pmic_voltage(uint32_t rail, upwr_callb callb); + + +/** + * upwr_vtm_power_measure() - request uPower to measure power consumption + * @ssel: This field determines which power switches will have their currents + * sampled to be accounted for a + * current/power measurement. Support 0~7 + + * SSEL bit # Power Switch + * 0 M33 core complex/platform/peripherals + * 1 Fusion Core and Peripherals + * 2 A35[0] core complex + * 3 A35[1] core complex + * 4 3DGPU + * 5 HiFi4 + * 6 DDR Controller (PHY and PLL NOT included) + * 7 PXP, EPDC + * + * @callb: response callback pointer; NULL if no callback needed. + * (polling used instead) + * + * The function requests uPower to measure power consumption + * The request is executed if arguments are within range, with no protections + * regarding the adequate voltage value for the given domain process, + * temperature and frequency. + * + * A callback can be optionally registered, and will be called upon the arrival + * of the request response from the uPower firmware, telling if it succeeded + * or not. + * + * A callback may not be registered (NULL pointer), in which case polling has + * to be used to check the response, by calling upwr_req_status or + * upwr_poll_req_status, using UPWR_SG_VOLTM as the service group argument. + * + * The power consumption data read from uPower via + * the callback argument ret, or written to the variable pointed by retptr, + * if polling is used (calls upwr_req_status or upwr_poll_req_status). + * ret (or *retptr) also returns the data written on writes. + * upower fw needs support cocurrent request from M33 and A35. + * + * Accurate to uA + * + * Context: no sleep, no locks taken/released. + * Return: 0 if ok, + * -1 if service group is busy, + * -3 if called in an invalid API state + * Note that this is not the error response from the request itself: + * it only tells if the request was successfully sent to the uPower. + */ +int upwr_vtm_power_measure(uint32_t ssel, upwr_callb callb); + +/** + * upwr_vtm_vmeter_measure() - request uPower to measure voltage + * @vdetsel: Voltage Detector Selector, support 0~3 + * 00b - RTD sense point + * 01b - LDO output + * 10b - APD domain sense point + * 11b - AVD domain sense point + * Refer to upower_defs.h + * @callb: response callback pointer; NULL if no callback needed. + * (polling used instead) + * + * The function requests uPower to use vmeter to measure voltage + * The request is executed if arguments are within range, with no protections + * regarding the adequate voltage value for the given domain process, + * temperature and frequency. + * + * A callback can be optionally registered, and will be called upon the arrival + * of the request response from the uPower firmware, telling if it succeeded + * or not. + * + * A callback may not be registered (NULL pointer), in which case polling has + * to be used to check the response, by calling upwr_req_status or + * upwr_poll_req_status, using UPWR_SG_VOLTM as the service group argument. + * + * The voltage data read from uPower via + * the callback argument ret, or written to the variable pointed by retptr, + * if polling is used (calls upwr_req_status or upwr_poll_req_status). + * ret (or *retptr) also returns the data written on writes. + * upower fw needs support cocurrent request from M33 and A35. + * + * Refer to RM COREREGVL (Core Regulator Voltage Level) + * uPower return VDETLVL to user, user can calculate the real voltage: + * + * 0b000000(0x00) - 0.595833V + * 0b100110(0x26) - 1.007498V + * <value> - 0.595833V + <value>x10.8333mV + * 0b110010(0x32) - 1.138V + * + * Context: no sleep, no locks taken/released. + * Return: 0 if ok, + * -1 if service group is busy, + * -3 if called in an invalid API state + * Note that this is not the error response from the request itself: + * it only tells if the request was successfully sent to the uPower. + */ +int upwr_vtm_vmeter_measure(uint32_t vdetsel, upwr_callb callb); + +/** + * upwr_vtm_pmic_config() - Configures the SoC PMIC (Power Management IC). + * @config: pointer to a PMIC-dependent struct defining the PMIC configuration. + * @size: size of the struct pointed by config, in bytes. + * @callb: pointer to the callback called when configurations are applied. + * NULL if no callback is required. + * + * The function requests uPower to change/define the PMIC configuration. + * + * A callback can be optionally registered, and will be called upon the arrival + * of the request response from the uPower firmware, telling if it succeeded + * or not. + * + * A callback may not be registered (NULL pointer), in which case polling has + * to be used to check the response, by calling upwr_req_status or + * upwr_poll_req_status, using UPWR_SG_PWRMGMT as the service group argument. + * + * Context: no sleep, no locks taken/released. + * Return: 0 if ok, -1 if service group is busy, + * -2 if the pointer conversion to physical address failed, + * -3 if called in an invalid API state. + * Note that this is not the error response from the request itself: + * it only tells if the request was successfully sent to the uPower. + */ +int upwr_vtm_pmic_config(const void *config, uint32_t size, upwr_callb callb); + +/**--------------------------------------------------------------- + * TEMPERATURE MANAGEMENT SERVICE GROUP + */ + +/** + * upwr_tpm_get_temperature() - request uPower to get temperature of one temperature sensor + * @sensor_id: temperature sensor ID, support 0~2 + * @callb: response callback pointer; NULL if no callback needed. + * (polling used instead) + * + * The function requests uPower to measure temperature + * The request is executed if arguments are within range, with no protections + * regarding the adequate voltage value for the given domain process, + * temperature and frequency. + * + * A callback can be optionally registered, and will be called upon the arrival + * of the request response from the uPower firmware, telling if it succeeded + * or not. + * + * A callback may not be registered (NULL pointer), in which case polling has + * to be used to check the response, by calling upwr_req_status or + * upwr_poll_req_status, using UPWR_SG_TEMPM as the service group argument. + * + * The temperature data read from uPower via + * the callback argument ret, or written to the variable pointed by retptr, + * if polling is used (calls upwr_req_status or upwr_poll_req_status). + * ret (or *retptr) also returns the data written on writes. + * + * uPower return TSEL to the caller (M33 or A35), caller calculate the real temperature + * Tsh = 0.000002673049*TSEL[7:0]^3 + 0.0003734262*TSEL[7:0]^2 + +0.4487042*TSEL[7:0] - 46.98694 + * + * upower fw needs support cocurrent request from M33 and A35. + * + * Context: no sleep, no locks taken/released. + * Return: 0 if ok, + * -1 if service group is busy, + * -3 if called in an invalid API state + * Note that this is not the error response from the request itself: + * it only tells if the request was successfully sent to the uPower. + */ +int upwr_tpm_get_temperature(uint32_t sensor_id, upwr_callb callb); + +/**--------------------------------------------------------------- + * DELAY MANAGEMENT SERVICE GROUP + */ + +/** + * upwr_dlm_get_delay_margin() - request uPower to get delay margin + * @path: The critical path + * @index: Use whitch delay meter + * @callb: response callback pointer; NULL if no callback needed. + * (polling used instead) + * + * The function requests uPower to get delay margin + * The request is executed if arguments are within range, with no protections + * regarding the adequate voltage value for the given domain process, + * temperature and frequency. + * + * A callback can be optionally registered, and will be called upon the arrival + * of the request response from the uPower firmware, telling if it succeeded + * or not. + * + * A callback may not be registered (NULL pointer), in which case polling has + * to be used to check the response, by calling upwr_req_status or + * upwr_poll_req_status, using UPWR_SG_DELAYM as the service group argument. + * + * The delay margin data read from uPower via + * the callback argument ret, or written to the variable pointed by retptr, + * if polling is used (calls upwr_req_status or upwr_poll_req_status). + * ret (or *retptr) also returns the data written on writes. + * upower fw needs support cocurrent request from M33 and A35. + * + * Context: no sleep, no locks taken/released. + * Return: 0 if ok, + * -1 if service group is busy, + * -3 if called in an invalid API state + * Note that this is not the error response from the request itself: + * it only tells if the request was successfully sent to the uPower. + */ +int upwr_dlm_get_delay_margin(uint32_t path, uint32_t index, upwr_callb callb); + +/** + * upwr_dlm_set_delay_margin() - request uPower to set delay margin + * @path: The critical path + * @index: Use whitch delay meter + * @delay_margin: the value of delay margin + * @callb: response callback pointer; NULL if no callback needed. + * (polling used instead) + * + * The function requests uPower to set delay margin + * The request is executed if arguments are within range, with no protections + * regarding the adequate voltage value for the given domain process, + * temperature and frequency. + * + * A callback can be optionally registered, and will be called upon the arrival + * of the request response from the uPower firmware, telling if it succeeded + * or not. + * + * A callback may not be registered (NULL pointer), in which case polling has + * to be used to check the response, by calling upwr_req_status or + * upwr_poll_req_status, using UPWR_SG_DELAYM as the service group argument. + * + * The result of the corresponding critical path, failed or not read from uPower via + * the callback argument ret, or written to the variable pointed by retptr, + * if polling is used (calls upwr_req_status or upwr_poll_req_status). + * ret (or *retptr) also returns the data written on writes. + * upower fw needs support cocurrent request from M33 and A35. + * + * Context: no sleep, no locks taken/released. + * Return: 0 if ok, + * -1 if service group is busy, + * -3 if called in an invalid API state + * Note that this is not the error response from the request itself: + * it only tells if the request was successfully sent to the uPower. + */ +int upwr_dlm_set_delay_margin(uint32_t path, uint32_t index, uint32_t delay_margin, upwr_callb callb); + +/** + * upwr_dlm_process_monitor() - request uPower to do process monitor + * @chain_sel: Chain Cell Type Selection + * Select the chain to be used for the clock signal generation. + * Support two types chain cell, 0~1 +0b - P4 type delay cells selected +1b - P16 type delay cells selected + * @callb: response callback pointer; NULL if no callback needed. + * (polling used instead) + * + * The function requests uPower to do process monitor + * The request is executed if arguments are within range, with no protections + * regarding the adequate voltage value for the given domain process, + * temperature and frequency. + * + * A callback can be optionally registered, and will be called upon the arrival + * of the request response from the uPower firmware, telling if it succeeded + * or not. + * + * A callback may not be registered (NULL pointer), in which case polling has + * to be used to check the response, by calling upwr_req_status or + * upwr_poll_req_status, using UPWR_SG_DELAYM as the service group argument. + * + * The result of process monitor, failed or not read from uPower via + * the callback argument ret, or written to the variable pointed by retptr, + * if polling is used (calls upwr_req_status or upwr_poll_req_status). + * ret (or *retptr) also returns the data written on writes. + * upower fw needs support cocurrent request from M33 and A35. + * + * Context: no sleep, no locks taken/released. + * Return: 0 if ok, + * -1 if service group is busy, + * -3 if called in an invalid API state + * Note that this is not the error response from the request itself: + * it only tells if the request was successfully sent to the uPower. + */ +int upwr_dlm_process_monitor(uint32_t chain_sel, upwr_callb callb); + +/**--------------------------------------------------------------- + * DIAGNOSE SERVICE GROUP + */ + +/** + * upwr_dgn_mode() - Sets the diagnostic mode. + * @mode: diagnostic mode, which can be: + * - UPWR_DGN_NONE: no diagnostic recorded + * - UPWR_DGN_TRACE: warnings, errors, service, internal activity recorded + * - UPWR_DGN_SRVREQ: warnings, errors, service activity recorded + * - UPWR_DGN_WARN: warnings and errors recorded + * - UPWR_DGN_ALL: trace, service, warnings, errors, task state recorded + * - UPWR_DGN_ERROR: only errors recorded + * - UPWR_DGN_ALL2ERR: record all until an error occurs, + * freeze recording on error + * - UPWR_DGN_ALL2HLT: record all until an error occurs, + * executes an ebreak on error, which halts the core if enabled through + * the debug interface + * @callb: pointer to the callback called when mode is changed. + * NULL if no callback is required. + * + * Context: no sleep, no locks taken/released. + * Return: 0 if ok, + * -1 if service group is busy, + * -3 if called in an invalid API state + */ +int upwr_dgn_mode(upwr_dgn_mode_t mode, const upwr_callb callb); + +/**--------------------------------------------------------------- + * AUXILIARY CALLS + */ + +/** + * upwr_rom_version() - informs the ROM firwmware version. + * @vmajor: pointer to the variable to get the firmware major version number. + * @vminor: pointer to the variable to get the firmware minor version number. + * @vfixes: pointer to the variable to get the firmware fixes number. + * + * Context: no sleep, no locks taken/released. + * Return: SoC id. + */ +uint32_t upwr_rom_version(uint32_t *vmajor, uint32_t *vminor, uint32_t *vfixes); + +/** + * upwr_ram_version() - informs the RAM firwmware version. + * @vminor: pointer to the variable to get the firmware minor version number. + * @vfixes: pointer to the variable to get the firmware fixes number. + * + * The 3 values returned are 0 if no RAM firmwmare was loaded and initialized. + * + * Context: no sleep, no locks taken/released. + * Return: firmware major version number. + */ +uint32_t upwr_ram_version(uint32_t *vminor, uint32_t *vfixes); + +/** + * upwr_req_status() - tells the status of the service group request, and + * returns a request return value, if any. + * @sg: service group of the request + * @sgfptr: pointer to the variable that will hold the function id of + * the last request completed; can be NULL, in which case it is not used. + * @errptr: pointer to the variable that will hold the error code; + * can be NULL, in which case it is not used. + * @retptr: pointer to the variable that will hold the value returned + * by the last request completed (invalid if the last request completed didn't + * return any value); can be NULL, in which case it is not used. + * Note that a request may return a value even if service error is returned + * (*errptr != UPWR_RESP_OK): that is dependent on the specific service. + * + * This call can be used in a poll loop of a service request completion in case + * a callback was not registered. + * + * Context: no sleep, no locks taken/released. + * Return: service request status: succeeded, failed, or ongoing (busy) + */ + +/* service request status */ +typedef enum { + UPWR_REQ_OK, /* request succeeded */ + UPWR_REQ_ERR, /* request failed */ + UPWR_REQ_BUSY /* request execution ongoing */ +} upwr_req_status_t; + +upwr_req_status_t upwr_req_status(upwr_sg_t sg, + uint32_t *sgfptr, + upwr_resp_t *errptr, + int *retptr); + +/** + * upwr_poll_req_status() - polls the status of the service group request, and + * returns a request return value, if any. + * @sg: service group of the request + * @sgfptr: pointer to the variable that will hold the function id of + * the last request completed; can be NULL, in which case it is not used. + * @errptr: pointer to the variable that will hold the error code; + * can be NULL, in which case it is not used. + * @retptr: pointer to the variable that will hold the value returned + * by the last request completed (invalid if the last request completed didn't + * return any value); can be NULL, in which case it is not used. + * Note that a request may return a value even if service error is returned + * (*errptr != UPWR_RESP_OK): that is dependent on the specific service. + * @attempts: maximum number of polling attempts; if attempts > 0 and is + * reached with no service response received, upwr_poll_req_status returns + * UPWR_REQ_BUSY and variables pointed by sgfptr, retptr and errptr are not + * updated; if attempts = 0, upwr_poll_req_status waits "forever". + * + * This call can be used to poll a service request completion in case a + * callback was not registered. + * + * Context: no sleep, no locks taken/released. + * Return: service request status: succeeded, failed, or ongoing (busy) + */ +upwr_req_status_t upwr_poll_req_status(upwr_sg_t sg, + uint32_t *sgfptr, + upwr_resp_t *errptr, + int *retptr, + uint32_t attempts); + +/** + * upwr_alarm_code() - returns the alarm code of the last alarm occurrence. + * + * The value returned is not meaningful if no alarm was issued by uPower. + * + * Context: no sleep, no locks taken/released. + * Return: alarm code, as defined by the type upwr_alarm_t in upwr_soc_defines.h + */ +upwr_alarm_t upwr_alarm_code(void); + +/**--------------------------------------------------------------- + * TRANSMIT/RECEIVE PRIMITIVES + * --------------------------------------------------------------- + */ + +typedef void (*UPWR_TX_CALLB_FUNC_T)(void); +typedef void (*UPWR_RX_CALLB_FUNC_T)(void); + +/** + * upwr_tx() - queues a message for transmission. + * @msg : pointer to the message sent. + * @size: message size in 32-bit words + * @callback: pointer to a function to be called when transmission done; + * can be NULL, in which case no callback is done. + * + * This is an auxiliary function used by the rest of the API calls. + * It is normally not called by the driver code, unless maybe for test purposes. + * + * Context: no sleep, no locks taken/released. + * Return: number of vacant positions left in the transmission queue, or + * -1 if the queue was already full when upwr_tx was called, or + * -2 if any argument is invalid (like size off-range) + */ +int upwr_tx(const uint32_t *msg, unsigned int size, + UPWR_TX_CALLB_FUNC_T callback); + +/** + * upwr_rx() - unqueues a received message from the reception queue. + * @msg: pointer to the message destination buffer. + * @size: pointer to variable to hold message size in 32-bit words. + * + * This is an auxiliary function used by the rest of the API calls. + * It is normally not called by the driver code, unless maybe for test purposes. + * + * Context: no sleep, no locks taken/released. + * Return: number of messages remaining in the reception queue, or + * -1 if the queue was already empty when upwr_rx was called, or + * -2 if any argument is invalid (like mu off-range) + */ +int upwr_rx(char *msg, unsigned int *size); + +/** + * upwr_rx_callback() - sets up a callback for a message receiving event. + * @callback: pointer to a function to be called when a message arrives; + * can be NULL, in which case no callback is done. + * + * This is an auxiliary function used by the rest of the API calls. + * It is normally not called by the driver code, unless maybe for test purposes. + * + * Context: no sleep, no locks taken/released. + * Return: 0 if ok; -2 if any argument is invalid (mu off-range). + */ +int upwr_rx_callback(UPWR_RX_CALLB_FUNC_T callback); + +/** + * msg_copy() - copies a message. + * @dest: pointer to the destination message. + * @src : pointer to the source message. + * @size: message size in words. + * + * This is an auxiliary function used by the rest of the API calls. + * It is normally not called by the driver code, unless maybe for test purposes. + * + * Context: no sleep, no locks taken/released. + * Return: none (void) + */ +void msg_copy(char *dest, char *src, unsigned int size); + +#endif /* UPWR_API_H */ diff --git a/plat/imx/imx8ulp/upower/upower_defs.h b/plat/imx/imx8ulp/upower/upower_defs.h new file mode 100644 index 00000000..118d7e0c --- /dev/null +++ b/plat/imx/imx8ulp/upower/upower_defs.h @@ -0,0 +1,742 @@ +/* SPDX-License-Identifier: BSD-3-Clause */ +/** + * Copyright 2019-2024 NXP + * + * KEYWORDS: micro-power uPower driver API + * ----------------------------------------------------------------------------- + * PURPOSE: uPower driver API #defines and typedefs shared with the firmware + * ----------------------------------------------------------------------------- + * PARAMETERS: + * PARAM NAME RANGE:DESCRIPTION: DEFAULTS: UNITS + * ----------------------------------------------------------------------------- + * REUSE ISSUES: no reuse issues + */ + +#ifndef UPWR_DEFS_H +#define UPWR_DEFS_H + +#include <stdint.h> + +#ifndef UPWR_PMC_SWT_WORDS +#define UPWR_PMC_SWT_WORDS (1U) +#endif + +#ifndef UPWR_PMC_MEM_WORDS +#define UPWR_PMC_MEM_WORDS (2U) +#endif + +/* **************************************************************************** + * DOWNSTREAM MESSAGES - COMMANDS/FUNCTIONS + * **************************************************************************** + */ +#define UPWR_SRVGROUP_BITS (4U) +#define UPWR_FUNCTION_BITS (4U) +#define UPWR_PWDOMAIN_BITS (4U) +#define UPWR_HEADER_BITS \ + (UPWR_SRVGROUP_BITS + UPWR_FUNCTION_BITS + UPWR_PWDOMAIN_BITS) +#define UPWR_ARG_BITS (32U - UPWR_HEADER_BITS) +#if ((UPWR_ARG_BITS & 1U) > 0U) +#error "UPWR_ARG_BITS must be an even number" +#endif +#define UPWR_ARG64_BITS (64U - UPWR_HEADER_BITS) +#define UPWR_HALF_ARG_BITS (UPWR_ARG_BITS >> 1U) +#define UPWR_DUAL_OFFSET_BITS ((UPWR_ARG_BITS + 32U) >> 1U) + +/* + * message header: header fields common to all downstream messages. + */ +struct upwr_msg_hdr { + uint32_t domain : UPWR_PWDOMAIN_BITS; /* power domain */ + uint32_t srvgrp : UPWR_SRVGROUP_BITS; /* service group */ + uint32_t function : UPWR_FUNCTION_BITS; /* function */ + uint32_t arg : UPWR_ARG_BITS; /* function-specific argument */ +}; + +/* generic 1-word downstream message format */ +typedef union { + struct upwr_msg_hdr hdr; + uint32_t word; /* message first word */ +} upwr_down_1w_msg; + +/* generic 2-word downstream message format */ +typedef struct { + struct upwr_msg_hdr hdr; + uint32_t word2; /* message second word */ +} upwr_down_2w_msg; + +/* message format for functions that receive a pointer/offset */ +typedef struct { + struct upwr_msg_hdr hdr; + uint32_t ptr; /* config struct offset */ +} upwr_pointer_msg; + +/* message format for functions that receive 2 pointers/offsets */ +typedef union { + struct upwr_msg_hdr hdr; + struct { + uint64_t rsv : UPWR_HEADER_BITS; + uint64_t ptr0 : UPWR_DUAL_OFFSET_BITS; + uint64_t ptr1 : UPWR_DUAL_OFFSET_BITS; + } ptrs; +} upwr_2pointer_msg; + +#define UPWR_SG_EXCEPT (0U) /* 0 = exception */ +#define UPWR_SG_PWRMGMT (1U) /* 1 = power management */ +#define UPWR_SG_DELAYM (2U) /* 2 = delay measurement */ +#define UPWR_SG_VOLTM (3U) /* 3 = voltage measurement */ +#define UPWR_SG_CURRM (4U) /* 4 = current measurement */ +#define UPWR_SG_TEMPM (5U) /* 5 = temperature measurement */ +#define UPWR_SG_DIAG (6U) /* 6 = diagnostic */ +#define UPWR_SG_COUNT (7U) + +typedef uint32_t upwr_sg_t; + +/* ************************************************************************* + * Initialization - downstream + ***************************************************************************/ +typedef upwr_down_1w_msg upwr_start_msg; /* start command message */ +typedef upwr_down_1w_msg upwr_power_on_msg; /* power on command message */ +typedef upwr_down_1w_msg upwr_boot_start_msg; /* boot start command message */ +typedef union { + struct upwr_msg_hdr hdr; + upwr_power_on_msg power_on; + upwr_boot_start_msg boot_start; + upwr_start_msg start; +} upwr_startup_down_msg; + +/* ************************************************************************* + * Service Group EXCEPTION - downstream + ***************************************************************************/ + +#define UPWR_XCP_INIT (0U) /* 0 = init msg (not a service request itself) */ +#define UPWR_XCP_PING (0U) /* 0 = also ping request, since its response isan init msg */ +#define UPWR_XCP_START (1U) /* 1 = service start: upwr_start *(not a service request itself) */ +#define UPWR_XCP_SHUTDOWN (2U) /* 2 = service shutdown: upwr_xcp_shutdown */ +#define UPWR_XCP_CONFIG (3U) /* 3 = uPower configuration: upwr_xcp_config */ +#define UPWR_XCP_SW_ALARM (4U) /* 4 = uPower software alarm: upwr_xcp_sw_alarm */ +#define UPWR_XCP_I2C (5U) /* 5 = I2C access: upwr_xcp_i2c_access */ +#define UPWR_XCP_SPARE_6 (6U) /* 6 = spare */ +#define UPWR_XCP_SET_DDR_RETN (7U) /* 7 = set/clear ddr retention */ +#define UPWR_XCP_SET_RTD_APD_LLWU (8U) /* 8 = set/clear rtd/apd llwu */ +#define UPWR_XCP_SPARE_8 (8U) /* 8 = spare */ +#define UPWR_XCP_SET_RTD_USE_DDR (9U) /* 9 = M33 core set it is using DDR or not */ +#define UPWR_XCP_SPARE_9 (9U) /* 9 = spare */ +#define UPWR_XCP_SPARE_10 (10U) /* 10 = spare */ +#define UPWR_XCP_SET_MIPI_DSI_ENA (10U) /* 10 = set/clear mipi dsi ena */ +#define UPWR_XCP_SPARE_11 (11U) /* 11 = spare */ +#define UPWR_XCP_GET_MIPI_DSI_ENA (11U) /* 11 = get mipi dsi ena status */ +#define UPWR_XCP_SPARE_12 (12U) /* 12 = spare */ +#define UPWR_XCP_SET_OSC_MODE (12U) /* 12 = set uPower OSC mode, high or low */ +#define UPWR_XCP_SPARE_13 (13U) /* 13 = spare */ +#define UPWR_XCP_SPARE_14 (14U) /* 14 = spare */ +#define UPWR_XCP_SPARE_15 (15U) /* 15 = spare */ +#define UPWR_XCP_F_COUNT (16U) + +typedef uint32_t upwr_xcp_f_t; +typedef upwr_down_1w_msg upwr_xcp_ping_msg; +typedef upwr_down_1w_msg upwr_xcp_shutdown_msg; +typedef upwr_power_on_msg upwr_xcp_power_on_msg; +typedef upwr_boot_start_msg upwr_xcp_boot_start_msg; +typedef upwr_start_msg upwr_xcp_start_msg; +typedef upwr_down_2w_msg upwr_xcp_config_msg; +typedef upwr_down_1w_msg upwr_xcp_swalarm_msg; +typedef upwr_down_1w_msg upwr_xcp_ddr_retn_msg; +typedef upwr_down_1w_msg upwr_xcp_set_mipi_dsi_ena_msg; +typedef upwr_down_1w_msg upwr_xcp_get_mipi_dsi_ena_msg; +typedef upwr_down_1w_msg upwr_xcp_rtd_use_ddr_msg; +typedef upwr_down_1w_msg upwr_xcp_rtd_apd_llwu_msg; +typedef upwr_down_1w_msg upwr_xcp_set_osc_mode_msg; +typedef upwr_pointer_msg upwr_xcp_i2c_msg; + + /* structure pointed by message upwr_xcp_i2c_msg */ +typedef struct { + uint16_t addr; + int8_t data_size; + uint8_t subaddr_size; + uint32_t subaddr; + uint32_t data; +} upwr_i2c_access; + +/* Exception all messages */ +typedef union { + struct upwr_msg_hdr hdr; /* message header */ + upwr_xcp_ping_msg ping; /* ping */ + upwr_xcp_start_msg start; /* service start */ + upwr_xcp_shutdown_msg shutdown; /* shutdown */ + upwr_xcp_boot_start_msg bootstart; /* boot start */ + upwr_xcp_config_msg config; /* uPower configuration */ + upwr_xcp_swalarm_msg swalarm; /* software alarm */ + upwr_xcp_i2c_msg i2c; /* I2C access */ + upwr_xcp_ddr_retn_msg set_ddr_retn; /* set ddr retention msg */ + upwr_xcp_set_mipi_dsi_ena_msg set_mipi_dsi_ena; /* set mipi dsi ena msg */ + upwr_xcp_get_mipi_dsi_ena_msg get_mipi_dsi_ena; /* get mipi dsi ena msg */ + upwr_xcp_rtd_use_ddr_msg set_rtd_use_ddr; /* set rtd is using ddr msg */ + upwr_xcp_rtd_apd_llwu_msg set_llwu; /* set rtd/apd llwu msg */ + upwr_xcp_set_osc_mode_msg set_osc_mode; /* set osc_mode msg */ +} upwr_xcp_msg; + +/* structure pointed by message upwr_volt_dva_req_id_msg */ +typedef struct { + uint32_t id_word0; + uint32_t id_word1; + uint32_t mode; +} upwr_dva_id_struct; + +/** + * PMIC voltage accuracy is 12.5 mV, 12500 uV + */ +#define PMIC_VOLTAGE_MIN_STEP 12500U + +/* ************************************************************************* + * Service Group POWER MANAGEMENT - downstream + ***************************************************************************/ + +#define UPWR_PWM_REGCFG (0U) /* 0 = regulator config: upwr_pwm_reg_config */ +#define UPWR_PWM_DEVMODE (0U) /* deprecated, for old compile */ +#define UPWR_PWM_VOLT (1U) /* 1 = voltage change: upwr_pwm_chng_reg_voltage */ +#define UPWR_PWM_SWITCH (2U) /* 2 = switch control: upwr_pwm_chng_switch_mem */ +#define UPWR_PWM_PWR_ON (3U) /* 3 = switch/RAM/ROM power on: upwr_pwm_power_on */ +#define UPWR_PWM_PWR_OFF (4U) /* 4 = switch/RAM/ROM power off: upwr_pwm_power_off */ +#define UPWR_PWM_RETAIN (5U) /* 5 = retain memory array: upwr_pwm_mem_retain */ +#define UPWR_PWM_DOM_BIAS (6U) /* 6 = Domain bias control: upwr_pwm_chng_dom_bias */ +#define UPWR_PWM_MEM_BIAS (7U) /* 7 = Memory bias control: upwr_pwm_chng_mem_bias */ +#define UPWR_PWM_PMICCFG (8U) /* 8 = PMIC configuration: upwr_pwm_pmic_config */ +#define UPWR_PWM_PMICMOD (8U) /* deprecated, for old compile */ +#define UPWR_PWM_PES (9U) /* 9 so far, no use */ +#define UPWR_PWM_CONFIG (10U) /* 10= apply power mode defined configuration */ +#define UPWR_PWM_CFGPTR (11U) /* 11= configuration pointer */ +#define UPWR_PWM_DOM_PWRON (12U) /* 12 = domain power on: upwr_pwm_dom_power_on */ +#define UPWR_PWM_BOOT (13U) /* 13 = boot start: upwr_pwm_boot_start */ +#define UPWR_PWM_FREQ (14U) /* 14 = domain frequency setup */ +#define UPWR_PWM_PARAM (15U) /* 15 = power management parameters */ +#define UPWR_PWM_F_COUNT (16U) + +typedef uint32_t upwr_pwm_f_t; + +#define MAX_PMETER_SSEL 7U + +#define UPWR_VTM_CHNG_PMIC_RAIL_VOLT (0U) /* 0 = change pmic rail voltage */ +#define UPWR_VTM_GET_PMIC_RAIL_VOLT (1U) /* 1 = get pmic rail voltage */ +#define UPWR_VTM_PMIC_CONFIG (2U) /* 2 = configure PMIC IC */ +#define UPWR_VTM_DVA_DUMP_INFO (3U) /* 3 = dump dva information */ +#define UPWR_VTM_DVA_REQ_ID (4U) /* 4 = dva request ID array */ +#define UPWR_VTM_DVA_REQ_DOMAIN (5U) /* 5 = dva request domain */ +#define UPWR_VTM_DVA_REQ_SOC (6U) /* 6 = dva request the whole SOC */ +#define UPWR_VTM_PMETER_MEAS (7U) /* 7 = pmeter measure */ +#define UPWR_VTM_VMETER_MEAS (8U) /* 8 = vmeter measure */ +#define UPWR_VTM_PMIC_COLD_RESET (9U) /* 9 = pmic cold reset */ +#define UPWR_VTM_SET_DVFS_PMIC_RAIL (10U) /* 10 = set which domain use which pmic rail, for DVFS use */ +#define UPWR_VTM_SET_PMIC_MODE (11U) /* 11 = set pmic mode */ +#define UPWR_VTM_F_COUNT (16U) + +typedef uint32_t upwr_volt_f_t; + +#define VMETER_SEL_RTD 0U +#define VMETER_SEL_LDO 1U +#define VMETER_SEL_APD 2U +#define VMETER_SEL_AVD 3U +#define VMETER_SEL_MAX 3U + +/** + * The total TSEL count is 256 + */ +#define MAX_TEMP_TSEL 256U + +/** + * Support 3 temperature sensor, sensor 0, 1, 2 + */ +#define MAX_TEMP_SENSOR 2U + +#define UPWR_TEMP_GET_CUR_TEMP (0U) /* 0 = get current temperature */ +#define UPWR_TEMP_F_COUNT (1U) +typedef uint32_t upwr_temp_f_t; + +#define UPWR_DMETER_GET_DELAY_MARGIN (0U) /* 0 = get delay margin */ +#define UPWR_DMETER_SET_DELAY_MARGIN (1U) /* 1 = set delay margin */ +#define UPWR_PMON_REQ (2U) /* 2 = process monitor service */ +#define UPWR_DMETER_F_COUNT (3U) + +typedef uint32_t upwr_dmeter_f_t; + +typedef upwr_down_1w_msg upwr_volt_pmeter_meas_msg; +typedef upwr_down_1w_msg upwr_volt_pmic_set_mode_msg; +typedef upwr_down_1w_msg upwr_volt_vmeter_meas_msg; + +struct upwr_reg_config_t { + uint32_t reg; +}; + + /* set of 32 switches */ +struct upwr_switch_board_t { + uint32_t on; /* Switch on state,1 bit per instance */ + uint32_t mask; /* actuation mask, 1 bit per instance */ +}; + + /* set of 32 RAM/ROM switches */ +struct upwr_mem_switches_t { + uint32_t array; /* RAM/ROM array state, 1 bit per instance */ + uint32_t perif; /* RAM/ROM peripheral state, 1 bit per instance */ + uint32_t mask; /* actuation mask, 1 bit per instance */ +}; + +typedef upwr_down_1w_msg upwr_pwm_dom_pwron_msg; /* domain power on message */ +typedef upwr_down_1w_msg upwr_pwm_boot_start_msg; /* boot start message */ + +/* functions with complex arguments use the pointer message formats: */ +typedef upwr_pointer_msg upwr_pwm_retain_msg; +typedef upwr_pointer_msg upwr_pwm_pmode_cfg_msg; + +#if (UPWR_ARG_BITS < UPWR_DOMBIAS_ARG_BITS) +#if ((UPWR_ARG_BITS + 32) < UPWR_DOMBIAS_ARG_BITS) +#error "too few message bits for domain bias argument" +#endif +#endif + +/* service upwr_pwm_chng_dom_bias message argument fields */ +#define UPWR_DOMBIAS_MODE_BITS (2U) +#define UPWR_DOMBIAS_RBB_BITS (8U) +#define UPWR_DOMBIAS_RSV_BITS (14U) +#define UPWR_DOMBIAS_ARG_BITS (UPWR_DOMBIAS_RSV_BITS + \ + (2U * UPWR_DOMBIAS_MODE_BITS) + \ + (4U * UPWR_DOMBIAS_RBB_BITS) + 2U) +/* + * upwr_pwm_dom_bias_args is an SoC-dependent message, + */ +typedef struct { + uint32_t: 12U; /* TODO: find a way to use UPWR_HEADER_BITS */ + uint32_t dommode : UPWR_DOMBIAS_MODE_BITS; + uint32_t avdmode : UPWR_DOMBIAS_MODE_BITS; + uint32_t domapply : 1U; + uint32_t avdapply : 1U; + uint32_t rsv : UPWR_DOMBIAS_RSV_BITS; + uint32_t domrbbn : UPWR_DOMBIAS_RBB_BITS; /* RTD/APD back bias N-well */ + uint32_t domrbbp : UPWR_DOMBIAS_RBB_BITS; /* RTD/APD back bias P-well */ + uint32_t avdrbbn : UPWR_DOMBIAS_RBB_BITS; /* AVD back bias N-well */ + uint32_t avdrbbp : UPWR_DOMBIAS_RBB_BITS; /* AVD back bias P-well */ +} upwr_pwm_dom_bias_args; + + +typedef union { + struct upwr_msg_hdr hdr; /* message header */ + struct { + upwr_pwm_dom_bias_args B; + } args; +} upwr_pwm_dom_bias_msg; + +/* service upwr_pwm_chng_mem_bias message argument fields */ +/* + * upwr_pwm_mem_bias_args is an SoC-dependent message, + * defined in upower_soc_defs.h + */ +typedef struct { + uint32_t: 12U; /* TODO: find a way to use UPWR_HEADER_BITS */ + uint32_t en : 1U; + uint32_t rsv : 19U; +} upwr_pwm_mem_bias_args; + +typedef union { + struct upwr_msg_hdr hdr; /* message header */ + struct { + upwr_pwm_mem_bias_args B; + } args; +} upwr_pwm_mem_bias_msg; + +typedef upwr_pointer_msg upwr_pwm_pes_seq_msg; + +/* upwr_pwm_reg_config-specific message format */ +typedef upwr_pointer_msg upwr_pwm_regcfg_msg; + +/* upwr_volt_pmic_volt-specific message format */ +typedef union { + struct upwr_msg_hdr hdr; /* message header */ + struct { + uint32_t rsv : UPWR_HEADER_BITS; + uint32_t domain : 8U; + uint32_t rail : 8U; + } args; +} upwr_volt_dom_pmic_rail_msg; + +typedef union { + struct upwr_msg_hdr hdr; + struct { + uint32_t rsv : UPWR_HEADER_BITS; + uint32_t rail : 4U; /* pmic rail id */ + uint32_t volt : 12U; /* voltage value, accurate to mV, support 0~3.3V */ + } args; +} upwr_volt_pmic_set_volt_msg; + +typedef union { + struct upwr_msg_hdr hdr; + struct { + uint32_t rsv : UPWR_HEADER_BITS; + uint32_t rail : 16U; /* pmic rail id */ + } args; +} upwr_volt_pmic_get_volt_msg; + +typedef union { + struct upwr_msg_hdr hdr; + struct { + uint32_t rsv :UPWR_HEADER_BITS; + uint32_t domain : 8U; + uint32_t mode : 8U; /* work mode */ + } args; +} upwr_volt_dva_req_domain_msg; + +typedef union { + struct upwr_msg_hdr hdr; + struct { + uint32_t rsv : UPWR_HEADER_BITS; + uint32_t mode : 16U; /* work mode */ + } args; +} upwr_volt_dva_req_soc_msg; + +typedef union { + struct upwr_msg_hdr hdr; + struct { + uint32_t rsv : UPWR_HEADER_BITS; + uint32_t addr_offset : 16U; /* addr_offset to 0x28330000 */ + } args; +} upwr_volt_dva_dump_info_msg; + +typedef upwr_pointer_msg upwr_volt_pmiccfg_msg; +typedef upwr_pointer_msg upwr_volt_dva_req_id_msg; +typedef upwr_down_1w_msg upwr_volt_pmic_cold_reset_msg; + +/* upwr_pwm_volt-specific message format */ +typedef union { + struct upwr_msg_hdr hdr; + struct { + uint32_t rsv : UPWR_HEADER_BITS; + uint32_t reg : UPWR_HALF_ARG_BITS; /* regulator id */ + uint32_t volt : UPWR_HALF_ARG_BITS; /* voltage value */ + } args; +} upwr_pwm_volt_msg; + +/* upwr_pwm_freq_setup-specific message format */ +/** + * DVA adjust stage + */ +#define DVA_ADJUST_STAGE_INVALID 0U +/* first stage, gross adjust, for increase frequency use */ +#define DVA_ADJUST_STAGE_ONE 1U +/* second stage, fine adjust for increase frequency use */ +#define DVA_ADJUST_STAGE_TWO 2U +/* combine first + second stage, for descrese frequency use */ +#define DVA_ADJUST_STAGE_FULL 3U + +/** + * This message structure is used for DVFS feature + * 1. Because user may use different PMIC or different board, + * the pmic regulator of RTD/APD may change, + * so, user need to tell uPower the regulator number. + * The number must be matched with PMIC IC and board. + * use 4 bits for pmic regulator, support to 16 regulator. + * + * use 2 bits for DVA stage + * + * use 10 bits for target frequency, accurate to MHz, support to 1024 MHz + */ +typedef union { + struct upwr_msg_hdr hdr; + struct { + uint32_t rsv : UPWR_HEADER_BITS; + uint32_t rail : 4; /* pmic regulator */ + uint32_t stage : 2; /* DVA stage */ + uint32_t target_freq : 10; /* target frequency */ + } args; +} upwr_pwm_freq_msg; + +typedef upwr_down_2w_msg upwr_pwm_param_msg; + +/* upwr_pwm_pmiccfg-specific message format */ +typedef upwr_pointer_msg upwr_pwm_pmiccfg_msg; + +/* functions that pass a pointer use message format upwr_pointer_msg */ +typedef upwr_pointer_msg upwr_pwm_cfgptr_msg; + +/* functions that pass 2 pointers use message format upwr_2pointer_msg + */ +typedef upwr_2pointer_msg upwr_pwm_switch_msg; +typedef upwr_2pointer_msg upwr_pwm_pwron_msg; +typedef upwr_2pointer_msg upwr_pwm_pwroff_msg; + +/* Power Management all messages */ +typedef union { + struct upwr_msg_hdr hdr; /* message header */ + upwr_pwm_param_msg param; /* power management parameters */ + upwr_pwm_dom_bias_msg dom_bias; /* domain bias message */ + upwr_pwm_mem_bias_msg mem_bias; /* memory bias message */ + upwr_pwm_pes_seq_msg pes; /* PE seq. message */ + upwr_pwm_pmode_cfg_msg pmode; /* power mode config message */ + upwr_pwm_regcfg_msg regcfg; /* regulator config message */ + upwr_pwm_volt_msg volt; /* set voltage message */ + upwr_pwm_freq_msg freq; /* set frequency message */ + upwr_pwm_switch_msg switches; /* switch control message */ + upwr_pwm_pwron_msg pwron; /* switch/RAM/ROM power on message */ + upwr_pwm_pwroff_msg pwroff; /* switch/RAM/ROM power off message */ + upwr_pwm_retain_msg retain; /* memory retain message */ + upwr_pwm_cfgptr_msg cfgptr; /* configuration pointer message*/ + upwr_pwm_dom_pwron_msg dompwron; /* domain power on message */ + upwr_pwm_boot_start_msg boot; /* boot start message */ +} upwr_pwm_msg; + +typedef union { + struct upwr_msg_hdr hdr; /* message header */ + upwr_volt_pmic_set_volt_msg set_pmic_volt; /* set pmic voltage message */ + upwr_volt_pmic_get_volt_msg get_pmic_volt; /* set pmic voltage message */ + upwr_volt_pmic_set_mode_msg set_pmic_mode; /* set pmic mode message */ + upwr_volt_pmiccfg_msg pmiccfg; /* PMIC configuration message */ + upwr_volt_dom_pmic_rail_msg dom_pmic_rail; /* domain bias message */ + upwr_volt_dva_dump_info_msg dva_dump_info; /* dump dva info message */ + upwr_volt_dva_req_id_msg dva_req_id; /* dump dva request id array message */ + upwr_volt_dva_req_domain_msg dva_req_domain; /* dump dva request domain message */ + upwr_volt_dva_req_soc_msg dva_req_soc; /* dump dva request whole soc message */ + upwr_volt_pmeter_meas_msg pmeter_meas_msg; /* pmeter measure message */ + upwr_volt_vmeter_meas_msg vmeter_meas_msg; /* vmeter measure message */ + upwr_volt_pmic_cold_reset_msg cold_reset_msg; /* pmic cold reset message */ +} upwr_volt_msg; + + +typedef union { + struct upwr_msg_hdr hdr; + struct { + uint32_t rsv : UPWR_HEADER_BITS; + uint32_t sensor_id : 16U; /* temperature sensor id */ + } args; +} upwr_temp_get_cur_temp_msg; + +typedef union { + struct upwr_msg_hdr hdr; + struct { + uint32_t rsv : UPWR_HEADER_BITS; + uint32_t index : 8U; /* the delay meter index */ + uint32_t path : 8U; /* the critical path number */ + } args; +} upwr_dmeter_get_delay_margin_msg; + +#define MAX_DELAY_MARGIN 63U +#define MAX_DELAY_CRITICAL_PATH 7U +#define MAX_DELAY_METER_NUM 1U + +typedef union { + struct upwr_msg_hdr hdr; + struct { + uint32_t rsv : UPWR_HEADER_BITS; + uint32_t index: 4U; /* the delay meter index */ + uint32_t path: 4U; /* the critical path number */ + uint32_t dm: 8U; /* the delay margin value of delay meter */ + } args; +} upwr_dmeter_set_delay_margin_msg; + +#define MAX_PMON_CHAIN_SEL 1U + +typedef union { + struct upwr_msg_hdr hdr; + struct { + uint32_t rsv : UPWR_HEADER_BITS; + uint32_t chain_sel : 16U; /* the process monitor delay chain sel */ + } args; +} upwr_pmon_msg; + +typedef union { + struct upwr_msg_hdr hdr; /* message header */ + upwr_temp_get_cur_temp_msg get_temp_msg; /* get current temperature message */ +} upwr_temp_msg; + +typedef union { + struct upwr_msg_hdr hdr; /* message header */ + upwr_dmeter_get_delay_margin_msg get_margin_msg; /* get delay margin message */ + upwr_dmeter_set_delay_margin_msg set_margin_msg; /* set delay margin message */ + upwr_pmon_msg pmon_msg; /* process monitor message */ +} upwr_dmeter_msg; + +typedef upwr_down_2w_msg upwr_down_max_msg; /* longest downstream msg */ + +/* + * upwr_dom_bias_cfg_t and upwr_mem_bias_cfg_t are SoC-dependent structs, + * defined in upower_soc_defs.h + */ +/* Power and mem switches */ +typedef struct { + volatile struct upwr_switch_board_t swt_board[UPWR_PMC_SWT_WORDS]; + volatile struct upwr_mem_switches_t swt_mem[UPWR_PMC_MEM_WORDS]; +} swt_config_t; + +/* ************************************************************************* + * Service Group DIAGNOSE - downstream + ***************************************************************************/ +/* Diagnose Functions */ +#define UPWR_DGN_MODE (0U) /* 0 = diagnose mode: upwr_dgn_mode */ +#define UPWR_DGN_F_COUNT (1U) +#define UPWR_DGN_BUFFER_EN (2U) +typedef uint32_t upwr_dgn_f_t; + +#define UPWR_DGN_ALL2ERR (0U) /* record all until an error occurs, freeze recording on error */ +#define UPWR_DGN_ALL2HLT (1U) /* record all until an error occurs, halt core on error */ +#define UPWR_DGN_ALL (2U) /* trace, warnings, errors, task state recorded */ +#define UPWR_DGN_MAX UPWR_DGN_ALL +#define UPWR_DGN_TRACE (3U) /* trace, warnings, errors recorded */ +#define UPWR_DGN_SRVREQ (4U) /* service request activity recorded */ +#define UPWR_DGN_WARN (5U) /* warnings and errors recorded */ +#define UPWR_DGN_ERROR (6U) /* only errors recorded */ +#define UPWR_DGN_NONE (7U) /* no diagnostic recorded */ +#define UPWR_DGN_COUNT (8U) +typedef uint32_t upwr_dgn_mode_t; + +typedef upwr_down_1w_msg upwr_dgn_mode_msg; + +typedef union { + struct upwr_msg_hdr hdr; + upwr_dgn_mode_msg mode_msg; +} upwr_dgn_msg; + +typedef struct { + struct upwr_msg_hdr hdr; + uint32_t buf_addr; +} upwr_dgn_v2_msg; + +/* diagnostics log types in the shared RAM log buffer */ + +typedef enum { + DGN_LOG_NONE = 0x00000000, + DGN_LOG_INFO = 0x10000000, + DGN_LOG_ERROR = 0x20000000, + DGN_LOG_ASSERT = 0x30000000, + DGN_LOG_EXCEPT = 0x40000000, + DGN_LOG_EVENT = 0x50000000, // old event trace + DGN_LOG_EVENTNEW = 0x60000000, // new event trace + DGN_LOG_SERVICE = 0x70000000, + DGN_LOG_TASKDEF = 0x80000000, + DGN_LOG_TASKEXE = 0x90000000, + DGN_LOG_MUTEX = 0xA0000000, + DGN_LOG_SEMAPH = 0xB0000000, + DGN_LOG_TIMER = 0xC0000000, + DGN_LOG_CALLTRACE = 0xD0000000, + DGN_LOG_DATA = 0xE0000000, + DGN_LOG_PCTRACE = 0xF0000000 +} upwr_dgn_log_t; + +/* **************************************************************************** + * UPSTREAM MESSAGES - RESPONSES + * **************************************************************************** + */ +/* generic ok/ko response message */ +#define UPWR_RESP_ERR_BITS (4U) +#define UPWR_RESP_HDR_BITS (UPWR_RESP_ERR_BITS+\ + UPWR_SRVGROUP_BITS+UPWR_FUNCTION_BITS) +#define UPWR_RESP_RET_BITS (32U - UPWR_RESP_HDR_BITS) + +#define UPWR_RESP_OK (0U) /* no error */ +#define UPWR_RESP_SG_BUSY (1U) /* service group is busy */ +#define UPWR_RESP_SHUTDOWN (2U) /* services not up or shutting down */ +#define UPWR_RESP_BAD_REQ (3U) /* invalid request */ +#define UPWR_RESP_BAD_STATE (4U) /* system state doesn't allow perform the request */ +#define UPWR_RESP_UNINSTALLD (5U) /* service or function not installed */ +#define UPWR_RESP_UNINSTALLED (5U) /* service or function not installed (alias) */ +#define UPWR_RESP_RESOURCE (6U) /* resource not available */ +#define UPWR_RESP_TIMEOUT (7U) /* service timeout */ +#define UPWR_RESP_COUNT (8U) + +typedef uint32_t upwr_resp_t; + +struct upwr_resp_hdr { + uint32_t errcode : UPWR_RESP_ERR_BITS; + uint32_t srvgrp : UPWR_SRVGROUP_BITS; /* service group */ + uint32_t function: UPWR_FUNCTION_BITS; + uint32_t ret : UPWR_RESP_RET_BITS; /* return value, if any */ +}; + +/* generic 1-word upstream message format */ +typedef union { + struct upwr_resp_hdr hdr; + uint32_t word; +} upwr_resp_msg; + +/* generic 2-word upstream message format */ +typedef struct { + struct upwr_resp_hdr hdr; + uint32_t word2; /* message second word */ +} upwr_up_2w_msg; + +typedef upwr_up_2w_msg upwr_up_max_msg; + +/* ************************************************************************* + * Exception/Initialization - upstream + ***************************************************************************/ +#define UPWR_SOC_BITS (7U) +#define UPWR_VMINOR_BITS (4U) +#define UPWR_VFIXES_BITS (4U) +#define UPWR_VMAJOR_BITS \ + (32U - UPWR_HEADER_BITS - UPWR_SOC_BITS - UPWR_VMINOR_BITS - UPWR_VFIXES_BITS) + +typedef struct { + uint32_t soc_id; + uint32_t vmajor; + uint32_t vminor; + uint32_t vfixes; +} upwr_code_vers_t; + +/* message sent by firmware initialization, received by upwr_init */ +typedef union { + struct upwr_resp_hdr hdr; + struct { + uint32_t rsv : UPWR_RESP_HDR_BITS; + uint32_t soc : UPWR_SOC_BITS; /* SoC identification */ + uint32_t vmajor : UPWR_VMAJOR_BITS; /* firmware major version */ + uint32_t vminor : UPWR_VMINOR_BITS; /* firmware minor version */ + uint32_t vfixes : UPWR_VFIXES_BITS; /* firmware fixes version */ + } args; +} upwr_init_msg; + +/* message sent by firmware when the core platform is powered up */ +typedef upwr_resp_msg upwr_power_up_msg; + +/* message sent by firmware when the core reset is released for boot */ +typedef upwr_resp_msg upwr_boot_up_msg; + +/* message sent by firmware when ready for service requests */ +#define UPWR_RAM_VMINOR_BITS (7) +#define UPWR_RAM_VFIXES_BITS (6) +#define UPWR_RAM_VMAJOR_BITS (32 - UPWR_HEADER_BITS \ + - UPWR_RAM_VFIXES_BITS - UPWR_RAM_VMINOR_BITS) +typedef union { + struct upwr_resp_hdr hdr; + struct { + uint32_t rsv : UPWR_RESP_HDR_BITS; + uint32_t vmajor : UPWR_RAM_VMAJOR_BITS; /* RAM fw major version */ + uint32_t vminor : UPWR_RAM_VMINOR_BITS; /* RAM fw minor version */ + uint32_t vfixes : UPWR_RAM_VFIXES_BITS; /* RAM fw fixes version */ + } args; +} upwr_ready_msg; + +/* message sent by firmware when shutdown finishes */ +typedef upwr_resp_msg upwr_shutdown_msg; + +typedef union { + struct upwr_resp_hdr hdr; + upwr_init_msg init; + upwr_power_up_msg pwrup; + upwr_boot_up_msg booted; + upwr_ready_msg ready; +} upwr_startup_up_msg; + +/* message sent by firmware for uPower config setting */ +typedef upwr_resp_msg upwr_config_resp_msg; + +/* message sent by firmware for uPower alarm */ +typedef upwr_resp_msg upwr_alarm_resp_msg; + +/* ************************************************************************* + * Power Management - upstream + ***************************************************************************/ +typedef upwr_resp_msg upwr_param_resp_msg; + +enum work_mode { + OVER_DRIVE, + NORMAL_DRIVE, + LOW_DRIVE +}; + +#define UTIMER3_MAX_COUNT 0xFFFFU + +#endif /* UPWR_DEFS_H */ diff --git a/plat/imx/imx8ulp/upower/upower_hal.c b/plat/imx/imx8ulp/upower/upower_hal.c new file mode 100644 index 00000000..337857b7 --- /dev/null +++ b/plat/imx/imx8ulp/upower/upower_hal.c @@ -0,0 +1,201 @@ +/* + * Copyright 2020-2024 NXP + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <errno.h> + +#include <common/debug.h> +#include <drivers/delay_timer.h> +#include <lib/mmio.h> + +#include "upower_api.h" +#include "upower_defs.h" + +#define UPOWER_AP_MU1_ADDR U(0x29280000) + +struct MU_t *muptr = (struct MU_t *)UPOWER_AP_MU1_ADDR; + +void upower_apd_inst_isr(upwr_isr_callb txrx_isr, + upwr_isr_callb excp_isr) +{ + /* Do nothing */ +} + +int upower_status(int status) +{ + int ret = -1; + + switch (status) { + case 0: + VERBOSE("finished successfully!\n"); + ret = 0; + break; + case -1: + VERBOSE("memory allocation or resource failed!\n"); + break; + case -2: + VERBOSE("invalid argument!\n"); + break; + case -3: + VERBOSE("called in an invalid API state!\n"); + break; + default: + VERBOSE("invalid return status\n"); + break; + } + + return ret; +} + + +void upower_wait_resp(void) +{ + while (muptr->RSR.B.RF0 == 0) { + udelay(100); + } + upwr_txrx_isr(); +} + +static void user_upwr_rdy_callb(uint32_t soc, uint32_t vmajor, uint32_t vminor) +{ + NOTICE("%s: soc=%x\n", __func__, soc); + NOTICE("%s: RAM version:%d.%d\n", __func__, vmajor, vminor); +} + +int upower_init(void) +{ + int status; + + status = upwr_init(APD_DOMAIN, muptr, NULL, NULL, upower_apd_inst_isr, NULL); + if (upower_status(status)) { + ERROR("%s: upower init failure\n", __func__); + return -EINVAL; + } + + NOTICE("%s: start uPower RAM service\n", __func__); + status = upwr_start(1, user_upwr_rdy_callb); + upower_wait_resp(); + /* poll status */ + if (upower_status(status)) { + NOTICE("%s: upower init failure\n", __func__); + return status; + } + + return 0; +} + +int upower_pwm(int domain_id, bool pwr_on) +{ + int ret, ret_val; + uint32_t swt; + + if (domain_id == 9U || domain_id == 11U || domain_id == 12U) { + swt = BIT_32(12) | BIT_32(11) | BIT_32(10) | BIT_32(9); + } else { + swt = BIT_32(domain_id); + } + + if (pwr_on) { + ret = upwr_pwm_power_on(&swt, NULL, NULL); + } else { + ret = upwr_pwm_power_off(&swt, NULL, NULL); + } + + if (ret) { + NOTICE("%s failed: ret: %d, pwr_on: %d\n", __func__, ret, pwr_on); + return ret; + } + upower_wait_resp(); + + ret = upwr_poll_req_status(UPWR_SG_PWRMGMT, NULL, NULL, &ret_val, 1000); + if (ret != UPWR_REQ_OK) { + NOTICE("Failure %d, %s\n", ret, __func__); + if (ret == UPWR_REQ_BUSY) { + return -EBUSY; + } else { + return -EINVAL; + } + } + + return 0; +} + +int upower_read_temperature(uint32_t sensor_id, int32_t *temperature) +{ + int ret, ret_val; + upwr_resp_t err_code; + int64_t t; + + ret = upwr_tpm_get_temperature(sensor_id, NULL); + if (ret) { + return ret; + } + + upower_wait_resp(); + ret = upwr_poll_req_status(UPWR_SG_TEMPM, NULL, &err_code, &ret_val, 1000); + if (ret > UPWR_REQ_OK) { + return ret; + } + + t = ret_val & 0xff; + *temperature = (2673049 * t * t * t / 10000000 + 3734262 * t * t / 100000 + + 4487042 * t / 100 - 4698694) / 100000; + + return 0; +} + +int upower_pmic_i2c_write(uint32_t reg_addr, uint32_t reg_val) +{ + int ret, ret_val; + upwr_resp_t err_code; + + ret = upwr_xcp_i2c_access(0x32, 1, 1, reg_addr, reg_val, NULL); + if (ret) { + WARN("pmic i2c read failed ret %d\n", ret); + return ret; + } + + upower_wait_resp(); + ret = upwr_poll_req_status(UPWR_SG_EXCEPT, NULL, &err_code, &ret_val, 1000); + if (ret != UPWR_REQ_OK) { + WARN("i2c poll Failure %d, err_code %d, ret_val 0x%x\n", + ret, err_code, ret_val); + return ret; + } + + VERBOSE("PMIC write reg[0x%x], val[0x%x]\n", reg_addr, reg_val); + + return 0; +} + +int upower_pmic_i2c_read(uint32_t reg_addr, uint32_t *reg_val) +{ + int ret, ret_val; + upwr_resp_t err_code; + + if (reg_val == NULL) { + return -1; + } + + ret = upwr_xcp_i2c_access(0x32, -1, 1, reg_addr, 0, NULL); + if (ret) { + WARN("pmic i2c read failed ret %d\n", ret); + return ret; + } + + upower_wait_resp(); + ret = upwr_poll_req_status(UPWR_SG_EXCEPT, NULL, &err_code, &ret_val, 1000); + if (ret != UPWR_REQ_OK) { + WARN("i2c poll Failure %d, err_code %d, ret_val 0x%x\n", + ret, err_code, ret_val); + return ret; + } + + *reg_val = ret_val; + + VERBOSE("PMIC read reg[0x%x], val[0x%x]\n", reg_addr, *reg_val); + + return 0; +} diff --git a/plat/imx/imx8ulp/upower/upower_soc_defs.h b/plat/imx/imx8ulp/upower/upower_soc_defs.h new file mode 100644 index 00000000..111be148 --- /dev/null +++ b/plat/imx/imx8ulp/upower/upower_soc_defs.h @@ -0,0 +1,1154 @@ +/* SPDX-License-Identifier: BSD-3-Clause */ +/** + * Copyright 2019-2024 NXP + * + * KEYWORDS: micro-power uPower driver API + * ----------------------------------------------------------------------------- + * PURPOSE: SoC-dependent uPower driver API #defines and typedefs shared + * with the firmware + * ----------------------------------------------------------------------------- + * PARAMETERS: + * PARAM NAME RANGE:DESCRIPTION: DEFAULTS: UNITS + * ----------------------------------------------------------------------------- + * REUSE ISSUES: no reuse issues + */ + +#ifndef UPWR_SOC_DEFS_H +#define UPWR_SOC_DEFS_H + +#include <stdbool.h> +#include <stdint.h> + +#include "upower_defs.h" + +#define UPWR_MU_MSG_SIZE (2U) /* words */ + +#ifdef NUM_PMC_SWT_WORDS +#define UPWR_PMC_SWT_WORDS NUM_PMC_SWT_WORDS +#endif + +#ifdef NUM_PMC_RAM_WORDS +#define UPWR_PMC_MEM_WORDS NUM_PMC_RAM_WORDS +#endif + +#ifndef UPWR_DRAM_SHARED_BASE_ADDR +#define UPWR_DRAM_SHARED_BASE_ADDR (0x28330000U) +#endif + +#ifndef UPWR_DRAM_SHARED_SIZE +#define UPWR_DRAM_SHARED_SIZE (2048U) +#endif + +#define UPWR_DRAM_SHARED_ENDPLUS (UPWR_DRAM_SHARED_BASE_ADDR+\ + UPWR_DRAM_SHARED_SIZE) + +#ifndef UPWR_API_BUFFER_BASE +#define UPWR_API_BUFFER_BASE (0x28330600U) +#endif + +#ifndef UPWR_API_BUFFER_ENDPLUS +#define UPWR_API_BUFFER_ENDPLUS (UPWR_DRAM_SHARED_ENDPLUS - 64U) +#endif + +#ifndef UPWR_PMC_SWT_WORDS +#define UPWR_PMC_SWT_WORDS (1U) +#endif + +#ifndef UPWR_PMC_MEM_WORDS +#define UPWR_PMC_MEM_WORDS (2U) +#endif + +#define UPWR_OSC_HI_FREQ (64U) // MHz +#define UPWR_OSC_LO_FREQ (16U) // MHz + +#ifndef UPWR_I2C_FREQ +#define UPWR_I2C_FREQ (UPWR_OSC_HI_FREQ * 1000000U) +#endif + +/* + * i.MX8ULP-dependent uPower API Definition + * + * This chapter documents the API definitions that are specific to the + * i.MX8ULP SoC. + * + */ + +/**--------------------------------------------------------------- + * INITIALIZATION, CONFIGURATION + * + * i.MX8ULP provides only one Message Unit (MU) for each core domain: + * Real Time Domain (RTD) and Application Domain (APD), which has two A35 cores. + * Both A35 cores in APD must share the same API instance, meaning upwr_init + * must be called only once for each domain. The API does not provide any + * mutually exclusion or locking mechanism for concurrent accesses from both + * APD cores, so any API arbitration, if needed, must be implemented by the + * API user code. + * + * A domain must not go to Power Down (PD) or Deep Power Down (DPD) power modes + * with any service still pending (response not received). + * + * Next sections describe the i.MX8ULP particularities of service calls. + * + */ + +/**+ + * upwr_start() + * + * i.MX8ULP ROM firmware provides only the launch option 0, which has no + * power mode transition support and provides the following services: + * - upwr_xcp_config + * - upwr_xcp_sw_alarm + * - upwr_pwm_param + * - upwr_pwm_power_on + * - upwr_pwm_power-off + * - upwr_pwm_mem_retain + * - upwr_pwm_chng_dom_bias + * - upwr_pwm_chng_mem_bias + * + * i.MX8ULP RAM firmware provides 2 launch options: + * + * 1. starts all tasks, services and power mode ones; + * this is the full-featured firmware option. + * 2. starts only the power mode tasks; services are not available with + * this option, and futher calls to upwr_start (from either domain) + * have no response; this option is mostly used to accelerate power mode + * mixed-signal simulations, and not intended to be used with silicon. + * + * Note: option 0 is also available if the RAM firmware is loaded. + */ + +/* service upwr_pwm_set_domain_pmic_rail message argument fields*/ +typedef struct { + uint32_t domain : 16U; + uint32_t rail : 16U; +} upwr_pwm_dom_pmic_rail_args; + +#define UPWR_FILL_DOMBIAS_ARGS(dom, bias, args) \ +do { \ + (args).B.domapply = (args).B.avdapply = 0U; \ + switch ((bias)->apply) { \ + case BIAS_APPLY_RTD_AVD: \ + (args).B.avdapply = 1U; \ + /* fall through */ \ + case BIAS_APPLY_RTD: \ + (dom) = (uint32_t)RTD_DOMAIN; \ + (args).B.domapply = 1U; \ + break; \ + case BIAS_APPLY_APD_AVD: \ + (args).B.avdapply = 1U; \ + /* fall through */ \ + case BIAS_APPLY_APD: \ + (dom) = (uint32_t)APD_DOMAIN; \ + (args).B.domapply = 1U; \ + break; \ + case BIAS_APPLY_AVD: \ + (args).B.avdapply = 1U; \ + break; \ + default: \ + break; \ + } \ + (args).B.dommode = (uint32_t)((bias)->dommode); \ + (args).B.avdmode = (uint32_t)((bias)->avdmode); \ + uint32_t sat = UPWR_BIAS2MILIV((1UL << UPWR_DOMBIAS_RBB_BITS) - 1UL);\ + (args).B.domrbbn = ((bias)->dombias.rbbn > sat) ? sat : \ + UPWR_BIAS_MILIV((bias)->dombias.rbbn); \ + (args).B.domrbbp = ((bias)->dombias.rbbp > sat) ? sat : \ + UPWR_BIAS_MILIV((bias)->dombias.rbbp); \ + (args).B.avdrbbn = ((bias)->avdbias.rbbn > sat) ? sat : \ + UPWR_BIAS_MILIV((bias)->avdbias.rbbn); \ + (args).B.avdrbbp = ((bias)->avdbias.rbbp > sat) ? sat : \ + UPWR_BIAS_MILIV((bias)->avdbias.rbbp); \ +} while (false) + +#define UPWR_FILL_MEMBIAS_ARGS(bias, args) \ +do { \ + (args).B.en = (bias)->en; \ +} while (false) + + +#define UPWR_APD_CORES (2U) +#define UPWR_RTD_CORES (1U) + +#define RTD_DOMAIN (0U) +#define APD_DOMAIN (1U) +#define UPWR_MAIN_DOMAINS (2U) +#define AVD_DOMAIN (2U) +#define UPWR_DOMAIN_COUNT (3U) +#define PSD_DOMAIN (3U) +#define UPWR_ALL_DOMAINS (4U) + +typedef uint32_t soc_domain_t; + +/*========================================================================= + * UNIT CONVERSION MACROS + * These macros convert physical units to the values passed as arguments + * in API functions. + *========================================================================= + */ + +#define UPWR_VOLT_MILIV(v) (v) /* voltage in mV to argument value */ +#define UPWR_VOLT_MICROV(v)((v) / 1000U) /* voltage in uV to argument value */ +#define UPWR_BIAS_MILIV(v) (((v) + 49UL) / 50UL) /* bias voltage(mV) to argument value */ +#define UPWR_BIAS2MILIV(v) ((v) * 50UL) /* inverse of UPWR_BIAS_MILIV */ +#define UPWR_FREQ_KHZ(f) (f) /* frequency (kHz) to argument value */ + +#define UPWR_DOMBIAS_MAX_MV (UPWR_BIAS2MILIV((1U << UPWR_DOMBIAS_RBB_BITS) - 1U)) + +/**--------------------------------------------------------------- + * EXCEPTION SERVICE GROUP + */ + +/**+ + * upwr_xcp_config() + * + * The i.MX8ULP uPower configuration struct contains the following bitfields: + * + * - ALARM_INT (1 bit): tells which RTD MU interrupt should be used for alarms; + * 1= MU GPI1; 0= MU GPI0; APD alarms always use GPI0. + * - CFG_IOMUX (1 bit): determintes if uPower configures i.MX8ULP IOMUX for + * I2C and mode pins used to control an external PMIC; + * 1= uPower firmware or PMIC driver configures i.MX8ULP IOMUX and mode pins; + * 0= i.MX8ULP IOMUX and mode pins not configured by uPower; + * - DGNBUFBITS (4 bits): determines the diagnostic buffer size according to + * the formula: size = 2^(DGNBUFBITS+3) bytes; + * + * Defaults are all zeroes; all other bits are reserved, and must be written 0. + */ + +typedef union { + uint32_t R; + struct { + uint32_t ALARM_INT : 1U; + uint32_t CFG_IOMUX : 1U; + uint32_t DGNBUFBITS : 4U; + uint32_t RSV : 26U; + } B; +} upwr_xcp_config_t; + +/**+ + * upwr_xcp_sw_alarm() + * + * Argument code is defined by the enum upwr_alarm_t, with the values: + * - UPWR_ALARM_INTERNAL: internal software error + * - UPWR_ALARM_EXCEPTION: uPower core exception, either illegal instruction or + * bus error + * - UPWR_ALARM_SLACK: delay path too slow, meaning a timing violation occurred + * or is iminent. + * - UPWR_ALARM_VOLTAGE: one of the measured voltages is below safety margins. + * + * Note that this service emulates an alarm that would normally be issued by + * uPower when it detects one of the causes above. A request to alarm the APD + * domain when it is powered off returns success, but is ineffective. + * + */ + +#define UPWR_ALARM_INTERNAL (0U) /* internal error */ +#define UPWR_ALARM_EXCEPTION (1U) /* core exception */ +#define UPWR_ALARM_SLACK (2U) /* delay path too slow */ +#define UPWR_ALARM_VOLTAGE (3U) /* voltage drop */ +#define UPWR_ALARM_LAST UPWR_ALARM_VOLTAGE + +typedef uint32_t upwr_alarm_t; + +/**--------------------------------------------------------------- + * POWER MANAGEMENT SERVICE GROUP + */ + +/* values in mV: */ +#define UPWR_RTD_RBBN_MAX (1300U) /* max. RTD Reverse Back Bias N-Well */ +#define UPWR_RTD_RBBN_MIN (100U) /* min. RTD Reverse Back Bias N-Well */ + +#define UPWR_RTD_RBBP_MAX (1300U) /* max. RTD Reverse Back Bias P-Well */ +#define UPWR_RTD_RBBP_MIN (100U) /* min. RTD Reverse Back Bias P-Well */ + +/* APD bias can only two values (mV): */ +#define UPWR_APD_RBBN_LO (1000U) /* low APD Reverse Back Bias N-Well */ +#define UPWR_APD_RBBN_HI (1300U) /* high APD Reverse Back Bias N-Well */ + +#define UPWR_APD_RBBP_LO (1000U) /* low APD Reverse Back Bias P-Well */ +#define UPWR_APD_RBBP_HI (1300U) /* high APD Reverse Back Bias P-Well */ + +/* AVD bias can only two values (mV): */ +#define UPWR_AVD_RBBN_LO (1000U) /* low AVD Reverse Back Bias N-Well */ +#define UPWR_AVD_RBBN_HI (1300U) /* high AVD Reverse Back Bias N-Well */ + +#define UPWR_AVD_RBBP_LO (1000U) /* low AVD Reverse Back Bias P-Well */ +#define UPWR_AVD_RBBP_HI (1300U) /* high AVD Reverse Back Bias P-Well */ + +/**+ + * upwr_pwm_param() + * + * Argument param is defined by the struct/union upwr_pwm_param_t with the + * following i.MX8ULP-specific bitfields: + * - DPD_ALLOW (1 bit): 1= allows uPower power mode to go Deep Power Down (DPD); + * uPower DPD also depends on other conditions, but if this bit is 0 uPower + * won't go DPD even if those conditions are met; it can go either Sleep or + * Deep Sleep (DSL) depending on the other configurations. + * - DSL_DIS (1 bit): if this bit is 1, uPower power mode won't go Deep Sleep + * (DSL) even if the other conditions for that are met; + * it may go Sleep instead. + * - SLP_ALLOW (1 bit): if this bit is 1, uPower power mode will go Sleep if + * the conditions for Partial Active are met; it may also go Deep Sleep if bit + * DSL_DIS=1. + * - DSL_BGAP_OFF (1 bit): 1= turns bandgap off when uPower goes Deep Sleep; + * 0= leaves bandgap on when uPower goes Deep Sleep (DSL). + * - DPD_BGAP_ON (1 bit): 1= leaves bandgap on when uPower goes Deep Power Down + * (DPD); 0= powers off bandgap when uPower goes Deep Power Down (DPD). + * + * Defaults are all zeroes; all other bits are reserved, and must be written 0. + */ + +typedef union { + uint32_t R; + struct { + uint32_t DPD_ALLOW : 1U; + uint32_t DSL_DIS : 1U; + uint32_t SLP_ALLOW : 1U; + uint32_t DSL_BGAP_OFF : 1U; + uint32_t DPD_BGAP_ON : 1U; + uint32_t RSV : 27U; + } B; +} upwr_pwm_param_t; + +/**+ + * upwr_pwm_chng_reg_voltage() + * + * Argument reg is defined by the enum upwr_pmc_reg_t, with regulator ids: + * - RTD_PMC_REG: RTD regulator + * - APD_PMC_REG: APD regulator + * - RTD_BIAS_PMC_REG: RTD bias regulator + * - APD_BIAS_PMC_REG: APD bias regulator + * - RTD_LVD_PMC_MON: RTD LVD regulator + * - APD_LVD_PMC_MON: APD LVD regulator + * - AVD_LVD_PMC_MON: AVD LVD regulator + * + * Argument volt is defined by the formula: + * + * argument = 92.30797633*V - 55.000138, rounded to the nearest integer, + * where V is the value in Volts, with a minimum of 0.595833 V (argument = 0). + * + */ + +/* Regulator ids */ +typedef enum { + RTD_PMC_REG, + APD_PMC_REG, + RTD_BIAS_PMC_REG, + APD_BIAS_PMC_REG, + RTD_LVD_PMC_MON, + APD_LVD_PMC_MON, + AVD_LVD_PMC_MON +} upwr_pmc_reg_t; + +/**+ + * upwr_pwm_freq_setup() + * + * Argument domain is either RTD_DOMAIN or APD_DOMAIN. + * Arguments nextfq and currfq are to be defined (TBD). + */ + +/**+ + * upwr_pwm_dom_power_on() + * + * The arguments must comply with the restrictions below, otherwise the service + * is not executed and returns error UPWR_RESP_BAD_REQ: + * - argument domain can only be APD_DOMAIN, because in i.MX8ULP it is not + * possible APD powered on (calling the service) with RTD completely + * powered off. + * - the call can only be made from the RTD domain, for the same reason. + * - argument boot can only be 1, because in i.MX8ULP it is not possible to + * power on the APD domain without starting the core boot. + * + * If APD is already powered on and booting/booted when the service is called, + * it returns success without doing anything. + */ + +/**+ + * upwr_pwm_boot_start() + * + * The arguments must comply with the restrictions below, otherwise the service + * is not executed and returns error UPWR_RESP_BAD_REQ: + * - argument domain can only be APD_DOMAIN, because in i.MX8ULP it is not + * possible APD powered on (calling the service) with RTD completely + * powered off. + * - the call can only be made from the RTD domain, for the same reason. + * + * If APD is already booted when the service is called, it returns success + * without doing anything. Otherwise, it returns the error UPWR_RESP_BAD_STATE, + * because in i.MX8ULP APD cannot be booted separately from power on. + */ + +/**+ + * upwr_pwm_power_on(), + * upwr_pwm_power_off(), + * upwr_pwm_mem_retain() + * + * These three service functions use the same arguments: + * + * argument swt is an array of one 32-bit word: uint32_t swt[1]; + * naturally the pointer to a single uint32_t variable may be passed. + * Each bit of the word corresponds to a switch, according to the i.MX8ULP + * Reference Manual Rev B draft 2 table 64 Power switch reset state, + * and the following formula: + * + * if switch number < 10 bit number = switch number; + * if switch number > 9 bit number = switch number + 3; + * + * bits 9, 10, 11 and 12 must have the same value (corresponding to switch 9) + * + * Note: this argument is not used in upwr_pwm_mem_retain. + * + * argument mem is an array of two 32-bit words: uint32_t mem[2]; + * naturally the pointer to a single uint64_t variable may be passed, since + * both ARM and RISC-V are little endian architectures. + * Each bit of the words corresponds to a memory, according to the i.MX8ULP + * Reference Manual table "Memory Partitions". + * + * Turning a memory completely on (array and peripheral) will automatically + * turn on its power switch, even if not explicitly commanded. + * Turning a memory's power switch off will automatically turn off its array + * and peripheral beforehand, even if not explicitly commanded. + * + * Argument restrictions: + * + * The swt and mem arguments must comply with the restrictions below, otherwise + * the service is not executed (no switch/memory is changed) and returns error + * UPWR_RESP_BAD_REQ: + * 1. one must not put a memory in retention coming from an off state. + * 2. switches 9, 10, 11 and 12 must be turned on/off simultaneously. + * 3. an AVD switch can only be turned off if all AVD switches belong to the + * domain requesting the service (as defined by registers SYSCTRL0, + * LPAV_MASTER_ALLOC_CTRL and LPAV_SLAVE_ALLOC_CTRL); + * there is no such restriction to turn the switch on. + * 4. an AVD memory can only be turned off or put in retention if all + * AVD memories belong to the domain requesting the service + * (as defined by registers SYSCTRL0, LPAV_MASTER_ALLOC_CTRL and + * LPAV_SLAVE_ALLOC_CTRL); there is no such restriction to turn on the + * memories. + * 5. EdgeLock RAMs must not be turned off, unless RTD domain is in + * Deep Power Down (DPD). + * 6. Power Switch 19 must be on to turn on switches 17 (MIPI/DSI), + * 18 (MIPI/CSI), and all AVD power switches. + * + * Service Errors: + * + * Besides the error UPWR_RESP_BAD_REQ caused by violations of the restrictions + * above, the services may fail with error UPWR_RESP_RESOURCE if a power mode + * transition or a similar service is executing at the same time. + * This error should be interpreted as a "try later" response, as the service + * will succeed once those concurrent executions are done, and no other is + * started. + */ + +/**+ + * upwr_pwm_chng_switch_mem() + * + * The bit numbers in the argument struct mask and on/off state fields + * are the same as for services upwr_pwm_power_on, upwr_pwm_power_off and + * upwr_pwm_mem_retain. + * + * Turning a memory completely on (array and peripheral) will automatically + * turn on its power switch, even if not explicitly commanded. + * + * Argument restrictions: + * + * Same argument restrictions as services upwr_pwm_power_on, upwr_pwm_power_off + * and upwr_pwm_mem_retain, plus the following: + * + * 1. one must not turn a memory peripheral on and a memory array off. + * 2. one must not put a memory in retention and switch its power switch off. + * + * Service Errors: + * + * Besides the error UPWR_RESP_BAD_REQ caused by violations of the restrictions + * above, the service may fail with error UPWR_RESP_RESOURCE if a power mode + * transition or a similar service is executing at the same time. + * This error should be interpreted as a "try later" response, as the service + * will succeed once those concurrent executions are done, and no other is + * started. + */ + +/**+ + * upwr_pwm_pmode_config() + * + * The same power switch and memory restrictions of service + * upwr_pwm_chng_switch_mem apply between power modes, however they are not + * enforced by this service, that is, it does not return service error. + * + * The default power mode configurations for RTD and APD are documented in the + * i.MX8ULP Reference Manual sections "Power mode details (real-time domain)" + * and "Power mode details (application domain)", respectively. + * If those configurations are satisfactory, this service does not have + * to be called. + * + * Power Mode Configuration Structure: + * + * Follows a description of the power mode configuration structure elements. + * - dom_swts: the same switch configuration structures used in service + * upwr_pwm_chng_switch_mem argument swt. + * - mem_swts: the same memory configuration structures used in service + * upwr_pwm_chng_switch_mem argument mem. + * - regs: an array of structs base_reg_cfg_t (see upower_soc_defs.h), + * one element for each regulator; base_reg_cfg_t has fields + * mode (regulator-dependent), lvl (voltage level in uV), + * comp (regulator-dependent complamentary info). + * - pads: pad configuration in low power; see pad_cfg_t definition below. + * - mons: domain monitors (LVD and HVD) configuration; + * see mon_cfg_t definition below. + * - avd_mons: same as mons for the AVD domain; see mon_cfg_t definition below. + * - dom_bbias: back-bias configuration for the domain; + * see base_bbias_cfg_t definition below. + * - avd_bbias: back-bias configuration for the AVD domain; + * see base_bbias_cfg_t definition below. + * - mem_bbias: back-bias configuration for the memory; + * see base_bbias_cfg_t definition below. + * - mem_fbias: forward-bias configuration for the memory; + * see base_fbias_cfg_t definition below. + * - pmic: PMIC-specific configuration + * + * Structure pad_cfg_t: + * + * Pad control for low power modes (power off, etc), 1 bit per pad segment. + * - rst : put pad segment in reset. + * - iso : put pad segment in isolation. + * - compl: specific pad segment information. + * - msk : select which pads will be updated. + * + * Structure mon_cfg_t: + * + * Configures a voltage monitor and its actions. + * There are monitors for RTD, APD and AVD, monitoring LVD and HVD. + * - lvl : Voltage level (in uV). + * - mode : Mode of monitor (ON, OFF, LP, etc). + * - compl: Extra info for the monitor. + * + * Structure base_bbias_cfg_t: + * + * Configures back-bias (for domain or memory). + * - mode : Back bias mode (OFF, RBB, ARBB, etc). + * - p_lvl: Voltage level of p-well (in mV). + * - n_lvl: Voltage level of n-well (in mV). + * - compl: Complementary bias-specific (enable reset, interrupt, clamp, etc). + * + * Structure base_fbias_cfg_t: + * + * Configure memory forward bias for a memory segment. + * + * - mode : Forward bias mode (OFF, ON). + * - msk : Selects which memory will be updated + * + */ + +/*========================================================================= + * Domain bias + *========================================================================= + */ + +/**+ + * upwr_pwm_chng_dom_bias() + * + * Argument bias is a pointer to a struct with fields: + * - apply: tells to which domains the bias must be applied; + * options are RTD only (BIAS_APPLY_RTD), RTD and AVD (BIAS_APPLY_RTD_AVD), + * APD only (BIAS_APPLY_APD), APD and AVD (BIAS_APPLY_APD_AVD), + * AVD only (BIAS_APPLY_AVD) + * - dommode: bias mode of the main domain (RTD or APD, determined by apply); + * options are disabled (NBB_BIAS_MODE), reverse back bias (RBB_BIAS_MODE), + * asymmetrical forward bias (AFBB_BIAS_MODE), asymmetrical reverse bias + * (ARBB_BIAS_MODE). + * - avdmode: bias mode of Audio-Video Domain (AVD); + * options are the same as dommode. + * - dombias: bias voltage level(s) for the main domain (RTD or APD, + * determined by apply); it is a structure with 2 fields, rbbn and rbbp, + * for the N-well and P-well voltages, respectively; values are in mV. + * - avdbias: bias voltage level(s) for the Audio-Video Domain (AVD); + * same fields as dombias; + * + * Argument restrictions: + * + * Voltage levels must comply with the #define-determined limits/options: + * between UPWR_RTD_RBBN_MIN and UPWR_RTD_RBBN_MAX (inclusive) for RTD N-well; + * between UPWR_RTD_RBBP_MIN and UPWR_RTD_RBBP_MAX (inclusive) for RTD P-well; + * either UPWR_APD_RBBN_LO or UPWR_APD_RBBN_HI for APD N-well; + * either UPWR_APD_RBBP_LO or UPWR_APD_RBBP_HI for APD P-well; + * either UPWR_AVD_RBBN_LO or UPWR_AVD_RBBN_HI for AVD N-well; + * either UPWR_AVD_RBBP_LO or UPWR_AVD_RBBP_HI for AVD P-well; + * + * But note that the limits/options above do not apply to all bias modes: + * rbbn is used and checked only in mode RBB_BIAS_MODE; + * rbbp is used and checked only in modes RBB_BIAS_MODE and ARBB_BIAS_MODE; + * modes AFBB_BIAS_MODE and NBB_BIAS_MODE use or check neither rbbn nor rbbp; + * + * Service error UPWR_RESP_BAD_REQ is returned if the voltage limits/options + * above are violated. + */ + +/* argument struct for service upwr_pwm_chng_dom_bias: + */ + +typedef enum { /* bias modes (both domain and memory): */ + NBB_BIAS_MODE = 0, /* bias disabled */ + RBB_BIAS_MODE = 1, /* reverse back bias enabled */ + AFBB_BIAS_MODE = 2, /* asymmetrical forward bias */ + ARBB_BIAS_MODE = 3 /* asymmetrical reverse bias */ +} upwr_bias_mode_t; + +/* Domain Bias config (one per domain) */ + +typedef enum { + BIAS_APPLY_RTD, /* apply to RTD only */ + BIAS_APPLY_RTD_AVD, /* apply to RTD and AVD */ + BIAS_APPLY_APD, /* apply to APD only */ + BIAS_APPLY_APD_AVD, /* apply to APD and AVD */ + BIAS_APPLY_AVD, /* apply to AVD only */ + BIAS_APPLY_COUNT /* number of apply options */ +} upwr_bias_apply_t; + +typedef struct { + uint16_t rbbn; /* reverse back bias N well (mV) */ + uint16_t rbbp; /* reverse back bias P well (mV) */ +} upwr_rbb_t; + +struct upwr_dom_bias_cfg_t { + upwr_bias_apply_t apply; /* bias application option */ + upwr_bias_mode_t dommode; /* RTD/APD bias mode config */ + upwr_bias_mode_t avdmode; /* AVD bias mode config */ + upwr_rbb_t dombias; /* RTD/APD reverse back bias */ + upwr_rbb_t avdbias; /* AVD reverse back bias */ +}; + +/* bias struct used in power mode config definitions */ + +/** + * When write power mode transition program, please read below comments carefully. + * The structure and logic is complex, There is a lot of extension and reuse. + * + * First, for mode, extend "uint32_t mode" to a union struct, add support for AVD: + * typedef union { + * uint32_t R; + * struct { + * uint32_t mode : 8; + * uint32_t rsrv_1 : 8; + * uint32_t avd_mode : 8; + * uint32_t rsrv_2 : 8; + * } B; + * } dom_bias_mode_cfg_t; + + Second, if mode is AFBB mode, no need to configure rbbn and rbbp, uPower firmware + will configure all SRAM_AFBB_0 or SRAM_AFBB_1 for corresponding domain. + + Third, if mode is RBB mode, extend "uint32_t rbbn" and "uint32_t rbbp" to a union + struct, add support for AVD: + typedef union { + uint32_t R; + struct { + uint32_t lvl : 8; + uint32_t rsrv_1 : 8; + uint32_t avd_lvl : 8; + uint32_t rsrv_2 : 8; + } B; +} dom_bias_lvl_cfg_t; + + * + */ +typedef struct { + uint32_t mode; /* Domain bias mode config, extend to dom_bias_mode_cfg_t to support RTD, APD, AVD */ + uint32_t rbbn; /* reverse back bias N well */ + uint32_t rbbp; /* reverse back bias P well */ +} UPWR_DOM_BIAS_CFG_T; + +/*========================================================================= + * Memory bias + *========================================================================= + */ +/**+ + * upwr_pwm_chng_mem_bias() + * + * Argument struct contains only the field en, which can be either 1 (bias + * enabled) or 0 (bias disabled). + * + * Argument domain must be either RTD_DOMAIN (Real Time Domain) or APD_DOMAIN + * (Application Domain). + */ + +/* Memory Bias config */ +struct upwr_mem_bias_cfg_t { + uint32_t en; /* Memory bias enable config */ +}; + +/* bias struct used in power mode config definitions */ +typedef struct { + uint32_t en; /* Memory bias enable config */ +} UPWR_MEM_BIAS_CFG_T; + +/* Split different Bias */ +struct upwr_pmc_bias_cfg_t { + UPWR_DOM_BIAS_CFG_T dombias_cfg; /* Domain Bias config */ + UPWR_MEM_BIAS_CFG_T membias_cfg; /* Memory Bias config */ +}; + +/*========================================================================= + * Power modes + *========================================================================= + */ + +/* from msb->lsb: Azure bit, dual boot bit, low power boot bit */ +typedef enum { + SOC_BOOT_SINGLE = 0, + SOC_BOOT_LOW_PWR = 1, + SOC_BOOT_DUAL = 2, + SOC_BOOT_AZURE = 4 +} SOC_BOOT_TYPE_T; + +#ifdef UPWR_COMP_RAM +/* Power modes for RTD domain */ +typedef enum { + DPD_RTD_PWR_MODE, /* Real Time Deep Power Down mode */ + PD_RTD_PWR_MODE, /* Real Time Power Down mode */ + DSL_RTD_PWR_MODE, /* Real Time Domain Deep Sleep Mode */ + HLD_RTD_PWR_MODE, /* Real Time Domain Hold Mode */ + SLP_RTD_PWR_MODE, /* Sleep Mode */ + ADMA_RTD_PWR_MODE,/* Active DMA Mode */ + ACT_RTD_PWR_MODE, /* Active Domain Mode */ + NUM_RTD_PWR_MODES +} upwr_ps_rtd_pwr_mode_t; + +/* Abstract power modes */ +typedef enum { + DPD_PWR_MODE, + PD_PWR_MODE, + PACT_PWR_MODE, + DSL_PWR_MODE, + HLD_PWR_MODE, + SLP_PWR_MODE, + ADMA_PWR_MODE, + ACT_PWR_MODE, + NUM_PWR_MODES, + NUM_APD_PWR_MODES = NUM_PWR_MODES, + TRANS_PWR_MODE = NUM_PWR_MODES, + INVALID_PWR_MODE = TRANS_PWR_MODE + 1 +} abs_pwr_mode_t; +#else /* UPWR_COMP_RAM */ +/* Power modes for RTD domain */ +#define DPD_RTD_PWR_MODE (0U) /* Real Time Deep Power Down mode */ +#define PD_RTD_PWR_MODE (1U) /* Real Time Power Down mode */ +#define DSL_RTD_PWR_MODE (2U) /* Real Time Domain Deep Sleep Mode */ +#define HLD_RTD_PWR_MODE (3U) /* Real Time Domain Hold Mode */ +#define SLP_RTD_PWR_MODE (4U) /* Sleep Mode */ +#define ADMA_RTD_PWR_MODE (5U) /* Active DMA Mode */ +#define ACT_RTD_PWR_MODE (6U) /* Active Domain Mode */ +#define NUM_RTD_PWR_MODES (7U) + +typedef uint32_t upwr_ps_rtd_pwr_mode_t; + +/* Abstract power modes */ +#define DPD_PWR_MODE (0U) +#define PD_PWR_MODE (1U) +#define PACT_PWR_MODE (2U) +#define DSL_PWR_MODE (3U) +#define HLD_PWR_MODE (4U) +#define SLP_PWR_MODE (5U) +#define ADMA_PWR_MODE (6U) +#define ACT_PWR_MODE (7U) +#define NUM_PWR_MODES (8U) +#define NUM_APD_PWR_MODES NUM_PWR_MODES +#define TRANS_PWR_MODE NUM_PWR_MODES +#define INVALID_PWR_MODE (TRANS_PWR_MODE + 1U) + +typedef uint32_t abs_pwr_mode_t; +#endif /* UPWR_COMP_RAM */ + +typedef struct { + abs_pwr_mode_t mode; + bool ok; +} pch_trans_t; + +typedef pch_trans_t rtd_trans_t; + +typedef struct { + abs_pwr_mode_t mode; + pch_trans_t core[UPWR_APD_CORES]; +} apd_trans_t; + +/* Codes for APD pwr mode as programmed in LPMODE reg */ +typedef enum { + ACT_APD_LPM, + SLP_APD_LPM = 1, + DSL_APD_LPM = 3, + PACT_APD_LPM = 7, + PD_APD_LPM = 15, + DPD_APD_LPM = 31, + HLD_APD_LPM = 63 +} upwr_apd_lpm_t; + +/* PowerSys low power config */ +struct upwr_powersys_cfg_t { + uint32_t lpm_mode; /* Powersys low power mode */ +}; + +/*=************************************************************************* + * RTD + *=*************************************************************************/ +/* Config pmc PADs */ +struct upwr_pmc_pad_cfg_t { + uint32_t pad_close; /* PMC PAD close config */ + uint32_t pad_reset; /* PMC PAD reset config */ + uint32_t pad_tqsleep; /* PMC PAD TQ Sleep config */ +}; + +/* Config regulator (internal and external) */ +struct upwr_reg_cfg_t { + uint32_t volt; /* Regulator voltage config */ + uint32_t mode; /* Regulator mode config */ +}; + +/* Config pmc monitors */ +struct upwr_pmc_mon_cfg_t { + uint32_t mon_hvd_en; /* PMC mon HVD */ + uint32_t mon_lvd_en; /* PMC mon LVD */ + uint32_t mon_lvdlvl; /* PMC mon LVDLVL */ +}; + +/* Same monitor config for RTD (for compatibility) */ +#define upwr_pmc_mon_rtd_cfg_t upwr_pmc_mon_cfg_t + +typedef swt_config_t ps_rtd_swt_cfgs_t[NUM_RTD_PWR_MODES]; +typedef swt_config_t ps_apd_swt_cfgs_t[NUM_APD_PWR_MODES]; + +/*=************************************************************************* + * APD + *=*************************************************************************/ + +/* PowerSys PMIC config */ +struct upwr_pmic_cfg_t { + uint32_t volt; + uint32_t mode; + uint32_t mode_msk; +}; + +typedef uint32_t offs_t; + +struct ps_apd_pwr_mode_cfg_t { + #ifdef UPWR_SIMULATOR_ONLY + struct upwr_switch_board_t *swt_board_offs; + struct upwr_mem_switches_t *swt_mem_offs; + #else + offs_t swt_board_offs; + offs_t swt_mem_offs; + #endif + struct upwr_pmic_cfg_t pmic_cfg; + struct upwr_pmc_pad_cfg_t pad_cfg; + struct upwr_pmc_bias_cfg_t bias_cfg; +}; + +/* Get the pointer to swt config */ +static inline struct upwr_switch_board_t* +get_apd_swt_cfg(volatile struct ps_apd_pwr_mode_cfg_t *cfg) +{ + char *ptr; + + ptr = (char *)cfg; + ptr += (uint64_t)cfg->swt_board_offs; + return (struct upwr_switch_board_t *)ptr; +} + +/* Get the pointer to mem config */ +static inline struct upwr_mem_switches_t* +get_apd_mem_cfg(volatile struct ps_apd_pwr_mode_cfg_t *cfg) +{ + char *ptr; + + ptr = (char *)cfg; + ptr += (uint64_t)cfg->swt_mem_offs; + return (struct upwr_mem_switches_t *)ptr; +} + +/* Power Mode configuration */ + +#define ps_rtd_pwr_mode_cfg_t upwr_power_mode_cfg_t + +/* these typedefs are just for RISC-V sizeof purpose */ +typedef uint32_t swt_board_ptr_t; +typedef uint32_t swt_mem_ptr_t; + +struct upwr_power_mode_cfg_t { + #ifdef UPWR_SIMULATOR_ONLY + struct upwr_switch_board_t *swt_board; /* Swt board for mem. */ + struct upwr_mem_switches_t *swt_mem; /* Swt to mem. arrays, perif */ + #else + #ifdef __LP64__ + uint32_t swt_board; + uint32_t swt_mem; + #else + struct upwr_switch_board_t *swt_board; /* Swt board for mem. */ + struct upwr_mem_switches_t *swt_mem; /* Swt to mem. arrays, perif */ + #endif + #endif + struct upwr_reg_cfg_t in_reg_cfg; /* internal regulator config*/ + struct upwr_reg_cfg_t pmic_cfg; /* external regulator - pmic*/ + struct upwr_pmc_pad_cfg_t pad_cfg; /* Pad conf for power trans*/ + struct upwr_pmc_mon_rtd_cfg_t mon_cfg; /*monitor configuration */ + struct upwr_pmc_bias_cfg_t bias_cfg; /* Memory/Domain Bias conf */ + struct upwr_powersys_cfg_t pwrsys_lpm_cfg; /* pwrsys low power config*/ +}; + +static inline unsigned int upwr_sizeof_pmode_cfg(uint32_t domain) +{ + switch (domain) { + case RTD_DOMAIN: + return sizeof(struct upwr_power_mode_cfg_t) + + (sizeof(struct upwr_switch_board_t)* + UPWR_PMC_SWT_WORDS) + + (sizeof(struct upwr_mem_switches_t)* + UPWR_PMC_MEM_WORDS) - + 2U * (sizeof(void *) - sizeof(swt_board_ptr_t)); + + /* fall through */ + case APD_DOMAIN: + return sizeof(struct ps_apd_pwr_mode_cfg_t) + + (sizeof(struct upwr_switch_board_t)* + UPWR_PMC_SWT_WORDS) + + (sizeof(struct upwr_mem_switches_t)* + UPWR_PMC_MEM_WORDS); + + /* fall through */ + default: + break; + } + + return 0; +} + +/*=************************************************************************* + * All configs + *=*************************************************************************/ + +/* LVD/HVD monitor config for a single domain */ + +/* Domain + AVD monitor config + * For RTD, mapped in mon_cfg.mon_hvd_en + * For APD, mapped temporarily in pad_cfg.pad_tqsleep + */ +typedef union upwr_mon_cfg_union_t { + volatile uint32_t R; + struct { + /* Original config, not change */ + volatile uint32_t rsrv_1 : 8; + /* DOM */ + volatile uint32_t dom_lvd_irq_ena : 1; + volatile uint32_t dom_lvd_rst_ena : 1; + volatile uint32_t dom_hvd_irq_ena : 1; + volatile uint32_t dom_hvd_rst_ena : 1; + volatile uint32_t dom_lvd_lvl : 4; + volatile uint32_t dom_lvd_ena : 1; + volatile uint32_t dom_hvd_ena : 1; + /* AVD */ + volatile uint32_t avd_lvd_irq_ena : 1; + volatile uint32_t avd_lvd_rst_ena : 1; + volatile uint32_t avd_hvd_irq_ena : 1; + volatile uint32_t avd_hvd_rst_ena : 1; + volatile uint32_t avd_lvd_lvl : 4; + volatile uint32_t avd_lvd_ena : 1; + volatile uint32_t avd_hvd_ena : 1; + } B; +} upwr_mon_cfg_t; + +/* Get the monitor config word from RAM (domaind and AVD) */ +static inline uint32_t get_mon_cfg(uint8_t dom, void *mode_cfg) +{ + if (dom == RTD_DOMAIN) { + return ((struct ps_rtd_pwr_mode_cfg_t *)mode_cfg)->mon_cfg.mon_hvd_en; + } else { + return ((struct ps_apd_pwr_mode_cfg_t *)mode_cfg)->pad_cfg.pad_tqsleep; + } +} + +/* Set the monitor config word in RAM (domaind and AVD) */ +static inline void set_mon_cfg(uint8_t dom, void *mode_cfg, + upwr_mon_cfg_t mon_cfg) +{ + uint32_t *cfg; + + if (dom == RTD_DOMAIN) { + cfg = (uint32_t *)&((struct ps_rtd_pwr_mode_cfg_t *)mode_cfg)->mon_cfg.mon_hvd_en; + } else { + cfg = (uint32_t *)&((struct ps_apd_pwr_mode_cfg_t *)mode_cfg)->pad_cfg.pad_tqsleep; + } + + *cfg = mon_cfg.R; +} + +#define PMIC_REG_VALID_TAG 0xAAU + +/** + * limit the max pmic register->value count to 8 + * each data cost 4 Bytes, totally 32 Bytes + */ +#define MAX_PMIC_REG_COUNT 0x8U + +/** + * the configuration structure for PMIC register setting + * + * @ tag: The TAG number to judge if the data is valid or not, valid tag is PMIC_REG_VALID_TAG + * @ power_mode : corresponding to each domain's power mode + * RTD refer to upwr_ps_rtd_pwr_mode_t + * APD refer to abs_pwr_mode_t + * @ i2c_addr : i2c address + * @ i2c_data : i2c data value + */ +struct ps_pmic_reg_data_cfg_t { + uint32_t tag : 8; + uint32_t power_mode : 8; + uint32_t i2c_addr : 8; + uint32_t i2c_data : 8; +}; + +/* Uniformize access to PMIC cfg for RTD and APD */ + +typedef union { + struct upwr_reg_cfg_t RTD; + struct upwr_pmic_cfg_t APD; +} pmic_cfg_t; + +/* Access to PMIC mode mask and AVD mode */ + +typedef union { + uint32_t R; + struct { + uint8_t mode; /* Domain PMIC mode */ + uint8_t msk; /* Domain PMIC mode mask */ + uint8_t avd_mode; /* AVD PMIC mode */ + uint8_t avd_msk; /* AVD PMIC mode mask */ + } B; +} pmic_mode_cfg_t; + +/* Access RTD, APD and AVD modes and masks */ +static inline pmic_mode_cfg_t *get_pmic_mode_cfg(uint8_t dom, pmic_cfg_t *cfg) +{ + uint32_t *mode_cfg; + + if (dom == RTD_DOMAIN) { + mode_cfg = &cfg->RTD.mode; + } else { + mode_cfg = &cfg->APD.mode; + } + + return (pmic_mode_cfg_t *)mode_cfg; +} + +static inline uint8_t get_pmic_mode(uint8_t dom, pmic_cfg_t *cfg) +{ + return get_pmic_mode_cfg(dom, cfg)->B.mode; +} + +static inline void set_pmic_mode(uint8_t dom, pmic_cfg_t *cfg, uint8_t mode) +{ + get_pmic_mode_cfg(dom, cfg)->B.mode = mode; +} + +static inline uint32_t get_pmic_mode_msk(uint8_t dom, pmic_cfg_t *cfg) +{ + pmic_mode_cfg_t *mode_cfg; + + if (dom == RTD_DOMAIN) { + mode_cfg = (pmic_mode_cfg_t *)&cfg->RTD.mode; + return mode_cfg->B.msk; + } else { + return cfg->APD.mode_msk; + } +} + +/* Getters and setters for AVD mode and mask */ +static inline uint8_t get_avd_pmic_mode(uint8_t dom, pmic_cfg_t *cfg) +{ + return get_pmic_mode_cfg(dom, cfg)->B.avd_mode; +} + +static inline void set_avd_pmic_mode(uint8_t dom, pmic_cfg_t *cfg, uint8_t mode) +{ + get_pmic_mode_cfg(dom, cfg)->B.avd_mode = mode; +} + +static inline uint8_t get_avd_pmic_mode_msk(uint8_t dom, pmic_cfg_t *cfg) +{ + return get_pmic_mode_cfg(dom, cfg)->B.avd_msk; +} + +static inline void set_avd_pmic_mode_msk(uint8_t dom, + pmic_cfg_t *cfg, + uint8_t msk) +{ + get_pmic_mode_cfg(dom, cfg)->B.avd_msk = msk; +} + +struct ps_delay_cfg_t { + uint32_t tag : 8U; + uint32_t rsv : 8U; + uint32_t exitdelay : 16U; // exit delay in us +}; + +#define PS_DELAY_TAG 0xA5U + +/* max exit delay = 0xffff = 65535 us = 65.5 ms (it is enough...) */ +/* with 8 bits, 256 -> not enough */ + +typedef struct ps_delay_cfg_t ps_rtd_delay_cfgs_t[NUM_RTD_PWR_MODES]; +typedef struct ps_delay_cfg_t ps_apd_delay_cfgs_t[NUM_APD_PWR_MODES]; + +typedef struct ps_rtd_pwr_mode_cfg_t ps_rtd_pwr_mode_cfgs_t[NUM_RTD_PWR_MODES]; +typedef struct ps_apd_pwr_mode_cfg_t ps_apd_pwr_mode_cfgs_t[NUM_APD_PWR_MODES]; +typedef struct ps_pmic_reg_data_cfg_t ps_rtd_pmic_reg_data_cfgs_t[MAX_PMIC_REG_COUNT]; +typedef struct ps_pmic_reg_data_cfg_t ps_apd_pmic_reg_data_cfgs_t[MAX_PMIC_REG_COUNT]; + +struct ps_pwr_mode_cfg_t { + ps_rtd_pwr_mode_cfgs_t ps_rtd_pwr_mode_cfg; + ps_rtd_swt_cfgs_t ps_rtd_swt_cfg; + ps_apd_pwr_mode_cfgs_t ps_apd_pwr_mode_cfg; + ps_apd_swt_cfgs_t ps_apd_swt_cfg; + ps_rtd_pmic_reg_data_cfgs_t ps_rtd_pmic_reg_data_cfg; + ps_apd_pmic_reg_data_cfgs_t ps_apd_pmic_reg_data_cfg; + ps_rtd_delay_cfgs_t ps_rtd_delay_cfg; + ps_apd_delay_cfgs_t ps_apd_delay_cfg; + +}; + +#define UPWR_XCP_MIN_ADDR (0x28350000U) +#define UPWR_XCP_MAX_ADDR (0x2836FFFCU) + +struct upwr_reg_access_t { + uint32_t addr; + uint32_t data; + uint32_t mask; /* mask=0 commands read */ +}; + +typedef upwr_pointer_msg upwr_xcp_access_msg; + +/* unions for the shared memory buffer */ + +typedef union { + struct upwr_reg_access_t reg_access; +} upwr_xcp_union_t; + +typedef union { + struct { + struct ps_rtd_pwr_mode_cfg_t rtd_struct; + struct upwr_switch_board_t rtd_switch; + struct upwr_mem_switches_t rtd_memory; + } rtd_pwr_mode; + struct { + struct ps_apd_pwr_mode_cfg_t apd_struct; + struct upwr_switch_board_t apd_switch; + struct upwr_mem_switches_t apd_memory; + } apd_pwr_mode; +} upwr_pwm_union_t; + +#define MAX_SG_EXCEPT_MEM_SIZE sizeof(upwr_xcp_union_t) +#define MAX_SG_PWRMGMT_MEM_SIZE sizeof(upwr_pwm_union_t) + +/** + * VOLTM group need shared memory for PMIC IC configuration + * 256 Bytes is enough for PMIC register array + */ +#define MAX_SG_VOLTM_MEM_SIZE 256U + +#endif /* UPWR_SOC_DEFS_H */ diff --git a/plat/imx/imx8ulp/xrdc/xrdc_config.h b/plat/imx/imx8ulp/xrdc/xrdc_config.h new file mode 100644 index 00000000..d2af55cd --- /dev/null +++ b/plat/imx/imx8ulp/xrdc/xrdc_config.h @@ -0,0 +1,136 @@ +/* + * Copyright 2020-2024 NXP + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <xrdc.h> + +#define SP(X) ((X) << 9) +#define SU(X) ((X) << 6) +#define NP(X) ((X) << 3) +#define NU(X) ((X) << 0) + +#define RWX 7 +#define RW 6 +#define R 4 +#define X 1 + +struct xrdc_mda_config imx8ulp_mda[] = { + { 0, 7, MDA_SA_PT }, /* A core */ + { 1, 1, MDA_SA_NS }, /* DMA1 */ + { 2, 1, MDA_SA_NS }, /* USB */ + { 3, 1, MDA_SA_NS }, /* PXP-> .M10 */ + { 4, 1, MDA_SA_NS }, /* ENET */ + { 5, 1, MDA_SA_PT }, /* CAAM */ + { 6, 1, MDA_SA_NS }, /* USDHC0 */ + { 7, 1, MDA_SA_NS }, /* USDHC1 */ + { 8, 1, MDA_SA_NS }, /* USDHC2 */ + { 9, 2, MDA_SA_NS }, /* HIFI4 */ + { 10, 3, MDA_SA_NS }, /* GPU3D */ + { 11, 3, MDA_SA_NS }, /* GPU2D */ + { 12, 3, MDA_SA_NS }, /* EPDC */ + { 13, 3, MDA_SA_NS }, /* DCNano */ + { 14, 3, MDA_SA_NS }, /* ISI */ + { 15, 3, MDA_SA_NS }, /* PXP->NIC_LPAV.M0 */ + { 16, 3, MDA_SA_NS }, /* DMA2 */ +}; + +#ifdef SPD_opteed +#define TEE_SHM_SIZE 0x400000 +#else +#define TEE_SHM_SIZE 0x0 +#endif + +#if defined(SPD_opteed) || defined(SPD_trusty) +#define DRAM_MEM_0_START (0x80000000) +#define DRAM_MEM_0_SIZE (BL32_BASE - 0x80000000) + +#define DRAM_MEM_1_START (BL32_BASE) +#define DRAM_MEM_1_SIZE (BL32_SIZE - TEE_SHM_SIZE) + +#ifndef SPD_trusty +#define DRAM_MEM_2_START (DRAM_MEM_1_START + DRAM_MEM_1_SIZE) +#define DRAM_MEM_2_SIZE (0x80000000 - DRAM_MEM_1_SIZE - DRAM_MEM_0_SIZE) +#else +#define SECURE_HEAP_START (0xA9600000) +#define SECURE_HEAP_SIZE (0x6000000) +#define DRAM_MEM_END (0x100000000) + +#define DRAM_MEM_2_START (DRAM_MEM_1_START + DRAM_MEM_1_SIZE) +#define DRAM_MEM_2_SIZE (SECURE_HEAP_START - DRAM_MEM_2_START) +#define DRAM_MEM_3_START (DRAM_MEM_2_START + DRAM_MEM_2_SIZE) +#define DRAM_MEM_3_SIZE (SECURE_HEAP_SIZE) +#define DRAM_MEM_4_START (DRAM_MEM_3_START + DRAM_MEM_3_SIZE) +#define DRAM_MEM_4_SIZE (DRAM_MEM_END - DRAM_MEM_4_START) +#endif +#endif + +struct xrdc_mrc_config imx8ulp_mrc[] = { + { 0, 0, 0x0, 0x30000, {0, 0, 0, 0, 0, 0, 0, 1}, {0xfff, 0} }, /* ROM1 */ + { 1, 0, 0x60000000, 0x10000000, {1, 1, 0, 0, 1, 0, 1, 1}, {0xfff, 0} }, /* Flexspi2 */ + { 2, 0, 0x22020000, 0x40000, {1, 1, 0, 0, 1, 0, 1, 1}, {0xfff, 0} }, /* SRAM2 */ + { 3, 0, 0x22010000, 0x10000, {1, 1, 0, 0, 1, 0, 1, 1}, {0xfff, 0} }, /* SRAM0 */ +#if defined(SPD_opteed) || defined(SPD_trusty) + { 4, 0, DRAM_MEM_0_START, DRAM_MEM_0_SIZE, {0, 1, 0, 0, 0, 0, 0, 1}, {0xfff, 0} }, /* DRAM for A35, DMA1, USDHC0*/ + { 4, 1, DRAM_MEM_1_START, DRAM_MEM_1_SIZE, {0, 1, 0, 0, 0, 0, 0, 1}, {0xfc0, 0} }, /* TEE DRAM for A35, DMA1, USDHC0*/ + { 4, 2, DRAM_MEM_2_START, DRAM_MEM_2_SIZE, {0, 1, 0, 0, 0, 0, 0, 1}, {0xfff, 0} }, /* DRAM for A35, DMA1, USDHC0*/ +#ifdef SPD_trusty + { 4, 3, DRAM_MEM_3_START, DRAM_MEM_3_SIZE, {0, 1, 0, 0, 0, 0, 0, 1}, {0xfc0, 0} }, /* DRAM for A35, DMA1, USDHC0*/ + { 4, 4, DRAM_MEM_4_START, DRAM_MEM_4_SIZE, {0, 1, 0, 0, 0, 0, 0, 1}, {0xfff, 0} }, /* DRAM for A35, DMA1, USDHC0*/ +#endif + + { 5, 0, DRAM_MEM_0_START, DRAM_MEM_0_SIZE, {0, 1, 0, 0, 0, 0, 0, 0}, {0xfff, 0} }, /* DRAM for NIC_PER */ + { 5, 1, DRAM_MEM_1_START, DRAM_MEM_1_SIZE, {0, 1, 0, 0, 0, 0, 0, 0}, {0xfc0, 0} }, /* TEE DRAM for NIC_PER */ + { 5, 2, DRAM_MEM_2_START, DRAM_MEM_2_SIZE, {0, 1, 0, 0, 0, 0, 0, 0}, {0xfff, 0} }, /* DRAM for NIC_PER */ +#ifdef SPD_trusty + { 5, 3, DRAM_MEM_3_START, DRAM_MEM_3_SIZE, {0, 1, 0, 0, 0, 0, 0, 0}, {0xfc0, 0} }, /* DRAM for NIC_PER */ + { 5, 4, DRAM_MEM_4_START, DRAM_MEM_4_SIZE, {0, 1, 0, 0, 0, 0, 0, 0}, {0xfff, 0} }, /* DRAM for NIC_PER */ +#endif + +#ifdef SPD_trusty + { 6, 0, DRAM_MEM_0_START, DRAM_MEM_0_SIZE, {1, 1, 0, 2, 1, 0, 1, 1}, {0xfff, 0x93f} }, /* DRAM for LPAV and RTD*/ + { 6, 1, DRAM_MEM_1_START, DRAM_MEM_1_SIZE, {1, 1, 0, 1, 1, 0, 1, 1}, {0xfc0, 0} }, /* TEE DRAM for LPAV and RTD*/ + { 6, 2, DRAM_MEM_2_START, DRAM_MEM_2_SIZE, {1, 1, 0, 2, 1, 0, 1, 1}, {0xfff, 0x93f} }, /* DRAM for LPAV and RTD*/ + { 6, 3, DRAM_MEM_3_START, DRAM_MEM_3_SIZE, {1, 1, 0, 1, 1, 0, 1, 1}, {0xfc0, 0} }, /* DRAM for LPAV and RTD*/ + { 6, 4, DRAM_MEM_4_START, DRAM_MEM_4_SIZE, {1, 1, 0, 2, 1, 0, 1, 1}, {0xfff, 0x93f} }, /* DRAM for LPAV and RTD*/ +#else + { 6, 0, DRAM_MEM_0_START, DRAM_MEM_0_SIZE, {1, 1, 0, 1, 1, 0, 1, 1}, {0xfff, 0} }, /* DRAM for LPAV and RTD*/ + { 6, 1, DRAM_MEM_1_START, DRAM_MEM_1_SIZE, {1, 1, 0, 1, 1, 0, 1, 1}, {0xfc0, 0} }, /* TEE DRAM for LPAV and RTD*/ + { 6, 2, DRAM_MEM_2_START, DRAM_MEM_2_SIZE, {1, 1, 0, 1, 1, 0, 1, 1}, {0xfff, 0} }, /* DRAM for LPAV and RTD*/ +#endif +#else + { 4, 0, 0x80000000, 0x80000000, {0, 1, 0, 0, 0, 0, 0, 1}, {0xfff, 0} }, /* DRAM for A35, DMA1, USDHC0*/ + { 5, 0, 0x80000000, 0x80000000, {0, 1, 0, 0, 0, 0, 0, 0}, {0xfff, 0} }, /* DRAM for NIC_PER */ + { 6, 0, 0x80000000, 0x80000000, {1, 1, 0, 1, 1, 0, 1, 1}, {0xfff, 0} }, /* DRAM for LPAV and RTD*/ +#endif + { 7, 0, 0x80000000, 0x10000000, {0, 0, 1, 0, 0, 0, 0, 0}, {0xfff, 0} }, /* DRAM for HIFI4 */ + { 7, 1, 0x90000000, 0x10000000, {0, 0, 1, 0, 0, 0, 0, 0}, {0xfff, 0} }, /* DRAM for HIFI4 */ + { 8, 0, 0x21000000, 0x10000, {1, 1, 1, 1, 1, 0, 1, 1}, {0xfff, 0} }, /* SRAM1 */ + { 9, 0, 0x1ffc0000, 0xc0000, {0, 0, 0, 0, 0, 0, 0, 0}, {0, 0} }, /* SSRAM for HIFI4 */ + { 10, 0, 0x1ffc0000, 0xc0000, {0, 0, 0, 0, 0, 0, 0, 0}, {0, 0} }, /* SSRAM for LPAV */ + { 11, 0, 0x21170000, 0x10000, {0, 0, 1, 0, 0, 0, 0, 2}, {0xfff, SP(RW) | SU(RW) | NP(RW)} }, /* HIFI4 TCM */ + { 11, 1, 0x21180000, 0x10000, {0, 0, 1, 0, 0, 0, 0, 2}, {SP(RW) | SU(RW) | NP(RW) | NU(RW), SP(RW) | SU(RW) | NP(RW)} }, /* HIFI4 TCM */ + { 12, 0, 0x2d400000, 0x100000, {0, 0, 0, 0, 0, 0, 0, 1}, {SP(RW) | SU(RW) | NP(RW) | NU(RW), 0} }, /* GIC500 */ +}; + +struct xrdc_pac_msc_config imx8ulp_pdac[] = { + { 0, PAC_SLOT_ALL, {0, 7, 0, 0, 0, 0, 0, 7} }, /* PAC0 */ + { 0, 44, {0, 7, 7, 0, 0, 0, 0, 7} }, /* PAC0 slot 44 for CGC1 */ + { 0, 36, {0, 0, 0, 0, 0, 0, 7, 7} }, /* PAC0 slot 36 for CMC1 */ + { 0, 41, {0, 0, 0, 0, 0, 0, 7, 7} }, /* PAC0 slot 41 for SIM_AD */ + { 1, PAC_SLOT_ALL, {0, 7, 0, 0, 0, 0, 0, 7} }, /* PAC1 */ + { 1, 0, {0, 7, 7, 0, 0, 0, 7, 7} }, /* PAC1 slot 0 for PCC4 */ + { 1, 6, {0, 7, 7, 0, 0, 0, 0, 7} }, /* PAC1 slot 6 for LPUART6 */ + { 1, 7, {0, 7, 7, 0, 0, 0, 0, 7} }, /* PAC1 slot 7 for LPUART7 */ + { 1, 9, {0, 7, 7, 7, 0, 0, 0, 7} }, /* SAI5 for HIFI4 and eDMA2 */ + { 1, 12, {0, 7, 7, 0, 0, 0, 7, 7} }, /* PAC1 slot 12 for IOMUXC1 */ + { 2, PAC_SLOT_ALL, {7, 7, 7, 7, 0, 0, 7, 7} }, /* PAC2 */ +}; + +struct xrdc_pac_msc_config imx8ulp_msc[] = { + { 0, 0, {0, 0, 0, 0, 0, 0, 7, 7} }, /* MSC0 GPIOE */ + { 0, 1, {0, 0, 0, 0, 0, 0, 7, 7} }, /* MSC0 GPIOF */ + { 1, MSC_SLOT_ALL, {0, 0, 0, 0, 0, 0, 7, 7} }, /* MSC1 GPIOD */ + { 2, MSC_SLOT_ALL, {0, 0, 0, 0, 0, 0, 7, 7} }, /* MSC2 GPU3D/2D/DCNANO/DDR registers */ +}; diff --git a/plat/imx/imx8ulp/xrdc/xrdc_core.c b/plat/imx/imx8ulp/xrdc/xrdc_core.c new file mode 100644 index 00000000..d022e4ca --- /dev/null +++ b/plat/imx/imx8ulp/xrdc/xrdc_core.c @@ -0,0 +1,327 @@ +/* + * Copyright 2020-2024 NXP + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <assert.h> +#include <errno.h> +#include <stdbool.h> + +#include <common/debug.h> +#include <lib/mmio.h> +#include <plat/common/platform.h> +#include <platform_def.h> + +#include "xrdc_config.h" + +#define XRDC_ADDR 0x292f0000 +#define MRC_OFFSET 0x2000 +#define MRC_STEP 0x200 + +#define XRDC_MGR_PAC_ID U(0) +#define XRDC_MGR_PAC_SLOT U(47) + +enum xrdc_comp_type { + MDA_TYPE = (1 << 16), + MRC_TYPE = (2 << 16), + PAC_TYPE = (3 << 16), + MSC_TYPE = (4 << 16), +}; + +enum xrdc_pd_type { + XRDC_AD_PD, + XRDC_HIFI_PD, + XRDC_AV_PD, +}; + +#define XRDC_TYPE_MASK (0x7 << 16) +#define XRDC_ID_MASK 0xFFFF +#define XRDC_ID(id) ((id) & XRDC_ID_MASK) + +typedef bool (*xrdc_check_func)(enum xrdc_comp_type type, uint16_t id); + +/* Access below XRDC needs enable PS 8 + * and HIFI clocks and release HIFI firstly + */ +uint32_t hifi_xrdc_list[] = { + (MDA_TYPE | XRDC_ID(9)), + (MRC_TYPE | XRDC_ID(7)), + (MRC_TYPE | XRDC_ID(9)), + (MRC_TYPE | XRDC_ID(11)), +}; + +/* Access below XRDC needs enable PS 16 firstly */ +uint32_t av_periph_xrdc_list[] = { + (MDA_TYPE | XRDC_ID(10)), + (MDA_TYPE | XRDC_ID(11)), + (MDA_TYPE | XRDC_ID(12)), + (MDA_TYPE | XRDC_ID(13)), + (MDA_TYPE | XRDC_ID(14)), + (MDA_TYPE | XRDC_ID(15)), + (MDA_TYPE | XRDC_ID(16)), + + (PAC_TYPE | XRDC_ID(2)), + + (MRC_TYPE | XRDC_ID(6)), + (MRC_TYPE | XRDC_ID(8)), + (MRC_TYPE | XRDC_ID(10)), + + (MSC_TYPE | XRDC_ID(1)), + (MSC_TYPE | XRDC_ID(2)), +}; + +uint32_t imx8ulp_pac_slots[] = { + 61, 23, 53 +}; + +uint32_t imx8ulp_msc_slots[] = { + 2, 1, 7 +}; + +static int xrdc_config_mrc_w0_w1(uint32_t mrc_con, uint32_t region, uint32_t w0, uint32_t size) +{ + + uint32_t w0_addr, w1_addr; + + w0_addr = XRDC_ADDR + MRC_OFFSET + mrc_con * 0x200 + region * 0x20; + w1_addr = w0_addr + 4; + + if ((size % 32) != 0) { + return -EINVAL; + } + + mmio_write_32(w0_addr, w0 & ~0x1f); + mmio_write_32(w1_addr, w0 + size - 1); + + return 0; +} + +static int xrdc_config_mrc_w2(uint32_t mrc_con, uint32_t region, uint32_t dxsel_all) +{ + uint32_t w2_addr; + + w2_addr = XRDC_ADDR + MRC_OFFSET + mrc_con * 0x200 + region * 0x20 + 0x8; + + mmio_write_32(w2_addr, dxsel_all); + + return 0; +} + +static int xrdc_config_mrc_w3_w4(uint32_t mrc_con, uint32_t region, uint32_t w3, uint32_t w4) +{ + uint32_t w3_addr = XRDC_ADDR + MRC_OFFSET + mrc_con * 0x200 + region * 0x20 + 0xC; + uint32_t w4_addr = w3_addr + 4; + + mmio_write_32(w3_addr, w3); + mmio_write_32(w4_addr, w4); + + return 0; +} + +static int xrdc_config_pac(uint32_t pac, uint32_t index, uint32_t dxacp) +{ + uint32_t w0_addr; + uint32_t val; + + if (pac > 2U) { + return -EINVAL; + } + + /* Skip the PAC slot for XRDC MGR, use Sentinel configuration */ + if (pac == XRDC_MGR_PAC_ID && index == XRDC_MGR_PAC_SLOT) { + return 0; + } + + w0_addr = XRDC_ADDR + 0x1000 + 0x400 * pac + 0x8 * index; + + mmio_write_32(w0_addr, dxacp); + + val = mmio_read_32(w0_addr + 4); + mmio_write_32(w0_addr + 4, val | BIT_32(31)); + + return 0; +} + +static int xrdc_config_msc(uint32_t msc, uint32_t index, uint32_t dxacp) +{ + uint32_t w0_addr; + uint32_t val; + + if (msc > 2) { + return -EINVAL; + } + + w0_addr = XRDC_ADDR + 0x4000 + 0x400 * msc + 0x8 * index; + + mmio_write_32(w0_addr, dxacp); + + val = mmio_read_32(w0_addr + 4); + mmio_write_32(w0_addr + 4, val | BIT_32(31)); + + return 0; +} + +static int xrdc_config_mda(uint32_t mda_con, uint32_t dom, enum xrdc_mda_sa sa) +{ + uint32_t w0_addr; + uint32_t val; + + w0_addr = XRDC_ADDR + 0x800 + mda_con * 0x20; + + val = mmio_read_32(w0_addr); + + if (val & BIT_32(29)) { + mmio_write_32(w0_addr, (val & (~0xFF)) | dom | + BIT_32(31) | 0x20 | ((sa & 0x3) << 6)); + } else { + mmio_write_32(w0_addr, dom | BIT_32(31)); + mmio_write_32(w0_addr + 0x4, dom | BIT_32(31)); + } + + return 0; +} + +static bool xrdc_check_pd(enum xrdc_comp_type type, + uint16_t id, enum xrdc_pd_type pd) +{ + unsigned int i, size; + uint32_t item = type | XRDC_ID(id); + uint32_t *list; + + if (pd == XRDC_HIFI_PD) { + size = ARRAY_SIZE(hifi_xrdc_list); + list = hifi_xrdc_list; + } else if (pd == XRDC_AV_PD) { + size = ARRAY_SIZE(av_periph_xrdc_list); + list = av_periph_xrdc_list; + } else { + return false; + } + + for (i = 0U; i < size; i++) { + if (item == list[i]) { + return true; + } + } + + return false; +} + +static bool xrdc_check_lpav(enum xrdc_comp_type type, uint16_t id) +{ + return xrdc_check_pd(type, id, XRDC_AV_PD); +} + +static bool xrdc_check_hifi(enum xrdc_comp_type type, uint16_t id) +{ + return xrdc_check_pd(type, id, XRDC_HIFI_PD); +} + +static bool xrdc_check_ad(enum xrdc_comp_type type, uint16_t id) +{ + return (!xrdc_check_pd(type, id, XRDC_HIFI_PD) && + !xrdc_check_pd(type, id, XRDC_AV_PD)); +} + +static int xrdc_apply_config(xrdc_check_func check_func) +{ + unsigned int i, j; + uint32_t val; + + for (i = 0U; i < ARRAY_SIZE(imx8ulp_mda); i++) { + if (check_func(MDA_TYPE, imx8ulp_mda[i].mda_id)) { + xrdc_config_mda(imx8ulp_mda[i].mda_id, + imx8ulp_mda[i].did, imx8ulp_mda[i].sa); + } + } + + for (i = 0U; i < ARRAY_SIZE(imx8ulp_mrc); i++) { + if (check_func(MRC_TYPE, imx8ulp_mrc[i].mrc_id)) { + xrdc_config_mrc_w0_w1(imx8ulp_mrc[i].mrc_id, + imx8ulp_mrc[i].region_id, + imx8ulp_mrc[i].region_start, + imx8ulp_mrc[i].region_size); + + val = 0; + for (j = 0U; j < DID_MAX; j++) { + val |= imx8ulp_mrc[i].dsel[j] << (3 * j); + } + + xrdc_config_mrc_w2(imx8ulp_mrc[i].mrc_id, imx8ulp_mrc[i].region_id, val); + xrdc_config_mrc_w3_w4(imx8ulp_mrc[i].mrc_id, imx8ulp_mrc[i].region_id, + 0, imx8ulp_mrc[i].accset[0] | (imx8ulp_mrc[i].accset[1] << 16) | BIT_32(31)); + } + } + + for (i = 0U; i < ARRAY_SIZE(imx8ulp_pdac); i++) { + if (check_func(PAC_TYPE, imx8ulp_pdac[i].pac_msc_id)) { + val = 0; + for (j = 0U; j < DID_MAX; j++) { + val |= imx8ulp_pdac[i].dsel[j] << (3 * j); + } + + if (imx8ulp_pdac[i].slot_id == PAC_SLOT_ALL) { + /* Apply to all slots*/ + for (j = 0U; j < imx8ulp_pac_slots[imx8ulp_pdac[i].pac_msc_id]; j++) { + xrdc_config_pac(imx8ulp_pdac[i].pac_msc_id, j, val); + } + } else { + if (imx8ulp_pdac[i].slot_id >= imx8ulp_pac_slots[imx8ulp_pdac[i].pac_msc_id]) { + return -EINVAL; + } + + xrdc_config_pac(imx8ulp_pdac[i].pac_msc_id, imx8ulp_pdac[i].slot_id, val); + } + } + } + + for (i = 0U; i < ARRAY_SIZE(imx8ulp_msc); i++) { + if (check_func(MSC_TYPE, imx8ulp_msc[i].pac_msc_id)) { + val = 0; + for (j = 0U; j < DID_MAX; j++) { + val |= imx8ulp_msc[i].dsel[j] << (3 * j); + } + + if (imx8ulp_msc[i].slot_id == MSC_SLOT_ALL) { + /* Apply to all slots*/ + for (j = 0U; j < imx8ulp_msc_slots[imx8ulp_msc[i].pac_msc_id]; j++) { + xrdc_config_msc(imx8ulp_msc[i].pac_msc_id, j, val); + } + } else { + if (imx8ulp_msc[i].slot_id >= imx8ulp_msc_slots[imx8ulp_msc[i].pac_msc_id]) { + return -EINVAL; + } + + xrdc_config_msc(imx8ulp_msc[i].pac_msc_id, imx8ulp_msc[i].slot_id, val); + } + } + } + + return 0; +} + +int xrdc_apply_lpav_config(void) +{ + /* Configure PAC2 to allow to access PCC5 */ + xrdc_config_pac(2, 39, 0xe00000); + + /* Enable the eDMA2 MP clock for MDA16 access */ + mmio_write_32(IMX_PCC5_BASE + 0x0, 0xc0000000); + return xrdc_apply_config(xrdc_check_lpav); +} + +int xrdc_apply_hifi_config(void) +{ + return xrdc_apply_config(xrdc_check_hifi); +} + +int xrdc_apply_apd_config(void) +{ + return xrdc_apply_config(xrdc_check_ad); +} + +void xrdc_enable(void) +{ + mmio_write_32(XRDC_ADDR, BIT(14) | BIT(15) | BIT(0)); +} diff --git a/plat/imx/imx93/imx93_bl31_setup.c b/plat/imx/imx93/imx93_bl31_setup.c index 8458f6c1..d997e9a8 100644 --- a/plat/imx/imx93/imx93_bl31_setup.c +++ b/plat/imx/imx93/imx93_bl31_setup.c @@ -20,6 +20,7 @@ #include <plat/common/platform.h> #include <imx8_lpuart.h> +#include <plat_common.h> #include <plat_imx8.h> #include <platform_def.h> @@ -90,6 +91,9 @@ void bl31_early_platform_setup2(u_register_t arg0, u_register_t arg1, bl33_image_ep_info.args.arg3 = BL32_FDT_OVERLAY_ADDR; bl32_image_ep_info.args.arg3 = BL32_FDT_OVERLAY_ADDR; #endif + + imx_bl31_params_parse(arg0, OCRAM_BASE, OCRAM_SIZE, + &bl32_image_ep_info, &bl33_image_ep_info); } void bl31_plat_arch_setup(void) @@ -136,11 +140,6 @@ void bl31_platform_setup(void) plat_gic_init(); } -void bl31_plat_runtime_setup(void) -{ - console_switch_state(CONSOLE_FLAG_RUNTIME); -} - entry_point_info_t *bl31_plat_get_next_image_ep_info(unsigned int type) { if (type == NON_SECURE) { diff --git a/plat/imx/imx93/include/platform_def.h b/plat/imx/imx93/include/platform_def.h index 7efbf1c6..f0d53cde 100644 --- a/plat/imx/imx93/include/platform_def.h +++ b/plat/imx/imx93/include/platform_def.h @@ -31,6 +31,9 @@ #define BL31_BASE U(0x204E0000) #define BL31_LIMIT U(0x20520000) +#define OCRAM_BASE U(0x20480000) +#define OCRAM_SIZE U(0xA0000) + /* non-secure uboot base */ /* TODO */ #define PLAT_NS_IMAGE_OFFSET U(0x80200000) diff --git a/plat/imx/imx93/platform.mk b/plat/imx/imx93/platform.mk index ed7e81f9..f506d8b1 100644 --- a/plat/imx/imx93/platform.mk +++ b/plat/imx/imx93/platform.mk @@ -19,9 +19,11 @@ IMX_GIC_SOURCES := ${GICV3_SOURCES} \ plat/common/plat_psci_common.c \ plat/imx/common/plat_imx8_gic.c -BL31_SOURCES += plat/common/aarch64/crash_console_helpers.S \ +BL31_SOURCES += common/desc_image_load.c \ + plat/common/aarch64/crash_console_helpers.S \ plat/imx/imx93/aarch64/plat_helpers.S \ plat/imx/imx93/plat_topology.c \ + plat/imx/common/imx_common.c \ plat/imx/common/lpuart_console.S \ plat/imx/imx93/trdc.c \ plat/imx/imx93/pwr_ctrl.c \ diff --git a/plat/intel/soc/agilex/bl2_plat_setup.c b/plat/intel/soc/agilex/bl2_plat_setup.c index 211a7b73..084539e8 100644 --- a/plat/intel/soc/agilex/bl2_plat_setup.c +++ b/plat/intel/soc/agilex/bl2_plat_setup.c @@ -1,6 +1,7 @@ /* * Copyright (c) 2019-2022, ARM Limited and Contributors. All rights reserved. - * Copyright (c) 2019-2022, Intel Corporation. All rights reserved. + * Copyright (c) 2019-2023, Intel Corporation. All rights reserved. + * Copyright (c) 2024, Altera Corporation. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -28,7 +29,9 @@ #include "socfpga_mailbox.h" #include "socfpga_private.h" #include "socfpga_reset_manager.h" +#include "socfpga_ros.h" #include "socfpga_system_manager.h" +#include "socfpga_vab.h" #include "wdt/watchdog.h" static struct mmc_device_info mmc_info; @@ -92,6 +95,7 @@ void bl2_el3_early_platform_setup(u_register_t x0, u_register_t x1, void bl2_el3_plat_arch_setup(void) { + unsigned long offset = 0; const mmap_region_t bl_regions[] = { MAP_REGION_FLAT(BL2_BASE, BL2_END - BL2_BASE, MT_MEMORY | MT_RW | MT_SECURE), @@ -110,7 +114,10 @@ void bl2_el3_plat_arch_setup(void) setup_page_tables(bl_regions, agilex_plat_mmap); - enable_mmu_el3(0); + /* + * TODO: mmu enable in latest phase + */ + // enable_mmu_el3(0); dw_mmc_params_t params = EMMC_INIT_PARAMS(0x100000, get_mmc_clk()); @@ -122,15 +129,20 @@ void bl2_el3_plat_arch_setup(void) switch (boot_source) { case BOOT_SOURCE_SDMMC: + NOTICE("SDMMC boot\n"); dw_mmc_init(¶ms, &mmc_info); - socfpga_io_setup(boot_source); + socfpga_io_setup(boot_source, PLAT_SDMMC_DATA_BASE); break; case BOOT_SOURCE_QSPI: + NOTICE("QSPI boot\n"); cad_qspi_init(0, QSPI_CONFIG_CPHA, QSPI_CONFIG_CPOL, QSPI_CONFIG_CSDA, QSPI_CONFIG_CSDADS, QSPI_CONFIG_CSEOT, QSPI_CONFIG_CSSOT, 0); - socfpga_io_setup(boot_source); + if (ros_qspi_get_ssbl_offset(&offset) != ROS_RET_OK) { + offset = PLAT_QSPI_DATA_BASE; + } + socfpga_io_setup(boot_source, offset); break; default: @@ -168,6 +180,20 @@ int bl2_plat_handle_post_image_load(unsigned int image_id) assert(bl_mem_params); +#if SOCFPGA_SECURE_VAB_AUTH + /* + * VAB Authentication start here. + * If failed to authenticate, shall not proceed to process BL31 and hang. + */ + int ret = 0; + + ret = socfpga_vab_init(image_id); + if (ret < 0) { + ERROR("SOCFPGA VAB Authentication failed\n"); + wfi(); + } +#endif + switch (image_id) { case BL33_IMAGE_ID: bl_mem_params->ep_info.args.arg0 = 0xffff & read_mpidr(); @@ -186,4 +212,3 @@ int bl2_plat_handle_post_image_load(unsigned int image_id) void bl2_platform_setup(void) { } - diff --git a/plat/intel/soc/agilex/bl31_plat_setup.c b/plat/intel/soc/agilex/bl31_plat_setup.c index b4e19def..4c10e7bd 100644 --- a/plat/intel/soc/agilex/bl31_plat_setup.c +++ b/plat/intel/soc/agilex/bl31_plat_setup.c @@ -1,6 +1,7 @@ /* * Copyright (c) 2019-2020, ARM Limited and Contributors. All rights reserved. * Copyright (c) 2019-2022, Intel Corporation. All rights reserved. + * Copyright (c) 2024, Altera Corporation. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -13,12 +14,16 @@ #include <drivers/ti/uart/uart_16550.h> #include <lib/mmio.h> #include <lib/xlat_tables/xlat_tables.h> +#include <plat/common/platform.h> #include "ccu/ncore_ccu.h" #include "socfpga_mailbox.h" #include "socfpga_private.h" #include "socfpga_sip_svc.h" +/* Get non-secure SPSR for BL33. Zephyr and Linux */ +uint32_t arm_get_spsr_for_bl33_entry(void); + static entry_point_info_t bl32_image_ep_info; static entry_point_info_t bl33_image_ep_info; @@ -59,9 +64,7 @@ void bl31_early_platform_setup2(u_register_t arg0, u_register_t arg1, u_register_t arg2, u_register_t arg3) { static console_t console; - mmio_write_64(PLAT_SEC_ENTRY, PLAT_SEC_WARM_ENTRY); - console_16550_register(PLAT_INTEL_UART_BASE, PLAT_UART_CLOCK, PLAT_BAUDRATE, &console); /* @@ -69,6 +72,33 @@ void bl31_early_platform_setup2(u_register_t arg0, u_register_t arg1, */ void *from_bl2 = (void *) arg0; +#if RESET_TO_BL31 + /* There are no parameters from BL2 if BL31 is a reset vector */ + assert(from_bl2 == NULL); + void *plat_params_from_bl2 = (void *) arg3; + + assert(plat_params_from_bl2 == NULL); + + /* Populate entry point information for BL33 */ + SET_PARAM_HEAD(&bl33_image_ep_info, + PARAM_EP, + VERSION_1, + 0); + +# if ARM_LINUX_KERNEL_AS_BL33 + /* + * According to the file ``Documentation/arm64/booting.txt`` of the + * Linux kernel tree, Linux expects the physical address of the device + * tree blob (DTB) in x0, while x1-x3 are reserved for future use and + * must be 0. + */ + bl33_image_ep_info.args.arg0 = (u_register_t)ARM_PRELOADED_DTB_BASE; + bl33_image_ep_info.args.arg1 = 0U; + bl33_image_ep_info.args.arg2 = 0U; + bl33_image_ep_info.args.arg3 = 0U; +# endif + +#else /* RESET_TO_BL31 */ bl_params_t *params_from_bl2 = (bl_params_t *)from_bl2; assert(params_from_bl2 != NULL); @@ -76,28 +106,38 @@ void bl31_early_platform_setup2(u_register_t arg0, u_register_t arg1, * Copy BL32 (if populated by BL31) and BL33 entry point information. * They are stored in Secure RAM, in BL31's address space. */ - if (params_from_bl2->h.type == PARAM_BL_PARAMS && params_from_bl2->h.version >= VERSION_2) { - bl_params_node_t *bl_params = params_from_bl2->head; - while (bl_params) { if (bl_params->image_id == BL33_IMAGE_ID) bl33_image_ep_info = *bl_params->ep_info; - bl_params = bl_params->next_params_info; } } else { struct socfpga_bl31_params *arg_from_bl2 = (struct socfpga_bl31_params *) from_bl2; - assert(arg_from_bl2->h.type == PARAM_BL31); assert(arg_from_bl2->h.version >= VERSION_1); - bl32_image_ep_info = *arg_from_bl2->bl32_ep_info; bl33_image_ep_info = *arg_from_bl2->bl33_ep_info; } + + bl33_image_ep_info.args.arg0 = (u_register_t)ARM_PRELOADED_DTB_BASE; + bl33_image_ep_info.args.arg1 = 0U; + bl33_image_ep_info.args.arg2 = 0U; + bl33_image_ep_info.args.arg3 = 0U; +#endif + + /* + * Tell BL31 where the non-trusted software image + * is located and the entry state information + */ +# if ARM_LINUX_KERNEL_AS_BL33 + bl33_image_ep_info.pc = plat_get_ns_image_entrypoint(); + bl33_image_ep_info.spsr = arm_get_spsr_for_bl33_entry(); +#endif + SET_SECURITY_STATE(bl33_image_ep_info.h.attr, NON_SECURE); } @@ -136,8 +176,6 @@ void bl31_platform_setup(void) (uint64_t)plat_secondary_cpus_bl31_entry); mailbox_hps_stage_notify(HPS_EXECUTION_STATE_SSBL); - - ncore_enable_ocram_firewall(); } const mmap_region_t plat_agilex_mmap[] = { @@ -174,8 +212,34 @@ void bl31_plat_arch_setup(void) #endif {0} }; - setup_page_tables(bl_regions, plat_agilex_mmap); enable_mmu_el3(0); } +/* Get non-secure image entrypoint for BL33. Zephyr and Linux */ +uintptr_t plat_get_ns_image_entrypoint(void) +{ +#ifdef PRELOADED_BL33_BASE + return PRELOADED_BL33_BASE; +#else + return PLAT_NS_IMAGE_OFFSET; +#endif +} + +/* Get non-secure SPSR for BL33. Zephyr and Linux */ +uint32_t arm_get_spsr_for_bl33_entry(void) +{ + unsigned int mode; + uint32_t spsr; + + /* Figure out what mode we enter the non-secure world in */ + mode = (el_implemented(2) != EL_IMPL_NONE) ? MODE_EL2 : MODE_EL1; + + /* + * TODO: Consider the possibility of specifying the SPSR in + * the FIP ToC and allowing the platform to have a say as + * well. + */ + spsr = SPSR_64((uint64_t)mode, MODE_SP_ELX, DISABLE_ALL_EXCEPTIONS); + return spsr; +} diff --git a/plat/intel/soc/agilex/include/agilex_clock_manager.h b/plat/intel/soc/agilex/include/agilex_clock_manager.h index ee222418..2ca69478 100644 --- a/plat/intel/soc/agilex/include/agilex_clock_manager.h +++ b/plat/intel/soc/agilex/include/agilex_clock_manager.h @@ -129,5 +129,6 @@ uint32_t get_uart_clk(void); uint32_t get_mmc_clk(void); uint32_t get_mpu_clk(void); uint32_t get_cpu_clk(void); +uint32_t get_mpu_periph_clk(void); #endif diff --git a/plat/intel/soc/agilex/include/agilex_memory_controller.h b/plat/intel/soc/agilex/include/agilex_memory_controller.h index 9db4292e..f0bbeea0 100644 --- a/plat/intel/soc/agilex/include/agilex_memory_controller.h +++ b/plat/intel/soc/agilex/include/agilex_memory_controller.h @@ -1,5 +1,6 @@ /* * Copyright (c) 2019, Intel Corporation. All rights reserved. + * Copyright (c) 2024, Altera Corporation. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -27,7 +28,7 @@ #define AGX_MPFE_HMC_ADP_ECCCTRL1 0xf8011100 #define AGX_MPFE_HMC_ADP_ECCCTRL2 0xf8011104 #define AGX_MPFE_HMC_ADP_RSTHANDSHAKESTAT 0xf8011218 -#define AGX_MPFE_HMC_ADP_RSTHANDSHAKESTAT_SEQ2CORE 0x000000ff +#define AGX_MPFE_HMC_ADP_RSTHANDSHAKESTAT_SEQ2CORE 0x0000000f #define AGX_MPFE_HMC_ADP_RSTHANDSHAKECTRL 0xf8011214 diff --git a/plat/intel/soc/agilex/include/agilex_system_manager.h b/plat/intel/soc/agilex/include/agilex_system_manager.h index cb9222d5..78aabde6 100644 --- a/plat/intel/soc/agilex/include/agilex_system_manager.h +++ b/plat/intel/soc/agilex/include/agilex_system_manager.h @@ -1,5 +1,6 @@ /* * Copyright (c) 2019-2023, Intel Corporation. All rights reserved. + * Copyright (c) 2024, Altera Corporation. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -121,7 +122,7 @@ #define SOCFPGA_SYSMGR_BOOT_SCRATCH_COLD_8 0x220 #define SOCFPGA_SYSMGR_BOOT_SCRATCH_COLD_9 0x224 #define SOCFPGA_SYSMGR_MPFE_CONFIG 0x228 -#define SOCFPGA_SYSMGR_MPFE_status 0x22C +#define SOCFPGA_SYSMGR_MPFE_STATUS 0x22C #define SOCFPGA_SYSMGR_BOOT_SCRATCH_WARM_0 0x230 #define SOCFPGA_SYSMGR_BOOT_SCRATCH_WARM_1 0x234 #define SOCFPGA_SYSMGR_BOOT_SCRATCH_WARM_2 0x238 @@ -143,6 +144,18 @@ #define SOCFPGA_SYSMGR_BOOT_SCRATCH_POR_8 0x278 #define SOCFPGA_SYSMGR_BOOT_SCRATCH_POR_9 0x27C +/* QSPI ECC from SDM register */ +#define SOCFPGA_ECC_QSPI_CTRL 0x08 +#define SOCFPGA_ECC_QSPI_ERRINTEN 0x10 +#define SOCFPGA_ECC_QSPI_ERRINTENS 0x14 +#define SOCFPGA_ECC_QSPI_ERRINTENR 0x18 +#define SOCFPGA_ECC_QSPI_INTMODE 0x1C +#define SOCFPGA_ECC_QSPI_INTSTAT 0x20 +#define SOCFPGA_ECC_QSPI_INTTEST 0x24 +#define SOCFPGA_ECC_QSPI_ECC_ACCCTRL 0x78 +#define SOCFPGA_ECC_QSPI_ECC_STARTACC 0x7C +#define SOCFPGA_ECC_QSPI_ECC_WDCTRL 0x80 + #define DMA0_STREAM_CTRL_REG 0x10D1217C #define DMA1_STREAM_CTRL_REG 0x10D12180 #define SDM_STREAM_CTRL_REG 0x10D12184 @@ -183,6 +196,9 @@ #define RMMUSECSID_REG_VAL BIT(5) /* Macros */ +#define SOCFPGA_ECC_QSPI(_reg) (SOCFPGA_ECC_QSPI_REG_BASE \ + + (SOCFPGA_ECC_QSPI_##_reg)) + #define SOCFPGA_SYSMGR(_reg) (SOCFPGA_SYSMGR_REG_BASE \ + (SOCFPGA_SYSMGR_##_reg)) diff --git a/plat/intel/soc/agilex/include/socfpga_plat_def.h b/plat/intel/soc/agilex/include/socfpga_plat_def.h index a744d093..840ffdd6 100644 --- a/plat/intel/soc/agilex/include/socfpga_plat_def.h +++ b/plat/intel/soc/agilex/include/socfpga_plat_def.h @@ -1,6 +1,7 @@ /* * Copyright (c) 2019-2022, ARM Limited and Contributors. All rights reserved. * Copyright (c) 2019-2023, Intel Corporation. All rights reserved. + * Copyright (c) 2024, Altera Corporation. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -9,14 +10,21 @@ #define PLAT_SOCFPGA_DEF_H #include "agilex_system_manager.h" +#include <lib/utils_def.h> #include <platform_def.h> /* Platform Setting */ -#define PLATFORM_MODEL PLAT_SOCFPGA_AGILEX -#define BOOT_SOURCE BOOT_SOURCE_SDMMC -#define PLAT_PRIMARY_CPU 0 +#define PLATFORM_MODEL PLAT_SOCFPGA_AGILEX +/* 1 = Flush cache, 0 = No cache flush. + * Default for Agilex is No cache flush. + * For Agilex FP8, set to Flush cache. + */ +#define CACHE_FLUSH 0 +#define PLAT_PRIMARY_CPU 0 #define PLAT_CLUSTER_ID_MPIDR_AFF_SHIFT MPIDR_AFF1_SHIFT -#define PLAT_CPU_ID_MPIDR_AFF_SHIFT MPIDR_AFF0_SHIFT +#define PLAT_CPU_ID_MPIDR_AFF_SHIFT MPIDR_AFF0_SHIFT +#define PLAT_HANDOFF_OFFSET 0xFFE3F000 +#define PLAT_TIMER_BASE_ADDR 0xFFD01000 /* FPGA config helpers */ #define INTEL_SIP_SMC_FPGA_CONFIG_ADDR 0x400000 @@ -26,6 +34,25 @@ #define CAD_QSPIDATA_OFST 0xff900000 #define CAD_QSPI_OFFSET 0xff8d2000 +/* FIP Setting */ +#define PLAT_FIP_BASE (0) +#if ARM_LINUX_KERNEL_AS_BL33 +#define PLAT_FIP_MAX_SIZE (0x8000000) +#else +#define PLAT_FIP_MAX_SIZE (0x1000000) +#endif + +/* SDMMC Setting */ +#if ARM_LINUX_KERNEL_AS_BL33 +#define PLAT_MMC_DATA_BASE (0x10000000) +#define PLAT_MMC_DATA_SIZE (0x100000) +#define SOCFPGA_MMC_BLOCK_SIZE U(32768) +#else +#define PLAT_MMC_DATA_BASE (0xffe3c000) +#define PLAT_MMC_DATA_SIZE (0x2000 +#define SOCFPGA_MMC_BLOCK_SIZE U(8192) +#endif + /* Register Mapping */ #define SOCFPGA_CCU_NOC_REG_BASE 0xf7000000 #define SOCFPGA_F2SDRAMMGR_REG_BASE U(0xf8024000) @@ -34,6 +61,7 @@ #define SOCFPGA_MEMCTRL_REG_BASE 0xf8011100 #define SOCFPGA_RSTMGR_REG_BASE 0xffd11000 #define SOCFPGA_SYSMGR_REG_BASE 0xffd12000 +#define SOCFPGA_ECC_QSPI_REG_BASE 0xffa22000 #define SOCFPGA_L4_PER_SCR_REG_BASE 0xffd21000 #define SOCFPGA_L4_SYS_SCR_REG_BASE 0xffd21100 @@ -64,34 +92,39 @@ #define DEVICE4_BASE (0x2000000000) #define DEVICE4_SIZE (0x0100000000) -#define BL2_BASE (0xffe00000) -#define BL2_LIMIT (0xffe1b000) +#define BL2_BASE (0xffe00000) +#define BL2_LIMIT (0xffe2b000) -#define BL31_BASE (0x1000) -#define BL31_LIMIT (0x81000) +#define BL31_BASE (0x1000) +#define BL31_LIMIT (0x81000) /******************************************************************************* * UART related constants ******************************************************************************/ -#define PLAT_UART0_BASE (0xFFC02000) -#define PLAT_UART1_BASE (0xFFC02100) +#define PLAT_UART0_BASE (0xFFC02000) +#define PLAT_UART1_BASE (0xFFC02100) + +/******************************************************************************* + * WDT related constants + ******************************************************************************/ +#define WDT_BASE (0xFFD00200) /******************************************************************************* * GIC related constants ******************************************************************************/ -#define PLAT_GIC_BASE (0xFFFC0000) -#define PLAT_GICC_BASE (PLAT_GIC_BASE + 0x2000) -#define PLAT_GICD_BASE (PLAT_GIC_BASE + 0x1000) -#define PLAT_GICR_BASE 0 +#define PLAT_GIC_BASE (0xFFFC0000) +#define PLAT_GICC_BASE (PLAT_GIC_BASE + 0x2000) +#define PLAT_GICD_BASE (PLAT_GIC_BASE + 0x1000) +#define PLAT_GICR_BASE 0 -#define PLAT_SYS_COUNTER_FREQ_IN_TICKS (400000000) -#define PLAT_HZ_CONVERT_TO_MHZ (1000000) +#define PLAT_SYS_COUNTER_FREQ_IN_TICKS (400000000) +#define PLAT_HZ_CONVERT_TO_MHZ (1000000) /******************************************************************************* * SDMMC related pointer function ******************************************************************************/ -#define SDMMC_READ_BLOCKS mmc_read_blocks -#define SDMMC_WRITE_BLOCKS mmc_write_blocks +#define SDMMC_READ_BLOCKS sdmmc_read_blocks +#define SDMMC_WRITE_BLOCKS mmc_write_blocks /******************************************************************************* * sysmgr.boot_scratch_cold6 & 7 (64bit) are used to indicate L2 reset @@ -100,6 +133,6 @@ #define L2_RESET_DONE_REG 0xFFD12218 /* Platform specific system counter */ -#define PLAT_SYS_COUNTER_FREQ_IN_MHZ get_cpu_clk() +#define PLAT_SYS_COUNTER_FREQ_IN_MHZ U(400) #endif /* PLAT_SOCFPGA_DEF_H */ diff --git a/plat/intel/soc/agilex/platform.mk b/plat/intel/soc/agilex/platform.mk index 5c92f725..d534b2ef 100644 --- a/plat/intel/soc/agilex/platform.mk +++ b/plat/intel/soc/agilex/platform.mk @@ -1,6 +1,7 @@ # # Copyright (c) 2019-2023, ARM Limited and Contributors. All rights reserved. -# Copyright (c) 2019-2022, Intel Corporation. All rights reserved. +# Copyright (c) 2019-2023, Intel Corporation. All rights reserved. +# Copyright (c) 2024, Altera Corporation. All rights reserved. # # SPDX-License-Identifier: BSD-3-Clause # @@ -27,6 +28,8 @@ PLAT_BL_COMMON_SOURCES := \ plat/intel/soc/common/aarch64/platform_common.c \ plat/intel/soc/common/aarch64/plat_helpers.S \ plat/intel/soc/common/drivers/ccu/ncore_ccu.c \ + plat/intel/soc/common/drivers/sdmmc/sdmmc.c \ + plat/intel/soc/common/lib/sha/sha.c \ plat/intel/soc/common/socfpga_delay_timer.c BL2_SOURCES += \ @@ -47,12 +50,15 @@ BL2_SOURCES += \ plat/intel/soc/agilex/soc/agilex_pinmux.c \ plat/intel/soc/common/bl2_plat_mem_params_desc.c \ plat/intel/soc/common/socfpga_image_load.c \ + plat/intel/soc/common/socfpga_ros.c \ plat/intel/soc/common/socfpga_storage.c \ + plat/intel/soc/common/socfpga_vab.c \ plat/intel/soc/common/soc/socfpga_emac.c \ plat/intel/soc/common/soc/socfpga_firewall.c \ plat/intel/soc/common/soc/socfpga_handoff.c \ plat/intel/soc/common/soc/socfpga_mailbox.c \ plat/intel/soc/common/soc/socfpga_reset_manager.c \ + plat/intel/soc/common/drivers/ddr/ddr.c \ plat/intel/soc/common/drivers/qspi/cadence_qspi.c \ plat/intel/soc/common/drivers/wdt/watchdog.c @@ -76,7 +82,38 @@ BL31_SOURCES += \ plat/intel/soc/common/soc/socfpga_mailbox.c \ plat/intel/soc/common/soc/socfpga_reset_manager.c +# Don't have the Linux kernel as a BL33 image by default +ARM_LINUX_KERNEL_AS_BL33 := 0 +$(eval $(call assert_boolean,ARM_LINUX_KERNEL_AS_BL33)) +$(eval $(call add_define,ARM_LINUX_KERNEL_AS_BL33)) +$(eval $(call add_define,ARM_PRELOADED_DTB_BASE)) + +# Configs for Boot Source +SOCFPGA_BOOT_SOURCE_SDMMC ?= 0 +SOCFPGA_BOOT_SOURCE_QSPI ?= 0 +SOCFPGA_BOOT_SOURCE_NAND ?= 0 + +$(eval $(call assert_booleans,\ + $(sort \ + SOCFPGA_BOOT_SOURCE_SDMMC \ + SOCFPGA_BOOT_SOURCE_QSPI \ + SOCFPGA_BOOT_SOURCE_NAND \ +))) +$(eval $(call add_defines,\ + $(sort \ + SOCFPGA_BOOT_SOURCE_SDMMC \ + SOCFPGA_BOOT_SOURCE_QSPI \ + SOCFPGA_BOOT_SOURCE_NAND \ +))) + +# Configs for VAB Authentication +SOCFPGA_SECURE_VAB_AUTH := 0 +$(eval $(call assert_boolean,SOCFPGA_SECURE_VAB_AUTH)) +$(eval $(call add_define,SOCFPGA_SECURE_VAB_AUTH)) + PROGRAMMABLE_RESET_ADDRESS := 0 RESET_TO_BL2 := 1 BL2_INV_DCACHE := 0 USE_COHERENT_MEM := 1 + +HANDLE_EA_EL3_FIRST_NS := 1 \ No newline at end of file diff --git a/plat/intel/soc/agilex/soc/agilex_clock_manager.c b/plat/intel/soc/agilex/soc/agilex_clock_manager.c index d32c3f14..391eac63 100644 --- a/plat/intel/soc/agilex/soc/agilex_clock_manager.c +++ b/plat/intel/soc/agilex/soc/agilex_clock_manager.c @@ -398,12 +398,36 @@ uint32_t get_mpu_clk(void) return mpu_clk; } +uint32_t get_l4_clk(void) +{ + uint32_t l4_clk; + + l4_clk = get_clk_freq(CLKMGR_MAINPLL_NOCCLK, CLKMGR_MAINPLL_PLLC1, + CLKMGR_PERPLL_PLLC1); + return l4_clk; +} + /* Get cpu freq clock */ uint32_t get_cpu_clk(void) { uint32_t cpu_clk; - cpu_clk = get_mpu_clk()/PLAT_HZ_CONVERT_TO_MHZ; + cpu_clk = get_l4_clk()/PLAT_HZ_CONVERT_TO_MHZ; return cpu_clk; } + +/* Return mpu_periph_clk clock frequency */ +uint32_t get_mpu_periph_clk(void) +{ + uint32_t mpu_periph_clk = 0; + /* mpu_periph_clk is mpu_clk, via a static /4 divider */ + mpu_periph_clk = (get_mpu_clk()/4)/PLAT_HZ_CONVERT_TO_MHZ; + return mpu_periph_clk; +} + +/* Return mpu_periph_clk tick */ +unsigned int plat_get_syscnt_freq2(void) +{ + return PLAT_SYS_COUNTER_FREQ_IN_TICKS; +} diff --git a/plat/intel/soc/agilex5/bl2_plat_setup.c b/plat/intel/soc/agilex5/bl2_plat_setup.c index a2fafd2f..fe5dc6e7 100644 --- a/plat/intel/soc/agilex5/bl2_plat_setup.c +++ b/plat/intel/soc/agilex5/bl2_plat_setup.c @@ -1,6 +1,7 @@ /* * Copyright (c) 2019-2021, ARM Limited and Contributors. All rights reserved. * Copyright (c) 2019-2023, Intel Corporation. All rights reserved. + * Copyright (c) 2024, Altera Corporation. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -19,9 +20,11 @@ #include <lib/xlat_tables/xlat_tables_v2.h> #include "agilex5_clock_manager.h" +#include "agilex5_ddr.h" #include "agilex5_memory_controller.h" #include "agilex5_mmc.h" #include "agilex5_pinmux.h" +#include "agilex5_power_manager.h" #include "agilex5_system_manager.h" #include "ccu/ncore_ccu.h" #include "combophy/combophy.h" @@ -34,6 +37,8 @@ #include "socfpga_mailbox.h" #include "socfpga_private.h" #include "socfpga_reset_manager.h" +#include "socfpga_ros.h" +#include "socfpga_vab.h" #include "wdt/watchdog.h" @@ -63,35 +68,94 @@ const mmap_region_t agilex_plat_mmap[] = { boot_source_type boot_source = BOOT_SOURCE; -void bl2_el3_early_platform_setup(u_register_t x0, u_register_t x1, - u_register_t x2, u_register_t x4) +void bl2_el3_early_platform_setup(u_register_t x0 __unused, + u_register_t x1 __unused, + u_register_t x2 __unused, + u_register_t x3 __unused) { static console_t console; + handoff reverse_handoff_ptr; + + /* Enable nonsecure access for peripherals and other misc components */ + enable_nonsecure_access(); + + /* Bring all the required peripherals out of reset */ + deassert_peripheral_reset(); - handoff reverse_handoff_ptr = { 0 }; + /* + * Initialize the UART console early in BL2 EL3 boot flow to get + * the error/notice messages wherever required. + */ + console_16550_register(PLAT_INTEL_UART_BASE, PLAT_UART_CLOCK, + PLAT_BAUDRATE, &console); + /* Generic delay timer init */ generic_delay_timer_init(); - config_clkmgr_handoff(&reverse_handoff_ptr); + + socfpga_delay_timer_init(); + + /* Get the handoff data */ + if ((socfpga_get_handoff(&reverse_handoff_ptr)) != 0) { + ERROR("SOCFPGA: Failed to get the correct handoff data\n"); + panic(); + } + + /* Configure the pinmux */ + config_pinmux(&reverse_handoff_ptr); + + /* Configure OCRAM to NON SECURE ACCESS */ + mmio_write_32(OCRAM_REGION_0_REG_BASE, OCRAM_NON_SECURE_ENABLE); + mmio_write_32(SOCFPGA_L4_PER_SCR_REG_BASE + SOCFPGA_SDMMC_SECU_BIT, + SOCFPGA_SDMMC_SECU_BIT_ENABLE); + mmio_write_32(SOCFPGA_L4_SYS_SCR_REG_BASE + SOCFPGA_SDMMC_SECU_BIT, + SOCFPGA_SDMMC_SECU_BIT_ENABLE); + mmio_write_32(SOCFPGA_LWSOC2FPGA_SCR_REG_BASE, + SOCFPGA_LWSOC2FPGA_ENABLE); + + /* Configure the clock manager */ + if ((config_clkmgr_handoff(&reverse_handoff_ptr)) != 0) { + ERROR("SOCFPGA: Failed to initialize the clock manager\n"); + panic(); + } + + /* Configure power manager PSS SRAM power gate */ + config_pwrmgr_handoff(&reverse_handoff_ptr); + + /* Initialize the mailbox to enable communication between HPS and SDM */ mailbox_init(); - enable_nonsecure_access(); - deassert_peripheral_reset(); + /* Perform a handshake with certain peripherals before issuing a reset */ + config_hps_hs_before_warm_reset(); + + /* TODO: watchdog init */ + //watchdog_init(clkmgr_get_rate(CLKMGR_WDT_CLK_ID)); + + /* Initialize the CCU module for hardware cache coherency */ + init_ncore_ccu(); + + socfpga_emac_init(); + + /* DDR and IOSSM driver init */ + agilex5_ddr_init(&reverse_handoff_ptr); + if (combo_phy_init(&reverse_handoff_ptr) != 0) { - ERROR("Combo Phy initialization failed\n"); + ERROR("SOCFPGA: Combo Phy initialization failed\n"); } - console_16550_register(PLAT_INTEL_UART_BASE, PLAT_UART_CLOCK, - PLAT_BAUDRATE, &console); - - /* Store magic number */ - mmio_write_32(L2_RESET_DONE_REG, PLAT_L2_RESET_REQ); + /* Enable FPGA bridges as required */ + if (!intel_mailbox_is_fpga_not_ready()) { + socfpga_bridges_enable(SOC2FPGA_MASK | LWHPS2FPGA_MASK | + FPGA2SOC_MASK | F2SDRAM0_MASK); + } } void bl2_el3_plat_arch_setup(void) { handoff reverse_handoff_ptr; + unsigned long offset = 0; - struct cdns_sdmmc_params params = EMMC_INIT_PARAMS((uintptr_t) &cdns_desc, get_mmc_clk()); + struct cdns_sdmmc_params params = EMMC_INIT_PARAMS((uintptr_t) &cdns_desc, + clkmgr_get_rate(CLKMGR_SDMMC_CLK_ID)); mmc_info.mmc_dev_type = MMC_DEVICE_TYPE; mmc_info.ocr_voltage = OCR_3_3_3_4 | OCR_3_2_3_3; @@ -102,8 +166,8 @@ void bl2_el3_plat_arch_setup(void) switch (boot_source) { case BOOT_SOURCE_SDMMC: NOTICE("SDMMC boot\n"); - sdmmc_init(&reverse_handoff_ptr, ¶ms, &mmc_info); - socfpga_io_setup(boot_source); + cdns_mmc_init(¶ms, &mmc_info); + socfpga_io_setup(boot_source, PLAT_SDMMC_DATA_BASE); break; case BOOT_SOURCE_QSPI: @@ -111,13 +175,16 @@ void bl2_el3_plat_arch_setup(void) cad_qspi_init(0, QSPI_CONFIG_CPHA, QSPI_CONFIG_CPOL, QSPI_CONFIG_CSDA, QSPI_CONFIG_CSDADS, QSPI_CONFIG_CSEOT, QSPI_CONFIG_CSSOT, 0); - socfpga_io_setup(boot_source); + if (ros_qspi_get_ssbl_offset(&offset) != ROS_RET_OK) { + offset = PLAT_QSPI_DATA_BASE; + } + socfpga_io_setup(boot_source, offset); break; case BOOT_SOURCE_NAND: NOTICE("NAND boot\n"); nand_init(&reverse_handoff_ptr); - socfpga_io_setup(boot_source); + socfpga_io_setup(boot_source, PLAT_NAND_DATA_BASE); break; default: @@ -154,6 +221,20 @@ int bl2_plat_handle_post_image_load(unsigned int image_id) assert(bl_mem_params); +#if SOCFPGA_SECURE_VAB_AUTH + /* + * VAB Authentication start here. + * If failed to authenticate, shall not proceed to process BL31 and hang. + */ + int ret = 0; + + ret = socfpga_vab_init(image_id); + if (ret < 0) { + ERROR("SOCFPGA VAB Authentication failed\n"); + wfi(); + } +#endif + switch (image_id) { case BL33_IMAGE_ID: bl_mem_params->ep_info.args.arg0 = 0xffff & read_mpidr(); diff --git a/plat/intel/soc/agilex5/bl31_plat_setup.c b/plat/intel/soc/agilex5/bl31_plat_setup.c index 5ae4bf77..c090117a 100644 --- a/plat/intel/soc/agilex5/bl31_plat_setup.c +++ b/plat/intel/soc/agilex5/bl31_plat_setup.c @@ -1,6 +1,7 @@ /* - * Copyright (c) 2019-2020, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2019-2024, ARM Limited and Contributors. All rights reserved. * Copyright (c) 2019-2023, Intel Corporation. All rights reserved. + * Copyright (c) 2024, Altera Corporation. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -17,6 +18,7 @@ #include <lib/xlat_tables/xlat_tables_v2.h> #include <plat/common/platform.h> +#include "agilex5_cache.h" #include "agilex5_power_manager.h" #include "ccu/ncore_ccu.h" #include "socfpga_mailbox.h" @@ -56,9 +58,8 @@ void bl31_early_platform_setup2(u_register_t arg0, u_register_t arg1, mmio_write_64(PLAT_SEC_ENTRY, PLAT_SEC_WARM_ENTRY); console_16550_register(PLAT_INTEL_UART_BASE, PLAT_UART_CLOCK, - PLAT_BAUDRATE, &console); + PLAT_BAUDRATE, &console); - init_ncore_ccu(); setup_smmu_stream_id(); /* @@ -167,10 +168,6 @@ void bl31_platform_setup(void) gicv3_rdistif_init(plat_my_core_pos()); gicv3_cpuif_enable(plat_my_core_pos()); mailbox_hps_stage_notify(HPS_EXECUTION_STATE_SSBL); -#if !defined(SIMICS_RUN) - ncore_enable_ocram_firewall(); -#endif - } const mmap_region_t plat_agilex_mmap[] = { @@ -186,18 +183,19 @@ const mmap_region_t plat_agilex_mmap[] = { /******************************************************************************* * Perform the very early platform specific architectural setup here. At the - * moment this is only intializes the mmu in a quick and dirty way. + * moment this is only initializes the mmu in a quick and dirty way. ******************************************************************************/ void bl31_plat_arch_setup(void) { uint32_t boot_core = 0x00; uint32_t cpuid = 0x00; - cpuid = read_mpidr(); - boot_core = (mmio_read_32(AGX5_PWRMGR(MPU_BOOTCONFIG)) & 0xC00); + cpuid = MPIDR_AFFLVL1_VAL(read_mpidr()); + boot_core = ((mmio_read_32(AGX5_PWRMGR(MPU_BOOTCONFIG)) & 0xC00) >> 10); NOTICE("BL31: Boot Core = %x\n", boot_core); NOTICE("BL31: CPU ID = %x\n", cpuid); - + INFO("BL31: Invalidate Data cache\n"); + invalidate_dcache_all(); } /* Get non-secure image entrypoint for BL33. Zephyr and Linux */ @@ -236,6 +234,9 @@ void bl31_plat_set_secondary_cpu_entrypoint(unsigned int cpu_id) unsigned int pchctlr_new = 0x00; uint32_t boot_core = 0x00; + /* Store magic number for SMP secondary cores boot */ + mmio_write_32(L2_RESET_DONE_REG, SMP_SEC_CORE_BOOT_REQ); + boot_core = (mmio_read_32(AGX5_PWRMGR(MPU_BOOTCONFIG)) & 0xC00); /* Update the p-channel based on cpu id */ pch_cpu = 1 << cpu_id; diff --git a/plat/intel/soc/agilex5/include/agilex5_cache.h b/plat/intel/soc/agilex5/include/agilex5_cache.h new file mode 100644 index 00000000..f7801b9a --- /dev/null +++ b/plat/intel/soc/agilex5/include/agilex5_cache.h @@ -0,0 +1,13 @@ +/* + * Copyright (c) 2024, Altera Corporation. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef AGX5_CACHE_H +#define AGX5_CACHE_H + +void invalidate_dcache_all(void); +void invalidate_cache_low_el(void); + +#endif /* AGX5_CACHE_H */ diff --git a/plat/intel/soc/agilex5/include/agilex5_clock_manager.h b/plat/intel/soc/agilex5/include/agilex5_clock_manager.h index 566a80d9..1165c904 100644 --- a/plat/intel/soc/agilex5/include/agilex5_clock_manager.h +++ b/plat/intel/soc/agilex5/include/agilex5_clock_manager.h @@ -1,5 +1,6 @@ /* * Copyright (c) 2019-2022, Intel Corporation. All rights reserved. + * Copyright (c) 2024, Altera Corporation. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -10,141 +11,304 @@ #include "socfpga_handoff.h" /* Clock Manager Registers */ -#define CLKMGR_OFFSET 0x10d10000 - -#define CLKMGR_CTRL 0x0 -#define CLKMGR_STAT 0x4 -#define CLKMGR_TESTIOCTROL 0x8 -#define CLKMGR_INTRGEN 0xc -#define CLKMGR_INTRMSK 0x10 -#define CLKMGR_INTRCLR 0x14 -#define CLKMGR_INTRSTS 0x18 -#define CLKMGR_INTRSTK 0x1c -#define CLKMGR_INTRRAW 0x20 +#define CLKMGR_BASE 0x10D10000 +#define CLKMGR_CTRL 0x00 +#define CLKMGR_STAT 0x04 +#define CLKMGR_TESTIOCTROL 0x08 +#define CLKMGR_INTRGEN 0x0C +#define CLKMGR_INTRMSK 0x10 +#define CLKMGR_INTRCLR 0x14 +#define CLKMGR_INTRSTS 0x18 +#define CLKMGR_INTRSTK 0x1C +#define CLKMGR_INTRRAW 0x20 + +/* Clock manager control related macros */ +#define CLKMGR(_reg) (CLKMGR_BASE + (CLKMGR_##_reg)) +#define CLKMGR_STAT_MAINPLLLOCKED BIT(8) +#define CLKMGR_STAT_PERPLLLOCKED BIT(16) + +#define CLKMGR_INTRCLR_MAINLOCKLOST BIT(2) +#define CLKMGR_INTRCLR_PERLOCKLOST BIT(3) + +#define CLKMGR_STAT_ALLPLLLOCKED (CLKMGR_STAT_MAINPLLLOCKED | \ + CLKMGR_STAT_PERPLLLOCKED) /* Main PLL Group */ -#define CLKMGR_MAINPLL 0x10d10024 -#define CLKMGR_MAINPLL_EN 0x0 -#define CLKMGR_MAINPLL_ENS 0x4 -#define CLKMGR_MAINPLL_BYPASS 0xc -#define CLKMGR_MAINPLL_BYPASSS 0x10 -#define CLKMGR_MAINPLL_BYPASSR 0x14 -#define CLKMGR_MAINPLL_NOCCLK 0x1c -#define CLKMGR_MAINPLL_NOCDIV 0x20 -#define CLKMGR_MAINPLL_PLLGLOB 0x24 -#define CLKMGR_MAINPLL_FDBCK 0x28 -#define CLKMGR_MAINPLL_MEM 0x2c -#define CLKMGR_MAINPLL_MEMSTAT 0x30 -#define CLKMGR_MAINPLL_VCOCALIB 0x34 -#define CLKMGR_MAINPLL_PLLC0 0x38 -#define CLKMGR_MAINPLL_PLLC1 0x3c -#define CLKMGR_MAINPLL_PLLC2 0x40 -#define CLKMGR_MAINPLL_PLLC3 0x44 -#define CLKMGR_MAINPLL_PLLM 0x48 -#define CLKMGR_MAINPLL_FHOP 0x4c -#define CLKMGR_MAINPLL_SSC 0x50 -#define CLKMGR_MAINPLL_LOSTLOCK 0x54 +#define CLKMGR_MAINPLL_BASE 0x10D10024 +#define CLKMGR_MAINPLL_EN 0x00 +#define CLKMGR_MAINPLL_ENS 0x04 +#define CLKMGR_MAINPLL_ENR 0x08 +#define CLKMGR_MAINPLL_BYPASS 0x0C +#define CLKMGR_MAINPLL_BYPASSS 0x10 +#define CLKMGR_MAINPLL_BYPASSR 0x14 +#define CLKMGR_MAINPLL_NOCCLK 0x1C +#define CLKMGR_MAINPLL_NOCDIV 0x20 +#define CLKMGR_MAINPLL_PLLGLOB 0x24 +#define CLKMGR_MAINPLL_FDBCK 0x28 +#define CLKMGR_MAINPLL_MEM 0x2C +#define CLKMGR_MAINPLL_MEMSTAT 0x30 +#define CLKMGR_MAINPLL_VCOCALIB 0x34 +#define CLKMGR_MAINPLL_PLLC0 0x38 +#define CLKMGR_MAINPLL_PLLC1 0x3C +#define CLKMGR_MAINPLL_PLLC2 0x40 +#define CLKMGR_MAINPLL_PLLC3 0x44 +#define CLKMGR_MAINPLL_PLLM 0x48 +#define CLKMGR_MAINPLL_FHOP 0x4C +#define CLKMGR_MAINPLL_SSC 0x50 +#define CLKMGR_MAINPLL_LOSTLOCK 0x54 + +#define CLKMGR_MAINPLL(_reg) (CLKMGR_MAINPLL_BASE + \ + (CLKMGR_MAINPLL_##_reg)) + +#define CLKMGR_XPLL_LOSTLOCK_BYPASSCLEAR BIT(0) +#define CLKMGR_XPLLGLOB_CLR_LOSTLOCK_BYPASS BIT(29) /* Peripheral PLL Group */ -#define CLKMGR_PERPLL 0x10d1007c -#define CLKMGR_PERPLL_EN 0x0 -#define CLKMGR_PERPLL_ENS 0x4 -#define CLKMGR_PERPLL_BYPASS 0xc -#define CLKMGR_PERPLL_EMACCTL 0x18 -#define CLKMGR_PERPLL_GPIODIV 0x1c -#define CLKMGR_PERPLL_PLLGLOB 0x20 -#define CLKMGR_PERPLL_FDBCK 0x24 -#define CLKMGR_PERPLL_MEM 0x28 -#define CLKMGR_PERPLL_MEMSTAT 0x2c -#define CLKMGR_PERPLL_PLLC0 0x30 -#define CLKMGR_PERPLL_PLLC1 0x34 -#define CLKMGR_PERPLL_VCOCALIB 0x38 -#define CLKMGR_PERPLL_PLLC2 0x3c -#define CLKMGR_PERPLL_PLLC3 0x40 -#define CLKMGR_PERPLL_PLLM 0x44 -#define CLKMGR_PERPLL_LOSTLOCK 0x50 +#define CLKMGR_PERPLL_BASE 0x10D1007C +#define CLKMGR_PERPLL_EN 0x00 +#define CLKMGR_PERPLL_ENS 0x04 +#define CLKMGR_PERPLL_ENR 0x08 +#define CLKMGR_PERPLL_BYPASS 0x0C +#define CLKMGR_PERPLL_BYPASSS 0x10 +#define CLKMGR_PERPLL_BYPASSR 0x14 +#define CLKMGR_PERPLL_EMACCTL 0x18 +#define CLKMGR_PERPLL_GPIODIV 0x1C +#define CLKMGR_PERPLL_PLLGLOB 0x20 +#define CLKMGR_PERPLL_FDBCK 0x24 +#define CLKMGR_PERPLL_MEM 0x28 +#define CLKMGR_PERPLL_MEMSTAT 0x2C +#define CLKMGR_PERPLL_VCOCALIB 0x30 +#define CLKMGR_PERPLL_PLLC0 0x34 +#define CLKMGR_PERPLL_PLLC1 0x38 +#define CLKMGR_PERPLL_PLLC2 0x3C +#define CLKMGR_PERPLL_PLLC3 0x40 +#define CLKMGR_PERPLL_PLLM 0x44 +#define CLKMGR_PERPLL_FHOP 0x48 +#define CLKMGR_PERPLL_SSC 0x4C +#define CLKMGR_PERPLL_LOSTLOCK 0x50 + +#define CLKMGR_PERPLL(_reg) (CLKMGR_PERPLL_BASE + \ + (CLKMGR_PERPLL_##_reg)) /* Altera Group */ -#define CLKMGR_ALTERA 0x10d100d0 -#define CLKMGR_ALTERA_JTAG 0x0 -#define CLKMGR_ALTERA_EMACACTR 0x4 -#define CLKMGR_ALTERA_EMACBCTR 0x8 -#define CLKMGR_ALTERA_EMACPTPCTR 0xc -#define CLKMGR_ALTERA_GPIODBCTR 0x10 -#define CLKMGR_ALTERA_S2FUSER0CTR 0x18 -#define CLKMGR_ALTERA_S2FUSER1CTR 0x1c -#define CLKMGR_ALTERA_PSIREFCTR 0x20 -#define CLKMGR_ALTERA_EXTCNTRST 0x24 -#define CLKMGR_ALTERA_USB31CTR 0x28 -#define CLKMGR_ALTERA_DSUCTR 0x2c -#define CLKMGR_ALTERA_CORE01CTR 0x30 -#define CLKMGR_ALTERA_CORE23CTR 0x34 -#define CLKMGR_ALTERA_CORE2CTR 0x38 -#define CLKMGR_ALTERA_CORE3CTR 0x3c - -/* Membus */ -#define CLKMGR_MEM_REQ BIT(24) -#define CLKMGR_MEM_WR BIT(25) -#define CLKMGR_MEM_ERR BIT(26) -#define CLKMGR_MEM_WDAT_OFFSET 16 -#define CLKMGR_MEM_ADDR 0x4027 -#define CLKMGR_MEM_WDAT 0x80 +#define CLKMGR_ALTERA_BASE 0x10D100D0 +#define CLKMGR_ALTERA_JTAG 0x00 +#define CLKMGR_ALTERA_EMACACTR 0x04 +#define CLKMGR_ALTERA_EMACBCTR 0x08 +#define CLKMGR_ALTERA_EMACPTPCTR 0x0C +#define CLKMGR_ALTERA_GPIODBCTR 0x10 +#define CLKMGR_ALTERA_S2FUSER0CTR 0x18 +#define CLKMGR_ALTERA_S2FUSER1CTR 0x1C +#define CLKMGR_ALTERA_PSIREFCTR 0x20 +#define CLKMGR_ALTERA_EXTCNTRST 0x24 +#define CLKMGR_ALTERA_USB31CTR 0x28 +#define CLKMGR_ALTERA_DSUCTR 0x2C +#define CLKMGR_ALTERA_CORE01CTR 0x30 +#define CLKMGR_ALTERA_CORE23CTR 0x34 +#define CLKMGR_ALTERA_CORE2CTR 0x38 +#define CLKMGR_ALTERA_CORE3CTR 0x3C +#define CLKMGR_ALTERA_SERIAL_CON_PLL_CTR 0x40 + +#define CLKMGR_ALTERA(_reg) (CLKMGR_ALTERA_BASE + \ + (CLKMGR_ALTERA_##_reg)) + +#define CLKMGR_ALTERA_EXTCNTRST_EMACACNTRST BIT(0) +#define CLKMGR_ALTERA_EXTCNTRST_EMACBCNTRST BIT(1) +#define CLKMGR_ALTERA_EXTCNTRST_EMACPTPCNTRST BIT(2) +#define CLKMGR_ALTERA_EXTCNTRST_GPIODBCNTRST BIT(3) +#define CLKMGR_ALTERA_EXTCNTRST_S2FUSER0CNTRST BIT(5) +#define CLKMGR_ALTERA_EXTCNTRST_S2FUSER1CNTRST BIT(6) +#define CLKMGR_ALTERA_EXTCNTRST_PSIREFCNTRST BIT(7) +#define CLKMGR_ALTERA_EXTCNTRST_USB31REFCNTRST BIT(8) +#define CLKMGR_ALTERA_EXTCNTRST_DSUCNTRST BIT(10) +#define CLKMGR_ALTERA_EXTCNTRST_CORE01CNTRST BIT(11) +#define CLKMGR_ALTERA_EXTCNTRST_CORE2CNTRST BIT(12) +#define CLKMGR_ALTERA_EXTCNTRST_CORE3CNTRST BIT(13) + +#define CLKMGR_ALTERA_EXTCNTRST_ALLCNTRST \ + (CLKMGR_ALTERA_EXTCNTRST_EMACACNTRST | \ + CLKMGR_ALTERA_EXTCNTRST_EMACBCNTRST | \ + CLKMGR_ALTERA_EXTCNTRST_EMACPTPCNTRST | \ + CLKMGR_ALTERA_EXTCNTRST_GPIODBCNTRST | \ + CLKMGR_ALTERA_EXTCNTRST_S2FUSER0CNTRST |\ + CLKMGR_ALTERA_EXTCNTRST_S2FUSER1CNTRST |\ + CLKMGR_ALTERA_EXTCNTRST_PSIREFCNTRST | \ + CLKMGR_ALTERA_EXTCNTRST_USB31REFCNTRST |\ + CLKMGR_ALTERA_EXTCNTRST_DSUCNTRST | \ + CLKMGR_ALTERA_EXTCNTRST_CORE01CNTRST | \ + CLKMGR_ALTERA_EXTCNTRST_CORE2CNTRST | \ + CLKMGR_ALTERA_EXTCNTRST_CORE3CNTRST) + +#define CLKMGR_ALTERA_CORE0 0 +#define CLKMGR_ALTERA_CORE1 1 +#define CLKMGR_ALTERA_CORE2 2 +#define CLKMGR_ALTERA_CORE3 3 + +/* PLL membus configuration macros */ +#define CLKMGR_MEM_REQ BIT(24) +#define CLKMGR_MEM_WR BIT(25) +#define CLKMGR_MEM_ERR BIT(26) +#define CLKMGR_MEM_WDAT_OFFSET 16 +#define CLKMGR_MEM_ADDR_MASK GENMASK(15, 0) +#define CLKMGR_MEM_ADDR_START 0x00004000 +#define CLKMGR_PLLCFG_SRC_SYNC_MODE 0x27 +#define CLKMGR_PLLCFG_OVRSHOOT_FREQ_LOCK 0xB3 +#define CLKMGR_PLLCFG_LOCK_SETTLE_TIME 0xE6 +#define CLKMGR_PLLCFG_DUTYCYCLE_CLKSLICE0 0x03 +#define CLKMGR_PLLCFG_DUTYCYCLE_CLKSLICE1 0x07 /* Clock Manager Macros */ -#define CLKMGR_CTRL_BOOTMODE_SET_MSK 0x00000001 -#define CLKMGR_STAT_BUSY_E_BUSY 0x1 -#define CLKMGR_STAT_BUSY(x) (((x) & 0x00000001) >> 0) -#define CLKMGR_STAT_MAINPLLLOCKED(x) (((x) & 0x00000100) >> 8) -#define CLKMGR_STAT_PERPLLLOCKED(x) (((x) & 0x00010000) >> 16) -#define CLKMGR_INTRCLR_MAINLOCKLOST_SET_MSK 0x00000004 -#define CLKMGR_INTRCLR_PERLOCKLOST_SET_MSK 0x00000008 -#define CLKMGR_INTOSC_HZ 460000000 +#define CLKMGR_CTRL_BOOTMODE_SET_MSK 0x00000001 +#define CLKMGR_STAT_BUSY_E_BUSY 0x1 +#define CLKMGR_STAT_BUSY(x) (((x) & 0x00000001) >> 0) +#define CLKMGR_INTRCLR_MAINLOCKLOST_SET_MSK 0x00000004 +#define CLKMGR_INTRCLR_PERLOCKLOST_SET_MSK 0x00000008 +#define CLKMGR_INTOSC_HZ 460000000 +#define CLKMGR_CTRL_BOOTMODE BIT(0) +#define CLKMGR_STAT_MAINPLL_LOCKED BIT(8) +#define CLKMGR_STAT_MAIN_TRANS BIT(9) +#define CLKMGR_STAT_PERPLL_LOCKED BIT(16) +#define CLKMGR_STAT_PERF_TRANS BIT(17) +#define CLKMGR_STAT_BOOTMODE BIT(24) +#define CLKMGR_STAT_BOOTCLKSRC BIT(25) +#define CLKMGR_STAT_ALLPLL_LOCKED_MASK (CLKMGR_STAT_MAINPLL_LOCKED | \ + CLKMGR_STAT_PERPLL_LOCKED) /* Main PLL Macros */ -#define CLKMGR_MAINPLL_EN_RESET 0x0000005e -#define CLKMGR_MAINPLL_ENS_RESET 0x0000005e +#define CLKMGR_MAINPLL_EN_RESET 0x0000005E +#define CLKMGR_MAINPLL_ENS_RESET 0x0000005E +#define CLKMGR_MAINPLL_PLLGLOB_PD_N BIT(0) +#define CLKMGR_MAINPLL_PLLGLOB_RST_N BIT(1) +#define CLKMGR_MAINPLL_PLLCX_EN BIT(27) +#define CLKMGR_MAINPLL_PLLCX_MUTE BIT(28) -/* Peripheral PLL Macros */ -#define CLKMGR_PERPLL_EN_RESET 0x040007FF -#define CLKMGR_PERPLL_ENS_RESET 0x040007FF - -#define CLKMGR_PERPLL_EN_SDMMCCLK BIT(5) -#define CLKMGR_PERPLL_GPIODIV_GPIODBCLK_SET(x) (((x) << 0) & 0x0000ffff) +#define CLKMGR_PERPLL_EN_SDMMCCLK BIT(5) +#define CLKMGR_PERPLL_GPIODIV_GPIODBCLK_SET(x) (((x) << 0) & 0x0000FFFF) +#define CLKMGR_PERPLL_PLLGLOB_PD_N BIT(0) +#define CLKMGR_PERPLL_PLLGLOB_RST_N BIT(1) +#define CLKMGR_PERPLL_PLLCX_EN BIT(27) +#define CLKMGR_PERPLL_PLLCX_MUTE BIT(28) /* Altera Macros */ -#define CLKMGR_ALTERA_EXTCNTRST_RESET 0xff +#define CLKMGR_ALTERA_EXTCNTRST_RESET 0xFF /* Shared Macros */ -#define CLKMGR_PSRC(x) (((x) & 0x00030000) >> 16) -#define CLKMGR_PSRC_MAIN 0 -#define CLKMGR_PSRC_PER 1 +#define CLKMGR_PLLGLOB_PSRC(x) (((x) & 0x00030000) >> 16) +#define CLKMGR_PSRC_MAIN 0 +#define CLKMGR_PSRC_PER 1 + +#define CLKMGR_PLLGLOB_PSRC_EOSC1 0x0 +#define CLKMGR_PLLGLOB_PSRC_INTOSC 0x1 +#define CLKMGR_PLLGLOB_PSRC_F2S 0x2 + +#define CLKMGR_PLLM_MDIV(x) ((x) & 0x000003FF) +#define CLKMGR_PLLGLOB_PD_SET_MSK 0x00000001 +#define CLKMGR_PLLGLOB_RST_SET_MSK 0x00000002 + +#define CLKMGR_PLLGLOB_REFCLKDIV(x) (((x) & 0x00003F00) >> 8) +#define CLKMGR_PLLGLOB_AREFCLKDIV(x) (((x) & 0x00000F00) >> 8) +#define CLKMGR_PLLGLOB_DREFCLKDIV(x) (((x) & 0x00003000) >> 12) + +#define CLKMGR_VCOCALIB_HSCNT_SET(x) (((x) << 0) & 0x000003FF) +#define CLKMGR_VCOCALIB_MSCNT_SET(x) (((x) << 16) & 0x00FF0000) + +#define CLKMGR_CLR_LOSTLOCK_BYPASS 0x20000000 + +#define CLKMGR_CLKSRC_MASK GENMASK(18, 16) +#define CLKMGR_CLKSRC_OFFSET 16 +#define CLKMGR_CLKSRC_MAIN 0 +#define CLKMGR_CLKSRC_PER 1 +#define CLKMGR_CLKSRC_OSC1 2 +#define CLKMGR_CLKSRC_INTOSC 3 +#define CLKMGR_CLKSRC_FPGA 4 +#define CLKMGR_PLLCX_DIV_MSK GENMASK(10, 0) + +#define GET_CLKMGR_CLKSRC(x) (((x) & CLKMGR_CLKSRC_MASK) >> \ + CLKMGR_CLKSRC_OFFSET) + +#define CLKMGR_MAINPLL_NOCDIV_L4MP_MASK GENMASK(5, 4) +#define CLKMGR_MAINPLL_NOCDIV_L4MP_OFFSET 4 +#define GET_CLKMGR_MAINPLL_NOCDIV_L4MP(x) (((x) & CLKMGR_MAINPLL_NOCDIV_L4MP_MASK) >> \ + CLKMGR_MAINPLL_NOCDIV_L4MP_OFFSET) + +#define CLKMGR_MAINPLL_NOCDIV_L4SP_MASK GENMASK(7, 6) +#define CLKMGR_MAINPLL_NOCDIV_L4SP_OFFSET 6 +#define GET_CLKMGR_MAINPLL_NOCDIV_L4SP(x) (((x) & CLKMGR_MAINPLL_NOCDIV_L4SP_MASK) >> \ + CLKMGR_MAINPLL_NOCDIV_L4SP_OFFSET) + +#define CLKMGR_MAINPLL_NOCDIV_SPHY_MASK GENMASK(17, 16) +#define CLKMGR_MAINPLL_NOCDIV_SPHY_OFFSET 16 +#define GET_CLKMGR_MAINPLL_NOCDIV_SPHY(x) (((x) & CLKMGR_MAINPLL_NOCDIV_SPHY_MASK) >> \ + CLKMGR_MAINPLL_NOCDIV_SPHY_OFFSET) + + +#define CLKMGR_MAINPLL_NOCDIV_L4SYSFREE_MASK GENMASK(3, 2) +#define CLKMGR_MAINPLL_NOCDIV_L4SYSFREE_OFFSET 2 +#define GET_CLKMGR_MAINPLL_NOCDIV_L4SYSFREE(x) (((x) & CLKMGR_MAINPLL_NOCDIV_L4SYSFREE_MASK) >> \ + CLKMGR_MAINPLL_NOCDIV_L4SYSFREE_OFFSET) + +#define CLKMGR_PERPLL_EMAC0_CLK_SRC_MASK BIT(26) +#define CLKMGR_PERPLL_EMAC0_CLK_SRC_OFFSET 26 +#define GET_CLKMGR_PERPLL_EMAC0_CLK_SRC(x) (((x) & CLKMGR_PERPLL_EMAC0_CLK_SRC_MASK) >> \ + CLKMGR_PERPLL_EMAC0_CLK_SRC_OFFSET) + +#define CLKMGR_ALTERA_EMACACTR_CLK_SRC_MASK GENMASK(18, 16) +#define CLKMGR_ALTERA_EMACACTR_CLK_SRC_OFFSET 16 +#define GET_CLKMGR_EMACACTR_CLK_SRC(x) (((x) & CLKMGR_ALTERA_EMACACTR_CLK_SRC_MASK) >> \ + CLKMGR_ALTERA_EMACACTR_CLK_SRC_OFFSET) + +#define CLKMGR_MPU_CLK_ID 0 +#define CLKMGR_MPU_PERIPH_CLK_ID 1 +#define CLKMGR_L4_MAIN_CLK_ID 2 +#define CLKMGR_L4_MP_CLK_ID 3 +#define CLKMGR_L4_SP_CLK_ID 4 +#define CLKMGR_WDT_CLK_ID 5 +#define CLKMGR_UART_CLK_ID 6 +#define CLKMGR_EMAC0_CLK_ID 7 +#define CLKMGR_EMAC1_CLK_ID 8 +#define CLKMGR_EMAC2_CLK_ID 9 +#define CLKMGR_EMAC_PTP_CLK_ID 10 +#define CLKMGR_SDMMC_CLK_ID 11 -#define CLKMGR_PLLGLOB_PSRC_EOSC1 0x0 -#define CLKMGR_PLLGLOB_PSRC_INTOSC 0x1 -#define CLKMGR_PLLGLOB_PSRC_F2S 0x2 +#define CLKMGR_MAINPLL_BYPASS_ALL (0xF6) +#define CLKMGR_PERPLL_BYPASS_ALL (0xEF) +#define CLKMGR_PLLCX_STAT BIT(29) +#define GET_PLLCX_STAT(x) ((x) & CLKMGR_PLLCX_STAT) -#define CLKMGR_PLLM_MDIV(x) ((x) & 0x000003ff) -#define CLKMGR_PLLGLOB_PD_SET_MSK 0x00000001 -#define CLKMGR_PLLGLOB_RST_SET_MSK 0x00000002 +#define CLKMGR_MAINPLL_TYPE (0) +#define CLKMGR_PERPLL_TYPE (1) -#define CLKMGR_PLLGLOB_REFCLKDIV(x) (((x) & 0x00003f00) >> 8) -#define CLKMGR_PLLGLOB_AREFCLKDIV(x) (((x) & 0x00000f00) >> 8) -#define CLKMGR_PLLGLOB_DREFCLKDIV(x) (((x) & 0x00003000) >> 12) +#define CLKMGR_MAX_RETRY_COUNT 1000 -#define CLKMGR_VCOCALIB_HSCNT_SET(x) (((x) << 0) & 0x000003ff) -#define CLKMGR_VCOCALIB_MSCNT_SET(x) (((x) << 16) & 0x00ff0000) +#define CLKMGR_PLLM_MDIV_MASK GENMASK(9, 0) +#define CLKMGR_PLLGLOB_PD_MASK BIT(0) +#define CLKMGR_PLLGLOB_RST_MASK BIT(1) +#define CLKMGR_PLLGLOB_AREFCLKDIV_MASK GENMASK(11, 8) +#define CLKMGR_PLLGLOB_DREFCLKDIV_MASK GENMASK(13, 12) +#define CLKMGR_PLLGLOB_REFCLKDIV_MASK GENMASK(13, 8) +#define CLKMGR_PLLGLOB_MODCLKDIV_MASK GENMASK(24, 27) +#define CLKMGR_PLLGLOB_AREFCLKDIV_OFFSET 8 +#define CLKMGR_PLLGLOB_DREFCLKDIV_OFFSET 12 +#define CLKMGR_PLLGLOB_REFCLKDIV_OFFSET 8 +#define CLKMGR_PLLGLOB_MODCLKDIV_OFFSET 24 +#define CLKMGR_PLLGLOB_VCO_PSRC_MASK GENMASK(17, 16) +#define CLKMGR_PLLGLOB_VCO_PSRC_OFFSET 16 +#define CLKMGR_PLLGLOB_CLR_LOSTLOCK_BYPASS_MASK BIT(29) -#define CLKMGR_CLR_LOSTLOCK_BYPASS 0x20000000 +#define CLKMGR_VCOCALIB_MSCNT_MASK GENMASK(23, 16) +#define CLKMGR_VCOCALIB_MSCNT_OFFSET 16 +#define CLKMGR_VCOCALIB_HSCNT_MASK GENMASK(9, 0) +#define CLKMGR_VCOCALIB_MSCNT_CONST 100 +#define CLKMGR_VCOCALIB_HSCNT_CONST 4 -typedef struct { - uint32_t clk_freq_of_eosc1; - uint32_t clk_freq_of_f2h_free; - uint32_t clk_freq_of_cb_intosc_ls; -} CLOCK_SOURCE_CONFIG; +int config_clkmgr_handoff(handoff *hoff_ptr); +uint32_t clkmgr_get_rate(uint32_t clk_id); -void config_clkmgr_handoff(handoff *hoff_ptr); -uint32_t get_wdt_clk(void); -uint32_t get_uart_clk(void); -uint32_t get_mmc_clk(void); +/* PLL configuration data structure in power-down state */ +typedef struct pll_cfg { + uint32_t addr; + uint32_t data; + uint32_t mask; +} pll_cfg_t; #endif diff --git a/plat/intel/soc/agilex5/include/agilex5_ddr.h b/plat/intel/soc/agilex5/include/agilex5_ddr.h new file mode 100644 index 00000000..631e006e --- /dev/null +++ b/plat/intel/soc/agilex5/include/agilex5_ddr.h @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2024, Altera Corporation. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef AGILEX5_DDR_H +#define AGILEX5_DDR_H + +#include <stdint.h> +#include <stdio.h> +#include <string.h> +#include <lib/utils_def.h> + +#include "socfpga_handoff.h" + +#define CONFIG_NR_DRAM_BANKS 1 + +typedef unsigned long long phys_addr_t; +typedef unsigned long long phys_size_t; +typedef phys_addr_t fdt_addr_t; + +/* DDR/RAM configuration */ +struct ddr_info { + phys_addr_t start; + phys_size_t size; +}; + +int agilex5_ddr_init(handoff *hoff_ptr); + +#endif /* AGILEX5_DDR_H */ diff --git a/plat/intel/soc/agilex5/include/agilex5_iossm_mailbox.h b/plat/intel/soc/agilex5/include/agilex5_iossm_mailbox.h new file mode 100644 index 00000000..1fd8ef6f --- /dev/null +++ b/plat/intel/soc/agilex5/include/agilex5_iossm_mailbox.h @@ -0,0 +1,155 @@ +/* + * Copyright (c) 2024, Altera Corporation. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef AGILEX5_IOSSM_MAILBOX_H +#define AGILEX5_IOSSM_MAILBOX_H + +#include <stdbool.h> +#include <stdint.h> +#include <stdlib.h> + +#include "lib/mmio.h" +#include "agilex5_ddr.h" + +#define TIMEOUT_5000MS 5000 +#define TIMEOUT TIMEOUT_5000MS +#define IOSSM_STATUS_CAL_SUCCESS BIT(0) +#define IOSSM_STATUS_CAL_FAIL BIT(1) +#define IOSSM_STATUS_CAL_BUSY BIT(2) +#define IOSSM_STATUS_COMMAND_RESPONSE_READY 1 +#define IOSSM_CMD_RESPONSE_STATUS_OFFSET 0x45C +#define IOSSM_CMD_RESPONSE_DATA_0_OFFSET 0x458 +#define IOSSM_CMD_RESPONSE_DATA_1_OFFSET 0x454 +#define IOSSM_CMD_RESPONSE_DATA_2_OFFSET 0x450 +#define IOSSM_CMD_REQ_OFFSET 0x43C +#define IOSSM_CMD_PARAM_0_OFFSET 0x438 +#define IOSSM_CMD_PARAM_1_OFFSET 0x434 +#define IOSSM_CMD_PARAM_2_OFFSET 0x430 +#define IOSSM_CMD_PARAM_3_OFFSET 0x42C +#define IOSSM_CMD_PARAM_4_OFFSET 0x428 +#define IOSSM_CMD_PARAM_5_OFFSET 0x424 +#define IOSSM_CMD_PARAM_6_OFFSET 0x420 +#define IOSSM_STATUS_OFFSET 0x400 +#define IOSSM_CMD_RESPONSE_DATA_SHORT_MASK GENMASK(31, 16) +#define IOSSM_CMD_RESPONSE_DATA_SHORT(data) (((data) & \ + IOSSM_CMD_RESPONSE_DATA_SHORT_MASK) >> 16) +#define MAX_IO96B_SUPPORTED 2 +#define MAX_MEM_INTERFACES_SUPPORTED 2 + +/* supported mailbox command type */ +enum iossm_mailbox_cmd_type { + CMD_NOP, + CMD_GET_SYS_INFO, + CMD_GET_MEM_INFO, + CMD_GET_MEM_CAL_INFO, + CMD_TRIG_CONTROLLER_OP, + CMD_TRIG_MEM_CAL_OP +}; + +/* supported mailbox command opcode */ +enum iossm_mailbox_cmd_opcode { + GET_MEM_INTF_INFO = 0x0001, + GET_MEM_TECHNOLOGY, + GET_MEMCLK_FREQ_KHZ, + GET_MEM_WIDTH_INFO, + ECC_ENABLE_SET = 0x0101, + ECC_ENABLE_STATUS, + ECC_INTERRUPT_STATUS, + ECC_INTERRUPT_ACK, + ECC_INTERRUPT_MASK, + ECC_WRITEBACK_ENABLE, + ECC_SCRUB_IN_PROGRESS_STATUS = 0x0201, + ECC_SCRUB_MODE_0_START, + ECC_SCRUB_MODE_1_START, + BIST_STANDARD_MODE_START = 0x0301, + BIST_RESULTS_STATUS, + BIST_MEM_INIT_START, + BIST_MEM_INIT_STATUS, + BIST_SET_DATA_PATTERN_UPPER, + BIST_SET_DATA_PATTERN_LOWER, + TRIG_MEM_CAL = 0x000a, + GET_MEM_CAL_STATUS +}; + +/* + * IOSSM mailbox required information + * + * @num_mem_interface: Number of memory interfaces instantiated + * @ip_type: IP type implemented on the IO96B + * @ip_instance_id: IP identifier for every IP instance implemented on the IO96B + */ +struct io96b_mb_ctrl { + uint32_t num_mem_interface; + uint32_t ip_type[2]; + uint32_t ip_instance_id[2]; +}; + +/* + * IOSSM mailbox response outputs + * + * @cmd_resp_status: Command Interface status + * @cmd_resp_data_*: More spaces for command response + */ +struct io96b_mb_resp { + uint32_t cmd_resp_status; + uint32_t cmd_resp_data_0; + uint32_t cmd_resp_data_1; + uint32_t cmd_resp_data_2; +}; + +/* + * IO96B instance specific information + * + * @size: Memory size + * @io96b_csr_addr: IO96B instance CSR address + * @cal_status: IO96B instance calibration status + * @mb_ctrl: IOSSM mailbox required information + */ +struct io96b_instance { + uint16_t size; + phys_addr_t io96b_csr_addr; + bool cal_status; + struct io96b_mb_ctrl mb_ctrl; +}; + +/* + * Overall IO96B instance(s) information + * + * @num_instance: Number of instance(s) assigned to HPS + * @overall_cal_status: Overall calibration status for all IO96B instance(s) + * @ddr_type: DDR memory type + * @ecc_status: ECC enable status (false = disabled, true = enabled) + * @overall_size: Total DDR memory size + * @io96b_0: IO96B 0 instance specific information + * @io96b_1: IO96B 1 instance specific information + */ +struct io96b_info { + uint8_t num_instance; + bool overall_cal_status; + const char *ddr_type; + bool ecc_status; + uint16_t overall_size; + struct io96b_instance io96b_0; + struct io96b_instance io96b_1; +}; + +int io96b_mb_req(phys_addr_t io96b_csr_addr, uint32_t ip_type, uint32_t instance_id, + uint32_t usr_cmd_type, uint32_t usr_cmd_opcode, uint32_t cmd_param_0, + uint32_t cmd_param_1, uint32_t cmd_param_2, uint32_t cmd_param_3, + uint32_t cmd_param_4, uint32_t cmd_param_5, uint32_t cmd_param_6, + uint32_t resp_data_len, struct io96b_mb_resp *resp); + +/* Supported IOSSM mailbox function */ +void io96b_mb_init(struct io96b_info *io96b_ctrl); +int io96b_cal_status(phys_addr_t addr); +void init_mem_cal(struct io96b_info *io96b_ctrl); +int trig_mem_cal(struct io96b_info *io96b_ctrl); +int get_mem_technology(struct io96b_info *io96b_ctrl); +int get_mem_width_info(struct io96b_info *io96b_ctrl); +int ecc_enable_status(struct io96b_info *io96b_ctrl); +int bist_mem_init_start(struct io96b_info *io96b_ctrl); + +#endif /* AGILEX5_IOSSM_MAILBOX_H */ diff --git a/plat/intel/soc/agilex5/include/agilex5_pinmux.h b/plat/intel/soc/agilex5/include/agilex5_pinmux.h index 8a8e8c78..78d19af2 100644 --- a/plat/intel/soc/agilex5/include/agilex5_pinmux.h +++ b/plat/intel/soc/agilex5/include/agilex5_pinmux.h @@ -1,5 +1,6 @@ /* * Copyright (c) 2019-2023, Intel Corporation. All rights reserved. + * Copyright (c) 2024, Altera Corporation. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -7,19 +8,13 @@ #ifndef AGX5_PINMUX_H #define AGX5_PINMUX_H -/* PINMUX REGISTER ADDRESS */ -#define AGX5_PINMUX_PIN0SEL 0x10d13000 -#define AGX5_PINMUX_IO0CTRL 0x10d13130 -#define AGX5_PINMUX_EMAC0_USEFPGA 0x10d13300 -#define AGX5_PINMUX_IO0_DELAY 0x10d13400 -#define AGX5_PERIPHERAL 0x10d14044 - #include "socfpga_handoff.h" -/* PINMUX DEFINE */ -#define PINMUX_HANDOFF_ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) -#define PINMUX_HANDOFF_CONFIG_ADDR 0xbeec -#define PINMUX_HANDOFF_CONFIG_VAL 0x7e000 +/* PINMUX REGISTER ADDRESS */ +#define AGX5_PINMUX_PIN0SEL 0x10D13000 +#define AGX5_PINMUX_IO0CTRL 0x10D13130 +#define AGX5_PINMUX_EMAC0_USEFPGA 0x10D13300 +#define AGX5_PINMUX_IO0_DELAY 0x10D13400 /* Macros */ #define SOCFPGA_PINMUX_SEL_NAND (0x03) @@ -142,6 +137,9 @@ #define SOCFPGA_PINMUX_JTAG_USEFPGA (0x50) #define SOCFPGA_PINMUX_SDMMC_USEFPGA (0x54) +#define SOCFPGA_PINUMX_USEFPGA(_reg) (AGX5_PINMUX_EMAC0_USEFPGA \ + + SOCFPGA_PINMUX_##_reg) + #define SOCFPGA_PINMUX_IO0DELAY (0x00) #define SOCFPGA_PINMUX_IO1DELAY (0x04) #define SOCFPGA_PINMUX_IO2DELAY (0x08) @@ -198,5 +196,4 @@ + (SOCFPGA_PINMUX_##_reg)) void config_pinmux(handoff *handoff); -void config_peripheral(handoff *handoff); #endif diff --git a/plat/intel/soc/agilex5/include/agilex5_power_manager.h b/plat/intel/soc/agilex5/include/agilex5_power_manager.h index 1bba74be..178fd5b6 100644 --- a/plat/intel/soc/agilex5/include/agilex5_power_manager.h +++ b/plat/intel/soc/agilex5/include/agilex5_power_manager.h @@ -1,5 +1,6 @@ /* * Copyright (c) 2022-2023, Intel Corporation. All rights reserved. + * Copyright (c) 2024, Altera Corporation. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -77,7 +78,5 @@ #define AGX5_PWRMGR_PSS_STAT_BUSY_E_BUSY 0x0 #define AGX5_PWRMGR_PSS_STAT_BUSY(x) (((x) & 0x000000FF) >> 0) -int pss_sram_power_off(handoff *hoff_ptr); -int wait_verify_fsm(uint16_t timeout, uint32_t peripheral_handoff); - +void config_pwrmgr_handoff(handoff *hoff_ptr); #endif diff --git a/plat/intel/soc/agilex5/include/agilex5_system_manager.h b/plat/intel/soc/agilex5/include/agilex5_system_manager.h index 9a58cdb1..ac4bf128 100644 --- a/plat/intel/soc/agilex5/include/agilex5_system_manager.h +++ b/plat/intel/soc/agilex5/include/agilex5_system_manager.h @@ -1,5 +1,6 @@ /* * Copyright (c) 2019-2023, Intel Corporation. All rights reserved. + * Copyright (c) 2024, Altera Corporation. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -24,6 +25,7 @@ #define SOCFPGA_SYSMGR_TSN_0_ACE 0x50 #define SOCFPGA_SYSMGR_TSN_1_ACE 0x54 #define SOCFPGA_SYSMGR_TSN_2_ACE 0x58 +#define SOCFPGA_SYSMGR_FPGA_BRIDGE_CTRL 0x5C #define SOCFPGA_SYSMGR_FPGAINTF_EN_1 0x68 #define SOCFPGA_SYSMGR_FPGAINTF_EN_2 0x6C #define SOCFPGA_SYSMGR_FPGAINTF_EN_3 0x70 @@ -121,7 +123,7 @@ #define SOCFPGA_SYSMGR_BOOT_SCRATCH_COLD_8 0x220 #define SOCFPGA_SYSMGR_BOOT_SCRATCH_COLD_9 0x224 #define SOCFPGA_SYSMGR_MPFE_CONFIG 0x228 -#define SOCFPGA_SYSMGR_MPFE_status 0x22C +#define SOCFPGA_SYSMGR_MPFE_STATUS 0x22C #define SOCFPGA_SYSMGR_BOOT_SCRATCH_WARM_0 0x230 #define SOCFPGA_SYSMGR_BOOT_SCRATCH_WARM_1 0x234 #define SOCFPGA_SYSMGR_BOOT_SCRATCH_WARM_2 0x238 @@ -142,6 +144,21 @@ #define SOCFPGA_SYSMGR_BOOT_SCRATCH_POR_7 0x274 #define SOCFPGA_SYSMGR_BOOT_SCRATCH_POR_8 0x278 #define SOCFPGA_SYSMGR_BOOT_SCRATCH_POR_9 0x27C +#define SOCFPGA_SYSMGR_SDM_BE_AWADDR_REMAP 0x280 +#define SOCFPGA_SYSMGR_SDM_BE_ARADDR_REMAP 0x284 + +/* QSPI ECC from SDM register */ +#define SOCFPGA_ECC_QSPI_CTRL 0x08 +#define SOCFPGA_ECC_QSPI_INITSTAT 0x0C +#define SOCFPGA_ECC_QSPI_ERRINTEN 0x10 +#define SOCFPGA_ECC_QSPI_ERRINTENS 0x14 +#define SOCFPGA_ECC_QSPI_ERRINTENR 0x18 +#define SOCFPGA_ECC_QSPI_INTMODE 0x1C +#define SOCFPGA_ECC_QSPI_INTSTAT 0x20 +#define SOCFPGA_ECC_QSPI_INTTEST 0x24 +#define SOCFPGA_ECC_QSPI_ECC_ACCCTRL 0x78 +#define SOCFPGA_ECC_QSPI_ECC_STARTACC 0x7C +#define SOCFPGA_ECC_QSPI_ECC_WDCTRL 0x80 #define DMA0_STREAM_CTRL_REG 0x10D1217C #define DMA1_STREAM_CTRL_REG 0x10D12180 @@ -168,11 +185,28 @@ #define SDM 0x000A000A #define CORE_SIGHT_DEBUG 0x000B000B +/* JTAG ID value for Agilex5 */ +#define A590_JTAG_ID 0x9000 +#define A594_JTAG_ID 0x40009000 +#define A5C0_JTAG_ID 0xC000 +#define A5C4_JTAG_ID 0x4000C000 +#define A5D0_JTAG_ID 0xD000 +#define A5D4_JTAG_ID 0x4000D000 +#define A5F0_JTAG_ID 0xC000 +#define A5F4_JTAG_ID 0x4000F000 +#define A510_JTAG_ID 0x1000 +#define A514_JTAG_ID 0x40001000 +#define A530_JTAG_ID 0x3000 +#define A534_JTAG_ID 0x40003000 +#define JTAG_ID_MASK 0xC000F000 + /* Field Masking */ #define SYSMGR_SDMMC_DRVSEL(x) (((x) & 0x7) << 0) #define SYSMGR_SDMMC_SMPLSEL(x) (((x) & 0x7) << 4) #define SYSMGR_F2S_BRIDGE_CTRL_EN BIT(0) +#define SYSMGR_SOC_BRIDGE_CTRL_EN BIT(0) +#define SYSMGR_LWSOC_BRIDGE_CTRL_EN BIT(1) #define IDLE_DATA_LWSOC2FPGA BIT(4) #define IDLE_DATA_SOC2FPGA BIT(0) #define IDLE_DATA_MASK (IDLE_DATA_LWSOC2FPGA \ @@ -187,9 +221,10 @@ #define RMMUSECSID_REG_VAL BIT(5) /* Macros */ +#define SOCFPGA_ECC_QSPI(_reg) (SOCFPGA_ECC_QSPI_REG_BASE \ + + (SOCFPGA_ECC_QSPI_##_reg)) #define SOCFPGA_SYSMGR(_reg) (SOCFPGA_SYSMGR_REG_BASE \ + (SOCFPGA_SYSMGR_##_reg)) - #define ENABLE_STREAMID WSTREAMIDEN_REG_CTRL \ | RSTREAMIDEN_REG_CTRL #define ENABLE_STREAMID_SECURE_TX WSTREAMIDEN_REG_CTRL \ diff --git a/plat/intel/soc/agilex5/include/socfpga_plat_def.h b/plat/intel/soc/agilex5/include/socfpga_plat_def.h index 8a49d613..c1f3cc50 100644 --- a/plat/intel/soc/agilex5/include/socfpga_plat_def.h +++ b/plat/intel/soc/agilex5/include/socfpga_plat_def.h @@ -1,6 +1,7 @@ /* * Copyright (c) 2019, ARM Limited and Contributors. All rights reserved. * Copyright (c) 2019-2023, Intel Corporation. All rights reserved. + * Copyright (c) 2024, Altera Corporation. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -10,31 +11,59 @@ #include "agilex5_memory_controller.h" #include "agilex5_system_manager.h" + #include <platform_def.h> /* Platform Setting */ #define PLATFORM_MODEL PLAT_SOCFPGA_AGILEX5 -#define BOOT_SOURCE BOOT_SOURCE_SDMMC +/* 1 = Flush cache, 0 = No cache flush. + * Default for Agilex5 is Cache flush. + */ +#define CACHE_FLUSH 1 #define MMC_DEVICE_TYPE 1 /* MMC = 0, SD = 1 */ #define XLAT_TABLES_V2 U(1) #define PLAT_PRIMARY_CPU_A55 0x000 #define PLAT_PRIMARY_CPU_A76 0x200 #define PLAT_CLUSTER_ID_MPIDR_AFF_SHIFT MPIDR_AFF2_SHIFT #define PLAT_CPU_ID_MPIDR_AFF_SHIFT MPIDR_AFF1_SHIFT -#define PLAT_L2_RESET_REQ 0xB007C0DE +#define PLAT_L2_RESET_REQ 0xB007C0DE +#define PLAT_HANDOFF_OFFSET 0x0007F000 +#define PLAT_TIMER_BASE_ADDR 0x10D01000 -/* System Counter */ /* TODO: Update back to 400MHz */ -#define PLAT_SYS_COUNTER_FREQ_IN_TICKS (80000000) -#define PLAT_SYS_COUNTER_FREQ_IN_MHZ (80) +/* System Counter */ +/* TODO: Update back to 400MHz. + * This shall be updated to read from L4 clock instead of hardcoded. + */ +#define PLAT_SYS_COUNTER_FREQ_IN_TICKS U(400000000) +#define PLAT_SYS_COUNTER_FREQ_IN_MHZ U(400) /* FPGA config helpers */ -#define INTEL_SIP_SMC_FPGA_CONFIG_ADDR 0x400000 -#define INTEL_SIP_SMC_FPGA_CONFIG_SIZE 0x2000000 +#define INTEL_SIP_SMC_FPGA_CONFIG_ADDR 0x80400000 +#define INTEL_SIP_SMC_FPGA_CONFIG_SIZE 0x82000000 /* QSPI Setting */ #define CAD_QSPIDATA_OFST 0x10900000 #define CAD_QSPI_OFFSET 0x108d2000 +/* FIP Setting */ +#define PLAT_FIP_BASE (0) +#if ARM_LINUX_KERNEL_AS_BL33 +#define PLAT_FIP_MAX_SIZE (0x8000000) +#else +#define PLAT_FIP_MAX_SIZE (0x1000000) +#endif + +/* SDMMC Setting */ +#if ARM_LINUX_KERNEL_AS_BL33 +#define PLAT_MMC_DATA_BASE (0x90000000) +#define PLAT_MMC_DATA_SIZE (0x100000) +#define SOCFPGA_MMC_BLOCK_SIZE U(32768) +#else +#define PLAT_MMC_DATA_BASE (0x0007D000) +#define PLAT_MMC_DATA_SIZE (0x2000) +#define SOCFPGA_MMC_BLOCK_SIZE U(8192) +#endif + /* Register Mapping */ #define SOCFPGA_CCU_NOC_REG_BASE 0x1c000000 #define SOCFPGA_F2SDRAMMGR_REG_BASE 0x18001000 @@ -45,14 +74,27 @@ #define SOCFPGA_SYSMGR_REG_BASE 0x10d12000 #define SOCFPGA_PINMUX_REG_BASE 0x10d13000 #define SOCFPGA_NAND_REG_BASE 0x10B80000 +#define SOCFPGA_ECC_QSPI_REG_BASE 0x10A22000 #define SOCFPGA_L4_PER_SCR_REG_BASE 0x10d21000 #define SOCFPGA_L4_SYS_SCR_REG_BASE 0x10d21100 #define SOCFPGA_SOC2FPGA_SCR_REG_BASE 0x10d21200 #define SOCFPGA_LWSOC2FPGA_SCR_REG_BASE 0x10d21300 +#define SOCFPGA_SDMMC_SECU_BIT 0x40 +#define SOCFPGA_LWSOC2FPGA_ENABLE 0xffe0301 +#define SOCFPGA_SDMMC_SECU_BIT_ENABLE 0x1010001 + /* Define maximum page size for NAND flash devices */ -#define PLATFORM_MTD_MAX_PAGE_SIZE U(0x1000) +#define PLATFORM_MTD_MAX_PAGE_SIZE U(0x2000) + +/* OCRAM Register*/ + +#define OCRAM_REG_BASE 0x108CC400 +#define OCRAM_REGION_0_OFFSET 0x18 +#define OCRAM_REGION_0_REG_BASE (OCRAM_REG_BASE + \ + OCRAM_REGION_0_OFFSET) +#define OCRAM_NON_SECURE_ENABLE 0x0 /******************************************************************************* * Platform memory map related constants @@ -83,17 +125,21 @@ #define GIC_SIZE (0x00100000) #define BL2_BASE (0x00000000) -#define BL2_LIMIT (0x0001b000) +#define BL2_LIMIT (0x0007E000) #define BL31_BASE (0x80000000) #define BL31_LIMIT (0x82000000) - /******************************************************************************* * UART related constants ******************************************************************************/ #define PLAT_UART0_BASE (0x10C02000) #define PLAT_UART1_BASE (0x10C02100) +/******************************************************************************* + * WDT related constants + ******************************************************************************/ +#define WDT_BASE (0x10D00200) + /******************************************************************************* * GIC related constants ******************************************************************************/ @@ -107,13 +153,13 @@ /******************************************************************************* * SDMMC related pointer function ******************************************************************************/ -#define SDMMC_READ_BLOCKS sdmmc_read_blocks -#define SDMMC_WRITE_BLOCKS sdmmc_write_blocks +#define SDMMC_READ_BLOCKS sdmmc_read_blocks +#define SDMMC_WRITE_BLOCKS sdmmc_write_blocks /******************************************************************************* * sysmgr.boot_scratch_cold6 & 7 (64bit) are used to indicate L2 reset * is done and HPS should trigger warm reset via RMR_EL3. ******************************************************************************/ -#define L2_RESET_DONE_REG 0x10D12218 +#define L2_RESET_DONE_REG 0x10D12218 #endif /* PLAT_SOCFPGA_DEF_H */ diff --git a/plat/intel/soc/agilex5/platform.mk b/plat/intel/soc/agilex5/platform.mk index 546bc2e7..a831c393 100644 --- a/plat/intel/soc/agilex5/platform.mk +++ b/plat/intel/soc/agilex5/platform.mk @@ -1,6 +1,7 @@ # # Copyright (c) 2019-2020, ARM Limited and Contributors. All rights reserved. # Copyright (c) 2019-2023, Intel Corporation. All rights reserved. +# Copyright (c) 2024, Altera Corporation. All rights reserved. # # SPDX-License-Identifier: BSD-3-Clause # @@ -8,6 +9,7 @@ include lib/xlat_tables_v2/xlat_tables.mk PLAT_INCLUDES := \ -Iplat/intel/soc/agilex5/include/ \ -Iplat/intel/soc/common/drivers/ \ + -Iplat/intel/soc/common/lib/sha/ \ -Iplat/intel/soc/common/include/ # GIC-600 configuration @@ -33,6 +35,7 @@ PLAT_BL_COMMON_SOURCES := \ plat/intel/soc/common/drivers/sdmmc/sdmmc.c \ plat/intel/soc/common/drivers/ddr/ddr.c \ plat/intel/soc/common/drivers/nand/nand.c \ + plat/intel/soc/common/lib/sha/sha.c \ plat/intel/soc/common/socfpga_delay_timer.c BL2_SOURCES += \ @@ -55,13 +58,16 @@ BL2_SOURCES += \ lib/cpus/aarch64/cortex_a76.S \ plat/intel/soc/agilex5/soc/agilex5_clock_manager.c \ plat/intel/soc/agilex5/soc/agilex5_memory_controller.c \ - plat/intel/soc/agilex5/soc/agilex5_mmc.c \ + plat/intel/soc/agilex5/soc/agilex5_mmc.c \ plat/intel/soc/agilex5/soc/agilex5_pinmux.c \ plat/intel/soc/agilex5/soc/agilex5_power_manager.c \ + plat/intel/soc/agilex5/soc/agilex5_ddr.c \ + plat/intel/soc/agilex5/soc/agilex5_iossm_mailbox.c \ plat/intel/soc/common/bl2_plat_mem_params_desc.c \ plat/intel/soc/common/socfpga_image_load.c \ + plat/intel/soc/common/socfpga_ros.c \ plat/intel/soc/common/socfpga_storage.c \ - plat/intel/soc/common/socfpga_vab.c \ + plat/intel/soc/common/socfpga_vab.c \ plat/intel/soc/common/soc/socfpga_emac.c \ plat/intel/soc/common/soc/socfpga_firewall.c \ plat/intel/soc/common/soc/socfpga_handoff.c \ @@ -83,6 +89,8 @@ BL31_SOURCES += \ lib/cpus/aarch64/cortex_a76.S \ plat/common/plat_psci_common.c \ plat/intel/soc/agilex5/bl31_plat_setup.c \ + plat/intel/soc/agilex5/soc/agilex5_cache.S \ + plat/intel/soc/agilex5/soc/agilex5_clock_manager.c \ plat/intel/soc/agilex5/soc/agilex5_power_manager.c \ plat/intel/soc/common/socfpga_psci.c \ plat/intel/soc/common/socfpga_sip_svc.c \ @@ -91,6 +99,7 @@ BL31_SOURCES += \ plat/intel/soc/common/sip/socfpga_sip_ecc.c \ plat/intel/soc/common/sip/socfpga_sip_fcs.c \ plat/intel/soc/common/soc/socfpga_mailbox.c \ + plat/intel/soc/common/soc/socfpga_system_manager.c \ plat/intel/soc/common/soc/socfpga_reset_manager.c # Configs for A76 and A55 @@ -99,8 +108,35 @@ USE_COHERENT_MEM := 0 CTX_INCLUDE_AARCH32_REGS := 0 ERRATA_A55_1530923 := 1 +# Don't have the Linux kernel as a BL33 image by default +ARM_LINUX_KERNEL_AS_BL33 := 0 +$(eval $(call assert_boolean,ARM_LINUX_KERNEL_AS_BL33)) +$(eval $(call add_define,ARM_LINUX_KERNEL_AS_BL33)) $(eval $(call add_define,ARM_PRELOADED_DTB_BASE)) +# Configs for Boot Source +SOCFPGA_BOOT_SOURCE_SDMMC ?= 0 +SOCFPGA_BOOT_SOURCE_QSPI ?= 0 +SOCFPGA_BOOT_SOURCE_NAND ?= 0 + +$(eval $(call assert_booleans,\ + $(sort \ + SOCFPGA_BOOT_SOURCE_SDMMC \ + SOCFPGA_BOOT_SOURCE_QSPI \ + SOCFPGA_BOOT_SOURCE_NAND \ +))) +$(eval $(call add_defines,\ + $(sort \ + SOCFPGA_BOOT_SOURCE_SDMMC \ + SOCFPGA_BOOT_SOURCE_QSPI \ + SOCFPGA_BOOT_SOURCE_NAND \ +))) + +# Configs for VAB Authentication +SOCFPGA_SECURE_VAB_AUTH := 0 +$(eval $(call assert_boolean,SOCFPGA_SECURE_VAB_AUTH)) +$(eval $(call add_define,SOCFPGA_SECURE_VAB_AUTH)) + PROGRAMMABLE_RESET_ADDRESS := 0 RESET_TO_BL2 := 1 -BL2_INV_DCACHE := 0 +BL2_INV_DCACHE := 0 \ No newline at end of file diff --git a/plat/intel/soc/agilex5/soc/agilex5_cache.S b/plat/intel/soc/agilex5/soc/agilex5_cache.S new file mode 100644 index 00000000..52ed5d35 --- /dev/null +++ b/plat/intel/soc/agilex5/soc/agilex5_cache.S @@ -0,0 +1,114 @@ +/* + * Copyright (c) 2024, Altera Corporation. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ +#include <arch.h> +#include <asm_macros.S> +#include <cpu_macros.S> +#include <plat_macros.S> + + .globl invalidate_dcache_all + .globl invalidate_cache_low_el + /* -------------------------------------------------------- + * Invalidate for NS EL2 and EL1 + * -------------------------------------------------------- + */ +func invalidate_cache_low_el + mrs x0,SCR_EL3 + orr x1,x0,#SCR_NS_BIT + msr SCR_EL3, x1 + isb + tlbi ALLE2 + dsb sy + tlbi ALLE1 + dsb sy +endfunc invalidate_cache_low_el + +.pushsection .text.asm_dcache_level, "ax" +func asm_dcache_level + lsl x12, x0, #1 + msr csselr_el1, x12 /* select cache level */ + isb /* sync change of cssidr_el1 */ + mrs x6, ccsidr_el1 /* read the new cssidr_el1 */ + ubfx x2, x6, #0, #3 /* x2 <- log2(cache line size)-4 */ + ubfx x3, x6, #3, #10 /* x3 <- number of cache ways - 1 */ + ubfx x4, x6, #13, #15 /* x4 <- number of cache sets - 1 */ + add x2, x2, #4 /* x2 <- log2(cache line size) */ + clz w5, w3 /* bit position of #ways */ + /* x12 <- cache level << 1 */ + /* x2 <- line length offset */ + /* x3 <- number of cache ways - 1 */ + /* x4 <- number of cache sets - 1 */ + /* x5 <- bit position of #ways */ + +loop_set: + mov x6, x3 /* x6 <- working copy of #ways */ +loop_way: + lsl x7, x6, x5 + orr x9, x12, x7 /* map way and level to cisw value */ + lsl x7, x4, x2 + orr x9, x9, x7 /* map set number to cisw value */ + tbz w1, #0, 1f + dc isw, x9 + b 2f +1: dc cisw, x9 /* clean & invalidate by set/way */ +2: subs x6, x6, #1 /* decrement the way */ + b.ge loop_way + subs x4, x4, #1 /* decrement the set */ + b.ge loop_set + + ret +endfunc asm_dcache_level +.popsection + +/* + * void __asm_flush_dcache_all(int invalidate_only) + * + * x0: 0 clean & invalidate, 1 invalidate only + * + * flush or invalidate all data cache by SET/WAY. + */ +.pushsection .text.asm_dcache_all, "ax" +func asm_dcache_all + mov x1, x0 + dsb sy + mrs x10, clidr_el1 /* read clidr_el1 */ + ubfx x11, x10, #24, #3 /* x11 <- loc */ + cbz x11, finished /* if loc is 0, exit */ + mov x15, x30 + mov x0, #0 /* start flush at cache level 0 */ + /* x0 <- cache level */ + /* x10 <- clidr_el1 */ + /* x11 <- loc */ + /* x15 <- return address */ + +loop_level: + add x12, x0, x0, lsl #1 /* x12 <- tripled cache level */ + lsr x12, x10, x12 + and x12, x12, #7 /* x12 <- cache type */ + cmp x12, #2 + b.lt skip /* skip if no cache or icache */ + bl asm_dcache_level /* x1 = 0 flush, 1 invalidate */ +skip: + add x0, x0, #1 /* increment cache level */ + cmp x11, x0 + b.gt loop_level + + mov x0, #0 + msr csselr_el1, x0 /* restore csselr_el1 */ + dsb sy + isb + mov x30, x15 + +finished: + ret +endfunc asm_dcache_all +.popsection + +.pushsection .text.invalidate_dcache_all, "ax" +func invalidate_dcache_all + mov x0, #0x1 + b asm_dcache_all +endfunc invalidate_dcache_all +.popsection diff --git a/plat/intel/soc/agilex5/soc/agilex5_clock_manager.c b/plat/intel/soc/agilex5/soc/agilex5_clock_manager.c index cc681532..603aaf8f 100644 --- a/plat/intel/soc/agilex5/soc/agilex5_clock_manager.c +++ b/plat/intel/soc/agilex5/soc/agilex5_clock_manager.c @@ -1,5 +1,6 @@ /* * Copyright (c) 2019-2023, Intel Corporation. All rights reserved. + * Copyright (c) 2024, Altera Corporation. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -13,241 +14,643 @@ #include "agilex5_clock_manager.h" #include "agilex5_system_manager.h" -#include "socfpga_handoff.h" - -uint32_t wait_pll_lock(void) +#include "socfpga_system_manager.h" + +/* Main and Peripheral PLL configurations in Power Down(PD) state. */ +static const pll_cfg_t pll_cfg_set[] = { + { + /* Enable source synchronous mode */ + CLKMGR_PLLCFG_SRC_SYNC_MODE, + BIT(7), + BIT(7) + }, + { + /* Limit the PLL overshoot frequency during lock */ + CLKMGR_PLLCFG_OVRSHOOT_FREQ_LOCK, + BIT(0), + BIT(0) + }, + { + /* To give the PLL more time to settle before lock is asserted */ + CLKMGR_PLLCFG_LOCK_SETTLE_TIME, + BIT(0), + BIT(0) + }, + { + /* To set the PLL centering duty cycle for clock slice 0 */ + CLKMGR_PLLCFG_DUTYCYCLE_CLKSLICE0, + 0x4A, + GENMASK(6, 0) + }, + { + /* To set the PLL centering duty cycle for clock slice 1 */ + CLKMGR_PLLCFG_DUTYCYCLE_CLKSLICE1, + 0x4A, + GENMASK(6, 0) + }, +}; + +static int wait_pll_lock(uint32_t mask) { uint32_t data; uint32_t count = 0; + uint32_t retry = 0U; do { - data = mmio_read_32(CLKMGR_OFFSET + CLKMGR_STAT); - count++; - if (count >= 1000) + /* return time out */ + if (count >= CLKMGR_MAX_RETRY_COUNT) { + ERROR("CLKMGR: Timed out to satisfy the PLL mask\n"); return -ETIMEDOUT; + } + + data = mmio_read_32(CLKMGR(STAT)) & mask; + /* wait for stable lock, make sure it is stable for these counts */ + if (data == mask) { + retry++; + } else { + retry = 0U; + } + + /* we are good now, break */ + if (retry >= 5U) { + break; + } + + count++; + } while (1); - } while ((CLKMGR_STAT_MAINPLLLOCKED(data) == 0) || - (CLKMGR_STAT_PERPLLLOCKED(data) == 0)); return 0; } -uint32_t wait_fsm(void) +static int wait_fsm(void) { uint32_t data; uint32_t count = 0; do { - data = mmio_read_32(CLKMGR_OFFSET + CLKMGR_STAT); + data = mmio_read_32(CLKMGR(STAT)); count++; - if (count >= 1000) + if (count >= CLKMGR_MAX_RETRY_COUNT) { + ERROR("CLKMGR: Timed out on fsm state\n"); return -ETIMEDOUT; - + } } while (CLKMGR_STAT_BUSY(data) == CLKMGR_STAT_BUSY_E_BUSY); return 0; } -uint32_t pll_source_sync_config(uint32_t pll_mem_offset, uint32_t data) +static uint32_t calc_pll_vcocalibration(uint32_t pllm, uint32_t pllglob) { - uint32_t val = 0; - uint32_t count = 0; - uint32_t req_status = 0; + uint32_t mdiv, refclkdiv, drefclkdiv, mscnt, hscnt, vcocalib; + + mdiv = pllm & CLKMGR_PLLM_MDIV_MASK; + drefclkdiv = ((pllglob & CLKMGR_PLLGLOB_DREFCLKDIV_MASK) >> + CLKMGR_PLLGLOB_DREFCLKDIV_OFFSET); + refclkdiv = ((pllglob & CLKMGR_PLLGLOB_REFCLKDIV_MASK) >> + CLKMGR_PLLGLOB_REFCLKDIV_OFFSET); + mscnt = CLKMGR_VCOCALIB_MSCNT_CONST / (mdiv * BIT(drefclkdiv)); + if (mscnt == 0) { + mscnt = 1; + } - val = (CLKMGR_MEM_WR | CLKMGR_MEM_REQ | - (data << CLKMGR_MEM_WDAT_OFFSET) | CLKMGR_MEM_ADDR); - mmio_write_32(pll_mem_offset, val); + hscnt = (mdiv * mscnt * BIT(drefclkdiv) / refclkdiv) - + CLKMGR_VCOCALIB_HSCNT_CONST; - do { - req_status = mmio_read_32(pll_mem_offset); + vcocalib = (hscnt & CLKMGR_VCOCALIB_HSCNT_MASK) | + ((mscnt << CLKMGR_VCOCALIB_MSCNT_OFFSET) & + CLKMGR_VCOCALIB_MSCNT_MASK); + + return vcocalib; +} + +static int pll_source_sync_wait(uint32_t pll_type, int retry_count) +{ + int count = 0; + uint32_t req_status; + + if ((pll_type == CLKMGR_MAINPLL_TYPE) != 0) { + req_status = mmio_read_32(CLKMGR_MAINPLL(MEM)); + } else { + req_status = mmio_read_32(CLKMGR_PERPLL(MEM)); + } + + /* Check for error bit set */ + if ((req_status & CLKMGR_MEM_ERR) != 0) { + INFO("CLKMGR: %s: Memory Error Status Signal Assert\n", __func__); + } + + while ((count < retry_count) && (req_status & CLKMGR_MEM_REQ)) { + if (pll_type == CLKMGR_MAINPLL_TYPE) + req_status = mmio_read_32(CLKMGR_MAINPLL(MEM)); + else + req_status = mmio_read_32(CLKMGR_PERPLL(MEM)); count++; - } while ((req_status & CLKMGR_MEM_REQ) && (count < 10)); + } - if (count >= 10) + if (count >= retry_count) { + ERROR("CLKMGR: %s: timeout with pll_type %d\n", __func__, pll_type); return -ETIMEDOUT; + } return 0; } -uint32_t pll_source_sync_read(uint32_t pll_mem_offset) +static int pll_source_sync_config(uint32_t pll_type, uint32_t addr_offset, + uint32_t wdat, int retry_count) { - uint32_t val = 0; - uint32_t rdata = 0; - uint32_t count = 0; - uint32_t req_status = 0; + uint32_t addr; + uint32_t val; - val = (CLKMGR_MEM_REQ | CLKMGR_MEM_ADDR); - mmio_write_32(pll_mem_offset, val); + addr = ((addr_offset | CLKMGR_MEM_ADDR_START) & CLKMGR_MEM_ADDR_MASK); + val = (CLKMGR_MEM_REQ | CLKMGR_MEM_WR | + (wdat << CLKMGR_MEM_WDAT_OFFSET) | addr); - do { - req_status = mmio_read_32(pll_mem_offset); - count++; - } while ((req_status & CLKMGR_MEM_REQ) && (count < 10)); + if ((pll_type == CLKMGR_MAINPLL_TYPE) != 0) { + mmio_write_32(CLKMGR_MAINPLL(MEM), val); + } else { + mmio_write_32(CLKMGR_PERPLL(MEM), val); + } - if (count >= 10) + return pll_source_sync_wait(pll_type, retry_count); +} + +static int pll_source_sync_read(uint32_t pll_type, uint32_t addr_offset, + uint32_t *rdata, int retry_count) +{ + uint32_t addr; + uint32_t val; + + addr = ((addr_offset | CLKMGR_MEM_ADDR_START) & CLKMGR_MEM_ADDR_MASK); + val = ((CLKMGR_MEM_REQ & ~CLKMGR_MEM_WR) | addr); + + if ((pll_type == CLKMGR_MAINPLL_TYPE) != 0) { + mmio_write_32(CLKMGR_MAINPLL(MEM), val); + } else { + mmio_write_32(CLKMGR_PERPLL(MEM), val); + } + + *rdata = 0; + + if ((pll_source_sync_wait(pll_type, retry_count)) != 0) { return -ETIMEDOUT; + } - rdata = mmio_read_32(pll_mem_offset + 0x4); - INFO("rdata (%x) = %x\n", pll_mem_offset + 0x4, rdata); + if ((pll_type == CLKMGR_MAINPLL_TYPE) != 0) { + *rdata = mmio_read_32(CLKMGR_MAINPLL(MEMSTAT)); + } else { + *rdata = mmio_read_32(CLKMGR_PERPLL(MEMSTAT)); + } - return rdata; + return 0; } -void config_clkmgr_handoff(handoff *hoff_ptr) +static void config_pll_pd_state(uint32_t pll_type) { - /* Take both PLL out of reset and power up */ + uint32_t rdata; - mmio_setbits_32(CLKMGR_MAINPLL + CLKMGR_MAINPLL_PLLGLOB, - CLKMGR_PLLGLOB_PD_SET_MSK | - CLKMGR_PLLGLOB_RST_SET_MSK); - mmio_setbits_32(CLKMGR_PERPLL + CLKMGR_PERPLL_PLLGLOB, - CLKMGR_PLLGLOB_PD_SET_MSK | - CLKMGR_PLLGLOB_RST_SET_MSK); + for (uint32_t i = 0; i < ARRAY_SIZE(pll_cfg_set); i++) { + (void)pll_source_sync_read(pll_type, pll_cfg_set[i].addr, &rdata, + CLKMGR_MAX_RETRY_COUNT); - /* PLL lock */ - wait_pll_lock(); + (void)pll_source_sync_config(pll_type, pll_cfg_set[i].addr, + ((rdata & ~pll_cfg_set[i].mask) | pll_cfg_set[i].data), + CLKMGR_MAX_RETRY_COUNT); + } +} + +int config_clkmgr_handoff(handoff *hoff_ptr) +{ + int ret = 0; + uint32_t mainpll_vcocalib; + uint32_t perpll_vcocalib; + + /* Enter boot mode before any configuration */ + mmio_setbits_32(CLKMGR(CTRL), CLKMGR_CTRL_BOOTMODE); /* Bypass all mainpllgrp's clocks to input clock ref */ - mmio_write_32(CLKMGR_MAINPLL + CLKMGR_MAINPLL_BYPASSS, 0xff); + mmio_setbits_32(CLKMGR_MAINPLL(BYPASS), CLKMGR_MAINPLL_BYPASS_ALL); + ret = wait_fsm(); + if (ret != 0) + return ret; + /* Bypass all perpllgrp's clocks to input clock ref */ - mmio_write_32(CLKMGR_PERPLL + CLKMGR_PERPLL_BYPASS, 0xff); + mmio_setbits_32(CLKMGR_PERPLL(BYPASS), CLKMGR_PERPLL_BYPASS_ALL); + ret = wait_fsm(); + if (ret != 0) + return ret; + + /* Take both PLL out of reset and power down */ + mmio_clrbits_32(CLKMGR_MAINPLL(PLLGLOB), + CLKMGR_MAINPLL_PLLGLOB_PD_N | CLKMGR_MAINPLL_PLLGLOB_RST_N); + mmio_clrbits_32(CLKMGR_PERPLL(PLLGLOB), + CLKMGR_PERPLL_PLLGLOB_PD_N | CLKMGR_PERPLL_PLLGLOB_RST_N); + + /* Setup main PLL dividers */ + mainpll_vcocalib = calc_pll_vcocalibration(hoff_ptr->main_pll_pllm, + hoff_ptr->main_pll_pllglob); + mmio_write_32(CLKMGR_MAINPLL(PLLGLOB), + hoff_ptr->main_pll_pllglob & ~CLKMGR_MAINPLL_PLLGLOB_RST_N); + mmio_write_32(CLKMGR_MAINPLL(FDBCK), hoff_ptr->main_pll_fdbck); + mmio_write_32(CLKMGR_MAINPLL(VCOCALIB), mainpll_vcocalib); + mmio_write_32(CLKMGR_MAINPLL(PLLC0), hoff_ptr->main_pll_pllc0); + mmio_write_32(CLKMGR_MAINPLL(PLLC1), hoff_ptr->main_pll_pllc1); + mmio_write_32(CLKMGR_MAINPLL(PLLC2), hoff_ptr->main_pll_pllc2); + mmio_write_32(CLKMGR_MAINPLL(PLLC3), hoff_ptr->main_pll_pllc3); + mmio_write_32(CLKMGR_MAINPLL(PLLM), hoff_ptr->main_pll_pllm); + mmio_write_32(CLKMGR_MAINPLL(NOCCLK), hoff_ptr->main_pll_nocclk); + mmio_write_32(CLKMGR_MAINPLL(NOCDIV), hoff_ptr->main_pll_nocdiv); + + /* Setup peripheral PLL dividers */ + perpll_vcocalib = calc_pll_vcocalibration(hoff_ptr->per_pll_pllm, + hoff_ptr->per_pll_pllglob); + mmio_write_32(CLKMGR_PERPLL(PLLGLOB), + hoff_ptr->per_pll_pllglob & ~CLKMGR_PERPLL_PLLGLOB_RST_N); + mmio_write_32(CLKMGR_PERPLL(FDBCK), hoff_ptr->per_pll_fdbck); + mmio_write_32(CLKMGR_PERPLL(VCOCALIB), perpll_vcocalib); + mmio_write_32(CLKMGR_PERPLL(PLLC0), hoff_ptr->per_pll_pllc0); + mmio_write_32(CLKMGR_PERPLL(PLLC1), hoff_ptr->per_pll_pllc1); + mmio_write_32(CLKMGR_PERPLL(PLLC2), hoff_ptr->per_pll_pllc2); + mmio_write_32(CLKMGR_PERPLL(PLLC3), hoff_ptr->per_pll_pllc3); + mmio_write_32(CLKMGR_PERPLL(PLLM), hoff_ptr->per_pll_pllm); + mmio_write_32(CLKMGR_PERPLL(EMACCTL), hoff_ptr->per_pll_emacctl); + mmio_write_32(CLKMGR_PERPLL(GPIODIV), hoff_ptr->per_pll_gpiodiv); + + /* Configure ping pong counters */ + mmio_write_32(CLKMGR_ALTERA(EMACACTR), hoff_ptr->alt_emacactr); + mmio_write_32(CLKMGR_ALTERA(EMACBCTR), hoff_ptr->alt_emacbctr); + mmio_write_32(CLKMGR_ALTERA(EMACPTPCTR), hoff_ptr->alt_emacptpctr); + mmio_write_32(CLKMGR_ALTERA(GPIODBCTR), hoff_ptr->alt_gpiodbctr); + mmio_write_32(CLKMGR_ALTERA(S2FUSER0CTR), hoff_ptr->alt_s2fuser0ctr); + mmio_write_32(CLKMGR_ALTERA(S2FUSER1CTR), hoff_ptr->alt_s2fuser1ctr); + mmio_write_32(CLKMGR_ALTERA(PSIREFCTR), hoff_ptr->alt_psirefctr); + mmio_write_32(CLKMGR_ALTERA(USB31CTR), hoff_ptr->alt_usb31ctr); + mmio_write_32(CLKMGR_ALTERA(DSUCTR), hoff_ptr->alt_dsuctr); + mmio_write_32(CLKMGR_ALTERA(CORE01CTR), hoff_ptr->alt_core01ctr); + mmio_write_32(CLKMGR_ALTERA(CORE23CTR), hoff_ptr->alt_core23ctr); + mmio_write_32(CLKMGR_ALTERA(CORE2CTR), hoff_ptr->alt_core2ctr); + mmio_write_32(CLKMGR_ALTERA(CORE3CTR), hoff_ptr->alt_core3ctr); - /* Pass clock source frequency into scratch register */ - mmio_write_32(SOCFPGA_SYSMGR(BOOT_SCRATCH_COLD_1), - hoff_ptr->hps_osc_clk_hz); - mmio_write_32(SOCFPGA_SYSMGR(BOOT_SCRATCH_COLD_2), - hoff_ptr->fpga_clk_hz); + /* Take both PLL out of reset and power up */ + mmio_setbits_32(CLKMGR_MAINPLL(PLLGLOB), + CLKMGR_MAINPLL_PLLGLOB_PD_N | CLKMGR_MAINPLL_PLLGLOB_RST_N); + mmio_setbits_32(CLKMGR_PERPLL(PLLGLOB), + CLKMGR_PERPLL_PLLGLOB_PD_N | CLKMGR_PERPLL_PLLGLOB_RST_N); + + /* Main PLL configuration in Powed down state */ + config_pll_pd_state(CLKMGR_MAINPLL_TYPE); + + /* Peripheral PLL configuration in Powed down state */ + config_pll_pd_state(CLKMGR_PERPLL_TYPE); + + /* Enable main PLL clkslices */ + mmio_setbits_32(CLKMGR_MAINPLL(PLLC0), CLKMGR_MAINPLL_PLLCX_EN); + mmio_setbits_32(CLKMGR_MAINPLL(PLLC1), CLKMGR_MAINPLL_PLLCX_EN); + mmio_setbits_32(CLKMGR_MAINPLL(PLLC2), CLKMGR_MAINPLL_PLLCX_EN); + mmio_setbits_32(CLKMGR_MAINPLL(PLLC3), CLKMGR_MAINPLL_PLLCX_EN); + + /* Enable periheral PLL clkslices */ + mmio_setbits_32(CLKMGR_PERPLL(PLLC0), CLKMGR_PERPLL_PLLCX_EN); + mmio_setbits_32(CLKMGR_PERPLL(PLLC1), CLKMGR_PERPLL_PLLCX_EN); + mmio_setbits_32(CLKMGR_PERPLL(PLLC2), CLKMGR_PERPLL_PLLCX_EN); + mmio_setbits_32(CLKMGR_PERPLL(PLLC3), CLKMGR_PERPLL_PLLCX_EN); + + /* Wait for main and peri PLL lock state */ + ret = wait_pll_lock(CLKMGR_STAT_ALLPLLLOCKED); + if (ret != 0) { + return ret; + } + + /* Main PLL and per PLL lost lock */ + mmio_setbits_32(CLKMGR_MAINPLL(LOSTLOCK), CLKMGR_XPLL_LOSTLOCK_BYPASSCLEAR); + mmio_setbits_32(CLKMGR_PERPLL(LOSTLOCK), CLKMGR_XPLL_LOSTLOCK_BYPASSCLEAR); + + /* Main PLL and per PLL clear lostlock bypass */ + mmio_setbits_32(CLKMGR_MAINPLL(PLLGLOB), CLKMGR_XPLLGLOB_CLR_LOSTLOCK_BYPASS); + mmio_setbits_32(CLKMGR_PERPLL(PLLGLOB), CLKMGR_XPLLGLOB_CLR_LOSTLOCK_BYPASS); + + /* Pass clock source frequency into boot scratch register */ + mmio_write_32(SOCFPGA_SYSMGR(BOOT_SCRATCH_COLD_1), hoff_ptr->hps_osc_clk_hz); + mmio_write_32(SOCFPGA_SYSMGR(BOOT_SCRATCH_COLD_2), hoff_ptr->fpga_clk_hz); /* Take all PLLs out of bypass */ - mmio_write_32(CLKMGR_MAINPLL + CLKMGR_MAINPLL_BYPASS, 0); - wait_fsm(); - mmio_write_32(CLKMGR_PERPLL + CLKMGR_PERPLL_BYPASS, 0); - wait_fsm(); + mmio_clrbits_32(CLKMGR_MAINPLL(BYPASS), CLKMGR_MAINPLL_BYPASS_ALL); + ret = wait_fsm(); + if (ret != 0) { + return ret; + } + + mmio_clrbits_32(CLKMGR_PERPLL(BYPASS), CLKMGR_PERPLL_BYPASS_ALL); + ret = wait_fsm(); + if (ret != 0) { + return ret; + } - /* Enable mainpllgrp's software-managed clock */ - mmio_write_32(CLKMGR_MAINPLL + CLKMGR_MAINPLL_EN, - CLKMGR_MAINPLL_EN_RESET); - mmio_write_32(CLKMGR_PERPLL + CLKMGR_PERPLL_EN, - CLKMGR_PERPLL_EN_RESET); + /* Clear the loss of lock bits (write 1 to clear) */ + mmio_write_32(CLKMGR(INTRCLR), + CLKMGR_INTRCLR_MAINLOCKLOST | CLKMGR_INTRCLR_PERLOCKLOST); + + /* Take all ping pong counters out of reset */ + mmio_clrbits_32(CLKMGR_ALTERA(EXTCNTRST), CLKMGR_ALTERA_EXTCNTRST_ALLCNTRST); + + /* Exit boot mode */ + mmio_clrbits_32(CLKMGR(CTRL), CLKMGR_CTRL_BOOTMODE); + + return 0; } /* Extract reference clock from platform clock source */ -uint32_t get_ref_clk(uint32_t pllglob) +uint32_t get_ref_clk(uint32_t pllglob_reg, uint32_t pllm_reg) { - uint32_t arefclkdiv, ref_clk; - uint32_t scr_reg; + uint32_t arefclkdiv, ref_clk, mdiv; + uint32_t pllglob_val, pllm_val; + + /* Read pllglob and pllm registers */ + pllglob_val = mmio_read_32(pllglob_reg); + pllm_val = mmio_read_32(pllm_reg); - switch (CLKMGR_PSRC(pllglob)) { + switch (CLKMGR_PLLGLOB_PSRC(pllglob_val)) { case CLKMGR_PLLGLOB_PSRC_EOSC1: - scr_reg = SOCFPGA_SYSMGR(BOOT_SCRATCH_COLD_1); - ref_clk = mmio_read_32(scr_reg); + ref_clk = mmio_read_32(SOCFPGA_SYSMGR(BOOT_SCRATCH_COLD_1)); break; + case CLKMGR_PLLGLOB_PSRC_INTOSC: ref_clk = CLKMGR_INTOSC_HZ; break; + case CLKMGR_PLLGLOB_PSRC_F2S: - scr_reg = SOCFPGA_SYSMGR(BOOT_SCRATCH_COLD_2); - ref_clk = mmio_read_32(scr_reg); + ref_clk = mmio_read_32(SOCFPGA_SYSMGR(BOOT_SCRATCH_COLD_2)); break; + default: ref_clk = 0; assert(0); break; } - arefclkdiv = CLKMGR_PLLGLOB_AREFCLKDIV(pllglob); + /* Get reference clock divider */ + arefclkdiv = CLKMGR_PLLGLOB_AREFCLKDIV(pllglob_val); ref_clk /= arefclkdiv; + /* Feedback clock divider */ + mdiv = CLKMGR_PLLM_MDIV(pllm_val); + ref_clk *= mdiv; + + VERBOSE("CLKMGR: %s: ref_clk %u\n", __func__, ref_clk); return ref_clk; } /* Calculate clock frequency based on parameter */ -uint32_t get_clk_freq(uint32_t psrc_reg, uint32_t main_pllc, uint32_t per_pllc) +uint32_t get_clk_freq(uint32_t psrc_reg, uint32_t mainpllc_reg, + uint32_t perpllc_reg) { - uint32_t ref_clk = 0; + uint32_t clock = 0; + uint32_t clk_psrc; + + /* + * Select source for the active 5:1 clock selection when the PLL + * is not bypassed + */ + clk_psrc = mmio_read_32(psrc_reg); + switch (GET_CLKMGR_CLKSRC(clk_psrc)) { + case CLKMGR_CLKSRC_MAIN: + clock = get_ref_clk(CLKMGR_MAINPLL(PLLGLOB), CLKMGR_MAINPLL(PLLM)); + clock /= (mmio_read_32(mainpllc_reg) & CLKMGR_PLLCX_DIV_MSK); + break; + + case CLKMGR_CLKSRC_PER: + clock = get_ref_clk(CLKMGR_PERPLL(PLLGLOB), CLKMGR_PERPLL(PLLM)); + clock /= (mmio_read_32(perpllc_reg) & CLKMGR_PLLCX_DIV_MSK); + break; - uint32_t clk_psrc, mdiv; - uint32_t pllm_reg, pllc_reg, pllc_div, pllglob_reg; + case CLKMGR_CLKSRC_OSC1: + clock = mmio_read_32(SOCFPGA_SYSMGR(BOOT_SCRATCH_COLD_1)); + break; + case CLKMGR_CLKSRC_INTOSC: + clock = CLKMGR_INTOSC_HZ; + break; - clk_psrc = mmio_read_32(CLKMGR_MAINPLL + psrc_reg); - clk_psrc = 0; + case CLKMGR_CLKSRC_FPGA: + clock = mmio_read_32(SOCFPGA_SYSMGR(BOOT_SCRATCH_COLD_2)); + break; - switch (clk_psrc) { - case 0: - pllm_reg = CLKMGR_MAINPLL + CLKMGR_MAINPLL_PLLM; - pllc_reg = CLKMGR_MAINPLL + main_pllc; - pllglob_reg = CLKMGR_MAINPLL + CLKMGR_MAINPLL_PLLGLOB; + default: + ERROR("CLKMGR: Invalid clock source select\n"); + assert(0); break; } - ref_clk = get_ref_clk(mmio_read_32(pllglob_reg)); - mdiv = CLKMGR_PLLM_MDIV(mmio_read_32(pllm_reg)); - ref_clk *= mdiv; + VERBOSE("CLKMGR: %s: clock type %lu and its value %u\n", + __func__, GET_CLKMGR_CLKSRC(clk_psrc), clock); - pllc_div = mmio_read_32(pllc_reg) & 0x7ff; - NOTICE("return = %d Hz\n", (ref_clk / pllc_div)); + return clock; +} - ref_clk = 200000000; - return (uint32_t) ref_clk; +/* Get L3 free clock */ +static uint32_t get_l3_main_free_clk(void) +{ + return get_clk_freq(CLKMGR_MAINPLL(NOCCLK), + CLKMGR_MAINPLL(PLLC3), + CLKMGR_PERPLL(PLLC1)); +} +/* Get L4 main clock */ +static uint32_t get_l4_main_clk(void) +{ + return get_l3_main_free_clk(); } -/* Return L3 interconnect clock */ -uint32_t get_l3_clk(void) +/* Get L4 mp clock */ +static uint32_t get_l4_mp_clk(void) { - uint32_t l3_clk; + uint32_t l3_main_free_clk = get_l3_main_free_clk(); + uint32_t mainpll_nocdiv_l4mp = BIT(GET_CLKMGR_MAINPLL_NOCDIV_L4MP( + mmio_read_32(CLKMGR_MAINPLL(NOCDIV)))); + + uint32_t l4_mp_clk = (l3_main_free_clk / mainpll_nocdiv_l4mp); - l3_clk = get_clk_freq(CLKMGR_MAINPLL_NOCCLK, CLKMGR_MAINPLL_PLLC1, - CLKMGR_PERPLL_PLLC1); - return l3_clk; + return l4_mp_clk; } -/* Calculate clock frequency to be used for watchdog timer */ -uint32_t get_wdt_clk(void) +/* Get L4 sp clock */ +static uint32_t get_l4_sp_clk(void) { - uint32_t l3_clk, l4_sys_clk; + uint32_t l3_main_free_clk = get_l3_main_free_clk(); + uint32_t mainpll_nocdiv_l4sp = BIT(GET_CLKMGR_MAINPLL_NOCDIV_L4SP( + mmio_read_32(CLKMGR_MAINPLL(NOCDIV)))); - l3_clk = get_l3_clk(); - l4_sys_clk = l3_clk / 4; + uint32_t l4_sp_clk = (l3_main_free_clk / mainpll_nocdiv_l4sp); - return l4_sys_clk; + return l4_sp_clk; } -/* Calculate clock frequency to be used for UART driver */ -uint32_t get_uart_clk(void) +/* Calculate clock frequency to be used for SDMMC driver */ +uint32_t get_sdmmc_clk(void) +{ + uint32_t l4_mp_clk = get_l4_mp_clk(); + uint32_t mainpll_nocdiv = mmio_read_32(CLKMGR_MAINPLL(NOCDIV)); + uint32_t sdmmc_clk = l4_mp_clk / BIT(GET_CLKMGR_MAINPLL_NOCDIV_SPHY(mainpll_nocdiv)); + + return sdmmc_clk; +} + +/* Get clock for ethernet mac0 */ +static uint32_t get_emaca_clk(void) { - uint32_t data32, l3_clk, l4_sp_clk; + uint32_t emaca_ctr = mmio_read_32(CLKMGR_ALTERA(EMACACTR)); + uint32_t perpll_emacctl = mmio_read_32(CLKMGR_PERPLL(EMACCTL)); + uint32_t perpll_emac_src = GET_CLKMGR_PERPLL_EMAC0_CLK_SRC(perpll_emacctl); + uint32_t emac_ctr_reg; + uint32_t emac_clock; + + if (perpll_emac_src != 0) { + emac_ctr_reg = CLKMGR_ALTERA(EMACBCTR); + } else { + emac_ctr_reg = CLKMGR_ALTERA(EMACACTR); + } - l3_clk = get_l3_clk(); + /* Get EMACA clock source */ + uint32_t emacactr_src = GET_CLKMGR_EMACACTR_CLK_SRC(emaca_ctr); - data32 = mmio_read_32(CLKMGR_MAINPLL + CLKMGR_MAINPLL_NOCDIV); - data32 = (data32 >> 16) & 0x3; + /* Read the desired EMAC register again */ + emaca_ctr = mmio_read_32(emac_ctr_reg); - l4_sp_clk = l3_clk >> data32; + /* Get the divider now */ + uint32_t emaca_ctr_div = emaca_ctr & GENMASK(10, 0); - return l4_sp_clk; + switch (emacactr_src) { + case CLKMGR_CLKSRC_MAIN: + emac_clock = get_ref_clk(CLKMGR_MAINPLL(PLLGLOB), CLKMGR_MAINPLL(PLLM)); + emac_clock /= (mmio_read_32(CLKMGR_MAINPLL(PLLC1)) & GENMASK(10, 0)); + break; + + case CLKMGR_CLKSRC_PER: + emac_clock = get_ref_clk(CLKMGR_PERPLL(PLLGLOB), CLKMGR_PERPLL(PLLM)); + emac_clock /= (mmio_read_32(CLKMGR_PERPLL(PLLC3)) & GENMASK(10, 0)); + break; + + default: + ERROR("CLKMGR: %s invalid clock source\n", __func__); + emac_clock = 0; + return emac_clock; + } + + emac_clock /= 1 + emaca_ctr_div; + + return emac_clock; } -/* Calculate clock frequency to be used for SDMMC driver */ -uint32_t get_mmc_clk(void) +/* Get MPU clock */ +static uint32_t get_mpu_clk(void) +{ + uint32_t cpu_id = MPIDR_AFFLVL1_VAL(read_mpidr()); + uint32_t ctr_reg = 0U; + uint32_t clock; + + if (cpu_id > CLKMGR_ALTERA_CORE1) { + clock = get_clk_freq(CLKMGR_ALTERA(CORE23CTR), + CLKMGR_MAINPLL(PLLC0), + CLKMGR_PERPLL(PLLC0)); + } else { + clock = get_clk_freq(CLKMGR_ALTERA(CORE01CTR), + CLKMGR_MAINPLL(PLLC1), + CLKMGR_PERPLL(PLLC0)); + } + + switch (cpu_id) { + case CLKMGR_ALTERA_CORE0: + case CLKMGR_ALTERA_CORE1: + ctr_reg = CLKMGR_ALTERA(CORE01CTR); + break; + + case CLKMGR_ALTERA_CORE2: + ctr_reg = CLKMGR_ALTERA(CORE2CTR); + break; + + case CLKMGR_ALTERA_CORE3: + ctr_reg = CLKMGR_ALTERA(CORE3CTR); + break; + + default: + break; + } + + /* Division setting for ping pong counter in clock slice */ + clock /= 1 + (mmio_read_32(ctr_reg) & CLKMGR_PLLCX_DIV_MSK); + + return clock; +} + +/* Calculate clock frequency to be used for watchdog timer */ +static uint32_t get_wdt_clk(void) +{ + uint32_t l3_main_free_clk = get_l3_main_free_clk(); + uint32_t mainpll_nocdiv_l4sysfreeclk = BIT(GET_CLKMGR_MAINPLL_NOCDIV_L4SYSFREE( + mmio_read_32(CLKMGR_MAINPLL(NOCDIV)))); + uint32_t l4_sys_free_clk = (l3_main_free_clk / mainpll_nocdiv_l4sysfreeclk); + + return l4_sys_free_clk; +} + +/* + * Calculate clock frequency to be used for UART driver. + * 'l4_sp_clk' (100MHz) will be used for slow peripherals like UART, I2C + * and Timers. + */ +static uint32_t get_uart_clk(void) +{ + return get_l4_sp_clk(); +} + +/* Return the clock value of a given system component */ +uint32_t clkmgr_get_rate(uint32_t clk_id) { - uint32_t mmc_clk; + uint32_t clk_rate; - //TODO: To update when handoff data is ready - //uint32_t data32; + switch (clk_id) { + case CLKMGR_MPU_CLK_ID: + clk_rate = get_mpu_clk(); + break; - //mmc_clk = get_clk_freq(CLKMGR_ALTERA_SDMMCCTR, CLKMGR_MAINPLL_PLLC3, CLKMGR_PERPLL_PLLC3); + case CLKMGR_L4_MAIN_CLK_ID: + clk_rate = get_l4_main_clk(); + break; - //data32 = mmio_read_32(CLKMGR_ALTERA + CLKMGR_ALTERA_SDMMCCTR); - //data32 = (data32 & 0x7ff) + 1; - //mmc_clk = (mmc_clk / data32) / 4; + case CLKMGR_L4_MP_CLK_ID: + clk_rate = get_l4_mp_clk(); + break; + case CLKMGR_L4_SP_CLK_ID: + clk_rate = get_l4_sp_clk(); + break; - mmc_clk = get_clk_freq(CLKMGR_MAINPLL_NOCCLK, CLKMGR_MAINPLL_PLLC3, - CLKMGR_PERPLL_PLLC3); + case CLKMGR_EMAC0_CLK_ID: + clk_rate = get_emaca_clk(); + break; - // TODO: To update when handoff data is ready - NOTICE("mmc_clk = %d Hz\n", mmc_clk); + case CLKMGR_SDMMC_CLK_ID: + clk_rate = get_sdmmc_clk(); + break; + + case CLKMGR_UART_CLK_ID: + clk_rate = get_uart_clk(); + break; - return mmc_clk; + case CLKMGR_WDT_CLK_ID: + clk_rate = get_wdt_clk(); + break; + + default: + ERROR("CLKMGR: %s: Invalid clock ID\n", __func__); + clk_rate = 0; + break; + } + + return clk_rate; +} + +/* Return mpu_periph_clk tick */ +unsigned int plat_get_syscnt_freq2(void) +{ + return PLAT_SYS_COUNTER_FREQ_IN_TICKS; } diff --git a/plat/intel/soc/agilex5/soc/agilex5_ddr.c b/plat/intel/soc/agilex5/soc/agilex5_ddr.c new file mode 100644 index 00000000..ef2ae57f --- /dev/null +++ b/plat/intel/soc/agilex5/soc/agilex5_ddr.c @@ -0,0 +1,434 @@ +/* + * Copyright (c) 2024, Altera Corporation. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <stddef.h> +#include <stdlib.h> +#include <common/debug.h> +#include <drivers/delay_timer.h> +#include "lib/mmio.h" + +#include "agilex5_ddr.h" +#include "agilex5_iossm_mailbox.h" + +/* + * TODO: We need to leverage the legacy products DDR drivers and consider + * the upcoming products like KM and then come up with common source code/driver + * architecture to address all the products in one view. + */ + +#define SYSMGR_BS_COLD3_DDR_RESET_TYPE_MASK GENMASK(31, 29) +#define SYSMGR_BS_COLD3_DDR_RESET_TYPE_SHIFT 29 +#define SYSMGR_BS_COLD3_DDR_DBE_MASK (1 << 1) +#define SYSMGR_BS_COLD3_OCRAM_DBE_MASK (1) +#define SYSMGR_BS_POR0_DDR_PROGRESS_MASK (1) + +/* MPFE NOC registers */ +#define F2SDRAM_SIDEBAND_FLAGOUTSET0 0x50 +#define F2SDRAM_SIDEBAND_FLAGOUTCLR0 0x54 +#define F2SDRAM_SIDEBAND_FLAGOUTSTATUS0 0x58 + +#define SOCFPGA_F2SDRAM_MGR_ADDRESS 0x18001000 +#define SOCFPGA_MPFE_SCR_IO96B0 0x18000D00 +#define SOCFPGA_MPFE_SCR_IO96B1 0x18000D04 +#define SOCFPGA_MPFE_NOC_SCHED_CSR 0x18000D08 + +#define SIDEBANDMGR_FLAGOUTSET0_REG (SOCFPGA_F2SDRAM_MGR_ADDRESS \ + + F2SDRAM_SIDEBAND_FLAGOUTSET0) +#define SIDEBANDMGR_FLAGOUTSTATUS0_REG (SOCFPGA_F2SDRAM_MGR_ADDRESS \ + +F2SDRAM_SIDEBAND_FLAGOUTSTATUS0) +#define SIDEBANDMGR_FLAGOUTCLR0_REG (SOCFPGA_F2SDRAM_MGR_ADDRESS \ + + F2SDRAM_SIDEBAND_FLAGOUTCLR0) +#define SZ_8 0x00000008 + + +/* Firewall MPU DDR SCR registers */ +#define FW_MPU_DDR_SCR_EN 0x00 +#define FW_MPU_DDR_SCR_EN_SET 0x04 +#define FW_MPU_DDR_SCR_MPUREGION0ADDR_BASE 0x10 +#define FW_MPU_DDR_SCR_MPUREGION0ADDR_BASEEXT 0x14 +#define FW_MPU_DDR_SCR_MPUREGION0ADDR_LIMIT 0x18 +#define FW_MPU_DDR_SCR_MPUREGION0ADDR_LIMITEXT 0x1c + +#define SOCFPGA_FW_DDR_CCU_DMI0_ADDRESS 0x18000800 +#define SOCFPGA_FW_DDR_CCU_DMI1_ADDRESS 0x18000A00 +#define SOCFPGA_FW_TBU2NOC_ADDRESS 0x18000C00 + +#define FW_MPU_DDR_SCR_NONMPUREGION0ADDR_BASE 0x90 +#define FW_MPU_DDR_SCR_NONMPUREGION0ADDR_BASEEXT 0x94 +#define FW_MPU_DDR_SCR_NONMPUREGION0ADDR_LIMIT 0x98 +#define FW_MPU_DDR_SCR_NONMPUREGION0ADDR_LIMITEXT 0x9c +#define FW_MPU_DDR_SCR_NONMPUREGION0ADDR_LIMITEXT_FIELD 0xff + +/* Firewall F2SDRAM DDR SCR registers */ +#define FW_F2SDRAM_DDR_SCR_EN 0x00 +#define FW_F2SDRAM_DDR_SCR_EN_SET 0x04 +#define FW_F2SDRAM_DDR_SCR_REGION0ADDR_BASE 0x10 +#define FW_F2SDRAM_DDR_SCR_REGION0ADDR_BASEEXT 0x14 +#define FW_F2SDRAM_DDR_SCR_REGION0ADDR_LIMIT 0x18 +#define FW_F2SDRAM_DDR_SCR_REGION0ADDR_LIMITEXT 0x1c + +#define FW_MPU_DDR_SCR_WRITEL(data, reg) \ + do { \ + mmio_write_32(SOCFPGA_FW_DDR_CCU_DMI0_ADDRESS + (reg), data); \ + mmio_write_32(SOCFPGA_FW_DDR_CCU_DMI1_ADDRESS + (reg), data); \ + } while (0) + +#define FW_F2SDRAM_DDR_SCR_WRITEL(data, reg) \ + mmio_write_32(SOCFPGA_FW_TBU2NOC_ADDRESS + (reg), data) + +/* DDR banks info set */ +static struct ddr_info ddr_info_set[CONFIG_NR_DRAM_BANKS]; + +/* Reset type */ +enum reset_type { + POR_RESET, + WARM_RESET, + COLD_RESET, + NCONFIG, + JTAG_CONFIG, + RSU_RECONFIG +}; + +/* Get reset type by reading boot scratch register cold3 */ +static inline enum reset_type get_reset_type(uint32_t sys_reg) +{ + return ((sys_reg & SYSMGR_BS_COLD3_DDR_RESET_TYPE_MASK) >> + SYSMGR_BS_COLD3_DDR_RESET_TYPE_SHIFT); +} + +/* DDR hang check before the reset */ +static inline bool is_ddr_init_hang(void) +{ + uint32_t sys_reg = mmio_read_32(SOCFPGA_SYSMGR(BOOT_SCRATCH_POR_0)); + + if ((sys_reg & SYSMGR_BS_POR0_DDR_PROGRESS_MASK) != 0) { + INFO("DDR: Hang before this reset\n"); + return true; + } + + return false; +} + +/* Set the DDR init progress bit */ +static inline void ddr_init_inprogress(bool start) +{ + if (start) { + mmio_setbits_32(SOCFPGA_SYSMGR(BOOT_SCRATCH_POR_0), + SYSMGR_BS_POR0_DDR_PROGRESS_MASK); + } else { + mmio_clrbits_32(SOCFPGA_SYSMGR(BOOT_SCRATCH_POR_0), + SYSMGR_BS_POR0_DDR_PROGRESS_MASK); + } +} + +/* Configure the IO96B CSRs address based on the handoff data */ +static void config_io96b_csr_addr(bool is_dualemif, struct io96b_info *io96b_ctrl) +{ + if (is_dualemif) + io96b_ctrl->num_instance = 2; + else + io96b_ctrl->num_instance = 1; + + /* Assign IO96B CSR base address if it is valid */ + for (int i = 0; i < io96b_ctrl->num_instance; i++) { + switch (i) { + case 0: + io96b_ctrl->io96b_0.io96b_csr_addr = 0x18400000; + INFO("DDR: IO96B0 0x%llx CSR enabled\n", + io96b_ctrl->io96b_0.io96b_csr_addr); + break; + + case 1: + io96b_ctrl->io96b_1.io96b_csr_addr = 0x18800000; + INFO("DDR: IO96B1 0x%llx CSR enabled\n", + io96b_ctrl->io96b_1.io96b_csr_addr); + break; + + default: + ERROR("%s: Invalid IO96B CSR\n", __func__); + } /* switch */ + } /* for */ +} + +static inline bool hps_ocram_dbe_status(void) +{ + uint32_t sys_reg = mmio_read_32(SOCFPGA_SYSMGR(BOOT_SCRATCH_COLD_3)); + + if ((sys_reg & SYSMGR_BS_COLD3_OCRAM_DBE_MASK) != 0) + return true; + + return false; +} + +static inline bool ddr_ecc_dbe_status(void) +{ + uint32_t sys_reg = mmio_read_32(SOCFPGA_SYSMGR(BOOT_SCRATCH_COLD_3)); + + if ((sys_reg & SYSMGR_BS_COLD3_DDR_DBE_MASK) != 0) + return true; + + return false; +} + +static void sdram_set_firewall_non_f2sdram(void) +{ + uint32_t i; + phys_size_t value; + uint32_t lower, upper; + + for (i = 0; i < CONFIG_NR_DRAM_BANKS; i++) { + if (ddr_info_set[i].size == 0) { + continue; + } + + value = ddr_info_set[i].start; + + /* + * Keep first 1MB of SDRAM memory region as secure region when + * using ATF flow, where the ATF code is located. + */ + value += SZ_1M; + + /* Setting non-secure MPU region base and base extended */ + lower = LO(value); + upper = HI(value); + + FW_MPU_DDR_SCR_WRITEL(lower, + FW_MPU_DDR_SCR_MPUREGION0ADDR_BASE + + (i * 4 * sizeof(uint32_t))); + FW_MPU_DDR_SCR_WRITEL(upper & 0xff, + FW_MPU_DDR_SCR_MPUREGION0ADDR_BASEEXT + + (i * 4 * sizeof(uint32_t))); + + /* Setting non-secure Non-MPU region base and base extended */ + FW_MPU_DDR_SCR_WRITEL(lower, + FW_MPU_DDR_SCR_NONMPUREGION0ADDR_BASE + + (i * 4 * sizeof(uint32_t))); + FW_MPU_DDR_SCR_WRITEL(upper & 0xff, + FW_MPU_DDR_SCR_NONMPUREGION0ADDR_BASEEXT + + (i * 4 * sizeof(uint32_t))); + + /* Setting non-secure MPU limit and limit extended */ + value = ddr_info_set[i].start + ddr_info_set[i].size - 1; + + lower = LO(value); + upper = HI(value); + + FW_MPU_DDR_SCR_WRITEL(lower, + FW_MPU_DDR_SCR_MPUREGION0ADDR_LIMIT + + (i * 4 * sizeof(uint32_t))); + FW_MPU_DDR_SCR_WRITEL(upper & 0xff, + FW_MPU_DDR_SCR_MPUREGION0ADDR_LIMITEXT + + (i * 4 * sizeof(uint32_t))); + + /* Setting non-secure Non-MPU limit and limit extended */ + FW_MPU_DDR_SCR_WRITEL(lower, + FW_MPU_DDR_SCR_NONMPUREGION0ADDR_LIMIT + + (i * 4 * sizeof(uint32_t))); + FW_MPU_DDR_SCR_WRITEL(upper & 0xff, + FW_MPU_DDR_SCR_NONMPUREGION0ADDR_LIMITEXT + + (i * 4 * sizeof(uint32_t))); + + FW_MPU_DDR_SCR_WRITEL(BIT(i) | BIT(i + 8), + FW_MPU_DDR_SCR_EN_SET); + } +} + +static void sdram_set_firewall_f2sdram(void) +{ + uint32_t i; + phys_size_t value; + uint32_t lower, upper; + + for (i = 0; i < CONFIG_NR_DRAM_BANKS; i++) { + if (ddr_info_set[i].size == 0) { + continue; + } + + value = ddr_info_set[i].start; + + /* Keep first 1MB of SDRAM memory region as secure region when + * using ATF flow, where the ATF code is located. + */ + value += SZ_1M; + + /* Setting base and base extended */ + lower = LO(value); + upper = HI(value); + FW_F2SDRAM_DDR_SCR_WRITEL(lower, + FW_F2SDRAM_DDR_SCR_REGION0ADDR_BASE + + (i * 4 * sizeof(uint32_t))); + FW_F2SDRAM_DDR_SCR_WRITEL(upper & 0xff, + FW_F2SDRAM_DDR_SCR_REGION0ADDR_BASEEXT + + (i * 4 * sizeof(uint32_t))); + + /* Setting limit and limit extended */ + value = ddr_info_set[i].start + ddr_info_set[i].size - 1; + + lower = LO(value); + upper = HI(value); + + FW_F2SDRAM_DDR_SCR_WRITEL(lower, + FW_F2SDRAM_DDR_SCR_REGION0ADDR_LIMIT + + (i * 4 * sizeof(uint32_t))); + FW_F2SDRAM_DDR_SCR_WRITEL(upper & 0xff, + FW_F2SDRAM_DDR_SCR_REGION0ADDR_LIMITEXT + + (i * 4 * sizeof(uint32_t))); + + FW_F2SDRAM_DDR_SCR_WRITEL(BIT(i), FW_F2SDRAM_DDR_SCR_EN_SET); + } +} + +static void sdram_set_firewall(void) +{ + sdram_set_firewall_non_f2sdram(); + sdram_set_firewall_f2sdram(); +} + +/* + * Agilex5 DDR/IOSSM controller initialization routine + */ +int agilex5_ddr_init(handoff *hoff_ptr) +{ + int ret; + bool full_mem_init = false; + phys_size_t hw_ddr_size; + phys_size_t config_ddr_size; + struct io96b_info io96b_ctrl; + enum reset_type reset_t = get_reset_type(mmio_read_32(SOCFPGA_SYSMGR( + BOOT_SCRATCH_COLD_3))); + bool is_dualport = hoff_ptr->ddr_config & BIT(0); + bool is_dualemif = hoff_ptr->ddr_config & BIT(1); + + NOTICE("DDR: Reset type is '%s'\n", + (reset_t == POR_RESET ? "Power-On" : (reset_t == COLD_RESET ? "Cold" : "Warm"))); + + /* DDR initialization progress status tracking */ + bool is_ddr_hang_bfr_rst = is_ddr_init_hang(); + + /* Set the DDR initialization progress */ + ddr_init_inprogress(true); + + /* Configure the IO96B CSR address based on the handoff data */ + config_io96b_csr_addr(is_dualemif, &io96b_ctrl); + + /* Configuring MPFE sideband manager registers */ + /* Dual port setting */ + if (is_dualport) + mmio_setbits_32(SIDEBANDMGR_FLAGOUTSET0_REG, BIT(4)); + + /* Dual EMIF setting */ + if (is_dualemif) { + /* Set mpfe_lite_active in the system manager */ + /* TODO: recheck on the bit value?? */ + mmio_setbits_32(SOCFPGA_SYSMGR(MPFE_CONFIG), BIT(8)); + + mmio_setbits_32(SIDEBANDMGR_FLAGOUTSET0_REG, BIT(5)); + } + + if (is_dualport || is_dualemif) + INFO("DDR: SIDEBANDMGR_FLAGOUTSTATUS0: 0x%x\n", + mmio_read_32(SIDEBANDMGR_FLAGOUTSTATUS0_REG)); + + /* Ensure calibration status passing */ + init_mem_cal(&io96b_ctrl); + + /* Initiate IOSSM mailbox */ + io96b_mb_init(&io96b_ctrl); + + /* Need to trigger re-calibration for DDR DBE */ + if (ddr_ecc_dbe_status()) { + io96b_ctrl.io96b_0.cal_status = false; + io96b_ctrl.io96b_1.cal_status = false; + io96b_ctrl.overall_cal_status = io96b_ctrl.io96b_0.cal_status || + io96b_ctrl.io96b_1.cal_status; + } + + /* Trigger re-calibration if calibration failed */ + if (!(io96b_ctrl.overall_cal_status)) { + NOTICE("DDR: Re-calibration in progress...\n"); + trig_mem_cal(&io96b_ctrl); + } + NOTICE("DDR: Calibration success\n"); + + /* DDR type, DDR size and ECC status) */ + ret = get_mem_technology(&io96b_ctrl); + if (ret != 0) { + ERROR("DDR: Failed to get DDR type\n"); + return ret; + } + + ret = get_mem_width_info(&io96b_ctrl); + if (ret != 0) { + ERROR("DDR: Failed to get DDR size\n"); + return ret; + } + + /* DDR size queried from the IOSSM controller */ + hw_ddr_size = (phys_size_t)io96b_ctrl.overall_size * SZ_1G / SZ_8; + + /* TODO: Hard code 1GB as of now, and DDR start and end address */ + config_ddr_size = 0x40000000; + ddr_info_set[0].start = 0x80000000; + ddr_info_set[0].size = 0x40000000; + + if (config_ddr_size != hw_ddr_size) { + WARN("DDR: DDR size configured is (%lld MiB)\n", config_ddr_size >> 20); + WARN("DDR: Mismatch with hardware size (%lld MiB).\n", hw_ddr_size >> 20); + } + + if (config_ddr_size > hw_ddr_size) { + ERROR("DDR: Confgured DDR size is greater than the hardware size - HANG!!!\n"); + while (1) + ; + } + + ret = ecc_enable_status(&io96b_ctrl); + if (ret != 0) { + ERROR("DDR: Failed to get DDR ECC status\n"); + return ret; + } + + /* + * HPS cold or warm reset? If yes, skip full memory initialization if + * ECC is enabled to preserve memory content. + */ + if (io96b_ctrl.ecc_status != 0) { + full_mem_init = hps_ocram_dbe_status() | ddr_ecc_dbe_status() | + is_ddr_hang_bfr_rst; + if ((full_mem_init == true) || (reset_t == WARM_RESET || + reset_t == COLD_RESET) == 0) { + ret = bist_mem_init_start(&io96b_ctrl); + if (ret != 0) { + ERROR("DDR: Failed to fully initialize DDR memory\n"); + return ret; + } + } + INFO("DDR: ECC initialized successfully\n"); + } + + sdram_set_firewall(); + + /* + * Firewall setting for MPFE CSRs, allow both secure and non-secure + * transactions. + */ + /* IO96B0_reg */ + mmio_setbits_32(SOCFPGA_MPFE_SCR_IO96B0, BIT(0)); + /* IO96B1_reg */ + mmio_setbits_32(SOCFPGA_MPFE_SCR_IO96B1, BIT(0)); + /* noc_scheduler_csr */ + mmio_setbits_32(SOCFPGA_MPFE_NOC_SCHED_CSR, BIT(0)); + + INFO("DDR: firewall init done\n"); + + /* Ending DDR driver initialization success tracking */ + ddr_init_inprogress(false); + + NOTICE("###DDR:init success###\n"); + + return 0; +} diff --git a/plat/intel/soc/agilex5/soc/agilex5_iossm_mailbox.c b/plat/intel/soc/agilex5/soc/agilex5_iossm_mailbox.c new file mode 100644 index 00000000..c2ab0472 --- /dev/null +++ b/plat/intel/soc/agilex5/soc/agilex5_iossm_mailbox.c @@ -0,0 +1,811 @@ +/* + * Copyright (c) 2024, Altera Corporation. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <errno.h> +#include <stdio.h> +#include <string.h> + +#include <common/debug.h> +#include <drivers/delay_timer.h> + +#include "agilex5_iossm_mailbox.h" + +/* supported DDR type list */ +static const char *ddr_type_list[7] = { + "DDR4", "DDR5", "DDR5_RDIMM", "LPDDR4", "LPDDR5", "QDRIV", "UNKNOWN" +}; + +static inline int wait_for_bit(const void *reg, + const uint32_t mask, + const bool set, + const unsigned int timeout_ms) +{ + uint32_t val; + uint32_t timeout_sec = (timeout_ms / 1000); + + while (timeout_sec > 0) { + val = mmio_read_32((uintptr_t)reg); + + INFO("IOSSM: timeout_sec %d, val %x\n", timeout_sec, val); + + if (!set) { + val = ~val; + } + + if ((val & mask) == mask) { + INFO("IOSSM: %s, success\n", __func__); + return 0; + } + + /* one second delay */ + mdelay(1000); + + timeout_sec--; + } + + ERROR("IOSSM: %s, failed, time out\n", __func__); + return -ETIMEDOUT; +} + +int io96b_mb_req(phys_addr_t io96b_csr_addr, uint32_t ip_type, uint32_t instance_id, + uint32_t usr_cmd_type, uint32_t usr_cmd_opcode, uint32_t cmd_param_0, + uint32_t cmd_param_1, uint32_t cmd_param_2, uint32_t cmd_param_3, + uint32_t cmd_param_4, uint32_t cmd_param_5, uint32_t cmd_param_6, + uint32_t resp_data_len, struct io96b_mb_resp *resp) +{ + int i; + int ret; + uint32_t cmd_req, cmd_resp; + + /* Initialized zeros for responses*/ + resp->cmd_resp_status = 0; + resp->cmd_resp_data_0 = 0; + resp->cmd_resp_data_1 = 0; + resp->cmd_resp_data_2 = 0; + + /* Ensure CMD_REQ is cleared before write any command request */ + ret = wait_for_bit((const void *)(io96b_csr_addr + IOSSM_CMD_REQ_OFFSET), + GENMASK(31, 0), 0, 10000); + + if (ret != 0) { + ERROR("%s: CMD_REQ not ready\n", __func__); + return -1; + } + + /* Write CMD_PARAM_* */ + for (i = 0; i < 6 ; i++) { + switch (i) { + case 0: + if (cmd_param_0 != 0) { + mmio_write_32(io96b_csr_addr + IOSSM_CMD_PARAM_0_OFFSET, + cmd_param_0); + } + break; + case 1: + if (cmd_param_1 != 0) { + mmio_write_32(io96b_csr_addr + IOSSM_CMD_PARAM_1_OFFSET, + cmd_param_1); + } + break; + case 2: + if (cmd_param_2 != 0) { + mmio_write_32(io96b_csr_addr + IOSSM_CMD_PARAM_2_OFFSET, + cmd_param_2); + } + break; + case 3: + if (cmd_param_3 != 0) { + mmio_write_32(io96b_csr_addr + IOSSM_CMD_PARAM_3_OFFSET, + cmd_param_3); + } + break; + case 4: + if (cmd_param_4 != 0) { + mmio_write_32(io96b_csr_addr + IOSSM_CMD_PARAM_4_OFFSET, + cmd_param_4); + } + break; + case 5: + if (cmd_param_5 != 0) { + mmio_write_32(io96b_csr_addr + IOSSM_CMD_PARAM_5_OFFSET, + cmd_param_5); + } + break; + case 6: + if (cmd_param_6 != 0) { + mmio_write_32(io96b_csr_addr + IOSSM_CMD_PARAM_6_OFFSET, + cmd_param_6); + } + break; + default: + ERROR("IOSSM: %s: Invalid command parameter\n", __func__); + } + } + + /* Write CMD_REQ (IP_TYPE, IP_INSTANCE_ID, CMD_TYPE and CMD_OPCODE) */ + cmd_req = (usr_cmd_opcode << 0) | (usr_cmd_type << 16) | (instance_id << 24) | + (ip_type << 29); + mmio_write_32(io96b_csr_addr + IOSSM_CMD_REQ_OFFSET, cmd_req); + INFO("IOSSM: %s: Write 0x%x to IOSSM_CMD_REQ_OFFSET 0x%llx\n", + __func__, cmd_req, io96b_csr_addr + IOSSM_CMD_REQ_OFFSET); + + /* Read CMD_RESPONSE_READY in CMD_RESPONSE_STATUS*/ + ret = wait_for_bit((const void *)(io96b_csr_addr + IOSSM_CMD_RESPONSE_STATUS_OFFSET), + IOSSM_STATUS_COMMAND_RESPONSE_READY, 1, 10000); + + if (ret != 0) { + ERROR("%s: CMD_RESPONSE ERROR:\n", __func__); + cmd_resp = (io96b_csr_addr + IOSSM_CMD_RESPONSE_STATUS_OFFSET); + ERROR("%s: STATUS_GENERAL_ERROR: 0x%x\n", __func__, (cmd_resp >> 1) & 0xF); + ERROR("%s: STATUS_CMD_RESPONSE_ERROR: 0x%x\n", __func__, (cmd_resp >> 5) & 0x7); + } + + /* read CMD_RESPONSE_STATUS*/ + resp->cmd_resp_status = mmio_read_32(io96b_csr_addr + IOSSM_CMD_RESPONSE_STATUS_OFFSET); + INFO("IOSSM: %s: CMD_RESPONSE_STATUS 0x%llx: 0x%x\n", + __func__, io96b_csr_addr + IOSSM_CMD_RESPONSE_STATUS_OFFSET, resp->cmd_resp_status); + + /* read CMD_RESPONSE_DATA_* */ + for (i = 0; i < resp_data_len; i++) { + switch (i) { + case 0: + resp->cmd_resp_data_0 = + mmio_read_32(io96b_csr_addr + IOSSM_CMD_RESPONSE_DATA_0_OFFSET); + + break; + case 1: + resp->cmd_resp_data_1 = + mmio_read_32(io96b_csr_addr + IOSSM_CMD_RESPONSE_DATA_1_OFFSET); + + break; + case 2: + resp->cmd_resp_data_2 = + mmio_read_32(io96b_csr_addr + IOSSM_CMD_RESPONSE_DATA_2_OFFSET); + break; + default: + ERROR("%s: Invalid response data\n", __func__); + } + } + + resp->cmd_resp_status = mmio_read_32(io96b_csr_addr + IOSSM_CMD_RESPONSE_STATUS_OFFSET); + INFO("IOSSM: %s: CMD_RESPONSE_STATUS 0x%llx: 0x%x\n", + __func__, io96b_csr_addr + IOSSM_CMD_RESPONSE_STATUS_OFFSET, resp->cmd_resp_status); + + /* write CMD_RESPONSE_READY = 0 */ + mmio_clrbits_32(io96b_csr_addr + IOSSM_CMD_RESPONSE_STATUS_OFFSET, + IOSSM_STATUS_COMMAND_RESPONSE_READY); + + resp->cmd_resp_status = mmio_read_32(io96b_csr_addr + IOSSM_CMD_RESPONSE_STATUS_OFFSET); + INFO("IOSSM: %s: CMD_RESPONSE_READY 0x%llx: 0x%x\n", + __func__, io96b_csr_addr + IOSSM_CMD_RESPONSE_STATUS_OFFSET, resp->cmd_resp_status); + + return 0; +} + +/* + * Initial function to be called to set memory interface IP type and instance ID + * IP type and instance ID need to be determined before sending mailbox command + */ +void io96b_mb_init(struct io96b_info *io96b_ctrl) +{ + struct io96b_mb_resp usr_resp; + uint8_t ip_type_ret, instance_id_ret; + int i, j, k; + + for (i = 0; i < io96b_ctrl->num_instance; i++) { + switch (i) { + case 0: + /* Get memory interface IP type & instance ID (IP identifier) */ + io96b_mb_req(io96b_ctrl->io96b_0.io96b_csr_addr, 0, 0, + CMD_GET_SYS_INFO, GET_MEM_INTF_INFO, 0, 0, + 0, 0, 0, 0, 0, 2, &usr_resp); + /* Retrieve number of memory interface(s) */ + io96b_ctrl->io96b_0.mb_ctrl.num_mem_interface = + IOSSM_CMD_RESPONSE_DATA_SHORT(usr_resp.cmd_resp_status) & 0x3; + + /* Retrieve memory interface IP type and instance ID (IP identifier) */ + j = 0; + for (k = 0; k < MAX_MEM_INTERFACES_SUPPORTED; k++) { + switch (k) { + case 0: + ip_type_ret = (usr_resp.cmd_resp_data_0 >> 29) & 0x7; + instance_id_ret = (usr_resp.cmd_resp_data_0 >> 24) & 0x1F; + break; + case 1: + ip_type_ret = (usr_resp.cmd_resp_data_1 >> 29) & 0x7; + instance_id_ret = (usr_resp.cmd_resp_data_1 >> 24) & 0x1F; + break; + } + + if (ip_type_ret != 0) { + io96b_ctrl->io96b_0.mb_ctrl.ip_type[j] = ip_type_ret; + io96b_ctrl->io96b_0.mb_ctrl.ip_instance_id[j] = + instance_id_ret; + j++; + } + } + break; + case 1: + /* Get memory interface IP type and instance ID (IP identifier) */ + io96b_mb_req(io96b_ctrl->io96b_1.io96b_csr_addr, 0, 0, CMD_GET_SYS_INFO, + GET_MEM_INTF_INFO, 0, 0, 0, 0, 0, 0, 0, 2, &usr_resp); + + /* Retrieve number of memory interface(s) */ + io96b_ctrl->io96b_1.mb_ctrl.num_mem_interface = + IOSSM_CMD_RESPONSE_DATA_SHORT(usr_resp.cmd_resp_status) & 0x3; + + /* Retrieve memory interface IP type and instance ID (IP identifier) */ + j = 0; + for (k = 0; k < MAX_MEM_INTERFACES_SUPPORTED; k++) { + switch (k) { + case 0: + ip_type_ret = (usr_resp.cmd_resp_data_0 >> 29) & 0x7; + instance_id_ret = (usr_resp.cmd_resp_data_0 >> 24) & 0x1F; + break; + case 1: + ip_type_ret = (usr_resp.cmd_resp_data_1 >> 29) & 0x7; + instance_id_ret = (usr_resp.cmd_resp_data_1 >> 24) & 0x1F; + break; + } + + if (ip_type_ret != 0) { + io96b_ctrl->io96b_1.mb_ctrl.ip_type[j] = ip_type_ret; + io96b_ctrl->io96b_1.mb_ctrl.ip_instance_id[j] = + instance_id_ret; + j++; + } + } + break; + } + + } +} + +static inline void hang(void) +{ + ERROR("IOSSM: %s: system is going to die :(\n", __func__); + while (1) + ; +} + +int io96b_cal_status(phys_addr_t addr) +{ + int cal_busy_status, cal_success_status; + phys_addr_t status_addr = addr + IOSSM_STATUS_OFFSET; + + /* Ensure calibration busy status */ + cal_busy_status = wait_for_bit((const void *)status_addr, IOSSM_STATUS_CAL_BUSY, + false, 15000); + if (cal_busy_status != 0) { + ERROR("IOSSM: One or more EMIF instances are busy with calibration\n"); + return -EBUSY; + } + + /* Calibration success status check */ + NOTICE("IOSSM: Calibration success status check...\n"); + cal_success_status = wait_for_bit((const void *)status_addr, IOSSM_STATUS_CAL_SUCCESS, + true, 15000); + if (cal_success_status != 0) { + ERROR("IOSSM: One/more EMIF instances either failed to calibrate/not completed\n"); + return -EBUSY; + } + + NOTICE("IOSSM: All EMIF instances within the IO96 have calibrated successfully!\n"); + return 0; +} + +void init_mem_cal(struct io96b_info *io96b_ctrl) +{ + int count, i, ret; + + /* Initialize overall calibration status */ + io96b_ctrl->overall_cal_status = false; + + /* Check initial calibration status for the assigned IO96B */ + count = 0; + for (i = 0; i < io96b_ctrl->num_instance; i++) { + switch (i) { + case 0: + ret = io96b_cal_status(io96b_ctrl->io96b_0.io96b_csr_addr); + if (ret != 0) { + io96b_ctrl->io96b_0.cal_status = false; + ERROR("%s: Initial DDR calibration IO96B_0 failed %d\n", + __func__, ret); + break; + } + io96b_ctrl->io96b_0.cal_status = true; + INFO("IOSSM: %s: Initial DDR calibration IO96B_0 succeed\n", __func__); + count++; + break; + case 1: + ret = io96b_cal_status(io96b_ctrl->io96b_1.io96b_csr_addr); + if (ret != 0) { + io96b_ctrl->io96b_1.cal_status = false; + ERROR("%s: Initial DDR calibration IO96B_1 failed %d\n", + __func__, ret); + break; + } + io96b_ctrl->io96b_1.cal_status = true; + INFO("IOSSM: %s: Initial DDR calibration IO96B_1 succeed\n", __func__); + count++; + break; + } + } + + if (count == io96b_ctrl->num_instance) + io96b_ctrl->overall_cal_status = true; +} + +/* + * Trying 3 times re-calibration if initial calibration failed + */ +int trig_mem_cal(struct io96b_info *io96b_ctrl) +{ + struct io96b_mb_resp usr_resp; + bool recal_success; + int i; + uint8_t cal_stat; + + for (i = 0; i < io96b_ctrl->num_instance; i++) { + switch (i) { + case 0: + if (!(io96b_ctrl->io96b_0.cal_status)) { + /* Get the memory calibration status for first memory interface */ + io96b_mb_req(io96b_ctrl->io96b_0.io96b_csr_addr, 0, 0, + CMD_TRIG_MEM_CAL_OP, GET_MEM_CAL_STATUS, 0, + 0, 0, 0, 0, 0, 0, 2, &usr_resp); + + recal_success = false; + + /* Re-calibration first memory interface with failed calibration */ + for (i = 0; i < 3; i++) { + cal_stat = usr_resp.cmd_resp_data_0 & GENMASK(2, 0); + if (cal_stat < 0x2) { + recal_success = true; + break; + } + io96b_mb_req(io96b_ctrl->io96b_0.io96b_csr_addr, + io96b_ctrl->io96b_0.mb_ctrl.ip_type[0], + io96b_ctrl->io96b_0.mb_ctrl.ip_instance_id[0], + CMD_TRIG_MEM_CAL_OP, TRIG_MEM_CAL, 0, 0, 0, 0, + 0, 0, 0, 2, &usr_resp); + mdelay(1000); + io96b_mb_req(io96b_ctrl->io96b_0.io96b_csr_addr, 0, 0, + CMD_TRIG_MEM_CAL_OP, GET_MEM_CAL_STATUS, + 0, 0, 0, 0, 0, 0, 0, 2, &usr_resp); + } + + if (!recal_success) { + ERROR("%s: Error as SDRAM calibration failed\n", __func__); + hang(); + } + + /* Get the memory calibration status for second memory interface */ + io96b_mb_req(io96b_ctrl->io96b_0.io96b_csr_addr, 0, 0, + CMD_TRIG_MEM_CAL_OP, GET_MEM_CAL_STATUS, 0, 0, 0, + 0, 0, 0, 0, 2, &usr_resp); + + recal_success = false; + + /* Re-calibration second memory interface with failed calibration*/ + for (i = 0; i < 3; i++) { + cal_stat = usr_resp.cmd_resp_data_1 & GENMASK(2, 0); + if (cal_stat < 0x2) { + recal_success = true; + break; + } + io96b_mb_req(io96b_ctrl->io96b_0.io96b_csr_addr, + io96b_ctrl->io96b_0.mb_ctrl.ip_type[1], + io96b_ctrl->io96b_0.mb_ctrl.ip_instance_id[1], + CMD_TRIG_MEM_CAL_OP, TRIG_MEM_CAL, 0, 0, 0, 0, + 0, 0, 0, 2, &usr_resp); + mdelay(1000); + io96b_mb_req(io96b_ctrl->io96b_0.io96b_csr_addr, 0, 0, + CMD_TRIG_MEM_CAL_OP, GET_MEM_CAL_STATUS, + 0, 0, 0, 0, 0, 0, 0, 2, &usr_resp); + } + + if (!recal_success) { + ERROR("IOSSMM: Error as SDRAM calibration failed\n"); + hang(); + } + + io96b_ctrl->io96b_0.cal_status = true; + } + break; + case 1: + if (!(io96b_ctrl->io96b_1.cal_status)) { + /* Get the memory calibration status for first memory interface */ + io96b_mb_req(io96b_ctrl->io96b_1.io96b_csr_addr, 0, 0, + CMD_TRIG_MEM_CAL_OP, GET_MEM_CAL_STATUS, 0, + 0, 0, 0, 0, 0, 0, 2, &usr_resp); + + recal_success = false; + + /* Re-calibration first memory interface with failed calibration */ + for (i = 0; i < 3; i++) { + cal_stat = usr_resp.cmd_resp_data_0 & GENMASK(2, 0); + if (cal_stat < 0x2) { + recal_success = true; + break; + } + io96b_mb_req(io96b_ctrl->io96b_1.io96b_csr_addr, + io96b_ctrl->io96b_1.mb_ctrl.ip_type[0], + io96b_ctrl->io96b_1.mb_ctrl.ip_instance_id[0], + CMD_TRIG_MEM_CAL_OP, TRIG_MEM_CAL, 0, 0, 0, 0, + 0, 0, 0, 2, &usr_resp); + mdelay(1000); + io96b_mb_req(io96b_ctrl->io96b_1.io96b_csr_addr, 0, 0, + CMD_TRIG_MEM_CAL_OP, GET_MEM_CAL_STATUS, + 0, 0, 0, 0, 0, 0, 0, 2, &usr_resp); + } + + if (!recal_success) { + ERROR("IOSSM: Error as SDRAM calibration failed\n"); + hang(); + } + + /* Get the memory calibration status for second memory interface */ + io96b_mb_req(io96b_ctrl->io96b_1.io96b_csr_addr, 0, 0, + CMD_TRIG_MEM_CAL_OP, GET_MEM_CAL_STATUS, 0, 0, 0, + 0, 0, 0, 0, 2, &usr_resp); + + recal_success = false; + + /* Re-calibration second memory interface with failed calibration*/ + for (i = 0; i < 3; i++) { + cal_stat = usr_resp.cmd_resp_data_0 & GENMASK(2, 0); + if (cal_stat < 0x2) { + recal_success = true; + break; + } + io96b_mb_req(io96b_ctrl->io96b_1.io96b_csr_addr, + io96b_ctrl->io96b_1.mb_ctrl.ip_type[1], + io96b_ctrl->io96b_1.mb_ctrl.ip_instance_id[1], + CMD_TRIG_MEM_CAL_OP, TRIG_MEM_CAL, 0, 0, 0, 0, + 0, 0, 0, 2, &usr_resp); + mdelay(1000); + io96b_mb_req(io96b_ctrl->io96b_1.io96b_csr_addr, 0, 0, + CMD_TRIG_MEM_CAL_OP, GET_MEM_CAL_STATUS, + 0, 0, 0, 0, 0, 0, 0, 2, &usr_resp); + } + + if (!recal_success) { + ERROR("IOSSM: Error as SDRAM calibration failed\n"); + hang(); + } + + io96b_ctrl->io96b_1.cal_status = true; + } + break; + } + } + + if (io96b_ctrl->io96b_0.cal_status && io96b_ctrl->io96b_1.cal_status) { + INFO("IOSSM: %s: Overall SDRAM calibration success\n", __func__); + io96b_ctrl->overall_cal_status = true; + } + + return 0; +} + +int get_mem_technology(struct io96b_info *io96b_ctrl) +{ + struct io96b_mb_resp usr_resp; + int i, j; + uint8_t ddr_type_ret; + + /* Initialize ddr type */ + io96b_ctrl->ddr_type = ddr_type_list[6]; + + /* Get and ensure all memory interface(s) same DDR type */ + for (i = 0; i < io96b_ctrl->num_instance; i++) { + switch (i) { + case 0: + for (j = 0; j < io96b_ctrl->io96b_0.mb_ctrl.num_mem_interface; j++) { + io96b_mb_req(io96b_ctrl->io96b_0.io96b_csr_addr, + io96b_ctrl->io96b_0.mb_ctrl.ip_type[j], + io96b_ctrl->io96b_0.mb_ctrl.ip_instance_id[j], + CMD_GET_MEM_INFO, GET_MEM_TECHNOLOGY, 0, 0, 0, 0, + 0, 0, 0, 0, &usr_resp); + + ddr_type_ret = + IOSSM_CMD_RESPONSE_DATA_SHORT(usr_resp.cmd_resp_status) + & GENMASK(2, 0); + + if (strcmp(io96b_ctrl->ddr_type, "UNKNOWN") == 0) + io96b_ctrl->ddr_type = ddr_type_list[ddr_type_ret]; + + if (ddr_type_list[ddr_type_ret] != io96b_ctrl->ddr_type) { + ERROR("IOSSM: Mismatch DDR type on IO96B_0\n"); + return -ENOEXEC; + } + } + break; + case 1: + for (j = 0; j < io96b_ctrl->io96b_1.mb_ctrl.num_mem_interface; j++) { + io96b_mb_req(io96b_ctrl->io96b_1.io96b_csr_addr, + io96b_ctrl->io96b_1.mb_ctrl.ip_type[j], + io96b_ctrl->io96b_1.mb_ctrl.ip_instance_id[j], + CMD_GET_MEM_INFO, GET_MEM_TECHNOLOGY, 0, 0, 0, + 0, 0, 0, 0, 0, &usr_resp); + + ddr_type_ret = + IOSSM_CMD_RESPONSE_DATA_SHORT(usr_resp.cmd_resp_status) + & GENMASK(2, 0); + + if (strcmp(io96b_ctrl->ddr_type, "UNKNOWN") == 0) + io96b_ctrl->ddr_type = ddr_type_list[ddr_type_ret]; + + if (ddr_type_list[ddr_type_ret] != io96b_ctrl->ddr_type) { + ERROR("IOSSM: Mismatch DDR type on IO96B_1\n"); + return -ENOEXEC; + } + } + break; + } + } + + return 0; +} + +int get_mem_width_info(struct io96b_info *io96b_ctrl) +{ + struct io96b_mb_resp usr_resp; + int i, j; + uint16_t memory_size = 0U; + uint16_t total_memory_size = 0U; + + /* Get all memory interface(s) total memory size on all instance(s) */ + for (i = 0; i < io96b_ctrl->num_instance; i++) { + switch (i) { + case 0: + memory_size = 0; + for (j = 0; j < io96b_ctrl->io96b_0.mb_ctrl.num_mem_interface; j++) { + io96b_mb_req(io96b_ctrl->io96b_0.io96b_csr_addr, + io96b_ctrl->io96b_0.mb_ctrl.ip_type[j], + io96b_ctrl->io96b_0.mb_ctrl.ip_instance_id[j], + CMD_GET_MEM_INFO, GET_MEM_WIDTH_INFO, 0, 0, 0, + 0, 0, 0, 0, 2, &usr_resp); + + memory_size = memory_size + + (usr_resp.cmd_resp_data_1 & GENMASK(7, 0)); + } + + if (memory_size == 0U) { + ERROR("IOSSM: %s: Failed to get valid memory size\n", __func__); + return -ENOEXEC; + } + + io96b_ctrl->io96b_0.size = memory_size; + + break; + case 1: + memory_size = 0; + for (j = 0; j < io96b_ctrl->io96b_1.mb_ctrl.num_mem_interface; j++) { + io96b_mb_req(io96b_ctrl->io96b_1.io96b_csr_addr, + io96b_ctrl->io96b_1.mb_ctrl.ip_type[j], + io96b_ctrl->io96b_1.mb_ctrl.ip_instance_id[j], + CMD_GET_MEM_INFO, GET_MEM_WIDTH_INFO, 0, 0, 0, + 0, 0, 0, 0, 2, &usr_resp); + + memory_size = memory_size + + (usr_resp.cmd_resp_data_1 & GENMASK(7, 0)); + } + + if (memory_size == 0U) { + ERROR("IOSSM: %s: Failed to get valid memory size\n", __func__); + return -ENOEXEC; + } + + io96b_ctrl->io96b_1.size = memory_size; + + break; + } + + total_memory_size = total_memory_size + memory_size; + } + + if (total_memory_size == 0U) { + ERROR("IOSSM: %s: Failed to get valid memory size\n", __func__); + return -ENOEXEC; + } + + io96b_ctrl->overall_size = total_memory_size; + + return 0; +} + +int ecc_enable_status(struct io96b_info *io96b_ctrl) +{ + struct io96b_mb_resp usr_resp; + int i, j; + bool ecc_stat_set = false; + bool ecc_stat; + + /* Initialize ECC status */ + io96b_ctrl->ecc_status = false; + + /* Get and ensure all memory interface(s) same ECC status */ + for (i = 0; i < io96b_ctrl->num_instance; i++) { + switch (i) { + case 0: + for (j = 0; j < io96b_ctrl->io96b_0.mb_ctrl.num_mem_interface; j++) { + io96b_mb_req(io96b_ctrl->io96b_0.io96b_csr_addr, + io96b_ctrl->io96b_0.mb_ctrl.ip_type[j], + io96b_ctrl->io96b_0.mb_ctrl.ip_instance_id[j], + CMD_TRIG_CONTROLLER_OP, ECC_ENABLE_STATUS, 0, 0, + 0, 0, 0, 0, 0, 0, &usr_resp); + + ecc_stat = ((IOSSM_CMD_RESPONSE_DATA_SHORT(usr_resp.cmd_resp_status) + & GENMASK(1, 0)) == 0 ? false : true); + + if (!ecc_stat_set) { + io96b_ctrl->ecc_status = ecc_stat; + ecc_stat_set = true; + } + + if (ecc_stat != io96b_ctrl->ecc_status) { + ERROR("IOSSM: %s: Mismatch DDR ECC status on IO96B_0\n", + __func__); + return -ENOEXEC; + } + } + break; + case 1: + for (j = 0; j < io96b_ctrl->io96b_1.mb_ctrl.num_mem_interface; j++) { + io96b_mb_req(io96b_ctrl->io96b_1.io96b_csr_addr, + io96b_ctrl->io96b_1.mb_ctrl.ip_type[j], + io96b_ctrl->io96b_1.mb_ctrl.ip_instance_id[j], + CMD_TRIG_CONTROLLER_OP, ECC_ENABLE_STATUS, 0, 0, + 0, 0, 0, 0, 0, 0, &usr_resp); + + ecc_stat = ((IOSSM_CMD_RESPONSE_DATA_SHORT(usr_resp.cmd_resp_status) + & GENMASK(1, 0)) == 0 ? false : true); + + if (!ecc_stat_set) { + io96b_ctrl->ecc_status = ecc_stat; + ecc_stat_set = true; + } + + if (ecc_stat != io96b_ctrl->ecc_status) { + ERROR("%s: Mismatch DDR ECC status on IO96B_1\n" + , __func__); + return -ENOEXEC; + } + } + break; + } + } + return 0; +} + +int bist_mem_init_start(struct io96b_info *io96b_ctrl) +{ + struct io96b_mb_resp usr_resp; + int i, j; + bool bist_start, bist_success; + uint32_t read_count; + uint32_t read_interval_ms; + + /* Full memory initialization BIST performed on all memory interface(s) */ + for (i = 0; i < io96b_ctrl->num_instance; i++) { + switch (i) { + case 0: + for (j = 0; j < io96b_ctrl->io96b_0.mb_ctrl.num_mem_interface; j++) { + bist_start = false; + bist_success = false; + read_interval_ms = 500U; + + /* Start memory initialization BIST on full memory address */ + io96b_mb_req(io96b_ctrl->io96b_0.io96b_csr_addr, + io96b_ctrl->io96b_0.mb_ctrl.ip_type[j], + io96b_ctrl->io96b_0.mb_ctrl.ip_instance_id[j], + CMD_TRIG_CONTROLLER_OP, BIST_MEM_INIT_START, 0x40, + 0, 0, 0, 0, 0, 0, 0, &usr_resp); + + bist_start = + (IOSSM_CMD_RESPONSE_DATA_SHORT(usr_resp.cmd_resp_status) + & 1); + + if (!bist_start) { + ERROR("IOSSM: %s: Failed to initialized memory on IO96B_0\n" + , __func__); + ERROR("IOSSM: %s: BIST_MEM_INIT_START Error code 0x%x\n", + __func__, + (IOSSM_CMD_RESPONSE_DATA_SHORT(usr_resp.cmd_resp_status) + & GENMASK(2, 1)) > 0x1); + return -ENOEXEC; + } + + /* Polling for the initiated memory initialization BIST status */ + read_count = read_interval_ms / TIMEOUT; + while (!bist_success) { + io96b_mb_req(io96b_ctrl->io96b_0.io96b_csr_addr, + io96b_ctrl->io96b_0.mb_ctrl.ip_type[j], + io96b_ctrl->io96b_0.mb_ctrl.ip_instance_id[j], + CMD_TRIG_CONTROLLER_OP, BIST_MEM_INIT_STATUS, + 0, 0, 0, 0, 0, 0, 0, 0, &usr_resp); + + bist_success = (IOSSM_CMD_RESPONSE_DATA_SHORT + (usr_resp.cmd_resp_status) & 1); + + if ((!bist_success) && (read_count == 0U)) { + ERROR("IOSSM: %s: Timeout init memory on IO96B_0\n" + , __func__); + ERROR("IOSSM: %s: BIST_MEM_INIT_STATUS Err code%x\n" + , __func__, (IOSSM_CMD_RESPONSE_DATA_SHORT + (usr_resp.cmd_resp_status) + & GENMASK(2, 1)) > 0x1); + return -ETIMEDOUT; + } + read_count--; + mdelay(read_interval_ms); + } + } + + NOTICE("IOSSM: %s: Memory initialized successfully on IO96B_0\n", __func__); + break; + + case 1: + for (j = 0; j < io96b_ctrl->io96b_1.mb_ctrl.num_mem_interface; j++) { + bist_start = false; + bist_success = false; + read_interval_ms = 500U; + + /* Start memory initialization BIST on full memory address */ + io96b_mb_req(io96b_ctrl->io96b_1.io96b_csr_addr, + io96b_ctrl->io96b_1.mb_ctrl.ip_type[j], + io96b_ctrl->io96b_1.mb_ctrl.ip_instance_id[j], + CMD_TRIG_CONTROLLER_OP, BIST_MEM_INIT_START, 0x40, + 0, 0, 0, 0, 0, 0, 0, &usr_resp); + + bist_start = + (IOSSM_CMD_RESPONSE_DATA_SHORT(usr_resp.cmd_resp_status) + & 1); + + if (!bist_start) { + ERROR("IOSSM: %s: Failed to initialized memory on IO96B_1\n" + , __func__); + ERROR("IOSSM: %s: BIST_MEM_INIT_START Error code 0x%x\n", + __func__, + (IOSSM_CMD_RESPONSE_DATA_SHORT(usr_resp.cmd_resp_status) + & GENMASK(2, 1)) > 0x1); + return -ENOEXEC; + } + + /* Polling for the initiated memory initialization BIST status */ + read_count = read_interval_ms / TIMEOUT; + while (!bist_success) { + io96b_mb_req(io96b_ctrl->io96b_1.io96b_csr_addr, + io96b_ctrl->io96b_1.mb_ctrl.ip_type[j], + io96b_ctrl->io96b_1.mb_ctrl.ip_instance_id[j], + CMD_TRIG_CONTROLLER_OP, BIST_MEM_INIT_STATUS, + 0, 0, 0, 0, 0, 0, 0, 0, &usr_resp); + + bist_success = (IOSSM_CMD_RESPONSE_DATA_SHORT + (usr_resp.cmd_resp_status) & 1); + + if ((!bist_success) && (read_count == 0U)) { + ERROR("IOSSM: %s: Timeout init memory on IO96B_1\n" + , __func__); + ERROR("IOSSM: %s: BIST_MEM_INIT_STATUS ErrCode %x\n" + , __func__, (IOSSM_CMD_RESPONSE_DATA_SHORT + (usr_resp.cmd_resp_status) + & GENMASK(2, 1)) > 0x1); + return -ETIMEDOUT; + } + read_count--; + mdelay(read_interval_ms); + } + } + + NOTICE("IOSSM: %s: Memory initialized successfully on IO96B_1\n", __func__); + break; + } + } + return 0; +} diff --git a/plat/intel/soc/agilex5/soc/agilex5_pinmux.c b/plat/intel/soc/agilex5/soc/agilex5_pinmux.c index 50d9e360..317b4d88 100644 --- a/plat/intel/soc/agilex5/soc/agilex5_pinmux.c +++ b/plat/intel/soc/agilex5/soc/agilex5_pinmux.c @@ -1,5 +1,6 @@ /* * Copyright (c) 2019-2023, Intel Corporation. All rights reserved. + * Copyright (c) 2024, Altera Corporation. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -186,40 +187,72 @@ const uint32_t sysmgr_pinmux_array_iodelay[] = { 0x0000011c, 0x00000000 }; -void config_fpgaintf_mod(void) +static void config_fpgaintf_mod(void) { - mmio_write_32(SOCFPGA_SYSMGR(FPGAINTF_EN_2), 1<<8); + uint32_t fpgaintf_en_val; + + /* + * System manager FPGA interface enable2 register, disable individual + * interfaces between the FPGA and HPS. + */ + fpgaintf_en_val = 0U; + if ((mmio_read_32(SOCFPGA_PINUMX_USEFPGA(NAND_USEFPGA)) & 0x01) != 0) + fpgaintf_en_val |= BIT(4); + if ((mmio_read_32(SOCFPGA_PINUMX_USEFPGA(SDMMC_USEFPGA)) & 0x01) != 0) + fpgaintf_en_val |= BIT(8); + if ((mmio_read_32(SOCFPGA_PINUMX_USEFPGA(SPIM0_USEFPGA)) & 0x01) != 0) + fpgaintf_en_val |= BIT(16); + if ((mmio_read_32(SOCFPGA_PINUMX_USEFPGA(SPIM1_USEFPGA)) & 0x01) != 0) + fpgaintf_en_val |= BIT(24); + mmio_write_32(SOCFPGA_SYSMGR(FPGAINTF_EN_2), fpgaintf_en_val); + + /* + * System manager FPGA interface enable3 register, disable individual + * interfaces between the FPGA and HPS. + */ + fpgaintf_en_val = 0U; + if ((mmio_read_32(SOCFPGA_PINUMX_USEFPGA(EMAC0_USEFPGA)) & 0x01) != 0) + fpgaintf_en_val |= BIT(0); + if ((mmio_read_32(SOCFPGA_PINUMX_USEFPGA(EMAC1_USEFPGA)) & 0x01) != 0) + fpgaintf_en_val |= BIT(8); + if ((mmio_read_32(SOCFPGA_PINUMX_USEFPGA(EMAC2_USEFPGA)) & 0x01) != 0) + fpgaintf_en_val |= BIT(16); + mmio_write_32(SOCFPGA_SYSMGR(FPGAINTF_EN_3), fpgaintf_en_val); } void config_pinmux(handoff *hoff_ptr) { - unsigned int i; + uint32_t i; - mmio_write_32(PINMUX_HANDOFF_CONFIG_ADDR, PINMUX_HANDOFF_CONFIG_VAL); - for (i = 0; i < PINMUX_HANDOFF_ARRAY_SIZE(hoff_ptr->pinmux_sel_array); i += 2) { - mmio_write_32(AGX5_PINMUX_PIN0SEL + - hoff_ptr->pinmux_sel_array[i], - hoff_ptr->pinmux_sel_array[i + 1]); + /* Configure the pin selection */ + for (i = 0; i < ARRAY_SIZE(hoff_ptr->pinmux_sel_array); i += 2) { + mmio_write_32(AGX5_PINMUX_PIN0SEL + hoff_ptr->pinmux_sel_array[i], + hoff_ptr->pinmux_sel_array[i+1]); } - config_fpgaintf_mod(); -} - -void config_peripheral(handoff *hoff_ptr) -{ - - // TODO: This need to be update due to peripheral_pwr_gate_array handoff change - // Pending SDM to pass over handoff data - // unsigned int i; + /* Configure the pin control */ + for (i = 0; i < ARRAY_SIZE(hoff_ptr->pinmux_io_array); i += 2) { + mmio_write_32(AGX5_PINMUX_IO0CTRL + hoff_ptr->pinmux_io_array[i], + hoff_ptr->pinmux_io_array[i+1]); + } - // for (i = 0; i < 4; i += 2) { - // mmio_write_32(AGX_EDGE_PERIPHERAL + - // hoff_ptr->peripheral_pwr_gate_array[i], - // hoff_ptr->peripheral_pwr_gate_array[i+1]); - // } + /* + * Configure the FPGA use. + * The actual generic handoff contains extra 4 elements, and these 4 elements + * are not applicable to the Agilex5 platform. Writing these extra 4 elements + * will cause the system to crash, so let's avoid writing them here. + */ + for (i = 0; i < (ARRAY_SIZE(hoff_ptr->pinmux_fpga_array) - 4); i += 2) { + mmio_write_32(AGX5_PINMUX_EMAC0_USEFPGA + hoff_ptr->pinmux_fpga_array[i], + hoff_ptr->pinmux_fpga_array[i+1]); + } + /* Configure the IO delay */ + for (i = 0; i < ARRAY_SIZE(hoff_ptr->pinmux_iodelay_array); i += 2) { + mmio_write_32(AGX5_PINMUX_IO0_DELAY + hoff_ptr->pinmux_iodelay_array[i], + hoff_ptr->pinmux_iodelay_array[i+1]); + } - // TODO: This need to be update due to peripheral_pwr_gate_array handoff change - mmio_write_32(AGX5_PERIPHERAL, - hoff_ptr->peripheral_pwr_gate_array); + /* Enable/Disable individual interfaces between the FPGA and HPS */ + config_fpgaintf_mod(); } diff --git a/plat/intel/soc/agilex5/soc/agilex5_power_manager.c b/plat/intel/soc/agilex5/soc/agilex5_power_manager.c index 0d81970e..ef3acf90 100644 --- a/plat/intel/soc/agilex5/soc/agilex5_power_manager.c +++ b/plat/intel/soc/agilex5/soc/agilex5_power_manager.c @@ -1,5 +1,6 @@ /* * Copyright (c) 2022-2023, Intel Corporation. All rights reserved. + * Copyright (c) 2024, Altera Corporation. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -13,7 +14,7 @@ #include "agilex5_power_manager.h" #include "socfpga_reset_manager.h" -int wait_verify_fsm(uint16_t timeout, uint32_t peripheral_handoff) +static int wait_verify_fsm(uint16_t timeout, uint32_t peripheral_handoff) { uint32_t data = 0; uint32_t count = 0; @@ -38,7 +39,7 @@ int wait_verify_fsm(uint16_t timeout, uint32_t peripheral_handoff) return 0; } -int pss_sram_power_off(handoff *hoff_ptr) +static int pss_sram_power_off(handoff *hoff_ptr) { int ret = 0; uint32_t peripheral_handoff = 0; @@ -66,7 +67,7 @@ void config_pwrmgr_handoff(handoff *hoff_ptr) { int ret = 0; - switch (hoff_ptr->header_magic) { + switch (hoff_ptr->peripheral_pwr_gate_magic) { case HANDOFF_MAGIC_PERIPHERAL: ret = pss_sram_power_off(hoff_ptr); break; diff --git a/plat/intel/soc/common/aarch64/plat_helpers.S b/plat/intel/soc/common/aarch64/plat_helpers.S index cbd01212..b3d5665b 100644 --- a/plat/intel/soc/common/aarch64/plat_helpers.S +++ b/plat/intel/soc/common/aarch64/plat_helpers.S @@ -1,5 +1,7 @@ /* * Copyright (c) 2019-2023, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2019-2023, Intel Corporation. All rights reserved. + * Copyright (c) 2024, Altera Corporation. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -96,18 +98,6 @@ func plat_my_core_pos endfunc plat_my_core_pos func warm_reset_req -#if PLATFORM_MODEL == PLAT_SOCFPGA_AGILEX5 - bl plat_is_my_cpu_primary - cbnz x0, warm_reset -warm_reset: - mov_imm x1, PLAT_SEC_ENTRY - str xzr, [x1] - mrs x1, rmr_el3 - orr x1, x1, #0x02 - msr rmr_el3, x1 - isb - dsb sy -#else str xzr, [x4] bl plat_is_my_cpu_primary cbz x0, cpu_in_wfi @@ -121,22 +111,35 @@ warm_reset: cpu_in_wfi: wfi b cpu_in_wfi -#endif endfunc warm_reset_req -/* TODO: Zephyr warm reset test */ #if PLATFORM_MODEL == PLAT_SOCFPGA_AGILEX5 func plat_get_my_entrypoint ldr x4, =L2_RESET_DONE_REG ldr x5, [x4] - ldr x1, =PLAT_L2_RESET_REQ + + /* Check for warm reset request */ + ldr x1, =L2_RESET_DONE_STATUS + cmp x1, x5 + b.eq warm_reset_req + + /* Check for SMP secondary cores boot request */ + ldr x1, =SMP_SEC_CORE_BOOT_REQ cmp x1, x5 - b.eq zephyr_reset_req + b.eq smp_request + + /* Otherwise it is cold reset */ + mov x0, #0 + ret +smp_request: + /* + * Return the address 'bl31_warm_entrypoint', which is passed to + * 'psci_setup' routine as part of BL31 initialization. + */ mov_imm x1, PLAT_SEC_ENTRY ldr x0, [x1] - ret -zephyr_reset_req: - ldr x0, =0x00 + /* Clear the mark up before return */ + str xzr, [x4] ret endfunc plat_get_my_entrypoint #else diff --git a/plat/intel/soc/common/aarch64/platform_common.c b/plat/intel/soc/common/aarch64/platform_common.c index b79a63c8..a0f50dcd 100644 --- a/plat/intel/soc/common/aarch64/platform_common.c +++ b/plat/intel/soc/common/aarch64/platform_common.c @@ -11,12 +11,6 @@ #include "socfpga_private.h" - -unsigned int plat_get_syscnt_freq2(void) -{ - return PLAT_SYS_COUNTER_FREQ_IN_TICKS; -} - unsigned long socfpga_get_ns_image_entrypoint(void) { return PLAT_NS_IMAGE_OFFSET; diff --git a/plat/intel/soc/common/bl2_plat_mem_params_desc.c b/plat/intel/soc/common/bl2_plat_mem_params_desc.c index 187c53ac..a09fb709 100644 --- a/plat/intel/soc/common/bl2_plat_mem_params_desc.c +++ b/plat/intel/soc/common/bl2_plat_mem_params_desc.c @@ -88,9 +88,27 @@ static bl_mem_params_node_t bl2_mem_params_descs[] = { .image_info.image_base = PLAT_NS_IMAGE_OFFSET, .image_info.image_max_size = 0x0 + 0x40000000 - PLAT_NS_IMAGE_OFFSET, +# if ARM_LINUX_KERNEL_AS_BL33 != 0 + .next_handoff_image_id = NT_FW_CONFIG_ID, + }, + + { + .image_id = NT_FW_CONFIG_ID, + SET_STATIC_PARAM_HEAD(ep_info, PARAM_IMAGE_BINARY, + VERSION_2, entry_point_info_t, + NON_SECURE | NON_EXECUTABLE), + SET_STATIC_PARAM_HEAD(image_info, PARAM_IMAGE_BINARY, + VERSION_2, image_info_t, 0), + .image_info.image_base = ARM_PRELOADED_DTB_BASE, + .image_info.image_max_size = + 0x0 + 0x40000000 - ARM_PRELOADED_DTB_BASE, .next_handoff_image_id = INVALID_IMAGE_ID, }, +#else + .next_handoff_image_id = INVALID_IMAGE_ID, + }, +# endif }; REGISTER_BL_IMAGE_DESCS(bl2_mem_params_descs) diff --git a/plat/intel/soc/common/drivers/ccu/ncore_ccu.c b/plat/intel/soc/common/drivers/ccu/ncore_ccu.c index 684a6256..931ffcf9 100644 --- a/plat/intel/soc/common/drivers/ccu/ncore_ccu.c +++ b/plat/intel/soc/common/drivers/ccu/ncore_ccu.c @@ -1,22 +1,522 @@ /* * Copyright (c) 2019-2023, Intel Corporation. All rights reserved. + * Copyright (c) 2024, Altera Corporation. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ #include <assert.h> #include <common/debug.h> +#include <drivers/delay_timer.h> #include <errno.h> #include <lib/mmio.h> #include <platform_def.h> #include "ncore_ccu.h" +#include "socfpga_mailbox.h" #include "socfpga_plat_def.h" #include "socfpga_system_manager.h" uint32_t poll_active_bit(uint32_t dir); -#define SMMU_DMI 1 +#define SMMU_DMI 1 +#define CCU_DMI0_DMIUSMCMCR SOCFPGA_CCU_NOC_REG_BASE + 0x7340 +#define CCU_DMI0_DMIUSMCMAR SOCFPGA_CCU_NOC_REG_BASE + 0x7344 +#define CCU_DMI0_DMIUSMCMCR_MNTOP GENMASK(3, 0) +#define MAX_DISTRIBUTED_MEM_INTERFACE 2 +#define FLUSH_ALL_ENTRIES 0x4 +#define CCU_DMI0_DMIUSMCMCR_ARRAY_ID GENMASK(21, 16) +#define ARRAY_ID_TAG 0x0 +#define ARRAY_ID_DATA 0x1 +#define CACHE_OPERATION_DONE BIT(0) +#define TIMEOUT_200MS 200 +#define __bf_shf(x) (__builtin_ffsll(x) - 1) + +#define FIELD_PREP(_mask, _val) \ + ({ \ + ((typeof(_mask))(_val) << __bf_shf(_mask)) & (_mask); \ + }) + +#if PLATFORM_MODEL == PLAT_SOCFPGA_AGILEX5 +ncore_ccu_reg_t ncore_ccu_modules[] = { + {"caiu0@1c000000", 0x1C000000, 0x00001000}, + {"ncaiu0@1c001000", 0x1C001000, 0x00001000}, + {"ncaiu1@1c002000", 0x1C002000, 0x00001000}, + {"ncaiu2@1c003000", 0x1C003000, 0x00001000}, + {"ncaiu3@1c004000", 0x1C004000, 0x00001000}, + {"dce0@1c005000", 0x1C005000, 0x00001000}, + {"dce1@1c006000", 0x1C006000, 0x00001000}, + {"dmi0@1c007000", 0x1C007000, 0x00001000}, + {"dmi1@1c008000", 0x1C008000, 0x00001000}, + {"noc_fw_l4_per@10d21000", 0x10D21000, 0x0000008C}, + {"noc_fw_l4_sys@10d21100", 0x10D21100, 0x00000098}, + {"noc_fw_lwsoc2fpga@10d21300", 0x10D21300, 0x00000004}, + {"noc_fw_soc2fpga@10d21200", 0x10D21200, 0x00000004}, + {"noc_fw_tcu@10d21400", 0x10D21400, 0x00000004} + }; + +ncore_ccu_t ccu_caiu0[] = { + /* CAIUAMIGR */ + {0x000003C0, 0x00000003, 0x0000001F}, + /* CAIUMIFSR */ + {0x000003C4, 0x00000000, 0x07070777}, + /* DII1_MPFEREGS */ + {0x00000414, 0x00018000, 0xFFFFFFFF}, + {0x00000418, 0x00000000, 0x000000FF}, + {0x00000410, 0xC0E00200, 0xC1F03E1F}, + /* DII2_GICREGS */ + {0x00000424, 0x0001D000, 0xFFFFFFFF}, + {0x00000428, 0x00000000, 0x000000FF}, + {0x00000420, 0xC0800400, 0xC1F03E1F}, + /* NCAIU0_LWSOC2FPGA */ + {0x00000444, 0x00020000, 0xFFFFFFFF}, + {0x00000448, 0x00000000, 0x000000FF}, + {0x00000440, 0xC1100006, 0xC1F03E1F}, + /* NCAIU0_SOC2FPGA_1G */ + {0x00000454, 0x00040000, 0xFFFFFFFF}, + {0x00000458, 0x00000000, 0x000000FF}, + {0x00000450, 0xC1200006, 0xC1F03E1F}, + /* DMI_SDRAM_2G */ + {0x00000464, 0x00080000, 0xFFFFFFFF}, + {0x00000468, 0x00000000, 0x000000FF}, + {0x00000460, 0x81300006, 0xC1F03E1F}, + /* NCAIU0_SOC2FPGA_16G */ + {0x00000474, 0x00400000, 0xFFFFFFFF}, + {0x00000478, 0x00000000, 0x000000FF}, + {0x00000470, 0xC1600006, 0xC1F03E1F}, + /* DMI_SDRAM_30G */ + {0x00000484, 0x00800000, 0xFFFFFFFF}, + {0x00000488, 0x00000000, 0x000000FF}, + {0x00000480, 0x81700006, 0xC1F03E1F}, + /* NCAIU0_SOC2FPGA_256G */ + {0x00000494, 0x04000000, 0xFFFFFFFF}, + {0x00000498, 0x00000000, 0x000000FF}, + {0x00000490, 0xC1A00006, 0xC1F03E1F}, + /* DMI_SDRAM_480G */ + {0x000004A4, 0x08000000, 0xFFFFFFFF}, + {0x000004A8, 0x00000000, 0x000000FF}, + {0x000004A0, 0x81B00006, 0xC1F03E1F} + }; + +ncore_ccu_t ccu_ncaiu0[] = { + /* NCAIU0AMIGR */ + {0x000003C0, 0x00000003, 0x0000001F}, + /* NCAIU0MIFSR */ + {0x000003C4, 0x00000000, 0x07070777}, + /* PSS */ + {0x00000404, 0x00010000, 0xFFFFFFFF}, + {0x00000408, 0x00000000, 0x000000FF}, + {0x00000400, 0xC0F00000, 0xC1F03E1F}, + /* DII1_MPFEREGS */ + {0x00000414, 0x00018000, 0xFFFFFFFF}, + {0x00000418, 0x00000000, 0x000000FF}, + {0x00000410, 0xC0E00200, 0xC1F03E1F}, + /* NCAIU0_LWSOC2FPGA */ + {0x00000444, 0x00020000, 0xFFFFFFFF}, + {0x00000448, 0x00000000, 0x000000FF}, + {0x00000440, 0xC1100006, 0xC1F03E1F}, + /* NCAIU0_SOC2FPGA_1G */ + {0x00000454, 0x00040000, 0xFFFFFFFF}, + {0x00000458, 0x00000000, 0x000000FF}, + {0x00000450, 0xC1200006, 0xC1F03E1F}, + /* DMI_SDRAM_2G */ + {0x00000464, 0x00080000, 0xFFFFFFFF}, + {0x00000468, 0x00000000, 0x000000FF}, + {0x00000460, 0x81300006, 0xC1F03E1F}, + /* NCAIU0_SOC2FPGA_16G */ + {0x00000474, 0x00400000, 0xFFFFFFFF}, + {0x00000478, 0x00000000, 0x000000FF}, + {0x00000470, 0xC1600006, 0xC1F03E1F}, + /* DMI_SDRAM_30G */ + {0x00000484, 0x00800000, 0xFFFFFFFF}, + {0x00000488, 0x00000000, 0x000000FF}, + {0x00000480, 0x81700006, 0xC1F03E1F}, + /* NCAIU0_SOC2FPGA_256G */ + {0x00000494, 0x04000000, 0xFFFFFFFF}, + {0x00000498, 0x00000000, 0x000000FF}, + {0x00000490, 0xC1A00006, 0xC1F03E1F}, + /* DMI_SDRAM_480G */ + {0x000004A4, 0x08000000, 0xFFFFFFFF}, + {0x000004A8, 0x00000000, 0x000000FF}, + {0x000004A0, 0x81B00006, 0xC1F03E1F} + }; + +ncore_ccu_t ccu_ncaiu1[] = { + /* NCAIU1AMIGR */ + {0x000003C0, 0x00000003, 0x0000001F}, + /* NCAIU1MIFSR */ + {0x000003C4, 0x00000000, 0x07070777}, + /* DMI_SDRAM_2G */ + {0x00000464, 0x00080000, 0xFFFFFFFF}, + {0x00000468, 0x00000000, 0x000000FF}, + {0x00000460, 0x81300006, 0xC1F03E1F}, + /* DMI_SDRAM_30G */ + {0x00000484, 0x00800000, 0xFFFFFFFF}, + {0x00000488, 0x00000000, 0x000000FF}, + {0x00000480, 0x81700006, 0xC1F03E1F}, + /* DMI_SDRAM_480G */ + {0x000004A4, 0x08000000, 0xFFFFFFFF}, + {0x000004A8, 0x00000000, 0x000000FF}, + {0x000004A0, 0x81B00006, 0xC1F03E1F} + }; + +ncore_ccu_t ccu_ncaiu2[] = { + /* NCAIU2AMIGR */ + {0x000003C0, 0x00000003, 0x0000001F}, + /* NCAIU2MIFSR */ + {0x000003C4, 0x00000000, 0x07070777}, + /* DMI_SDRAM_2G */ + {0x00000464, 0x00080000, 0xFFFFFFFF}, + {0x00000468, 0x00000000, 0x000000FF}, + {0x00000460, 0x81300006, 0xC1F03E1F}, + /* DMI_SDRAM_30G */ + {0x00000484, 0x00800000, 0xFFFFFFFF}, + {0x00000488, 0x00000000, 0x000000FF}, + {0x00000480, 0x81700006, 0xC1F03E1F}, + /* DMI_SDRAM_480G */ + {0x000004A4, 0x08000000, 0xFFFFFFFF}, + {0x000004A8, 0x00000000, 0x000000FF}, + {0x000004A0, 0x81B00006, 0xC1F03E1F} + }; + +ncore_ccu_t ccu_ncaiu3[] = { + /* NCAIU3AMIGR */ + {0x000003C0, 0x00000003, 0x0000001F}, + /* NCAIU3MIFSR */ + {0x000003C4, 0x00000000, 0x07070777}, + /* DII1_MPFEREGS */ + {0x00000414, 0x00018000, 0xFFFFFFFF}, + {0x00000418, 0x00000000, 0x000000FF}, + {0x00000410, 0xC0E00200, 0xC1F03E1F}, + /* DMI_SDRAM_2G */ + {0x00000464, 0x00080000, 0xFFFFFFFF}, + {0x00000468, 0x00000000, 0x000000FF}, + {0x00000460, 0x81300006, 0xC1F03E1F}, + /* DMI_SDRAM_30G */ + {0x00000484, 0x00800000, 0xFFFFFFFF}, + {0x00000488, 0x00000000, 0x000000FF}, + {0x00000480, 0x81700006, 0xC1F03E1F}, + /* DMI_SDRAM_480G */ + {0x000004A4, 0x08000000, 0xFFFFFFFF}, + {0x000004A8, 0x00000000, 0x000000FF}, + {0x000004A0, 0x81B00006, 0xC1F03E1F} + }; + +ncore_ccu_t ccu_dce0[] = { + /* DCEUAMIGR0 */ + {0x000003C0, 0x00000003, 0x0000001F}, + /* DCEUMIFSR0 */ + {0x000003C4, 0x00000000, 0x07070777}, + /* DMI_SDRAM_2G */ + {0x00000464, 0x00080000, 0xFFFFFFFF}, + {0x00000468, 0x00000000, 0x000000FF}, + {0x00000460, 0x81300006, 0xC1F03E1F}, + /* DMI_SDRAM_30G */ + {0x00000484, 0x00800000, 0xFFFFFFFF}, + {0x00000488, 0x00000000, 0x000000FF}, + {0x00000480, 0x81700006, 0xC1F03E1F}, + /* DMI_SDRAM_480G */ + {0x000004A4, 0x08000000, 0xFFFFFFFF}, + {0x000004A8, 0x00000000, 0x000000FF}, + {0x000004A0, 0x81B00006, 0xC1F03E1F} + }; + +ncore_ccu_t ccu_dce1[] = { + /* DCEUAMIGR1 */ + {0x000003C0, 0x00000003, 0x0000001F}, + /* DCEUMIFSR1 */ + {0x000003C4, 0x00000000, 0x07070777}, + /* DMI_SDRAM_2G */ + {0x00000464, 0x00080000, 0xFFFFFFFF}, + {0x00000468, 0x00000000, 0x000000FF}, + {0x00000460, 0x81300006, 0xC1F03E1F}, + /* DMI_SDRAM_30G */ + {0x00000484, 0x00800000, 0xFFFFFFFF}, + {0x00000488, 0x00000000, 0x000000FF}, + {0x00000480, 0x81700006, 0xC1F03E1F}, + /* DMI_SDRAM_480G */ + {0x000004A4, 0x08000000, 0xFFFFFFFF}, + {0x000004A8, 0x00000000, 0x000000FF}, + {0x000004A0, 0x81B00006, 0xC1F03E1F} + }; + +ncore_ccu_t ccu_dmi0[] = { + /* DMIUSMCTCR */ + {0x00000300, 0x00000001, 0x00000003}, + {0x00000300, 0x00000003, 0x00000003} + }; + +ncore_ccu_t ccu_dmi1[] = { + /* DMIUSMCTCR */ + {0x00000300, 0x00000001, 0x00000003}, + {0x00000300, 0x00000003, 0x00000003} + }; + +ncore_ccu_t ccu_noc_fw_l4_per[] = { + /* NAND */ + {0x00000000, 0x01010001, 0x01010001}, + /* USB0 */ + {0x0000000C, 0x01010001, 0x01010001}, + /* USB1 */ + {0x00000010, 0x01010001, 0x01010001}, + /* SPI_MAIN0 */ + {0x0000001C, 0x01010301, 0x01010301}, + /* SPI_MAIN1 */ + {0x00000020, 0x01010301, 0x01010301}, + /* SPI_SECONDARY0 */ + {0x00000024, 0x01010301, 0x01010301}, + /* SPI_SECONDARY1 */ + {0x00000028, 0x01010301, 0x01010301}, + /* EMAC0 */ + {0x0000002C, 0x01010001, 0x01010001}, + /* EMAC1 */ + {0x00000030, 0x01010001, 0x01010001}, + /* EMAC2 */ + {0x00000034, 0x01010001, 0x01010001}, + /* SDMMC */ + {0x00000040, 0x01010001, 0x01010001}, + /* GPIO0 */ + {0x00000044, 0x01010301, 0x01010301}, + /* GPIO1 */ + {0x00000048, 0x01010301, 0x01010301}, + /* I2C0 */ + {0x00000050, 0x01010301, 0x01010301}, + /* I2C1 */ + {0x00000054, 0x01010301, 0x01010301}, + /* I2C2 */ + {0x00000058, 0x01010301, 0x01010301}, + /* I2C3 */ + {0x0000005C, 0x01010301, 0x01010301}, + /* I2C4 */ + {0x00000060, 0x01010301, 0x01010301}, + /* SP_TIMER0 */ + {0x00000064, 0x01010301, 0x01010301}, + /* SP_TIMER1 */ + {0x00000068, 0x01010301, 0x01010301}, + /* UART0 */ + {0x0000006C, 0x01010301, 0x01010301}, + /* UART1 */ + {0x00000070, 0x01010301, 0x01010301}, + /* I3C0 */ + {0x00000074, 0x01010301, 0x01010301}, + /* I3C1 */ + {0x00000078, 0x01010301, 0x01010301}, + /* DMA0 */ + {0x0000007C, 0x01010001, 0x01010001}, + /* DMA1 */ + {0x00000080, 0x01010001, 0x01010001}, + /* COMBO_PHY */ + {0x00000084, 0x01010001, 0x01010001}, + /* NAND_SDMA */ + {0x00000088, 0x01010301, 0x01010301} + }; + +ncore_ccu_t ccu_noc_fw_l4_sys[] = { + /* DMA_ECC */ + {0x00000008, 0x01010001, 0x01010001}, + /* EMAC0RX_ECC */ + {0x0000000C, 0x01010001, 0x01010001}, + /* EMAC0TX_ECC */ + {0x00000010, 0x01010001, 0x01010001}, + /* EMAC1RX_ECC */ + {0x00000014, 0x01010001, 0x01010001}, + /* EMAC1TX_ECC */ + {0x00000018, 0x01010001, 0x01010001}, + /* EMAC2RX_ECC */ + {0x0000001C, 0x01010001, 0x01010001}, + /* EMAC2TX_ECC */ + {0x00000020, 0x01010001, 0x01010001}, + /* NAND_ECC */ + {0x0000002C, 0x01010001, 0x01010001}, + /* NAND_READ_ECC */ + {0x00000030, 0x01010001, 0x01010001}, + /* NAND_WRITE_ECC */ + {0x00000034, 0x01010001, 0x01010001}, + /* OCRAM_ECC */ + {0x00000038, 0x01010001, 0x01010001}, + /* SDMMC_ECC */ + {0x00000040, 0x01010001, 0x01010001}, + /* USB0_ECC */ + {0x00000044, 0x01010001, 0x01010001}, + /* USB1_CACHEECC */ + {0x00000048, 0x01010001, 0x01010001}, + /* CLOCK_MANAGER */ + {0x0000004C, 0x01010001, 0x01010001}, + /* IO_MANAGER */ + {0x00000054, 0x01010001, 0x01010001}, + /* RESET_MANAGER */ + {0x00000058, 0x01010001, 0x01010001}, + /* SYSTEM_MANAGER */ + {0x0000005C, 0x01010001, 0x01010001}, + /* OSC0_TIMER */ + {0x00000060, 0x01010301, 0x01010301}, + /* OSC1_TIMER0*/ + {0x00000064, 0x01010301, 0x01010301}, + /* WATCHDOG0 */ + {0x00000068, 0x01010301, 0x01010301}, + /* WATCHDOG1 */ + {0x0000006C, 0x01010301, 0x01010301}, + /* WATCHDOG2 */ + {0x00000070, 0x01010301, 0x01010301}, + /* WATCHDOG3 */ + {0x00000074, 0x01010301, 0x01010301}, + /* DAP */ + {0x00000078, 0x03010001, 0x03010001}, + /* WATCHDOG4 */ + {0x0000007C, 0x01010301, 0x01010301}, + /* POWER_MANAGER */ + {0x00000080, 0x01010001, 0x01010001}, + /* USB1_RXECC */ + {0x00000084, 0x01010001, 0x01010001}, + /* USB1_TXECC */ + {0x00000088, 0x01010001, 0x01010001}, + /* L4_NOC_PROBES */ + {0x00000090, 0x01010001, 0x01010001}, + /* L4_NOC_QOS */ + {0x00000094, 0x01010001, 0x01010001} + }; + +ncore_ccu_t ccu_noc_fw_lwsoc2fpga[] = { + /* LWSOC2FPGA_CSR */ + {0x00000000, 0x0FFE0301, 0x0FFE0301} + }; + +ncore_ccu_t ccu_noc_fw_soc2fpga[] = { + /* SOC2FPGA_CSR */ + {0x00000000, 0x0FFE0301, 0x0FFE0301} + }; + +ncore_ccu_t ccu_noc_fw_tcu[] = { + /* TCU_CSR */ + {0x00000000, 0x01010001, 0x01010001} + }; + +uint32_t init_ncore_ccu(void) +{ + ncore_ccu_t *ccu_module_table = NULL; + uint32_t base; + uint32_t size; + uint32_t val; + uint32_t offset; + uint32_t mask; + uint32_t set_mask = 0U; + uint32_t reg = 0U; + + for (int index = 0; index < ARRAY_SIZE(ncore_ccu_modules); index++) { + base = ncore_ccu_modules[index].base; + size = ncore_ccu_modules[index].size; + + switch (index) { + case 0: + ccu_module_table = ccu_caiu0; + size = (sizeof(ccu_caiu0) / CCU_WORD_BYTE) / CCU_OFFSET_VAL_MASK; + break; + + case 1: + ccu_module_table = ccu_ncaiu0; + size = (sizeof(ccu_ncaiu0) / CCU_WORD_BYTE) / CCU_OFFSET_VAL_MASK; + break; + + case 2: + ccu_module_table = ccu_ncaiu1; + size = (sizeof(ccu_ncaiu1) / CCU_WORD_BYTE) / CCU_OFFSET_VAL_MASK; + break; + + case 3: + ccu_module_table = ccu_ncaiu2; + size = (sizeof(ccu_ncaiu2) / CCU_WORD_BYTE) / CCU_OFFSET_VAL_MASK; + break; + + case 4: + ccu_module_table = ccu_ncaiu3; + size = (sizeof(ccu_ncaiu3) / CCU_WORD_BYTE) / CCU_OFFSET_VAL_MASK; + break; + + case 5: + ccu_module_table = ccu_dce0; + size = (sizeof(ccu_dce0) / CCU_WORD_BYTE) / CCU_OFFSET_VAL_MASK; + break; + + case 6: + ccu_module_table = ccu_dce1; + size = (sizeof(ccu_dce1) / CCU_WORD_BYTE) / CCU_OFFSET_VAL_MASK; + break; + + case 7: + ccu_module_table = ccu_dmi0; + size = (sizeof(ccu_dmi0) / CCU_WORD_BYTE) / CCU_OFFSET_VAL_MASK; + break; + + case 8: + ccu_module_table = ccu_dmi1; + size = (sizeof(ccu_dmi1) / CCU_WORD_BYTE) / CCU_OFFSET_VAL_MASK; + break; + + case 9: + ccu_module_table = ccu_noc_fw_l4_per; + size = (sizeof(ccu_noc_fw_l4_per) / CCU_WORD_BYTE) / CCU_OFFSET_VAL_MASK; + break; + + case 10: + ccu_module_table = ccu_noc_fw_l4_sys; + size = (sizeof(ccu_noc_fw_l4_sys) / CCU_WORD_BYTE) / CCU_OFFSET_VAL_MASK; + break; + + case 11: + ccu_module_table = ccu_noc_fw_lwsoc2fpga; + size = (sizeof(ccu_noc_fw_lwsoc2fpga) / CCU_WORD_BYTE) / CCU_OFFSET_VAL_MASK; + break; + + case 12: + ccu_module_table = ccu_noc_fw_soc2fpga; + size = (sizeof(ccu_noc_fw_soc2fpga) / CCU_WORD_BYTE) / CCU_OFFSET_VAL_MASK; + break; + + case 13: + ccu_module_table = ccu_noc_fw_tcu; + size = (sizeof(ccu_noc_fw_tcu) / CCU_WORD_BYTE) / CCU_OFFSET_VAL_MASK; + break; + + default: + break; + } + + VERBOSE("CCU node base addr 0x%x, name %s, size 0x%x and module table %p\n", + base, ncore_ccu_modules[index].name, size, (uint32_t *)ccu_module_table); + + /* + * First element: offset + * Second element: val + * Third element: mask + */ + for (int i = 0; i < size; i++) { + offset = ccu_module_table[i].offset; + val = ccu_module_table[i].val; + + /* Reads the masking bit value from the list */ + mask = ccu_module_table[i].mask; + + if (mask != 0) { + if (mask == 0xFFFFFFFF) { + reg = base + offset; + mmio_write_32((uintptr_t)reg, val); + } else { + /* Mask the value with the masking bits */ + set_mask = val & mask; + reg = base + offset; + + /* Clears and sets specific bits in the register */ + mmio_clrsetbits_32((uintptr_t)reg, mask, set_mask); + } + } + + } + + } + + return 0; +} +#endif static coh_ss_id_t subsystem_id; void get_subsystem_id(void) @@ -29,6 +529,7 @@ void get_subsystem_id(void) subsystem_id.num_directory = directory; subsystem_id.num_coh_agent = coh_agent; } + uint32_t directory_init(void) { uint32_t dir_sf_mtn, dir_sf_en; @@ -42,7 +543,7 @@ uint32_t directory_init(void) /* Poll Active Bit */ ret = poll_active_bit(dir); if (ret != 0) { - ERROR("Timeout during active bit polling"); + ERROR("Timeout during active bit polling\n"); return -ETIMEDOUT; } /* Disable snoop filter, a bit per snoop filter */ @@ -51,6 +552,7 @@ uint32_t directory_init(void) } return 0; } + uint32_t coherent_agent_intfc_init(void) { uint32_t dir, ca, ca_id, ca_type, ca_snoop_en; @@ -65,11 +567,12 @@ uint32_t coherent_agent_intfc_init(void) ca_type = CACHING_AGENT_TYPE(ca_id); if (ca_type == ACE_W_DVM || ca_type == ACE_L_W_DVM) mmio_setbits_32(NCORE_CCU_CSR(NCORE_CSADSER0), - BIT(ca)); + BIT(ca)); } } return 0; } + uint32_t poll_active_bit(uint32_t dir) { uint32_t timeout = 80000; @@ -81,6 +584,7 @@ uint32_t poll_active_bit(uint32_t dir) } return -1; } + void bypass_ocram_firewall(void) { mmio_clrbits_32(COH_CPU0_BYPASS_REG(NCORE_FW_OCRAM_BLK_CGF1), @@ -92,6 +596,7 @@ void bypass_ocram_firewall(void) mmio_clrbits_32(COH_CPU0_BYPASS_REG(NCORE_FW_OCRAM_BLK_CGF4), OCRAM_PRIVILEGED_MASK | OCRAM_SECURE_MASK); } + void ncore_enable_ocram_firewall(void) { mmio_setbits_32(COH_CPU0_BYPASS_REG(NCORE_FW_OCRAM_BLK_CGF1), @@ -103,6 +608,8 @@ void ncore_enable_ocram_firewall(void) mmio_setbits_32(COH_CPU0_BYPASS_REG(NCORE_FW_OCRAM_BLK_CGF4), OCRAM_PRIVILEGED_MASK | OCRAM_SECURE_MASK); } + +#if PLATFORM_MODEL != PLAT_SOCFPGA_AGILEX5 uint32_t init_ncore_ccu(void) { uint32_t status; @@ -112,6 +619,7 @@ uint32_t init_ncore_ccu(void) bypass_ocram_firewall(); return status; } +#endif void setup_smmu_stream_id(void) { @@ -130,11 +638,10 @@ void setup_smmu_stream_id(void) mmio_write_32(SOCFPGA_SYSMGR(IO_TBU_STREAM_ID_AX_REG_2_TSN0), TSN0); mmio_write_32(SOCFPGA_SYSMGR(IO_TBU_STREAM_ID_AX_REG_2_TSN1), TSN1); mmio_write_32(SOCFPGA_SYSMGR(IO_TBU_STREAM_ID_AX_REG_2_TSN2), TSN2); - /* Enabled Stream ctrl register for Agilex5 */ mmio_write_32(SOCFPGA_SYSMGR(DMA_TBU_STREAM_CTRL_REG_0_DMA0), ENABLE_STREAMID); mmio_write_32(SOCFPGA_SYSMGR(DMA_TBU_STREAM_CTRL_REG_0_DMA1), ENABLE_STREAMID); - mmio_write_32(SOCFPGA_SYSMGR(SDM_TBU_STREAM_CTRL_REG_1_SDM), ENABLE_STREAMID_SECURE_TX); + mmio_write_32(SOCFPGA_SYSMGR(SDM_TBU_STREAM_CTRL_REG_1_SDM), ENABLE_STREAMID); mmio_write_32(SOCFPGA_SYSMGR(IO_TBU_STREAM_CTRL_REG_2_USB2), ENABLE_STREAMID); mmio_write_32(SOCFPGA_SYSMGR(IO_TBU_STREAM_CTRL_REG_2_USB3), ENABLE_STREAMID); mmio_write_32(SOCFPGA_SYSMGR(IO_TBU_STREAM_CTRL_REG_2_SDMMC), ENABLE_STREAMID); @@ -144,3 +651,61 @@ void setup_smmu_stream_id(void) mmio_write_32(SOCFPGA_SYSMGR(TSN_TBU_STREAM_CTRL_REG_3_TSN1), ENABLE_STREAMID); mmio_write_32(SOCFPGA_SYSMGR(TSN_TBU_STREAM_CTRL_REG_3_TSN2), ENABLE_STREAMID); } + +#if PLATFORM_MODEL == PLAT_SOCFPGA_AGILEX5 +/* TODO: Temp added this here*/ +static int poll_idle_status(uint32_t addr, uint32_t mask, uint32_t match, uint32_t delay_ms) +{ + int time_out = delay_ms; + + while (time_out-- > 0) { + + if ((mmio_read_32(addr) & mask) == match) { + return 0; + } + udelay(1000); + } + + return -ETIMEDOUT; +} + +int flush_l3_dcache(void) +{ + int i; + int ret = 0; + + /* Flushing all entries in CCU system memory cache */ + for (i = 0; i < MAX_DISTRIBUTED_MEM_INTERFACE; i++) { + mmio_write_32(FIELD_PREP(CCU_DMI0_DMIUSMCMCR_MNTOP, FLUSH_ALL_ENTRIES) | + FIELD_PREP(CCU_DMI0_DMIUSMCMCR_ARRAY_ID, ARRAY_ID_TAG), + (uintptr_t)(CCU_DMI0_DMIUSMCMCR + (i * 0x1000))); + + /* Wait for cache maintenance operation done */ + ret = poll_idle_status((CCU_DMI0_DMIUSMCMAR + + (i * 0x1000)), CACHE_OPERATION_DONE, + CACHE_OPERATION_DONE, TIMEOUT_200MS); + + if (ret != 0) { + VERBOSE("%s: Timeout while waiting for flushing tag in DMI%d done\n", + __func__, i); + return ret; + } + + mmio_write_32(FIELD_PREP(CCU_DMI0_DMIUSMCMCR_MNTOP, FLUSH_ALL_ENTRIES) | + FIELD_PREP(CCU_DMI0_DMIUSMCMCR_ARRAY_ID, ARRAY_ID_DATA), + (uintptr_t)(CCU_DMI0_DMIUSMCMCR + (i * 0x1000))); + + /* Wait for cache maintenance operation done */ + ret = poll_idle_status((CCU_DMI0_DMIUSMCMAR + + (i * 0x1000)), CACHE_OPERATION_DONE, + CACHE_OPERATION_DONE, TIMEOUT_200MS); + + if (ret != 0) { + VERBOSE("%s: Timeout while waiting for flushing data in DMI%d done\n", + __func__, i); + } + } + + return ret; +} +#endif diff --git a/plat/intel/soc/common/drivers/ccu/ncore_ccu.h b/plat/intel/soc/common/drivers/ccu/ncore_ccu.h index 6cdbeb8c..a89c098d 100644 --- a/plat/intel/soc/common/drivers/ccu/ncore_ccu.h +++ b/plat/intel/soc/common/drivers/ccu/ncore_ccu.h @@ -1,5 +1,6 @@ /* * Copyright (c) 2019-2023, Intel Corporation. All rights reserved. + * Copyright (c) 2024, Altera Corporation. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -9,192 +10,206 @@ #include <stdbool.h> #include <stdint.h> +#include "socfpga_plat_def.h" + #ifndef CCU_ACTIVATE_COH_FPGA -#define CCU_ACTIVATE_COH_FPGA 0 +#define CCU_ACTIVATE_COH_FPGA 0 #endif -// Address map for ccu init -#define addr_CAIUIDR1 (0x1C000000) -#define addr_GRBUNRRUCR (0x1c0ffff8) -#define base_addr_NRS_CAIU0 (0x1c000000) -#define base_addr_NRS_NCAIU0 (0x1c001000) -#define base_addr_NRS_NCAIU1 (0x1c002000) -#define base_addr_NRS_NCAIU2 (0x1c003000) -#define base_addr_NRS_NCAIU3 (0x1c004000) -#define base_addr_NRS_DCE0 (0x1c005000) -#define base_addr_NRS_DCE1 (0x1c006000) -//#define base_addr_NRS_DMI0 (0x1c007000) -//#define base_addr_NRS_DMI1 (0x1c008000) -//DMI -#define ALT_CCU_CCU_DMI0_DMIUSMCTCR_ADDR 0x1C007300 -#define ALT_CCU_CCU_DMI1_DMIUSMCTCR_ADDR 0x1C008300 -//DSU -#define ALT_CCU_DSU_CAIUAMIGR_ADDR 0x1C0003C0 -#define ALT_CCU_DSU_CAIUMIFSR_ADDR 0x1C0003C4 -#define ALT_CCU_DSU_CAIUGPRBLR1_ADDR 0x1C000414 -#define ALT_CCU_DSU_CAIUGPRBHR1_ADDR 0x1C000418 -#define ALT_CCU_DSU_CAIUGPRAR1_ADDR 0x1C000410 -#define ALT_CCU_DSU_CAIUGPRBLR2_ADDR 0x1C000424 -#define ALT_CCU_DSU_CAIUGPRBHR2_ADDR 0x1C000428 -#define ALT_CCU_DSU_CAIUGPRAR2_ADDR 0x1C000420 -#define ALT_CCU_DSU_CAIUGPRBLR4_ADDR 0x1C000444 -#define ALT_CCU_DSU_CAIUGPRBHR4_ADDR 0x1C000448 -#define ALT_CCU_DSU_CAIUGPRAR4_ADDR 0x1C000440 -#define ALT_CCU_DSU_CAIUGPRBLR5_ADDR 0x1C000454 -#define ALT_CCU_DSU_CAIUGPRBHR5_ADDR 0x1C000458 -#define ALT_CCU_DSU_CAIUGPRAR5_ADDR 0x1C000450 -#define ALT_CCU_DSU_CAIUGPRBLR6_ADDR 0x1C000464 -#define ALT_CCU_DSU_CAIUGPRBHR6_ADDR 0x1C000468 -#define ALT_CCU_DSU_CAIUGPRAR6_ADDR 0x1C000460 -#define ALT_CCU_DSU_CAIUGPRBLR7_ADDR 0x1C000474 -#define ALT_CCU_DSU_CAIUGPRBHR7_ADDR 0x1C000478 -#define ALT_CCU_DSU_CAIUGPRAR7_ADDR 0x1C000470 -#define ALT_CCU_DSU_CAIUGPRBLR8_ADDR 0x1C000484 -#define ALT_CCU_DSU_CAIUGPRBHR8_ADDR 0x1C000488 -#define ALT_CCU_DSU_CAIUGPRAR8_ADDR 0x1C000480 -#define ALT_CCU_DSU_CAIUGPRBLR9_ADDR 0x1C000494 -#define ALT_CCU_DSU_CAIUGPRBHR9_ADDR 0x1C000498 -#define ALT_CCU_DSU_CAIUGPRAR9_ADDR 0x1C000490 -#define ALT_CCU_DSU_CAIUGPRBLR10_ADDR 0x1C0004A4 -#define ALT_CCU_DSU_CAIUGPRBHR10_ADDR 0x1C0004A8 -#define ALT_CCU_DSU_CAIUGPRAR10_ADDR 0x1C0004A0 -//GIC -#define ALT_CCU_GIC_M_XAIUAMIGR_ADDR 0x1C0023C0 -#define ALT_CCU_GIC_M_XAIUMIFSR_ADDR 0x1C0023C4 -#define ALT_CCU_GIC_M_XAIUGPRBLR1_ADDR 0x1C002414 -#define ALT_CCU_GIC_M_XAIUGPRBHR1_ADDR 0x1C002418 -#define ALT_CCU_GIC_M_XAIUGPRAR1_ADDR 0x1C002410 -#define ALT_CCU_GIC_M_XAIUGPRBLR6_ADDR 0x1C002464 -#define ALT_CCU_GIC_M_XAIUGPRBHR6_ADDR 0x1C002468 -#define ALT_CCU_GIC_M_XAIUGPRAR6_ADDR 0x1C002460 -#define ALT_CCU_GIC_M_XAIUGPRBLR8_ADDR 0x1C002484 -#define ALT_CCU_GIC_M_XAIUGPRBHR8_ADDR 0x1C002488 -#define ALT_CCU_GIC_M_XAIUGPRAR8_ADDR 0x1C002480 -#define ALT_CCU_GIC_M_XAIUGPRBLR10_ADDR 0x1C0024A4 -#define ALT_CCU_GIC_M_XAIUGPRBHR10_ADDR 0x1C0024A8 -#define ALT_CCU_GIC_M_XAIUGPRAR10_ADDR 0x1C0024A0 -//FPGA2SOC -#define ALT_CCU_FPGA2SOC_XAIUAMIGR_ADDR 0x1C0013C0 -#define ALT_CCU_FPGA2SOC_XAIUMIFSR_ADDR 0x1C0013C4 -#define ALT_CCU_FPGA2SOC_XAIUGPRBLR1_ADDR 0x1C001414 -#define ALT_CCU_FPGA2SOC_XAIUGPRBHR1_ADDR 0x1C001418 -#define ALT_CCU_FPGA2SOC_XAIUGPRAR1_ADDR 0x1C001410 -#define ALT_CCU_FPGA2SOC_XAIUGPRBLR6_ADDR 0x1C001464 -#define ALT_CCU_FPGA2SOC_XAIUGPRBHR6_ADDR 0x1C001468 -#define ALT_CCU_FPGA2SOC_XAIUGPRAR6_ADDR 0x1C001460 -#define ALT_CCU_FPGA2SOC_XAIUGPRBLR8_ADDR 0x1C001484 -#define ALT_CCU_FPGA2SOC_XAIUGPRBHR8_ADDR 0x1C001488 -#define ALT_CCU_FPGA2SOC_XAIUGPRAR8_ADDR 0x1C001480 -#define ALT_CCU_FPGA2SOC_XAIUGPRBLR10_ADDR 0x1C0014A4 -#define ALT_CCU_FPGA2SOC_XAIUGPRBHR10_ADDR 0x1C0014A8 -#define ALT_CCU_FPGA2SOC_XAIUGPRAR10_ADDR 0x1C0014A0 -//TCU -#define ALT_CCU_TCU_BASE 0x1C003000 -#define ALT_CCU_TCU_XAIUAMIGR_ADDR ALT_CCU_TCU_BASE + 0x03C0 -#define ALT_CCU_TCU_XAIUMIFSR_ADDR ALT_CCU_TCU_BASE + 0x03C4 -#define ALT_CCU_TCU_XAIUGPRBLR0_ADDR ALT_CCU_TCU_BASE + 0x0404 -#define ALT_CCU_TCU_XAIUGPRBHR0_ADDR ALT_CCU_TCU_BASE + 0x0408 -#define ALT_CCU_TCU_XAIUGPRAR0_ADDR ALT_CCU_TCU_BASE + 0x0400 -#define ALT_CCU_TCU_XAIUGPRBLR1_ADDR ALT_CCU_TCU_BASE + 0x0414 -#define ALT_CCU_TCU_XAIUGPRBHR1_ADDR ALT_CCU_TCU_BASE + 0x0418 -#define ALT_CCU_TCU_XAIUGPRAR1_ADDR ALT_CCU_TCU_BASE + 0x0410 -#define ALT_CCU_TCU_XAIUGPRBLR2_ADDR ALT_CCU_TCU_BASE + 0x0424 -#define ALT_CCU_TCU_XAIUGPRBHR2_ADDR ALT_CCU_TCU_BASE + 0x0428 -#define ALT_CCU_TCU_XAIUGPRAR2_ADDR ALT_CCU_TCU_BASE + 0x0420 -#define ALT_CCU_TCU_XAIUGPRBLR6_ADDR 0x1C003464 -#define ALT_CCU_TCU_XAIUGPRBHR6_ADDR 0x1C003468 -#define ALT_CCU_TCU_XAIUGPRAR6_ADDR 0x1C003460 -#define ALT_CCU_TCU_XAIUGPRBLR8_ADDR 0x1C003484 -#define ALT_CCU_TCU_XAIUGPRBHR8_ADDR 0x1C003488 -#define ALT_CCU_TCU_XAIUGPRAR8_ADDR 0x1C003480 -#define ALT_CCU_TCU_XAIUGPRBLR10_ADDR 0x1C0034A4 -#define ALT_CCU_TCU_XAIUGPRBHR10_ADDR 0x1C0034A8 -#define ALT_CCU_TCU_XAIUGPRAR10_ADDR 0x1C0034A0 -//IOM -#define ALT_CCU_CCU_IOM_XAIUAMIGR_ADDR 0x1C0043C0 -#define ALT_CCU_CCU_IOM_XAIUMIFSR_ADDR 0x1C0013C4 -#define ALT_CCU_IOM_XAIUGPRBLR1_ADDR 0x1C001414 -#define ALT_CCU_IOM_XAIUGPRBHR1_ADDR 0x1C001418 -#define ALT_CCU_IOM_XAIUGPRAR1_ADDR 0x1C001410 -#define ALT_CCU_CCU_IOM_XAIUGPRBLR6_ADDR 0x1C001464 -#define ALT_CCU_CCU_IOM_XAIUGPRBHR6_ADDR 0x1C001468 -#define ALT_CCU_CCU_IOM_XAIUGPRAR6_ADDR 0x1C001460 -#define ALT_CCU_CCU_IOM_XAIUGPRBLR8_ADDR 0x1C001484 -#define ALT_CCU_CCU_IOM_XAIUGPRBHR8_ADDR 0x1C001488 -#define ALT_CCU_CCU_IOM_XAIUGPRAR8_ADDR 0x1C001480 -#define ALT_CCU_CCU_IOM_XAIUGPRBLR10_ADDR 0x1C0014A4 -#define ALT_CCU_CCU_IOM_XAIUGPRBHR10_ADDR 0x1C0014A8 -#define ALT_CCU_CCU_IOM_XAIUGPRAR10_ADDR 0x1C0014A0 -//DCE -#define ALT_CCU_DCE0_DCEUAMIGR_ADDR 0x1C0053C0 -#define ALT_CCU_DCE0_DCEUMIFSR_ADDR 0x1C0053C4 -#define ALT_CCU_DCE0_DCEUGPRBLR6_ADDR 0x1C005464 -#define ALT_CCU_DCE0_DCEUGPRBHR6_ADDR 0x1C005468 -#define ALT_CCU_DCE0_DCEUGPRAR6_ADDR 0x1C005460 -#define ALT_CCU_DCE0_DCEUGPRBLR8_ADDR 0x1C005484 -#define ALT_CCU_DCE0_DCEUGPRBHR8_ADDR 0x1C005488 -#define ALT_CCU_DCE0_DCEUGPRAR8_ADDR 0x1C005480 -#define ALT_CCU_DCE0_DCEUGPRBLR10_ADDR 0x1C0054A4 -#define ALT_CCU_DCE0_DCEUGPRBHR10_ADDR 0x1C0054A8 -#define ALT_CCU_DCE0_DCEUGPRAR10_ADDR 0x1C0054A0 -#define ALT_CCU_DCE1_DCEUAMIGR_ADDR 0x1C0063C0 -#define ALT_CCU_DCE1_DCEUMIFSR_ADDR 0x1C0063C4 -#define ALT_CCU_DCE1_DCEUGPRBLR6_ADDR 0x1C006464 -#define ALT_CCU_DCE1_DCEUGPRBHR6_ADDR 0x1C006468 -#define ALT_CCU_DCE1_DCEUGPRAR6_ADDR 0x1C006460 -#define ALT_CCU_DCE1_DCEUGPRBLR8_ADDR 0x1C006484 -#define ALT_CCU_DCE1_DCEUGPRBHR8_ADDR 0x1C006488 -#define ALT_CCU_DCE1_DCEUGPRAR8_ADDR 0x1C006480 -#define ALT_CCU_DCE1_DCEUGPRBLR10_ADDR 0x1C0064A4 -#define ALT_CCU_DCE1_DCEUGPRBHR10_ADDR 0x1C0064A8 -#define ALT_CCU_DCE1_DCEUGPRAR10_ADDR 0x1C0064A0 -#define offset_NRS_GPRAR0 (0x400) -#define offset_NRS_GPRBLR0 (0x404) -#define offset_NRS_GPRBHR0 (0x408) -#define offset_NRS_GPRAR1 (0x410) -#define offset_NRS_GPRBLR1 (0x414) -#define offset_NRS_GPRBHR1 (0x418) -#define offset_NRS_GPRAR2 (0x420) -#define offset_NRS_GPRBLR2 (0x424) -#define offset_NRS_GPRBHR2 (0x428) -#define offset_NRS_GPRAR3 (0x430) -#define offset_NRS_GPRBLR3 (0x434) -#define offset_NRS_GPRBHR3 (0x438) -#define offset_NRS_GPRAR4 (0x440) -#define offset_NRS_GPRBLR4 (0x444) -#define offset_NRS_GPRBHR4 (0x448) -#define offset_NRS_GPRAR5 (0x450) -#define offset_NRS_GPRBLR5 (0x454) -#define offset_NRS_GPRBHR5 (0x458) -#define offset_NRS_GPRAR6 (0x460) -#define offset_NRS_GPRBLR6 (0x464) -#define offset_NRS_GPRBHR6 (0x468) -#define offset_NRS_GPRAR7 (0x470) -#define offset_NRS_GPRBLR7 (0x474) -#define offset_NRS_GPRBHR7 (0x478) -#define offset_NRS_GPRAR8 (0x480) -#define offset_NRS_GPRBLR8 (0x484) -#define offset_NRS_GPRBHR8 (0x488) -#define offset_NRS_GPRAR9 (0x490) -#define offset_NRS_GPRBLR9 (0x494) -#define offset_NRS_GPRBHR9 (0x498) -#define offset_NRS_GPRAR10 (0x4a0) -#define offset_NRS_GPRBLR10 (0x4a4) -#define offset_NRS_GPRBHR10 (0x4a8) -#define offset_NRS_AMIGR (0x3c0) -#define offset_NRS_MIFSR (0x3c4) -#define offset_NRS_DMIUSMCTCR (0x300) -#define base_addr_DII0_PSSPERIPHS (0x10000) -#define base_addr_DII0_LWHPS2FPGA (0x20000) -#define base_addr_DII0_HPS2FPGA_1G (0x40000) -#define base_addr_DII0_HPS2FPGA_15G (0x400000) -#define base_addr_DII0_HPS2FPGA_240G (0x4000000) -#define base_addr_DII1_MPFEREGS (0x18000) -#define base_addr_DII2_GICREGS (0x1D000) -#define base_addr_DII3_OCRAM (0x0) -#define base_addr_BHR (0x0) -#define base_addr_DMI_SDRAM_2G (0x80000) -#define base_addr_DMI_SDRAM_30G (0x800000) -#define base_addr_DMI_SDRAM_480G (0x8000000) + +/* Macros */ +#define CCU_OFFSET_VAL_MASK 3U +#define CCU_WORD_BYTE 4U + +// Address Map for CCU Init +#define addr_CAIUIDR1 SOCFPGA_CCU_NOC_REG_BASE + 0x00000 +#define addr_GRBUNRRUCR SOCFPGA_CCU_NOC_REG_BASE + 0xFFFF8 +#define base_addr_NRS_CAIU0 SOCFPGA_CCU_NOC_REG_BASE + 0x00000 +#define base_addr_NRS_NCAIU0 SOCFPGA_CCU_NOC_REG_BASE + 0x01000 +#define base_addr_NRS_NCAIU1 SOCFPGA_CCU_NOC_REG_BASE + 0x02000 +#define base_addr_NRS_NCAIU2 SOCFPGA_CCU_NOC_REG_BASE + 0x03000 +#define base_addr_NRS_NCAIU3 SOCFPGA_CCU_NOC_REG_BASE + 0x04000 +#define base_addr_NRS_DCE0 SOCFPGA_CCU_NOC_REG_BASE + 0x05000 +#define base_addr_NRS_DCE1 SOCFPGA_CCU_NOC_REG_BASE + 0x06000 +//#define base_addr_NRS_DMI0 SOCFPGA_CCU_NOC_REG_BASE + 0x07000 +//#define base_addr_NRS_DMI1 SOCFPGA_CCU_NOC_REG_BASE + 0x08000 + +/* DMI */ +#define ALT_CCU_CCU_DMI0_DMIUSMCTCR_ADDR SOCFPGA_CCU_NOC_REG_BASE + 0x7300 +#define ALT_CCU_CCU_DMI1_DMIUSMCTCR_ADDR SOCFPGA_CCU_NOC_REG_BASE + 0x8300 + +/* DSU */ +#define ALT_CCU_DSU_CAIUAMIGR_ADDR SOCFPGA_CCU_NOC_REG_BASE + 0x3C0 +#define ALT_CCU_DSU_CAIUMIFSR_ADDR SOCFPGA_CCU_NOC_REG_BASE + 0x3C4 +#define ALT_CCU_DSU_CAIUGPRBLR1_ADDR SOCFPGA_CCU_NOC_REG_BASE + 0x414 +#define ALT_CCU_DSU_CAIUGPRBHR1_ADDR SOCFPGA_CCU_NOC_REG_BASE + 0x418 +#define ALT_CCU_DSU_CAIUGPRAR1_ADDR SOCFPGA_CCU_NOC_REG_BASE + 0x410 +#define ALT_CCU_DSU_CAIUGPRBLR2_ADDR SOCFPGA_CCU_NOC_REG_BASE + 0x424 +#define ALT_CCU_DSU_CAIUGPRBHR2_ADDR SOCFPGA_CCU_NOC_REG_BASE + 0x428 +#define ALT_CCU_DSU_CAIUGPRAR2_ADDR SOCFPGA_CCU_NOC_REG_BASE + 0x420 +#define ALT_CCU_DSU_CAIUGPRBLR4_ADDR SOCFPGA_CCU_NOC_REG_BASE + 0x444 +#define ALT_CCU_DSU_CAIUGPRBHR4_ADDR SOCFPGA_CCU_NOC_REG_BASE + 0x448 +#define ALT_CCU_DSU_CAIUGPRAR4_ADDR SOCFPGA_CCU_NOC_REG_BASE + 0x440 +#define ALT_CCU_DSU_CAIUGPRBLR5_ADDR SOCFPGA_CCU_NOC_REG_BASE + 0x454 +#define ALT_CCU_DSU_CAIUGPRBHR5_ADDR SOCFPGA_CCU_NOC_REG_BASE + 0x458 +#define ALT_CCU_DSU_CAIUGPRAR5_ADDR SOCFPGA_CCU_NOC_REG_BASE + 0x450 +#define ALT_CCU_DSU_CAIUGPRBLR6_ADDR SOCFPGA_CCU_NOC_REG_BASE + 0x464 +#define ALT_CCU_DSU_CAIUGPRBHR6_ADDR SOCFPGA_CCU_NOC_REG_BASE + 0x468 +#define ALT_CCU_DSU_CAIUGPRAR6_ADDR SOCFPGA_CCU_NOC_REG_BASE + 0x460 +#define ALT_CCU_DSU_CAIUGPRBLR7_ADDR SOCFPGA_CCU_NOC_REG_BASE + 0x474 +#define ALT_CCU_DSU_CAIUGPRBHR7_ADDR SOCFPGA_CCU_NOC_REG_BASE + 0x478 +#define ALT_CCU_DSU_CAIUGPRAR7_ADDR SOCFPGA_CCU_NOC_REG_BASE + 0x470 +#define ALT_CCU_DSU_CAIUGPRBLR8_ADDR SOCFPGA_CCU_NOC_REG_BASE + 0x484 +#define ALT_CCU_DSU_CAIUGPRBHR8_ADDR SOCFPGA_CCU_NOC_REG_BASE + 0x488 +#define ALT_CCU_DSU_CAIUGPRAR8_ADDR SOCFPGA_CCU_NOC_REG_BASE + 0x480 +#define ALT_CCU_DSU_CAIUGPRBLR9_ADDR SOCFPGA_CCU_NOC_REG_BASE + 0x494 +#define ALT_CCU_DSU_CAIUGPRBHR9_ADDR SOCFPGA_CCU_NOC_REG_BASE + 0x498 +#define ALT_CCU_DSU_CAIUGPRAR9_ADDR SOCFPGA_CCU_NOC_REG_BASE + 0x490 +#define ALT_CCU_DSU_CAIUGPRBLR10_ADDR SOCFPGA_CCU_NOC_REG_BASE + 0x4A4 +#define ALT_CCU_DSU_CAIUGPRBHR10_ADDR SOCFPGA_CCU_NOC_REG_BASE + 0x4A8 +#define ALT_CCU_DSU_CAIUGPRAR10_ADDR SOCFPGA_CCU_NOC_REG_BASE + 0x4A0 + +/* GIC */ +#define ALT_CCU_GIC_M_XAIUAMIGR_ADDR SOCFPGA_CCU_NOC_REG_BASE + 0x23C0 +#define ALT_CCU_GIC_M_XAIUMIFSR_ADDR SOCFPGA_CCU_NOC_REG_BASE + 0x23C4 +#define ALT_CCU_GIC_M_XAIUGPRBLR1_ADDR SOCFPGA_CCU_NOC_REG_BASE + 0x2414 +#define ALT_CCU_GIC_M_XAIUGPRBHR1_ADDR SOCFPGA_CCU_NOC_REG_BASE + 0x2418 +#define ALT_CCU_GIC_M_XAIUGPRAR1_ADDR SOCFPGA_CCU_NOC_REG_BASE + 0x2410 +#define ALT_CCU_GIC_M_XAIUGPRBLR6_ADDR SOCFPGA_CCU_NOC_REG_BASE + 0x2464 +#define ALT_CCU_GIC_M_XAIUGPRBHR6_ADDR SOCFPGA_CCU_NOC_REG_BASE + 0x2468 +#define ALT_CCU_GIC_M_XAIUGPRAR6_ADDR SOCFPGA_CCU_NOC_REG_BASE + 0x2460 +#define ALT_CCU_GIC_M_XAIUGPRBLR8_ADDR SOCFPGA_CCU_NOC_REG_BASE + 0x2484 +#define ALT_CCU_GIC_M_XAIUGPRBHR8_ADDR SOCFPGA_CCU_NOC_REG_BASE + 0x2488 +#define ALT_CCU_GIC_M_XAIUGPRAR8_ADDR SOCFPGA_CCU_NOC_REG_BASE + 0x2480 +#define ALT_CCU_GIC_M_XAIUGPRBLR10_ADDR SOCFPGA_CCU_NOC_REG_BASE + 0x24A4 +#define ALT_CCU_GIC_M_XAIUGPRBHR10_ADDR SOCFPGA_CCU_NOC_REG_BASE + 0x24A8 +#define ALT_CCU_GIC_M_XAIUGPRAR10_ADDR SOCFPGA_CCU_NOC_REG_BASE + 0x24A0 + +/* FPGA2SOC */ +#define ALT_CCU_FPGA2SOC_BASE SOCFPGA_CCU_NOC_REG_BASE + 0x1000 +#define ALT_CCU_FPGA2SOC_XAIUAMIGR_ADDR SOCFPGA_CCU_NOC_REG_BASE + 0x13C0 +#define ALT_CCU_FPGA2SOC_XAIUMIFSR_ADDR SOCFPGA_CCU_NOC_REG_BASE + 0x13C4 +#define ALT_CCU_FPGA2SOC_XAIUGPRBLR1_ADDR SOCFPGA_CCU_NOC_REG_BASE + 0x1414 +#define ALT_CCU_FPGA2SOC_XAIUGPRBHR1_ADDR SOCFPGA_CCU_NOC_REG_BASE + 0x1418 +#define ALT_CCU_FPGA2SOC_XAIUGPRAR1_ADDR SOCFPGA_CCU_NOC_REG_BASE + 0x1410 +#define ALT_CCU_FPGA2SOC_XAIUGPRBLR6_ADDR SOCFPGA_CCU_NOC_REG_BASE + 0x1464 +#define ALT_CCU_FPGA2SOC_XAIUGPRBHR6_ADDR SOCFPGA_CCU_NOC_REG_BASE + 0x1468 +#define ALT_CCU_FPGA2SOC_XAIUGPRAR6_ADDR SOCFPGA_CCU_NOC_REG_BASE + 0x1460 +#define ALT_CCU_FPGA2SOC_XAIUGPRBLR8_ADDR SOCFPGA_CCU_NOC_REG_BASE + 0x1484 +#define ALT_CCU_FPGA2SOC_XAIUGPRBHR8_ADDR SOCFPGA_CCU_NOC_REG_BASE + 0x1488 +#define ALT_CCU_FPGA2SOC_XAIUGPRAR8_ADDR SOCFPGA_CCU_NOC_REG_BASE + 0x1480 +#define ALT_CCU_FPGA2SOC_XAIUGPRBLR10_ADDR SOCFPGA_CCU_NOC_REG_BASE + 0x14A4 +#define ALT_CCU_FPGA2SOC_XAIUGPRBHR10_ADDR SOCFPGA_CCU_NOC_REG_BASE + 0x14A8 +#define ALT_CCU_FPGA2SOC_XAIUGPRAR10_ADDR SOCFPGA_CCU_NOC_REG_BASE + 0x14A0 + +/* TCU */ +#define ALT_CCU_TCU_XAIUAMIGR_ADDR SOCFPGA_CCU_NOC_REG_BASE + 0x33C0 +#define ALT_CCU_TCU_XAIUMIFSR_ADDR SOCFPGA_CCU_NOC_REG_BASE + 0x33C4 +#define ALT_CCU_TCU_XAIUGPRBLR0_ADDR SOCFPGA_CCU_NOC_REG_BASE + 0x3404 +#define ALT_CCU_TCU_XAIUGPRBHR0_ADDR SOCFPGA_CCU_NOC_REG_BASE + 0x3408 +#define ALT_CCU_TCU_XAIUGPRAR0_ADDR SOCFPGA_CCU_NOC_REG_BASE + 0x3400 +#define ALT_CCU_TCU_XAIUGPRBLR1_ADDR SOCFPGA_CCU_NOC_REG_BASE + 0x3414 +#define ALT_CCU_TCU_XAIUGPRBHR1_ADDR SOCFPGA_CCU_NOC_REG_BASE + 0x3418 +#define ALT_CCU_TCU_XAIUGPRAR1_ADDR SOCFPGA_CCU_NOC_REG_BASE + 0x3410 +#define ALT_CCU_TCU_XAIUGPRBLR2_ADDR SOCFPGA_CCU_NOC_REG_BASE + 0x3424 +#define ALT_CCU_TCU_XAIUGPRBHR2_ADDR SOCFPGA_CCU_NOC_REG_BASE + 0x3428 +#define ALT_CCU_TCU_XAIUGPRAR2_ADDR SOCFPGA_CCU_NOC_REG_BASE + 0x3420 +#define ALT_CCU_TCU_XAIUGPRBLR6_ADDR SOCFPGA_CCU_NOC_REG_BASE + 0x3464 +#define ALT_CCU_TCU_XAIUGPRBHR6_ADDR SOCFPGA_CCU_NOC_REG_BASE + 0x3468 +#define ALT_CCU_TCU_XAIUGPRAR6_ADDR SOCFPGA_CCU_NOC_REG_BASE + 0x3460 +#define ALT_CCU_TCU_XAIUGPRBLR8_ADDR SOCFPGA_CCU_NOC_REG_BASE + 0x3484 +#define ALT_CCU_TCU_XAIUGPRBHR8_ADDR SOCFPGA_CCU_NOC_REG_BASE + 0x3488 +#define ALT_CCU_TCU_XAIUGPRAR8_ADDR SOCFPGA_CCU_NOC_REG_BASE + 0x3480 +#define ALT_CCU_TCU_XAIUGPRBLR10_ADDR SOCFPGA_CCU_NOC_REG_BASE + 0x34A4 +#define ALT_CCU_TCU_XAIUGPRBHR10_ADDR SOCFPGA_CCU_NOC_REG_BASE + 0x34A8 +#define ALT_CCU_TCU_XAIUGPRAR10_ADDR SOCFPGA_CCU_NOC_REG_BASE + 0x34A0 + +/* IOM */ +#define ALT_CCU_CCU_IOM_XAIUAMIGR_ADDR SOCFPGA_CCU_NOC_REG_BASE + 0x43C0 +#define ALT_CCU_CCU_IOM_XAIUMIFSR_ADDR SOCFPGA_CCU_NOC_REG_BASE + 0x13C4 +#define ALT_CCU_IOM_XAIUGPRBLR1_ADDR SOCFPGA_CCU_NOC_REG_BASE + 0x1414 +#define ALT_CCU_IOM_XAIUGPRBHR1_ADDR SOCFPGA_CCU_NOC_REG_BASE + 0x1418 +#define ALT_CCU_IOM_XAIUGPRAR1_ADDR SOCFPGA_CCU_NOC_REG_BASE + 0x1410 +#define ALT_CCU_CCU_IOM_XAIUGPRBLR6_ADDR SOCFPGA_CCU_NOC_REG_BASE + 0x1464 +#define ALT_CCU_CCU_IOM_XAIUGPRBHR6_ADDR SOCFPGA_CCU_NOC_REG_BASE + 0x1468 +#define ALT_CCU_CCU_IOM_XAIUGPRAR6_ADDR SOCFPGA_CCU_NOC_REG_BASE + 0x1460 +#define ALT_CCU_CCU_IOM_XAIUGPRBLR8_ADDR SOCFPGA_CCU_NOC_REG_BASE + 0x1484 +#define ALT_CCU_CCU_IOM_XAIUGPRBHR8_ADDR SOCFPGA_CCU_NOC_REG_BASE + 0x1488 +#define ALT_CCU_CCU_IOM_XAIUGPRAR8_ADDR SOCFPGA_CCU_NOC_REG_BASE + 0x1480 +#define ALT_CCU_CCU_IOM_XAIUGPRBLR10_ADDR SOCFPGA_CCU_NOC_REG_BASE + 0x14A4 +#define ALT_CCU_CCU_IOM_XAIUGPRBHR10_ADDR SOCFPGA_CCU_NOC_REG_BASE + 0x14A8 +#define ALT_CCU_CCU_IOM_XAIUGPRAR10_ADDR SOCFPGA_CCU_NOC_REG_BASE + 0x14A0 + +/* DCE */ +#define ALT_CCU_DCE0_DCEUAMIGR_ADDR SOCFPGA_CCU_NOC_REG_BASE + 0x53C0 +#define ALT_CCU_DCE0_DCEUMIFSR_ADDR SOCFPGA_CCU_NOC_REG_BASE + 0x53C4 +#define ALT_CCU_DCE0_DCEUGPRBLR6_ADDR SOCFPGA_CCU_NOC_REG_BASE + 0x5464 +#define ALT_CCU_DCE0_DCEUGPRBHR6_ADDR SOCFPGA_CCU_NOC_REG_BASE + 0x5468 +#define ALT_CCU_DCE0_DCEUGPRAR6_ADDR SOCFPGA_CCU_NOC_REG_BASE + 0x5460 +#define ALT_CCU_DCE0_DCEUGPRBLR8_ADDR SOCFPGA_CCU_NOC_REG_BASE + 0x5484 +#define ALT_CCU_DCE0_DCEUGPRBHR8_ADDR SOCFPGA_CCU_NOC_REG_BASE + 0x5488 +#define ALT_CCU_DCE0_DCEUGPRAR8_ADDR SOCFPGA_CCU_NOC_REG_BASE + 0x5480 +#define ALT_CCU_DCE0_DCEUGPRBLR10_ADDR SOCFPGA_CCU_NOC_REG_BASE + 0x54A4 +#define ALT_CCU_DCE0_DCEUGPRBHR10_ADDR SOCFPGA_CCU_NOC_REG_BASE + 0x54A8 +#define ALT_CCU_DCE0_DCEUGPRAR10_ADDR SOCFPGA_CCU_NOC_REG_BASE + 0x54A0 +#define ALT_CCU_DCE1_DCEUAMIGR_ADDR SOCFPGA_CCU_NOC_REG_BASE + 0x63C0 +#define ALT_CCU_DCE1_DCEUMIFSR_ADDR SOCFPGA_CCU_NOC_REG_BASE + 0x63C4 +#define ALT_CCU_DCE1_DCEUGPRBLR6_ADDR SOCFPGA_CCU_NOC_REG_BASE + 0x6464 +#define ALT_CCU_DCE1_DCEUGPRBHR6_ADDR SOCFPGA_CCU_NOC_REG_BASE + 0x6468 +#define ALT_CCU_DCE1_DCEUGPRAR6_ADDR SOCFPGA_CCU_NOC_REG_BASE + 0x6460 +#define ALT_CCU_DCE1_DCEUGPRBLR8_ADDR SOCFPGA_CCU_NOC_REG_BASE + 0x6484 +#define ALT_CCU_DCE1_DCEUGPRBHR8_ADDR SOCFPGA_CCU_NOC_REG_BASE + 0x6488 +#define ALT_CCU_DCE1_DCEUGPRAR8_ADDR SOCFPGA_CCU_NOC_REG_BASE + 0x6480 +#define ALT_CCU_DCE1_DCEUGPRBLR10_ADDR SOCFPGA_CCU_NOC_REG_BASE + 0x64A4 +#define ALT_CCU_DCE1_DCEUGPRBHR10_ADDR SOCFPGA_CCU_NOC_REG_BASE + 0x64A8 +#define ALT_CCU_DCE1_DCEUGPRAR10_ADDR SOCFPGA_CCU_NOC_REG_BASE + 0x64A0 +#define offset_NRS_GPRAR0 0x400 +#define offset_NRS_GPRBLR0 0x404 +#define offset_NRS_GPRBHR0 0x408 +#define offset_NRS_GPRAR1 0x410 +#define offset_NRS_GPRBLR1 0x414 +#define offset_NRS_GPRBHR1 0x418 +#define offset_NRS_GPRAR2 0x420 +#define offset_NRS_GPRBLR2 0x424 +#define offset_NRS_GPRBHR2 0x428 +#define offset_NRS_GPRAR3 0x430 +#define offset_NRS_GPRBLR3 0x434 +#define offset_NRS_GPRBHR3 0x438 +#define offset_NRS_GPRAR4 0x440 +#define offset_NRS_GPRBLR4 0x444 +#define offset_NRS_GPRBHR4 0x448 +#define offset_NRS_GPRAR5 0x450 +#define offset_NRS_GPRBLR5 0x454 +#define offset_NRS_GPRBHR5 0x458 +#define offset_NRS_GPRAR6 0x460 +#define offset_NRS_GPRBLR6 0x464 +#define offset_NRS_GPRBHR6 0x468 +#define offset_NRS_GPRAR7 0x470 +#define offset_NRS_GPRBLR7 0x474 +#define offset_NRS_GPRBHR7 0x478 +#define offset_NRS_GPRAR8 0x480 +#define offset_NRS_GPRBLR8 0x484 +#define offset_NRS_GPRBHR8 0x488 +#define offset_NRS_GPRAR9 0x490 +#define offset_NRS_GPRBLR9 0x494 +#define offset_NRS_GPRBHR9 0x498 +#define offset_NRS_GPRAR10 0x4A0 +#define offset_NRS_GPRBLR10 0x4A4 +#define offset_NRS_GPRBHR10 0x4A8 +#define offset_NRS_AMIGR 0x3C0 +#define offset_NRS_MIFSR 0x3C4 +#define offset_NRS_DMIUSMCTCR 0x300 +#define base_addr_DII0_PSSPERIPHS 0x10000 +#define base_addr_DII0_LWHPS2FPGA 0x20000 +#define base_addr_DII0_HPS2FPGA_1G 0x40000 +#define base_addr_DII0_HPS2FPGA_15G 0x400000 +#define base_addr_DII0_HPS2FPGA_240G 0x4000000 +#define base_addr_DII1_MPFEREGS 0x18000 +#define base_addr_DII2_GICREGS 0x1D000 +#define base_addr_DII3_OCRAM 0x0 +#define base_addr_BHR 0x0 +#define base_addr_DMI_SDRAM_2G 0x80000 +#define base_addr_DMI_SDRAM_30G 0x800000 +#define base_addr_DMI_SDRAM_480G 0x8000000 // ((0x0<<9) | (0xf<<20) | (0x1<<30) | (0x1<<31)) #define wr_DII0_PSSPERIPHS 0xC0F00000 // ((0x0<<9) | (0x11<<20) | (0x1<<30) | (0x1<<31)) @@ -228,54 +243,46 @@ // ((0x1<<1) | (0x1<<2) | (0x0<<9) | (0x17<<20) | (0x0<<30) | (0x1<<31)) #define wr_DMI_SDRAM_30G 0x81700006 // ((0x0<<9) | (0x1a<<20) | (0x0<<30) | (0x1<<31)) -#define wr_DMI_SDRAM_240G_ORDERED 0x81a00000 +#define wr_DMI_SDRAM_240G_ORDERED 0x81A00000 // ((0x1<<1) | (0x1<<2) | (0x0<<9) | (0x1a<<20) | (0x0<<30) | (0x1<<31)) -#define wr_DMI_SDRAM_240G 0x81a00006 +#define wr_DMI_SDRAM_240G 0x81A00006 // ((0x0<<9) | (0x1b<<20) | (0x0<<30) | (0x1<<31)) -#define wr_DMI_SDRAM_480G_ORDERED 0x81b00000 +#define wr_DMI_SDRAM_480G_ORDERED 0x81B00000 // ((0x1<<1) | (0x1<<2) | (0x0<<9) | (0x1b<<20) | (0x0<<30) | (0x1<<31)) -#define wr_DMI_SDRAM_480G 0x81b00006 +#define wr_DMI_SDRAM_480G 0x81B00006 typedef enum CCU_REGION_SECURITY_e { - // - // Allow secure accesses only. - // + /* Allow secure accesses only. */ CCU_REGION_SECURITY_SECURE_ONLY, - // - // Allow non-secure accesses only. - // + + /* Allow non-secure accesses only. */ CCU_REGION_SECURITY_NON_SECURE_ONLY, - // - // Allow accesses of any security state. - // + + /* Allow accesses of any security state. */ CCU_REGION_SECURITY_DONT_CARE } CCU_REGION_SECURITY_t; + typedef enum CCU_REGION_PRIVILEGE_e { - // - // Allow privileged accesses only. - // + /* Allow privileged accesses only. */ CCU_REGION_PRIVILEGE_PRIVILEGED_ONLY, - // - // Allow unprivileged accesses only. - // + /* Allow unprivileged accesses only. */ CCU_REGION_PRIVILEGE_NON_PRIVILEGED_ONLY, - // - // Allow accesses of any privilege. - // + /* Allow accesses of any privilege. */ CCU_REGION_PRIVILEGE_DONT_CARE } CCU_REGION_PRIVILEGE_t; -// -// Initializes the CCU by enabling all regions except RAM 1 - 5. -// This is needed because of an RTL change around 2016.02.24. -// -// Runtime measurement: -// - arm : 14,830,000 ps (2016.05.31; sanity/printf_aarch32) -// - aarch64 : 14,837,500 ps (2016.05.31; sanity/printf) -// -// Runtime history: -// - arm : 20,916,668 ps (2016.05.30; sanity/printf_aarch32) -// - aarch64 : 20,924,168 ps (2016.05.30; sanity/printf) -// + +/* + * Initializes the CCU by enabling all regions except RAM 1 - 5. + * This is needed because of an RTL change around 2016.02.24. + * + * Runtime measurement: + * - arm : 14,830,000 ps (2016.05.31; sanity/printf_aarch32) + * - aarch64 : 14,837,500 ps (2016.05.31; sanity/printf) + * + * Runtime history: + * - arm : 20,916,668 ps (2016.05.30; sanity/printf_aarch32) + * - aarch64 : 20,924,168 ps (2016.05.30; sanity/printf) + */ int ccu_hps_init(void); typedef enum ccu_hps_ram_region_e { @@ -287,19 +294,21 @@ typedef enum ccu_hps_ram_region_e { ccu_hps_ram_region_ramspace5 = 5, } ccu_hps_ram_region_t; -// Disables a RAM (OCRAM) region with the given ID. +/* Disables a RAM (OCRAM) region with the given ID. */ int ccu_hps_ram_region_disable(int id); -// Enables a RAM (OCRAM) region with the given ID. +/* Enables a RAM (OCRAM) region with the given ID. */ int ccu_hps_ram_region_enable(int id); -// Attempts to remap a RAM (OCRAM) region with the given ID to span the given -// start and end address. It also assigns the security and privilege policy. -// Regions must be a power-of-two size with a minimum size of 64B. +/* + * Attempts to remap a RAM (OCRAM) region with the given ID to span the given + * start and end address. It also assigns the security and privilege policy. + * Regions must be a power-of-two size with a minimum size of 64B. + */ int ccu_hps_ram_region_remap(int id, uintptr_t start, uintptr_t end, -CCU_REGION_SECURITY_t security, CCU_REGION_PRIVILEGE_t privilege); + CCU_REGION_SECURITY_t security, CCU_REGION_PRIVILEGE_t privilege); -// Verifies that all enabled RAM (OCRAM) regions does not overlap. +/* Verifies that all enabled RAM (OCRAM) regions does not overlap. */ int ccu_hps_ram_validate(void); typedef enum ccu_hps_mem_region_e { @@ -312,19 +321,21 @@ typedef enum ccu_hps_mem_region_e { ccu_hps_mem_region_memspace1e = 6, } ccu_hps_mem_region_t; -// Disables mem0 (DDR) region with the given ID. +/* Disables mem0 (DDR) region with the given ID. */ int ccu_hps_mem0_region_disable(int id); -// Enables mem0 (DDR) region with the given ID. +/* Enables mem0 (DDR) region with the given ID. */ int ccu_hps_mem0_region_enable(int id); -// Attempts to remap mem0 (DDR) region with the given ID to span the given -// start and end address. It also assigns the security nad privlege policy. -// Regions must be a power-of-two in size with a minimum size of 64B. +/* + * Attempts to remap mem0 (DDR) region with the given ID to span the given + * start and end address. It also assigns the security nad privlege policy. + * Regions must be a power-of-two in size with a minimum size of 64B. + */ int ccu_hps_mem0_region_remap(int id, uintptr_t start, uintptr_t end, -CCU_REGION_SECURITY_t security, CCU_REGION_PRIVILEGE_t privilege); + CCU_REGION_SECURITY_t security, CCU_REGION_PRIVILEGE_t privilege); -// Verifies that all enabled mem0 (DDR) regions does not overlap. +/* Verifies that all enabled mem0 (DDR) regions does not overlap. */ int ccu_hps_mem0_validate(void); typedef enum ccu_hps_ios_region_e { @@ -342,14 +353,23 @@ typedef enum ccu_hps_ios_region_e { ccu_hps_ios_region_iospace2c = 11, } ccu_hps_ios_region_t; -// Disables the IOS (IO Slave) region with the given ID. +/* Disables the IOS (IO Slave) region with the given ID. */ int ccu_hps_ios_region_disable(int id); -// Enables the IOS (IO Slave) region with the given ID. +/* Enables the IOS (IO Slave) region with the given ID. */ int ccu_hps_ios_region_enable(int id); +typedef struct ncore_ccu_reg { + char name[50]; + uint32_t base; + uint32_t size; + } ncore_ccu_reg_t; -#define NCORE_CCU_OFFSET 0xf7000000 +typedef struct ncore_ccu { + uint32_t offset; + uint32_t val; + uint32_t mask; + } ncore_ccu_t; /* Coherent Sub-System Address Map */ #define NCORE_CAIU_OFFSET 0x00000 @@ -358,43 +378,49 @@ int ccu_hps_ios_region_enable(int id); #define NCORE_NCBU_SIZE 0x01000 #define NCORE_DIRU_OFFSET 0x80000 #define NCORE_DIRU_SIZE 0x01000 -#define NCORE_CMIU_OFFSET 0xc0000 +#define NCORE_CMIU_OFFSET 0xC0000 #define NCORE_CMIU_SIZE 0x01000 -#define NCORE_CSR_OFFSET 0xff000 +#define NCORE_CSR_OFFSET 0xFF000 #define NCORE_CSADSERO 0x00040 -#define NCORE_CSUIDR 0x00ff8 -#define NCORE_CSIDR 0x00ffc +#define NCORE_CSUIDR 0x00FF8 +#define NCORE_CSIDR 0x00FFC + /* Directory Unit Register Map */ #define NCORE_DIRUSFER 0x00010 #define NCORE_DIRUMRHER 0x00070 #define NCORE_DIRUSFMCR 0x00080 #define NCORE_DIRUSFMAR 0x00084 + /* Coherent Agent Interface Unit Register Map */ -#define NCORE_CAIUIDR 0x00ffc +#define NCORE_CAIUIDR 0x00FFC + /* Snoop Enable Register */ #define NCORE_DIRUCASER0 0x00040 #define NCORE_DIRUCASER1 0x00044 #define NCORE_DIRUCASER2 0x00048 -#define NCORE_DIRUCASER3 0x0004c +#define NCORE_DIRUCASER3 0x0004C #define NCORE_CSADSER0 0x00040 #define NCORE_CSADSER1 0x00044 #define NCORE_CSADSER2 0x00048 -#define NCORE_CSADSER3 0x0004c +#define NCORE_CSADSER3 0x0004C + /* Protocols Definition */ #define ACE_W_DVM 0 #define ACE_L_W_DVM 1 #define ACE_WO_DVM 2 #define ACE_L_WO_DVM 3 -/* Bypass OC Ram Firewall */ + +/* Bypass OCRAM Firewall */ #define NCORE_FW_OCRAM_BLK_BASE 0x100200 #define NCORE_FW_OCRAM_BLK_CGF1 0x04 #define NCORE_FW_OCRAM_BLK_CGF2 0x08 -#define NCORE_FW_OCRAM_BLK_CGF3 0x0c +#define NCORE_FW_OCRAM_BLK_CGF3 0x0C #define NCORE_FW_OCRAM_BLK_CGF4 0x10 #define OCRAM_PRIVILEGED_MASK BIT(29) #define OCRAM_SECURE_MASK BIT(30) + /* Macros */ -#define NCORE_CCU_REG(base) (NCORE_CCU_OFFSET + (base)) +#define NCORE_CCU_REG(base) (SOCFPGA_CCU_NOC_REG_BASE + (base)) #define NCORE_CCU_CSR(reg) (NCORE_CCU_REG(NCORE_CSR_OFFSET)\ + (reg)) #define NCORE_CCU_DIR(reg) (NCORE_CCU_REG(NCORE_DIRU_OFFSET)\ @@ -407,14 +433,14 @@ int ccu_hps_ios_region_enable(int id); + NCORE_CAIU_SIZE * (x)) #define COH_CPU0_BYPASS_REG(reg) (NCORE_CCU_REG(NCORE_FW_OCRAM_BLK_BASE)\ + (reg)) -#define CSUIDR_NUM_CMI(x) (((x) & 0x3f000000) >> 24) -#define CSUIDR_NUM_DIR(x) (((x) & 0x003f0000) >> 16) -#define CSUIDR_NUM_NCB(x) (((x) & 0x00003f00) >> 8) -#define CSUIDR_NUM_CAI(x) (((x) & 0x0000007f) >> 0) -#define CSIDR_NUM_SF(x) (((x) & 0x007c0000) >> 18) +#define CSUIDR_NUM_CMI(x) (((x) & 0x3F000000) >> 24) +#define CSUIDR_NUM_DIR(x) (((x) & 0x003F0000) >> 16) +#define CSUIDR_NUM_NCB(x) (((x) & 0x00003F00) >> 8) +#define CSUIDR_NUM_CAI(x) (((x) & 0x0000007F) >> 0) +#define CSIDR_NUM_SF(x) (((x) & 0x007C0000) >> 18) #define SNOOP_FILTER_ID(x) (((x) << 16)) #define CACHING_AGENT_BIT(x) (((x) & 0x08000) >> 15) -#define CACHING_AGENT_TYPE(x) (((x) & 0xf0000) >> 16) +#define CACHING_AGENT_TYPE(x) (((x) & 0xF0000) >> 16) typedef struct coh_ss_id { uint8_t num_coh_mem; @@ -427,5 +453,6 @@ typedef struct coh_ss_id { uint32_t init_ncore_ccu(void); void ncore_enable_ocram_firewall(void); void setup_smmu_stream_id(void); +int flush_l3_dcache(void); #endif diff --git a/plat/intel/soc/common/drivers/ddr/ddr.c b/plat/intel/soc/common/drivers/ddr/ddr.c index 188302f7..62f03ef2 100644 --- a/plat/intel/soc/common/drivers/ddr/ddr.c +++ b/plat/intel/soc/common/drivers/ddr/ddr.c @@ -7,6 +7,7 @@ #include <assert.h> #include <errno.h> #include <common/debug.h> +#include <drivers/delay_timer.h> #include "ddr.h" #include <lib/mmio.h> #include "socfpga_handoff.h" @@ -340,3 +341,143 @@ int ddr_init(void) NOTICE("DDR init successfully\n"); return status; } + +int ddr_config_scrubber(phys_addr_t umctl2_base, enum ddr_type umctl2_type) +{ + uint32_t temp[9] = {0}; + int ret = 0; + + /* Write default value to prevent scrubber stop due to lower power */ + mmio_write_32(0, umctl2_base + DDR4_PWRCTL_OFFSET); + + /* To backup user configurations in temp array */ + temp[0] = mmio_read_32(umctl2_base + DDR4_SBRCTL_OFFSET); + temp[1] = mmio_read_32(umctl2_base + DDR4_SBRWDATA0_OFFSET); + temp[2] = mmio_read_32(umctl2_base + DDR4_SBRSTART0_OFFSET); + if (umctl2_type == DDR_TYPE_DDR4) { + temp[3] = mmio_read_32(umctl2_base + DDR4_SBRWDATA1_OFFSET); + temp[4] = mmio_read_32(umctl2_base + DDR4_SBRSTART1_OFFSET); + } + temp[5] = mmio_read_32(umctl2_base + DDR4_SBRRANGE0_OFFSET); + temp[6] = mmio_read_32(umctl2_base + DDR4_SBRRANGE1_OFFSET); + temp[7] = mmio_read_32(umctl2_base + DDR4_ECCCFG0_OFFSET); + temp[8] = mmio_read_32(umctl2_base + DDR4_ECCCFG1_OFFSET); + + if (umctl2_type != DDR_TYPE_DDR4) { + /* Lock ECC region, ensure this regions is not being accessed */ + mmio_setbits_32(umctl2_base + DDR4_ECCCFG1_OFFSET, + LPDDR4_ECCCFG1_ECC_REGIONS_PARITY_LOCK); + } + /* Disable input traffic per port */ + mmio_clrbits_32(umctl2_base + DDR4_PCTRL0_OFFSET, DDR4_PCTRL0_PORT_EN); + /* Disables scrubber */ + mmio_clrbits_32(umctl2_base + DDR4_SBRCTL_OFFSET, DDR4_SBRCTL_SCRUB_EN); + /* Polling all scrub writes data have been sent */ + ret = poll_idle_status((umctl2_base + DDR4_SBRSTAT_OFFSET), + DDR4_SBRSTAT_SCRUB_BUSY, true, 5000); + + if (ret) { + INFO("%s: Timeout while waiting for", __func__); + INFO(" sending all scrub data\n"); + return ret; + } + + /* LPDDR4 supports inline ECC only */ + if (umctl2_type != DDR_TYPE_DDR4) { + /* + * Setting all regions for protected, this is required for + * srubber to init whole LPDDR4 expect ECC region + */ + mmio_write_32(((ONE_EIGHT << + LPDDR4_ECCCFG0_ECC_REGION_MAP_GRANU_SHIFT) | + (ALL_PROTECTED << LPDDR4_ECCCFG0_ECC_REGION_MAP_SHIFT)), + umctl2_base + DDR4_ECCCFG0_OFFSET); + } + + /* Scrub_burst = 1, scrub_mode = 1(performs writes) */ + mmio_write_32(DDR4_SBRCTL_SCRUB_BURST_1 | DDR4_SBRCTL_SCRUB_WRITE, + umctl2_base + DDR4_SBRCTL_OFFSET); + + /* Wipe DDR content after calibration */ + ret = ddr_zerofill_scrubber(umctl2_base, umctl2_type); + if (ret) { + ERROR("Failed to clear DDR content\n"); + } + + /* Polling all scrub writes data have been sent */ + ret = poll_idle_status((umctl2_base + DDR4_SBRSTAT_OFFSET), + DDR4_SBRSTAT_SCRUB_BUSY, true, 5000); + if (ret) { + INFO("%s: Timeout while waiting for", __func__); + INFO(" sending all scrub data\n"); + return ret; + } + + /* Disables scrubber */ + mmio_clrbits_32(umctl2_base + DDR4_SBRCTL_OFFSET, DDR4_SBRCTL_SCRUB_EN); + + /* Restore user configurations */ + mmio_write_32(temp[0], umctl2_base + DDR4_SBRCTL_OFFSET); + mmio_write_32(temp[1], umctl2_base + DDR4_SBRWDATA0_OFFSET); + mmio_write_32(temp[2], umctl2_base + DDR4_SBRSTART0_OFFSET); + if (umctl2_type == DDR_TYPE_DDR4) { + mmio_write_32(temp[3], umctl2_base + DDR4_SBRWDATA1_OFFSET); + mmio_write_32(temp[4], umctl2_base + DDR4_SBRSTART1_OFFSET); + } + mmio_write_32(temp[5], umctl2_base + DDR4_SBRRANGE0_OFFSET); + mmio_write_32(temp[6], umctl2_base + DDR4_SBRRANGE1_OFFSET); + mmio_write_32(temp[7], umctl2_base + DDR4_ECCCFG0_OFFSET); + mmio_write_32(temp[8], umctl2_base + DDR4_ECCCFG1_OFFSET); + + /* Enables ECC scrub on scrubber */ + if (!(mmio_read_32(umctl2_base + DDR4_SBRCTL_OFFSET) & DDR4_SBRCTL_SCRUB_WRITE)) { + /* Enables scrubber */ + mmio_setbits_32(umctl2_base + DDR4_SBRCTL_OFFSET, DDR4_SBRCTL_SCRUB_EN); + } + + return 0; +} + +int ddr_zerofill_scrubber(phys_addr_t umctl2_base, enum ddr_type umctl2_type) +{ + int ret = 0; + + /* Zeroing whole DDR */ + mmio_write_32(0, umctl2_base + DDR4_SBRWDATA0_OFFSET); + mmio_write_32(0, umctl2_base + DDR4_SBRSTART0_OFFSET); + if (umctl2_type == DDR_TYPE_DDR4) { + mmio_write_32(0, umctl2_base + DDR4_SBRWDATA1_OFFSET); + mmio_write_32(0, umctl2_base + DDR4_SBRSTART1_OFFSET); + } + mmio_write_32(0, umctl2_base + DDR4_SBRRANGE0_OFFSET); + mmio_write_32(0, umctl2_base + DDR4_SBRRANGE1_OFFSET); + + NOTICE("Enabling scrubber (zeroing whole DDR) ...\n"); + + /* Enables scrubber */ + mmio_setbits_32(umctl2_base + DDR4_SBRCTL_OFFSET, DDR4_SBRCTL_SCRUB_EN); + /* Polling all scrub writes commands have been sent */ + ret = poll_idle_status((umctl2_base + DDR4_SBRSTAT_OFFSET), + DDR4_SBRSTAT_SCRUB_DONE, true, 5000); + if (ret) { + INFO("%s: Timeout while waiting for", __func__); + INFO(" sending all scrub commands\n"); + return ret; + } + + return 0; +} + +int poll_idle_status(uint32_t addr, uint32_t mask, uint32_t match, uint32_t delay_ms) +{ + int time_out = delay_ms; + + while (time_out-- > 0) { + + if ((mmio_read_32(addr) & mask) == match) { + return 0; + } + udelay(1000); + } + return -ETIMEDOUT; +} diff --git a/plat/intel/soc/common/drivers/ddr/ddr.h b/plat/intel/soc/common/drivers/ddr/ddr.h index 416b64e8..e50cda83 100644 --- a/plat/intel/soc/common/drivers/ddr/ddr.h +++ b/plat/intel/soc/common/drivers/ddr/ddr.h @@ -10,6 +10,28 @@ #include <lib/mmio.h> #include "socfpga_handoff.h" +enum ddr_type { + DDR_TYPE_LPDDR4_0, + DDR_TYPE_LPDDR4_1, + DDR_TYPE_DDR4, + DDR_TYPE_LPDDR5_0, + DDR_TYPE_LPDDR5_1, + DDR_TYPE_DDR5, + DDR_TYPE_UNKNOWN +}; + +/* Region size for ECCCFG0.ecc_region_map */ +enum region_size { + ONE_EIGHT, + ONE_SIXTEENTH, + ONE_THIRTY_SECOND, + ONE_SIXTY_FOURTH +}; + +/* DATATYPE DEFINATION */ +typedef unsigned long long phys_addr_t; +typedef unsigned long long phys_size_t; + /* MACRO DEFINATION */ #define IO96B_0_REG_BASE 0x18400000 #define IO96B_1_REG_BASE 0x18800000 @@ -86,6 +108,34 @@ #define IOSSM_MB_WRITE(addr, data) mmio_write_32(addr, data) +/* DDR4 Register */ +#define DDR4_PWRCTL_OFFSET 0x30 +#define DDR4_SBRCTL_OFFSET 0x0F24 +#define DDR4_SBRSTAT_OFFSET 0x0F28 +#define DDR4_SBRWDATA0_OFFSET 0x0F2C +#define DDR4_SBRSTART0_OFFSET 0x0F38 +#define DDR4_SBRWDATA1_OFFSET 0x0F30 +#define DDR4_SBRSTART1_OFFSET 0x0F3C +#define DDR4_SBRRANGE0_OFFSET 0x0F40 +#define DDR4_SBRRANGE1_OFFSET 0x0F44 +#define DDR4_ECCCFG0_OFFSET 0x70 +#define DDR4_ECCCFG1_OFFSET 0x74 +#define DDR4_PCTRL0_OFFSET 0x0490 + +#define LPDDR4_ECCCFG0_ECC_REGION_MAP_GRANU_SHIFT 30 +#define ALL_PROTECTED 0x7F +#define LPDDR4_ECCCFG0_ECC_REGION_MAP_SHIFT 8 + + + +#define LPDDR4_ECCCFG1_ECC_REGIONS_PARITY_LOCK BIT(4) +#define DDR4_PCTRL0_PORT_EN BIT(0) +#define DDR4_SBRCTL_SCRUB_EN BIT(0) +#define DDR4_SBRSTAT_SCRUB_BUSY BIT(0) +#define DDR4_SBRCTL_SCRUB_BURST_1 BIT(4) +#define DDR4_SBRCTL_SCRUB_WRITE BIT(2) +#define DDR4_SBRSTAT_SCRUB_DONE BIT(1) + /* FUNCTION DEFINATION */ int ddr_calibration_check(void); @@ -109,4 +159,10 @@ void ddr_enable_firewall(void); bool is_ddr_init_in_progress(void); +int ddr_zerofill_scrubber(phys_addr_t umctl2_base, enum ddr_type umctl2_type); + +int ddr_config_scrubber(phys_addr_t umctl2_base, enum ddr_type umctl2_type); + +int poll_idle_status(uint32_t addr, uint32_t mask, uint32_t match, uint32_t delay_ms); + #endif diff --git a/plat/intel/soc/common/drivers/nand/nand.c b/plat/intel/soc/common/drivers/nand/nand.c index c6acbe3b..7fd955a5 100644 --- a/plat/intel/soc/common/drivers/nand/nand.c +++ b/plat/intel/soc/common/drivers/nand/nand.c @@ -38,18 +38,12 @@ static void nand_pinmux_config(void) mmio_write_32(SOCFPGA_PINMUX(PIN12SEL), SOCFPGA_PINMUX_SEL_NAND); mmio_write_32(SOCFPGA_PINMUX(PIN13SEL), SOCFPGA_PINMUX_SEL_NAND); mmio_write_32(SOCFPGA_PINMUX(PIN14SEL), SOCFPGA_PINMUX_SEL_NAND); - mmio_write_32(SOCFPGA_PINMUX(PIN16SEL), SOCFPGA_PINMUX_SEL_NAND); - mmio_write_32(SOCFPGA_PINMUX(PIN17SEL), SOCFPGA_PINMUX_SEL_NAND); - mmio_write_32(SOCFPGA_PINMUX(PIN18SEL), SOCFPGA_PINMUX_SEL_NAND); - mmio_write_32(SOCFPGA_PINMUX(PIN19SEL), SOCFPGA_PINMUX_SEL_NAND); - mmio_write_32(SOCFPGA_PINMUX(PIN20SEL), SOCFPGA_PINMUX_SEL_NAND); - mmio_write_32(SOCFPGA_PINMUX(PIN21SEL), SOCFPGA_PINMUX_SEL_NAND); - mmio_write_32(SOCFPGA_PINMUX(PIN22SEL), SOCFPGA_PINMUX_SEL_NAND); - mmio_write_32(SOCFPGA_PINMUX(PIN23SEL), SOCFPGA_PINMUX_SEL_NAND); } int nand_init(handoff *hoff_ptr) { + (void)(hoff_ptr); + /* NAND pin mux configuration */ nand_pinmux_config(); diff --git a/plat/intel/soc/common/drivers/qspi/cadence_qspi.c b/plat/intel/soc/common/drivers/qspi/cadence_qspi.c index da8a8bde..18aa48e0 100644 --- a/plat/intel/soc/common/drivers/qspi/cadence_qspi.c +++ b/plat/intel/soc/common/drivers/qspi/cadence_qspi.c @@ -14,6 +14,7 @@ #include "cadence_qspi.h" #include "socfpga_plat_def.h" +#include "wdt/watchdog.h" #define LESS(a, b) (((a) < (b)) ? (a) : (b)) #define MORE(a, b) (((a) > (b)) ? (a) : (b)) @@ -634,8 +635,9 @@ int cad_qspi_indirect_page_bound_write(uint32_t offset, int cad_qspi_read_bank(uint8_t *buffer, uint32_t offset, uint32_t size) { int status; - uint32_t read_count = 0, *read_data; + uint32_t read_count = 0; int level = 1, count = 0, i; + uint8_t *read_data; status = cad_qspi_indirect_read_start_bank(offset, size); @@ -647,12 +649,15 @@ int cad_qspi_read_bank(uint8_t *buffer, uint32_t offset, uint32_t size) level = CAD_QSPI_SRAMFILL_INDRDPART( mmio_read_32(CAD_QSPI_OFFSET + CAD_QSPI_SRAMFILL)); - read_data = (uint32_t *)(buffer + read_count); + read_data = (uint8_t *)(buffer + read_count); for (i = 0; i < level; ++i) - *read_data++ = mmio_read_32(CAD_QSPIDATA_OFST); + *read_data++ = mmio_read_8(CAD_QSPIDATA_OFST); - read_count += level * sizeof(uint32_t); + read_count += level * sizeof(uint8_t); count++; +#if ARM_LINUX_KERNEL_AS_BL33 + watchdog_sw_rst(); +#endif } while (level > 0); } diff --git a/plat/intel/soc/common/drivers/sdmmc/sdmmc.c b/plat/intel/soc/common/drivers/sdmmc/sdmmc.c index 8666f54c..62698a90 100644 --- a/plat/intel/soc/common/drivers/sdmmc/sdmmc.c +++ b/plat/intel/soc/common/drivers/sdmmc/sdmmc.c @@ -1,5 +1,6 @@ /* * Copyright (c) 2022-2023, Intel Corporation. All rights reserved. + * Copyright (c) 2024, Altera Corporation. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -17,52 +18,26 @@ #include <lib/mmio.h> #include <lib/utils.h> -#include "agilex5_pinmux.h" #include "sdmmc.h" +#include "socfpga_mailbox.h" +#include "wdt/watchdog.h" static const struct mmc_ops *ops; static unsigned int mmc_ocr_value; -static struct mmc_csd_emmc mmc_csd; -static struct sd_switch_status sd_switch_func_status; -static unsigned char mmc_ext_csd[512] __aligned(16); static unsigned int mmc_flags; -static struct mmc_device_info *mmc_dev_info; static unsigned int rca; -static unsigned int scr[2]__aligned(16) = { 0 }; extern const struct mmc_ops cdns_sdmmc_ops; extern struct cdns_sdmmc_params cdns_params; extern struct cdns_sdmmc_combo_phy sdmmc_combo_phy_reg; extern struct cdns_sdmmc_sdhc sdmmc_sdhc_reg; -static bool is_cmd23_enabled(void) +bool is_cmd23_enabled(void) { return ((mmc_flags & MMC_FLAG_CMD23) != 0U); } -static bool is_sd_cmd6_enabled(void) -{ - return ((mmc_flags & MMC_FLAG_SD_CMD6) != 0U); -} - -/* TODO: Will romove once ATF driver is developed */ -void sdmmc_pin_config(void) -{ - /* temp use base + addr. Official must change to common method */ - mmio_write_32(AGX5_PINMUX_PIN0SEL+0x00, 0x0); - mmio_write_32(AGX5_PINMUX_PIN0SEL+0x04, 0x0); - mmio_write_32(AGX5_PINMUX_PIN0SEL+0x08, 0x0); - mmio_write_32(AGX5_PINMUX_PIN0SEL+0x0C, 0x0); - mmio_write_32(AGX5_PINMUX_PIN0SEL+0x10, 0x0); - mmio_write_32(AGX5_PINMUX_PIN0SEL+0x14, 0x0); - mmio_write_32(AGX5_PINMUX_PIN0SEL+0x18, 0x0); - mmio_write_32(AGX5_PINMUX_PIN0SEL+0x1C, 0x0); - mmio_write_32(AGX5_PINMUX_PIN0SEL+0x20, 0x0); - mmio_write_32(AGX5_PINMUX_PIN0SEL+0x24, 0x0); - mmio_write_32(AGX5_PINMUX_PIN0SEL+0x28, 0x0); -} - -static int sdmmc_send_cmd(unsigned int idx, unsigned int arg, +int sdmmc_send_cmd(unsigned int idx, unsigned int arg, unsigned int r_type, unsigned int *r_data) { struct mmc_cmd cmd; @@ -92,7 +67,7 @@ static int sdmmc_send_cmd(unsigned int idx, unsigned int arg, return ret; } -static int sdmmc_device_state(void) +int sdmmc_device_state(void) { int retries = DEFAULT_SDMMC_MAX_RETRIES; unsigned int resp_data[4]; @@ -123,520 +98,12 @@ static int sdmmc_device_state(void) return MMC_GET_STATE(resp_data[0]); } -static int sdmmc_set_ext_csd(unsigned int ext_cmd, unsigned int value) -{ - int ret; - - ret = sdmmc_send_cmd(MMC_CMD(6), - EXTCSD_WRITE_BYTES | EXTCSD_CMD(ext_cmd) | - EXTCSD_VALUE(value) | EXTCSD_CMD_SET_NORMAL, - MMC_RESPONSE_R1B, NULL); - if (ret != 0) { - return ret; - } - - do { - ret = sdmmc_device_state(); - if (ret < 0) { - return ret; - } - } while (ret == MMC_STATE_PRG); - - return 0; -} - -static int sdmmc_mmc_sd_switch(unsigned int bus_width) -{ - int ret; - int retries = DEFAULT_SDMMC_MAX_RETRIES; - unsigned int bus_width_arg = 0; - - /* CMD55: Application Specific Command */ - ret = sdmmc_send_cmd(MMC_CMD(55), rca << RCA_SHIFT_OFFSET, - MMC_RESPONSE_R5, NULL); - if (ret != 0) { - return ret; - } - - ret = ops->prepare(0, (uintptr_t)&scr, sizeof(scr)); - if (ret != 0) { - return ret; - } - - /* ACMD51: SEND_SCR */ - do { - ret = sdmmc_send_cmd(MMC_ACMD(51), 0, MMC_RESPONSE_R1, NULL); - if ((ret != 0) && (retries == 0)) { - ERROR("ACMD51 failed after %d retries (ret=%d)\n", - DEFAULT_SDMMC_MAX_RETRIES, ret); - return ret; - } - - retries--; - } while (ret != 0); - - ret = ops->read(0, (uintptr_t)&scr, sizeof(scr)); - if (ret != 0) { - return ret; - } - - if (((scr[0] & SD_SCR_BUS_WIDTH_4) != 0U) && - (bus_width == MMC_BUS_WIDTH_4)) { - bus_width_arg = 2; - } - - /* CMD55: Application Specific Command */ - ret = sdmmc_send_cmd(MMC_CMD(55), rca << RCA_SHIFT_OFFSET, - MMC_RESPONSE_R5, NULL); - if (ret != 0) { - return ret; - } - - /* ACMD6: SET_BUS_WIDTH */ - ret = sdmmc_send_cmd(MMC_ACMD(6), bus_width_arg, MMC_RESPONSE_R1, NULL); - if (ret != 0) { - return ret; - } - - do { - ret = sdmmc_device_state(); - if (ret < 0) { - return ret; - } - } while (ret == MMC_STATE_PRG); - - return 0; -} - -static int sdmmc_set_ios(unsigned int clk, unsigned int bus_width) -{ - int ret; - unsigned int width = bus_width; - - if (mmc_dev_info->mmc_dev_type != MMC_IS_EMMC) { - if (width == MMC_BUS_WIDTH_8) { - WARN("Wrong bus config for SD-card, force to 4\n"); - width = MMC_BUS_WIDTH_4; - } - ret = sdmmc_mmc_sd_switch(width); - if (ret != 0) { - return ret; - } - } else if (mmc_csd.spec_vers == 4U) { - ret = sdmmc_set_ext_csd(CMD_EXTCSD_BUS_WIDTH, - (unsigned int)width); - if (ret != 0) { - return ret; - } - } else { - VERBOSE("Wrong MMC type or spec version\n"); - } - - return ops->set_ios(clk, width); -} - -static int sdmmc_fill_device_info(void) -{ - unsigned long long c_size; - unsigned int speed_idx; - unsigned int nb_blocks; - unsigned int freq_unit; - int ret = 0; - struct mmc_csd_sd_v2 *csd_sd_v2; - - switch (mmc_dev_info->mmc_dev_type) { - case MMC_IS_EMMC: - mmc_dev_info->block_size = MMC_BLOCK_SIZE; - - ret = ops->prepare(0, (uintptr_t)&mmc_ext_csd, - sizeof(mmc_ext_csd)); - if (ret != 0) { - return ret; - } - - /* MMC CMD8: SEND_EXT_CSD */ - ret = sdmmc_send_cmd(MMC_CMD(8), 0, MMC_RESPONSE_R1, NULL); - if (ret != 0) { - return ret; - } - - ret = ops->read(0, (uintptr_t)&mmc_ext_csd, - sizeof(mmc_ext_csd)); - if (ret != 0) { - return ret; - } - - do { - ret = sdmmc_device_state(); - if (ret < 0) { - return ret; - } - } while (ret != MMC_STATE_TRAN); - - nb_blocks = (mmc_ext_csd[CMD_EXTCSD_SEC_CNT] << 0) | - (mmc_ext_csd[CMD_EXTCSD_SEC_CNT + 1] << 8) | - (mmc_ext_csd[CMD_EXTCSD_SEC_CNT + 2] << 16) | - (mmc_ext_csd[CMD_EXTCSD_SEC_CNT + 3] << 24); - - mmc_dev_info->device_size = (unsigned long long)nb_blocks * - mmc_dev_info->block_size; - - break; - - case MMC_IS_SD: - /* - * Use the same mmc_csd struct, as required fields here - * (READ_BL_LEN, C_SIZE, CSIZE_MULT) are common with eMMC. - */ - mmc_dev_info->block_size = BIT_32(mmc_csd.read_bl_len); - - c_size = ((unsigned long long)mmc_csd.c_size_high << 2U) | - (unsigned long long)mmc_csd.c_size_low; - assert(c_size != 0xFFFU); - - mmc_dev_info->device_size = (c_size + 1U) * - BIT_64(mmc_csd.c_size_mult + 2U) * - mmc_dev_info->block_size; - - break; - - case MMC_IS_SD_HC: - assert(mmc_csd.csd_structure == 1U); - - mmc_dev_info->block_size = MMC_BLOCK_SIZE; - - /* Need to use mmc_csd_sd_v2 struct */ - csd_sd_v2 = (struct mmc_csd_sd_v2 *)&mmc_csd; - c_size = ((unsigned long long)csd_sd_v2->c_size_high << 16) | - (unsigned long long)csd_sd_v2->c_size_low; - - mmc_dev_info->device_size = (c_size + 1U) << SDMMC_MULT_BY_512K_SHIFT; - - break; - - default: - ret = -EINVAL; - break; - } - - if (ret < 0) { - return ret; - } - - speed_idx = (mmc_csd.tran_speed & CSD_TRAN_SPEED_MULT_MASK) >> - CSD_TRAN_SPEED_MULT_SHIFT; - - assert(speed_idx > 0U); - - if (mmc_dev_info->mmc_dev_type == MMC_IS_EMMC) { - mmc_dev_info->max_bus_freq = tran_speed_base[speed_idx]; - } else { - mmc_dev_info->max_bus_freq = sd_tran_speed_base[speed_idx]; - } - - freq_unit = mmc_csd.tran_speed & CSD_TRAN_SPEED_UNIT_MASK; - while (freq_unit != 0U) { - mmc_dev_info->max_bus_freq *= 10U; - --freq_unit; - } - - mmc_dev_info->max_bus_freq *= 10000U; - - return 0; -} - -static int sdmmc_sd_switch(unsigned int mode, unsigned char group, - unsigned char func) -{ - unsigned int group_shift = (group - 1U) * 4U; - unsigned int group_mask = GENMASK(group_shift + 3U, group_shift); - unsigned int arg; - int ret; - - ret = ops->prepare(0, (uintptr_t)&sd_switch_func_status, - sizeof(sd_switch_func_status)); - if (ret != 0) { - return ret; - } - - /* MMC CMD6: SWITCH_FUNC */ - arg = mode | SD_SWITCH_ALL_GROUPS_MASK; - arg &= ~group_mask; - arg |= func << group_shift; - ret = sdmmc_send_cmd(MMC_CMD(6), arg, MMC_RESPONSE_R1, NULL); - if (ret != 0) { - return ret; - } - - return ops->read(0, (uintptr_t)&sd_switch_func_status, - sizeof(sd_switch_func_status)); -} - -static int sdmmc_sd_send_op_cond(void) -{ - int n; - unsigned int resp_data[4]; - - for (n = 0; n < SEND_SDMMC_OP_COND_MAX_RETRIES; n++) { - int ret; - - /* CMD55: Application Specific Command */ - ret = sdmmc_send_cmd(MMC_CMD(55), 0, MMC_RESPONSE_R1, NULL); - if (ret != 0) { - return ret; - } - - /* ACMD41: SD_SEND_OP_COND */ - ret = sdmmc_send_cmd(MMC_ACMD(41), OCR_HCS | - mmc_dev_info->ocr_voltage, MMC_RESPONSE_R3, - &resp_data[0]); - if (ret != 0) { - return ret; - } - - if ((resp_data[0] & OCR_POWERUP) != 0U) { - mmc_ocr_value = resp_data[0]; - - if ((mmc_ocr_value & OCR_HCS) != 0U) { - mmc_dev_info->mmc_dev_type = MMC_IS_SD_HC; - } else { - mmc_dev_info->mmc_dev_type = MMC_IS_SD; - } - - return 0; - } - - mdelay(10); - } - - ERROR("ACMD41 failed after %d retries\n", SEND_SDMMC_OP_COND_MAX_RETRIES); - - return -EIO; -} - -static int sdmmc_reset_to_idle(void) -{ - int ret; - - /* CMD0: reset to IDLE */ - ret = sdmmc_send_cmd(MMC_CMD(0), 0, 0, NULL); - if (ret != 0) { - return ret; - } - - mdelay(2); - - return 0; -} - -static int sdmmc_send_op_cond(void) -{ - int ret, n; - unsigned int resp_data[4]; - - ret = sdmmc_reset_to_idle(); - if (ret != 0) { - return ret; - } - - for (n = 0; n < SEND_SDMMC_OP_COND_MAX_RETRIES; n++) { - ret = sdmmc_send_cmd(MMC_CMD(1), OCR_SECTOR_MODE | - OCR_VDD_MIN_2V7 | OCR_VDD_MIN_1V7, - MMC_RESPONSE_R3, &resp_data[0]); - if (ret != 0) { - return ret; - } - - if ((resp_data[0] & OCR_POWERUP) != 0U) { - mmc_ocr_value = resp_data[0]; - return 0; - } - - mdelay(10); - } - - ERROR("CMD1 failed after %d retries\n", SEND_SDMMC_OP_COND_MAX_RETRIES); - - return -EIO; -} - -static int sdmmc_enumerate(unsigned int clk, unsigned int bus_width) -{ - int ret; - unsigned int resp_data[4]; - - ops->init(); - - ret = sdmmc_reset_to_idle(); - if (ret != 0) { - return ret; - } - - if (mmc_dev_info->mmc_dev_type == MMC_IS_EMMC) { - ret = sdmmc_send_op_cond(); - } else { - /* CMD8: Send Interface Condition Command */ - ret = sdmmc_send_cmd(MMC_CMD(8), VHS_2_7_3_6_V | CMD8_CHECK_PATTERN, - MMC_RESPONSE_R5, &resp_data[0]); - - if ((ret == 0) && ((resp_data[0] & 0xffU) == CMD8_CHECK_PATTERN)) { - ret = sdmmc_sd_send_op_cond(); - } - } - if (ret != 0) { - return ret; - } - - /* CMD2: Card Identification */ - ret = sdmmc_send_cmd(MMC_CMD(2), 0, MMC_RESPONSE_R2, NULL); - if (ret != 0) { - return ret; - } - - /* CMD3: Set Relative Address */ - if (mmc_dev_info->mmc_dev_type == MMC_IS_EMMC) { - rca = MMC_FIX_RCA; - ret = sdmmc_send_cmd(MMC_CMD(3), rca << RCA_SHIFT_OFFSET, - MMC_RESPONSE_R1, NULL); - if (ret != 0) { - return ret; - } - } else { - ret = sdmmc_send_cmd(MMC_CMD(3), 0, - MMC_RESPONSE_R6, &resp_data[0]); - if (ret != 0) { - return ret; - } - - rca = (resp_data[0] & 0xFFFF0000U) >> 16; - } - - /* CMD9: CSD Register */ - ret = sdmmc_send_cmd(MMC_CMD(9), rca << RCA_SHIFT_OFFSET, - MMC_RESPONSE_R2, &resp_data[0]); - if (ret != 0) { - return ret; - } - - memcpy(&mmc_csd, &resp_data, sizeof(resp_data)); - - /* CMD7: Select Card */ - ret = sdmmc_send_cmd(MMC_CMD(7), rca << RCA_SHIFT_OFFSET, - MMC_RESPONSE_R1, NULL); - if (ret != 0) { - return ret; - } - - do { - ret = sdmmc_device_state(); - if (ret < 0) { - return ret; - } - } while (ret != MMC_STATE_TRAN); - - ret = sdmmc_set_ios(clk, bus_width); - if (ret != 0) { - return ret; - } - - ret = sdmmc_fill_device_info(); - if (ret != 0) { - return ret; - } - - if (is_sd_cmd6_enabled() && - (mmc_dev_info->mmc_dev_type == MMC_IS_SD_HC)) { - /* Try to switch to High Speed Mode */ - ret = sdmmc_sd_switch(SD_SWITCH_FUNC_CHECK, 1U, 1U); - if (ret != 0) { - return ret; - } - - if ((sd_switch_func_status.support_g1 & BIT(9)) == 0U) { - /* High speed not supported, keep default speed */ - return 0; - } - - ret = sdmmc_sd_switch(SD_SWITCH_FUNC_SWITCH, 1U, 1U); - if (ret != 0) { - return ret; - } - - if ((sd_switch_func_status.sel_g2_g1 & 0x1U) == 0U) { - /* Cannot switch to high speed, keep default speed */ - return 0; - } - - mmc_dev_info->max_bus_freq = 50000000U; - ret = ops->set_ios(clk, bus_width); - } - - return ret; -} - size_t sdmmc_read_blocks(int lba, uintptr_t buf, size_t size) { - int ret; - unsigned int cmd_idx, cmd_arg; - - assert((ops != NULL) && - (ops->read != NULL) && - (size != 0U) && - ((size & MMC_BLOCK_MASK) == 0U)); - - ret = ops->prepare(lba, buf, size); - if (ret != 0) { - return 0; - } - - if (is_cmd23_enabled()) { - /* Set block count */ - ret = sdmmc_send_cmd(MMC_CMD(23), size / MMC_BLOCK_SIZE, - MMC_RESPONSE_R1, NULL); - if (ret != 0) { - return 0; - } - - cmd_idx = MMC_CMD(18); - } else { - if (size > MMC_BLOCK_SIZE) { - cmd_idx = MMC_CMD(18); - } else { - cmd_idx = MMC_CMD(17); - } - } + mmc_read_blocks(lba, buf, size); - if (((mmc_ocr_value & OCR_ACCESS_MODE_MASK) == OCR_BYTE_MODE) && - (mmc_dev_info->mmc_dev_type != MMC_IS_SD_HC)) { - cmd_arg = lba * MMC_BLOCK_SIZE; - } else { - cmd_arg = lba; - } - - ret = sdmmc_send_cmd(cmd_idx, cmd_arg, MMC_RESPONSE_R1, NULL); - if (ret != 0) { - return 0; - } - - ret = ops->read(lba, buf, size); - if (ret != 0) { - return 0; - } - - /* Wait buffer empty */ - do { - ret = sdmmc_device_state(); - if (ret < 0) { - return 0; - } - } while ((ret != MMC_STATE_TRAN) && (ret != MMC_STATE_DATA)); - - if (!is_cmd23_enabled() && (size > MMC_BLOCK_SIZE)) { - ret = sdmmc_send_cmd(MMC_CMD(12), 0, MMC_RESPONSE_R1B, NULL); - if (ret != 0) { - return 0; - } - } + /* Restart watchdog for reading each chunk byte */ + watchdog_sw_rst(); return size; } @@ -707,63 +174,3 @@ size_t sdmmc_write_blocks(int lba, const uintptr_t buf, size_t size) return size; } - -int sd_or_mmc_init(const struct mmc_ops *ops_ptr, unsigned int clk, - unsigned int width, unsigned int flags, - struct mmc_device_info *device_info) -{ - assert((ops_ptr != NULL) && - (ops_ptr->init != NULL) && - (ops_ptr->send_cmd != NULL) && - (ops_ptr->set_ios != NULL) && - (ops_ptr->prepare != NULL) && - (ops_ptr->read != NULL) && - (ops_ptr->write != NULL) && - (device_info != NULL) && - (clk != 0) && - ((width == MMC_BUS_WIDTH_1) || - (width == MMC_BUS_WIDTH_4) || - (width == MMC_BUS_WIDTH_8) || - (width == MMC_BUS_WIDTH_DDR_4) || - (width == MMC_BUS_WIDTH_DDR_8))); - - ops = ops_ptr; - mmc_flags = flags; - mmc_dev_info = device_info; - - return sdmmc_enumerate(clk, width); -} - -int sdmmc_init(handoff *hoff_ptr, struct cdns_sdmmc_params *params, struct mmc_device_info *info) -{ - int result = 0; - - /* SDMMC pin mux configuration */ - sdmmc_pin_config(); - cdns_set_sdmmc_var(&sdmmc_combo_phy_reg, &sdmmc_sdhc_reg); - result = cdns_sd_host_init(&sdmmc_combo_phy_reg, &sdmmc_sdhc_reg); - if (result < 0) { - return result; - } - - assert((params != NULL) && - ((params->reg_base & MMC_BLOCK_MASK) == 0) && - ((params->desc_base & MMC_BLOCK_MASK) == 0) && - ((params->desc_size & MMC_BLOCK_MASK) == 0) && - ((params->reg_pinmux & MMC_BLOCK_MASK) == 0) && - ((params->reg_phy & MMC_BLOCK_MASK) == 0) && - (params->desc_size > 0) && - (params->clk_rate > 0) && - ((params->bus_width == MMC_BUS_WIDTH_1) || - (params->bus_width == MMC_BUS_WIDTH_4) || - (params->bus_width == MMC_BUS_WIDTH_8))); - - memcpy(&cdns_params, params, sizeof(struct cdns_sdmmc_params)); - cdns_params.cdn_sdmmc_dev_type = info->mmc_dev_type; - cdns_params.cdn_sdmmc_dev_mode = SD_DS; - - result = sd_or_mmc_init(&cdns_sdmmc_ops, params->clk_rate, params->bus_width, - params->flags, info); - - return result; -} diff --git a/plat/intel/soc/common/drivers/sdmmc/sdmmc.h b/plat/intel/soc/common/drivers/sdmmc/sdmmc.h index 16c6b04b..3f6119c4 100644 --- a/plat/intel/soc/common/drivers/sdmmc/sdmmc.h +++ b/plat/intel/soc/common/drivers/sdmmc/sdmmc.h @@ -1,5 +1,6 @@ /* * Copyright (c) 2022-2023, Intel Corporation. All rights reserved. + * Copyright (c) 2024, Altera Corporation. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -33,10 +34,12 @@ static const unsigned char sd_tran_speed_base[16] = { * @hoff_ptr: Pointer to the hand-off data * Return: 0 on success, a negative errno on failure */ -int sdmmc_init(handoff *hoff_ptr, struct cdns_sdmmc_params *params, - struct mmc_device_info *info); -int sd_or_mmc_init(const struct mmc_ops *ops_ptr, unsigned int clk, - unsigned int width, unsigned int flags, - struct mmc_device_info *device_info); void sdmmc_pin_config(void); +size_t sdmmc_read_blocks(int lba, uintptr_t buf, size_t size); +size_t sdmmc_write_blocks(int lba, const uintptr_t buf, size_t size); +int sdmmc_device_state(void); +bool is_cmd23_enabled(void); +int sdmmc_send_cmd(unsigned int idx, unsigned int arg, + unsigned int r_type, unsigned int *r_data); + #endif diff --git a/plat/intel/soc/common/drivers/wdt/watchdog.h b/plat/intel/soc/common/drivers/wdt/watchdog.h index 4ee4cff5..940ebf39 100644 --- a/plat/intel/soc/common/drivers/wdt/watchdog.h +++ b/plat/intel/soc/common/drivers/wdt/watchdog.h @@ -7,11 +7,8 @@ #ifndef CAD_WATCHDOG_H #define CAD_WATCHDOG_H -#if PLATFORM_MODEL == PLAT_SOCFPGA_AGILEX5 -#define WDT_BASE (0x10D00200) -#else -#define WDT_BASE (0xFFD00200) -#endif +#include "socfpga_plat_def.h" + #define WDT_REG_SIZE_OFFSET (0x4) #define WDT_MIN_CYCLES (65536) #define WDT_PERIOD (20) diff --git a/plat/intel/soc/common/include/platform_def.h b/plat/intel/soc/common/include/platform_def.h index 49fc567a..a820e414 100644 --- a/plat/intel/soc/common/include/platform_def.h +++ b/plat/intel/soc/common/include/platform_def.h @@ -1,6 +1,7 @@ /* * Copyright (c) 2019-2022, ARM Limited and Contributors. All rights reserved. * Copyright (c) 2019-2023, Intel Corporation. All rights reserved. + * Copyright (c) 2024, Altera Corporation. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -21,6 +22,18 @@ #define PLAT_SOCFPGA_AGILEX5 4 #define SIMICS_RUN 1 #define MAX_IO_MTD_DEVICES U(1) +/* Boot Source configuration + * TODO: Shall consider "assert_numeric" in the future + */ +#if SOCFPGA_BOOT_SOURCE_NAND +#define BOOT_SOURCE BOOT_SOURCE_NAND +#elif SOCFPGA_BOOT_SOURCE_SDMMC +#define BOOT_SOURCE BOOT_SOURCE_SDMMC +#elif SOCFPGA_BOOT_SOURCE_QSPI +#define BOOT_SOURCE BOOT_SOURCE_QSPI +#else +#define BOOT_SOURCE BOOT_SOURCE_SDMMC +#endif /* sysmgr.boot_scratch_cold4 & 5 used for CPU release address for SPL */ #define PLAT_CPU_RELEASE_ADDR 0xffd12210 @@ -28,21 +41,25 @@ /* Magic word to indicate L2 reset is completed */ #define L2_RESET_DONE_STATUS 0x1228E5E7 +/* Magic word to differentiate for SMP secondary core boot request */ +#define SMP_SEC_CORE_BOOT_REQ 0x1228E5E8 + /* Define next boot image name and offset */ /* Get non-secure image entrypoint for BL33. Zephyr and Linux */ -#if PLATFORM_MODEL == PLAT_SOCFPGA_AGILEX5 - -#ifndef PRELOADED_BL33_BASE -#define PLAT_NS_IMAGE_OFFSET 0x80200000 -#else +#ifdef PRELOADED_BL33_BASE #define PLAT_NS_IMAGE_OFFSET PRELOADED_BL33_BASE -#endif -#define PLAT_HANDOFF_OFFSET 0x0003F000 - +#else +#if PLATFORM_MODEL == PLAT_SOCFPGA_AGILEX5 +#define PLAT_NS_IMAGE_OFFSET 0x80200000 #else #define PLAT_NS_IMAGE_OFFSET 0x10000000 -#define PLAT_HANDOFF_OFFSET 0xFFE3F000 #endif +#endif /* #if PLATFORM_MODEL == PLAT_SOCFPGA_AGILEX5 */ + +#define PLAT_QSPI_DATA_BASE (0x3C00000) +#define PLAT_NAND_DATA_BASE (0x0200000) +#define PLAT_SDMMC_DATA_BASE (0x0) + /******************************************************************************* * Platform binary types for linking diff --git a/plat/intel/soc/common/include/socfpga_handoff.h b/plat/intel/soc/common/include/socfpga_handoff.h index b2913c7f..7e1d0c0b 100644 --- a/plat/intel/soc/common/include/socfpga_handoff.h +++ b/plat/intel/soc/common/include/socfpga_handoff.h @@ -1,5 +1,6 @@ /* * Copyright (c) 2019-2023, Intel Corporation. All rights reserved. + * Copyright (c) 2024, Altera Corporation. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -10,12 +11,12 @@ #define HANDOFF_MAGIC_HEADER 0x424f4f54 /* BOOT */ #define HANDOFF_MAGIC_PINMUX_SEL 0x504d5558 /* PMUX */ #define HANDOFF_MAGIC_IOCTLR 0x494f4354 /* IOCT */ -#define HANDOFF_MAGIC_FPGA 0x46504741 /* FPGA */ +#define HANDOFF_MAGIC_FPGA 0x46504741 /* FPGA */ #define HANDOFF_MAGIC_IODELAY 0x444c4159 /* DLAY */ -#define HANDOFF_MAGIC_CLOCK 0x434c4b53 /* CLKS */ -#define HANDOFF_MAGIC_MISC 0x4d495343 /* MISC */ +#define HANDOFF_MAGIC_CLOCK 0x434c4b53 /* CLKS */ +#define HANDOFF_MAGIC_MISC 0x4d495343 /* MISC */ #define HANDOFF_MAGIC_PERIPHERAL 0x50455249 /* PERIPHERAL */ -#define HANDOFF_MAGIC_DDR 0x5344524d /* DDR */ +#define HANDOFF_MAGIC_DDR 0x5344524d /* DDR */ #include <socfpga_plat_def.h> @@ -126,6 +127,8 @@ typedef struct handoff_t { uint32_t clock_magic; uint32_t clock_length; uint32_t _pad_0x588_0x590[2]; + + /* main group PLL */ uint32_t main_pll_nocclk; uint32_t main_pll_nocdiv; uint32_t main_pll_pllglob; @@ -135,6 +138,8 @@ typedef struct handoff_t { uint32_t main_pll_pllc2; uint32_t main_pll_pllc3; uint32_t main_pll_pllm; + + /* peripheral group PLL */ uint32_t per_pll_emacctl; uint32_t per_pll_gpiodiv; uint32_t per_pll_pllglob; @@ -144,29 +149,25 @@ typedef struct handoff_t { uint32_t per_pll_pllc2; uint32_t per_pll_pllc3; uint32_t per_pll_pllm; + + /* control group */ uint32_t alt_emacactr; uint32_t alt_emacbctr; uint32_t alt_emacptpctr; uint32_t alt_gpiodbctr; - uint32_t alt_sdmmcctr; uint32_t alt_s2fuser0ctr; uint32_t alt_s2fuser1ctr; uint32_t alt_psirefctr; - /* TODO: Temp added for clk manager. */ - uint32_t qspi_clk_khz; + uint32_t alt_usb31ctr; + uint32_t alt_dsuctr; + uint32_t alt_core01ctr; + uint32_t alt_core23ctr; + uint32_t alt_core2ctr; + uint32_t alt_core3ctr; uint32_t hps_osc_clk_hz; uint32_t fpga_clk_hz; - /* TODO: Temp added for clk manager. */ - uint32_t ddr_reset_type; - /* TODO: Temp added for clk manager. */ - uint32_t hps_status_coldreset; - /* TODO: Temp remove due to add in extra handoff data */ - //uint32_t _pad_0x604_0x610[3]; + uint32_t _pad_0x604_0x610[3]; #endif - /* misc configuration */ - uint32_t misc_magic; - uint32_t misc_length; - uint32_t _pad_0x618_0x620[2]; #if PLATFORM_MODEL == PLAT_SOCFPGA_AGILEX5 /* peripheral configuration - select */ @@ -179,7 +180,7 @@ typedef struct handoff_t { uint32_t ddr_magic; uint32_t ddr_length; uint32_t _pad_0x1C_0x20[2]; - uint32_t ddr_array[4]; /* offset, value */ + uint32_t ddr_config; /* BIT[0]-Dual Port. BIT[1]-Dual EMIF */ #endif } handoff; diff --git a/plat/intel/soc/common/include/socfpga_mailbox.h b/plat/intel/soc/common/include/socfpga_mailbox.h index 77d3af95..82f9fd3a 100644 --- a/plat/intel/soc/common/include/socfpga_mailbox.h +++ b/plat/intel/soc/common/include/socfpga_mailbox.h @@ -1,5 +1,6 @@ /* * Copyright (c) 2019-2023, Intel Corporation. All rights reserved. + * Copyright (c) 2024, Altera Corporation. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -68,12 +69,14 @@ /* SEU Commands */ #define MBOX_CMD_SEU_ERR_READ 0x3C +#define MBOX_CMD_SAFE_INJECT_SEU_ERR 0x41 /* RSU Commands */ #define MBOX_GET_SUBPARTITION_TABLE 0x5A #define MBOX_RSU_STATUS 0x5B #define MBOX_RSU_UPDATE 0x5C #define MBOX_HPS_STAGE_NOTIFY 0x5D +#define MBOX_RSU_GET_DEVICE_INFO 0x74 /* FCS Command */ #define MBOX_FCS_GET_PROVISION 0x7B @@ -107,7 +110,7 @@ #define MBOX_GET_MEASUREMENT 0x183 /* Miscellaneous commands */ -#define MBOX_GET_ROM_PATCH_SHA384 0x1B0 +#define MBOX_GET_ROM_PATCH_SHA384 0x1B0 /* Mailbox Definitions */ @@ -196,9 +199,9 @@ #define RSU_VERSION_ACMF_MASK 0xff00 /* Config Status Macros */ -#define CONFIG_STATUS_WORD_SIZE 16U -#define CONFIG_STATUS_FW_VER_OFFSET 1 -#define CONFIG_STATUS_FW_VER_MASK 0x00FFFFFF +#define CONFIG_STATUS_WORD_SIZE 16U +#define CONFIG_STATUS_FW_VER_OFFSET 1 +#define CONFIG_STATUS_FW_VER_MASK 0x00FFFFFF /* Data structure */ @@ -242,12 +245,19 @@ void mailbox_clear_response(void); int intel_mailbox_get_config_status(uint32_t cmd, bool init_done); int intel_mailbox_is_fpga_not_ready(void); +#if PLATFORM_MODEL == PLAT_SOCFPGA_AGILEX5 +void intel_smmu_hps_remapper_init(uint64_t *mem); +int intel_smmu_hps_remapper_config(uint32_t remapper_bypass); +#endif + int mailbox_rsu_get_spt_offset(uint32_t *resp_buf, uint32_t resp_buf_len); int mailbox_rsu_status(uint32_t *resp_buf, uint32_t resp_buf_len); +int mailbox_rsu_get_device_info(uint32_t *resp_buf, uint32_t resp_buf_len); int mailbox_rsu_update(uint32_t *flash_offset); int mailbox_hps_stage_notify(uint32_t execution_stage); int mailbox_hwmon_readtemp(uint32_t chan, uint32_t *resp_buf); int mailbox_hwmon_readvolt(uint32_t chan, uint32_t *resp_buf); int mailbox_seu_err_status(uint32_t *resp_buf, uint32_t resp_buf_len); +int mailbox_safe_inject_seu_err(uint32_t *arg, unsigned int len); #endif /* SOCFPGA_MBOX_H */ diff --git a/plat/intel/soc/common/include/socfpga_private.h b/plat/intel/soc/common/include/socfpga_private.h index 9d389e3c..fbe18c3e 100644 --- a/plat/intel/soc/common/include/socfpga_private.h +++ b/plat/intel/soc/common/include/socfpga_private.h @@ -1,5 +1,6 @@ /* - * Copyright (c) 2019-2022, Intel Corporation. All rights reserved. + * Copyright (c) 2019-2023, Intel Corporation. All rights reserved. + * Copyright (c) 2024, Altera Corporation. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -7,7 +8,6 @@ #ifndef SOCFPGA_PRIVATE_H #define SOCFPGA_PRIVATE_H -#include "socfpga_plat_def.h" #define EMMC_DESC_SIZE (1<<20) @@ -24,8 +24,8 @@ typedef enum { BOOT_SOURCE_FPGA = 0, BOOT_SOURCE_SDMMC, BOOT_SOURCE_NAND, - BOOT_SOURCE_RSVD, - BOOT_SOURCE_QSPI + BOOT_SOURCE_QSPI, + BOOT_SOURCE_RSVD } boot_source_type; /******************************************************************************* @@ -34,7 +34,7 @@ typedef enum { void enable_nonsecure_access(void); -void socfpga_io_setup(int boot_source); +void socfpga_io_setup(int boot_source, unsigned long offset); void socfgpa_configure_mmu_el3(unsigned long total_base, unsigned long total_size, diff --git a/plat/intel/soc/common/include/socfpga_reset_manager.h b/plat/intel/soc/common/include/socfpga_reset_manager.h index 9d06a3d3..93cc9456 100644 --- a/plat/intel/soc/common/include/socfpga_reset_manager.h +++ b/plat/intel/soc/common/include/socfpga_reset_manager.h @@ -155,6 +155,8 @@ #define RSTMGR_HDSKACK_F2SDRAM0ACK 0x00000800 #define RSTMGR_HDSKACK_FPGA2SOCACK 0x00001000 #define RSTMGR_HDSKACK_FPGAHSACK_DASRT 0x00000000 +#define RSTMGR_HDSKACK_LWSOC2FPGAACK_DASRT 0x00000000 +#define RSTMGR_HDSKACK_SOC2FPGAACK_DASRT 0x00000000 #define RSTMGR_HDSKACK_F2SDRAM0ACK_DASRT 0x00000000 #define RSTMGR_HDSKACK_FPGA2SOCACK_DASRT 0x00000000 diff --git a/plat/intel/soc/common/include/socfpga_ros.h b/plat/intel/soc/common/include/socfpga_ros.h new file mode 100644 index 00000000..10cabd31 --- /dev/null +++ b/plat/intel/soc/common/include/socfpga_ros.h @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2024, Intel Corporation. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef SOCFPGA_ROS_H +#define SOCFPGA_ROS_H + +#include <arch_helpers.h> +#include <lib/utils_def.h> + +/** status response*/ +#define ROS_RET_OK (0x00U) +#define ROS_RET_INVALID (0x01U) +#define ROS_RET_NOT_RSU_MODE (0x02U) +#define ROS_QSPI_READ_ERROR (0x03U) +#define ROS_SPT_BAD_MAGIC_NUM (0x04U) +#define ROS_SPT_CRC_ERROR (0x05U) +#define ROS_IMAGE_INDEX_ERR (0x06U) +#define ROS_IMAGE_PARTNUM_OVFL (0x07U) + +#define ADDR_64(h, l) (((((unsigned long)(h)) & 0xffffffff) << 32) | \ + (((unsigned long)(l)) & 0xffffffff)) + +#define RSU_GET_SPT_RESP_SIZE (4U) + +#define RSU_STATUS_RES_SIZE (9U) + +#define SPT_MAGIC_NUMBER (0x57713427U) +#define SPT_VERSION (0U) +#define SPT_FLAG_RESERVED (1U) +#define SPT_FLAG_READONLY (2U) + +#define SPT_MAX_PARTITIONS (127U) +#define SPT_PARTITION_NAME_LENGTH (16U) +#define SPT_RSVD_LENGTH (4U) +#define SPT_SIZE (4096U) +/*BOOT_INFO + FACTORY_IMAGE + SPT0 + SPT1 + CPB0 + CPB1 + FACTORY_IM.SSBL+ *APP* + *APP*.SSBL*/ +#define SPT_MIN_PARTITIONS (9U) + +#define FACTORY_IMAGE "FACTORY_IMAGE" +#define FACTORY_SSBL "FACTORY_IM.SSBL" +#define SSBL_SUFFIX ".SSBL" + +typedef struct { + const uint32_t magic_number; + const uint32_t version; + const uint32_t partitions; + uint32_t checksum; + const uint32_t __RSVD[SPT_RSVD_LENGTH]; + struct { + const char name[SPT_PARTITION_NAME_LENGTH]; + const uint64_t offset; + const uint32_t length; + const uint32_t flags; + } partition[SPT_MAX_PARTITIONS]; +} __packed spt_table_t; + +uint32_t ros_qspi_get_ssbl_offset(unsigned long *offset); + +#endif /* SOCFPGA_ROS_H */ diff --git a/plat/intel/soc/common/include/socfpga_sip_svc.h b/plat/intel/soc/common/include/socfpga_sip_svc.h index 06683010..31474c46 100644 --- a/plat/intel/soc/common/include/socfpga_sip_svc.h +++ b/plat/intel/soc/common/include/socfpga_sip_svc.h @@ -1,5 +1,6 @@ /* - * Copyright (c) 2019-2022, Intel Corporation. All rights reserved. + * Copyright (c) 2019-2023, Intel Corporation. All rights reserved. + * Copyright (c) 2024, Altera Corporation. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -9,82 +10,83 @@ /* SiP status response */ -#define INTEL_SIP_SMC_STATUS_OK 0 -#define INTEL_SIP_SMC_STATUS_BUSY 0x1 -#define INTEL_SIP_SMC_STATUS_REJECTED 0x2 -#define INTEL_SIP_SMC_STATUS_NO_RESPONSE 0x3 -#define INTEL_SIP_SMC_STATUS_ERROR 0x4 -#define INTEL_SIP_SMC_RSU_ERROR 0x7 -#define INTEL_SIP_SMC_SEU_ERR_READ_ERROR 0x8 +#define INTEL_SIP_SMC_STATUS_OK 0 +#define INTEL_SIP_SMC_STATUS_BUSY 0x1 +#define INTEL_SIP_SMC_STATUS_REJECTED 0x2 +#define INTEL_SIP_SMC_STATUS_NO_RESPONSE 0x3 +#define INTEL_SIP_SMC_STATUS_ERROR 0x4 +#define INTEL_SIP_SMC_RSU_ERROR 0x7 +#define INTEL_SIP_SMC_SEU_ERR_READ_ERROR 0x8 /* SiP mailbox error code */ -#define GENERIC_RESPONSE_ERROR 0x3FF +#define GENERIC_RESPONSE_ERROR 0x3FF /* SiP V2 command code range */ -#define INTEL_SIP_SMC_CMD_MASK 0xFFFF -#define INTEL_SIP_SMC_CMD_V2_RANGE_BEGIN 0x400 -#define INTEL_SIP_SMC_CMD_V2_RANGE_END 0x4FF +#define INTEL_SIP_SMC_CMD_MASK 0xFFFF +#define INTEL_SIP_SMC_CMD_V2_RANGE_BEGIN 0x400 +#define INTEL_SIP_SMC_CMD_V2_RANGE_END 0x4FF /* SiP V2 protocol header */ -#define INTEL_SIP_SMC_HEADER_JOB_ID_MASK 0xF -#define INTEL_SIP_SMC_HEADER_JOB_ID_OFFSET 0U -#define INTEL_SIP_SMC_HEADER_CID_MASK 0xF -#define INTEL_SIP_SMC_HEADER_CID_OFFSET 4U -#define INTEL_SIP_SMC_HEADER_VERSION_MASK 0xF -#define INTEL_SIP_SMC_HEADER_VERSION_OFFSET 60U +#define INTEL_SIP_SMC_HEADER_JOB_ID_MASK 0xF +#define INTEL_SIP_SMC_HEADER_JOB_ID_OFFSET 0U +#define INTEL_SIP_SMC_HEADER_CID_MASK 0xF +#define INTEL_SIP_SMC_HEADER_CID_OFFSET 4U +#define INTEL_SIP_SMC_HEADER_VERSION_MASK 0xF +#define INTEL_SIP_SMC_HEADER_VERSION_OFFSET 60U /* SMC SiP service function identifier for version 1 */ /* FPGA Reconfig */ -#define INTEL_SIP_SMC_FPGA_CONFIG_START 0xC2000001 -#define INTEL_SIP_SMC_FPGA_CONFIG_WRITE 0x42000002 -#define INTEL_SIP_SMC_FPGA_CONFIG_COMPLETED_WRITE 0xC2000003 -#define INTEL_SIP_SMC_FPGA_CONFIG_ISDONE 0xC2000004 -#define INTEL_SIP_SMC_FPGA_CONFIG_GET_MEM 0xC2000005 +#define INTEL_SIP_SMC_FPGA_CONFIG_START 0xC2000001 +#define INTEL_SIP_SMC_FPGA_CONFIG_WRITE 0x42000002 +#define INTEL_SIP_SMC_FPGA_CONFIG_COMPLETED_WRITE 0xC2000003 +#define INTEL_SIP_SMC_FPGA_CONFIG_ISDONE 0xC2000004 +#define INTEL_SIP_SMC_FPGA_CONFIG_GET_MEM 0xC2000005 /* FPGA Bitstream Flag */ -#define FLAG_PARTIAL_CONFIG BIT(0) -#define FLAG_AUTHENTICATION BIT(1) -#define CONFIG_TEST_FLAG(_flag, _type) (((flag) & FLAG_##_type) \ - == FLAG_##_type) +#define FLAG_PARTIAL_CONFIG BIT(0) +#define FLAG_AUTHENTICATION BIT(1) +#define CONFIG_TEST_FLAG(_flag, _type) (((flag) & FLAG_##_type) \ + == FLAG_##_type) /* Secure Register Access */ -#define INTEL_SIP_SMC_REG_READ 0xC2000007 -#define INTEL_SIP_SMC_REG_WRITE 0xC2000008 -#define INTEL_SIP_SMC_REG_UPDATE 0xC2000009 +#define INTEL_SIP_SMC_REG_READ 0xC2000007 +#define INTEL_SIP_SMC_REG_WRITE 0xC2000008 +#define INTEL_SIP_SMC_REG_UPDATE 0xC2000009 /* Remote System Update */ -#define INTEL_SIP_SMC_RSU_STATUS 0xC200000B -#define INTEL_SIP_SMC_RSU_UPDATE 0xC200000C -#define INTEL_SIP_SMC_RSU_NOTIFY 0xC200000E -#define INTEL_SIP_SMC_RSU_RETRY_COUNTER 0xC200000F -#define INTEL_SIP_SMC_RSU_DCMF_VERSION 0xC2000010 -#define INTEL_SIP_SMC_RSU_COPY_DCMF_VERSION 0xC2000011 -#define INTEL_SIP_SMC_RSU_MAX_RETRY 0xC2000012 -#define INTEL_SIP_SMC_RSU_COPY_MAX_RETRY 0xC2000013 -#define INTEL_SIP_SMC_RSU_DCMF_STATUS 0xC2000014 -#define INTEL_SIP_SMC_RSU_COPY_DCMF_STATUS 0xC2000015 +#define INTEL_SIP_SMC_RSU_STATUS 0xC200000B +#define INTEL_SIP_SMC_RSU_UPDATE 0xC200000C +#define INTEL_SIP_SMC_RSU_NOTIFY 0xC200000E +#define INTEL_SIP_SMC_RSU_RETRY_COUNTER 0xC200000F +#define INTEL_SIP_SMC_RSU_DCMF_VERSION 0xC2000010 +#define INTEL_SIP_SMC_RSU_COPY_DCMF_VERSION 0xC2000011 +#define INTEL_SIP_SMC_RSU_MAX_RETRY 0xC2000012 +#define INTEL_SIP_SMC_RSU_COPY_MAX_RETRY 0xC2000013 +#define INTEL_SIP_SMC_RSU_DCMF_STATUS 0xC2000014 +#define INTEL_SIP_SMC_RSU_COPY_DCMF_STATUS 0xC2000015 +#define INTEL_SIP_SMC_RSU_GET_DEVICE_INFO 0xC2000016 /* Hardware monitor */ -#define INTEL_SIP_SMC_HWMON_READTEMP 0xC2000020 -#define INTEL_SIP_SMC_HWMON_READVOLT 0xC2000021 -#define TEMP_CHANNEL_MAX (1 << 15) -#define VOLT_CHANNEL_MAX (1 << 15) +#define INTEL_SIP_SMC_HWMON_READTEMP 0xC2000020 +#define INTEL_SIP_SMC_HWMON_READVOLT 0xC2000021 +#define TEMP_CHANNEL_MAX (1 << 15) +#define VOLT_CHANNEL_MAX (1 << 15) /* ECC */ -#define INTEL_SIP_SMC_ECC_DBE 0xC200000D +#define INTEL_SIP_SMC_ECC_DBE 0xC200000D /* Generic Command */ -#define INTEL_SIP_SMC_SERVICE_COMPLETED 0xC200001E -#define INTEL_SIP_SMC_FIRMWARE_VERSION 0xC200001F -#define INTEL_SIP_SMC_HPS_SET_BRIDGES 0xC2000032 -#define INTEL_SIP_SMC_GET_ROM_PATCH_SHA384 0xC2000040 +#define INTEL_SIP_SMC_SERVICE_COMPLETED 0xC200001E +#define INTEL_SIP_SMC_FIRMWARE_VERSION 0xC200001F +#define INTEL_SIP_SMC_HPS_SET_BRIDGES 0xC2000032 +#define INTEL_SIP_SMC_GET_ROM_PATCH_SHA384 0xC2000040 -#define SERVICE_COMPLETED_MODE_ASYNC 0x00004F4E +#define SERVICE_COMPLETED_MODE_ASYNC 0x00004F4E /* Mailbox Command */ -#define INTEL_SIP_SMC_MBOX_SEND_CMD 0xC200003C -#define INTEL_SIP_SMC_GET_USERCODE 0xC200003D +#define INTEL_SIP_SMC_MBOX_SEND_CMD 0xC200003C +#define INTEL_SIP_SMC_GET_USERCODE 0xC200003D /* FPGA Crypto Services */ #define INTEL_SIP_SMC_FCS_RANDOM_NUMBER 0xC200005A @@ -138,23 +140,25 @@ #define INTEL_SIP_SMC_FCS_ECDSA_GET_PUBKEY_FINALIZE 0xC200008B #define INTEL_SIP_SMC_FCS_ECDH_REQUEST_INIT 0xC200008C #define INTEL_SIP_SMC_FCS_ECDH_REQUEST_FINALIZE 0xC200008E +#define INTEL_SIP_SMC_FCS_SDM_REMAPPER_CONFIG 0xC2000201 /* SEU ERR */ -#define INTEL_SIP_SMC_SEU_ERR_STATUS 0xC2000099 +#define INTEL_SIP_SMC_SEU_ERR_STATUS 0xC2000099 +#define INTEL_SIP_SMC_SAFE_INJECT_SEU_ERR 0xC200009A -#define INTEL_SIP_SMC_FCS_SHA_MODE_MASK 0xF -#define INTEL_SIP_SMC_FCS_DIGEST_SIZE_MASK 0xF -#define INTEL_SIP_SMC_FCS_DIGEST_SIZE_OFFSET 4U -#define INTEL_SIP_SMC_FCS_ECC_ALGO_MASK 0xF +#define INTEL_SIP_SMC_FCS_SHA_MODE_MASK 0xF +#define INTEL_SIP_SMC_FCS_DIGEST_SIZE_MASK 0xF +#define INTEL_SIP_SMC_FCS_DIGEST_SIZE_OFFSET 4U +#define INTEL_SIP_SMC_FCS_ECC_ALGO_MASK 0xF /* ECC DBE */ -#define WARM_RESET_WFI_FLAG BIT(31) -#define SYSMGR_ECC_DBE_COLD_RST_MASK (SYSMGR_ECC_OCRAM_MASK |\ - SYSMGR_ECC_DDR0_MASK |\ - SYSMGR_ECC_DDR1_MASK) +#define WARM_RESET_WFI_FLAG BIT(31) +#define SYSMGR_ECC_DBE_COLD_RST_MASK (SYSMGR_ECC_OCRAM_MASK |\ + SYSMGR_ECC_DDR0_MASK |\ + SYSMGR_ECC_DDR1_MASK) /* Non-mailbox SMC Call */ -#define INTEL_SIP_SMC_SVC_VERSION 0xC2000200 +#define INTEL_SIP_SMC_SVC_VERSION 0xC2000200 /** * SMC SiP service function identifier for version 2 @@ -162,31 +166,31 @@ */ /* V2: Non-mailbox function identifier */ -#define INTEL_SIP_SMC_V2_GET_SVC_VERSION 0xC2000400 -#define INTEL_SIP_SMC_V2_REG_READ 0xC2000401 -#define INTEL_SIP_SMC_V2_REG_WRITE 0xC2000402 -#define INTEL_SIP_SMC_V2_REG_UPDATE 0xC2000403 -#define INTEL_SIP_SMC_V2_HPS_SET_BRIDGES 0xC2000404 -#define INTEL_SIP_SMC_V2_RSU_UPDATE_ADDR 0xC2000405 +#define INTEL_SIP_SMC_V2_GET_SVC_VERSION 0xC2000400 +#define INTEL_SIP_SMC_V2_REG_READ 0xC2000401 +#define INTEL_SIP_SMC_V2_REG_WRITE 0xC2000402 +#define INTEL_SIP_SMC_V2_REG_UPDATE 0xC2000403 +#define INTEL_SIP_SMC_V2_HPS_SET_BRIDGES 0xC2000404 +#define INTEL_SIP_SMC_V2_RSU_UPDATE_ADDR 0xC2000405 /* V2: Mailbox function identifier */ -#define INTEL_SIP_SMC_V2_MAILBOX_SEND_COMMAND 0xC2000420 -#define INTEL_SIP_SMC_V2_MAILBOX_POLL_RESPONSE 0xC2000421 +#define INTEL_SIP_SMC_V2_MAILBOX_SEND_COMMAND 0xC2000420 +#define INTEL_SIP_SMC_V2_MAILBOX_POLL_RESPONSE 0xC2000421 /* SMC function IDs for SiP Service queries */ -#define SIP_SVC_CALL_COUNT 0x8200ff00 -#define SIP_SVC_UID 0x8200ff01 -#define SIP_SVC_VERSION 0x8200ff03 +#define SIP_SVC_CALL_COUNT 0x8200ff00 +#define SIP_SVC_UID 0x8200ff01 +#define SIP_SVC_VERSION 0x8200ff03 /* SiP Service Calls version numbers */ /* * Increase if there is any backward compatibility impact */ -#define SIP_SVC_VERSION_MAJOR 2 +#define SIP_SVC_VERSION_MAJOR 2 /* * Increase if there is new SMC function ID being added */ -#define SIP_SVC_VERSION_MINOR 2 +#define SIP_SVC_VERSION_MINOR 2 /* Structure Definitions */ diff --git a/plat/intel/soc/common/include/socfpga_system_manager.h b/plat/intel/soc/common/include/socfpga_system_manager.h index f860f575..346cfe10 100644 --- a/plat/intel/soc/common/include/socfpga_system_manager.h +++ b/plat/intel/soc/common/include/socfpga_system_manager.h @@ -1,5 +1,6 @@ /* * Copyright (c) 2019-2023, Intel Corporation. All rights reserved. + * Copyright (c) 2024, Altera Corporation. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -14,7 +15,6 @@ #define SOCFPGA_SYSMGR_SDMMC 0x28 /* Field Masking */ - #define SYSMGR_SDMMC_DRVSEL(x) (((x) & 0x7) << 0) #define SYSMGR_SDMMC_SMPLSEL(x) (((x) & 0x7) << 4) @@ -33,4 +33,8 @@ #define SOCFPGA_SYSMGR(_reg) (SOCFPGA_SYSMGR_REG_BASE \ + (SOCFPGA_SYSMGR_##_reg)) +/* Function Prototype */ +uint32_t intel_hps_get_jtag_id(void); +bool is_agilex5_A5F0(void); + #endif /* SOCFPGA_SYSTEMMANAGER_H */ diff --git a/plat/intel/soc/common/include/socfpga_vab.h b/plat/intel/soc/common/include/socfpga_vab.h index f6081df7..4587d7fc 100644 --- a/plat/intel/soc/common/include/socfpga_vab.h +++ b/plat/intel/soc/common/include/socfpga_vab.h @@ -1,5 +1,6 @@ /* * Copyright (c) 2020-2023, Intel Corporation. All rights reserved. + * Copyright (c) 2024, Altera Corporation. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -7,10 +8,28 @@ #ifndef SOCFPGA_VAB_H #define SOCFPGA_VAB_H - #include <stdlib.h> #include "socfpga_fcs.h" +/* Macros */ +#define IS_BYTE_ALIGNED(x, a) (((x) & ((typeof(x))(a) - 1)) == 0) +#define BYTE_ALIGN(x, a) __ALIGN_MASK((x), (typeof(x))(a)-1) +#define __ALIGN_MASK(x, mask) (((x)+(mask))&~(mask)) +#define VAB_CERT_HEADER_SIZE sizeof(struct fcs_hps_vab_certificate_header) +#define VAB_CERT_MAGIC_OFFSET offsetof(struct fcs_hps_vab_certificate_header, d) +#define VAB_CERT_FIT_SHA384_OFFSET offsetof(struct fcs_hps_vab_certificate_data, fcs_sha384[0]) +#define SDM_CERT_MAGIC_NUM 0x25D04E7F +#define CHUNKSZ_PER_WD_RESET (256 * 1024) +#define CCERT_CMD_TEST_PGM_MASK 0x80000000 //TODO: ATF FDT location + +/* SHA related return Macro */ +#define ENOVABCERT 1 /* VAB certificate not available */ +#define EIMGERR 2 /* Image format/size not valid */ +#define ETIMEOUT 3 /* Execution timeout */ +#define EPROCESS 4 /* Process error */ +#define EKEYREJECTED 5 /* Key was rejected by service */ +#define EINITREJECTED 6 /* VAB init was rejected */ + struct fcs_hps_vab_certificate_data { uint32_t vab_cert_magic_num; /* offset 0x10 */ uint32_t flags; @@ -27,28 +46,9 @@ struct fcs_hps_vab_certificate_header { /* keychain starts at offset 0x50 */ }; -/* Macros */ -#define IS_BYTE_ALIGNED(x, a) (((x) & ((typeof(x))(a) - 1)) == 0) -#define BYTE_ALIGN(x, a) __ALIGN_MASK((x), (typeof(x))(a)-1) -#define __ALIGN_MASK(x, mask) (((x)+(mask))&~(mask)) -#define VAB_CERT_HEADER_SIZE sizeof(struct fcs_hps_vab_certificate_header) -#define VAB_CERT_MAGIC_OFFSET offsetof(struct fcs_hps_vab_certificate_header, d) -#define VAB_CERT_FIT_SHA384_OFFSET offsetof(struct fcs_hps_vab_certificate_data, fcs_sha384[0]) -#define SDM_CERT_MAGIC_NUM 0x25D04E7F -#define CHUNKSZ_PER_WD_RESET (256 * 1024) - -/* SHA related return Macro */ -#define ENOVABIMG 1 /* VAB certificate not available */ -#define EIMGERR 2 /* Image format/size not valid */ -#define ETIMEOUT 3 /* Execution timeout */ -#define EPROCESS 4 /* Process error */ -#define EKEYREJECTED 5/* Key was rejected by service */ - /* Function Definitions */ -static size_t get_img_size(uint8_t *img_buf, size_t img_buf_sz); -int socfpga_vendor_authentication(void **p_image, size_t *p_size); -static uint32_t get_unaligned_le32(const void *p); -void sha384_csum_wd(const unsigned char *input, unsigned int ilen, -unsigned char *output, unsigned int chunk_sz); - +size_t get_img_size(uint8_t *img_buf, size_t img_buf_sz); +uint32_t get_unaligned_le32(const void *p); +int socfpga_vab_authentication(void **p_image, size_t *p_size); +int socfpga_vab_init(unsigned int image_id); #endif diff --git a/plat/intel/soc/common/lib/sha/sha.c b/plat/intel/soc/common/lib/sha/sha.c new file mode 100644 index 00000000..9a6adc6f --- /dev/null +++ b/plat/intel/soc/common/lib/sha/sha.c @@ -0,0 +1,253 @@ +/* + * Copyright (c) 2024, Altera Corporation. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + + +#include <assert.h> +#include <errno.h> +#include <stdlib.h> + +#include <arch_helpers.h> +#include <common/bl_common.h> +#include <common/debug.h> +#include <common/tbbr/tbbr_img_def.h> +#include <drivers/delay_timer.h> +#include <lib/mmio.h> +#include <lib/utils.h> +#include <plat/common/platform.h> +#include <tools_share/firmware_image_package.h> + +#include "sha.h" +#include "wdt/watchdog.h" + +/* SHA384 certificate ID */ +#define SHA384_H0 0xcbbb9d5dc1059ed8ULL +#define SHA384_H1 0x629a292a367cd507ULL +#define SHA384_H2 0x9159015a3070dd17ULL +#define SHA384_H3 0x152fecd8f70e5939ULL +#define SHA384_H4 0x67332667ffc00b31ULL +#define SHA384_H5 0x8eb44a8768581511ULL +#define SHA384_H6 0xdb0c2e0d64f98fa7ULL +#define SHA384_H7 0x47b5481dbefa4fa4ULL + +/* SHA512 certificate ID */ +#define SHA512_H0 0x6a09e667f3bcc908ULL +#define SHA512_H1 0xbb67ae8584caa73bULL +#define SHA512_H2 0x3c6ef372fe94f82bULL +#define SHA512_H3 0xa54ff53a5f1d36f1ULL +#define SHA512_H4 0x510e527fade682d1ULL +#define SHA512_H5 0x9b05688c2b3e6c1fULL +#define SHA512_H6 0x1f83d9abfb41bd6bULL +#define SHA512_H7 0x5be0cd19137e2179ULL + +void sha384_init(sha512_context *ctx) +{ + ctx->state[0] = SHA384_H0; + ctx->state[1] = SHA384_H1; + ctx->state[2] = SHA384_H2; + ctx->state[3] = SHA384_H3; + ctx->state[4] = SHA384_H4; + ctx->state[5] = SHA384_H5; + ctx->state[6] = SHA384_H6; + ctx->state[7] = SHA384_H7; + ctx->count[0] = ctx->count[1] = 0; +} + +void sha384_update(sha512_context *ctx, const uint8_t *input, uint32_t length) +{ + sha512_base_do_update(ctx, input, length); +} + +void sha384_finish(sha512_context *ctx, uint8_t digest[SHA384_SUM_LEN]) +{ + int i; + + sha512_base_do_finalize(ctx); + for (i = 0; i < SHA384_SUM_LEN / sizeof(uint64_t); i++) + PUT_UINT64_BE(ctx->state[i], digest, i * 8); +} + +void sha384_start(const unsigned char *input, unsigned int len, + unsigned char *output, unsigned int chunk_sz) +{ + /* TODO: Shall trigger watchdog for each chuck byte. */ + sha512_context ctx; + const unsigned char *end; + unsigned char *curr; + int chunk; + + sha384_init(&ctx); + + curr = (unsigned char *)input; + end = input + len; + while (curr < end) { + chunk = end - curr; + if (chunk > chunk_sz) { + chunk = chunk_sz; + } + sha384_update(&ctx, curr, chunk); + curr += chunk; + watchdog_sw_rst(); + } + + sha384_finish(&ctx, output); +} + +/* SHA512 Start Here */ +void sha512_init(sha512_context *ctx) +{ + ctx->state[0] = SHA512_H0; + ctx->state[1] = SHA512_H1; + ctx->state[2] = SHA512_H2; + ctx->state[3] = SHA512_H3; + ctx->state[4] = SHA512_H4; + ctx->state[5] = SHA512_H5; + ctx->state[6] = SHA512_H6; + ctx->state[7] = SHA512_H7; + ctx->count[0] = ctx->count[1] = 0; +} + +void sha512_update(sha512_context *ctx, const uint8_t *input, uint32_t length) +{ + sha512_base_do_update(ctx, input, length); +} + +void sha512_finish(sha512_context *ctx, uint8_t digest[SHA512_SUM_LEN]) +{ + int i; + + sha512_base_do_finalize(ctx); + for (i = 0; i < SHA512_SUM_LEN / sizeof(uint64_t); i++) + PUT_UINT64_BE(ctx->state[i], digest, i * 8); +} + +void sha512_start(const unsigned char *input, unsigned int len, unsigned char *output) +{ + /* TODO: Shall trigger watchdog for each chuck byte. */ + sha512_context ctx; + + sha384_init(&ctx); + sha512_update(&ctx, input, len); + sha512_finish(&ctx, output); +} + +void sha512_transform(uint64_t *state, const uint8_t *input) +{ + uint64_t a, b, c, d, e, f, g, h, t1, t2; + + int i; + uint64_t W[16]; + + /* load the state into our registers */ + a = state[0]; b = state[1]; c = state[2]; d = state[3]; + e = state[4]; f = state[5]; g = state[6]; h = state[7]; + + /* now iterate */ + for (i = 0 ; i < 80; i += 8) { + if (!(i & 8)) { + int j; + + if (i < 16) { + /* load the input */ + for (j = 0; j < 16; j++) + LOAD_OP(i + j, W, input); + } else { + for (j = 0; j < 16; j++) { + BLEND_OP(i + j, W); + } + } + } + + t1 = h + e1(e) + Ch(e, f, g) + sha512_K[i] + W[(i & 15)]; + t2 = e0(a) + Maj(a, b, c); d += t1; h = t1 + t2; + t1 = g + e1(d) + Ch(d, e, f) + sha512_K[i+1] + W[(i & 15) + 1]; + t2 = e0(h) + Maj(h, a, b); c += t1; g = t1 + t2; + t1 = f + e1(c) + Ch(c, d, e) + sha512_K[i+2] + W[(i & 15) + 2]; + t2 = e0(g) + Maj(g, h, a); b += t1; f = t1 + t2; + t1 = e + e1(b) + Ch(b, c, d) + sha512_K[i+3] + W[(i & 15) + 3]; + t2 = e0(f) + Maj(f, g, h); a += t1; e = t1 + t2; + t1 = d + e1(a) + Ch(a, b, c) + sha512_K[i+4] + W[(i & 15) + 4]; + t2 = e0(e) + Maj(e, f, g); h += t1; d = t1 + t2; + t1 = c + e1(h) + Ch(h, a, b) + sha512_K[i+5] + W[(i & 15) + 5]; + t2 = e0(d) + Maj(d, e, f); g += t1; c = t1 + t2; + t1 = b + e1(g) + Ch(g, h, a) + sha512_K[i+6] + W[(i & 15) + 6]; + t2 = e0(c) + Maj(c, d, e); f += t1; b = t1 + t2; + t1 = a + e1(f) + Ch(f, g, h) + sha512_K[i+7] + W[(i & 15) + 7]; + t2 = e0(b) + Maj(b, c, d); e += t1; a = t1 + t2; + } + + state[0] += a; state[1] += b; state[2] += c; state[3] += d; + state[4] += e; state[5] += f; state[6] += g; state[7] += h; + + /* erase our data */ + a = b = c = d = e = f = g = h = t1 = t2 = 0; +} + +void sha512_block_fn(sha512_context *sst, const uint8_t *src, + int blocks) +{ + while (blocks--) { + sha512_transform(sst->state, src); + src += SHA512_BLOCK_SIZE; + } +} + + +void sha512_base_do_finalize(sha512_context *sctx) +{ + const int bit_offset = SHA512_BLOCK_SIZE - sizeof(uint64_t[2]); + uint64_t *bits = (uint64_t *)(sctx->buf + bit_offset); + unsigned int partial = sctx->count[0] % SHA512_BLOCK_SIZE; + + sctx->buf[partial++] = 0x80; + if (partial > bit_offset) { + memset(sctx->buf + partial, 0x0, SHA512_BLOCK_SIZE - partial); + partial = 0; + + sha512_block_fn(sctx, sctx->buf, 1); + } + + memset(sctx->buf + partial, 0x0, bit_offset - partial); + bits[0] = cpu_to_be64(sctx->count[1] << 3 | sctx->count[0] >> 61); + bits[1] = cpu_to_be64(sctx->count[0] << 3); + + sha512_block_fn(sctx, sctx->buf, 1); +} + +void sha512_base_do_update(sha512_context *sctx, + const uint8_t *data, + unsigned int len) +{ + unsigned int partial = sctx->count[0] % SHA512_BLOCK_SIZE; + + sctx->count[0] += len; + if (sctx->count[0] < len) + sctx->count[1]++; + + if (((partial + len) >= SHA512_BLOCK_SIZE)) { + int blocks; + + if (partial) { + int p = SHA512_BLOCK_SIZE - partial; + + memcpy(sctx->buf + partial, data, p); + data += p; + len -= p; + + sha512_block_fn(sctx, sctx->buf, 1); + } + + blocks = len / SHA512_BLOCK_SIZE; + len %= SHA512_BLOCK_SIZE; + + if (blocks) { + sha512_block_fn(sctx, data, blocks); + data += blocks * SHA512_BLOCK_SIZE; + } + partial = 0; + } + if (len) + memcpy(sctx->buf + partial, data, len); +} diff --git a/plat/intel/soc/common/lib/sha/sha.h b/plat/intel/soc/common/lib/sha/sha.h new file mode 100644 index 00000000..41b5fa8d --- /dev/null +++ b/plat/intel/soc/common/lib/sha/sha.h @@ -0,0 +1,166 @@ +/* + * Copyright (c) 2024, Altera Corporation. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef SOCFPGA_SHA_H +#define SOCFPGA_SHA_H + +#include <stdlib.h> + + +#define SHA384_SUM_LEN 48 +#define SHA384_DER_LEN 19 +#define SHA512_SUM_LEN 64 +#define SHA512_DER_LEN 19 +#define SHA512_BLOCK_SIZE 128 + + +/* MACRO Function */ +#define GET_UINT64_BE(n, b, i) { \ + (n) = ((unsigned long long) (b)[(i)] << 56) |\ + ((unsigned long long) (b)[(i) + 1] << 48) |\ + ((unsigned long long) (b)[(i) + 2] << 40) |\ + ((unsigned long long) (b)[(i) + 3] << 32) |\ + ((unsigned long long) (b)[(i) + 4] << 24) |\ + ((unsigned long long) (b)[(i) + 5] << 16) |\ + ((unsigned long long) (b)[(i) + 6] << 8) |\ + ((unsigned long long) (b)[(i) + 7]);\ +} + +#define PUT_UINT64_BE(n, b, i) { \ + (b)[(i)] = (unsigned char) ((n) >> 56);\ + (b)[(i) + 1] = (unsigned char) ((n) >> 48);\ + (b)[(i) + 2] = (unsigned char) ((n) >> 40);\ + (b)[(i) + 3] = (unsigned char) ((n) >> 32);\ + (b)[(i) + 4] = (unsigned char) ((n) >> 24);\ + (b)[(i) + 5] = (unsigned char) ((n) >> 16);\ + (b)[(i) + 6] = (unsigned char) ((n) >> 8);\ + (b)[(i) + 7] = (unsigned char) ((n));\ +} + +#define e0(x) (ror64(x, 28) ^ ror64(x, 34) ^ ror64(x, 39)) +#define e1(x) (ror64(x, 14) ^ ror64(x, 18) ^ ror64(x, 41)) +#define s0(x) (ror64(x, 1) ^ ror64(x, 8) ^ (x >> 7)) +#define s1(x) (ror64(x, 19) ^ ror64(x, 61) ^ (x >> 6)) + +/* Inline Function Definitions */ +/* ror64() to rotate its right in 64 bits. */ +static inline uint64_t ror64(uint64_t input, unsigned int shift) +{ + return (input >> (shift & 63)) | (input << ((-shift) & 63)); +} + +static inline uint64_t Ch(uint64_t x, uint64_t y, uint64_t z) +{ + return z ^ (x & (y ^ z)); +} + +static inline uint64_t Maj(uint64_t x, uint64_t y, uint64_t z) +{ + return (x & y) | (z & (x | y)); +} + +static inline void LOAD_OP(int I, uint64_t *W, const uint8_t *input) +{ + GET_UINT64_BE(W[I], input, I*8); +} + +static inline void BLEND_OP(int I, uint64_t *W) +{ + W[I & 15] += s1(W[(I-2) & 15]) + W[(I-7) & 15] + s0(W[(I-15) & 15]); +} + +#if __BYTE_ORDER == __LITTLE_ENDIAN +inline uint32_t le32_to_cpue(const uint32_t *p) +{ + return (uint32_t)*p; +} +#else +inline uint32_t le32_to_cpue(const uint32_t *p) +{ + return swab32(*p); +} +#endif + +static const uint64_t sha512_K[80] = { + 0x428a2f98d728ae22ULL, 0x7137449123ef65cdULL, 0xb5c0fbcfec4d3b2fULL, + 0xe9b5dba58189dbbcULL, 0x3956c25bf348b538ULL, 0x59f111f1b605d019ULL, + 0x923f82a4af194f9bULL, 0xab1c5ed5da6d8118ULL, 0xd807aa98a3030242ULL, + 0x12835b0145706fbeULL, 0x243185be4ee4b28cULL, 0x550c7dc3d5ffb4e2ULL, + 0x72be5d74f27b896fULL, 0x80deb1fe3b1696b1ULL, 0x9bdc06a725c71235ULL, + 0xc19bf174cf692694ULL, 0xe49b69c19ef14ad2ULL, 0xefbe4786384f25e3ULL, + 0x0fc19dc68b8cd5b5ULL, 0x240ca1cc77ac9c65ULL, 0x2de92c6f592b0275ULL, + 0x4a7484aa6ea6e483ULL, 0x5cb0a9dcbd41fbd4ULL, 0x76f988da831153b5ULL, + 0x983e5152ee66dfabULL, 0xa831c66d2db43210ULL, 0xb00327c898fb213fULL, + 0xbf597fc7beef0ee4ULL, 0xc6e00bf33da88fc2ULL, 0xd5a79147930aa725ULL, + 0x06ca6351e003826fULL, 0x142929670a0e6e70ULL, 0x27b70a8546d22ffcULL, + 0x2e1b21385c26c926ULL, 0x4d2c6dfc5ac42aedULL, 0x53380d139d95b3dfULL, + 0x650a73548baf63deULL, 0x766a0abb3c77b2a8ULL, 0x81c2c92e47edaee6ULL, + 0x92722c851482353bULL, 0xa2bfe8a14cf10364ULL, 0xa81a664bbc423001ULL, + 0xc24b8b70d0f89791ULL, 0xc76c51a30654be30ULL, 0xd192e819d6ef5218ULL, + 0xd69906245565a910ULL, 0xf40e35855771202aULL, 0x106aa07032bbd1b8ULL, + 0x19a4c116b8d2d0c8ULL, 0x1e376c085141ab53ULL, 0x2748774cdf8eeb99ULL, + 0x34b0bcb5e19b48a8ULL, 0x391c0cb3c5c95a63ULL, 0x4ed8aa4ae3418acbULL, + 0x5b9cca4f7763e373ULL, 0x682e6ff3d6b2b8a3ULL, 0x748f82ee5defb2fcULL, + 0x78a5636f43172f60ULL, 0x84c87814a1f0ab72ULL, 0x8cc702081a6439ecULL, + 0x90befffa23631e28ULL, 0xa4506cebde82bde9ULL, 0xbef9a3f7b2c67915ULL, + 0xc67178f2e372532bULL, 0xca273eceea26619cULL, 0xd186b8c721c0c207ULL, + 0xeada7dd6cde0eb1eULL, 0xf57d4f7fee6ed178ULL, 0x06f067aa72176fbaULL, + 0x0a637dc5a2c898a6ULL, 0x113f9804bef90daeULL, 0x1b710b35131c471bULL, + 0x28db77f523047d84ULL, 0x32caab7b40c72493ULL, 0x3c9ebe0a15c9bebcULL, + 0x431d67c49c100d4cULL, 0x4cc5d4becb3e42b6ULL, 0x597f299cfc657e2aULL, + 0x5fcb6fab3ad6faecULL, 0x6c44198c4a475817ULL, +}; + +#define __cpu_to_le64(x) ((__force __le64)(__u64)(x)) + +#define _uswap_64(x, sfx) \ + ((((x) & 0xff00000000000000##sfx) >> 56) |\ + (((x) & 0x00ff000000000000##sfx) >> 40) |\ + (((x) & 0x0000ff0000000000##sfx) >> 24) |\ + (((x) & 0x000000ff00000000##sfx) >> 8) |\ + (((x) & 0x00000000ff000000##sfx) << 8) |\ + (((x) & 0x0000000000ff0000##sfx) << 24) |\ + (((x) & 0x000000000000ff00##sfx) << 40) |\ + (((x) & 0x00000000000000ff##sfx) << 56)) + +#if defined(__GNUC__) +#define uswap_64(x) _uswap_64(x, ull) +#else +#define uswap_64(x) _uswap_64(x) +#endif + +#if __BYTE_ORDER == __LITTLE_ENDIAN +#define cpu_to_be64(x) uswap_64(x) +#else +#define cpu_to_be64(x) (x) +#endif + +typedef struct { + uint64_t state[SHA512_SUM_LEN / 8]; + uint64_t count[2]; + uint8_t buf[SHA512_BLOCK_SIZE]; +} sha512_context; + +/* Function Definitions */ +/* SHA384 Start Here */ +void sha384_init(sha512_context *ctx); +void sha384_update(sha512_context *ctx, const uint8_t *input, uint32_t length); +void sha384_finish(sha512_context *ctx, uint8_t digest[SHA384_SUM_LEN]); +void sha384_start(const unsigned char *input, unsigned int len, + unsigned char *output, unsigned int chunk_sz); +/* SHA512 Start Here */ +void sha512_init(sha512_context *ctx); +void sha512_update(sha512_context *ctx, const uint8_t *input, uint32_t length); +void sha512_finish(sha512_context *ctx, uint8_t digest[SHA512_SUM_LEN]); +void sha512_start(const unsigned char *input, unsigned int len, + unsigned char *output); +void sha512_transform(uint64_t *state, const uint8_t *input); +void sha512_block_fn(sha512_context *sst, const uint8_t *src, int blocks); +void sha512_base_do_finalize(sha512_context *sctx); +void sha512_base_do_update(sha512_context *sctx, const uint8_t *data, + unsigned int len); + +#endif diff --git a/plat/intel/soc/common/sip/socfpga_sip_fcs.c b/plat/intel/soc/common/sip/socfpga_sip_fcs.c index beaa7208..91df934f 100644 --- a/plat/intel/soc/common/sip/socfpga_sip_fcs.c +++ b/plat/intel/soc/common/sip/socfpga_sip_fcs.c @@ -1,5 +1,6 @@ /* - * Copyright (c) 2020-2022, Intel Corporation. All rights reserved. + * Copyright (c) 2020-2023, Intel Corporation. All rights reserved. + * Copyright (c) 2024, Altera Corporation. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -247,14 +248,6 @@ uint32_t intel_fcs_encryption(uint32_t src_addr, uint32_t src_size, int status; uint32_t load_size; - fcs_encrypt_payload payload = { - FCS_ENCRYPTION_DATA_0, - src_addr, - src_size, - dst_addr, - dst_size }; - load_size = sizeof(payload) / MBOX_WORD_BYTE; - if (!is_address_in_ddr_range(src_addr, src_size) || !is_address_in_ddr_range(dst_addr, dst_size)) { return INTEL_SIP_SMC_STATUS_REJECTED; @@ -264,6 +257,14 @@ uint32_t intel_fcs_encryption(uint32_t src_addr, uint32_t src_size, return INTEL_SIP_SMC_STATUS_REJECTED; } + fcs_encrypt_payload payload = { + FCS_ENCRYPTION_DATA_0, + src_addr, + src_size, + dst_addr, + dst_size }; + load_size = sizeof(payload) / MBOX_WORD_BYTE; + status = mailbox_send_cmd_async(send_id, MBOX_FCS_ENCRYPT_REQ, (uint32_t *) &payload, load_size, CMD_INDIRECT); @@ -283,6 +284,15 @@ uint32_t intel_fcs_decryption(uint32_t src_addr, uint32_t src_size, uint32_t load_size; uintptr_t id_offset; + if (!is_address_in_ddr_range(src_addr, src_size) || + !is_address_in_ddr_range(dst_addr, dst_size)) { + return INTEL_SIP_SMC_STATUS_REJECTED; + } + + if (!is_size_4_bytes_aligned(src_size)) { + return INTEL_SIP_SMC_STATUS_REJECTED; + } + inv_dcache_range(src_addr, src_size); /* flush cache before mmio read to avoid reading old values */ id_offset = src_addr + FCS_OWNER_ID_OFFSET; fcs_decrypt_payload payload = { @@ -295,15 +305,6 @@ uint32_t intel_fcs_decryption(uint32_t src_addr, uint32_t src_size, dst_size }; load_size = sizeof(payload) / MBOX_WORD_BYTE; - if (!is_address_in_ddr_range(src_addr, src_size) || - !is_address_in_ddr_range(dst_addr, dst_size)) { - return INTEL_SIP_SMC_STATUS_REJECTED; - } - - if (!is_size_4_bytes_aligned(src_size)) { - return INTEL_SIP_SMC_STATUS_REJECTED; - } - status = mailbox_send_cmd_async(send_id, MBOX_FCS_DECRYPT_REQ, (uint32_t *) &payload, load_size, CMD_INDIRECT); @@ -1164,8 +1165,8 @@ int intel_fcs_mac_verify_update_finalize(uint32_t session_id, return INTEL_SIP_SMC_STATUS_REJECTED; } - memcpy((uint8_t *) &payload[i], (uint8_t *) mac_offset, - src_size - data_size); + memcpy_s(&payload[i], (src_size - data_size) / MBOX_WORD_BYTE, + (void *) mac_offset, (src_size - data_size) / MBOX_WORD_BYTE); i += (src_size - data_size) / MBOX_WORD_BYTE; } @@ -1298,8 +1299,8 @@ int intel_fcs_mac_verify_smmu_update_finalize(uint32_t session_id, return INTEL_SIP_SMC_STATUS_REJECTED; } - memcpy((uint8_t *) &payload[i], (uint8_t *) mac_offset, - src_size - data_size); + memcpy_s(&payload[i], (src_size - data_size) / MBOX_WORD_BYTE, + (void *) mac_offset, (src_size - data_size) / MBOX_WORD_BYTE); memset((void *) dst_addr, 0, *dst_size); @@ -1401,8 +1402,8 @@ int intel_fcs_ecdsa_hash_sign_finalize(uint32_t session_id, uint32_t context_id, return INTEL_SIP_SMC_STATUS_REJECTED; } - memcpy((uint8_t *) &payload[i], (uint8_t *) hash_data_addr, - src_size); + memcpy_s(&payload[i], src_size / MBOX_WORD_BYTE, + (void *) hash_data_addr, src_size / MBOX_WORD_BYTE); i += src_size / MBOX_WORD_BYTE; @@ -1502,8 +1503,8 @@ int intel_fcs_ecdsa_hash_sig_verify_finalize(uint32_t session_id, uint32_t conte return INTEL_SIP_SMC_STATUS_REJECTED; } - memcpy((uint8_t *) &payload[i], - (uint8_t *) hash_sig_pubkey_addr, src_size); + memcpy_s(&payload[i], src_size / MBOX_WORD_BYTE, + (void *) hash_sig_pubkey_addr, src_size / MBOX_WORD_BYTE); i += (src_size / MBOX_WORD_BYTE); @@ -1839,8 +1840,8 @@ int intel_fcs_ecdsa_sha2_data_sig_verify_update_finalize(uint32_t session_id, return INTEL_SIP_SMC_STATUS_REJECTED; } - memcpy((uint8_t *) &payload[i], (uint8_t *) sig_pubkey_offset, - src_size - data_size); + memcpy_s(&payload[i], (src_size - data_size) / MBOX_WORD_BYTE, + (void *) sig_pubkey_offset, (src_size - data_size) / MBOX_WORD_BYTE); i += (src_size - data_size) / MBOX_WORD_BYTE; } @@ -1971,8 +1972,8 @@ int intel_fcs_ecdsa_sha2_data_sig_verify_smmu_update_finalize(uint32_t session_i return INTEL_SIP_SMC_STATUS_REJECTED; } - memcpy((uint8_t *) &payload[i], (uint8_t *) sig_pubkey_offset, - src_size - data_size); + memcpy_s(&payload[i], (src_size - data_size) / MBOX_WORD_BYTE, + (void *) sig_pubkey_offset, (src_size - data_size) / MBOX_WORD_BYTE); memset((void *) dst_addr, 0, *dst_size); @@ -2023,6 +2024,10 @@ int intel_fcs_ecdsa_get_pubkey_finalize(uint32_t session_id, uint32_t context_id return INTEL_SIP_SMC_STATUS_REJECTED; } + if (!is_address_in_ddr_range(dst_addr, *dst_size)) { + return INTEL_SIP_SMC_STATUS_REJECTED; + } + if (fcs_ecdsa_get_pubkey_param.session_id != session_id || fcs_ecdsa_get_pubkey_param.context_id != context_id) { return INTEL_SIP_SMC_STATUS_REJECTED; @@ -2141,7 +2146,8 @@ int intel_fcs_ecdh_request_finalize(uint32_t session_id, uint32_t context_id, return INTEL_SIP_SMC_STATUS_REJECTED; } - memcpy((uint8_t *) &payload[i], (uint8_t *) pubkey, src_size); + memcpy_s(&payload[i], src_size / MBOX_WORD_BYTE, + (void *) pubkey, src_size / MBOX_WORD_BYTE); i += src_size / MBOX_WORD_BYTE; status = mailbox_send_cmd(MBOX_JOB_ID, MBOX_FCS_ECDH_REQUEST, @@ -2171,14 +2177,28 @@ int intel_fcs_aes_crypt_init(uint32_t session_id, uint32_t context_id, param_addr_ptr = (uint64_t *) param_addr; + /* Check if mbox_error is not NULL or 0xF or 0x3FF */ + if (mbox_error == NULL || *mbox_error > 0xF || + (*mbox_error != 0 && *mbox_error != 0x3FF)) { + return INTEL_SIP_SMC_STATUS_REJECTED; + } + + /* Check if param_addr is not 0 or larger that 0xFFFFFFFFFF */ + if (param_addr == 0 || param_addr > 0xFFFFFFFFFF) { + return INTEL_SIP_SMC_STATUS_REJECTED; + } + /* - * Since crypto param size vary between mode. - * Check ECB here and limit to size 12 bytes + * Check if not ECB, CBC and CTR mode, addr ptr is NULL. + * Return "Reject" status */ - if (((*param_addr_ptr & FCS_CRYPTO_BLOCK_MODE_MASK) == FCS_CRYPTO_ECB_MODE) && - (param_size > FCS_CRYPTO_ECB_BUFFER_SIZE)) { + if ((param_addr_ptr == NULL) || + (((*param_addr_ptr & FCS_CRYPTO_BLOCK_MODE_MASK) != FCS_CRYPTO_ECB_MODE) && + ((*param_addr_ptr & FCS_CRYPTO_BLOCK_MODE_MASK) != FCS_CRYPTO_CBC_MODE) && + ((*param_addr_ptr & FCS_CRYPTO_BLOCK_MODE_MASK) != FCS_CRYPTO_CTR_MODE))) { return INTEL_SIP_SMC_STATUS_REJECTED; } + /* * Since crypto param size vary between mode. * Check CBC/CTR here and limit to size 28 bytes @@ -2189,7 +2209,12 @@ int intel_fcs_aes_crypt_init(uint32_t session_id, uint32_t context_id, return INTEL_SIP_SMC_STATUS_REJECTED; } - if (mbox_error == NULL) { + /* + * Since crypto param size vary between mode. + * Check ECB here and limit to size 12 bytes + */ + if (((*param_addr_ptr & FCS_CRYPTO_BLOCK_MODE_MASK) == FCS_CRYPTO_ECB_MODE) && + (param_size > FCS_CRYPTO_ECB_BUFFER_SIZE)) { return INTEL_SIP_SMC_STATUS_REJECTED; } @@ -2200,8 +2225,8 @@ int intel_fcs_aes_crypt_init(uint32_t session_id, uint32_t context_id, fcs_aes_init_payload.param_size = param_size; fcs_aes_init_payload.key_id = key_id; - memcpy((uint8_t *) fcs_aes_init_payload.crypto_param, - (uint8_t *) param_addr, param_size); + memcpy_s(fcs_aes_init_payload.crypto_param, param_size / MBOX_WORD_BYTE, + (void *) param_addr, param_size / MBOX_WORD_BYTE); fcs_aes_init_payload.is_updated = 0; @@ -2234,7 +2259,8 @@ int intel_fcs_aes_crypt_update_finalize(uint32_t session_id, } if ((!is_8_bytes_aligned(dst_addr)) || - (!is_32_bytes_aligned(dst_size))) { + (!is_32_bytes_aligned(dst_size)) || + (!is_address_in_ddr_range(dst_addr, dst_size))) { return INTEL_SIP_SMC_STATUS_REJECTED; } @@ -2280,9 +2306,10 @@ int intel_fcs_aes_crypt_update_finalize(uint32_t session_id, return INTEL_SIP_SMC_STATUS_REJECTED; } - memcpy((uint8_t *) &fcs_aes_crypt_payload[i], - (uint8_t *) fcs_aes_init_payload.crypto_param, - fcs_aes_init_payload.param_size); + memcpy_s(&fcs_aes_crypt_payload[i], + fcs_aes_init_payload.param_size / MBOX_WORD_BYTE, + (void *) fcs_aes_init_payload.crypto_param, + fcs_aes_init_payload.param_size / MBOX_WORD_BYTE); i += fcs_aes_init_payload.param_size / MBOX_WORD_BYTE; } diff --git a/plat/intel/soc/common/soc/socfpga_handoff.c b/plat/intel/soc/common/soc/socfpga_handoff.c index 526c6e11..69747688 100644 --- a/plat/intel/soc/common/soc/socfpga_handoff.c +++ b/plat/intel/soc/common/soc/socfpga_handoff.c @@ -1,5 +1,6 @@ /* * Copyright (c) 2019-2023, Intel Corporation. All rights reserved. + * Copyright (c) 2024, Altera Corporation. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -15,15 +16,21 @@ int socfpga_get_handoff(handoff *reverse_hoff_ptr) { int i; + int j; uint32_t *buffer; - handoff *handoff_ptr = (handoff *) PLAT_HANDOFF_OFFSET; + uint32_t *handoff_ptr = (uint32_t *) PLAT_HANDOFF_OFFSET; + uint32_t *reverse_hoff_ptr_dst = (uint32_t *) reverse_hoff_ptr; if (sizeof(*handoff_ptr) > sizeof(handoff)) { return -EOVERFLOW; } - memcpy(reverse_hoff_ptr, handoff_ptr, sizeof(handoff)); - buffer = (uint32_t *)reverse_hoff_ptr; + for (j = 0; j < sizeof(handoff) / 4; j++) { + memcpy_s((void *) (reverse_hoff_ptr_dst + j), 1, + (void *) (handoff_ptr + j), 1); + } + + buffer = (uint32_t *)reverse_hoff_ptr_dst; /* convert big endian to little endian */ for (i = 0; i < sizeof(handoff) / 4; i++) diff --git a/plat/intel/soc/common/soc/socfpga_mailbox.c b/plat/intel/soc/common/soc/socfpga_mailbox.c index d93fc8a5..5d31e999 100644 --- a/plat/intel/soc/common/soc/socfpga_mailbox.c +++ b/plat/intel/soc/common/soc/socfpga_mailbox.c @@ -1,5 +1,6 @@ /* - * Copyright (c) 2020-2022, Intel Corporation. All rights reserved. + * Copyright (c) 2020-2023, Intel Corporation. All rights reserved. + * Copyright (c) 2024, Altera Corporation. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -167,7 +168,7 @@ int mailbox_read_response(unsigned int *job_id, uint32_t *response, } if (MBOX_RESP_ERR(resp_data) > 0U) { - INFO("Error in response: %x\n", resp_data); + INFO("SDM response: Return Code: 0x%x\n", MBOX_RESP_ERR(resp_data)); return -MBOX_RESP_ERR(resp_data); } @@ -251,7 +252,7 @@ int mailbox_read_response_async(unsigned int *job_id, uint32_t *header, return MBOX_RET_ERROR; } - memcpy((uint8_t *) response, + memcpy_s((uint8_t *) response, *resp_len * MBOX_WORD_BYTE, (uint8_t *) mailbox_resp_ctr.payload->data, *resp_len * MBOX_WORD_BYTE); } @@ -336,7 +337,7 @@ int mailbox_poll_response(uint32_t job_id, uint32_t urgent, uint32_t *response, } if (MBOX_RESP_ERR(resp_data) > 0U) { - INFO("Error in response: %x\n", resp_data); + INFO("SDM response: Return Code: 0x%x\n", MBOX_RESP_ERR(resp_data)); return -MBOX_RESP_ERR(resp_data); } @@ -578,6 +579,13 @@ int mailbox_rsu_status(uint32_t *resp_buf, unsigned int resp_buf_len) return ret; } +int mailbox_rsu_get_device_info(uint32_t *resp_buf, unsigned int resp_buf_len) +{ + return mailbox_send_cmd(MBOX_JOB_ID, MBOX_RSU_GET_DEVICE_INFO, NULL, 0U, + CMD_CASUAL, resp_buf, + &resp_buf_len); +} + int mailbox_rsu_update(uint32_t *flash_offset) { return mailbox_send_cmd(MBOX_JOB_ID, MBOX_RSU_UPDATE, @@ -644,7 +652,7 @@ int intel_mailbox_get_config_status(uint32_t cmd, bool init_done) res = response[RECONFIG_STATUS_SOFTFUNC_STATUS]; if ((res & SOFTFUNC_STATUS_SEU_ERROR) != 0U) { - ERROR("SoftFunction Status SEU ERROR\n"); + return MBOX_CFGSTAT_STATE_ERROR_HARDWARE; } if ((res & SOFTFUNC_STATUS_CONF_DONE) == 0U) { @@ -696,3 +704,9 @@ int mailbox_seu_err_status(uint32_t *resp_buf, unsigned int resp_buf_len) CMD_CASUAL, resp_buf, &resp_buf_len); } + +int mailbox_safe_inject_seu_err(uint32_t *arg, unsigned int len) +{ + return mailbox_send_cmd(MBOX_JOB_ID, MBOX_CMD_SAFE_INJECT_SEU_ERR, arg, len, + CMD_CASUAL, NULL, NULL); +} diff --git a/plat/intel/soc/common/soc/socfpga_reset_manager.c b/plat/intel/soc/common/soc/socfpga_reset_manager.c index 7db86c78..c7d70767 100644 --- a/plat/intel/soc/common/soc/socfpga_reset_manager.c +++ b/plat/intel/soc/common/soc/socfpga_reset_manager.c @@ -1,5 +1,6 @@ /* * Copyright (c) 2019-2023, Intel Corporation. All rights reserved. + * Copyright (c) 2024, Altera Corporation. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -75,7 +76,7 @@ void deassert_peripheral_reset(void) RSTMGR_FIELD(PER0, DMAIF6) | RSTMGR_FIELD(PER0, DMAIF7)); -#if PLATFORM_MODEL == PLAT_SOCFPGA_AGILEX +#if (PLATFORM_MODEL == PLAT_SOCFPGA_AGILEX) || (PLATFORM_MODEL == PLAT_SOCFPGA_AGILEX5) mmio_clrbits_32(SOCFPGA_RSTMGR(BRGMODRST), RSTMGR_FIELD(BRG, MPFE)); #endif @@ -106,30 +107,11 @@ static int poll_idle_status(uint32_t addr, uint32_t mask, uint32_t match, uint32 } udelay(1000); } - return -ETIMEDOUT; -} - -#if PLATFORM_MODEL == PLAT_SOCFPGA_AGILEX5 -static int poll_idle_status_by_counter(uint32_t addr, uint32_t mask, - uint32_t match, uint32_t delay_ms) -{ - int time_out = delay_ms; - - while (time_out-- > 0) { - - if ((mmio_read_32(addr) & mask) == match) { - return 0; - } - /* ToDo: Shall use udelay for product release */ - for (int i = 0; i < 2000; i++) { - /* dummy delay */ - } - } return -ETIMEDOUT; } -#endif +#if PLATFORM_MODEL != PLAT_SOCFPGA_AGILEX5 static int poll_idle_status_by_clkcycles(uint32_t addr, uint32_t mask, uint32_t match, uint32_t delay_clk_cycles) { @@ -144,6 +126,7 @@ static int poll_idle_status_by_clkcycles(uint32_t addr, uint32_t mask, } return -ETIMEDOUT; } +#endif static void socfpga_s2f_bridge_mask(uint32_t mask, uint32_t *brg_mask, @@ -404,9 +387,11 @@ int socfpga_bridges_reset(uint32_t mask) return ret; } +/* TODO: Function too long, shall refactor */ int socfpga_bridges_enable(uint32_t mask) { int ret = 0; + int ret_hps = 0; uint32_t brg_mask = 0; uint32_t noc_mask = 0; uint32_t f2s_idlereq = 0; @@ -416,84 +401,182 @@ int socfpga_bridges_enable(uint32_t mask) uint32_t f2s_respempty = 0; uint32_t f2s_cmdidle = 0; #if PLATFORM_MODEL == PLAT_SOCFPGA_AGILEX5 - uint32_t delay = 0; + uint32_t brg_lst = 0; #endif /* Enable s2f bridge */ socfpga_s2f_bridge_mask(mask, &brg_mask, &noc_mask); #if PLATFORM_MODEL == PLAT_SOCFPGA_AGILEX5 - /* Enable SOC2FPGA bridge */ - if (brg_mask & RSTMGR_BRGMODRSTMASK_SOC2FPGA) { - /* Write Reset Manager hdskreq[soc2fpga_flush_req] = 1 */ - NOTICE("Set S2F hdskreq ...\n"); +/**************** SOC2FPGA ****************/ + brg_lst = mmio_read_32(SOCFPGA_RSTMGR(BRGMODRST)); + if ((brg_mask & RSTMGR_BRGMODRSTMASK_SOC2FPGA) + && ((brg_lst & RSTMGR_BRGMODRSTMASK_SOC2FPGA) != 0)) { + /* + * To request handshake + * Write Reset Manager hdskreq[soc2fpga_flush_req] = 1 + */ + VERBOSE("Set S2F hdskreq ...\n"); mmio_setbits_32(SOCFPGA_RSTMGR(HDSKREQ), - RSTMGR_HDSKREQ_SOC2FPGAREQ); + ((~(brg_lst) << 9) & (RSTMGR_HDSKREQ_LWSOC2FPGAREQ + | RSTMGR_HDSKREQ_SOC2FPGAREQ)) + | (RSTMGR_HDSKREQ_SOC2FPGAREQ)); - /* Read Reset Manager hdskack[soc2fpga] = 1 */ - ret = poll_idle_status_by_counter(SOCFPGA_RSTMGR(HDSKACK), - RSTMGR_HDSKACK_SOC2FPGAACK, RSTMGR_HDSKACK_SOC2FPGAACK, - 300); + udelay(1000); + + /* + * To poll idle status + * Read Reset Manager hdskack[soc2fpga] = 1 + */ + if ((mmio_read_32(SOCFPGA_RSTMGR(BRGMODRST)) + & RSTMGR_BRGMODRST_SOC2FPGA) == 0x00) { + ret = poll_idle_status(SOCFPGA_RSTMGR(HDSKACK), + RSTMGR_HDSKREQ_SOC2FPGAREQ, RSTMGR_HDSKREQ_SOC2FPGAREQ, + 300); + } + + udelay(1000); if (ret < 0) { ERROR("S2F bridge enable: Timeout hdskack\n"); } - /* Write Reset Manager hdskreq[soc2fpga_flush_req] = 0 */ - NOTICE("Clear S2F hdskreq ...\n"); + /* + * To assert reset + * Write Reset Manager hdskreq[soc2fpga_flush_req] = 0 + */ + VERBOSE("Assert S2F ...\n"); + mmio_setbits_32(SOCFPGA_RSTMGR(BRGMODRST), + (~brg_lst & 0x3) | RSTMGR_BRGMODRST_SOC2FPGA); + + udelay(1000); + + /* + * To clear idle request + * Write Reset Manager hdskreq[soc2fpga_flush_req] = 0 + */ + VERBOSE("Clear S2F hdskreq ...\n"); mmio_clrbits_32(SOCFPGA_RSTMGR(HDSKREQ), - RSTMGR_HDSKREQ_SOC2FPGAREQ); + ((~(brg_lst) << 9) & (RSTMGR_HDSKREQ_LWSOC2FPGAREQ + | RSTMGR_HDSKREQ_SOC2FPGAREQ)) + | (RSTMGR_HDSKREQ_SOC2FPGAREQ)); - /* Write Reset Manager brgmodrst[soc2fpga] = 1 */ - NOTICE("Assert S2F ...\n"); - mmio_setbits_32(SOCFPGA_RSTMGR(BRGMODRST), - RSTMGR_BRGMODRST_SOC2FPGA); + udelay(1000); - /* ToDo: Shall use udelay for product release */ - for (delay = 0; delay < 1000; delay++) { - /* dummy delay */ - } + /* + * To clear ack status + * Write Reset Manager hdskack[soc2fpga_flush_ack] = 1 + * This bit is W1S/W1C + */ + VERBOSE("Clear S2F hdskack ...\n"); + mmio_setbits_32(SOCFPGA_RSTMGR(HDSKACK), + ((~(brg_lst) << 9) & (RSTMGR_HDSKREQ_LWSOC2FPGAREQ + | RSTMGR_HDSKREQ_SOC2FPGAREQ)) + | (RSTMGR_HDSKACK_SOC2FPGAACK)); + + udelay(1000); - /* Write Reset Manager brgmodrst[soc2fpga] = 0 */ - NOTICE("Deassert S2F ...\n"); + /* + * To deassert reset + * Write Reset Manager brgmodrst[soc2fpga] = 0 + */ + VERBOSE("Deassert S2F ...\n"); mmio_clrbits_32(SOCFPGA_RSTMGR(BRGMODRST), - RSTMGR_BRGMODRST_SOC2FPGA); + (~brg_lst & (RSTMGR_BRGMODRST_SOC2FPGA + | RSTMGR_BRGMODRST_LWHPS2FPGA)) + | RSTMGR_BRGMODRST_SOC2FPGA); + + /* Set System Manager soc bridge control register[soc2fpga_ready_latency_enable] = 1 */ + VERBOSE("Set SOC soc2fpga_ready_latency_enable ...\n"); + mmio_setbits_32(SOCFPGA_SYSMGR(FPGA_BRIDGE_CTRL), + SYSMGR_SOC_BRIDGE_CTRL_EN); } +/**************** LWSOCFPGA ****************/ + /* Enable LWSOC2FPGA bridge */ - if (brg_mask & RSTMGR_BRGMODRSTMASK_LWHPS2FPGA) { - /* Write Reset Manager hdskreq[lwsoc2fpga_flush_req] = 1 */ - NOTICE("Set LWS2F hdskreq ...\n"); + brg_lst = mmio_read_32(SOCFPGA_RSTMGR(BRGMODRST)); + if ((brg_mask & RSTMGR_BRGMODRSTMASK_LWHPS2FPGA) + && ((brg_lst & RSTMGR_BRGMODRSTMASK_LWHPS2FPGA) != 0)) { + /* + * To request handshake + * Write Reset Manager hdskreq[lwsoc2fpga_flush_req] = 1 + */ + VERBOSE("Set LWS2F hdskreq ...\n"); mmio_setbits_32(SOCFPGA_RSTMGR(HDSKREQ), - RSTMGR_HDSKREQ_LWSOC2FPGAREQ); + ((~(brg_lst) << 9) & (RSTMGR_HDSKREQ_LWSOC2FPGAREQ + | RSTMGR_HDSKREQ_SOC2FPGAREQ)) + | (RSTMGR_HDSKREQ_LWSOC2FPGAREQ)); - /* Read Reset Manager hdskack[lwsoc2fpga] = 1 */ - ret = poll_idle_status_by_counter(SOCFPGA_RSTMGR(HDSKACK), - RSTMGR_HDSKACK_LWSOC2FPGAACK, RSTMGR_HDSKACK_LWSOC2FPGAACK, - 300); + udelay(1000); + + /* + * To poll idle status + * Read Reset Manager hdskack[lwsoc2fpga] = 1 + */ + if ((mmio_read_32(SOCFPGA_RSTMGR(BRGMODRST)) + & RSTMGR_BRGMODRST_LWHPS2FPGA) == 0x00) { + ret = poll_idle_status(SOCFPGA_RSTMGR(HDSKACK), + RSTMGR_HDSKREQ_LWSOC2FPGAREQ, RSTMGR_HDSKREQ_LWSOC2FPGAREQ, + 300); + } + + udelay(1000); if (ret < 0) { ERROR("LWS2F bridge enable: Timeout hdskack\n"); } - /* Write Reset Manager hdskreq[lwsoc2fpga_flush_req] = 0 */ - NOTICE("Clear LWS2F hdskreq ...\n"); + /* + * To assert reset + * Write Reset Manager brgmodrst[lwsoc2fpga] = 1 + */ + VERBOSE("Assert LWS2F ...\n"); + mmio_setbits_32(SOCFPGA_RSTMGR(BRGMODRST), + (~brg_lst & (RSTMGR_BRGMODRST_SOC2FPGA + | RSTMGR_BRGMODRST_LWHPS2FPGA)) + | RSTMGR_BRGMODRST_LWHPS2FPGA); + + udelay(1000); + + /* + * To clear idle request + * Write Reset Manager hdskreq[lwsoc2fpga_flush_req] = 0 + */ + VERBOSE("Clear LWS2F hdskreq ...\n"); mmio_clrbits_32(SOCFPGA_RSTMGR(HDSKREQ), - RSTMGR_HDSKREQ_LWSOC2FPGAREQ); + ((~(brg_lst) << 9) & (RSTMGR_HDSKREQ_LWSOC2FPGAREQ + | RSTMGR_HDSKREQ_SOC2FPGAREQ)) + | (RSTMGR_HDSKREQ_LWSOC2FPGAREQ)); - /* Write Reset Manager brgmodrst[lwsoc2fpga] = 1 */ - NOTICE("Assert LWS2F ...\n"); - mmio_setbits_32(SOCFPGA_RSTMGR(BRGMODRST), - RSTMGR_BRGMODRST_LWHPS2FPGA); + udelay(1000); - /* ToDo: Shall use udelay for product release */ - for (delay = 0; delay < 1000; delay++) { - /* dummy delay */ - } + /* + * To clear ack status + * Write Reset Manager hdskack[lwsoc2fpga_flush_ack] = 1 + * This bit is W1S/W1C + */ + VERBOSE("Clear LWS2F hdskack ...\n"); + mmio_setbits_32(SOCFPGA_RSTMGR(HDSKACK), + ((~(brg_lst) << 9) & (RSTMGR_HDSKREQ_LWSOC2FPGAREQ + | RSTMGR_HDSKREQ_SOC2FPGAREQ)) + | (RSTMGR_HDSKACK_SOC2FPGAACK)); - /* Write Reset Manager brgmodrst[lwsoc2fpga] = 0 */ - NOTICE("Deassert LWS2F ...\n"); + udelay(1000); + + /* + * To deassert reset + * Write Reset Manager brgmodrst[lwsoc2fpga] = 0 + */ + VERBOSE("Deassert LWS2F ...\n"); mmio_clrbits_32(SOCFPGA_RSTMGR(BRGMODRST), - RSTMGR_BRGMODRST_LWHPS2FPGA); + ((~brg_lst & (RSTMGR_BRGMODRST_SOC2FPGA + | RSTMGR_BRGMODRST_LWHPS2FPGA))) + | RSTMGR_BRGMODRST_LWHPS2FPGA); + + /* Set System Manager lwsoc bridge control register[lwsoc2fpga_ready_latency_enable] = 1 */ + VERBOSE("Set LWSOC lwsoc2fpga_ready_latency_enable ...\n"); + mmio_setbits_32(SOCFPGA_SYSMGR(FPGA_BRIDGE_CTRL), + SYSMGR_LWSOC_BRIDGE_CTRL_EN); } #else if (brg_mask != 0U) { @@ -505,11 +588,8 @@ int socfpga_bridges_enable(uint32_t mask) mmio_clrbits_32(SOCFPGA_RSTMGR(BRGMODRST), brg_mask); /* Wait until idle ack becomes 0 */ - ret = poll_idle_status(SOCFPGA_SYSMGR(NOC_IDLEACK), - noc_mask, 0, 300); - if (ret < 0) { - ERROR("S2F bridge enable: Timeout idle ack\n"); - } + ret_hps = poll_idle_status(SOCFPGA_SYSMGR(NOC_IDLEACK), + noc_mask, 0, 1000); } #endif @@ -519,173 +599,252 @@ int socfpga_bridges_enable(uint32_t mask) &f2s_idleack, &f2s_respempty, &f2s_cmdidle); #if PLATFORM_MODEL == PLAT_SOCFPGA_AGILEX5 /* Enable FPGA2SOC bridge */ - if (brg_mask & RSTMGR_BRGMODRSTMASK_FPGA2SOC) { - /* Write Reset Manager hdsken[fpgahsen] = 1 */ - NOTICE("Set FPGA hdsken(fpgahsen) ...\n"); + + if ((brg_mask & RSTMGR_BRGMODRSTMASK_FPGA2SOC) + && ((brg_lst & RSTMGR_BRGMODRSTMASK_FPGA2SOC) != 0)) { + /* + * To request handshake + * Write Reset Manager hdsken[fpgahsen] = 1 + */ + VERBOSE("Set FPGA hdsken(fpgahsen) ...\n"); mmio_setbits_32(SOCFPGA_RSTMGR(HDSKEN), RSTMGR_HDSKEN_FPGAHSEN); - /* Write Reset Manager hdskreq[fpgahsreq] = 1 */ - NOTICE("Set FPGA hdskreq(fpgahsreq) ...\n"); + /* + * To request handshake + * Write Reset Manager hdskreq[fpgahsreq] = 1 + */ + VERBOSE("Set FPGA hdskreq(fpgahsreq) ...\n"); mmio_setbits_32(SOCFPGA_RSTMGR(HDSKREQ), RSTMGR_HDSKREQ_FPGAHSREQ); - /* Read Reset Manager hdskack[fpgahsack] = 1 */ - NOTICE("Get FPGA hdskack(fpgahsack) ...\n"); - ret = poll_idle_status_by_counter(SOCFPGA_RSTMGR(HDSKACK), - RSTMGR_HDSKACK_FPGAHSACK, RSTMGR_HDSKACK_FPGAHSACK, - 300); + /* + * To poll idle status + * Read Reset Manager hdskack[fpgahsack] = 1 + */ + VERBOSE("Get FPGA hdskack(fpgahsack) ...\n"); + if ((mmio_read_32(SOCFPGA_RSTMGR(BRGMODRST)) + & RSTMGR_BRGMODRST_FPGA2SOC) == 0x00) { + ret = poll_idle_status(SOCFPGA_RSTMGR(HDSKACK), + RSTMGR_HDSKACK_FPGAHSACK, RSTMGR_HDSKACK_FPGAHSACK, + 300); + } if (ret < 0) { ERROR("FPGA bridge fpga handshake fpgahsreq: Timeout\n"); } - /* Write Reset Manager hdskreq[f2s_flush_req] = 1 */ - NOTICE("Set F2S hdskreq(f2s_flush_req) ...\n"); + /* + * To fence and drain traffic + * Write Reset Manager hdskreq[f2s_flush_req] = 1 + */ + VERBOSE("Set F2S hdskreq(f2s_flush_req) ...\n"); mmio_setbits_32(SOCFPGA_RSTMGR(HDSKREQ), RSTMGR_HDSKREQ_FPGA2SOCREQ); - /* Read Reset Manager hdskack[f2s_flush_ack] = 1 */ - NOTICE("Get F2S hdskack(f2s_flush_ack) ...\n"); - ret = poll_idle_status_by_counter(SOCFPGA_RSTMGR(HDSKACK), - RSTMGR_HDSKACK_FPGA2SOCACK, RSTMGR_HDSKACK_FPGA2SOCACK, - 300); + /* + * To poll idle status + * Read Reset Manager hdskack[f2s_flush_ack] = 1 + */ + VERBOSE("Get F2S hdskack(f2s_flush_ack) ...\n"); + if ((mmio_read_32(SOCFPGA_RSTMGR(BRGMODRST)) + & RSTMGR_BRGMODRST_FPGA2SOC) == 0x00) { + ret = poll_idle_status(SOCFPGA_RSTMGR(HDSKACK), + RSTMGR_HDSKACK_FPGA2SOCACK, RSTMGR_HDSKACK_FPGA2SOCACK, + 300); + } if (ret < 0) { ERROR("F2S bridge fpga handshake f2sdram_flush_req: Timeout\n"); } - /* Write Reset Manager hdskreq[fpgahsreq] = 1 */ - NOTICE("Clear FPGA hdskreq(fpgahsreq) ...\n"); + /* + * To clear idle request + * Write Reset Manager hdskreq[fpgahsreq] = 1 + */ + VERBOSE("Clear FPGA hdskreq(fpgahsreq) ...\n"); mmio_clrbits_32(SOCFPGA_RSTMGR(HDSKREQ), RSTMGR_HDSKREQ_FPGAHSREQ); - /* Write Reset Manager hdskreq[f2s_flush_req] = 1 */ - NOTICE("Clear F2S hdskreq(f2s_flush_req) ...\n"); + /* + * To clear idle request + * Write Reset Manager hdskreq[f2s_flush_req] = 1 + */ + VERBOSE("Clear F2S hdskreq(f2s_flush_req) ...\n"); mmio_clrbits_32(SOCFPGA_RSTMGR(HDSKREQ), RSTMGR_HDSKREQ_FPGA2SOCREQ); - /* Read Reset Manager hdskack[f2s_flush_ack] = 0 */ - NOTICE("Get F2SDRAM hdskack(f2s_flush_ack) ...\n"); - ret = poll_idle_status_by_counter(SOCFPGA_RSTMGR(HDSKACK), - RSTMGR_HDSKACK_FPGA2SOCACK, RSTMGR_HDSKACK_FPGA2SOCACK_DASRT, - 300); + /* + * To poll idle status + * Read Reset Manager hdskack[f2s_flush_ack] = 0 + */ + VERBOSE("Get F2SDRAM hdskack(f2s_flush_ack) ...\n"); + if ((mmio_read_32(SOCFPGA_RSTMGR(BRGMODRST)) + & RSTMGR_BRGMODRST_FPGA2SOC) == 0x00) { + ret = poll_idle_status(SOCFPGA_RSTMGR(HDSKACK), + RSTMGR_HDSKACK_FPGA2SOCACK, RSTMGR_HDSKACK_FPGA2SOCACK_DASRT, + 300); + } if (ret < 0) { ERROR("F2S bridge fpga handshake f2s_flush_ack: Timeout\n"); } - /* Read Reset Manager hdskack[fpgahsack] = 0 */ - NOTICE("Get FPGA hdskack(fpgahsack) ...\n"); - ret = poll_idle_status_by_counter(SOCFPGA_RSTMGR(HDSKACK), - RSTMGR_HDSKACK_FPGAHSACK, RSTMGR_HDSKACK_FPGAHSACK_DASRT, - 300); + /* + * To poll idle status + * Read Reset Manager hdskack[fpgahsack] = 0 + */ + VERBOSE("Get FPGA hdskack(fpgahsack) ...\n"); + if ((mmio_read_32(SOCFPGA_RSTMGR(BRGMODRST)) + & RSTMGR_BRGMODRST_FPGA2SOC) == 0x00) { + ret = poll_idle_status(SOCFPGA_RSTMGR(HDSKACK), + RSTMGR_HDSKACK_FPGAHSACK, RSTMGR_HDSKACK_FPGAHSACK_DASRT, + 300); + } if (ret < 0) { ERROR("F2S bridge fpga handshake fpgahsack: Timeout\n"); } - /* Write Reset Manager brgmodrst[fpga2soc] = 1 */ - NOTICE("Assert F2S ...\n"); + /* + * To assert reset + * Write Reset Manager brgmodrst[fpga2soc] = 1 + */ + VERBOSE("Assert F2S ...\n"); mmio_setbits_32(SOCFPGA_RSTMGR(BRGMODRST), RSTMGR_BRGMODRST_FPGA2SOC); - /* ToDo: Shall use udelay for product release */ - for (delay = 0; delay < 1000; delay++) { - /* dummy delay */ - } + udelay(1000); - /* Write Reset Manager brgmodrst[fpga2soc] = 0 */ - NOTICE("Deassert F2S ...\n"); + /* + * To deassert reset + * Write Reset Manager brgmodrst[fpga2soc] = 0 + */ + VERBOSE("Deassert F2S ...\n"); mmio_clrbits_32(SOCFPGA_RSTMGR(BRGMODRST), RSTMGR_BRGMODRST_FPGA2SOC); /* Write System Manager f2s bridge control register[f2soc_enable] = 1 */ - NOTICE("Deassert F2S f2soc_enable ...\n"); + VERBOSE("Deassert F2S f2soc_enable ...\n"); mmio_setbits_32(SOCFPGA_SYSMGR(F2S_BRIDGE_CTRL), SYSMGR_F2S_BRIDGE_CTRL_EN); } /* Enable FPGA2SDRAM bridge */ - if (brg_mask & RSTMGR_BRGMODRSTMASK_F2SDRAM0) { - /* Write Reset Manager hdsken[fpgahsen] = 1 */ - NOTICE("Set F2SDRAM hdsken(fpgahsen) ...\n"); + if ((brg_mask & RSTMGR_BRGMODRSTMASK_F2SDRAM0) + && ((brg_lst & RSTMGR_BRGMODRSTMASK_F2SDRAM0) != 0)) { + /* + * To request handshake + * Write Reset Manager hdsken[fpgahsen] = 1 + */ + VERBOSE("Set F2SDRAM hdsken(fpgahsen) ...\n"); mmio_setbits_32(SOCFPGA_RSTMGR(HDSKEN), RSTMGR_HDSKEN_FPGAHSEN); - /* Write Reset Manager hdskreq[fpgahsreq] = 1 */ - NOTICE("Set F2SDRAM hdskreq(fpgahsreq) ...\n"); + /* + * To request handshake + * Write Reset Manager hdskreq[fpgahsreq] = 1 + */ + VERBOSE("Set F2SDRAM hdskreq(fpgahsreq) ...\n"); mmio_setbits_32(SOCFPGA_RSTMGR(HDSKREQ), RSTMGR_HDSKREQ_FPGAHSREQ); - /* Read Reset Manager hdskack[fpgahsack] = 1 */ - NOTICE("Get F2SDRAM hdskack(fpgahsack) ...\n"); - ret = poll_idle_status_by_counter(SOCFPGA_RSTMGR(HDSKACK), - RSTMGR_HDSKACK_FPGAHSACK, RSTMGR_HDSKACK_FPGAHSACK, - 300); + /* + * To poll idle status + * Read Reset Manager hdskack[fpgahsack] = 1 + */ + VERBOSE("Get F2SDRAM hdskack(fpgahsack) ...\n"); + if ((mmio_read_32(SOCFPGA_RSTMGR(BRGMODRST)) + & RSTMGR_BRGMODRSTMASK_F2SDRAM0) == 0x00) { + ret = poll_idle_status(SOCFPGA_RSTMGR(HDSKACK), + RSTMGR_HDSKACK_FPGAHSACK, RSTMGR_HDSKACK_FPGAHSACK, + 300); + } if (ret < 0) { ERROR("F2SDRAM bridge fpga handshake fpgahsreq: Timeout\n"); } - /* Write Reset Manager hdskreq[f2sdram_flush_req] = 1 */ - NOTICE("Set F2SDRAM hdskreq(f2sdram_flush_req) ...\n"); + /* + * To fence and drain traffic + * Write Reset Manager hdskreq[f2sdram_flush_req] = 1 + */ + VERBOSE("Set F2SDRAM hdskreq(f2sdram_flush_req) ...\n"); mmio_setbits_32(SOCFPGA_RSTMGR(HDSKREQ), RSTMGR_HDSKREQ_F2SDRAM0REQ); - /* Read Reset Manager hdskack[f2sdram_flush_ack] = 1 */ - NOTICE("Get F2SDRAM hdskack(f2sdram_flush_ack) ...\n"); - ret = poll_idle_status_by_counter(SOCFPGA_RSTMGR(HDSKACK), - RSTMGR_HDSKACK_F2SDRAM0ACK, RSTMGR_HDSKACK_F2SDRAM0ACK, - 300); + /* + * To poll idle status + * Read Reset Manager hdskack[f2sdram_flush_ack] = 1 + */ + VERBOSE("Get F2SDRAM hdskack(f2sdram_flush_ack) ...\n"); + if ((mmio_read_32(SOCFPGA_RSTMGR(BRGMODRST)) + & RSTMGR_BRGMODRSTMASK_F2SDRAM0) == 0x00) { + ret = poll_idle_status(SOCFPGA_RSTMGR(HDSKACK), + RSTMGR_HDSKACK_F2SDRAM0ACK, RSTMGR_HDSKACK_F2SDRAM0ACK, + 300); + } if (ret < 0) { ERROR("F2SDRAM bridge fpga handshake f2sdram_flush_req: Timeout\n"); } - /* Write Reset Manager hdskreq[fpgahsreq] = 1 */ - NOTICE("Clear F2SDRAM hdskreq(fpgahsreq) ...\n"); + /* + * To clear idle request + * Write Reset Manager hdskreq[fpgahsreq] = 1 + */ + VERBOSE("Clear F2SDRAM hdskreq(fpgahsreq) ...\n"); mmio_clrbits_32(SOCFPGA_RSTMGR(HDSKREQ), RSTMGR_HDSKREQ_FPGAHSREQ); - /* Write Reset Manager hdskreq[f2sdram_flush_req] = 1 */ - NOTICE("Clear F2SDRAM hdskreq(f2sdram_flush_req) ...\n"); + /* + * To clear idle request + * Write Reset Manager hdskreq[f2sdram_flush_req] = 1 + */ + VERBOSE("Clear F2SDRAM hdskreq(f2sdram_flush_req) ...\n"); mmio_clrbits_32(SOCFPGA_RSTMGR(HDSKREQ), RSTMGR_HDSKREQ_F2SDRAM0REQ); - /* Read Reset Manager hdskack[f2sdram_flush_ack] = 0 */ - NOTICE("Get F2SDRAM hdskack(f2sdram_flush_ack) ...\n"); - ret = poll_idle_status_by_counter(SOCFPGA_RSTMGR(HDSKACK), - RSTMGR_HDSKACK_F2SDRAM0ACK, RSTMGR_HDSKACK_F2SDRAM0ACK_DASRT, - 300); + /* + * To poll idle status + * Read Reset Manager hdskack[f2sdram_flush_ack] = 0 + */ + VERBOSE("Get F2SDRAM hdskack(f2sdram_flush_ack) ...\n"); + if ((mmio_read_32(SOCFPGA_RSTMGR(BRGMODRST)) + & RSTMGR_BRGMODRSTMASK_F2SDRAM0) == 0x00) { + ret = poll_idle_status(SOCFPGA_RSTMGR(HDSKACK), + RSTMGR_HDSKACK_F2SDRAM0ACK, RSTMGR_HDSKACK_F2SDRAM0ACK_DASRT, + 300); + } if (ret < 0) { ERROR("F2SDRAM bridge fpga handshake f2sdram_flush_ack: Timeout\n"); } - /* Read Reset Manager hdskack[fpgahsack] = 0 */ - NOTICE("Get F2SDRAM hdskack(fpgahsack) ...\n"); - ret = poll_idle_status_by_counter(SOCFPGA_RSTMGR(HDSKACK), - RSTMGR_HDSKACK_FPGAHSACK, RSTMGR_HDSKACK_FPGAHSACK_DASRT, - 300); + /* + * To poll idle status + * Read Reset Manager hdskack[fpgahsack] = 0 + */ + VERBOSE("Get F2SDRAM hdskack(fpgahsack) ...\n"); + if ((mmio_read_32(SOCFPGA_RSTMGR(BRGMODRST)) + & RSTMGR_BRGMODRSTMASK_F2SDRAM0) == 0x00) { + ret = poll_idle_status(SOCFPGA_RSTMGR(HDSKACK), + RSTMGR_HDSKACK_FPGAHSACK, RSTMGR_HDSKACK_FPGAHSACK_DASRT, + 300); + } if (ret < 0) { ERROR("F2SDRAM bridge fpga handshake fpgahsack: Timeout\n"); } - /* Write Reset Manager brgmodrst[fpga2sdram] = 1 */ - NOTICE("Assert F2SDRAM ...\n"); + /* + * To assert reset + * Write Reset Manager brgmodrst[fpga2sdram] = 1 + */ + VERBOSE("Assert F2SDRAM ...\n"); mmio_setbits_32(SOCFPGA_RSTMGR(BRGMODRST), RSTMGR_BRGMODRST_F2SSDRAM0); - /* ToDo: Shall use udelay for product release */ - for (delay = 0; delay < 1000; delay++) { - /* dummy delay */ - } - - /* Write Reset Manager brgmodrst[fpga2sdram] = 0 */ - NOTICE("Deassert F2SDRAM ...\n"); - mmio_clrbits_32(SOCFPGA_RSTMGR(BRGMODRST), - RSTMGR_BRGMODRST_F2SSDRAM0); + udelay(1000); /* - * Clear fpga2sdram_manager_main_SidebandManager_FlagOutClr0 - * f2s_ready_latency_enable + * To deassert reset + * Write Reset Manager brgmodrst[fpga2sdram] = 0 */ - NOTICE("Clear F2SDRAM f2s_ready_latency_enable ...\n"); - mmio_setbits_32(SOCFPGA_F2SDRAMMGR(SIDEBANDMGR_FLAGOUTCLR0), - FLAGOUTCLR0_F2SDRAM0_ENABLE); + VERBOSE("Deassert F2SDRAM ...\n"); + mmio_clrbits_32(SOCFPGA_RSTMGR(BRGMODRST), + RSTMGR_BRGMODRST_F2SSDRAM0); } #else if (brg_mask != 0U) { @@ -711,6 +870,7 @@ int socfpga_bridges_enable(uint32_t mask) udelay(5); } #endif + ret = ret | ret_hps; return ret; } @@ -774,6 +934,83 @@ int socfpga_bridges_disable(uint32_t mask) /* Disable s2f bridge */ socfpga_s2f_bridge_mask(mask, &brg_mask, &noc_mask); +#if PLATFORM_MODEL == PLAT_SOCFPGA_AGILEX5 + /* Disable SOC2FPGA bridge */ + if (brg_mask & RSTMGR_BRGMODRSTMASK_SOC2FPGA) { + /* + * To clear handshake + * Write Reset Manager hdskreq[soc2fpga_flush_req] = 0 + */ + VERBOSE("Set S2F hdskreq ...\n"); + mmio_clrbits_32(SOCFPGA_RSTMGR(HDSKREQ), + RSTMGR_HDSKREQ_SOC2FPGAREQ); + + /* + * To poll idle status + * Read Reset Manager hdskack[soc2fpga] = 0 + */ + ret = poll_idle_status(SOCFPGA_RSTMGR(HDSKACK), + RSTMGR_HDSKACK_SOC2FPGAACK, RSTMGR_HDSKACK_SOC2FPGAACK_DASRT, + 300); + + if (ret < 0) { + ERROR("S2F bridge enable: Timeout hdskack\n"); + } + + /* + * To assert reset + * Write Reset Manager brgmodrst[soc2fpga] = 1 + */ + VERBOSE("Assert S2F ...\n"); + mmio_setbits_32(SOCFPGA_RSTMGR(BRGMODRST), + RSTMGR_BRGMODRST_SOC2FPGA); + + /* Clear System Manager soc bridge control register[soc2fpga_ready_latency_enable] = 1 */ + VERBOSE("Clear SOC soc2fpga_ready_latency_enable ...\n"); + mmio_clrbits_32(SOCFPGA_SYSMGR(FPGA_BRIDGE_CTRL), + SYSMGR_SOC_BRIDGE_CTRL_EN); + + udelay(1000); + } + + /* Disable LWSOC2FPGA bridge */ + if (brg_mask & RSTMGR_BRGMODRSTMASK_LWHPS2FPGA) { + /* + * To clear handshake + * Write Reset Manager hdskreq[lwsoc2fpga_flush_req] = 0 + */ + VERBOSE("Set LWS2F hdskreq ...\n"); + mmio_clrbits_32(SOCFPGA_RSTMGR(HDSKREQ), + RSTMGR_HDSKREQ_LWSOC2FPGAREQ); + + /* + * To poll idle status + * Read Reset Manager hdskack[lwsoc2fpga] = 0 + */ + ret = poll_idle_status(SOCFPGA_RSTMGR(HDSKACK), + RSTMGR_HDSKACK_LWSOC2FPGAACK, RSTMGR_HDSKACK_LWSOC2FPGAACK_DASRT, + 300); + + if (ret < 0) { + ERROR("LWS2F bridge enable: Timeout hdskack\n"); + } + + /* + * To assert reset + * Write Reset Manager brgmodrst[lwsoc2fpga] = 1 + */ + VERBOSE("Assert LWS2F ...\n"); + mmio_setbits_32(SOCFPGA_RSTMGR(BRGMODRST), + RSTMGR_BRGMODRST_LWHPS2FPGA); + + /* Clear System Manager lwsoc bridge control register[lwsoc2fpga_ready_latency_enable] = 1 */ + VERBOSE("Clear LWSOC lwsoc2fpga_ready_latency_enable ...\n"); + mmio_clrbits_32(SOCFPGA_SYSMGR(FPGA_BRIDGE_CTRL), + SYSMGR_LWSOC_BRIDGE_CTRL_EN); + + udelay(1000); + } +#else if (brg_mask != 0U) { mmio_setbits_32(SOCFPGA_SYSMGR(NOC_IDLEREQ_SET), noc_mask); @@ -796,11 +1033,146 @@ int socfpga_bridges_disable(uint32_t mask) mmio_write_32(SOCFPGA_SYSMGR(NOC_TIMEOUT), 0); } +#endif /* Disable f2s bridge */ socfpga_f2s_bridge_mask(mask, &brg_mask, &f2s_idlereq, &f2s_force_drain, &f2s_en, &f2s_idleack, &f2s_respempty, &f2s_cmdidle); +#if PLATFORM_MODEL == PLAT_SOCFPGA_AGILEX5 + /* Disable FPGA2SOC bridge */ + if (brg_mask & RSTMGR_BRGMODRSTMASK_FPGA2SOC) { + /* + * To request handshake + * Write Reset Manager hdsken[fpgahsen] = 1 + */ + VERBOSE("Set FPGA hdsken(fpgahsen) ...\n"); + mmio_setbits_32(SOCFPGA_RSTMGR(HDSKEN), RSTMGR_HDSKEN_FPGAHSEN); + + /* + * To clear handshake request + * Write Reset Manager hdskreq[fpgahsreq] = 0 + */ + VERBOSE("Clear FPGA hdskreq(fpgahsreq) ...\n"); + mmio_clrbits_32(SOCFPGA_RSTMGR(HDSKREQ), RSTMGR_HDSKREQ_FPGAHSREQ); + + /* + * To clear handshake request + * Write Reset Manager hdskreq[f2s_flush_req] = 0 + */ + VERBOSE("Clear F2S hdskreq(f2s_flush_req) ...\n"); + mmio_clrbits_32(SOCFPGA_RSTMGR(HDSKREQ), + RSTMGR_HDSKREQ_FPGA2SOCREQ); + + /* + * To poll idle status + * Read Reset Manager hdskack[f2s_flush_ack] = 0 + */ + VERBOSE("Get F2SDRAM hdskack(f2s_flush_ack) ...\n"); + ret = poll_idle_status(SOCFPGA_RSTMGR(HDSKACK), + RSTMGR_HDSKACK_FPGA2SOCACK, RSTMGR_HDSKACK_FPGA2SOCACK_DASRT, + 300); + + if (ret < 0) { + ERROR("F2S bridge fpga handshake f2s_flush_ack: Timeout\n"); + } + + /* + * To poll idle status + * Read Reset Manager hdskack[fpgahsack] = 0 + */ + VERBOSE("Get FPGA hdskack(fpgahsack) ...\n"); + ret = poll_idle_status(SOCFPGA_RSTMGR(HDSKACK), + RSTMGR_HDSKACK_FPGAHSACK, RSTMGR_HDSKACK_FPGAHSACK_DASRT, + 300); + + if (ret < 0) { + ERROR("F2S bridge fpga handshake fpgahsack: Timeout\n"); + } + + /* + * To assert reset + * Write Reset Manager brgmodrst[fpga2soc] = 1 + */ + VERBOSE("Assert F2S ...\n"); + mmio_setbits_32(SOCFPGA_RSTMGR(BRGMODRST), RSTMGR_BRGMODRST_FPGA2SOC); + + udelay(1000); + + /* Write System Manager f2s bridge control register[f2soc_enable] = 0 */ + VERBOSE("Assert F2S f2soc_enable ...\n"); + mmio_clrbits_32(SOCFPGA_SYSMGR(F2S_BRIDGE_CTRL), + SYSMGR_F2S_BRIDGE_CTRL_EN); + } + + /* Disable FPGA2SDRAM bridge */ + if (brg_mask & RSTMGR_BRGMODRSTMASK_F2SDRAM0) { + /* + * To request handshake + * Write Reset Manager hdsken[fpgahsen] = 1 + */ + VERBOSE("Set F2SDRAM hdsken(fpgahsen) ...\n"); + mmio_setbits_32(SOCFPGA_RSTMGR(HDSKEN), RSTMGR_HDSKEN_FPGAHSEN); + + /* + * To clear handshake request + * Write Reset Manager hdskreq[fpgahsreq] = 0 + */ + VERBOSE("Clear F2SDRAM hdskreq(fpgahsreq) ...\n"); + mmio_clrbits_32(SOCFPGA_RSTMGR(HDSKREQ), RSTMGR_HDSKREQ_FPGAHSREQ); + + /* + * To clear handshake request + * Write Reset Manager hdskreq[f2sdram_flush_req] = 0 + */ + VERBOSE("Clear F2SDRAM hdskreq(f2sdram_flush_req) ...\n"); + mmio_clrbits_32(SOCFPGA_RSTMGR(HDSKREQ), RSTMGR_HDSKREQ_F2SDRAM0REQ); + + /* + * To poll idle status + * Read Reset Manager hdskack[f2sdram_flush_ack] = 0 + */ + VERBOSE("Get F2SDRAM hdskack(f2sdram_flush_ack) ...\n"); + ret = poll_idle_status(SOCFPGA_RSTMGR(HDSKACK), + RSTMGR_HDSKACK_F2SDRAM0ACK, RSTMGR_HDSKACK_F2SDRAM0ACK_DASRT, + 300); + + if (ret < 0) { + ERROR("F2SDRAM bridge fpga handshake f2sdram_flush_ack: Timeout\n"); + } + + /* + * To poll idle status + * Read Reset Manager hdskack[fpgahsack] = 0 + */ + VERBOSE("Get F2SDRAM hdskack(fpgahsack) ...\n"); + ret = poll_idle_status(SOCFPGA_RSTMGR(HDSKACK), + RSTMGR_HDSKACK_FPGAHSACK, RSTMGR_HDSKACK_FPGAHSACK_DASRT, + 300); + + if (ret < 0) { + ERROR("F2SDRAM bridge fpga handshake fpgahsack: Timeout\n"); + } + + /* + * To assert reset + * Write Reset Manager brgmodrst[fpga2sdram] = 1 + */ + VERBOSE("Assert F2SDRAM ...\n"); + mmio_setbits_32(SOCFPGA_RSTMGR(BRGMODRST), + RSTMGR_BRGMODRST_F2SSDRAM0); + + udelay(1000); + + /* + * Assert fpga2sdram_manager_main_SidebandManager_FlagOutClr0 + * f2s_ready_latency_enable + */ + VERBOSE("Assert F2SDRAM f2s_ready_latency_enable ...\n"); + mmio_clrbits_32(SOCFPGA_F2SDRAMMGR(SIDEBANDMGR_FLAGOUTCLR0), + FLAGOUTCLR0_F2SDRAM0_ENABLE); + } +#else if (brg_mask != 0U) { if (mmio_read_32(SOCFPGA_RSTMGR(BRGMODRST)) & brg_mask) { @@ -843,6 +1215,7 @@ int socfpga_bridges_disable(uint32_t mask) mmio_setbits_32(SOCFPGA_F2SDRAMMGR(SIDEBANDMGR_FLAGOUTCLR0), f2s_idlereq); } +#endif return ret; } @@ -910,4 +1283,4 @@ int socfpga_cpurstrelease(unsigned int cpu_id) } while (timeout-- > 0); return RSTMGR_RET_ERROR; -} +} \ No newline at end of file diff --git a/plat/intel/soc/common/soc/socfpga_system_manager.c b/plat/intel/soc/common/soc/socfpga_system_manager.c new file mode 100644 index 00000000..4223b2b1 --- /dev/null +++ b/plat/intel/soc/common/soc/socfpga_system_manager.c @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2024, Altera Corporation. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <assert.h> +#include <errno.h> +#include <common/debug.h> +#include <drivers/delay_timer.h> +#include <lib/mmio.h> +#include <platform_def.h> + +#include "socfpga_system_manager.h" + +uint32_t intel_hps_get_jtag_id(void) +{ + uint32_t jtag_id = 0x00; + + jtag_id = (mmio_read_32(SOCFPGA_SYSMGR(BOOT_SCRATCH_COLD_4))); + + INFO("%s: JTAG ID: 0x%x\n", __func__, jtag_id); + + return jtag_id; +} + +/* Check for Agilex5 SM4 */ +bool is_agilex5_A5F0(void) +{ + return ((intel_hps_get_jtag_id() & JTAG_ID_MASK) == A5F0_JTAG_ID); +} diff --git a/plat/intel/soc/common/socfpga_delay_timer.c b/plat/intel/soc/common/socfpga_delay_timer.c index 8fce5cf8..37590094 100644 --- a/plat/intel/soc/common/socfpga_delay_timer.c +++ b/plat/intel/soc/common/socfpga_delay_timer.c @@ -1,5 +1,6 @@ /* * Copyright (c) 2019-2022, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2024, Altera Corporation. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -10,7 +11,6 @@ #include <lib/mmio.h> #include "socfpga_plat_def.h" - #if PLATFORM_MODEL == PLAT_SOCFPGA_AGILEX #include "agilex_clock_manager.h" #elif PLATFORM_MODEL == PLAT_SOCFPGA_N5X @@ -19,7 +19,7 @@ #include "s10_clock_manager.h" #endif -#define SOCFPGA_GLOBAL_TIMER 0xffd01000 +#define SOCFPGA_GLOBAL_TIMER PLAT_TIMER_BASE_ADDR #define SOCFPGA_GLOBAL_TIMER_EN 0x3 static timer_ops_t plat_timer_ops; @@ -44,7 +44,6 @@ void socfpga_delay_timer_init_args(void) plat_timer_ops.clk_div = PLAT_SYS_COUNTER_FREQ_IN_MHZ; timer_init(&plat_timer_ops); - } void socfpga_delay_timer_init(void) @@ -52,9 +51,6 @@ void socfpga_delay_timer_init(void) socfpga_delay_timer_init_args(); mmio_write_32(SOCFPGA_GLOBAL_TIMER, SOCFPGA_GLOBAL_TIMER_EN); - NOTICE("BL31 CLK freq = %d MHz\n", PLAT_SYS_COUNTER_FREQ_IN_MHZ); - asm volatile("msr cntp_ctl_el0, %0" : : "r" (SOCFPGA_GLOBAL_TIMER_EN)); asm volatile("msr cntp_tval_el0, %0" : : "r" (~0)); - } diff --git a/plat/intel/soc/common/socfpga_image_load.c b/plat/intel/soc/common/socfpga_image_load.c index a5c32799..ee791584 100644 --- a/plat/intel/soc/common/socfpga_image_load.c +++ b/plat/intel/soc/common/socfpga_image_load.c @@ -1,5 +1,6 @@ /* * Copyright (c) 2019, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2024, Altera Corporation. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -12,7 +13,13 @@ ******************************************************************************/ void plat_flush_next_bl_params(void) { + /* + * We cannot flush these descriptors on the Agilex5 platform, + * since the BL2 runs on the OCRAM and this OCRAM is not cache coherent. + */ +#if PLATFORM_MODEL != PLAT_SOCFPGA_AGILEX5 flush_bl_params_desc(); +#endif } /******************************************************************************* diff --git a/plat/intel/soc/common/socfpga_psci.c b/plat/intel/soc/common/socfpga_psci.c index c93e13f5..50d4820a 100644 --- a/plat/intel/soc/common/socfpga_psci.c +++ b/plat/intel/soc/common/socfpga_psci.c @@ -1,6 +1,7 @@ /* * Copyright (c) 2019-2023, ARM Limited and Contributors. All rights reserved. * Copyright (c) 2019-2023, Intel Corporation. All rights reserved. + * Copyright (c) 2024, Altera Corporation. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -16,8 +17,13 @@ #include <lib/mmio.h> #include <lib/psci/psci.h> #include <plat/common/platform.h> +#if PLATFORM_MODEL == PLAT_SOCFPGA_AGILEX5 +#include "agilex5_cache.h" +#endif +#include "ccu/ncore_ccu.h" #include "socfpga_mailbox.h" #include "socfpga_plat_def.h" +#include "socfpga_private.h" #include "socfpga_reset_manager.h" #include "socfpga_sip_svc.h" #include "socfpga_system_manager.h" @@ -183,11 +189,20 @@ static void __dead2 socfpga_system_reset(void) { uint32_t addr_buf[2]; - memcpy(addr_buf, &intel_rsu_update_address, - sizeof(intel_rsu_update_address)); + memcpy_s(addr_buf, sizeof(intel_rsu_update_address), + &intel_rsu_update_address, sizeof(intel_rsu_update_address)); + if (intel_rsu_update_address) { mailbox_rsu_update(addr_buf); } else { +#if CACHE_FLUSH + /* ATF Flush and Invalidate Cache */ + dcsw_op_all(DCCISW); + invalidate_cache_low_el(); +#if PLATFORM_MODEL == PLAT_SOCFPGA_AGILEX5 + flush_l3_dcache(); +#endif +#endif mailbox_reset_cold(); } @@ -198,6 +213,16 @@ static void __dead2 socfpga_system_reset(void) static int socfpga_system_reset2(int is_vendor, int reset_type, u_register_t cookie) { + +#if CACHE_FLUSH + /* + * ATF Flush and Invalidate Cache due to hardware limitation + * of auto Flush and Invalidate Cache. + */ + dcsw_op_all(DCCISW); + invalidate_cache_low_el(); +#endif + #if PLATFORM_MODEL == PLAT_SOCFPGA_AGILEX5 mailbox_reset_warm(reset_type); #else diff --git a/plat/intel/soc/common/socfpga_ros.c b/plat/intel/soc/common/socfpga_ros.c new file mode 100644 index 00000000..ea373845 --- /dev/null +++ b/plat/intel/soc/common/socfpga_ros.c @@ -0,0 +1,188 @@ +/* + * Copyright (c) 2024, Intel Corporation. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +/* system header files*/ +#include <assert.h> +#include <endian.h> +#include <string.h> + +/* CRC function header */ +#include <common/tf_crc32.h> + +/* Cadense qspi driver*/ +#include <qspi/cadence_qspi.h> + +/* Mailbox driver*/ +#include <socfpga_mailbox.h> + +#include <socfpga_ros.h> + +static void swap_bits(char *const data, uint32_t len) +{ + uint32_t x, y; + char tmp; + + for (x = 0U; x < len; x++) { + tmp = 0U; + for (y = 0U; y < 8; y++) { + tmp <<= 1; + if (data[x] & 1) { + tmp |= 1; + } + data[x] >>= 1; + } + data[x] = tmp; + } +} + +static uint32_t get_current_image_index(spt_table_t *spt_buf, uint32_t *const img_index) +{ + if (spt_buf == NULL || img_index == NULL) { + return ROS_RET_INVALID; + } + + uint32_t ret; + unsigned long current_image; + uint32_t rsu_status[RSU_STATUS_RES_SIZE]; + + if (spt_buf->partitions < SPT_MIN_PARTITIONS || spt_buf->partitions > SPT_MAX_PARTITIONS) { + return ROS_IMAGE_PARTNUM_OVFL; + } + + ret = mailbox_rsu_status(rsu_status, RSU_STATUS_RES_SIZE); + if (ret != MBOX_RET_OK) { + return ROS_RET_NOT_RSU_MODE; + } + + current_image = ADDR_64(rsu_status[1], rsu_status[0]); + NOTICE("ROS: Current image is at 0x%08lx\n", current_image); + + *img_index = 0U; + for (uint32_t index = 0U ; index < spt_buf->partitions; index++) { + if (spt_buf->partition[index].offset == current_image) { + *img_index = index; + break; + } + } + + if (*img_index == 0U) { + return ROS_IMAGE_INDEX_ERR; + } + + return ROS_RET_OK; +} + +static uint32_t load_and_check_spt(spt_table_t *spt_ptr, size_t offset) +{ + + if (spt_ptr == NULL || offset == 0U) { + return ROS_RET_INVALID; + } + + int ret; + uint32_t calc_crc; + static spt_table_t spt_data; + + ret = cad_qspi_read(spt_ptr, offset, SPT_SIZE); + if (ret != 0U) { + return ROS_QSPI_READ_ERROR; + } + + if (spt_ptr->magic_number != SPT_MAGIC_NUMBER) { + return ROS_SPT_BAD_MAGIC_NUM; + } + + if (spt_ptr->partitions < SPT_MIN_PARTITIONS || spt_ptr->partitions > SPT_MAX_PARTITIONS) { + return ROS_IMAGE_PARTNUM_OVFL; + } + + memcpy_s(&spt_data, SPT_SIZE, spt_ptr, SPT_SIZE); + spt_data.checksum = 0U; + swap_bits((char *)&spt_data, SPT_SIZE); + + calc_crc = tf_crc32(0, (uint8_t *)&spt_data, SPT_SIZE); + if (bswap32(spt_ptr->checksum) != calc_crc) { + return ROS_SPT_CRC_ERROR; + } + + NOTICE("ROS: SPT table at 0x%08lx is verified\n", offset); + return ROS_RET_OK; +} + +static uint32_t get_spt(spt_table_t *spt_buf) +{ + if (spt_buf == NULL) { + return ROS_RET_INVALID; + } + + uint32_t ret; + uint32_t spt_offset[RSU_GET_SPT_RESP_SIZE]; + + /* Get SPT offset from SDM via mailbox commands */ + ret = mailbox_rsu_get_spt_offset(spt_offset, RSU_GET_SPT_RESP_SIZE); + if (ret != MBOX_RET_OK) { + WARN("ROS: Not booted in RSU mode\n"); + return ROS_RET_NOT_RSU_MODE; + } + + /* Print the SPT table addresses */ + VERBOSE("ROS: SPT0 0x%08lx\n", ADDR_64(spt_offset[0], spt_offset[1])); + VERBOSE("ROS: SPT1 0x%08lx\n", ADDR_64(spt_offset[2], spt_offset[3])); + + /* Load and validate SPT1*/ + ret = load_and_check_spt(spt_buf, ADDR_64(spt_offset[2], spt_offset[3])); + if (ret != ROS_RET_OK) { + /* Load and validate SPT0*/ + ret = load_and_check_spt(spt_buf, ADDR_64(spt_offset[0], spt_offset[1])); + if (ret != ROS_RET_OK) { + WARN("Both SPT tables are unusable\n"); + return ret; + } + } + + return ROS_RET_OK; +} + +uint32_t ros_qspi_get_ssbl_offset(unsigned long *offset) +{ + if (offset == NULL) { + return ROS_RET_INVALID; + } + + uint32_t ret, img_index; + char ssbl_name[SPT_PARTITION_NAME_LENGTH]; + static spt_table_t spt; + + ret = get_spt(&spt); + if (ret != ROS_RET_OK) { + return ret; + } + + ret = get_current_image_index(&spt, &img_index); + if (ret != ROS_RET_OK) { + return ret; + } + + if (strncmp(spt.partition[img_index].name, FACTORY_IMAGE, + SPT_PARTITION_NAME_LENGTH) == 0U) { + strlcpy(ssbl_name, FACTORY_SSBL, SPT_PARTITION_NAME_LENGTH); + } else { + strlcpy(ssbl_name, spt.partition[img_index].name, + SPT_PARTITION_NAME_LENGTH); + strlcat(ssbl_name, SSBL_SUFFIX, SPT_PARTITION_NAME_LENGTH); + } + + for (uint32_t index = 0U; index < spt.partitions; index++) { + if (strncmp(spt.partition[index].name, ssbl_name, + SPT_PARTITION_NAME_LENGTH) == 0U) { + *offset = spt.partition[index].offset; + NOTICE("ROS: Corresponding SSBL is at 0x%08lx\n", *offset); + return ROS_RET_OK; + } + } + + return ROS_IMAGE_INDEX_ERR; +} diff --git a/plat/intel/soc/common/socfpga_sip_svc.c b/plat/intel/soc/common/socfpga_sip_svc.c index c6530cf3..3c223c14 100644 --- a/plat/intel/soc/common/socfpga_sip_svc.c +++ b/plat/intel/soc/common/socfpga_sip_svc.c @@ -1,5 +1,7 @@ /* * Copyright (c) 2019-2023, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2019-2023, Intel Corporation. All rights reserved. + * Copyright (c) 2024, Altera Corporation. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -26,6 +28,9 @@ static int read_block, max_blocks; static uint32_t send_id, rcv_id; static uint32_t bytes_per_block, blocks_submitted; static bool bridge_disable; +#if PLATFORM_MODEL == PLAT_SOCFPGA_AGILEX5 +static uint32_t g_remapper_bypass; +#endif /* RSU static variables */ static uint32_t rsu_dcmf_ver[4] = {0}; @@ -229,6 +234,10 @@ static int intel_fpga_config_start(uint32_t flag) request_type = BITSTREAM_AUTH; } +#if PLATFORM_MODEL == PLAT_SOCFPGA_AGILEX5 + intel_smmu_hps_remapper_init(0U); +#endif + mailbox_clear_response(); mailbox_send_cmd(MBOX_JOB_ID, MBOX_CMD_CANCEL, NULL, 0U, @@ -280,6 +289,9 @@ static bool is_fpga_config_buffer_full(void) bool is_address_in_ddr_range(uint64_t addr, uint64_t size) { + uint128_t dram_max_sz = (uint128_t)DRAM_BASE + (uint128_t)DRAM_SIZE; + uint128_t dram_region_end = (uint128_t)addr + (uint128_t)size; + if (!addr && !size) { return true; } @@ -289,7 +301,7 @@ bool is_address_in_ddr_range(uint64_t addr, uint64_t size) if (addr < BL31_LIMIT) { return false; } - if (addr + size > DRAM_BASE + DRAM_SIZE) { + if (dram_region_end > dram_max_sz) { return false; } @@ -307,6 +319,10 @@ static uint32_t intel_fpga_config_write(uint64_t mem, uint64_t size) return INTEL_SIP_SMC_STATUS_REJECTED; } +#if PLATFORM_MODEL == PLAT_SOCFPGA_AGILEX5 + intel_smmu_hps_remapper_init(&mem); +#endif + for (i = 0; i < FPGA_CONFIG_BUFFER_SIZE; i++) { int j = (i + current_buffer) % FPGA_CONFIG_BUFFER_SIZE; @@ -406,6 +422,7 @@ static int is_out_of_sec_range(uint64_t reg_addr) case(SOCFPGA_MEMCTRL(DIAGINTTEST)): /* DIAGINTTEST */ case(SOCFPGA_MEMCTRL(DERRADDRA)): /* DERRADDRA */ + case(SOCFPGA_ECC_QSPI(INITSTAT)): /* ECC_QSPI_INITSTAT */ case(SOCFPGA_SYSMGR(EMAC_0)): /* EMAC0 */ case(SOCFPGA_SYSMGR(EMAC_1)): /* EMAC1 */ case(SOCFPGA_SYSMGR(EMAC_2)): /* EMAC2 */ @@ -420,8 +437,19 @@ static int is_out_of_sec_range(uint64_t reg_addr) case(SOCFPGA_SYSMGR(BOOT_SCRATCH_COLD_1)): /* BOOT_SCRATCH_COLD1 */ case(SOCFPGA_SYSMGR(BOOT_SCRATCH_COLD_8)): /* BOOT_SCRATCH_COLD8 */ case(SOCFPGA_SYSMGR(BOOT_SCRATCH_COLD_9)): /* BOOT_SCRATCH_COLD9 */ - return 0; #endif + case(SOCFPGA_ECC_QSPI(CTRL)): /* ECC_QSPI_CTRL */ + case(SOCFPGA_ECC_QSPI(ERRINTEN)): /* ECC_QSPI_ERRINTEN */ + case(SOCFPGA_ECC_QSPI(ERRINTENS)): /* ECC_QSPI_ERRINTENS */ + case(SOCFPGA_ECC_QSPI(ERRINTENR)): /* ECC_QSPI_ERRINTENR */ + case(SOCFPGA_ECC_QSPI(INTMODE)): /* ECC_QSPI_INTMODE */ + case(SOCFPGA_ECC_QSPI(ECC_ACCCTRL)): /* ECC_QSPI_ECC_ACCCTRL */ + case(SOCFPGA_ECC_QSPI(ECC_STARTACC)): /* ECC_QSPI_ECC_STARTACC */ + case(SOCFPGA_ECC_QSPI(ECC_WDCTRL)): /* ECC_QSPI_ECC_WDCTRL */ + case(SOCFPGA_ECC_QSPI(INTSTAT)): /* ECC_QSPI_INTSTAT */ + case(SOCFPGA_ECC_QSPI(INTTEST)): /* ECC_QSPI_INTMODE */ + return 0; + default: break; } @@ -448,7 +476,15 @@ uint32_t intel_secure_reg_write(uint64_t reg_addr, uint32_t val, return INTEL_SIP_SMC_STATUS_ERROR; } - mmio_write_32(reg_addr, val); + switch (reg_addr) { + case(SOCFPGA_ECC_QSPI(INTSTAT)): /* ECC_QSPI_INTSTAT */ + case(SOCFPGA_ECC_QSPI(INTTEST)): /* ECC_QSPI_INTMODE */ + mmio_write_16(reg_addr, val); + break; + default: + mmio_write_32(reg_addr, val); + break; + } return intel_secure_reg_read(reg_addr, retval); } @@ -477,6 +513,16 @@ static uint32_t intel_rsu_status(uint64_t *respbuf, unsigned int respbuf_sz) return INTEL_SIP_SMC_STATUS_OK; } +static uint32_t intel_rsu_get_device_info(uint32_t *respbuf, + unsigned int respbuf_sz) +{ + if (mailbox_rsu_get_device_info((uint32_t *)respbuf, respbuf_sz) < 0) { + return INTEL_SIP_SMC_RSU_ERROR; + } + + return INTEL_SIP_SMC_STATUS_OK; +} + uint32_t intel_rsu_update(uint64_t update_address) { if (update_address > SIZE_MAX) { @@ -689,15 +735,56 @@ uint32_t intel_hps_set_bridges(uint64_t enable, uint64_t mask) } /* SDM SEU Error services */ -static uint32_t intel_sdm_seu_err_read(uint64_t *respbuf, unsigned int respbuf_sz) +static uint32_t intel_sdm_seu_err_read(uint32_t *respbuf, unsigned int respbuf_sz) { - if (mailbox_seu_err_status((uint32_t *)respbuf, respbuf_sz) < 0) { + if (mailbox_seu_err_status(respbuf, respbuf_sz) < 0) { return INTEL_SIP_SMC_SEU_ERR_READ_ERROR; } return INTEL_SIP_SMC_STATUS_OK; } +/* SDM SAFE SEU Error inject services */ +static uint32_t intel_sdm_safe_inject_seu_err(uint32_t *command, uint32_t len) +{ + if (mailbox_safe_inject_seu_err(command, len) < 0) { + return INTEL_SIP_SMC_SEU_ERR_READ_ERROR; + } + + return INTEL_SIP_SMC_STATUS_OK; +} + +#if PLATFORM_MODEL == PLAT_SOCFPGA_AGILEX5 +/* SMMU HPS Remapper */ +void intel_smmu_hps_remapper_init(uint64_t *mem) +{ + /* Read out Bit 1 value */ + uint32_t remap = (mmio_read_32(SOCFPGA_SYSMGR(BOOT_SCRATCH_POR_1)) & 0x02); + + if ((remap == 0x00) && (g_remapper_bypass == 0x00)) { + /* Update DRAM Base address for SDM SMMU */ + mmio_write_32(SOCFPGA_SYSMGR(SDM_BE_ARADDR_REMAP), DRAM_BASE); + mmio_write_32(SOCFPGA_SYSMGR(SDM_BE_AWADDR_REMAP), DRAM_BASE); + *mem = *mem - DRAM_BASE; + } else { + *mem = *mem - DRAM_BASE; + } +} + +int intel_smmu_hps_remapper_config(uint32_t remapper_bypass) +{ + /* Read out the JTAG-ID from boot scratch register */ + if (is_agilex5_A5F0() != 0) { + if (remapper_bypass == 0x01) { + g_remapper_bypass = remapper_bypass; + mmio_write_32(SOCFPGA_SYSMGR(SDM_BE_ARADDR_REMAP), 0); + mmio_write_32(SOCFPGA_SYSMGR(SDM_BE_AWADDR_REMAP), 0); + } + } + return INTEL_SIP_SMC_STATUS_OK; +} +#endif + /* * This function is responsible for handling all SiP calls from the NS world */ @@ -714,7 +801,8 @@ uintptr_t sip_smc_handler_v1(uint32_t smc_fid, uint32_t retval = 0, completed_addr[3]; uint32_t retval2 = 0; uint32_t mbox_error = 0; - uint64_t retval64, rsu_respbuf[9], seu_respbuf[3]; + uint64_t retval64, rsu_respbuf[9]; + uint32_t seu_respbuf[3]; int status = INTEL_SIP_SMC_STATUS_OK; int mbox_status; unsigned int len_in_resp; @@ -819,6 +907,16 @@ uintptr_t sip_smc_handler_v1(uint32_t smc_fid, status = intel_rsu_copy_dcmf_version(x1, x2); SMC_RET1(handle, status); + case INTEL_SIP_SMC_RSU_GET_DEVICE_INFO: + status = intel_rsu_get_device_info((uint32_t *)rsu_respbuf, + ARRAY_SIZE(rsu_respbuf)); + if (status) { + SMC_RET1(handle, status); + } else { + SMC_RET5(handle, status, rsu_respbuf[0], rsu_respbuf[1], + rsu_respbuf[2], rsu_respbuf[3]); + } + case INTEL_SIP_SMC_RSU_DCMF_STATUS: SMC_RET2(handle, INTEL_SIP_SMC_STATUS_OK, ((uint64_t)rsu_dcmf_stat[3] << 48) | @@ -1210,6 +1308,12 @@ uintptr_t sip_smc_handler_v1(uint32_t smc_fid, x5, x6, true, &send_id); SMC_RET1(handle, status); +#if PLATFORM_MODEL == PLAT_SOCFPGA_AGILEX5 + case INTEL_SIP_SMC_FCS_SDM_REMAPPER_CONFIG: + status = intel_smmu_hps_remapper_config(x1); + SMC_RET1(handle, status); +#endif + case INTEL_SIP_SMC_GET_ROM_PATCH_SHA384: status = intel_fcs_get_rom_patch_sha384(x1, &retval64, &mbox_error); @@ -1229,6 +1333,10 @@ uintptr_t sip_smc_handler_v1(uint32_t smc_fid, SMC_RET3(handle, seu_respbuf[0], seu_respbuf[1], seu_respbuf[2]); } + case INTEL_SIP_SMC_SAFE_INJECT_SEU_ERR: + status = intel_sdm_safe_inject_seu_err((uint32_t *)&x1, (uint32_t)x2); + SMC_RET1(handle, status); + default: return socfpga_sip_handler(smc_fid, x1, x2, x3, x4, cookie, handle, flags); diff --git a/plat/intel/soc/common/socfpga_storage.c b/plat/intel/soc/common/socfpga_storage.c index e80f0747..66b5216c 100644 --- a/plat/intel/soc/common/socfpga_storage.c +++ b/plat/intel/soc/common/socfpga_storage.c @@ -1,6 +1,7 @@ /* * Copyright (c) 2019, ARM Limited and Contributors. All rights reserved. * Copyright (c) 2019-2023, Intel Corporation. All rights reserved. + * Copyright (c) 2024, Altera Corporation. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -24,17 +25,9 @@ #include "drivers/sdmmc/sdmmc.h" #include "socfpga_private.h" +#include "socfpga_ros.h" -#define PLAT_FIP_BASE (0) -#define PLAT_FIP_MAX_SIZE (0x1000000) -#define PLAT_MMC_DATA_BASE (0xffe3c000) -#define PLAT_MMC_DATA_SIZE (0x2000) -#define PLAT_QSPI_DATA_BASE (0x3C00000) -#define PLAT_QSPI_DATA_SIZE (0x1000000) -#define PLAT_NAND_DATA_BASE (0x0200000) -#define PLAT_NAND_DATA_SIZE (0x1000000) - static const io_dev_connector_t *fip_dev_con; static const io_dev_connector_t *boot_dev_con; @@ -55,6 +48,12 @@ static const io_uuid_spec_t bl33_uuid_spec = { .uuid = UUID_NON_TRUSTED_FIRMWARE_BL33, }; +# if ARM_LINUX_KERNEL_AS_BL33 != 0 +static const io_uuid_spec_t nt_fw_config_uuid_spec = { + .uuid = UUID_NT_FW_CONFIG, +}; +# endif + uintptr_t a2_lba_offset; const char a2[] = {0xa2, 0x0}; @@ -101,6 +100,13 @@ static const struct plat_io_policy policies[] = { (uintptr_t) &bl33_uuid_spec, check_fip }, +# if ARM_LINUX_KERNEL_AS_BL33 != 0 + [NT_FW_CONFIG_ID] = { + &fip_dev_handle, + (uintptr_t)&nt_fw_config_uuid_spec, + check_fip + }, +# endif [GPT_IMAGE_ID] = { &boot_dev_handle, (uintptr_t) &gpt_block_spec, @@ -136,9 +142,10 @@ static int check_fip(const uintptr_t spec) return result; } -void socfpga_io_setup(int boot_source) +void socfpga_io_setup(int boot_source, unsigned long offset) { int result; + fip_spec.offset = offset; switch (boot_source) { case BOOT_SOURCE_SDMMC: @@ -152,7 +159,6 @@ void socfpga_io_setup(int boot_source) case BOOT_SOURCE_QSPI: register_io_dev = ®ister_io_dev_memmap; - fip_spec.offset = PLAT_QSPI_DATA_BASE; break; #if PLATFORM_MODEL == PLAT_SOCFPGA_AGILEX5 @@ -161,7 +167,6 @@ void socfpga_io_setup(int boot_source) nand_dev_spec.ops.init = cdns_nand_init_mtd; nand_dev_spec.ops.read = cdns_nand_read; nand_dev_spec.ops.write = NULL; - fip_spec.offset = PLAT_NAND_DATA_BASE; break; #endif diff --git a/plat/intel/soc/common/socfpga_vab.c b/plat/intel/soc/common/socfpga_vab.c index e16610ca..969abb34 100644 --- a/plat/intel/soc/common/socfpga_vab.c +++ b/plat/intel/soc/common/socfpga_vab.c @@ -1,6 +1,7 @@ /* * Copyright (c) 2019, ARM Limited and Contributors. All rights reserved. * Copyright (c) 2019-2023, Intel Corporation. All rights reserved. + * Copyright (c) 2024, Altera Corporation. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -8,18 +9,23 @@ #include <assert.h> #include <errno.h> +#include "../lib/sha/sha.h" + #include <arch_helpers.h> +#include <common/bl_common.h> #include <common/debug.h> +#include <common/desc_image_load.h> #include <common/tbbr/tbbr_img_def.h> #include <drivers/delay_timer.h> #include <lib/mmio.h> #include <lib/utils.h> +#include <plat/common/platform.h> #include <tools_share/firmware_image_package.h> #include "socfpga_mailbox.h" #include "socfpga_vab.h" -static size_t get_img_size(uint8_t *img_buf, size_t img_buf_sz) +size_t get_img_size(uint8_t *img_buf, size_t img_buf_sz) { uint8_t *img_buf_end = img_buf + img_buf_sz; uint32_t cert_sz = get_unaligned_le32(img_buf_end - sizeof(uint32_t)); @@ -35,9 +41,33 @@ static size_t get_img_size(uint8_t *img_buf, size_t img_buf_sz) return 0; } +int socfpga_vab_init(unsigned int image_id) +{ + int ret = 0; + size_t image_size; + void *image_base_ptr; + /* + * Get information about the images to load. + */ + bl_mem_params_node_t *bl_mem_params = get_bl_mem_params_node(image_id); + + assert(bl_mem_params); + + if (bl_mem_params == NULL) { + ERROR("SOCFPGA VAB Init failed\n"); + return -EINITREJECTED; + } + + if ((image_id == BL31_IMAGE_ID) || (image_id == BL33_IMAGE_ID)) { + image_base_ptr = (void *)bl_mem_params->image_info.image_base; + image_size = bl_mem_params->image_info.image_size; + ret = socfpga_vab_authentication(&image_base_ptr, &image_size); + } + return ret; +} -int socfpga_vendor_authentication(void **p_image, size_t *p_size) +int socfpga_vab_authentication(void **p_image, size_t *p_size) { int retry_count = 20; uint8_t hash384[FCS_SHA384_WORD_SIZE]; @@ -46,51 +76,47 @@ int socfpga_vendor_authentication(void **p_image, size_t *p_size) uint8_t *cert_hash_ptr, *mbox_relocate_data_addr; uint32_t resp = 0, resp_len = 1; int ret = 0; + uint8_t u8_buf_static[MBOX_DATA_MAX_LEN]; + + mbox_relocate_data_addr = u8_buf_static; img_addr = (uintptr_t)*p_image; img_sz = get_img_size((uint8_t *)img_addr, *p_size); if (!img_sz) { - NOTICE("VAB certificate not found in image!\n"); - return -ENOVABIMG; + ERROR("VAB certificate not found in image!\n"); + return -ENOVABCERT; } if (!IS_BYTE_ALIGNED(img_sz, sizeof(uint32_t))) { - NOTICE("Image size (%d bytes) not aliged to 4 bytes!\n", img_sz); + ERROR("Image size (%d bytes) not aliged to 4 bytes!\n", img_sz); return -EIMGERR; } /* Generate HASH384 from the image */ - /* TODO: This part need to cross check !!!!!! */ - sha384_csum_wd((uint8_t *)img_addr, img_sz, hash384, CHUNKSZ_PER_WD_RESET); - cert_hash_ptr = (uint8_t *)(img_addr + img_sz + - VAB_CERT_MAGIC_OFFSET + VAB_CERT_FIT_SHA384_OFFSET); + sha384_start((uint8_t *)img_addr, img_sz, hash384, CHUNKSZ_PER_WD_RESET); + cert_hash_ptr = (uint8_t *)(img_addr + img_sz + VAB_CERT_MAGIC_OFFSET + + VAB_CERT_FIT_SHA384_OFFSET); /* * Compare the SHA384 found in certificate against the SHA384 * calculated from image */ if (memcmp(hash384, cert_hash_ptr, FCS_SHA384_WORD_SIZE)) { - NOTICE("SHA384 does not match!\n"); + ERROR("SHA384 does not match!\n"); return -EKEYREJECTED; } - mbox_data_addr = img_addr + img_sz - sizeof(uint32_t); /* Size in word (32bits) */ mbox_data_sz = (BYTE_ALIGN(*p_size - img_sz, sizeof(uint32_t))) >> 2; - NOTICE("mbox_data_addr = %lx mbox_data_sz = %d\n", mbox_data_addr, mbox_data_sz); + VERBOSE("mbox_data_addr = %lx mbox_data_sz = %d\n", mbox_data_addr, mbox_data_sz); - /* TODO: This part need to cross check !!!!!! */ - // mbox_relocate_data_addr = (uint8_t *)malloc(mbox_data_sz * sizeof(uint32_t)); - // if (!mbox_relocate_data_addr) { - // NOTICE("Cannot allocate memory for VAB certificate relocation!\n"); - // return -ENOMEM; - // } + memcpy_s(mbox_relocate_data_addr, mbox_data_sz * sizeof(uint32_t), + (uint8_t *)mbox_data_addr, mbox_data_sz * sizeof(uint32_t)); - memcpy(mbox_relocate_data_addr, (uint8_t *)mbox_data_addr, mbox_data_sz * sizeof(uint32_t)); - *(uint32_t *)mbox_relocate_data_addr = 0; + *((unsigned int *)mbox_relocate_data_addr) = CCERT_CMD_TEST_PGM_MASK; do { /* Invoke SMC call to ATF to send the VAB certificate to SDM */ @@ -109,7 +135,6 @@ int socfpga_vendor_authentication(void **p_image, size_t *p_size) /* Free the relocate certificate memory space */ zeromem((void *)&mbox_relocate_data_addr, sizeof(uint32_t)); - /* Exclude the size of the VAB certificate from image size */ *p_size = img_sz; @@ -121,40 +146,32 @@ int socfpga_vendor_authentication(void **p_image, size_t *p_size) /* 0x85 = Not allowed under current security setting */ if (ret == MBOX_RESP_ERR(0x85)) { /* SDM bypass authentication */ - NOTICE("Image Authentication bypassed at address\n"); + ERROR("Image Authentication bypassed at address\n"); return 0; } - NOTICE("VAB certificate authentication failed in SDM\n"); + ERROR("VAB certificate authentication failed in SDM\n"); /* 0x1FF = The device is busy */ if (ret == MBOX_RESP_ERR(0x1FF)) { - NOTICE("Operation timed out\n"); + ERROR("Operation timed out\n"); return -ETIMEOUT; } else if (ret == MBOX_WRONG_ID) { - NOTICE("No such process\n"); + ERROR("No such process\n"); return -EPROCESS; } + return -EAUTH; } else { /* If Certificate Process Status has error */ if (resp) { - NOTICE("VAB certificate execution format error\n"); + ERROR("VAB certificate execution format error\n"); return -EIMGERR; } } - NOTICE("Image Authentication bypassed at address\n"); + NOTICE("%s 0x%lx (%d bytes)\n", "Image Authentication passed at address", img_addr, img_sz); return ret; - -} - -static uint32_t get_unaligned_le32(const void *p) -{ - /* TODO: Temp for testing */ - //return le32_to_cpup((__le32 *)p); - return 0; } -void sha384_csum_wd(const unsigned char *input, unsigned int ilen, - unsigned char *output, unsigned int chunk_sz) +uint32_t get_unaligned_le32(const void *p) { - /* TODO: Update sha384 start, update and finish */ + return le32_to_cpue((uint32_t *)p); } diff --git a/plat/intel/soc/n5x/bl31_plat_setup.c b/plat/intel/soc/n5x/bl31_plat_setup.c index a5337cee..cb5ced6f 100644 --- a/plat/intel/soc/n5x/bl31_plat_setup.c +++ b/plat/intel/soc/n5x/bl31_plat_setup.c @@ -116,8 +116,6 @@ void bl31_platform_setup(void) (uint64_t)plat_secondary_cpus_bl31_entry); mailbox_hps_stage_notify(HPS_EXECUTION_STATE_SSBL); - - ncore_enable_ocram_firewall(); } const mmap_region_t plat_dm_mmap[] = { diff --git a/plat/intel/soc/n5x/include/n5x_clock_manager.h b/plat/intel/soc/n5x/include/n5x_clock_manager.h index 14a57173..95a3d5c9 100644 --- a/plat/intel/soc/n5x/include/n5x_clock_manager.h +++ b/plat/intel/soc/n5x/include/n5x_clock_manager.h @@ -1,16 +1,15 @@ /* * Copyright (c) 2019-2023, Intel Corporation. All rights reserved. + * Copyright (c) 2024, Altera Corporation. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ -#ifndef CLOCKMANAGER_H -#define CLOCKMANAGER_H +#ifndef N5X_SOCFPGA_CLOCKMANAGER_H +#define N5X_SOCFPGA_CLOCKMANAGER_H -#include "socfpga_handoff.h" /* MACRO DEFINITION */ -#define SOCFPGA_GLOBAL_TIMER 0xffd01000 #define SOCFPGA_GLOBAL_TIMER_EN 0x3 #define CLKMGR_PLLGLOB_VCO_PSRC_MASK GENMASK(17, 16) @@ -56,5 +55,6 @@ uint64_t get_l4_clk(void); uint32_t get_clk_freq(uint32_t psrc_reg); uint32_t get_mpu_clk(void); uint32_t get_cpu_clk(void); +uint32_t get_mpu_periph_clk(void); -#endif +#endif /* N5X_SOCFPGA_CLOCKMANAGER_H */ diff --git a/plat/intel/soc/n5x/include/n5x_system_manager.h b/plat/intel/soc/n5x/include/n5x_system_manager.h index b6282197..fd789a27 100644 --- a/plat/intel/soc/n5x/include/n5x_system_manager.h +++ b/plat/intel/soc/n5x/include/n5x_system_manager.h @@ -1,5 +1,6 @@ /* * Copyright (c) 2019-2023, Intel Corporation. All rights reserved. + * Copyright (c) 2024, Altera Corporation. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -121,7 +122,7 @@ #define SOCFPGA_SYSMGR_BOOT_SCRATCH_COLD_8 0x220 #define SOCFPGA_SYSMGR_BOOT_SCRATCH_COLD_9 0x224 #define SOCFPGA_SYSMGR_MPFE_CONFIG 0x228 -#define SOCFPGA_SYSMGR_MPFE_status 0x22C +#define SOCFPGA_SYSMGR_MPFE_STATUS 0x22C #define SOCFPGA_SYSMGR_BOOT_SCRATCH_WARM_0 0x230 #define SOCFPGA_SYSMGR_BOOT_SCRATCH_WARM_1 0x234 #define SOCFPGA_SYSMGR_BOOT_SCRATCH_WARM_2 0x238 @@ -143,6 +144,18 @@ #define SOCFPGA_SYSMGR_BOOT_SCRATCH_POR_8 0x278 #define SOCFPGA_SYSMGR_BOOT_SCRATCH_POR_9 0x27C +/* QSPI ECC from SDM register */ +#define SOCFPGA_ECC_QSPI_CTRL 0x08 +#define SOCFPGA_ECC_QSPI_ERRINTEN 0x10 +#define SOCFPGA_ECC_QSPI_ERRINTENS 0x14 +#define SOCFPGA_ECC_QSPI_ERRINTENR 0x18 +#define SOCFPGA_ECC_QSPI_INTMODE 0x1C +#define SOCFPGA_ECC_QSPI_INTSTAT 0x20 +#define SOCFPGA_ECC_QSPI_INTTEST 0x24 +#define SOCFPGA_ECC_QSPI_ECC_ACCCTRL 0x78 +#define SOCFPGA_ECC_QSPI_ECC_STARTACC 0x7C +#define SOCFPGA_ECC_QSPI_ECC_WDCTRL 0x80 + #define DMA0_STREAM_CTRL_REG 0x10D1217C #define DMA1_STREAM_CTRL_REG 0x10D12180 #define SDM_STREAM_CTRL_REG 0x10D12184 @@ -186,6 +199,9 @@ #define RMMUSECSID_REG_VAL BIT(5) /* Macros */ +#define SOCFPGA_ECC_QSPI(_reg) (SOCFPGA_ECC_QSPI_REG_BASE \ + + (SOCFPGA_ECC_QSPI_##_reg)) + #define SOCFPGA_SYSMGR(_reg) (SOCFPGA_SYSMGR_REG_BASE \ + (SOCFPGA_SYSMGR_##_reg)) #define ENABLE_STREAMID WSTREAMIDEN_REG_CTRL | \ diff --git a/plat/intel/soc/n5x/include/socfpga_plat_def.h b/plat/intel/soc/n5x/include/socfpga_plat_def.h index a06bbc4c..6f0a40be 100644 --- a/plat/intel/soc/n5x/include/socfpga_plat_def.h +++ b/plat/intel/soc/n5x/include/socfpga_plat_def.h @@ -1,6 +1,7 @@ /* * Copyright (c) 2020-2022, ARM Limited and Contributors. All rights reserved. * Copyright (c) 2020-2023, Intel Corporation. All rights reserved. + * Copyright (c) 2024, Altera Corporation. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -8,15 +9,17 @@ #ifndef PLAT_SOCFPGA_DEF_H #define PLAT_SOCFPGA_DEF_H -#include "n5x_system_manager.h" #include <platform_def.h> +#include <lib/utils_def.h> +#include "n5x_system_manager.h" /* Platform Setting */ -#define PLATFORM_MODEL PLAT_SOCFPGA_N5X -#define BOOT_SOURCE BOOT_SOURCE_SDMMC -#define PLAT_PRIMARY_CPU 0 +#define PLATFORM_MODEL PLAT_SOCFPGA_N5X +#define PLAT_PRIMARY_CPU 0 #define PLAT_CLUSTER_ID_MPIDR_AFF_SHIFT MPIDR_AFF1_SHIFT -#define PLAT_CPU_ID_MPIDR_AFF_SHIFT MPIDR_AFF0_SHIFT +#define PLAT_CPU_ID_MPIDR_AFF_SHIFT MPIDR_AFF0_SHIFT +#define PLAT_HANDOFF_OFFSET 0xFFE3F000 +#define PLAT_TIMER_BASE_ADDR 0xFFD01000 /* FPGA config helpers */ #define INTEL_SIP_SMC_FPGA_CONFIG_ADDR 0x400000 @@ -26,19 +29,27 @@ #define CAD_QSPIDATA_OFST 0xff900000 #define CAD_QSPI_OFFSET 0xff8d2000 +/* FIP Setting */ +#define PLAT_FIP_BASE (0) +#define PLAT_FIP_MAX_SIZE (0x1000000) + +/* SDMMC Setting */ +#define PLAT_MMC_DATA_BASE (0xffe3c000) +#define PLAT_MMC_DATA_SIZE (0x2000) +#define SOCFPGA_MMC_BLOCK_SIZE U(8192) + /* Register Mapping */ #define SOCFPGA_CCU_NOC_REG_BASE U(0xf7000000) #define SOCFPGA_F2SDRAMMGR_REG_BASE U(0xf8024000) - #define SOCFPGA_MMC_REG_BASE U(0xff808000) - #define SOCFPGA_RSTMGR_REG_BASE U(0xffd11000) #define SOCFPGA_SYSMGR_REG_BASE U(0xffd12000) +#define SOCFPGA_ECC_QSPI_REG_BASE U(0xffa22000) -#define SOCFPGA_L4_PER_SCR_REG_BASE U(0xffd21000) -#define SOCFPGA_L4_SYS_SCR_REG_BASE U(0xffd21100) -#define SOCFPGA_SOC2FPGA_SCR_REG_BASE U(0xffd21200) -#define SOCFPGA_LWSOC2FPGA_SCR_REG_BASE U(0xffd21300) +#define SOCFPGA_L4_PER_SCR_REG_BASE U(0xffd21000) +#define SOCFPGA_L4_SYS_SCR_REG_BASE U(0xffd21100) +#define SOCFPGA_SOC2FPGA_SCR_REG_BASE U(0xffd21200) +#define SOCFPGA_LWSOC2FPGA_SCR_REG_BASE U(0xffd21300) /******************************************************************************* @@ -65,34 +76,39 @@ #define DEVICE4_BASE (0x2000000000) #define DEVICE4_SIZE (0x0100000000) -#define BL2_BASE (0xffe00000) -#define BL2_LIMIT (0xffe1b000) +#define BL2_BASE (0xffe00000) +#define BL2_LIMIT (0xffe1b000) -#define BL31_BASE (0x1000) -#define BL31_LIMIT (0x81000) +#define BL31_BASE (0x1000) +#define BL31_LIMIT (0x81000) /******************************************************************************* * UART related constants ******************************************************************************/ -#define PLAT_UART0_BASE (0xFFC02000) -#define PLAT_UART1_BASE (0xFFC02100) +#define PLAT_UART0_BASE (0xFFC02000) +#define PLAT_UART1_BASE (0xFFC02100) + +/******************************************************************************* + * WDT related constants + ******************************************************************************/ +#define WDT_BASE (0xFFD00200) /******************************************************************************* * GIC related constants ******************************************************************************/ -#define PLAT_GIC_BASE (0xFFFC0000) -#define PLAT_GICC_BASE (PLAT_GIC_BASE + 0x2000) -#define PLAT_GICD_BASE (PLAT_GIC_BASE + 0x1000) -#define PLAT_GICR_BASE 0 +#define PLAT_GIC_BASE (0xFFFC0000) +#define PLAT_GICC_BASE (PLAT_GIC_BASE + 0x2000) +#define PLAT_GICD_BASE (PLAT_GIC_BASE + 0x1000) +#define PLAT_GICR_BASE 0 -#define PLAT_SYS_COUNTER_FREQ_IN_TICKS (400000000) -#define PLAT_HZ_CONVERT_TO_MHZ (1000000) +#define PLAT_SYS_COUNTER_FREQ_IN_TICKS (400000000) +#define PLAT_HZ_CONVERT_TO_MHZ (1000000) /******************************************************************************* * SDMMC related pointer function ******************************************************************************/ -#define SDMMC_READ_BLOCKS mmc_read_blocks -#define SDMMC_WRITE_BLOCKS mmc_write_blocks +#define SDMMC_READ_BLOCKS mmc_read_blocks +#define SDMMC_WRITE_BLOCKS mmc_write_blocks /******************************************************************************* * sysmgr.boot_scratch_cold6 & 7 (64bit) are used to indicate L2 reset @@ -101,6 +117,6 @@ #define L2_RESET_DONE_REG 0xFFD12218 /* Platform specific system counter */ -#define PLAT_SYS_COUNTER_FREQ_IN_MHZ get_cpu_clk() +#define PLAT_SYS_COUNTER_FREQ_IN_MHZ U(400) #endif /* PLAT_SOCFPGA_DEF_H */ diff --git a/plat/intel/soc/n5x/platform.mk b/plat/intel/soc/n5x/platform.mk index 95f076fa..4770f8d3 100644 --- a/plat/intel/soc/n5x/platform.mk +++ b/plat/intel/soc/n5x/platform.mk @@ -1,5 +1,6 @@ # -# Copyright (c) 2020-2022, Intel Corporation. All rights reserved. +# Copyright (c) 2019-2023, Intel Corporation. All rights reserved. +# Copyright (c) 2024, Altera Corporation. All rights reserved. # # SPDX-License-Identifier: BSD-3-Clause # @@ -46,6 +47,30 @@ BL31_SOURCES += \ plat/intel/soc/common/soc/socfpga_mailbox.c \ plat/intel/soc/common/soc/socfpga_reset_manager.c +# Don't have the Linux kernel as a BL33 image by default +ARM_LINUX_KERNEL_AS_BL33 := 0 +$(eval $(call assert_boolean,ARM_LINUX_KERNEL_AS_BL33)) +$(eval $(call add_define,ARM_LINUX_KERNEL_AS_BL33)) +$(eval $(call add_define,ARM_PRELOADED_DTB_BASE)) + +# Configs for Boot Source +SOCFPGA_BOOT_SOURCE_SDMMC ?= 0 +SOCFPGA_BOOT_SOURCE_QSPI ?= 0 +SOCFPGA_BOOT_SOURCE_NAND ?= 0 + +$(eval $(call assert_booleans,\ + $(sort \ + SOCFPGA_BOOT_SOURCE_SDMMC \ + SOCFPGA_BOOT_SOURCE_QSPI \ + SOCFPGA_BOOT_SOURCE_NAND \ +))) +$(eval $(call add_defines,\ + $(sort \ + SOCFPGA_BOOT_SOURCE_SDMMC \ + SOCFPGA_BOOT_SOURCE_QSPI \ + SOCFPGA_BOOT_SOURCE_NAND \ +))) + PROGRAMMABLE_RESET_ADDRESS := 0 RESET_TO_BL2 := 1 BL2_INV_DCACHE := 0 diff --git a/plat/intel/soc/n5x/soc/n5x_clock_manager.c b/plat/intel/soc/n5x/soc/n5x_clock_manager.c index f32e0f8b..c33140d4 100644 --- a/plat/intel/soc/n5x/soc/n5x_clock_manager.c +++ b/plat/intel/soc/n5x/soc/n5x_clock_manager.c @@ -12,8 +12,7 @@ #include "n5x_clock_manager.h" #include "n5x_system_manager.h" - - +#include "socfpga_handoff.h" uint64_t clk_get_pll_output_hz(void) { @@ -151,7 +150,22 @@ uint32_t get_cpu_clk(void) { uint32_t cpu_clk = 0; - cpu_clk = get_mpu_clk()/PLAT_HZ_CONVERT_TO_MHZ; + cpu_clk = get_l4_clk()/PLAT_HZ_CONVERT_TO_MHZ; return cpu_clk; } + +/* Return mpu_periph_clk clock frequency */ +uint32_t get_mpu_periph_clk(void) +{ + uint32_t mpu_periph_clk = 0; + /* mpu_periph_clk is mpu_clk, via a static /4 divider */ + mpu_periph_clk = (get_mpu_clk()/4)/PLAT_HZ_CONVERT_TO_MHZ; + return mpu_periph_clk; +} + +/* Return mpu_periph_clk tick */ +unsigned int plat_get_syscnt_freq2(void) +{ + return PLAT_SYS_COUNTER_FREQ_IN_TICKS; +} diff --git a/plat/intel/soc/stratix10/bl2_plat_setup.c b/plat/intel/soc/stratix10/bl2_plat_setup.c index 73e3216a..d140394d 100644 --- a/plat/intel/soc/stratix10/bl2_plat_setup.c +++ b/plat/intel/soc/stratix10/bl2_plat_setup.c @@ -122,14 +122,14 @@ void bl2_el3_plat_arch_setup(void) switch (boot_source) { case BOOT_SOURCE_SDMMC: dw_mmc_init(¶ms, &mmc_info); - socfpga_io_setup(boot_source); + socfpga_io_setup(boot_source, PLAT_SDMMC_DATA_BASE); break; case BOOT_SOURCE_QSPI: cad_qspi_init(0, QSPI_CONFIG_CPHA, QSPI_CONFIG_CPOL, QSPI_CONFIG_CSDA, QSPI_CONFIG_CSDADS, QSPI_CONFIG_CSEOT, QSPI_CONFIG_CSSOT, 0); - socfpga_io_setup(boot_source); + socfpga_io_setup(boot_source, PLAT_QSPI_DATA_BASE); break; default: diff --git a/plat/intel/soc/stratix10/bl31_plat_setup.c b/plat/intel/soc/stratix10/bl31_plat_setup.c index ba00e820..d0aa9729 100644 --- a/plat/intel/soc/stratix10/bl31_plat_setup.c +++ b/plat/intel/soc/stratix10/bl31_plat_setup.c @@ -123,8 +123,6 @@ void bl31_platform_setup(void) (uint64_t)plat_secondary_cpus_bl31_entry); mailbox_hps_stage_notify(HPS_EXECUTION_STATE_SSBL); - - enable_ocram_firewall(); } const mmap_region_t plat_stratix10_mmap[] = { diff --git a/plat/intel/soc/stratix10/include/s10_clock_manager.h b/plat/intel/soc/stratix10/include/s10_clock_manager.h index 5f763755..c7632bed 100644 --- a/plat/intel/soc/stratix10/include/s10_clock_manager.h +++ b/plat/intel/soc/stratix10/include/s10_clock_manager.h @@ -95,7 +95,8 @@ uint32_t get_wdt_clk(void); uint32_t get_uart_clk(void); uint32_t get_mmc_clk(void); uint32_t get_l3_clk(uint32_t ref_clk); -uint32_t get_ref_clk(uint32_t pllglob); uint32_t get_cpu_clk(void); +uint32_t get_ref_clk(uint32_t pllglob); +uint32_t get_mpu_periph_clk(void); #endif diff --git a/plat/intel/soc/stratix10/include/s10_memory_controller.h b/plat/intel/soc/stratix10/include/s10_memory_controller.h index 155b2795..056f6cf7 100644 --- a/plat/intel/soc/stratix10/include/s10_memory_controller.h +++ b/plat/intel/soc/stratix10/include/s10_memory_controller.h @@ -1,5 +1,6 @@ /* * Copyright (c) 2019, Intel Corporation. All rights reserved. + * Copyright (c) 2024, Altera Corporation. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -26,7 +27,7 @@ #define S10_MPFE_HMC_ADP_ECCCTRL1 0xf8011100 #define S10_MPFE_HMC_ADP_ECCCTRL2 0xf8011104 #define S10_MPFE_HMC_ADP_RSTHANDSHAKESTAT 0xf8011218 -#define S10_MPFE_HMC_ADP_RSTHANDSHAKESTAT_SEQ2CORE 0x000000ff +#define S10_MPFE_HMC_ADP_RSTHANDSHAKESTAT_SEQ2CORE 0x0000000f #define S10_MPFE_HMC_ADP_RSTHANDSHAKECTRL 0xf8011214 diff --git a/plat/intel/soc/stratix10/include/s10_system_manager.h b/plat/intel/soc/stratix10/include/s10_system_manager.h index 88c0b469..dcc1517e 100644 --- a/plat/intel/soc/stratix10/include/s10_system_manager.h +++ b/plat/intel/soc/stratix10/include/s10_system_manager.h @@ -1,5 +1,6 @@ /* * Copyright (c) 2019-2023, Intel Corporation. All rights reserved. + * Copyright (c) 2024, Altera Corporation. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -120,7 +121,7 @@ #define SOCFPGA_SYSMGR_BOOT_SCRATCH_COLD_8 0x220 #define SOCFPGA_SYSMGR_BOOT_SCRATCH_COLD_9 0x224 #define SOCFPGA_SYSMGR_MPFE_CONFIG 0x228 -#define SOCFPGA_SYSMGR_MPFE_status 0x22C +#define SOCFPGA_SYSMGR_MPFE_STATUS 0x22C #define SOCFPGA_SYSMGR_BOOT_SCRATCH_WARM_0 0x230 #define SOCFPGA_SYSMGR_BOOT_SCRATCH_WARM_1 0x234 #define SOCFPGA_SYSMGR_BOOT_SCRATCH_WARM_2 0x238 @@ -142,6 +143,18 @@ #define SOCFPGA_SYSMGR_BOOT_SCRATCH_POR_8 0x278 #define SOCFPGA_SYSMGR_BOOT_SCRATCH_POR_9 0x27C +/* QSPI ECC from SDM register */ +#define SOCFPGA_ECC_QSPI_CTRL 0x08 +#define SOCFPGA_ECC_QSPI_ERRINTEN 0x10 +#define SOCFPGA_ECC_QSPI_ERRINTENS 0x14 +#define SOCFPGA_ECC_QSPI_ERRINTENR 0x18 +#define SOCFPGA_ECC_QSPI_INTMODE 0x1C +#define SOCFPGA_ECC_QSPI_INTSTAT 0x20 +#define SOCFPGA_ECC_QSPI_INTTEST 0x24 +#define SOCFPGA_ECC_QSPI_ECC_ACCCTRL 0x78 +#define SOCFPGA_ECC_QSPI_ECC_STARTACC 0x7C +#define SOCFPGA_ECC_QSPI_ECC_WDCTRL 0x80 + #define DMA0_STREAM_CTRL_REG 0x10D1217C #define DMA1_STREAM_CTRL_REG 0x10D12180 #define SDM_STREAM_CTRL_REG 0x10D12184 @@ -182,6 +195,8 @@ #define RMMUSECSID_REG_VAL BIT(5) /* Macros */ +#define SOCFPGA_ECC_QSPI(_reg) (SOCFPGA_ECC_QSPI_REG_BASE \ + + (SOCFPGA_ECC_QSPI_##_reg)) #define SOCFPGA_SYSMGR(_reg) (SOCFPGA_SYSMGR_REG_BASE \ + (SOCFPGA_SYSMGR_##_reg)) diff --git a/plat/intel/soc/stratix10/include/socfpga_plat_def.h b/plat/intel/soc/stratix10/include/socfpga_plat_def.h index 7c9f15ac..90345c3b 100644 --- a/plat/intel/soc/stratix10/include/socfpga_plat_def.h +++ b/plat/intel/soc/stratix10/include/socfpga_plat_def.h @@ -1,5 +1,6 @@ /* * Copyright (c) 2019-2023, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2024, Altera Corporation. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -8,14 +9,16 @@ #define PLAT_SOCFPGA_DEF_H #include <platform_def.h> +#include <lib/utils_def.h> #include "s10_system_manager.h" /* Platform Setting */ -#define PLATFORM_MODEL PLAT_SOCFPGA_STRATIX10 -#define BOOT_SOURCE BOOT_SOURCE_SDMMC -#define PLAT_PRIMARY_CPU 0 +#define PLATFORM_MODEL PLAT_SOCFPGA_STRATIX10 +#define PLAT_PRIMARY_CPU 0 #define PLAT_CLUSTER_ID_MPIDR_AFF_SHIFT MPIDR_AFF1_SHIFT -#define PLAT_CPU_ID_MPIDR_AFF_SHIFT MPIDR_AFF0_SHIFT +#define PLAT_CPU_ID_MPIDR_AFF_SHIFT MPIDR_AFF0_SHIFT +#define PLAT_HANDOFF_OFFSET 0xFFE3F000 +#define PLAT_TIMER_BASE_ADDR 0xFFD01000 /* FPGA config helpers */ #define INTEL_SIP_SMC_FPGA_CONFIG_ADDR 0x400000 @@ -25,6 +28,15 @@ #define CAD_QSPIDATA_OFST 0xff900000 #define CAD_QSPI_OFFSET 0xff8d2000 +/* FIP Setting */ +#define PLAT_FIP_BASE (0) +#define PLAT_FIP_MAX_SIZE (0x1000000) + +/* SDMMC Setting */ +#define PLAT_MMC_DATA_BASE (0xffe3c000) +#define PLAT_MMC_DATA_SIZE (0x2000) +#define SOCFPGA_MMC_BLOCK_SIZE U(8192) + /* Register Mapping */ #define SOCFPGA_CCU_NOC_REG_BASE 0xf7000000 #define SOCFPGA_F2SDRAMMGR_REG_BASE U(0xf8024000) @@ -33,6 +45,7 @@ #define SOCFPGA_RSTMGR_REG_BASE 0xffd11000 #define SOCFPGA_SYSMGR_REG_BASE 0xffd12000 +#define SOCFPGA_ECC_QSPI_REG_BASE 0xffa22000 #define SOCFPGA_L4_PER_SCR_REG_BASE 0xffd21000 #define SOCFPGA_L4_SYS_SCR_REG_BASE 0xffd21100 @@ -63,34 +76,39 @@ #define DEVICE4_BASE (0x2000000000) #define DEVICE4_SIZE (0x0100000000) -#define BL2_BASE (0xffe00000) -#define BL2_LIMIT (0xffe1b000) +#define BL2_BASE (0xffe00000) +#define BL2_LIMIT (0xffe2b000) -#define BL31_BASE (0x1000) -#define BL31_LIMIT (0x81000) +#define BL31_BASE (0x1000) +#define BL31_LIMIT (0x81000) /******************************************************************************* * UART related constants ******************************************************************************/ -#define PLAT_UART0_BASE (0xFFC02000) -#define PLAT_UART1_BASE (0xFFC02100) +#define PLAT_UART0_BASE (0xFFC02000) +#define PLAT_UART1_BASE (0xFFC02100) + +/******************************************************************************* + * WDT related constants + ******************************************************************************/ +#define WDT_BASE (0xFFD00200) /******************************************************************************* * GIC related constants ******************************************************************************/ -#define PLAT_GIC_BASE (0xFFFC0000) -#define PLAT_GICC_BASE (PLAT_GIC_BASE + 0x2000) -#define PLAT_GICD_BASE (PLAT_GIC_BASE + 0x1000) -#define PLAT_GICR_BASE 0 +#define PLAT_GIC_BASE (0xFFFC0000) +#define PLAT_GICC_BASE (PLAT_GIC_BASE + 0x2000) +#define PLAT_GICD_BASE (PLAT_GIC_BASE + 0x1000) +#define PLAT_GICR_BASE 0 -#define PLAT_SYS_COUNTER_FREQ_IN_TICKS (400000000) -#define PLAT_HZ_CONVERT_TO_MHZ (1000000) +#define PLAT_SYS_COUNTER_FREQ_IN_TICKS (400000000) +#define PLAT_HZ_CONVERT_TO_MHZ (1000000) /******************************************************************************* * SDMMC related pointer function ******************************************************************************/ -#define SDMMC_READ_BLOCKS mmc_read_blocks -#define SDMMC_WRITE_BLOCKS mmc_write_blocks +#define SDMMC_READ_BLOCKS mmc_read_blocks +#define SDMMC_WRITE_BLOCKS mmc_write_blocks /******************************************************************************* * sysmgr.boot_scratch_cold6 & 7 (64bit) are used to indicate L2 reset @@ -99,7 +117,7 @@ #define L2_RESET_DONE_REG 0xFFD12218 /* Platform specific system counter */ -#define PLAT_SYS_COUNTER_FREQ_IN_MHZ get_cpu_clk() +#define PLAT_SYS_COUNTER_FREQ_IN_MHZ U(400) #endif /* PLATSOCFPGA_DEF_H */ diff --git a/plat/intel/soc/stratix10/platform.mk b/plat/intel/soc/stratix10/platform.mk index 6bc96fb6..4cd7032e 100644 --- a/plat/intel/soc/stratix10/platform.mk +++ b/plat/intel/soc/stratix10/platform.mk @@ -1,6 +1,7 @@ # # Copyright (c) 2019-2023, ARM Limited and Contributors. All rights reserved. -# Copyright (c) 2019-2022, Intel Corporation. All rights reserved. +# Copyright (c) 2019-2023, Intel Corporation. All rights reserved. +# Copyright (c) 2024, Altera Corporation. All rights reserved. # # SPDX-License-Identifier: BSD-3-Clause # @@ -26,6 +27,7 @@ PLAT_BL_COMMON_SOURCES := \ lib/xlat_tables/xlat_tables_common.c \ plat/intel/soc/common/aarch64/platform_common.c \ plat/intel/soc/common/aarch64/plat_helpers.S \ + plat/intel/soc/common/drivers/ccu/ncore_ccu.c \ plat/intel/soc/common/socfpga_delay_timer.c \ plat/intel/soc/common/soc/socfpga_firewall.c @@ -53,6 +55,7 @@ BL2_SOURCES += \ plat/intel/soc/common/soc/socfpga_mailbox.c \ plat/intel/soc/common/soc/socfpga_reset_manager.c \ plat/intel/soc/common/drivers/qspi/cadence_qspi.c \ + plat/intel/soc/common/drivers/ddr/ddr.c \ plat/intel/soc/common/drivers/wdt/watchdog.c include lib/zlib/zlib.mk @@ -75,6 +78,30 @@ BL31_SOURCES += \ plat/intel/soc/common/soc/socfpga_mailbox.c \ plat/intel/soc/common/soc/socfpga_reset_manager.c +# Don't have the Linux kernel as a BL33 image by default +ARM_LINUX_KERNEL_AS_BL33 := 0 +$(eval $(call assert_boolean,ARM_LINUX_KERNEL_AS_BL33)) +$(eval $(call add_define,ARM_LINUX_KERNEL_AS_BL33)) +$(eval $(call add_define,ARM_PRELOADED_DTB_BASE)) + +# Configs for Boot Source +SOCFPGA_BOOT_SOURCE_SDMMC ?= 0 +SOCFPGA_BOOT_SOURCE_QSPI ?= 0 +SOCFPGA_BOOT_SOURCE_NAND ?= 0 + +$(eval $(call assert_booleans,\ + $(sort \ + SOCFPGA_BOOT_SOURCE_SDMMC \ + SOCFPGA_BOOT_SOURCE_QSPI \ + SOCFPGA_BOOT_SOURCE_NAND \ +))) +$(eval $(call add_defines,\ + $(sort \ + SOCFPGA_BOOT_SOURCE_SDMMC \ + SOCFPGA_BOOT_SOURCE_QSPI \ + SOCFPGA_BOOT_SOURCE_NAND \ +))) + PROGRAMMABLE_RESET_ADDRESS := 0 RESET_TO_BL2 := 1 USE_COHERENT_MEM := 1 diff --git a/plat/intel/soc/stratix10/soc/s10_clock_manager.c b/plat/intel/soc/stratix10/soc/s10_clock_manager.c index 416d3592..0a3b77b1 100644 --- a/plat/intel/soc/stratix10/soc/s10_clock_manager.c +++ b/plat/intel/soc/stratix10/soc/s10_clock_manager.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019-2022, Intel Corporation. All rights reserved. + * Copyright (c) 2019-2023, Intel Corporation. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -230,6 +230,40 @@ uint32_t get_ref_clk(uint32_t pllglob) return ref_clk; } +/* Calculate clock frequency based on parameter */ +uint32_t get_clk_freq(uint32_t psrc_reg, uint32_t main_pllc, uint32_t per_pllc) +{ + uint32_t clk_psrc, ref_clk; + uint32_t pllc_reg, pllc_div, pllglob_reg; + + clk_psrc = mmio_read_32(ALT_CLKMGR_MAINPLL + psrc_reg); + + switch (ALT_CLKMGR_PSRC(clk_psrc)) { + case ALT_CLKMGR_SRC_MAIN: + pllc_reg = ALT_CLKMGR_MAINPLL + main_pllc; + pllglob_reg = ALT_CLKMGR_MAINPLL + ALT_CLKMGR_MAINPLL_PLLGLOB; + break; + case ALT_CLKMGR_SRC_PER: + pllc_reg = ALT_CLKMGR_PERPLL + per_pllc; + pllglob_reg = ALT_CLKMGR_PERPLL + ALT_CLKMGR_PERPLL_PLLGLOB; + break; + default: + return 0; + } + + ref_clk = get_ref_clk(mmio_read_32(pllglob_reg)); + + pllc_div = mmio_read_32(pllc_reg) & 0xff; + + if (pllc_div != 0) { + ref_clk = (ref_clk / pllc_div) / (clk_psrc + 1); + return ref_clk; + } else { + VERBOSE("PLL DIV is 0\n"); + return 0; + } +} + /* Calculate L3 interconnect main clock */ uint32_t get_l3_clk(uint32_t ref_clk) { @@ -308,6 +342,17 @@ uint32_t get_mmc_clk(void) return mmc_clk; } +/* Return MPU clock */ +uint32_t get_mpu_clk(void) +{ + uint32_t mpu_clk; + + mpu_clk = get_clk_freq(ALT_CLKMGR_MAINPLL_NOCCLK, ALT_CLKMGR_MAINPLL_PLLC0, + ALT_CLKMGR_PERPLL_PLLC0); + + return mpu_clk; +} + /* Get cpu freq clock */ uint32_t get_cpu_clk(void) { @@ -320,3 +365,18 @@ uint32_t get_cpu_clk(void) return cpu_clk; } + +/* Return mpu_periph_clk clock frequency */ +uint32_t get_mpu_periph_clk(void) +{ + uint32_t mpu_periph_clk = 0; + /* mpu_periph_clk is mpu_clk, via a static /4 divider */ + mpu_periph_clk = (get_mpu_clk()/4)/PLAT_HZ_CONVERT_TO_MHZ; + return mpu_periph_clk; +} + +/* Return mpu_periph_clk tick */ +unsigned int plat_get_syscnt_freq2(void) +{ + return PLAT_SYS_COUNTER_FREQ_IN_TICKS; +} diff --git a/plat/marvell/armada/a3k/common/a3700_common.mk b/plat/marvell/armada/a3k/common/a3700_common.mk index b9c28de1..e8f892d2 100644 --- a/plat/marvell/armada/a3k/common/a3700_common.mk +++ b/plat/marvell/armada/a3k/common/a3700_common.mk @@ -150,90 +150,89 @@ $(TBB): FORCE $(if $(wildcard $(CRYPTOPP_LIBDIR)/*),,$(error "Either 'CRYPTOPP_PATH' or 'CRYPTOPP_LIB' was set to '$(CRYPTOPP_LIBDIR)', but '$(CRYPTOPP_LIBDIR)' does not exist")) $(if $(wildcard $(CRYPTOPP_INCDIR)/*),,$(error "Either 'CRYPTOPP_PATH' or 'CRYPTOPP_INCDIR' was set to '$(CRYPTOPP_INCDIR)', but '$(CRYPTOPP_INCDIR)' does not exist")) ifdef CRYPTOPP_PATH - $(Q)$(MAKE) --no-print-directory -C $(CRYPTOPP_PATH) -f GNUmakefile + $(q)$(MAKE) --no-print-directory -C $(CRYPTOPP_PATH) -f GNUmakefile endif - $(Q)$(MAKE) --no-print-directory -C $(WTP)/wtptp/src/TBB_Linux -f TBB_linux.mak LIBDIR=$(CRYPTOPP_LIBDIR) INCDIR=$(CRYPTOPP_INCDIR) + $(q)$(MAKE) --no-print-directory -C $(WTP)/wtptp/src/TBB_Linux -f TBB_linux.mak LIBDIR=$(CRYPTOPP_LIBDIR) INCDIR=$(CRYPTOPP_INCDIR) $(WTMI_MULTI_IMG): FORCE - $(Q)$(MAKE) --no-print-directory -C $(WTP) WTMI_IMG=$(WTMI_IMG) DDR_TOPOLOGY=$(DDR_TOPOLOGY) CLOCKSPRESET=$(CLOCKSPRESET) WTMI + $(q)$(MAKE) --no-print-directory -C $(WTP) WTMI_IMG=$(WTMI_IMG) DDR_TOPOLOGY=$(DDR_TOPOLOGY) CLOCKSPRESET=$(CLOCKSPRESET) WTMI $(BUILD_PLAT)/wtmi.bin: $(WTMI_MULTI_IMG) - $(Q)cp -a $(WTMI_MULTI_IMG) $(BUILD_PLAT)/wtmi.bin + $(q)cp -a $(WTMI_MULTI_IMG) $(BUILD_PLAT)/wtmi.bin $(TIMDDRTOOL): FORCE # Do not remove! Following checks are required to ensure correct TF-A builds, removing these checks leads to broken TF-A builds $(if $(value MV_DDR_PATH),,$(error "Platform '${PLAT}' for ddr tool requires MV_DDR_PATH. Please set MV_DDR_PATH to point to the right directory")) $(if $(wildcard $(value MV_DDR_PATH)/*),,$(error "'MV_DDR_PATH=$(value MV_DDR_PATH)' was specified, but '$(value MV_DDR_PATH)' directory does not exist")) $(if $(shell git -C $(value MV_DDR_PATH) rev-parse --show-cdup 2>&1),$(error "'MV_DDR_PATH=$(value MV_DDR_PATH)' was specified, but '$(value MV_DDR_PATH)' does not contain valid mv-ddr-marvell git repository")) - $(Q)$(MAKE) --no-print-directory -C $(WTP) MV_DDR_PATH=$(MV_DDR_PATH) DDR_TOPOLOGY=$(DDR_TOPOLOGY) mv_ddr - -$(BUILD_PLAT)/$(UART_IMAGE): $(BUILD_PLAT)/$(BOOT_IMAGE) $(BUILD_PLAT)/wtmi.bin $(TBB) $(TIMBUILD) $(TIMDDRTOOL) - @$(ECHO_BLANK_LINE) - @echo "Building uart images" - $(Q)mkdir -p $(BUILD_PLAT)/$(BUILD_UART) - $(Q)cp -a $(BUILD_PLAT)/wtmi.bin $(BUILD_PLAT)/$(BUILD_UART)/wtmi.bin - $(Q)cp -a $(BUILD_PLAT)/$(BOOT_IMAGE) $(BUILD_PLAT)/$(BUILD_UART)/$(BOOT_IMAGE) - $(Q)cd $(BUILD_PLAT)/$(BUILD_UART) && $(TIMBUILD) $(TIMBLDUARTARGS) - $(Q)sed -i 's|WTMI_IMG|wtmi.bin|1' $(TIM_UART_CFG) - $(Q)sed -i 's|BOOT_IMAGE|$(BOOT_IMAGE)|1' $(TIM_UART_CFG) + $(q)$(MAKE) --no-print-directory -C $(WTP) MV_DDR_PATH=$(MV_DDR_PATH) DDR_TOPOLOGY=$(DDR_TOPOLOGY) mv_ddr + +$(BUILD_PLAT)/$(UART_IMAGE): $(BUILD_PLAT)/$(BOOT_IMAGE) $(BUILD_PLAT)/wtmi.bin $(TBB) $(TIMBUILD) $(TIMDDRTOOL) | $(BUILD_PLAT)/$(BUILD_UART)/ $$(@D)/ + $(s)echo + $(s)echo "Building uart images" + $(q)cp -a $(BUILD_PLAT)/wtmi.bin $(BUILD_PLAT)/$(BUILD_UART)/wtmi.bin + $(q)cp -a $(BUILD_PLAT)/$(BOOT_IMAGE) $(BUILD_PLAT)/$(BUILD_UART)/$(BOOT_IMAGE) + $(q)cd $(BUILD_PLAT)/$(BUILD_UART) && $(TIMBUILD) $(TIMBLDUARTARGS) + $(q)sed -i 's|WTMI_IMG|wtmi.bin|1' $(TIM_UART_CFG) + $(q)sed -i 's|BOOT_IMAGE|$(BOOT_IMAGE)|1' $(TIM_UART_CFG) ifeq ($(MARVELL_SECURE_BOOT),1) - $(Q)sed -i 's|WTMI_IMG|wtmi.bin|1' $(TIMN_UART_CFG) - $(Q)sed -i 's|BOOT_IMAGE|$(BOOT_IMAGE)|1' $(TIMN_UART_CFG) + $(q)sed -i 's|WTMI_IMG|wtmi.bin|1' $(TIMN_UART_CFG) + $(q)sed -i 's|BOOT_IMAGE|$(BOOT_IMAGE)|1' $(TIMN_UART_CFG) endif - $(Q)cd $(BUILD_PLAT)/$(BUILD_UART) && $(TBB) -r $(TIM_UART_CFG) -v -D + $(q)cd $(BUILD_PLAT)/$(BUILD_UART) && $(TBB) -r $(TIM_UART_CFG) -v -D ifeq ($(MARVELL_SECURE_BOOT),1) - $(Q)cd $(BUILD_PLAT)/$(BUILD_UART) && $(TBB) -r $(TIMN_UART_CFG) + $(q)cd $(BUILD_PLAT)/$(BUILD_UART) && $(TBB) -r $(TIMN_UART_CFG) endif - $(Q)tar czf $(BUILD_PLAT)/$(UART_IMAGE) -C $(BUILD_PLAT) $(UART_IMAGES) - @$(ECHO_BLANK_LINE) - @echo "Built $@ successfully" - @$(ECHO_BLANK_LINE) + $(q)tar czf $(BUILD_PLAT)/$(UART_IMAGE) -C $(BUILD_PLAT) $(UART_IMAGES) + $(s)echo + $(s)echo "Built $@ successfully" + $(s)echo $(BUILD_PLAT)/$(FLASH_IMAGE): $(BUILD_PLAT)/$(BOOT_IMAGE) $(BUILD_PLAT)/wtmi.bin $(TBB) $(TIMBUILD) $(TIMDDRTOOL) $(TIM2IMG) - @$(ECHO_BLANK_LINE) - @echo "Building flash image" - $(Q)cd $(BUILD_PLAT) && $(TIMBUILD) $(TIMBLDARGS) - $(Q)sed -i 's|WTMI_IMG|wtmi.bin|1' $(TIM_CFG) - $(Q)sed -i 's|BOOT_IMAGE|$(BOOT_IMAGE)|1' $(TIM_CFG) + $(s)echo + $(s)echo "Building flash image" + $(q)cd $(BUILD_PLAT) && $(TIMBUILD) $(TIMBLDARGS) + $(q)sed -i 's|WTMI_IMG|wtmi.bin|1' $(TIM_CFG) + $(q)sed -i 's|BOOT_IMAGE|$(BOOT_IMAGE)|1' $(TIM_CFG) ifeq ($(MARVELL_SECURE_BOOT),1) - $(Q)sed -i 's|WTMI_IMG|wtmi.bin|1' $(TIMN_CFG) - $(Q)sed -i 's|BOOT_IMAGE|$(BOOT_IMAGE)|1' $(TIMN_CFG) - @$(ECHO_BLANK_LINE) - @echo "======================================================="; - @echo " Secure boot. Encrypting wtmi and boot-image"; - @echo "======================================================="; - @$(ECHO_BLANK_LINE) - $(Q)cp $(BUILD_PLAT)/wtmi.bin $(BUILD_PLAT)/wtmi-align.bin - $(Q)truncate -s %16 $(BUILD_PLAT)/wtmi-align.bin - $(Q)${OPENSSL_BIN_PATH}/openssl enc -aes-256-cbc -e -in $(BUILD_PLAT)/wtmi-align.bin \ + $(q)sed -i 's|WTMI_IMG|wtmi.bin|1' $(TIMN_CFG) + $(q)sed -i 's|BOOT_IMAGE|$(BOOT_IMAGE)|1' $(TIMN_CFG) + $(s)echo + $(s)echo "======================================================="; + $(s)echo " Secure boot. Encrypting wtmi and boot-image"; + $(s)echo "======================================================="; + $(s)echo + $(q)cp $(BUILD_PLAT)/wtmi.bin $(BUILD_PLAT)/wtmi-align.bin + $(q)truncate -s %16 $(BUILD_PLAT)/wtmi-align.bin + $(q)${OPENSSL_BIN_PATH}/openssl enc -aes-256-cbc -e -in $(BUILD_PLAT)/wtmi-align.bin \ -out $(BUILD_PLAT)/$(WTMI_ENC_IMG) \ -K `cat $(IMAGESPATH)/aes-256.txt` -nosalt \ -iv `cat $(IMAGESPATH)/iv.txt` -p - $(Q)truncate -s %16 $(BUILD_PLAT)/$(BOOT_IMAGE); - $(Q)${OPENSSL_BIN_PATH}/openssl enc -aes-256-cbc -e -in $(BUILD_PLAT)/$(BOOT_IMAGE) \ + $(q)truncate -s %16 $(BUILD_PLAT)/$(BOOT_IMAGE); + $(q)${OPENSSL_BIN_PATH}/openssl enc -aes-256-cbc -e -in $(BUILD_PLAT)/$(BOOT_IMAGE) \ -out $(BUILD_PLAT)/$(BOOT_ENC_IMAGE) \ -K `cat $(IMAGESPATH)/aes-256.txt` -nosalt \ -iv `cat $(IMAGESPATH)/iv.txt` -p endif - $(Q)cd $(BUILD_PLAT) && $(TBB) -r $(TIM_CFG) -v -D + $(q)cd $(BUILD_PLAT) && $(TBB) -r $(TIM_CFG) -v -D ifeq ($(MARVELL_SECURE_BOOT),1) - $(Q)cd $(BUILD_PLAT) && $(TBB) -r $(TIMN_CFG) - $(Q)sed -i 's|wtmi.bin|$(WTMI_ENC_IMG)|1' $(TIMN_CFG) - $(Q)sed -i 's|$(BOOT_IMAGE)|$(BOOT_ENC_IMAGE)|1' $(TIMN_CFG) + $(q)cd $(BUILD_PLAT) && $(TBB) -r $(TIMN_CFG) + $(q)sed -i 's|wtmi.bin|$(WTMI_ENC_IMG)|1' $(TIMN_CFG) + $(q)sed -i 's|$(BOOT_IMAGE)|$(BOOT_ENC_IMAGE)|1' $(TIMN_CFG) endif - $(Q)cd $(BUILD_PLAT) && $(TIM2IMG) $(TIM2IMGARGS) -o $(BUILD_PLAT)/$(FLASH_IMAGE) - @$(ECHO_BLANK_LINE) - @echo "Built $@ successfully" - @$(ECHO_BLANK_LINE) + $(q)cd $(BUILD_PLAT) && $(TIM2IMG) $(TIM2IMGARGS) -o $(BUILD_PLAT)/$(FLASH_IMAGE) + $(s)echo + $(s)echo "Built $@ successfully" + $(s)echo clean realclean distclean: mrvl_clean .PHONY: mrvl_clean mrvl_clean: - -$(Q)$(MAKE) --no-print-directory -C $(WTP) MV_DDR_PATH=$(MV_DDR_PATH) clean - -$(Q)$(MAKE) --no-print-directory -C $(WTP)/wtptp/src/TBB_Linux -f TBB_linux.mak clean + -$(q)$(MAKE) --no-print-directory -C $(WTP) MV_DDR_PATH=$(MV_DDR_PATH) clean + -$(q)$(MAKE) --no-print-directory -C $(WTP)/wtptp/src/TBB_Linux -f TBB_linux.mak clean ifdef CRYPTOPP_PATH - -$(Q)$(MAKE) --no-print-directory -C $(CRYPTOPP_PATH) -f GNUmakefile clean + -$(q)$(MAKE) --no-print-directory -C $(CRYPTOPP_PATH) -f GNUmakefile clean endif else # WTP diff --git a/plat/marvell/armada/a3k/common/cm3_system_reset.c b/plat/marvell/armada/a3k/common/cm3_system_reset.c index f105d599..030a614c 100644 --- a/plat/marvell/armada/a3k/common/cm3_system_reset.c +++ b/plat/marvell/armada/a3k/common/cm3_system_reset.c @@ -8,11 +8,22 @@ #include <stdbool.h> #include <common/debug.h> +#include <drivers/arm/gic_common.h> +#include <drivers/arm/gicv3.h> #include <drivers/delay_timer.h> #include <lib/mmio.h> +#include <lib/utils_def.h> +#include <a3700_pm.h> +#include <platform_def.h> #include <mvebu_def.h> +/* IO Decoder Error Interrupt Status Registers */ +#define MVEBU_DEC_WIN_REGS_BASE(p) (MVEBU_REGS_BASE + 0xC000 + \ + (p) * 0x100) +#define MVEBU_DEC_WIN_ERR_INT_STS_REG(p) (MVEBU_DEC_WIN_REGS_BASE(p) + \ + 0xF8) + /* Cortex-M3 Secure Processor Mailbox Registers */ #define MVEBU_RWTM_PARAM0_REG (MVEBU_RWTM_REG_BASE) #define MVEBU_RWTM_CMD_REG (MVEBU_RWTM_REG_BASE + 0x40) @@ -23,6 +34,122 @@ #define MVEBU_RWTM_REBOOT_CMD 0x0009 #define MVEBU_RWTM_REBOOT_MAGIC 0xDEADBEEF +static inline uint32_t a3700_gicd_read(uint32_t reg) +{ + return mmio_read_32(PLAT_MARVELL_GICD_BASE + reg); +} + +static inline void a3700_gicd_write(uint32_t reg, uint32_t value) +{ + mmio_write_32(PLAT_MARVELL_GICD_BASE + reg, value); +} + +static void a3700_gicd_ctlr_clear_bits(uint32_t bits) +{ + uint32_t val; + + val = a3700_gicd_read(GICD_CTLR); + if ((val & bits) != 0U) { + a3700_gicd_write(GICD_CTLR, val & ~bits); + mdelay(1); + + if ((a3700_gicd_read(GICD_CTLR) & GICD_CTLR_RWP_BIT) != 0U) { + ERROR("could not clear bits 0x%x in GIC distributor control\n", + bits); + } + } +} + +static void a3700_gic_dist_disable_irqs(void) +{ + int i; + + for (i = 32; i < 224; i += 32) { + a3700_gicd_write(GICD_ICENABLER + (i >> 3), GENMASK_32(31, 0)); + } +} + +static inline uintptr_t a3700_rdist_base(unsigned int proc) +{ + return PLAT_MARVELL_GICR_BASE + (proc << GICR_V3_PCPUBASE_SHIFT); +} + +static inline uint32_t a3700_gicr_read(unsigned int proc, uint32_t reg) +{ + return mmio_read_32(a3700_rdist_base(proc) + reg); +} + +static inline void a3700_gicr_write(unsigned int proc, uint32_t reg, + uint32_t value) +{ + mmio_write_32(a3700_rdist_base(proc) + reg, value); +} + +static void a3700_gic_redist_disable_irqs(unsigned int proc) +{ + a3700_gicr_write(proc, GICR_ICENABLER0, GENMASK_32(31, 0)); + mdelay(1); + + if ((a3700_gicr_read(proc, GICR_CTLR) & GICR_CTLR_RWP_BIT) != 0U) { + ERROR("could not disable core %u PPIs & SGIs\n", proc); + } +} + +static void a3700_gic_redist_mark_asleep(unsigned int proc) +{ + a3700_gicr_write(proc, GICR_WAKER, + a3700_gicr_read(proc, GICR_WAKER) | WAKER_PS_BIT); + mdelay(1); + + if ((a3700_gicr_read(proc, GICR_WAKER) & WAKER_CA_BIT) == 0U) { + ERROR("could not mark core %u redistributor asleep\n", proc); + } +} + +static void a3700_io_addr_dec_ack_err_irq(void) +{ + unsigned int periph; + + for (periph = 0; periph < 16; ++periph) { + /* periph 6 does not exist */ + if (periph == 6) + continue; + + mmio_write_32(MVEBU_DEC_WIN_ERR_INT_STS_REG(periph), + GENMASK_32(1, 0)); + } +} + +static void a3700_gic_reset(void) +{ + a3700_gic_redist_disable_irqs(0); + a3700_gic_redist_disable_irqs(1); + + a3700_gic_redist_mark_asleep(0); + a3700_gic_redist_mark_asleep(1); + + a3700_io_addr_dec_ack_err_irq(); + + a3700_pm_ack_irq(); + + a3700_gic_dist_disable_irqs(); + + a3700_gicd_ctlr_clear_bits(CTLR_ENABLE_G0_BIT | CTLR_ENABLE_G1NS_BIT | + CTLR_ENABLE_G1S_BIT); + + /* Clearing ARE_S and ARE_NS bits is undefined in the specification, but + * works if the previous operations are successful. We need to do it in + * order to put GIC into the same state it was in just after reset. If + * this is successful, the rWTM firmware in the secure coprocessor will + * reset all other peripherals one by one, load new firmware and boot + * it, all without triggering the true warm reset via the WARM_RESET + * register (which may hang the board). + */ + + a3700_gicd_ctlr_clear_bits(CTLR_ARE_S_BIT); + a3700_gicd_ctlr_clear_bits(CTLR_ARE_NS_BIT); +} + static inline bool rwtm_completed(void) { return (mmio_read_32(MVEBU_RWTM_HOST_INT_RESET_REG) & @@ -43,6 +170,11 @@ void cm3_system_reset(void) { int tries = 5; + /* Put GIC into the same state it was just after reset. This is needed + * for the reset issue workaround to work. + */ + a3700_gic_reset(); + for (; tries > 0; --tries) { mmio_clrbits_32(MVEBU_RWTM_HOST_INT_RESET_REG, MVEBU_RWTM_HOST_INT_SP_COMPLETE); diff --git a/plat/marvell/armada/a3k/common/include/a3700_pm.h b/plat/marvell/armada/a3k/common/include/a3700_pm.h index 44dbb9f7..1be82b2b 100644 --- a/plat/marvell/armada/a3k/common/include/a3700_pm.h +++ b/plat/marvell/armada/a3k/common/include/a3700_pm.h @@ -48,6 +48,8 @@ struct pm_wake_up_src_config { struct pm_wake_up_src_config *mv_wake_up_src_config_get(void); +void a3700_pm_ack_irq(void); + void cm3_system_reset(void); #endif /* A3700_PM_H */ diff --git a/plat/marvell/armada/a3k/common/plat_pm.c b/plat/marvell/armada/a3k/common/plat_pm.c index e2d15abf..d573b79f 100644 --- a/plat/marvell/armada/a3k/common/plat_pm.c +++ b/plat/marvell/armada/a3k/common/plat_pm.c @@ -197,7 +197,7 @@ void marvell_psci_arch_init(int die_index) { } -static void a3700_pm_ack_irq(void) +void a3700_pm_ack_irq(void) { uint32_t reg; diff --git a/plat/marvell/armada/a8k/common/a8k_common.mk b/plat/marvell/armada/a8k/common/a8k_common.mk index 4d8a87fd..bdad8b5f 100644 --- a/plat/marvell/armada/a8k/common/a8k_common.mk +++ b/plat/marvell/armada/a8k/common/a8k_common.mk @@ -176,17 +176,17 @@ clean realclean distclean: mrvl_clean .PHONY: mrvl_clean mrvl_clean: - @echo " Doimage CLEAN" - ${Q}${MAKE} PLAT=${PLAT} --no-print-directory -C ${DOIMAGEPATH} clean + $(s)echo " Doimage CLEAN" + $(q)${MAKE} PLAT=${PLAT} --no-print-directory -C ${DOIMAGEPATH} clean ${DOIMAGETOOL}: FORCE - @$(DOIMAGE_LIBS_CHECK) - ${Q}${MAKE} --no-print-directory -C ${DOIMAGEPATH} + $(q)$(DOIMAGE_LIBS_CHECK) + $(q)${MAKE} --no-print-directory -C ${DOIMAGEPATH} ${BUILD_PLAT}/${FLASH_IMAGE}: ${ROM_BIN_EXT} ${BUILD_PLAT}/${BOOT_IMAGE} ${DOIMAGETOOL} - @${ECHO_BLANK_LINE} - @echo "Building flash image" - ${Q}${DOIMAGETOOL} ${DOIMAGE_FLAGS} ${BUILD_PLAT}/${BOOT_IMAGE} ${BUILD_PLAT}/${FLASH_IMAGE} - @${ECHO_BLANK_LINE} - @echo "Built $@ successfully" - @${ECHO_BLANK_LINE} + $(s)echo + $(s)echo "Building flash image" + $(q)${DOIMAGETOOL} ${DOIMAGE_FLAGS} ${BUILD_PLAT}/${BOOT_IMAGE} ${BUILD_PLAT}/${FLASH_IMAGE} + $(s)echo + $(s)echo "Built $@ successfully" + $(s)echo diff --git a/plat/marvell/armada/a8k/common/ble/ble.mk b/plat/marvell/armada/a8k/common/ble/ble.mk index 752ab419..5ab61239 100644 --- a/plat/marvell/armada/a8k/common/ble/ble.mk +++ b/plat/marvell/armada/a8k/common/ble/ble.mk @@ -32,4 +32,4 @@ $(MV_DDR_LIB): FORCE $(if $(value MV_DDR_PATH),,$(error "Platform '$(PLAT)' for BLE requires MV_DDR_PATH. Please set MV_DDR_PATH to point to the right directory")) $(if $(wildcard $(value MV_DDR_PATH)/*),,$(error "'MV_DDR_PATH=$(value MV_DDR_PATH)' was specified, but '$(value MV_DDR_PATH)' directory does not exist")) $(if $(shell git -C $(value MV_DDR_PATH) rev-parse --show-cdup 2>&1),$(error "'MV_DDR_PATH=$(value MV_DDR_PATH)' was specified, but '$(value MV_DDR_PATH)' does not contain valid mv-ddr-marvell git repository")) - @+make -C $(MV_DDR_PATH) --no-print-directory PLAT_INCLUDES="$(MV_DDR_INCLUDES)" PLATFORM=$(PLAT) ARCH=AARCH64 OBJ_DIR=$(BUILD_PLAT)/ble + $(q)+make -C $(MV_DDR_PATH) --no-print-directory PLAT_INCLUDES="$(MV_DDR_INCLUDES)" PLATFORM=$(PLAT) ARCH=AARCH64 OBJ_DIR=$(BUILD_PLAT)/ble diff --git a/plat/marvell/armada/common/marvell_bl31_setup.c b/plat/marvell/armada/common/marvell_bl31_setup.c index 26ba9065..b3641e30 100644 --- a/plat/marvell/armada/common/marvell_bl31_setup.c +++ b/plat/marvell/armada/common/marvell_bl31_setup.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2018 Marvell International Ltd. + * Copyright (C) 2018-2024 Marvell International Ltd. * * SPDX-License-Identifier: BSD-3-Clause * https://spdx.org/licenses @@ -181,8 +181,6 @@ void marvell_bl31_platform_setup(void) */ void marvell_bl31_plat_runtime_setup(void) { - console_switch_state(CONSOLE_FLAG_RUNTIME); - /* Initialize the runtime console */ marvell_console_runtime_init(); } diff --git a/plat/marvell/armada/common/marvell_common.mk b/plat/marvell/armada/common/marvell_common.mk index f0e6edf2..25612321 100644 --- a/plat/marvell/armada/common/marvell_common.mk +++ b/plat/marvell/armada/common/marvell_common.mk @@ -84,13 +84,13 @@ endif $(BUILD_PLAT)/$(BOOT_IMAGE): $(BUILD_PLAT)/bl1.bin $(BUILD_PLAT)/$(FIP_NAME) $(if $(shell find $(BUILD_PLAT)/bl1.bin -type f -size +128k),$(error "Image '$(BUILD_PLAT)/bl1.bin' is bigger than 128kB")) - @cp $(BUILD_PLAT)/bl1.bin $(BUILD_PLAT)/$(BOOT_IMAGE) || { rm -f $(BUILD_PLAT)/$(BOOT_IMAGE); false; } - @truncate -s %128K $(BUILD_PLAT)/$(BOOT_IMAGE) || { rm -f $(BUILD_PLAT)/$(BOOT_IMAGE); false; } - @cat $(BUILD_PLAT)/$(FIP_NAME) >> $(BUILD_PLAT)/$(BOOT_IMAGE) || { rm -f $(BUILD_PLAT)/$(BOOT_IMAGE); false; } - @truncate -s %4 $(BUILD_PLAT)/$(BOOT_IMAGE) || { rm -f $(BUILD_PLAT)/$(BOOT_IMAGE); false; } - @$(ECHO_BLANK_LINE) - @echo "Built $@ successfully" - @$(ECHO_BLANK_LINE) + $(q)cp $(BUILD_PLAT)/bl1.bin $(BUILD_PLAT)/$(BOOT_IMAGE) || { rm -f $(BUILD_PLAT)/$(BOOT_IMAGE); false; } + $(q)truncate -s %128K $(BUILD_PLAT)/$(BOOT_IMAGE) || { rm -f $(BUILD_PLAT)/$(BOOT_IMAGE); false; } + $(q)cat $(BUILD_PLAT)/$(FIP_NAME) >> $(BUILD_PLAT)/$(BOOT_IMAGE) || { rm -f $(BUILD_PLAT)/$(BOOT_IMAGE); false; } + $(q)truncate -s %4 $(BUILD_PLAT)/$(BOOT_IMAGE) || { rm -f $(BUILD_PLAT)/$(BOOT_IMAGE); false; } + $(s)echo + $(s)echo "Built $@ successfully" + $(s)echo .PHONY: mrvl_bootimage mrvl_bootimage: $(BUILD_PLAT)/$(BOOT_IMAGE) diff --git a/plat/mediatek/build_helpers/mtk_build_helpers.mk b/plat/mediatek/build_helpers/mtk_build_helpers.mk index 83a4dd2a..0cb2014a 100644 --- a/plat/mediatek/build_helpers/mtk_build_helpers.mk +++ b/plat/mediatek/build_helpers/mtk_build_helpers.mk @@ -27,12 +27,10 @@ endef # Determine option variable is defined or not then define it define add_defined_option ifdef $(1) -ifeq ($(findstring $(value $(1)), $(uppercase_table)),) -DEFINES += -D$(1)$(if $(value $(1)),=$(value $(1)),) -else ifeq ($(strip $(value $(1))),y) DEFINES += -D$(1)$(if $(value $(1)),=1,) -endif +else ifneq ($(strip $(value $(1))),n) +DEFINES += -D$(1)$(if $(value $(1)),=$(value $(1)),) endif endif endef @@ -73,15 +71,6 @@ define MAKE_MODULE $(eval SOURCES := $(2)) $(eval OBJS_TEMP := $(addprefix $(BUILD_DIR)/$(MODULE)/,$(call SOURCES_TO_OBJS,$(SOURCES)))) $(eval MODULE_OBJS += $(OBJS_TEMP)) - # We use sort only to get a list of unique object directory names. - # ordering is not relevant but sort removes duplicates. - $(eval TEMP_OBJ_DIRS := $(sort $(dir ${OBJS_TEMP} ${LINKERFILE}))) - # The $(dir ) function leaves a trailing / on the directory names - # Rip off the / to match directory names with make rule targets. - $(eval OBJ_DIRS := $(patsubst %/,%,$(TEMP_OBJ_DIRS))) - -$(eval $(foreach objd,${OBJ_DIRS},$(call MAKE_PREREQ_DIR,${objd},${BUILD_DIR}))) -${3}_dirs: | ${OBJ_DIRS} $(eval $(call MAKE_OBJS,$(BUILD_DIR)/$(MODULE),$(SOURCES),${3})) @@ -103,12 +92,8 @@ MTK_PROJECT_CFG := $(MTK_PLAT)/project/$(PLAT)/project_config.mk MTK_OPTIONS := $(MTK_PLAT)/build_helpers/options.mk MTK_COND_EVAL := $(MTK_PLAT)/build_helpers/conditional_eval_options.mk -# Indicate which BL should be built in command line -ifeq (${NEED_BL32},yes) -MTK_BL := bl32 -else MTK_BL := bl31 -endif + # Include common, platform, board level config include $(MTK_COMMON_CFG) include $(MTK_PLAT_CFG) diff --git a/plat/mediatek/common/mtk_bl31_setup.c b/plat/mediatek/common/mtk_bl31_setup.c index 7c9db8bb..0d264b9d 100644 --- a/plat/mediatek/common/mtk_bl31_setup.c +++ b/plat/mediatek/common/mtk_bl31_setup.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, MediaTek Inc. All rights reserved. + * Copyright (c) 2022-2024, MediaTek Inc. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -166,7 +166,6 @@ void bl31_platform_setup(void) void bl31_plat_runtime_setup(void) { mtk_init_one_level(MTK_INIT_LVL_PLAT_RUNTIME); - console_switch_state(CONSOLE_FLAG_RUNTIME); } unsigned int plat_get_syscnt_freq2(void) diff --git a/plat/mediatek/common/mtk_smc_handlers.c b/plat/mediatek/common/mtk_smc_handlers.c index 5a3ad1fb..beb06da8 100644 --- a/plat/mediatek/common/mtk_smc_handlers.c +++ b/plat/mediatek/common/mtk_smc_handlers.c @@ -99,13 +99,13 @@ static void print_smc_descriptor(const struct smc_descriptor pool[]) { const struct smc_descriptor *p_smc_desc; - INFO("print smc descriptor pool\n"); + VERBOSE("print smc descriptor pool\n"); for (p_smc_desc = &pool[0]; (char *)p_smc_desc < (char *)MTK_SMC_POOL_END_UNALIGNED; p_smc_desc++) { - INFO("descriptor name:%s\n", p_smc_desc->smc_name); - INFO("descriptor index:%d\n", *p_smc_desc->smc_descriptor_index); - INFO("smc id 32:0x%x, smc id 64:0x%x\n", + VERBOSE("descriptor name:%s\n", p_smc_desc->smc_name); + VERBOSE("descriptor index:%d\n", *p_smc_desc->smc_descriptor_index); + VERBOSE("smc id 32:0x%x, smc id 64:0x%x\n", p_smc_desc->smc_id_aarch32, p_smc_desc->smc_id_aarch64); } } diff --git a/plat/mediatek/drivers/apusys/apusys_rv/2.0/apusys_rv.c b/plat/mediatek/drivers/apusys/apusys_rv/2.0/apusys_rv.c index 86c4b81c..cb57668f 100644 --- a/plat/mediatek/drivers/apusys/apusys_rv/2.0/apusys_rv.c +++ b/plat/mediatek/drivers/apusys/apusys_rv/2.0/apusys_rv.c @@ -37,18 +37,8 @@ void apusys_rv_mbox_mpu_init(void) int apusys_kernel_apusys_rv_setup_reviser(void) { - static bool apusys_rv_setup_reviser_called; - spin_lock(&apusys_rv_lock); - if (apusys_rv_setup_reviser_called) { - WARN(MODULE_TAG "%s: already initialized\n", __func__); - spin_unlock(&apusys_rv_lock); - return -1; - } - - apusys_rv_setup_reviser_called = true; - mmio_write_32(USERFW_CTXT, CFG_4GB_SEL_EN | CFG_4GB_SEL); mmio_write_32(SECUREFW_CTXT, CFG_4GB_SEL_EN | CFG_4GB_SEL); @@ -74,18 +64,8 @@ int apusys_kernel_apusys_rv_setup_reviser(void) int apusys_kernel_apusys_rv_reset_mp(void) { - static bool apusys_rv_reset_mp_called; - spin_lock(&apusys_rv_lock); - if (apusys_rv_reset_mp_called) { - WARN(MODULE_TAG "%s: already initialized\n", __func__); - spin_unlock(&apusys_rv_lock); - return -1; - } - - apusys_rv_reset_mp_called = true; - mmio_write_32(MD32_SYS_CTRL, MD32_SYS_CTRL_RST); dsb(); @@ -106,18 +86,8 @@ int apusys_kernel_apusys_rv_reset_mp(void) int apusys_kernel_apusys_rv_setup_boot(void) { - static bool apusys_rv_setup_boot_called; - spin_lock(&apusys_rv_lock); - if (apusys_rv_setup_boot_called) { - WARN(MODULE_TAG "%s: already initialized\n", __func__); - spin_unlock(&apusys_rv_lock); - return -1; - } - - apusys_rv_setup_boot_called = true; - mmio_write_32(MD32_BOOT_CTRL, APU_SEC_FW_IOVA); mmio_write_32(MD32_PRE_DEFINE, (PREDEFINE_CACHE_TCM << PREDEF_1G_OFS) | @@ -130,55 +100,17 @@ int apusys_kernel_apusys_rv_setup_boot(void) int apusys_kernel_apusys_rv_start_mp(void) { - static bool apusys_rv_start_mp_called; - spin_lock(&apusys_rv_lock); - - if (apusys_rv_start_mp_called) { - WARN(MODULE_TAG "%s: already initialized\n", __func__); - spin_unlock(&apusys_rv_lock); - return -1; - } - - apusys_rv_start_mp_called = true; - mmio_write_32(MD32_RUNSTALL, MD32_RUN); - spin_unlock(&apusys_rv_lock); return 0; } -static bool watch_dog_is_timeout(void) -{ - if (mmio_read_32(WDT_INT) != WDT_INT_W1C) { - ERROR(MODULE_TAG "%s: WDT does not timeout\n", __func__); - return false; - } - return true; -} - int apusys_kernel_apusys_rv_stop_mp(void) { - static bool apusys_rv_stop_mp_called; - spin_lock(&apusys_rv_lock); - - if (apusys_rv_stop_mp_called) { - WARN(MODULE_TAG "%s: already initialized\n", __func__); - spin_unlock(&apusys_rv_lock); - return -1; - } - - if (watch_dog_is_timeout() == false) { - spin_unlock(&apusys_rv_lock); - return -1; - } - - apusys_rv_stop_mp_called = true; - mmio_write_32(MD32_RUNSTALL, MD32_STALL); - spin_unlock(&apusys_rv_lock); return 0; @@ -186,19 +118,10 @@ int apusys_kernel_apusys_rv_stop_mp(void) int apusys_kernel_apusys_rv_setup_sec_mem(void) { - static bool apusys_rv_setup_sec_mem_called; int ret; spin_lock(&apusys_rv_lock); - if (apusys_rv_setup_sec_mem_called) { - WARN(MODULE_TAG "%s: already initialized\n", __func__); - spin_unlock(&apusys_rv_lock); - return -1; - } - - apusys_rv_setup_sec_mem_called = true; - ret = set_apu_emi_mpu_region(); if (ret != 0) { ERROR(MODULE_TAG "%s: set emimpu protection failed\n", __func__); @@ -230,12 +153,6 @@ int apusys_kernel_apusys_rv_clear_wdt_isr(void) int apusys_kernel_apusys_rv_cg_gating(void) { spin_lock(&apusys_rv_lock); - - if (watch_dog_is_timeout() == false) { - spin_unlock(&apusys_rv_lock); - return -1; - } - mmio_write_32(MD32_CLK_CTRL, MD32_CLK_DIS); spin_unlock(&apusys_rv_lock); @@ -245,12 +162,6 @@ int apusys_kernel_apusys_rv_cg_gating(void) int apusys_kernel_apusys_rv_cg_ungating(void) { spin_lock(&apusys_rv_lock); - - if (watch_dog_is_timeout() == false) { - spin_unlock(&apusys_rv_lock); - return -1; - } - mmio_write_32(MD32_CLK_CTRL, MD32_CLK_EN); spin_unlock(&apusys_rv_lock); diff --git a/plat/mediatek/drivers/apusys/mt8188/apusys_devapc.c b/plat/mediatek/drivers/apusys/mt8188/apusys_devapc.c index da5242a7..f4ff7630 100644 --- a/plat/mediatek/drivers/apusys/mt8188/apusys_devapc.c +++ b/plat/mediatek/drivers/apusys/mt8188/apusys_devapc.c @@ -271,15 +271,8 @@ int apusys_devapc_ao_init(void) int apusys_devapc_rcx_init(void) { - static bool apusys_devapc_rcx_init_called; enum apusys_apc_err_status ret; - if (apusys_devapc_rcx_init_called == true) { - INFO(MODULE_TAG "%s: init more than once!\n", __func__); - return -1; - } - apusys_devapc_rcx_init_called = true; - apusys_devapc_init("APUAPC_CTRL_RCX", APU_CTRL_DAPC_RCX_BASE); apusys_devapc_init("APUAPC_NOC_RCX", APU_NOC_DAPC_RCX_BASE); diff --git a/plat/mediatek/drivers/emi_mpu/emi_mpu.h b/plat/mediatek/drivers/emi_mpu/emi_mpu.h index ef7134cb..329a45e7 100644 --- a/plat/mediatek/drivers/emi_mpu/emi_mpu.h +++ b/plat/mediatek/drivers/emi_mpu/emi_mpu.h @@ -18,7 +18,7 @@ #define FORBIDDEN (5) #define SEC_R_NSEC_RW (6) -#define LOCK (1) +#define LOCK (1UL) #define UNLOCK (0) #if (EMI_MPU_DGROUP_NUM == 1) @@ -69,6 +69,7 @@ int emi_mpu_init(void); int emi_mpu_optee_handler(uint64_t encoded_addr, uint64_t zone_size, uint64_t zone_info); int emi_mpu_set_protection(struct emi_region_info_t *region_info); +int emi_mpu_clear_protection(unsigned int region); void set_emi_mpu_regions(void); int set_apu_emi_mpu_region(void); #endif diff --git a/plat/mediatek/drivers/emi_mpu/emi_mpu_common.c b/plat/mediatek/drivers/emi_mpu/emi_mpu_common.c index 8810be32..1e732f08 100644 --- a/plat/mediatek/drivers/emi_mpu/emi_mpu_common.c +++ b/plat/mediatek/drivers/emi_mpu/emi_mpu_common.c @@ -39,13 +39,13 @@ static int _emi_mpu_set_protection(unsigned int start, unsigned int end, } #if ENABLE_EMI_MPU_SW_LOCK - if (region_lock_state[region] == 1) { + if (region_lock_state[region] == LOCK) { WARN("invalid region\n"); return -1; } if ((dgroup == 0) && ((apc >> 31) & 0x1)) { - region_lock_state[region] = 1; + region_lock_state[region] = LOCK; } apc &= EMI_MPU_APC_SW_LOCK_MASK; @@ -73,6 +73,50 @@ static int _emi_mpu_set_protection(unsigned int start, unsigned int end, return 0; } +int emi_mpu_clear_protection(unsigned int region) +{ + unsigned int dgroup; + + if (region >= EMI_MPU_REGION_NUM) { + WARN("invalid region number\n"); + return -1; + } + +#if ENABLE_EMI_MPU_SW_LOCK + if (region_lock_state[region] == LOCK) { + WARN("SW:region is locked\n"); + return -1; + } +#endif + if (mmio_read_32(EMI_MPU_APC(region, 0)) & (LOCK << 31UL)) { + WARN("HW:EMI-MPU region is locked\n"); + return -1; + } + +#if defined(SUB_EMI_MPU_BASE) + if (mmio_read_32(SUB_EMI_MPU_APC(region, 0)) & (LOCK << 31UL)) { + WARN("HW:SUB EMI-MPU region is locked\n"); + return -1; + } +#endif + + for (dgroup = 0; dgroup < EMI_MPU_DGROUP_NUM; dgroup++) + mmio_write_32(EMI_MPU_APC(region, dgroup), 0x0); + + mmio_write_32(EMI_MPU_SA(region), 0x0); + mmio_write_32(EMI_MPU_EA(region), 0x0); + +#if defined(SUB_EMI_MPU_BASE) + for (dgroup = 0; dgroup < EMI_MPU_DGROUP_NUM; dgroup++) + mmio_write_32(SUB_EMI_MPU_APC(region, dgroup), 0x0); + + mmio_write_32(SUB_EMI_MPU_SA(region), 0); + mmio_write_32(SUB_EMI_MPU_EA(region), 0); +#endif + return 0; +} + + static void dump_emi_mpu_regions(void) { int region, i; diff --git a/plat/mediatek/drivers/emi_mpu/mt8188/emi_mpu.c b/plat/mediatek/drivers/emi_mpu/mt8188/emi_mpu.c index e8882f07..c46cca8f 100644 --- a/plat/mediatek/drivers/emi_mpu/mt8188/emi_mpu.c +++ b/plat/mediatek/drivers/emi_mpu/mt8188/emi_mpu.c @@ -14,11 +14,35 @@ void set_emi_mpu_regions(void) { struct emi_region_info_t region_info; + /* BL31 address */ + region_info.start = TZRAM_BASE; + region_info.end = TZRAM_BASE + TZRAM_SIZE - 1; + region_info.region = BL31_EMI_REGION_ID; + SET_ACCESS_PERMISSION(region_info.apc, LOCK, + FORBIDDEN, FORBIDDEN, FORBIDDEN, FORBIDDEN, + FORBIDDEN, FORBIDDEN, FORBIDDEN, FORBIDDEN, + FORBIDDEN, FORBIDDEN, FORBIDDEN, FORBIDDEN, + FORBIDDEN, FORBIDDEN, FORBIDDEN, SEC_RW); + emi_mpu_set_protection(®ion_info); + +#ifndef SPD_NONE + /* BL32 address */ + region_info.start = BL32_REGION_BASE; + region_info.end = BL32_REGION_BASE + BL32_REGION_SIZE - 1; + region_info.region = BL32_REGION_ID; + SET_ACCESS_PERMISSION(region_info.apc, LOCK, + FORBIDDEN, FORBIDDEN, FORBIDDEN, FORBIDDEN, + FORBIDDEN, FORBIDDEN, FORBIDDEN, FORBIDDEN, + FORBIDDEN, FORBIDDEN, FORBIDDEN, FORBIDDEN, + FORBIDDEN, FORBIDDEN, SEC_RW, SEC_RW); + emi_mpu_set_protection(®ion_info); +#endif + /* SCP core0 DRAM */ - region_info.start = 0x50000000ULL; - region_info.end = 0x528FFFFFULL; - region_info.region = 2; - SET_ACCESS_PERMISSION(region_info.apc, 1, + region_info.start = SCP_CORE0_REGION_BASE; + region_info.end = SCP_CORE0_REGION_BASE + SCP_CORE0_REGION_SIZE - 1; + region_info.region = SCP_CORE0_REGION_ID; + SET_ACCESS_PERMISSION(region_info.apc, LOCK, FORBIDDEN, FORBIDDEN, FORBIDDEN, FORBIDDEN, FORBIDDEN, FORBIDDEN, FORBIDDEN, NO_PROTECTION, FORBIDDEN, FORBIDDEN, FORBIDDEN, FORBIDDEN, @@ -26,10 +50,10 @@ void set_emi_mpu_regions(void) emi_mpu_set_protection(®ion_info); /* SCP core1 DRAM */ - region_info.start = 0x70000000ULL; - region_info.end = 0x729FFFFFULL; - region_info.region = 3; - SET_ACCESS_PERMISSION(region_info.apc, 1, + region_info.start = SCP_CORE1_REGION_BASE; + region_info.end = SCP_CORE1_REGION_BASE + SCP_CORE1_REGION_SIZE - 1; + region_info.region = SCP_CORE1_REGION_ID; + SET_ACCESS_PERMISSION(region_info.apc, LOCK, FORBIDDEN, FORBIDDEN, FORBIDDEN, FORBIDDEN, FORBIDDEN, FORBIDDEN, FORBIDDEN, NO_PROTECTION, FORBIDDEN, FORBIDDEN, FORBIDDEN, FORBIDDEN, @@ -37,10 +61,10 @@ void set_emi_mpu_regions(void) emi_mpu_set_protection(®ion_info); /* DSP protect address */ - region_info.start = 0x60000000ULL; - region_info.end = 0x610FFFFFULL; - region_info.region = 4; - SET_ACCESS_PERMISSION(region_info.apc, 1, + region_info.start = DSP_PROTECT_REGION_BASE; + region_info.end = DSP_PROTECT_REGION_BASE + DSP_PROTECT_REGION_SIZE - 1; + region_info.region = DSP_PROTECT_REGION_ID; + SET_ACCESS_PERMISSION(region_info.apc, LOCK, FORBIDDEN, FORBIDDEN, FORBIDDEN, FORBIDDEN, FORBIDDEN, FORBIDDEN, FORBIDDEN, FORBIDDEN, FORBIDDEN, FORBIDDEN, FORBIDDEN, NO_PROTECTION, @@ -48,10 +72,10 @@ void set_emi_mpu_regions(void) emi_mpu_set_protection(®ion_info); /* All default settings */ - region_info.start = 0x40000000ULL; - region_info.end = 0x1FFFF0000ULL; - region_info.region = 31; - SET_ACCESS_PERMISSION(region_info.apc, 1, + region_info.start = DRAM_START_ADDR; + region_info.end = DRAM_START_ADDR + DRAM_MAX_SIZE - 1; + region_info.region = ALL_DEFAULT_REGION_ID; + SET_ACCESS_PERMISSION(region_info.apc, LOCK, FORBIDDEN, FORBIDDEN, NO_PROTECTION, NO_PROTECTION, NO_PROTECTION, FORBIDDEN, NO_PROTECTION, NO_PROTECTION, NO_PROTECTION, SEC_R_NSEC_RW, NO_PROTECTION, FORBIDDEN, @@ -65,7 +89,7 @@ int set_apu_emi_mpu_region(void) region_info.start = (unsigned long long)APUSYS_SEC_BUF_PA; region_info.end = (unsigned long long)(APUSYS_SEC_BUF_PA + APUSYS_SEC_BUF_SZ) - 1; - region_info.region = APUSYS_SEC_BUF_EMI_REGION; + region_info.region = APUSYS_SEC_BUF_EMI_REGION_ID; SET_ACCESS_PERMISSION(region_info.apc, UNLOCK, FORBIDDEN, FORBIDDEN, FORBIDDEN, FORBIDDEN, @@ -86,32 +110,42 @@ static inline uint32_t get_decoded_zone_id(uint32_t info) return ((info & 0xFFFF0000) >> MPU_PHYSICAL_ADDR_SHIFT_BITS); } +static inline uint32_t get_decoded_set_clear_info(uint32_t info) +{ + return (info & 0x0000FFFF); +} + int emi_mpu_optee_handler(uint64_t encoded_addr, uint64_t zone_size, uint64_t zone_info) { uint64_t phys_addr = get_decoded_phys_addr(encoded_addr); struct emi_region_info_t region_info; - enum MPU_REQ_ORIGIN_ZONE_ID zone_id = get_decoded_zone_id(zone_info); + enum region_ids zone_id = get_decoded_zone_id(zone_info); + uint32_t is_set = get_decoded_set_clear_info(zone_info); INFO("encoded_addr = 0x%lx, zone_size = 0x%lx, zone_info = 0x%lx\n", encoded_addr, zone_size, zone_info); - if (zone_id != MPU_REQ_ORIGIN_TEE_ZONE_SVP) { + if (zone_id < SVP_DRAM_REGION_ID_START || zone_id > SVP_DRAM_REGION_ID_END) { ERROR("Invalid param %s, %d\n", __func__, __LINE__); return MTK_SIP_E_INVALID_PARAM; } - /* SVP DRAM */ - region_info.start = phys_addr; - region_info.end = phys_addr + zone_size; - region_info.region = 4; - SET_ACCESS_PERMISSION(region_info.apc, 1, - FORBIDDEN, FORBIDDEN, FORBIDDEN, FORBIDDEN, - FORBIDDEN, FORBIDDEN, FORBIDDEN, FORBIDDEN, - FORBIDDEN, FORBIDDEN, FORBIDDEN, FORBIDDEN, - FORBIDDEN, FORBIDDEN, FORBIDDEN, SEC_RW); - - emi_mpu_set_protection(®ion_info); + if (is_set > 0) { + /* SVP DRAM */ + region_info.start = phys_addr; + region_info.end = phys_addr + zone_size - 1; + region_info.region = zone_id; + SET_ACCESS_PERMISSION(region_info.apc, UNLOCK, + FORBIDDEN, FORBIDDEN, FORBIDDEN, FORBIDDEN, + FORBIDDEN, FORBIDDEN, FORBIDDEN, FORBIDDEN, + FORBIDDEN, SEC_RW, FORBIDDEN, FORBIDDEN, + FORBIDDEN, FORBIDDEN, SEC_RW, SEC_RW); + + emi_mpu_set_protection(®ion_info); + } else { /* clear region protection */ + emi_mpu_clear_protection(zone_id); + } return 0; -} \ No newline at end of file +} diff --git a/plat/mediatek/drivers/emi_mpu/mt8188/emi_mpu_priv.h b/plat/mediatek/drivers/emi_mpu/mt8188/emi_mpu_priv.h index cc7f7f1e..b64020d7 100644 --- a/plat/mediatek/drivers/emi_mpu/mt8188/emi_mpu_priv.h +++ b/plat/mediatek/drivers/emi_mpu/mt8188/emi_mpu_priv.h @@ -7,7 +7,7 @@ #ifndef EMI_MPU_PRIV_H #define EMI_MPU_PRIV_H -#define ENABLE_EMI_MPU_SW_LOCK (1) +#define ENABLE_EMI_MPU_SW_LOCK (0) #define EMI_MPU_CTRL (EMI_MPU_BASE + 0x000) #define EMI_MPU_DBG (EMI_MPU_BASE + 0x004) @@ -38,13 +38,37 @@ #define EMI_MPU_DOMAIN_NUM (16) #define EMI_MPU_REGION_NUM (32) #define EMI_MPU_ALIGN_BITS (16) -#define DRAM_OFFSET (0x40000000 >> EMI_MPU_ALIGN_BITS) +#define DRAM_START_ADDR (0x40000000ULL) +#define DRAM_OFFSET (DRAM_START_ADDR >> EMI_MPU_ALIGN_BITS) +#define DRAM_MAX_SIZE (0x200000000ULL) +#define BL32_REGION_BASE (0x43000000ULL) +#define BL32_REGION_SIZE (0x4600000ULL) +#define SCP_CORE0_REGION_BASE (0x50000000ULL) +#define SCP_CORE0_REGION_SIZE (0x800000ULL) +#define SCP_CORE1_REGION_BASE (0x70000000ULL) +#define SCP_CORE1_REGION_SIZE (0xa000000ULL) +#define DSP_PROTECT_REGION_BASE (0x60000000ULL) +#define DSP_PROTECT_REGION_SIZE (0x1100000ULL) #define EMI_MPU_DGROUP_NUM (EMI_MPU_DOMAIN_NUM / 8) /* APU EMI MPU Setting */ -#define APUSYS_SEC_BUF_EMI_REGION (21) #define APUSYS_SEC_BUF_PA (0x55000000) #define APUSYS_SEC_BUF_SZ (0x100000) +#define SVP_DRAM_REGION_COUNT (10) + +enum region_ids { + BL31_EMI_REGION_ID = 0, + BL32_REGION_ID, + SCP_CORE0_REGION_ID, + SCP_CORE1_REGION_ID, + DSP_PROTECT_REGION_ID, + SVP_DRAM_REGION_ID_START = 5, + SVP_DRAM_REGION_ID_END = SVP_DRAM_REGION_ID_START + SVP_DRAM_REGION_COUNT - 1, + + APUSYS_SEC_BUF_EMI_REGION_ID = 21, + + ALL_DEFAULT_REGION_ID = 31, +}; #endif diff --git a/plat/mediatek/drivers/gic600/mt_gic_v3.c b/plat/mediatek/drivers/gic600/mt_gic_v3.c index 85f9e37e..2f9765c1 100644 --- a/plat/mediatek/drivers/gic600/mt_gic_v3.c +++ b/plat/mediatek/drivers/gic600/mt_gic_v3.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020-2022, MediaTek Inc. All rights reserved. + * Copyright (c) 2020-2024, MediaTek Inc. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -27,6 +27,10 @@ static uint32_t rdist_has_saved[PLATFORM_CORE_COUNT]; /* we save and restore the GICv3 context on system suspend */ gicv3_dist_ctx_t dist_ctx; +static const interrupt_prop_t mtk_interrupt_props[] = { + PLAT_MTK_G1S_IRQ_PROPS(INTR_GROUP1S) +}; + static unsigned int mt_mpidr_to_core_pos(u_register_t mpidr) { return plat_core_pos_by_mpidr(mpidr); @@ -35,6 +39,8 @@ static unsigned int mt_mpidr_to_core_pos(u_register_t mpidr) gicv3_driver_data_t mt_gicv3_data = { .gicd_base = MT_GIC_BASE, .gicr_base = MT_GIC_RDIST_BASE, + .interrupt_props = mtk_interrupt_props, + .interrupt_props_num = ARRAY_SIZE(mtk_interrupt_props), .rdistif_num = PLATFORM_CORE_COUNT, .rdistif_base_addrs = rdistif_base_addrs, .mpidr_to_core_pos = mt_mpidr_to_core_pos, diff --git a/plat/mediatek/drivers/iommu/mt8188/mtk_iommu_plat.c b/plat/mediatek/drivers/iommu/mt8188/mtk_iommu_plat.c index 1d6863ff..64a10f1a 100644 --- a/plat/mediatek/drivers/iommu/mt8188/mtk_iommu_plat.c +++ b/plat/mediatek/drivers/iommu/mt8188/mtk_iommu_plat.c @@ -4,7 +4,7 @@ * SPDX-License-Identifier: BSD-3-Clause */ -#include <mtk_iommu_plat.h> +#include <mtk_iommu_priv.h> #include <mtk_mmap_pool.h> #include <platform_def.h> @@ -42,7 +42,7 @@ #define MMU_DEV_PCIE_0 (0) #define IFR_CFG_GROUP_NUM (1) -static struct mtk_smi_larb_config mt8188_larb_cfg[SMI_LARB_NUM] = { +static struct mtk_smi_larb_config mt8188_larb_cfg[] = { [SMI_L0_ID] = LARB_CFG_ENTRY(SMI_LARB_0_BASE, 7, 0), [SMI_L1_ID] = LARB_CFG_ENTRY(SMI_LARB_1_BASE, 7, 0), [SMI_L2_ID] = LARB_CFG_ENTRY(SMI_LARB_2_BASE, 5, 0), @@ -80,12 +80,24 @@ static uint32_t mt8188_ifr_mst_cfg_base[IFR_CFG_GROUP_NUM] = { static uint32_t mt8188_ifr_mst_cfg_offs[IFR_CFG_GROUP_NUM] = { PERICFG_AO_IOMMU_1, }; -static struct mtk_ifr_mst_config mt8188_ifr_mst_cfg[MMU_DEV_NUM] = { +static struct mtk_ifr_mst_config mt8188_ifr_mst_cfg[] = { [MMU_DEV_PCIE_0] = IFR_MST_CFG_ENTRY(0, 18), }; struct mtk_smi_larb_config *g_larb_cfg = &mt8188_larb_cfg[0]; +const unsigned int g_larb_num = ARRAY_SIZE(mt8188_larb_cfg); + +static struct mtk_secure_iommu_config mt8188_secure_iommu_config[] = { + SEC_IOMMU_CFG_ENTRY(VDO_SECURE_IOMMU_BASE), + SEC_IOMMU_CFG_ENTRY(VPP_SECURE_IOMMU_BASE), +}; + +struct mtk_secure_iommu_config *g_sec_iommu_cfg = &mt8188_secure_iommu_config[0]; +const unsigned int g_sec_iommu_num = ARRAY_SIZE(mt8188_secure_iommu_config); + struct mtk_ifr_mst_config *g_ifr_mst_cfg = &mt8188_ifr_mst_cfg[0]; +const unsigned int g_ifr_mst_num = ARRAY_SIZE(mt8188_ifr_mst_cfg); + uint32_t *g_ifr_mst_cfg_base = &mt8188_ifr_mst_cfg_base[0]; uint32_t *g_ifr_mst_cfg_offs = &mt8188_ifr_mst_cfg_offs[0]; diff --git a/plat/mediatek/drivers/iommu/mt8188/mtk_iommu_plat.h b/plat/mediatek/drivers/iommu/mt8188/mtk_iommu_plat.h index a59e0c78..a3f38a53 100644 --- a/plat/mediatek/drivers/iommu/mt8188/mtk_iommu_plat.h +++ b/plat/mediatek/drivers/iommu/mt8188/mtk_iommu_plat.h @@ -7,18 +7,13 @@ #ifndef IOMMU_PLAT_H #define IOMMU_PLAT_H -#include <mtk_iommu_priv.h> - /* mm iommu */ -#define SMI_LARB_NUM (26) -extern struct mtk_smi_larb_config *g_larb_cfg; +#define ATF_MTK_SMI_LARB_CFG_SUPPORT -/* infra iommu */ -#define MMU_DEV_NUM (1) -extern struct mtk_ifr_mst_config *g_ifr_mst_cfg; -extern uint32_t *g_ifr_mst_cfg_base; -extern uint32_t *g_ifr_mst_cfg_offs; +/* mm iommu, sec bank dump */ +#define ATF_MTK_IOMMU_CFG_SUPPORT -extern void mtk_infra_iommu_enable_protect(void); +/* infra iommu */ +#define ATF_MTK_INFRA_MASTER_CFG_SUPPORT #endif /* IOMMU_PLAT_H */ diff --git a/plat/mediatek/drivers/iommu/mtk_iommu_priv.h b/plat/mediatek/drivers/iommu/mtk_iommu_priv.h index 3404d313..bae36948 100644 --- a/plat/mediatek/drivers/iommu/mtk_iommu_priv.h +++ b/plat/mediatek/drivers/iommu/mtk_iommu_priv.h @@ -9,6 +9,7 @@ #include <common/debug.h> #include <lib/mmio.h> +#include <mtk_iommu_plat.h> #include <mtk_sip_svc.h> #define LARB_CFG_ENTRY(bs, p_nr, dom) \ @@ -22,9 +23,13 @@ #define IFR_MST_CFG_ENTRY(idx, bit) \ { .cfg_addr_idx = (idx), .r_mmu_en_bit = (bit), } +#define SEC_IOMMU_CFG_ENTRY(s_bs) \ + { .base = (s_bs), } + enum IOMMU_ATF_CMD { IOMMU_ATF_CMD_CONFIG_SMI_LARB, /* For mm master to enable iommu */ IOMMU_ATF_CMD_CONFIG_INFRA_IOMMU, /* For infra master to enable iommu */ + IOMMU_ATF_CMD_GET_SECURE_IOMMU_STATUS, /* For secure iommu translation fault report */ IOMMU_ATF_CMD_COUNT, }; @@ -41,4 +46,30 @@ struct mtk_ifr_mst_config { uint8_t r_mmu_en_bit; }; +struct mtk_secure_iommu_config { + uint32_t base; +}; + + +#ifdef ATF_MTK_SMI_LARB_CFG_SUPPORT +/* mm smi larb security feature is used */ +extern struct mtk_smi_larb_config *g_larb_cfg; +extern const unsigned int g_larb_num; +#endif + +#ifdef ATF_MTK_INFRA_MASTER_CFG_SUPPORT +/* infra iommu is used */ +extern struct mtk_ifr_mst_config *g_ifr_mst_cfg; +extern const unsigned int g_ifr_mst_num; +extern uint32_t *g_ifr_mst_cfg_base; +extern uint32_t *g_ifr_mst_cfg_offs; +extern void mtk_infra_iommu_enable_protect(void); +#endif + +#ifdef ATF_MTK_IOMMU_CFG_SUPPORT +/* secure iommu is used */ +extern struct mtk_secure_iommu_config *g_sec_iommu_cfg; +extern const unsigned int g_sec_iommu_num; +#endif + #endif /* IOMMU_PRIV_H */ diff --git a/plat/mediatek/drivers/iommu/mtk_iommu_smc.c b/plat/mediatek/drivers/iommu/mtk_iommu_smc.c index e9987254..7d701143 100644 --- a/plat/mediatek/drivers/iommu/mtk_iommu_smc.c +++ b/plat/mediatek/drivers/iommu/mtk_iommu_smc.c @@ -5,7 +5,7 @@ */ #include <stddef.h> -#include <mtk_iommu_plat.h> +#include <mtk_iommu_priv.h> /* defination */ /* smi larb */ @@ -23,12 +23,23 @@ /* infra master */ #define IFR_CFG_MMU_EN_MSK(r_bit) (0x3 << (r_bit)) +/* secure iommu */ +#define MMU_INT_CONTROL0 (0x120) +#define INT_CLR BIT(12) +#define MMU_FAULT_ST1 (0x134) +#define MMU_AXI_0_ERR_MASK GENMASK(6, 0) +#define MMU_AXI_FAULT_STATUS(bus) (0x13c + (bus) * 8) +#define MMU_AXI_INVLD_PA(bus) (0x140 + (bus) * 8) +#define MMU_AXI_INT_ID(bus) (0x150 + (bus) * 4) + /* smi larb configure */ /* * If multimedia security config is enabled, the SMI config register must be * configurated in security world. * And the SRAM path is also configurated here to enhance security. */ +#ifdef ATF_MTK_SMI_LARB_CFG_SUPPORT + static void mtk_smi_larb_port_config_to_sram( const struct mtk_smi_larb_config *larb, uint32_t port_id) @@ -55,7 +66,7 @@ static int mtk_smi_larb_port_config_sec(uint32_t larb_id, uint32_t mmu_en_msk) uint32_t to_sram; uint8_t mmu_en; - if (larb_id >= SMI_LARB_NUM) { + if (larb_id >= g_larb_num) { return MTK_SIP_E_INVALID_PARAM; } @@ -75,6 +86,11 @@ static int mtk_smi_larb_port_config_sec(uint32_t larb_id, uint32_t mmu_en_msk) return MTK_SIP_E_SUCCESS; } +#endif /* ATF_MTK_SMI_LARB_CFG_SUPPORT */ + +/* infra iommu configure */ +#ifdef ATF_MTK_INFRA_MASTER_CFG_SUPPORT + static int mtk_infra_master_config_sec(uint32_t dev_id_msk, uint32_t enable) { const struct mtk_ifr_mst_config *ifr_cfg; @@ -82,11 +98,11 @@ static int mtk_infra_master_config_sec(uint32_t dev_id_msk, uint32_t enable) mtk_infra_iommu_enable_protect(); - if (dev_id_msk >= BIT(MMU_DEV_NUM)) { + if (dev_id_msk >= BIT(g_ifr_mst_num)) { return MTK_SIP_E_INVALID_PARAM; } - for (dev_id = 0U; dev_id < MMU_DEV_NUM; dev_id++) { + for (dev_id = 0U; dev_id < g_ifr_mst_num; dev_id++) { if ((dev_id_msk & BIT(dev_id)) == 0U) { continue; } @@ -105,10 +121,50 @@ static int mtk_infra_master_config_sec(uint32_t dev_id_msk, uint32_t enable) return MTK_SIP_E_SUCCESS; } +#endif /* ATF_MTK_INFRA_MASTER_CFG_SUPPORT */ + +/* secure iommu */ +#ifdef ATF_MTK_IOMMU_CFG_SUPPORT +/* Report secure IOMMU fault status to normal world for the debug version */ +static int mtk_secure_iommu_fault_report(uint32_t sec_mmu_base, + uint32_t *f_sta, uint32_t *f_pa, + uint32_t *f_id) +{ + const struct mtk_secure_iommu_config *mmu_cfg = NULL; + uint32_t __maybe_unused bus_id, fault_type; + uint32_t i; + int ret = MTK_SIP_E_NOT_SUPPORTED; -static u_register_t mtk_iommu_handler(u_register_t x1, u_register_t x2, - u_register_t x3, u_register_t x4, - void *handle, struct smccc_res *smccc_ret) + for (i = 0; i < g_sec_iommu_num; i++) { + if (g_sec_iommu_cfg[i].base == sec_mmu_base) { + mmu_cfg = &g_sec_iommu_cfg[i]; + break; + } + } + + if (!mmu_cfg) + return MTK_SIP_E_INVALID_PARAM; +#if DEBUG + fault_type = mmio_read_32(mmu_cfg->base + MMU_FAULT_ST1); + bus_id = (fault_type & MMU_AXI_0_ERR_MASK) ? 0 : 1; + + if (f_sta) + *f_sta = mmio_read_32(mmu_cfg->base + MMU_AXI_FAULT_STATUS(bus_id)); + if (f_pa) + *f_pa = mmio_read_32(mmu_cfg->base + MMU_AXI_INVLD_PA(bus_id)); + if (f_id) + *f_id = mmio_read_32(mmu_cfg->base + MMU_AXI_INT_ID(bus_id)); + ret = MTK_SIP_E_SUCCESS; +#endif + mmio_setbits_32(mmu_cfg->base + MMU_INT_CONTROL0, INT_CLR); + + return ret; +} +#endif /* ATF_MTK_IOMMU_CFG_SUPPORT */ + +u_register_t mtk_iommu_handler(u_register_t x1, u_register_t x2, + u_register_t x3, u_register_t x4, + void *handle, struct smccc_res *smccc_ret) { uint32_t cmd_id = x1, mdl_id = x2, val = x3; int ret = MTK_SIP_E_NOT_SUPPORTED; @@ -117,12 +173,25 @@ static u_register_t mtk_iommu_handler(u_register_t x1, u_register_t x2, (void)handle; switch (cmd_id) { +#ifdef ATF_MTK_SMI_LARB_CFG_SUPPORT case IOMMU_ATF_CMD_CONFIG_SMI_LARB: ret = mtk_smi_larb_port_config_sec(mdl_id, val); break; +#endif +#ifdef ATF_MTK_INFRA_MASTER_CFG_SUPPORT case IOMMU_ATF_CMD_CONFIG_INFRA_IOMMU: ret = mtk_infra_master_config_sec(mdl_id, val); break; +#endif +#ifdef ATF_MTK_IOMMU_CFG_SUPPORT + case IOMMU_ATF_CMD_GET_SECURE_IOMMU_STATUS: + (void)val; + ret = mtk_secure_iommu_fault_report(mdl_id, + (uint32_t *)&smccc_ret->a1, + (uint32_t *)&smccc_ret->a2, + (uint32_t *)&smccc_ret->a3); + break; +#endif default: break; } diff --git a/plat/mediatek/drivers/iommu/mtk_iommu_smc.h b/plat/mediatek/drivers/iommu/mtk_iommu_smc.h new file mode 100644 index 00000000..9537dbe9 --- /dev/null +++ b/plat/mediatek/drivers/iommu/mtk_iommu_smc.h @@ -0,0 +1,15 @@ +/* + * Copyright (c) 2023, MediaTek Inc. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef IOMMU_SMC_H +#define IOMMU_SMC_H + +#include <mtk_sip_svc.h> + +u_register_t mtk_iommu_handler(u_register_t x1, u_register_t x2, + u_register_t x3, u_register_t x4, + void *handle, struct smccc_res *smccc_ret); +#endif diff --git a/plat/mediatek/drivers/rng/mt8186/rng_plat.c b/plat/mediatek/drivers/rng/mt8186/rng_plat.c new file mode 100644 index 00000000..691b9233 --- /dev/null +++ b/plat/mediatek/drivers/rng/mt8186/rng_plat.c @@ -0,0 +1,153 @@ +/* + * Copyright (c) 2024, MediaTek Inc. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <assert.h> +#include <errno.h> +#include <stdint.h> + +#include <common/debug.h> +#include <drivers/delay_timer.h> +#include <lib/mmio.h> +#include <lib/smccc.h> +#include <lib/spinlock.h> +#include <plat/common/platform.h> +#include <platform_def.h> +#include <services/trng_svc.h> +#include <smccc_helpers.h> + +#include <mtk_mmap_pool.h> +#include <mtk_sip_svc.h> +#include "rng_plat.h" + +static spinlock_t rng_lock; + +static int trng_wait(uint32_t reg, uint32_t expected_value) +{ + uint64_t timeout = timeout_init_us(TRNG_TIME_OUT); + uint32_t value = 0; + + do { + value = mmio_read_32(reg); + if ((value & expected_value) == expected_value) + return 0; + + udelay(10); + } while (!timeout_elapsed(timeout)); + + return -ETIMEDOUT; +} + +static int trng_write(uint32_t reg, uint32_t value, + uint32_t read_reg, uint32_t expected_value) +{ + int retry = MTK_TRNG_MAX_ROUND; + uint32_t read_value = 0; + + do { + mmio_write_32(reg, value); + + read_value = mmio_read_32(read_reg); + if ((read_value & value) == expected_value) + return 0; + + udelay(10); + } while (--retry > 0); + + return -ETIMEDOUT; +} + +static uint32_t trng_prng(uint32_t *rand) +{ + int32_t ret = 0; + uint32_t seed[4] = {0}; + + if (rand == NULL) + return MTK_SIP_E_INVALID_PARAM; + + /* ungate */ + ret = trng_write(TRNG_PDN_CLR, TRNG_PDN_VALUE, TRNG_PDN_STATUS, 0); + if (ret) { + ERROR("%s: ungate fail\n", __func__); + return MTK_SIP_E_NOT_SUPPORTED; + } + + /* read random data once and drop it */ + seed[0] = mmio_read_32(TRNG_DATA); + + /* enable von-neumann extractor */ + mmio_setbits_32(TRNG_CONF, TRNG_CONF_VON_EN); + + /* start */ + mmio_setbits_32(TRNG_CTRL, TRNG_CTRL_START); + + /* get seeds from trng */ + for (int i = 0; i < ARRAY_SIZE(seed); i++) { + ret = trng_wait(TRNG_CTRL, TRNG_CTRL_RDY); + if (ret) { + ERROR("%s: trng NOT ready\n", __func__); + return MTK_SIP_E_NOT_SUPPORTED; + } + + seed[i] = mmio_read_32(TRNG_DATA); + } + + /* stop */ + mmio_clrbits_32(TRNG_CTRL, TRNG_CTRL_START); + + /* gate */ + ret = trng_write(TRNG_PDN_SET, TRNG_PDN_VALUE, TRNG_PDN_STATUS, TRNG_PDN_VALUE); + if (ret) { + ERROR("%s: gate fail\n", __func__); + return MTK_SIP_E_NOT_SUPPORTED; + } + + for (int i = 0; i < ARRAY_SIZE(seed); i++) + rand[i] = seed[i]; + + return 0; +} + +static uint32_t get_true_rnd(uint32_t *val, uint32_t num) +{ + uint32_t rand[4] = {0}; + uint32_t ret; + + if (val == NULL || num > ARRAY_SIZE(rand)) + return MTK_SIP_E_INVALID_PARAM; + + spin_lock(&rng_lock); + ret = trng_prng(rand); + spin_unlock(&rng_lock); + + for (int i = 0; i < num; i++) + val[i] = rand[i]; + + return ret; +} + +/* + * plat_get_entropy - get 64-bit random number data which is used form + * atf early stage + * output - out: output 64-bit entropy combine with 2 32-bit random number + */ +bool plat_get_entropy(uint64_t *out) +{ + uint32_t entropy_pool[2] = {0}; + uint32_t ret; + + assert(out); + assert(!check_uptr_overflow((uintptr_t)out, sizeof(*out))); + + /* Get 2 32-bits entropy */ + ret = get_true_rnd(entropy_pool, ARRAY_SIZE(entropy_pool)); + if (ret) + return false; + + /* Output 8 bytes entropy combine with 2 32-bit random number. */ + *out = ((uint64_t)entropy_pool[0] << 32) | entropy_pool[1]; + + return true; +} diff --git a/plat/mediatek/drivers/rng/mt8186/rng_plat.h b/plat/mediatek/drivers/rng/mt8186/rng_plat.h new file mode 100644 index 00000000..ab22c45d --- /dev/null +++ b/plat/mediatek/drivers/rng/mt8186/rng_plat.h @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2024, MediaTek Inc. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef RNG_PLAT_H +#define RNG_PLAT_H + +#define TRNG_TIME_OUT 1000 +#define MTK_TRNG_MAX_ROUND 4 + +/******************************************************************************* + * TRNG related constants + ******************************************************************************/ +#define TRNG_BASE_SIZE 0x1000 +#define TRNG_CTRL (TRNG_BASE + 0x0000) +#define TRNG_TIME (TRNG_BASE + 0x0004) +#define TRNG_DATA (TRNG_BASE + 0x0008) +#define TRNG_CONF (TRNG_BASE + 0x000C) +#define TRNG_CTRL_RDY 0x80000000 +#define TRNG_CTRL_START 0x00000001 +#define TRNG_CONF_VON_EN 0x00000020 +#define TRNG_PDN_BASE_SIZE 0x1000 +#define TRNG_PDN_SET (INFRACFG_AO_BASE + 0x0088) +#define TRNG_PDN_CLR (INFRACFG_AO_BASE + 0x008C) +#define TRNG_PDN_STATUS (INFRACFG_AO_BASE + 0x0094) +#define TRNG_PDN_VALUE 0x200 + +#endif /* RNG_PLAT_H */ diff --git a/plat/mediatek/drivers/rng/mt8188/rng_plat.c b/plat/mediatek/drivers/rng/mt8188/rng_plat.c new file mode 100644 index 00000000..361be222 --- /dev/null +++ b/plat/mediatek/drivers/rng/mt8188/rng_plat.c @@ -0,0 +1,96 @@ +/* + * Copyright (c) 2024, MediaTek Inc. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <assert.h> +#include <stdbool.h> +#include <stdint.h> + +#include <common/debug.h> +#include <drivers/delay_timer.h> +#include <lib/mmio.h> +#include <lib/smccc.h> +#include <plat/common/platform.h> +#include <platform_def.h> +#include <services/trng_svc.h> +#include <smccc_helpers.h> + +#include "rng_plat.h" + +static void trng_external_swrst(void) +{ + /* External swrst to reset whole rng module */ + mmio_setbits_32(TRNG_SWRST_SET_REG, RNG_SWRST_B); + mmio_setbits_32(TRNG_SWRST_CLR_REG, RNG_SWRST_B); + + /* Disable irq */ + mmio_clrbits_32(RNG_IRQ_CFG, IRQ_EN); + /* Set default cutoff value */ + mmio_write_32(RNG_HTEST, RNG_DEFAULT_CUTOFF); + /* Enable rng */ + mmio_setbits_32(RNG_EN, DRBG_EN | NRBG_EN); +} + +static bool get_entropy_32(uint32_t *out) +{ + uint64_t time = timeout_init_us(MTK_TIMEOUT_POLL); + int retry_times = 0; + + while (!(mmio_read_32(RNG_STATUS) & DRBG_VALID)) { + if (mmio_read_32(RNG_STATUS) & (RNG_ERROR | APB_ERROR)) { + mmio_clrbits_32(RNG_EN, DRBG_EN | NRBG_EN); + + mmio_clrbits_32(RNG_SWRST, SWRST_B); + mmio_setbits_32(RNG_SWRST, SWRST_B); + + mmio_setbits_32(RNG_EN, DRBG_EN | NRBG_EN); + } + + if (timeout_elapsed(time)) { + trng_external_swrst(); + time = timeout_init_us(MTK_TIMEOUT_POLL); + retry_times++; + } + + if (retry_times > MTK_RETRY_CNT) { + ERROR("%s: trng NOT ready\n", __func__); + return false; + } + } + + *out = mmio_read_32(RNG_OUT); + + return true; +} + +/* Get random number from HWRNG and return 8 bytes of entropy. + * Return 'true' when random value generated successfully, otherwise return + * 'false'. + */ +bool plat_get_entropy(uint64_t *out) +{ + uint32_t seed[2] = { 0 }; + int i = 0; + + assert(out); + assert(!check_uptr_overflow((uintptr_t)out, sizeof(*out))); + + /* Disable interrupt mode */ + mmio_clrbits_32(RNG_IRQ_CFG, IRQ_EN); + /* Set rng health test cutoff value */ + mmio_write_32(RNG_HTEST, RNG_DEFAULT_CUTOFF); + /* Enable rng module */ + mmio_setbits_32(RNG_EN, DRBG_EN | NRBG_EN); + + for (i = 0; i < ARRAY_SIZE(seed); i++) { + if (!get_entropy_32(&seed[i])) + return false; + } + + /* Output 8 bytes entropy by combining 2 32-bit random numbers. */ + *out = ((uint64_t)seed[0] << 32) | seed[1]; + + return true; +} diff --git a/plat/mediatek/drivers/rng/mt8188/rng_plat.h b/plat/mediatek/drivers/rng/mt8188/rng_plat.h new file mode 100644 index 00000000..37ef2711 --- /dev/null +++ b/plat/mediatek/drivers/rng/mt8188/rng_plat.h @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2024, MediaTek Inc. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef RNG_PLAT_H +#define RNG_PLAT_H + +#include <lib/utils_def.h> + +#define MTK_TIMEOUT_POLL 1000 + +#define MTK_RETRY_CNT 10 + +#define RNG_DEFAULT_CUTOFF 0x04871C0B + +/******************************************************************************* + * TRNG related constants + ******************************************************************************/ +#define RNG_STATUS (TRNG_BASE + 0x0004) +#define RNG_SWRST (TRNG_BASE + 0x0010) +#define RNG_IRQ_CFG (TRNG_BASE + 0x0014) +#define RNG_EN (TRNG_BASE + 0x0020) +#define RNG_HTEST (TRNG_BASE + 0x0028) +#define RNG_OUT (TRNG_BASE + 0x0030) +#define RNG_RAW (TRNG_BASE + 0x0038) +#define RNG_SRC (TRNG_BASE + 0x0050) + +#define RAW_VALID BIT(12) +#define DRBG_VALID BIT(4) +#define RAW_EN BIT(8) +#define NRBG_EN BIT(4) +#define DRBG_EN BIT(0) +#define IRQ_EN BIT(0) +#define SWRST_B BIT(0) +/* Error conditions */ +#define RNG_ERROR GENMASK_32(28, 24) +#define APB_ERROR BIT(16) + +/* External swrst */ +#define TRNG_SWRST_SET_REG (INFRACFG_AO_BASE + 0x150) +#define TRNG_SWRST_CLR_REG (INFRACFG_AO_BASE + 0x154) +#define RNG_SWRST_B BIT(13) + +#endif /* RNG_PLAT_H */ diff --git a/plat/mediatek/drivers/rng/rng.c b/plat/mediatek/drivers/rng/rng.c new file mode 100644 index 00000000..d611168b --- /dev/null +++ b/plat/mediatek/drivers/rng/rng.c @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2024, MediaTek Inc. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <stdbool.h> +#include <stdint.h> + +#include <lib/smccc.h> +#include <plat/common/plat_trng.h> + +#include <mtk_sip_svc.h> + +DEFINE_SVC_UUID2(_plat_trng_uuid, + 0xf6b2c8d9, 0x1abb, 0x4d83, 0xb2, 0x3f, + 0x5c, 0x51, 0xb6, 0xef, 0xfc, 0xaf +); +uuid_t plat_trng_uuid; + +void plat_entropy_setup(void) +{ + uint64_t placeholder; + + plat_trng_uuid = _plat_trng_uuid; + + /* Initialise the entropy source and trigger RNG generation */ + plat_get_entropy(&placeholder); +} diff --git a/plat/mediatek/drivers/rng/rules.mk b/plat/mediatek/drivers/rng/rules.mk new file mode 100644 index 00000000..5bcd2cd7 --- /dev/null +++ b/plat/mediatek/drivers/rng/rules.mk @@ -0,0 +1,17 @@ +# +# Copyright (c) 2024, MediaTek Inc. All rights reserved. +# +# SPDX-License-Identifier: BSD-3-Clause +# + +LOCAL_DIR := $(call GET_LOCAL_DIR) + +MODULE := rng + +PLAT_INCLUDES += -I${LOCAL_DIR}/${MTK_SOC} +PLAT_INCLUDES += -I${LOCAL_DIR} + +LOCAL_SRCS-y := ${LOCAL_DIR}/rng.c +LOCAL_SRCS-y += ${LOCAL_DIR}/${MTK_SOC}/rng_plat.c + +$(eval $(call MAKE_MODULE,$(MODULE),$(LOCAL_SRCS-y),$(MTK_BL))) diff --git a/plat/mediatek/mt8188/include/plat_helpers.h b/plat/mediatek/include/plat_helpers.h similarity index 71% rename from plat/mediatek/mt8188/include/plat_helpers.h rename to plat/mediatek/include/plat_helpers.h index eb78623c..b86ed239 100644 --- a/plat/mediatek/mt8188/include/plat_helpers.h +++ b/plat/mediatek/include/plat_helpers.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2024, Mediatek Inc. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ diff --git a/plat/mediatek/mt8186/include/plat_helpers.h b/plat/mediatek/mt8186/include/plat_helpers.h deleted file mode 100644 index ebc9fa01..00000000 --- a/plat/mediatek/mt8186/include/plat_helpers.h +++ /dev/null @@ -1,12 +0,0 @@ -/* - * Copyright (c) 2021, ARM Limited and Contributors. All rights reserved. - * - * SPDX-License-Identifier: BSD-3-Clause - */ - -#ifndef __PLAT_HELPERS_H__ -#define __PLAT_HELPERS_H__ - -unsigned int plat_mediatek_calc_core_pos(u_register_t mpidr); - -#endif /* __PLAT_HELPERS_H__ */ diff --git a/plat/mediatek/mt8186/include/platform_def.h b/plat/mediatek/mt8186/include/platform_def.h index 850ce2fc..707f4a5b 100644 --- a/plat/mediatek/mt8186/include/platform_def.h +++ b/plat/mediatek/mt8186/include/platform_def.h @@ -1,6 +1,6 @@ /* - * Copyright (c) 2021-2022, ARM Limited and Contributors. All rights reserved. - * Copyright (c) 2021-2022, MediaTek Inc. All rights reserved. + * Copyright (c) 2021-2024, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2021-2024, MediaTek Inc. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -76,6 +76,11 @@ ******************************************************************************/ #define MSDC0_BASE (IO_PHYS + 0x01230000) +/******************************************************************************* + * TRNG related constants + ******************************************************************************/ +#define TRNG_BASE (IO_PHYS + 0x0020F000) + /******************************************************************************* * GIC-600 & interrupt handling related constants ******************************************************************************/ @@ -83,6 +88,8 @@ #define BASE_GICD_BASE MT_GIC_BASE #define MT_GIC_RDIST_BASE (MT_GIC_BASE + 0x40000) +#define PLAT_MTK_G1S_IRQ_PROPS(grp) + #define SYS_CIRQ_BASE (IO_PHYS + 0x204000) #define CIRQ_REG_NUM (11) #define CIRQ_IRQ_NUM (326) diff --git a/plat/mediatek/mt8186/platform.mk b/plat/mediatek/mt8186/platform.mk index 2bd2fb4d..9c03340c 100644 --- a/plat/mediatek/mt8186/platform.mk +++ b/plat/mediatek/mt8186/platform.mk @@ -1,5 +1,5 @@ # -# Copyright (c) 2021-2023, MediaTek Inc. All rights reserved. +# Copyright (c) 2021-2024, MediaTek Inc. All rights reserved. # # SPDX-License-Identifier: BSD-3-Clause # @@ -7,6 +7,9 @@ MTK_PLAT := plat/mediatek MTK_PLAT_SOC := ${MTK_PLAT}/${PLAT} +# True Random Number Generator firmware Interface +TRNG_SUPPORT := 1 + PLAT_INCLUDES := -I${MTK_PLAT}/common/ \ -I${MTK_PLAT}/drivers/cirq/ \ -I${MTK_PLAT}/drivers/gic600/ \ @@ -79,6 +82,11 @@ BL31_SOURCES += common/desc_image_load.c \ ${MTK_PLAT_SOC}/plat_sip_calls.c \ ${MTK_PLAT_SOC}/plat_topology.c +ifeq (${TRNG_SUPPORT},1) +BL31_SOURCES += ${MTK_PLAT}/drivers/rng/rng.c \ + ${MTK_PLAT}/drivers/rng/${PLAT}/rng_plat.c +endif + # Build SPM drivers include ${MTK_PLAT_SOC}/drivers/spm/build.mk diff --git a/plat/mediatek/mt8188/include/platform_def.h b/plat/mediatek/mt8188/include/platform_def.h index 0a7ae6d4..dccb052f 100644 --- a/plat/mediatek/mt8188/include/platform_def.h +++ b/plat/mediatek/mt8188/include/platform_def.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022-2023, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2022-2024, ARM Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -96,6 +96,11 @@ /* Base MTK_platform compatible GIC memory map */ #define BASE_GICD_BASE (MT_GIC_BASE) #define MT_GIC_RDIST_BASE (MT_GIC_BASE + 0x40000) +#define DEV_IRQ_ID 580 + +#define PLAT_MTK_G1S_IRQ_PROPS(grp) \ + INTR_PROP_DESC(DEV_IRQ_ID, GIC_HIGHEST_SEC_PRIORITY, grp, \ + GIC_INTR_CFG_LEVEL) /******************************************************************************* * CIRQ related constants @@ -107,7 +112,13 @@ #define CIRQ_SPI_START (96) /******************************************************************************* - * MM IOMMU & SMI related constants + * MM IOMMU related constants + ******************************************************************************/ +#define VDO_SECURE_IOMMU_BASE (IO_PHYS + 0x0c028000 + 0x4000) +#define VPP_SECURE_IOMMU_BASE (IO_PHYS + 0x04018000 + 0x4000) + +/******************************************************************************* + * SMI larb constants ******************************************************************************/ #define SMI_LARB_0_BASE (IO_PHYS + 0x0c022000) #define SMI_LARB_1_BASE (IO_PHYS + 0x0c023000) @@ -178,6 +189,11 @@ #define EMI_MPU_BASE (IO_PHYS + 0x00226000) #define SUB_EMI_MPU_BASE (IO_PHYS + 0x00225000) +/******************************************************************************* + * TRNG related constants + ******************************************************************************/ +#define TRNG_BASE (IO_PHYS + 0x0020F000) + /******************************************************************************* * System counter frequency related constants ******************************************************************************/ diff --git a/plat/mediatek/mt8188/plat_config.mk b/plat/mediatek/mt8188/plat_config.mk index 2e3392f1..82ef7e8f 100644 --- a/plat/mediatek/mt8188/plat_config.mk +++ b/plat/mediatek/mt8188/plat_config.mk @@ -1,5 +1,5 @@ # -# Copyright (c) 2022-2023, MediaTek Inc. All rights reserved. +# Copyright (c) 2022-2024, MediaTek Inc. All rights reserved. # # SPDX-License-Identifier: BSD-3-Clause # @@ -46,5 +46,8 @@ CONFIG_MTK_SUPPORT_SYSTEM_SUSPEND := y CPU_PM_TINYSYS_SUPPORT := y MTK_PUBEVENT_ENABLE := y +# True Random Number Generator firmware Interface +TRNG_SUPPORT := 1 + MACH_MT8188 := 1 $(eval $(call add_define,MACH_MT8188)) diff --git a/plat/mediatek/mt8188/platform.mk b/plat/mediatek/mt8188/platform.mk index 5096e15a..b7764476 100644 --- a/plat/mediatek/mt8188/platform.mk +++ b/plat/mediatek/mt8188/platform.mk @@ -1,5 +1,5 @@ # -# Copyright (c) 2022-2023, MediaTek Inc. All rights reserved. +# Copyright (c) 2022-2024, MediaTek Inc. All rights reserved. # # SPDX-License-Identifier: BSD-3-Clause # @@ -39,6 +39,9 @@ MODULES-y += $(MTK_PLAT)/drivers/mcusys MODULES-y += $(MTK_PLAT)/drivers/pmic MODULES-y += $(MTK_PLAT)/drivers/pmic_wrap MODULES-y += $(MTK_PLAT)/drivers/ptp3 +ifeq (${TRNG_SUPPORT},1) +MODULES-y += $(MTK_PLAT)/drivers/rng +endif MODULES-y += $(MTK_PLAT)/drivers/rtc MODULES-y += $(MTK_PLAT)/drivers/spm MODULES-y += $(MTK_PLAT)/drivers/timer diff --git a/plat/mediatek/mt8192/drivers/emi_mpu/emi_mpu.c b/plat/mediatek/mt8192/drivers/emi_mpu/emi_mpu.c index 26bed29e..7b867d86 100644 --- a/plat/mediatek/mt8192/drivers/emi_mpu/emi_mpu.c +++ b/plat/mediatek/mt8192/drivers/emi_mpu/emi_mpu.c @@ -97,7 +97,7 @@ void emi_mpu_init(void) /* PCI-e protect address(64MB) */ region_info.start = 0xC0000000ULL; - region_info.end = 0xC3FF0000ULL; + region_info.end = 0xC3FFFFFFULL; region_info.region = 1; SET_ACCESS_PERMISSION(region_info.apc, 1, FORBIDDEN, FORBIDDEN, FORBIDDEN, FORBIDDEN, @@ -108,7 +108,7 @@ void emi_mpu_init(void) /* SCP protect address */ region_info.start = 0x50000000ULL; - region_info.end = 0x513F0000ULL; + region_info.end = 0x513FFFFFULL; region_info.region = 2; SET_ACCESS_PERMISSION(region_info.apc, 1, FORBIDDEN, FORBIDDEN, FORBIDDEN, FORBIDDEN, @@ -117,21 +117,10 @@ void emi_mpu_init(void) NO_PROT, FORBIDDEN, FORBIDDEN, NO_PROT); emi_mpu_set_protection(®ion_info); - /* DSP protect address */ - region_info.start = 0x40000000ULL; /* dram base addr */ - region_info.end = 0x1FFFF0000ULL; - region_info.region = 3; - SET_ACCESS_PERMISSION(region_info.apc, 1, - FORBIDDEN, FORBIDDEN, FORBIDDEN, FORBIDDEN, - FORBIDDEN, FORBIDDEN, FORBIDDEN, FORBIDDEN, - FORBIDDEN, FORBIDDEN, FORBIDDEN, FORBIDDEN, - FORBIDDEN, FORBIDDEN, FORBIDDEN, NO_PROT); - emi_mpu_set_protection(®ion_info); - /* Forbidden All */ region_info.start = 0x40000000ULL; /* dram base addr */ - region_info.end = 0x1FFFF0000ULL; - region_info.region = 4; + region_info.end = 0x1FFFFFFFFULL; + region_info.region = 3; SET_ACCESS_PERMISSION(region_info.apc, 1, FORBIDDEN, FORBIDDEN, FORBIDDEN, FORBIDDEN, FORBIDDEN, FORBIDDEN, FORBIDDEN, FORBIDDEN, diff --git a/plat/mediatek/mt8192/include/plat_helpers.h b/plat/mediatek/mt8192/include/plat_helpers.h deleted file mode 100644 index 9b550ee2..00000000 --- a/plat/mediatek/mt8192/include/plat_helpers.h +++ /dev/null @@ -1,12 +0,0 @@ -/* - * Copyright (c) 2020, ARM Limited and Contributors. All rights reserved. - * - * SPDX-License-Identifier: BSD-3-Clause - */ - -#ifndef __PLAT_HELPERS_H__ -#define __PLAT_HELPERS_H__ - -unsigned int plat_mediatek_calc_core_pos(u_register_t mpidr); - -#endif /* __PLAT_HELPERS_H__ */ diff --git a/plat/mediatek/mt8192/include/platform_def.h b/plat/mediatek/mt8192/include/platform_def.h index ec377b5a..1b25e00c 100644 --- a/plat/mediatek/mt8192/include/platform_def.h +++ b/plat/mediatek/mt8192/include/platform_def.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2020-2024, ARM Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -82,6 +82,8 @@ #define BASE_GICD_BASE MT_GIC_BASE #define MT_GIC_RDIST_BASE (MT_GIC_BASE + 0x40000) +#define PLAT_MTK_G1S_IRQ_PROPS(grp) + #define SYS_CIRQ_BASE (IO_PHYS + 0x204000) #define CIRQ_REG_NUM 14 #define CIRQ_IRQ_NUM 439 diff --git a/plat/mediatek/mt8195/drivers/emi_mpu/emi_mpu.c b/plat/mediatek/mt8195/drivers/emi_mpu/emi_mpu.c index b6e5a2d9..8e4a6754 100644 --- a/plat/mediatek/mt8195/drivers/emi_mpu/emi_mpu.c +++ b/plat/mediatek/mt8195/drivers/emi_mpu/emi_mpu.c @@ -118,7 +118,7 @@ void emi_mpu_init(void) /* SCP DRAM */ region_info.start = 0x50000000ULL; - region_info.end = 0x51400000ULL; + region_info.end = 0x513FFFFFULL; region_info.region = 2; SET_ACCESS_PERMISSION(region_info.apc, 1, FORBIDDEN, FORBIDDEN, FORBIDDEN, FORBIDDEN, diff --git a/plat/mediatek/mt8195/include/plat_helpers.h b/plat/mediatek/mt8195/include/plat_helpers.h deleted file mode 100644 index ebc9fa01..00000000 --- a/plat/mediatek/mt8195/include/plat_helpers.h +++ /dev/null @@ -1,12 +0,0 @@ -/* - * Copyright (c) 2021, ARM Limited and Contributors. All rights reserved. - * - * SPDX-License-Identifier: BSD-3-Clause - */ - -#ifndef __PLAT_HELPERS_H__ -#define __PLAT_HELPERS_H__ - -unsigned int plat_mediatek_calc_core_pos(u_register_t mpidr); - -#endif /* __PLAT_HELPERS_H__ */ diff --git a/plat/mediatek/mt8195/include/platform_def.h b/plat/mediatek/mt8195/include/platform_def.h index 8696f2a1..a70abecd 100644 --- a/plat/mediatek/mt8195/include/platform_def.h +++ b/plat/mediatek/mt8195/include/platform_def.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021-2022, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2021-2024, ARM Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -95,6 +95,11 @@ /* Base MTK_platform compatible GIC memory map */ #define BASE_GICD_BASE MT_GIC_BASE #define MT_GIC_RDIST_BASE (MT_GIC_BASE + 0x40000) +#define DEV_IRQ_ID 580 + +#define PLAT_MTK_G1S_IRQ_PROPS(grp) \ + INTR_PROP_DESC(DEV_IRQ_ID, GIC_HIGHEST_SEC_PRIORITY, grp, \ + GIC_INTR_CFG_LEVEL) #define SYS_CIRQ_BASE (IO_PHYS + 0x204000) #define CIRQ_REG_NUM 23 diff --git a/plat/nuvoton/common/nuvoton_helpers.S b/plat/nuvoton/common/nuvoton_helpers.S index 09035a19..9c788158 100644 --- a/plat/nuvoton/common/nuvoton_helpers.S +++ b/plat/nuvoton/common/nuvoton_helpers.S @@ -151,9 +151,9 @@ func plat_wait_for_warm_boot */ bl plat_my_core_pos lsl x0, x0, #3 - mov x8, x0 mov_imm x2, PLAT_NPCM_TM_HOLD_BASE add x0, x0, x2 + mov x8, x0 mov_imm x2, PLAT_NPCM_TRUSTED_NOTIFICATION_BASE add x8, x8, x2 /* diff --git a/plat/nuvoton/npcm845x/npcm845x_bl31_setup.c b/plat/nuvoton/npcm845x/npcm845x_bl31_setup.c index 08448db6..4b29bbca 100644 --- a/plat/nuvoton/npcm845x/npcm845x_bl31_setup.c +++ b/plat/nuvoton/npcm845x/npcm845x_bl31_setup.c @@ -47,6 +47,20 @@ static entry_point_info_t bl33_image_ep_info; BL31_END - BL31_START, \ MT_MEMORY | MT_RW | EL3_PAS) +#if RECLAIM_INIT_CODE +IMPORT_SYM(unsigned long, __INIT_CODE_START__, BL_INIT_CODE_BASE); +IMPORT_SYM(unsigned long, __INIT_CODE_END__, BL_CODE_END_UNALIGNED); + +#define BL_INIT_CODE_END ((BL_CODE_END_UNALIGNED + PAGE_SIZE - 1) & \ + ~(PAGE_SIZE - 1)) + +#define MAP_BL_INIT_CODE MAP_REGION_FLAT( \ + BL_INIT_CODE_BASE, \ + BL_INIT_CODE_END - \ + BL_INIT_CODE_BASE, \ + MT_CODE | MT_SECURE) +#endif /* RECLAIM_INIT_CODE */ + #if SEPARATE_NOBITS_REGION #define MAP_BL31_NOBITS MAP_REGION_FLAT( \ BL31_NOBITS_BASE, \ @@ -103,7 +117,10 @@ int board_uart_init(void) unsigned int plat_get_syscnt_freq2(void) { - return (unsigned int)COUNTER_FREQUENCY; + /* + * Do not overwrite the value set by BootBlock + */ + return (unsigned int)read_cntfrq_el0(); } /****************************************************************************** @@ -117,6 +134,7 @@ unsigned int plat_get_syscnt_freq2(void) void bl31_early_platform_setup2(u_register_t arg0, u_register_t arg1, u_register_t arg2, u_register_t arg3) { + arg0 = arg1 = arg2 = arg3 = 0; #if RESET_TO_BL31 void *from_bl2 = (void *)arg0; void *plat_params_from_bl2 = (void *)arg3; @@ -309,24 +327,11 @@ void __init npcm845x_bl31_plat_arch_setup(void) { const mmap_region_t bl_regions[] = { MAP_BL31_TOTAL, -#if SEPARATE_NOBITS_REGION - MAP_BL31_NOBITS, -#endif /* SEPARATE_NOBITS_REGION */ ARM_MAP_BL_RO, -#if USE_ROMLIB - ARM_MAP_ROMLIB_CODE, - ARM_MAP_ROMLIB_DATA, -#endif /* USE_ROMLIB */ #if USE_COHERENT_MEM ARM_MAP_BL_COHERENT_RAM, #endif /* USE_COHERENT_MEM */ ARM_MAP_SHARED_RAM, -#ifdef SECONDARY_BRINGUP - ARM_MAP_NS_DRAM1, - #ifdef BL32_BASE - ARM_MAP_BL32_CORE_MEM - #endif /* BL32_BASE */ -#endif /* SECONDARY_BRINGUP */ {0} }; setup_page_tables(bl_regions, plat_arm_get_mmap()); diff --git a/plat/nuvoton/npcm845x/platform.mk b/plat/nuvoton/npcm845x/platform.mk index 92c7e2fe..1959aacf 100644 --- a/plat/nuvoton/npcm845x/platform.mk +++ b/plat/nuvoton/npcm845x/platform.mk @@ -12,6 +12,7 @@ RESET_TO_BL31 := 1 SPMD_SPM_AT_SEL2 := 0 #temporary until the RAM size is reduced USE_COHERENT_MEM := 1 +INIT_UNUSED_NS_EL2 := 1 $(eval $(call add_define,RESET_TO_BL31)) @@ -21,12 +22,29 @@ ifeq (${ARCH}, aarch64) # Trusted DRAM (if available) or the TZC secured area of DRAM. # TZC secured DRAM is the default. +ARM_TSP_RAM_LOCATION ?= dram + +ifeq (${ARM_TSP_RAM_LOCATION}, tsram) +ARM_TSP_RAM_LOCATION_ID = ARM_TRUSTED_SRAM_ID +else ifeq (${ARM_TSP_RAM_LOCATION}, tdram) +ARM_TSP_RAM_LOCATION_ID = ARM_TRUSTED_DRAM_ID +else ifeq (${ARM_TSP_RAM_LOCATION}, dram) +ARM_TSP_RAM_LOCATION_ID = ARM_DRAM_ID +else +$(error "Unsupported ARM_TSP_RAM_LOCATION value") +endif + +# Process flags # Process ARM_BL31_IN_DRAM flag ARM_BL31_IN_DRAM := 0 $(eval $(call assert_boolean,ARM_BL31_IN_DRAM)) $(eval $(call add_define,ARM_BL31_IN_DRAM)) +else +ARM_TSP_RAM_LOCATION_ID = ARM_TRUSTED_SRAM_ID endif +$(eval $(call add_define,ARM_TSP_RAM_LOCATION_ID)) + # For the original power-state parameter format, the State-ID can be encoded # according to the recommended encoding or zero. This flag determines which # State-ID encoding to be parsed. @@ -140,11 +158,25 @@ $(error For SEPARATE_NOBITS_REGION, RECLAIM_INIT_CODE cannot be supported) endif endif +# Disable ARM Cryptocell by default +ARM_CRYPTOCELL_INTEG := 0 +$(eval $(call assert_boolean,ARM_CRYPTOCELL_INTEG)) +$(eval $(call add_define,ARM_CRYPTOCELL_INTEG)) + # Enable PIE support for RESET_TO_BL31 case ifeq (${RESET_TO_BL31},1) ENABLE_PIE := 1 endif +# CryptoCell integration relies on coherent buffers for passing data from +# the AP CPU to the CryptoCell + +ifeq (${ARM_CRYPTOCELL_INTEG},1) +ifeq (${USE_COHERENT_MEM},0) +$(error "ARM_CRYPTOCELL_INTEG needs USE_COHERENT_MEM to be set.") +endif +endif + PLAT_INCLUDES := -Iinclude/plat/nuvoton/npcm845x \ -Iinclude/plat/nuvoton/common \ -Iinclude/drivers/nuvoton/npcm845x \ @@ -287,7 +319,8 @@ endif # Pointer Authentication sources ifeq (${ENABLE_PAUTH}, 1) -PLAT_BL_COMMON_SOURCES += plat/arm/common/aarch64/arm_pauth.c +PLAT_BL_COMMON_SOURCES += plat/arm/common/aarch64/arm_pauth.c \ + lib/extensions/pauth/pauth_helpers.S endif ifeq (${SPD},spmd) @@ -325,7 +358,11 @@ BL2_SOURCES += ${AUTH_SOURCES} \ $(eval $(call TOOL_ADD_IMG,ns_bl2u,--fwu,FWU_)) # We expect to locate the *.mk files under the directories specified below +ifeq (${ARM_CRYPTOCELL_INTEG},0) CRYPTO_LIB_MK := drivers/auth/mbedtls/mbedtls_crypto.mk +else +CRYPTO_LIB_MK := drivers/auth/cryptocell/cryptocell_crypto.mk +endif IMG_PARSER_LIB_MK := drivers/auth/mbedtls/mbedtls_x509.mk @@ -336,6 +373,12 @@ $(info Including ${IMG_PARSER_LIB_MK}) include ${IMG_PARSER_LIB_MK} endif +ifeq (${RECLAIM_INIT_CODE}, 1) +ifeq (${ARM_XLAT_TABLES_LIB_V1}, 1) +$(error "To reclaim init code xlat tables v2 must be used") +endif +endif + ifeq (${MEASURED_BOOT},1) MEASURED_BOOT_MK := drivers/measured_boot/measured_boot.mk $(info Including ${MEASURED_BOOT_MK}) @@ -352,3 +395,6 @@ BL2U_SOURCES := DEBUG_CONSOLE ?= 0 $(eval $(call add_define,DEBUG_CONSOLE)) + +$(eval $(call add_define,ARM_TSP_RAM_LOCATION_ID)) + diff --git a/plat/nvidia/tegra/common/tegra_bl31_setup.c b/plat/nvidia/tegra/common/tegra_bl31_setup.c index e3068b69..09eda84b 100644 --- a/plat/nvidia/tegra/common/tegra_bl31_setup.c +++ b/plat/nvidia/tegra/common/tegra_bl31_setup.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015-2020, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2015-2024, ARM Limited and Contributors. All rights reserved. * Copyright (c) 2020-2023, NVIDIA Corporation. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause diff --git a/plat/nvidia/tegra/common/tegra_fiq_glue.c b/plat/nvidia/tegra/common/tegra_fiq_glue.c index 5309d98c..4ff98883 100644 --- a/plat/nvidia/tegra/common/tegra_fiq_glue.c +++ b/plat/nvidia/tegra/common/tegra_fiq_glue.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016-2020, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2016-2024, Arm Limited and Contributors. All rights reserved. * Copyright (c) 2020, NVIDIA Corporation. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause @@ -142,7 +142,7 @@ int32_t tegra_fiq_get_intr_context(void) val = read_ctx_reg((gpregs_ctx), (uint32_t)(CTX_GPREG_SP_EL0)); write_ctx_reg((gpregs_ctx), (uint32_t)(CTX_GPREG_X2), (val)); - val = read_ctx_reg((el1state_ctx), (uint32_t)(CTX_SP_EL1)); + val = read_el1_ctx_common(el1state_ctx, sp_el1); write_ctx_reg((gpregs_ctx), (uint32_t)(CTX_GPREG_X3), (val)); return 0; diff --git a/plat/nvidia/tegra/platform.mk b/plat/nvidia/tegra/platform.mk index 23655649..6ca90a0a 100644 --- a/plat/nvidia/tegra/platform.mk +++ b/plat/nvidia/tegra/platform.mk @@ -1,5 +1,5 @@ # -# Copyright (c) 2015-2019, ARM Limited and Contributors. All rights reserved. +# Copyright (c) 2015-2024, Arm Limited and Contributors. All rights reserved. # Copyright (c) 2020, NVIDIA Corporation. All rights reserved. # # SPDX-License-Identifier: BSD-3-Clause @@ -82,7 +82,7 @@ override LIBC_SRCS := $(addprefix lib/libc/, \ INCLUDES += -Iinclude/lib/libc \ -Iinclude/lib/libc/$(ARCH) \ -ifneq ($(findstring armlink,$(notdir $(LD))),) +ifeq ($($(ARCH)-ld-id),arm-link) # o suppress warnings for section mismatches, undefined symbols # o use only those libraries that are specified in the input file # list to resolve references diff --git a/plat/nvidia/tegra/soc/t194/plat_psci_handlers.c b/plat/nvidia/tegra/soc/t194/plat_psci_handlers.c index 83d815af..82328831 100644 --- a/plat/nvidia/tegra/soc/t194/plat_psci_handlers.c +++ b/plat/nvidia/tegra/soc/t194/plat_psci_handlers.c @@ -356,10 +356,10 @@ int32_t tegra_soc_pwr_domain_on_finish(const psci_power_state_t *target_state) * will re-init this info from non-secure software when the * core come online. */ - actlr_elx = read_ctx_reg((get_el1_sysregs_ctx(ctx)), (CTX_ACTLR_EL1)); + actlr_elx = read_el1_ctx_common((get_el1_sysregs_ctx(ctx)), actlr_el1); actlr_elx &= ~DENVER_CPU_PMSTATE_MASK; actlr_elx |= DENVER_CPU_PMSTATE_C1; - write_ctx_reg((get_el1_sysregs_ctx(ctx)), (CTX_ACTLR_EL1), (actlr_elx)); + write_el1_ctx_common((get_el1_sysregs_ctx(ctx)), actlr_el1, actlr_elx); /* * Check if we are exiting from deep sleep and restore SE diff --git a/plat/nxp/common/fip_handler/fuse_fip/fuse.mk b/plat/nxp/common/fip_handler/fuse_fip/fuse.mk index 4e84d020..14ddefd2 100644 --- a/plat/nxp/common/fip_handler/fuse_fip/fuse.mk +++ b/plat/nxp/common/fip_handler/fuse_fip/fuse.mk @@ -1,6 +1,6 @@ # # Copyright 2018-2020 NXP -# Copyright (c) 2023, Arm Limited. All rights reserved. +# Copyright (c) 2023-2024, Arm Limited. All rights reserved. # # SPDX-License-Identifier: BSD-3-Clause # @@ -73,7 +73,7 @@ endif ifeq (${FUSE_PROV_FILE},) else ${BUILD_PLAT}/${FUSE_PROV_FILE_SB}: ${FUSE_PROV_FILE} - @echo " Generating CSF Header for $@ $<" + $(s)echo " Generating CSF Header for $@ $<" $(CST_DIR)/create_hdr_esbc --in $< --out $@ --app_off ${CSF_HDR_SZ} \ --app $< ${FUSE_INPUT_FILE} endif @@ -81,7 +81,7 @@ endif ifeq (${FUSE_UP_FILE},) else ${BUILD_PLAT}/${FUSE_UP_FILE_SB}: ${FUSE_UP_FILE} - @echo " Generating CSF Header for $@ $<" + $(s)echo " Generating CSF Header for $@ $<" $(CST_DIR)/create_hdr_esbc --in $< --out $@ --app_off ${CSF_HDR_SZ} \ --app $< ${FUSE_INPUT_FILE} endif @@ -94,6 +94,6 @@ ifeq (${FUSE_FIP_DEPS},) endif ${FIPTOOL} create ${FUSE_FIP_ARGS} $@ ${FIPTOOL} info $@ - @${ECHO_BLANK_LINE} - @echo "Built $@ successfully" - @${ECHO_BLANK_LINE} + $(s)echo + $(s)echo "Built $@ successfully" + $(s)echo diff --git a/plat/nxp/common/tbbr/tbbr.mk b/plat/nxp/common/tbbr/tbbr.mk index 4aac9d63..02333986 100644 --- a/plat/nxp/common/tbbr/tbbr.mk +++ b/plat/nxp/common/tbbr/tbbr.mk @@ -130,15 +130,15 @@ else $(BUILD_PLAT)/bl2/nxp_rotpk.o: $(ROTPK_HASH) certificates: $(ROT_KEY) - $(ROT_KEY): | $(BUILD_PLAT) - @echo " OPENSSL $@" - @if [ ! -f $(ROT_KEY) ]; then \ + $(ROT_KEY): | $$(@D)/ + $(s)echo " OPENSSL $@" + $(q)if [ ! -f $(ROT_KEY) ]; then \ ${OPENSSL_BIN_PATH}/openssl genrsa 2048 > $@ 2>/dev/null; \ fi - $(ROTPK_HASH): $(ROT_KEY) - @echo " OPENSSL $@" - $(Q)${OPENSSL_BIN_PATH}/openssl rsa -in $< -pubout -outform DER 2>/dev/null |\ + $(ROTPK_HASH): $(ROT_KEY) | $$(@D)/ + $(s)echo " OPENSSL $@" + $(q)${OPENSSL_BIN_PATH}/openssl rsa -in $< -pubout -outform DER 2>/dev/null |\ ${OPENSSL_BIN_PATH}/openssl dgst -sha256 -binary > $@ 2>/dev/null endif #MBEDTLS_DIR diff --git a/plat/nxp/s32/s32g274ardb2/include/plat_console.h b/plat/nxp/s32/s32g274ardb2/include/plat_console.h new file mode 100644 index 00000000..43c2bfd8 --- /dev/null +++ b/plat/nxp/s32/s32g274ardb2/include/plat_console.h @@ -0,0 +1,12 @@ +/* + * Copyright 2024 NXP + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef PLAT_CONSOLE_H +#define PLAT_CONSOLE_H + +void console_s32g2_register(void); + +#endif diff --git a/plat/nxp/s32/s32g274ardb2/include/plat_helpers.h b/plat/nxp/s32/s32g274ardb2/include/plat_helpers.h new file mode 100644 index 00000000..18582ec4 --- /dev/null +++ b/plat/nxp/s32/s32g274ardb2/include/plat_helpers.h @@ -0,0 +1,12 @@ +/* + * Copyright 2024 NXP + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef PLAT_HELPERS_H +#define PLAT_HELPERS_H + +unsigned int s32g2_core_pos_by_mpidr(u_register_t mpidr); + +#endif /* PLAT_HELPERS_H */ diff --git a/plat/nxp/s32/s32g274ardb2/include/plat_io_storage.h b/plat/nxp/s32/s32g274ardb2/include/plat_io_storage.h new file mode 100644 index 00000000..ea013009 --- /dev/null +++ b/plat/nxp/s32/s32g274ardb2/include/plat_io_storage.h @@ -0,0 +1,12 @@ +/* + * Copyright 2024 NXP + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef PLAT_IO_STORAGE_H +#define PLAT_IO_STORAGE_H + +void plat_s32g2_io_setup(void); + +#endif diff --git a/plat/nxp/s32/s32g274ardb2/include/plat_macros.S b/plat/nxp/s32/s32g274ardb2/include/plat_macros.S new file mode 100644 index 00000000..8f0c4726 --- /dev/null +++ b/plat/nxp/s32/s32g274ardb2/include/plat_macros.S @@ -0,0 +1,22 @@ +/* + * Copyright 2024 NXP + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef PLAT_MACROS_S +#define PLAT_MACROS_S + +/* --------------------------------------------- + * The below required platform porting macro + * prints out relevant GIC and CCI registers + * whenever an unhandled exception is taken in + * BL31. + * Clobbers: x0 - x10, x16, x17, sp + * --------------------------------------------- + */ +.macro plat_crash_print_regs +.endm + +#endif /* PLAT_MACROS_S */ + diff --git a/plat/nxp/s32/s32g274ardb2/include/platform_def.h b/plat/nxp/s32/s32g274ardb2/include/platform_def.h new file mode 100644 index 00000000..1a4c495a --- /dev/null +++ b/plat/nxp/s32/s32g274ardb2/include/platform_def.h @@ -0,0 +1,77 @@ +/* + * Copyright 2024 NXP + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef PLATFORM_DEF_H +#define PLATFORM_DEF_H + +#include <plat/common/common_def.h> + +#define PLATFORM_STACK_SIZE U(0x1000) + +/* Caches */ +#define CACHE_WRITEBACK_SHIFT U(6) +#define CACHE_WRITEBACK_GRANULE (U(1) << CACHE_WRITEBACK_SHIFT) + +/* CPU Topology */ +#define PLATFORM_CORE_COUNT U(4) +#define PLATFORM_SYSTEM_COUNT U(1) +#define PLATFORM_CLUSTER_COUNT U(2) +#define PLATFORM_PRIMARY_CPU U(0) +#define PLATFORM_MPIDR_CPU_MASK_BITS U(1) +#define PLATFORM_MAX_CPUS_PER_CLUSTER U(2) + +/* Power Domains */ +#define PLAT_NUM_PWR_DOMAINS (PLATFORM_SYSTEM_COUNT + \ + PLATFORM_CLUSTER_COUNT + \ + PLATFORM_CORE_COUNT) +#define PLAT_MAX_PWR_LVL MPIDR_AFFLVL2 +#define PLAT_MAX_OFF_STATE U(2) +#define PLAT_MAX_RET_STATE U(1) +#define PLAT_MAX_PWR_LVL_STATES U(2) + +/* BL2 stage */ +#define BL2_BASE UL(0x34078000) +#define BL2_LIMIT UL(0x34100000) + +/* BL31 stage */ +#define BL31_BASE UL(0x34200000) +#define BL31_LIMIT UL(0x34300000) + +/* It is a dummy value for now, given the missing DDR */ +#define BL33_BASE UL(0x34500000) +#define BL33_LIMIT UL(0x345FF000) + +#define PLAT_PHY_ADDR_SPACE_SIZE (ULL(1) << 36) +/* We'll be doing a 1:1 mapping anyway */ +#define PLAT_VIRT_ADDR_SPACE_SIZE (ULL(1) << 36) + +#define MAX_MMAP_REGIONS U(8) +#define MAX_XLAT_TABLES U(32) + +/* Console settings */ +#define UART_BASE UL(0x401C8000) +#define UART_BAUDRATE U(115200) +#define UART_CLOCK_HZ U(125000000) + +#define S32G_FIP_BASE UL(0x34100000) +#define S32G_FIP_SIZE UL(0x100000) + +#define MAX_IO_HANDLES U(2) +#define MAX_IO_DEVICES U(2) + +/* GIC settings */ +#define S32G_GIC_BASE UL(0x50800000) +#define PLAT_GICD_BASE S32G_GIC_BASE +#define PLAT_GICR_BASE (S32G_GIC_BASE + UL(0x80000)) + +/* Generic timer frequency; this goes directly into CNTFRQ_EL0. + * Its end-value is 5MHz; this is based on the assumption that + * GPR00[CA53_COUNTER_CLK_DIV_VAL] contains the reset value of 0x7, hence + * producing a divider value of 8, applied to the FXOSC frequency of 40MHz. + */ +#define COUNTER_FREQUENCY U(5000000) + +#endif /* PLATFORM_DEF_H */ diff --git a/plat/nxp/s32/s32g274ardb2/include/s32cc-ncore.h b/plat/nxp/s32/s32g274ardb2/include/s32cc-ncore.h new file mode 100644 index 00000000..0c0870f7 --- /dev/null +++ b/plat/nxp/s32/s32g274ardb2/include/s32cc-ncore.h @@ -0,0 +1,102 @@ +/* + * Copyright 2019-2021, 2024 NXP + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef S32G2_NCORE_H +#define S32G2_NCORE_H + +#include <stdbool.h> + +#define NCORE_BASE_ADDR UL(0x50400000) + +#define A53_CLUSTER0_CAIU U(0) +#define A53_CLUSTER1_CAIU U(1) + +/** + * Directory Unit Registers + * + * The directory provides a point of serialization for establishing transaction + * ordering and sequences coherence operations and memory accesses. + */ +#define NCORE_DIRU(N) (NCORE_BASE_ADDR + UL(0x80000) + ((N) * UL(0x1000))) + +/* DIRU Snoop Filtering Enable */ +#define NCORE_DIRUSFE(N) (NCORE_DIRU(N) + UL(0x10)) +#define NCORE_DIRUSFE_SFEN(SF) BIT_32(SF) + +/* DIRU Caching Agent Snoop Enable */ +#define NCORE_DIRUCASE(N) (NCORE_DIRU(N) + UL(0x40)) +#define NCORE_DIRUCASE_CASNPEN(CAIU) BIT_32(CAIU) + +/* DIRU Snoop Filter Maintenance Control */ +#define NCORE_DIRUSFMC(N) (NCORE_DIRU(N) + UL(0x80)) +#define NCORE_DIRUSFMC_SFID(SF) ((SF) << 16U) +#define NCORE_DIRUSFMC_SFMNTOP_ALL U(0x0) + +/* DIRU Snoop Filter Maintenance Activity */ +#define NCORE_DIRUSFMA(N) (NCORE_DIRU(N) + UL(0x84)) +#define NCORE_DIRUSFMA_MNTOPACTV BIT_32(0) + +/** + * Coherent Agent Interface Unit Registers + * + * CAI provides a means for a fully-coherent agent to be connected to the Ncore. + * The CAI behaves as a fully-coherent slave. + */ +#define NCORE_CAIU(N) (NCORE_BASE_ADDR + ((N) * UL(0x1000))) +#define NCORE_CAIU0_BASE_ADDR NCORE_BASE_ADDR + +/* CAIU Transaction Control */ +#define NCORE_CAIUTC_OFF UL(0x0) +#define NCORE_CAIUTC_ISOLEN_SHIFT U(1) +#define NCORE_CAIUTC_ISOLEN_MASK BIT_32(NCORE_CAIUTC_ISOLEN_SHIFT) + +#define NCORE_CAIUTC(N) (NCORE_CAIU(N) + NCORE_CAIUTC_OFF) + +/* CAIU Identification */ +#define NCORE_CAIUID(n) (NCORE_CAIU(n) + UL(0xFFC)) +#define NCORE_CAIUID_TYPE GENMASK_32(U(19), U(16)) +#define NCORE_CAIUID_TYPE_ACE_DVM U(0x0) + +/** + * Coherent Subsystem Registers + */ +#define NCORE_CSR (NCORE_BASE_ADDR + UL(0xFF000)) + +/* Coherent Subsystem ACE DVM Snoop Enable */ +#define NCORE_CSADSE (NCORE_CSR + UL(0x40)) +#define NCORE_CSADSE_DVMSNPEN(CAIU) BIT_32(CAIU) + +/* Coherent Subsystem Identification */ +#define NCORE_CSID (NCORE_CSR + UL(0xFFC)) +#define NCORE_CSID_NUMSFS_SHIFT U(18) +#define NCORE_CSID_NUMSFS_MASK GENMASK_32(U(22), NCORE_CSID_NUMSFS_SHIFT) +#define NCORE_CSID_NUMSFS(CSIDR) (((CSIDR) & NCORE_CSID_NUMSFS_MASK) \ + >> NCORE_CSID_NUMSFS_SHIFT) + +/* Coherent Subsystem Unit Identification */ +#define NCORE_CSUID (NCORE_CSR + UL(0xFF8)) +#define NCORE_CSUID_NUMCMIUS_SHIFT U(24) +#define NCORE_CSUID_NUMCMIUS_MASK GENMASK_32(U(29), NCORE_CSUID_NUMCMIUS_SHIFT) +#define NCORE_CSUID_NUMCMIUS(CSUIDR) (((CSUIDR) & NCORE_CSUID_NUMCMIUS_MASK) \ + >> NCORE_CSUID_NUMCMIUS_SHIFT) +#define NCORE_CSUID_NUMDIRUS_SHIFT U(16) +#define NCORE_CSUID_NUMDIRUS_MASK GENMASK_32(U(21), NCORE_CSUID_NUMDIRUS_SHIFT) +#define NCORE_CSUID_NUMDIRUS(CSUIDR) (((CSUIDR) & NCORE_CSUID_NUMDIRUS_MASK) \ + >> NCORE_CSUID_NUMDIRUS_SHIFT) +#define NCORE_CSUID_NUMNCBUS_SHIFT U(8) +#define NCORE_CSUID_NUMNCBUS_MASK GENMASK_32(U(13), NCORE_CSUID_NUMNCBUS_SHIFT) +#define NCORE_CSUID_NUMNCBUS(CSUIDR) (((CSUIDR) & NCORE_CSUID_NUMNCBUS_MASK) \ + >> NCORE_CSUID_NUMNCBUS_SHIFT) + +#ifndef __ASSEMBLER__ +void ncore_caiu_online(uint32_t caiu); +void ncore_caiu_offline(uint32_t caiu); +void ncore_init(void); +bool ncore_is_caiu_online(uint32_t caiu); +void ncore_disable_caiu_isolation(uint32_t caiu); +#endif /* __ASSEMBLER__ */ + +#endif /* S32G2_NCORE_H */ diff --git a/plat/nxp/s32/s32g274ardb2/plat_bl2_el3_setup.c b/plat/nxp/s32/s32g274ardb2/plat_bl2_el3_setup.c new file mode 100644 index 00000000..4645f01e --- /dev/null +++ b/plat/nxp/s32/s32g274ardb2/plat_bl2_el3_setup.c @@ -0,0 +1,80 @@ +/* + * Copyright 2024 NXP + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <common/debug.h> +#include <common/desc_image_load.h> +#include <lib/mmio.h> +#include <plat/common/platform.h> +#include <plat_console.h> +#include <s32cc-clk-drv.h> +#include <plat_io_storage.h> +#include <s32cc-ncore.h> + +#define SIUL2_PC09_MSCR UL(0x4009C2E4) +#define SIUL2_PC10_MSCR UL(0x4009C2E8) +#define SIUL2_PC10_LIN0_IMCR UL(0x4009CA40) + +#define LIN0_TX_MSCR_CFG U(0x00214001) +#define LIN0_RX_MSCR_CFG U(0x00094000) +#define LIN0_RX_IMCR_CFG U(0x00000002) + +struct bl_load_info *plat_get_bl_image_load_info(void) +{ + return get_bl_load_info_from_mem_params_desc(); +} + +struct bl_params *plat_get_next_bl_params(void) +{ + return get_next_bl_params_from_mem_params_desc(); +} + +void plat_flush_next_bl_params(void) +{ + flush_bl_params_desc(); +} + +void bl2_platform_setup(void) +{ +} + +static void linflex_config_pinctrl(void) +{ + /* set PC09 - MSCR[41] - for UART0 TXD */ + mmio_write_32(SIUL2_PC09_MSCR, LIN0_TX_MSCR_CFG); + /* set PC10 - MSCR[42] - for UART0 RXD */ + mmio_write_32(SIUL2_PC10_MSCR, LIN0_RX_MSCR_CFG); + /* set PC10 - MSCR[512]/IMCR[0] - for UART0 RXD */ + mmio_write_32(SIUL2_PC10_LIN0_IMCR, LIN0_RX_IMCR_CFG); +} + +void bl2_el3_early_platform_setup(u_register_t arg0, u_register_t arg1, + u_register_t arg2, u_register_t arg3) +{ + int ret; + + ret = s32cc_init_early_clks(); + if (ret != 0) { + panic(); + } + + linflex_config_pinctrl(); + console_s32g2_register(); + + /* Restore (clear) the CAIUTC[IsolEn] bit for the primary cluster, which + * we have manually set during early BL2 boot. + */ + ncore_disable_caiu_isolation(A53_CLUSTER0_CAIU); + + ncore_init(); + ncore_caiu_online(A53_CLUSTER0_CAIU); + + plat_s32g2_io_setup(); +} + +void bl2_el3_plat_arch_setup(void) +{ +} + diff --git a/plat/nxp/s32/s32g274ardb2/plat_bl2_image_desc.c b/plat/nxp/s32/s32g274ardb2/plat_bl2_image_desc.c new file mode 100644 index 00000000..1fc77941 --- /dev/null +++ b/plat/nxp/s32/s32g274ardb2/plat_bl2_image_desc.c @@ -0,0 +1,41 @@ +/* + * Copyright 2024 NXP + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <common/desc_image_load.h> +#include <plat/common/platform.h> + +static bl_mem_params_node_t bl2_mem_params_descs[] = { + { + .image_id = BL31_IMAGE_ID, + + SET_STATIC_PARAM_HEAD(ep_info, PARAM_EP, VERSION_2, + entry_point_info_t, + SECURE | EXECUTABLE | EP_FIRST_EXE), + .ep_info.spsr = SPSR_64(MODE_EL3, MODE_SP_ELX, + DISABLE_ALL_EXCEPTIONS), + .ep_info.pc = BL31_BASE, + + SET_STATIC_PARAM_HEAD(image_info, PARAM_EP, VERSION_2, + image_info_t, IMAGE_ATTRIB_PLAT_SETUP), + .image_info.image_max_size = BL31_LIMIT - BL31_BASE, + .image_info.image_base = BL31_BASE, + .next_handoff_image_id = BL33_IMAGE_ID, + }, + { + .image_id = BL33_IMAGE_ID, + + SET_STATIC_PARAM_HEAD(ep_info, PARAM_EP, VERSION_2, + entry_point_info_t, + NON_SECURE | EXECUTABLE), + SET_STATIC_PARAM_HEAD(image_info, PARAM_EP, VERSION_2, + image_info_t, 0), + .image_info.image_max_size = BL33_LIMIT - BL33_BASE, + .image_info.image_base = BL33_BASE, + .next_handoff_image_id = INVALID_IMAGE_ID, + }, +}; + +REGISTER_BL_IMAGE_DESCS(bl2_mem_params_descs) diff --git a/plat/nxp/s32/s32g274ardb2/plat_bl31_setup.c b/plat/nxp/s32/s32g274ardb2/plat_bl31_setup.c new file mode 100644 index 00000000..03bf35cc --- /dev/null +++ b/plat/nxp/s32/s32g274ardb2/plat_bl31_setup.c @@ -0,0 +1,75 @@ +/* + * Copyright 2024 NXP + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <drivers/arm/gicv3.h> +#include <plat/common/platform.h> +#include <plat_console.h> + +static entry_point_info_t bl33_image_ep_info; + +static unsigned int s32g2_mpidr_to_core_pos(unsigned long mpidr); + +static uint32_t get_spsr_for_bl33_entry(void) +{ + unsigned long mode = MODE_EL1; + uint32_t spsr; + + spsr = SPSR_64(mode, MODE_SP_ELX, DISABLE_ALL_EXCEPTIONS); + + return spsr; +} + +void bl31_early_platform_setup2(u_register_t arg0, u_register_t arg1, + u_register_t arg2, u_register_t arg3) +{ + console_s32g2_register(); + + SET_PARAM_HEAD(&bl33_image_ep_info, PARAM_EP, VERSION_1, 0); + bl33_image_ep_info.pc = BL33_BASE; + bl33_image_ep_info.spsr = get_spsr_for_bl33_entry(); + SET_SECURITY_STATE(bl33_image_ep_info.h.attr, NON_SECURE); +} + +void bl31_plat_arch_setup(void) +{ +} + +struct entry_point_info *bl31_plat_get_next_image_ep_info(uint32_t type) +{ + return &bl33_image_ep_info; +} + +void bl31_platform_setup(void) +{ + static uintptr_t rdistif_base_addrs[PLATFORM_CORE_COUNT]; + static gicv3_driver_data_t plat_gic_data = { + .gicd_base = PLAT_GICD_BASE, + .gicr_base = PLAT_GICR_BASE, + .rdistif_num = PLATFORM_CORE_COUNT, + .rdistif_base_addrs = rdistif_base_addrs, + .mpidr_to_core_pos = s32g2_mpidr_to_core_pos, + }; + + unsigned int pos = plat_my_core_pos(); + + gicv3_driver_init(&plat_gic_data); + gicv3_distif_init(); + gicv3_rdistif_init(pos); + gicv3_cpuif_enable(pos); +} + +static unsigned int s32g2_mpidr_to_core_pos(unsigned long mpidr) +{ + int core; + + core = plat_core_pos_by_mpidr(mpidr); + if (core < 0) { + return 0; + } + + return (unsigned int)core; +} + diff --git a/plat/nxp/s32/s32g274ardb2/plat_console.c b/plat/nxp/s32/s32g274ardb2/plat_console.c new file mode 100644 index 00000000..542fa7be --- /dev/null +++ b/plat/nxp/s32/s32g274ardb2/plat_console.c @@ -0,0 +1,29 @@ +/* + * Copyright 2024 NXP + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <common/debug.h> +#include <linflex.h> +#include <plat_console.h> +#include <platform_def.h> + +void console_s32g2_register(void) +{ + static console_t s32g2_console = { + .next = NULL, + .flags = 0u, + }; + int ret; + + ret = console_linflex_register(UART_BASE, UART_CLOCK_HZ, + UART_BAUDRATE, &s32g2_console); + if (ret == 0) { + panic(); + } + + console_set_scope(&s32g2_console, + CONSOLE_FLAG_BOOT | CONSOLE_FLAG_CRASH | + CONSOLE_FLAG_TRANSLATE_CRLF); +} diff --git a/plat/nxp/s32/s32g274ardb2/plat_helpers.S b/plat/nxp/s32/s32g274ardb2/plat_helpers.S new file mode 100644 index 00000000..71219004 --- /dev/null +++ b/plat/nxp/s32/s32g274ardb2/plat_helpers.S @@ -0,0 +1,129 @@ +/* + * Copyright 2024 NXP + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <asm_macros.S> +#include <platform_def.h> +#include <s32cc-ncore.h> + +.globl plat_crash_console_flush +.globl plat_crash_console_init +.globl plat_crash_console_putc +.globl plat_is_my_cpu_primary +.globl plat_my_core_pos +.globl plat_reset_handler +.globl plat_secondary_cold_boot_setup +.globl platform_mem_init +.globl s32g2_core_pos_by_mpidr + +/* int plat_crash_console_init(void); */ +func plat_crash_console_init + mov_imm x0, UART_BASE + mov_imm x1, UART_CLOCK_HZ + mov_imm x2, UART_BAUDRATE + b console_linflex_core_init +endfunc plat_crash_console_init + +/* int plat_crash_console_putc(int); */ +func plat_crash_console_putc + mov_imm x1, UART_BASE + b console_linflex_core_putc + ret +endfunc plat_crash_console_putc + +/* void plat_crash_console_flush(void); */ +func plat_crash_console_flush + mov_imm x0, UART_BASE + b console_linflex_core_flush + ret +endfunc plat_crash_console_flush + +/** + * unsigned int s32g2_core_pos_by_mpidr(u_register_t mpidr); + * + * In: x0 - MPIDR_EL1 + * Out: x0 + * Clobber list: x0, x1 + */ +func s32g2_core_pos_by_mpidr + and x1, x0, #MPIDR_CPU_MASK + and x0, x0, #MPIDR_CLUSTER_MASK + lsr x0, x0, #MPIDR_AFF1_SHIFT + add x0, x1, x0, lsl #PLATFORM_MPIDR_CPU_MASK_BITS + ret +endfunc s32g2_core_pos_by_mpidr + +/** + * unsigned int plat_my_core_pos(void); + * + * Out: x0 + * Clobber list: x0, x1, x8 + */ +func plat_my_core_pos + mov x8, x30 + mrs x0, mpidr_el1 + bl s32g2_core_pos_by_mpidr + mov x30, x8 + ret +endfunc plat_my_core_pos + +/** + * unsigned int plat_is_my_cpu_primary(void); + * + * Clobber list: x0, x1, x7, x8 + */ +func plat_is_my_cpu_primary + mov x7, x30 + bl plat_my_core_pos + cmp x0, #PLATFORM_PRIMARY_CPU + cset x0, eq + mov x30, x7 + ret +endfunc plat_is_my_cpu_primary + + +/** + * void plat_secondary_cold_boot_setup (void); + */ +func plat_secondary_cold_boot_setup + ret +endfunc plat_secondary_cold_boot_setup + +/** + * void plat_reset_handler(void); + * + * Set the CAIUTC[IsolEn] bit for the primary A53 cluster. + * This is so cache invalidate operations from the early TF-A boot code + * won't cause Ncore to crash. + * + * Clobber list: x0, x1, x2 + */ +func plat_reset_handler + mov x0, #NCORE_CAIU0_BASE_ADDR + ldr w1, [x0, #NCORE_CAIUTC_OFF] + movz w2, #1 + lsl w2, w2, #NCORE_CAIUTC_ISOLEN_SHIFT + orr w1, w1, w2 + str w1, [x0, #NCORE_CAIUTC_OFF] + ret +endfunc plat_reset_handler + +/* void platform_mem_init(void); */ +func platform_mem_init + mov x10, x30 + mov x0, #BL31_BASE + mov x1, #(BL31_LIMIT & 0xFFFFU) + movk x1, #(BL31_LIMIT >> 16), lsl #16 + sub x1, x1, x0 + bl zeromem + mov x0, #BL33_BASE + mov x1, #(BL33_LIMIT & 0xFFFFU) + movk x1, #(BL33_LIMIT >> 16), lsl #16 + sub x1, x1, x0 + bl zeromem + mov x30, x10 + ret +endfunc platform_mem_init + diff --git a/plat/nxp/s32/s32g274ardb2/plat_io_storage.c b/plat/nxp/s32/s32g274ardb2/plat_io_storage.c new file mode 100644 index 00000000..db6bcc51 --- /dev/null +++ b/plat/nxp/s32/s32g274ardb2/plat_io_storage.c @@ -0,0 +1,135 @@ +/* + * Copyright 2024 NXP + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <assert.h> + +#include <drivers/io/io_driver.h> +#include <drivers/io/io_fip.h> +#include <drivers/io/io_memmap.h> +#include <plat/common/platform.h> +#include <tools_share/firmware_image_package.h> + +#include <plat_io_storage.h> + +struct plat_io_policy { + uintptr_t *dev_handle; + uintptr_t image_spec; + int (*check)(const uintptr_t spec); +}; + +static int open_memmap(const uintptr_t spec); +static int open_fip(const uintptr_t spec); + +static uintptr_t fip_dev_handle; + +static uintptr_t memmap_dev_handle; + +static int open_memmap(const uintptr_t spec) +{ + uintptr_t temp_handle = 0U; + int result; + + result = io_dev_init(memmap_dev_handle, (uintptr_t)0); + if (result != 0) { + return result; + } + + result = io_open(memmap_dev_handle, spec, &temp_handle); + if (result == 0) { + (void)io_close(temp_handle); + } + + return result; +} + +static int open_fip(const uintptr_t spec) +{ + uintptr_t temp_handle = 0U; + int result; + + /* See if a Firmware Image Package is available */ + result = io_dev_init(fip_dev_handle, (uintptr_t)FIP_IMAGE_ID); + if (result != 0) { + return result; + } + + result = io_open(fip_dev_handle, spec, &temp_handle); + if (result == 0) { + (void)io_close(temp_handle); + } + + return result; +} + +void plat_s32g2_io_setup(void) +{ + static const io_dev_connector_t *memmap_dev_con; + static const io_dev_connector_t *fip_dev_con; + + int result __unused; + + result = register_io_dev_memmap(&memmap_dev_con); + assert(result == 0); + + result = io_dev_open(memmap_dev_con, (uintptr_t)0, + &memmap_dev_handle); + assert(result == 0); + + result = register_io_dev_fip(&fip_dev_con); + assert(result == 0); + + result = io_dev_open(fip_dev_con, (uintptr_t)0, + &fip_dev_handle); + assert(result == 0); +} + +int plat_get_image_source(unsigned int image_id, uintptr_t *dev_handle, + uintptr_t *image_spec) +{ + static const io_block_spec_t fip_block_spec = { + .offset = S32G_FIP_BASE, + .length = S32G_FIP_SIZE, + }; + + static const io_uuid_spec_t bl31_uuid_spec = { + .uuid = UUID_EL3_RUNTIME_FIRMWARE_BL31, + }; + + static const io_uuid_spec_t bl33_uuid_spec = { + .uuid = UUID_NON_TRUSTED_FIRMWARE_BL33, + }; + + static const struct plat_io_policy policies[BL33_IMAGE_ID + 1] = { + [FIP_IMAGE_ID] = { + .dev_handle = &memmap_dev_handle, + .image_spec = (uintptr_t)&fip_block_spec, + .check = open_memmap, + }, + [BL31_IMAGE_ID] = { + .dev_handle = &fip_dev_handle, + .image_spec = (uintptr_t)&bl31_uuid_spec, + .check = open_fip, + }, + [BL33_IMAGE_ID] = { + .dev_handle = &fip_dev_handle, + .image_spec = (uintptr_t)&bl33_uuid_spec, + .check = open_fip, + }, + }; + const struct plat_io_policy *policy; + int result; + + assert(image_id < ARRAY_SIZE(policies)); + + policy = &policies[image_id]; + result = policy->check(policy->image_spec); + assert(result == 0); + + *image_spec = policy->image_spec; + *dev_handle = *policy->dev_handle; + + return result; +} diff --git a/plat/nxp/s32/s32g274ardb2/platform.mk b/plat/nxp/s32/s32g274ardb2/platform.mk new file mode 100644 index 00000000..7d6e960a --- /dev/null +++ b/plat/nxp/s32/s32g274ardb2/platform.mk @@ -0,0 +1,76 @@ +# +# Copyright 2024 NXP +# +# SPDX-License-Identifier: BSD-3-Clause +# + +include drivers/arm/gic/v3/gicv3.mk +include lib/xlat_tables_v2/xlat_tables.mk + +PLAT_DRIVERS_PATH := drivers/nxp +PLAT_COMMON_PATH := plat/nxp/common +PLAT_S32G274ARDB2 := plat/nxp/s32/s32g274ardb2 + +CONSOLE := LINFLEX + +include ${PLAT_COMMON_PATH}/plat_make_helper/plat_build_macros.mk + +# Flag to apply S32 erratum ERR051700. This erratum applies to all S32 +# revisions. +S32_ERRATA_LIST += ERRATA_S32_051700 + +PLAT_INCLUDES = \ + -I${PLAT_S32G274ARDB2}/include + +PROGRAMMABLE_RESET_ADDRESS := 1 + +COLD_BOOT_SINGLE_CPU := 0 + +ENABLE_SVE_FOR_NS := 0 + +RESET_TO_BL2 := 1 + +INIT_UNUSED_NS_EL2 := 1 + +ERRATA_A53_855873 := 1 +ERRATA_A53_836870 := 1 +ERRATA_A53_1530924 := 1 +ERRATA_SPECULATIVE_AT := 1 +ERRATA_S32_051700 := 1 + +# Selecting Drivers for SoC +$(eval $(call SET_NXP_MAKE_FLAG,CONSOLE_NEEDED,BL_COMM)) +$(eval $(call SET_NXP_MAKE_FLAG,CLK_NEEDED,BL_COMM)) + +include ${PLAT_DRIVERS_PATH}/drivers.mk + +BL_COMMON_SOURCES += \ + ${PLAT_S32G274ARDB2}/plat_console.c \ + ${PLAT_S32G274ARDB2}/plat_helpers.S \ + +BL2_SOURCES += \ + ${BL_COMMON_SOURCES} \ + ${PLAT_S32G274ARDB2}/plat_bl2_el3_setup.c \ + ${PLAT_S32G274ARDB2}/plat_bl2_image_desc.c \ + ${PLAT_S32G274ARDB2}/plat_io_storage.c \ + ${PLAT_S32G274ARDB2}/s32cc_ncore.c \ + common/desc_image_load.c \ + drivers/io/io_fip.c \ + drivers/io/io_memmap.c \ + drivers/io/io_storage.c \ + lib/cpus/aarch64/cortex_a53.S \ + +BL31_SOURCES += \ + ${GICV3_SOURCES} \ + ${PLAT_S32G274ARDB2}/plat_bl31_setup.c \ + ${PLAT_S32G274ARDB2}/s32g2_psci.c \ + ${PLAT_S32G274ARDB2}/s32g2_soc.c \ + ${XLAT_TABLES_LIB_SRCS} \ + lib/cpus/aarch64/cortex_a53.S \ + plat/common/plat_gicv3.c \ + plat/common/plat_psci_common.c \ + +# process all errata flags +$(eval $(call default_zeros, $(S32_ERRATA_LIST))) +$(eval $(call add_defines, $(S32_ERRATA_LIST))) +$(eval $(call assert_booleans, $(S32_ERRATA_LIST))) diff --git a/plat/nxp/s32/s32g274ardb2/s32cc_ncore.c b/plat/nxp/s32/s32g274ardb2/s32cc_ncore.c new file mode 100644 index 00000000..aa60ac46 --- /dev/null +++ b/plat/nxp/s32/s32g274ardb2/s32cc_ncore.c @@ -0,0 +1,99 @@ +/* + * Copyright 2019-2021, 2024 NXP + * + * SPDX-License-Identifier: BSD-3-Clause + */ +#include <lib/mmio.h> +#include <platform_def.h> + +#include <s32cc-ncore.h> + +static void ncore_diru_online(uint32_t diru) +{ + uint32_t numsfs, sf; + + numsfs = NCORE_CSID_NUMSFS(mmio_read_32(NCORE_CSID)) + 1U; + + /* Initialize all entries maintenance operation for each snoop filter */ + for (sf = 0U; sf < numsfs; sf++) { + mmio_write_32(NCORE_DIRUSFMC(diru), NCORE_DIRUSFMC_SFID(sf) | + NCORE_DIRUSFMC_SFMNTOP_ALL); + + while ((mmio_read_32(NCORE_DIRUSFMA(diru)) & NCORE_DIRUSFMA_MNTOPACTV) != 0U) { + } + + mmio_setbits_32(NCORE_DIRUSFE(diru), NCORE_DIRUSFE_SFEN(sf)); + } +} + +void ncore_disable_caiu_isolation(uint32_t caiu) +{ + /* Exit from low-power state */ + mmio_clrbits_32(NCORE_CAIUTC(caiu), NCORE_CAIUTC_ISOLEN_MASK); +} + +static void set_caiu(uint32_t caiu, bool on) +{ + uint32_t dirucase, csadser, caiuidr; + uint32_t numdirus, diru; + + /* Enable or disable snoop messages to the CAI for each DIRU */ + numdirus = NCORE_CSUID_NUMDIRUS(mmio_read_32(NCORE_CSUID)); + for (diru = 0; diru < numdirus; diru++) { + dirucase = mmio_read_32(NCORE_DIRUCASE(diru)); + + if (on) { + dirucase |= NCORE_DIRUCASE_CASNPEN(caiu); + } else { + dirucase &= ~NCORE_DIRUCASE_CASNPEN(caiu); + } + + mmio_write_32(NCORE_DIRUCASE(diru), dirucase); + } + + /* Enable or disable DVM messages to the CAI */ + caiuidr = mmio_read_32(NCORE_CAIUID(caiu)); + if ((caiuidr & NCORE_CAIUID_TYPE) == NCORE_CAIUID_TYPE_ACE_DVM) { + csadser = mmio_read_32(NCORE_CSADSE); + + if (on) { + csadser |= NCORE_CSADSE_DVMSNPEN(caiu); + } else { + csadser &= ~NCORE_CSADSE_DVMSNPEN(caiu); + } + + mmio_write_32(NCORE_CSADSE, csadser); + } +} + +void ncore_caiu_online(uint32_t caiu) +{ + set_caiu(caiu, true); +} + +void ncore_caiu_offline(uint32_t caiu) +{ + set_caiu(caiu, false); +} + +bool ncore_is_caiu_online(uint32_t caiu) +{ + uint32_t stat = mmio_read_32(NCORE_CSADSE); + + return ((stat & NCORE_CSADSE_DVMSNPEN(caiu)) != 0U); +} + +void ncore_init(void) +{ + uint32_t csuidr = mmio_read_32(NCORE_CSUID); + uint32_t numdirus, diru; + + numdirus = NCORE_CSUID_NUMDIRUS(csuidr); + for (diru = 0U; diru < numdirus; diru++) { + /** + * Transition the directory to an online state by ensuring that + * all DIRUs within the interface are operational. + */ + ncore_diru_online(diru); + } +} diff --git a/plat/nxp/s32/s32g274ardb2/s32g2_psci.c b/plat/nxp/s32/s32g274ardb2/s32g2_psci.c new file mode 100644 index 00000000..2d02d94b --- /dev/null +++ b/plat/nxp/s32/s32g274ardb2/s32g2_psci.c @@ -0,0 +1,20 @@ +/* + * Copyright 2024 NXP + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <lib/psci/psci.h> +#include <plat/common/platform.h> + +int plat_setup_psci_ops(uintptr_t sec_entrypoint, + const plat_psci_ops_t **psci_ops) +{ + static const plat_psci_ops_t s32g2_psci_ops = { + }; + + *psci_ops = &s32g2_psci_ops; + + return 0; +} + diff --git a/plat/nxp/s32/s32g274ardb2/s32g2_soc.c b/plat/nxp/s32/s32g274ardb2/s32g2_soc.c new file mode 100644 index 00000000..00013523 --- /dev/null +++ b/plat/nxp/s32/s32g274ardb2/s32g2_soc.c @@ -0,0 +1,52 @@ +/* + * Copyright 2024 NXP + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <plat/common/platform.h> +#include <plat_helpers.h> + +const unsigned char *plat_get_power_domain_tree_desc(void) +{ + static const unsigned char s32g_power_domain_tree_desc[] = { + PLATFORM_SYSTEM_COUNT, + PLATFORM_CLUSTER_COUNT, + PLATFORM_CORE_COUNT / U(2), + PLATFORM_CORE_COUNT / U(2), + }; + + return s32g_power_domain_tree_desc; +} + +int plat_core_pos_by_mpidr(u_register_t mpidr) +{ + unsigned int cluster_id, cpu_id, core_id; + u_register_t mpidr_priv = mpidr; + + mpidr_priv &= MPIDR_AFFINITY_MASK; + + if ((mpidr_priv & ~(MPIDR_CLUSTER_MASK | MPIDR_CPU_MASK)) != 0) { + return -1; + } + + cluster_id = MPIDR_AFFLVL1_VAL(mpidr_priv); + cpu_id = MPIDR_AFFLVL0_VAL(mpidr_priv); + + if ((cluster_id >= PLATFORM_CLUSTER_COUNT) || + (cpu_id >= PLATFORM_MAX_CPUS_PER_CLUSTER)) { + return -1; + } + + core_id = s32g2_core_pos_by_mpidr(mpidr_priv); + if (core_id >= PLATFORM_CORE_COUNT) { + return -1; + } + + return (int)core_id; +} + +unsigned int plat_get_syscnt_freq2(void) +{ + return COUNTER_FREQUENCY; +} diff --git a/plat/nxp/soc-lx2160a/ddr_fip.mk b/plat/nxp/soc-lx2160a/ddr_fip.mk index f14a9e86..c303cedb 100644 --- a/plat/nxp/soc-lx2160a/ddr_fip.mk +++ b/plat/nxp/soc-lx2160a/ddr_fip.mk @@ -38,8 +38,6 @@ ifeq (${DDR_DMEM_RDIMM_2D},) DDR_DMEM_RDIMM_2D := ${DDR_PHY_BIN_PATH}/ddr4_rdimm2d_pmu_train_dmem.bin endif -$(shell mkdir -p '${BUILD_PLAT}') - ifeq (${DDR_FIP_NAME},) ifeq (${TRUSTED_BOARD_BOOT},1) DDR_FIP_NAME := ddr_fip_sec.bin @@ -73,11 +71,11 @@ CRTTOOL ?= ${CRTTOOLPATH}/cert_create${BIN_EXT} ifneq (${GENERATE_COT},0) ddr_certificates: ${DDR_CRT_DEPS} ${CRTTOOL} - ${Q}${CRTTOOL} ${DDR_CRT_ARGS} - @${ECHO_BLANK_LINE} - @echo "Built $@ successfully" - @echo "DDR certificates can be found in ${BUILD_PLAT}" - @${ECHO_BLANK_LINE} + $(q)${CRTTOOL} ${DDR_CRT_ARGS} + $(s)echo + $(s)echo "Built $@ successfully" + $(s)echo "DDR certificates can be found in ${BUILD_PLAT}" + $(s)echo endif endif endif @@ -88,10 +86,10 @@ FIPTOOL ?= ${FIPTOOLPATH}/fiptool${BIN_EXT} ${BUILD_PLAT}/${DDR_FIP_NAME}: ${DDR_FIP_DEPS} ${FIPTOOL} $(eval ${CHECK_DDR_FIP_CMD}) - ${Q}${FIPTOOL} create ${DDR_FIP_ARGS} $@ - ${Q}${FIPTOOL} info $@ - @${ECHO_BLANK_LINE} - @echo "Built $@ successfully" - @${ECHO_BLANK_LINE} + $(q)${FIPTOOL} create ${DDR_FIP_ARGS} $@ + $(q)${FIPTOOL} info $@ + $(s)echo + $(s)echo "Built $@ successfully" + $(s)echo fip_ddr: ${BUILD_PLAT}/${DDR_FIP_NAME} diff --git a/plat/nxp/soc-lx2160a/ddr_sb.mk b/plat/nxp/soc-lx2160a/ddr_sb.mk index c11651e3..119fc089 100644 --- a/plat/nxp/soc-lx2160a/ddr_sb.mk +++ b/plat/nxp/soc-lx2160a/ddr_sb.mk @@ -36,7 +36,7 @@ DDR_INPUT_FILE:= drivers/nxp/auth/csf_hdr_parser/${CSF_FILE} endif %.sb: % - @echo " Generating CSF Header for $@ $<" + $(s)echo " Generating CSF Header for $@ $<" $(CST_DIR)/create_hdr_esbc --in $< --out $@ --app_off ${CSF_HDR_SZ} \ --app $< ${DDR_INPUT_FILE} diff --git a/plat/nxp/soc-lx2160a/ddr_tbbr.mk b/plat/nxp/soc-lx2160a/ddr_tbbr.mk index deb475b0..836a431f 100644 --- a/plat/nxp/soc-lx2160a/ddr_tbbr.mk +++ b/plat/nxp/soc-lx2160a/ddr_tbbr.mk @@ -39,8 +39,6 @@ NTFW_NVCTR_VAL ?= 0 # Pass the non-volatile counters to the cert_create tool $(eval $(call CERT_ADD_CMD_OPT,${TFW_NVCTR_VAL},--tfw-nvctr,DDR_)) -$(shell mkdir -p '${BUILD_PLAT}') - ifeq (${DDR_KEY},) DDR_KEY=${BUILD_PLAT}/ddr.pem endif diff --git a/plat/qemu/common/common.mk b/plat/qemu/common/common.mk index 2dcac69b..ed95bc67 100644 --- a/plat/qemu/common/common.mk +++ b/plat/qemu/common/common.mk @@ -1,5 +1,5 @@ # -# Copyright (c) 2023, Linaro Limited and Contributors. All rights reserved. +# Copyright (c) 2023-2024, Linaro Limited and Contributors. All rights reserved. # # SPDX-License-Identifier: BSD-3-Clause # @@ -88,8 +88,8 @@ ifeq (${ARCH},aarch64) # # We go v8.0 by default and will enable all features we want -ARM_ARCH_MAJOR := 8 -ARM_ARCH_MINOR := 0 +ARM_ARCH_MAJOR ?= 8 +ARM_ARCH_MINOR ?= 0 # 8.0 ENABLE_FEAT_CSV2_2 := 2 @@ -106,12 +106,16 @@ ENABLE_FEAT_RAS := 0 # 8.4 ENABLE_FEAT_SEL2 := 2 ENABLE_FEAT_DIT := 2 +ENABLE_TRF_FOR_NS := 2 # 8.5 ENABLE_FEAT_RNG := 2 -ENABLE_FEAT_SB := 2 +# TF-A currently does not do dynamic detection of FEAT_SB. +# Compiler puts SB instruction when it is enabled. +ENABLE_FEAT_SB := 0 # 8.6 +ENABLE_FEAT_ECV := 2 ENABLE_FEAT_FGT := 2 # 8.7 @@ -126,6 +130,11 @@ else ENABLE_SME_FOR_NS := 2 endif +ifeq (${ENABLE_RME},1) +BL31_SOURCES += plat/qemu/common/qemu_plat_attest_token.c \ + plat/qemu/common/qemu_realm_attest_key.c +endif + # Treating this as a memory-constrained port for now USE_COHERENT_MEM := 0 diff --git a/plat/qemu/common/qemu_bl2_mem_params_desc.c b/plat/qemu/common/qemu_bl2_mem_params_desc.c index bb1797d1..c444be41 100644 --- a/plat/qemu/common/qemu_bl2_mem_params_desc.c +++ b/plat/qemu/common/qemu_bl2_mem_params_desc.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2022, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2017-2024, Arm Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -67,11 +67,28 @@ static bl_mem_params_node_t bl2_mem_params_descs[] = { # ifdef QEMU_LOAD_BL32 .next_handoff_image_id = BL32_IMAGE_ID, +# elif ENABLE_RME + .next_handoff_image_id = RMM_IMAGE_ID, # else .next_handoff_image_id = BL33_IMAGE_ID, # endif }, #endif /* __aarch64__ */ + +#if ENABLE_RME + /* Fill RMM related information */ + { .image_id = RMM_IMAGE_ID, + SET_STATIC_PARAM_HEAD(ep_info, PARAM_EP, + VERSION_2, entry_point_info_t, EP_REALM | EXECUTABLE), + .ep_info.pc = RMM_BASE, + SET_STATIC_PARAM_HEAD(image_info, PARAM_EP, + VERSION_2, image_info_t, 0), + .image_info.image_base = RMM_BASE, + .image_info.image_max_size = RMM_LIMIT - RMM_BASE, + .next_handoff_image_id = BL33_IMAGE_ID, + }, +#endif /* ENABLE_RME */ + # ifdef QEMU_LOAD_BL32 #ifdef __aarch64__ @@ -95,7 +112,11 @@ static bl_mem_params_node_t bl2_mem_params_descs[] = { .image_info.image_base = BL32_BASE, .image_info.image_max_size = BL32_LIMIT - BL32_BASE, +#if ENABLE_RME + .next_handoff_image_id = RMM_IMAGE_ID, +#else .next_handoff_image_id = BL33_IMAGE_ID, +#endif }, /* diff --git a/plat/qemu/common/qemu_bl2_setup.c b/plat/qemu/common/qemu_bl2_setup.c index 231f23a7..c96e4b95 100644 --- a/plat/qemu/common/qemu_bl2_setup.c +++ b/plat/qemu/common/qemu_bl2_setup.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015-2023, Arm Limited and Contributors. All rights reserved. + * Copyright (c) 2015-2024, Arm Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -11,6 +11,7 @@ #include <platform_def.h> +#include <arch_features.h> #include <arch_helpers.h> #include <common/bl_common.h> #include <common/debug.h> @@ -18,42 +19,41 @@ #include <common/fdt_fixup.h> #include <common/fdt_wrappers.h> #include <lib/optee_utils.h> -#if TRANSFER_LIST #include <lib/transfer_list.h> -#endif #include <lib/utils.h> #include <plat/common/platform.h> +#if ENABLE_RME +#include <qemu_pas_def.h> +#endif #include "qemu_private.h" #define MAP_BL2_TOTAL MAP_REGION_FLAT( \ bl2_tzram_layout.total_base, \ bl2_tzram_layout.total_size, \ - MT_MEMORY | MT_RW | MT_SECURE) + MT_MEMORY | MT_RW | EL3_PAS) #define MAP_BL2_RO MAP_REGION_FLAT( \ BL_CODE_BASE, \ BL_CODE_END - BL_CODE_BASE, \ - MT_CODE | MT_SECURE), \ + MT_CODE | EL3_PAS), \ MAP_REGION_FLAT( \ BL_RO_DATA_BASE, \ BL_RO_DATA_END \ - BL_RO_DATA_BASE, \ - MT_RO_DATA | MT_SECURE) + MT_RO_DATA | EL3_PAS) #if USE_COHERENT_MEM #define MAP_BL_COHERENT_RAM MAP_REGION_FLAT( \ BL_COHERENT_RAM_BASE, \ BL_COHERENT_RAM_END \ - BL_COHERENT_RAM_BASE, \ - MT_DEVICE | MT_RW | MT_SECURE) + MT_DEVICE | MT_RW | EL3_PAS) #endif /* Data structure which holds the extents of the trusted SRAM for BL2 */ static meminfo_t bl2_tzram_layout __aligned(CACHE_WRITEBACK_GRANULE); -#if TRANSFER_LIST static struct transfer_list_header *bl2_tl; -#endif void bl2_early_platform_setup2(u_register_t arg0, u_register_t arg1, u_register_t arg2, u_register_t arg3) @@ -101,12 +101,24 @@ static void update_dt(void) return; } +#if ENABLE_RME + if (fdt_add_reserved_memory(fdt, "rmm", REALM_DRAM_BASE, + REALM_DRAM_SIZE)) { + ERROR("Failed to reserve RMM memory in Device Tree\n"); + return; + } + + INFO("Reserved RMM memory [0x%lx, 0x%lx] in Device tree\n", + (uintptr_t)REALM_DRAM_BASE, + (uintptr_t)REALM_DRAM_BASE + REALM_DRAM_SIZE - 1); +#endif + ret = fdt_pack(fdt); if (ret < 0) ERROR("Failed to pack Device Tree at %p: error %d\n", fdt, ret); #if TRANSFER_LIST - // create a TE + /* create a TE */ te = transfer_list_add(bl2_tl, TL_TAG_FDT, fdt_totalsize(fdt), fdt); if (!te) { ERROR("Failed to add FDT entry to Transfer List\n"); @@ -138,6 +150,54 @@ void qemu_bl2_sync_transfer_list(void) #endif } +#if ENABLE_RME +static void bl2_plat_gpt_setup(void) +{ + /* + * The GPT library might modify the gpt regions structure to optimize + * the layout, so the array cannot be constant. + */ + pas_region_t pas_regions[] = { + QEMU_PAS_ROOT, + QEMU_PAS_SECURE, + QEMU_PAS_GPTS, + QEMU_PAS_NS0, + QEMU_PAS_REALM, + QEMU_PAS_NS1, + }; + + /* + * Initialize entire protected space to GPT_GPI_ANY. With each L0 entry + * covering 1GB (currently the only supported option), then covering + * 256TB of RAM (48-bit PA) would require a 2MB L0 region. At the + * moment we use a 8KB table, which covers 1TB of RAM (40-bit PA). + */ + if (gpt_init_l0_tables(GPCCR_PPS_1TB, PLAT_QEMU_L0_GPT_BASE, + PLAT_QEMU_L0_GPT_SIZE + + PLAT_QEMU_GPT_BITLOCK_SIZE) < 0) { + ERROR("gpt_init_l0_tables() failed!\n"); + panic(); + } + + /* Carve out defined PAS ranges. */ + if (gpt_init_pas_l1_tables(GPCCR_PGS_4K, + PLAT_QEMU_L1_GPT_BASE, + PLAT_QEMU_L1_GPT_SIZE, + pas_regions, + (unsigned int)(sizeof(pas_regions) / + sizeof(pas_region_t))) < 0) { + ERROR("gpt_init_pas_l1_tables() failed!\n"); + panic(); + } + + INFO("Enabling Granule Protection Checks\n"); + if (gpt_enable() < 0) { + ERROR("gpt_enable() failed!\n"); + panic(); + } +} +#endif + void bl2_plat_arch_setup(void) { const mmap_region_t bl_regions[] = { @@ -145,17 +205,32 @@ void bl2_plat_arch_setup(void) MAP_BL2_RO, #if USE_COHERENT_MEM MAP_BL_COHERENT_RAM, +#endif +#if ENABLE_RME + MAP_RMM_DRAM, + MAP_GPT_L0_REGION, + MAP_GPT_L1_REGION, #endif {0} }; setup_page_tables(bl_regions, plat_qemu_get_mmap()); +#if ENABLE_RME + /* BL2 runs in EL3 when RME enabled. */ + assert(is_feat_rme_present()); + enable_mmu_el3(0); + + /* Initialise and enable granule protection after MMU. */ + bl2_plat_gpt_setup(); +#else /* ENABLE_RME */ + #ifdef __aarch64__ enable_mmu_el1(0); #else enable_mmu_svc_mon(0); #endif +#endif /* ENABLE_RME */ } /******************************************************************************* @@ -243,6 +318,23 @@ static int load_sps_from_tb_fw_config(struct image_info *image_info) } #endif /*defined(SPD_spmd) && SPMD_SPM_AT_SEL2*/ +#if defined(SPD_opteed) || defined(AARCH32_SP_OPTEE) || defined(SPMC_OPTEE) +static int handoff_pageable_part(uint64_t pagable_part) +{ +#if TRANSFER_LIST + struct transfer_list_entry *te; + + te = transfer_list_add(bl2_tl, TL_TAG_OPTEE_PAGABLE_PART, + sizeof(pagable_part), &pagable_part); + if (!te) { + INFO("Cannot add TE for pageable part\n"); + return -1; + } +#endif + return 0; +} +#endif + static int qemu_bl2_handle_post_image_load(unsigned int image_id) { int err = 0; @@ -256,12 +348,32 @@ static int qemu_bl2_handle_post_image_load(unsigned int image_id) #endif #if TRANSFER_LIST struct transfer_list_header *ns_tl = NULL; - struct transfer_list_entry *te = NULL; #endif assert(bl_mem_params); switch (image_id) { +#if TRANSFER_LIST + case BL31_IMAGE_ID: + /* + * arg0 is a bl_params_t reserved for bl31_early_platform_setup2 + * we just need arg1 and arg3 for BL31 to update the TL from S + * to NS memory before it exits + */ +#ifdef __aarch64__ + if (GET_RW(bl_mem_params->ep_info.spsr) == MODE_RW_64) { + bl_mem_params->ep_info.args.arg1 = + TRANSFER_LIST_HANDOFF_X1_VALUE(REGISTER_CONVENTION_VERSION); + } else +#endif + { + bl_mem_params->ep_info.args.arg1 = + TRANSFER_LIST_HANDOFF_R1_VALUE(REGISTER_CONVENTION_VERSION); + } + + bl_mem_params->ep_info.args.arg3 = (uintptr_t)bl2_tl; + break; +#endif case BL32_IMAGE_ID: #if defined(SPD_opteed) || defined(AARCH32_SP_OPTEE) || defined(SPMC_OPTEE) pager_mem_params = get_bl_mem_params_node(BL32_EXTRA1_IMAGE_ID); @@ -276,8 +388,21 @@ static int qemu_bl2_handle_post_image_load(unsigned int image_id) if (err != 0) { WARN("OPTEE header parse error.\n"); } + + /* add TL_TAG_OPTEE_PAGABLE_PART entry to the TL */ + if (handoff_pageable_part(bl_mem_params->ep_info.args.arg1)) { + return -1; + } #endif + INFO("Handoff to BL32\n"); + bl_mem_params->ep_info.spsr = qemu_get_spsr_for_bl32_entry(); + if (TRANSFER_LIST && + transfer_list_set_handoff_args(bl2_tl, + &bl_mem_params->ep_info)) + break; + + INFO("Using default arguments\n"); #if defined(SPMC_OPTEE) /* * Explicit zeroes to unused registers since they may have @@ -301,7 +426,6 @@ static int qemu_bl2_handle_post_image_load(unsigned int image_id) bl_mem_params->ep_info.args.arg2 = ARM_PRELOADED_DTB_BASE; bl_mem_params->ep_info.args.arg3 = 0; #endif - bl_mem_params->ep_info.spsr = qemu_get_spsr_for_bl32_entry(); break; case BL33_IMAGE_ID: @@ -328,7 +452,7 @@ static int qemu_bl2_handle_post_image_load(unsigned int image_id) bl_mem_params->ep_info.args.arg3 = 0U; #elif TRANSFER_LIST if (bl2_tl) { - // relocate the tl to pre-allocate NS memory + /* relocate the tl to pre-allocate NS memory */ ns_tl = transfer_list_relocate(bl2_tl, (void *)(uintptr_t)FW_NS_HANDOFF_BASE, bl2_tl->max_size); @@ -337,37 +461,18 @@ static int qemu_bl2_handle_post_image_load(unsigned int image_id) (unsigned long)FW_NS_HANDOFF_BASE); return -1; } - NOTICE("Transfer list handoff to BL33\n"); - transfer_list_dump(ns_tl); - - te = transfer_list_find(ns_tl, TL_TAG_FDT); + } - bl_mem_params->ep_info.args.arg1 = - TRANSFER_LIST_SIGNATURE | - REGISTER_CONVENTION_VERSION_MASK; - bl_mem_params->ep_info.args.arg3 = (uintptr_t)ns_tl; - - if (GET_RW(bl_mem_params->ep_info.spsr) == MODE_RW_32) { - // aarch32 - bl_mem_params->ep_info.args.arg0 = 0; - bl_mem_params->ep_info.args.arg2 = te ? - (uintptr_t)transfer_list_entry_data(te) - : 0; - } else { - // aarch64 - bl_mem_params->ep_info.args.arg0 = te ? - (uintptr_t)transfer_list_entry_data(te) - : 0; - bl_mem_params->ep_info.args.arg2 = 0; - } - } else { - // Legacy handoff + INFO("Handoff to BL33\n"); + if (!transfer_list_set_handoff_args(ns_tl, + &bl_mem_params->ep_info)) { + INFO("Invalid TL, fallback to default arguments\n"); bl_mem_params->ep_info.args.arg0 = 0xffff & read_mpidr(); } #else /* BL33 expects to receive the primary CPU MPID (through r0) */ bl_mem_params->ep_info.args.arg0 = 0xffff & read_mpidr(); -#endif // ARM_LINUX_KERNEL_AS_BL33 +#endif /* ARM_LINUX_KERNEL_AS_BL33 */ break; #ifdef SPD_spmd diff --git a/plat/qemu/common/qemu_bl31_setup.c b/plat/qemu/common/qemu_bl31_setup.c index f309efdc..0a70cc29 100644 --- a/plat/qemu/common/qemu_bl31_setup.c +++ b/plat/qemu/common/qemu_bl31_setup.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015-2023, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2015-2024, Arm Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -8,6 +8,8 @@ #include <common/bl_common.h> #include <drivers/arm/pl061_gpio.h> +#include <lib/gpt_rme/gpt_rme.h> +#include <lib/transfer_list.h> #include <plat/common/platform.h> #include "qemu_private.h" @@ -40,6 +42,10 @@ */ static entry_point_info_t bl32_image_ep_info; static entry_point_info_t bl33_image_ep_info; +#if ENABLE_RME +static entry_point_info_t rmm_image_ep_info; +#endif +static struct transfer_list_header *bl31_tl; /******************************************************************************* * Perform any BL3-1 early platform setup. Here is an opportunity to copy @@ -72,13 +78,18 @@ void bl31_early_platform_setup2(u_register_t arg0, u_register_t arg1, bl_params_node_t *bl_params = params_from_bl2->head; /* - * Copy BL33 and BL32 (if present), entry point information. + * Copy BL33, BL32 and RMM (if present), entry point information. * They are stored in Secure RAM, in BL2's address space. */ while (bl_params) { if (bl_params->image_id == BL32_IMAGE_ID) bl32_image_ep_info = *bl_params->ep_info; +#if ENABLE_RME + if (bl_params->image_id == RMM_IMAGE_ID) + rmm_image_ep_info = *bl_params->ep_info; +#endif + if (bl_params->image_id == BL33_IMAGE_ID) bl33_image_ep_info = *bl_params->ep_info; @@ -87,6 +98,16 @@ void bl31_early_platform_setup2(u_register_t arg0, u_register_t arg1, if (!bl33_image_ep_info.pc) panic(); +#if ENABLE_RME + if (!rmm_image_ep_info.pc) + panic(); +#endif + + if (TRANSFER_LIST && arg1 == (TRANSFER_LIST_SIGNATURE | + REGISTER_CONVENTION_VERSION_MASK) && + transfer_list_check_header((void *)arg3) != TL_OPS_NON) { + bl31_tl = (void *)arg3; /* saved TL address from BL2 */ + } } void bl31_plat_arch_setup(void) @@ -96,6 +117,11 @@ void bl31_plat_arch_setup(void) MAP_BL31_RO, #if USE_COHERENT_MEM MAP_BL_COHERENT_RAM, +#endif +#if ENABLE_RME + MAP_GPT_L0_REGION, + MAP_GPT_L1_REGION, + MAP_RMM_SHARED_MEM, #endif {0} }; @@ -103,6 +129,20 @@ void bl31_plat_arch_setup(void) setup_page_tables(bl_regions, plat_qemu_get_mmap()); enable_mmu_el3(0); + +#if ENABLE_RME + /* + * Initialise Granule Protection library and enable GPC for the primary + * processor. The tables have already been initialized by a previous BL + * stage, so there is no need to provide any PAS here. This function + * sets up pointers to those tables. + */ + if (gpt_runtime_init() < 0) { + ERROR("gpt_runtime_init() failed!\n"); + panic(); + } +#endif /* ENABLE_RME */ + } static void qemu_gpio_init(void) @@ -121,7 +161,7 @@ void bl31_platform_setup(void) unsigned int plat_get_syscnt_freq2(void) { - return SYS_COUNTER_FREQ_IN_TICKS; + return read_cntfrq_el0(); } /******************************************************************************* @@ -135,8 +175,18 @@ entry_point_info_t *bl31_plat_get_next_image_ep_info(uint32_t type) entry_point_info_t *next_image_info; assert(sec_state_is_valid(type)); - next_image_info = (type == NON_SECURE) - ? &bl33_image_ep_info : &bl32_image_ep_info; + if (type == NON_SECURE) { + next_image_info = &bl33_image_ep_info; + } +#if ENABLE_RME + else if (type == REALM) { + next_image_info = &rmm_image_ep_info; + } +#endif + else { + next_image_info = &bl32_image_ep_info; + } + /* * None of the images on the ARM development platforms can have 0x0 * as the entrypoint @@ -146,3 +196,19 @@ entry_point_info_t *bl31_plat_get_next_image_ep_info(uint32_t type) else return NULL; } + +void bl31_plat_runtime_setup(void) +{ +#if TRANSFER_LIST + if (bl31_tl) { + /* + * update the TL from S to NS memory before jump to BL33 + * to reflect all changes in TL done by BL32 + */ + memcpy((void *)FW_NS_HANDOFF_BASE, bl31_tl, bl31_tl->max_size); + } +#endif + + console_flush(); + console_switch_state(CONSOLE_FLAG_RUNTIME); +} diff --git a/plat/qemu/common/qemu_common.c b/plat/qemu/common/qemu_common.c index d4488a4b..9ccb2c8a 100644 --- a/plat/qemu/common/qemu_common.c +++ b/plat/qemu/common/qemu_common.c @@ -1,55 +1,60 @@ /* - * Copyright (c) 2015-2023, Arm Limited and Contributors. All rights reserved. + * Copyright (c) 2015-2024, Arm Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ +#include <string.h> + #include <platform_def.h> #include <arch_helpers.h> #include <common/bl_common.h> #include <lib/xlat_tables/xlat_tables_v2.h> #include <services/el3_spmc_ffa_memory.h> +#if ENABLE_RME +#include <services/rmm_core_manifest.h> +#endif #include <plat/common/platform.h> #include "qemu_private.h" #define MAP_DEVICE0 MAP_REGION_FLAT(DEVICE0_BASE, \ DEVICE0_SIZE, \ - MT_DEVICE | MT_RW | MT_SECURE) + MT_DEVICE | MT_RW | EL3_PAS) #ifdef DEVICE1_BASE #define MAP_DEVICE1 MAP_REGION_FLAT(DEVICE1_BASE, \ DEVICE1_SIZE, \ - MT_DEVICE | MT_RW | MT_SECURE) + MT_DEVICE | MT_RW | EL3_PAS) #endif #ifdef DEVICE2_BASE #define MAP_DEVICE2 MAP_REGION_FLAT(DEVICE2_BASE, \ DEVICE2_SIZE, \ - MT_DEVICE | MT_RW | MT_SECURE) + MT_DEVICE | MT_RW | EL3_PAS) #endif #define MAP_SHARED_RAM MAP_REGION_FLAT(SHARED_RAM_BASE, \ SHARED_RAM_SIZE, \ - MT_DEVICE | MT_RW | MT_SECURE) + MT_DEVICE | MT_RW | EL3_PAS) #define MAP_BL32_MEM MAP_REGION_FLAT(BL32_MEM_BASE, BL32_MEM_SIZE, \ - MT_MEMORY | MT_RW | MT_SECURE) + MT_MEMORY | MT_RW | EL3_PAS) #define MAP_NS_DRAM0 MAP_REGION_FLAT(NS_DRAM0_BASE, NS_DRAM0_SIZE, \ MT_MEMORY | MT_RW | MT_NS) #define MAP_FLASH0 MAP_REGION_FLAT(QEMU_FLASH0_BASE, QEMU_FLASH0_SIZE, \ - MT_MEMORY | MT_RO | MT_SECURE) + MT_MEMORY | MT_RO | EL3_PAS) #define MAP_FLASH1 MAP_REGION_FLAT(QEMU_FLASH1_BASE, QEMU_FLASH1_SIZE, \ - MT_MEMORY | MT_RO | MT_SECURE) + MT_MEMORY | MT_RO | EL3_PAS) #ifdef FW_HANDOFF_BASE #define MAP_FW_HANDOFF MAP_REGION_FLAT(FW_HANDOFF_BASE, FW_HANDOFF_SIZE, \ - MT_MEMORY | MT_RW | MT_SECURE) + MT_MEMORY | MT_RW | EL3_PAS) #endif #ifdef FW_NS_HANDOFF_BASE #define MAP_FW_NS_HANDOFF MAP_REGION_FLAT(FW_NS_HANDOFF_BASE, FW_HANDOFF_SIZE, \ @@ -138,6 +143,19 @@ static const mmap_region_t plat_qemu_mmap[] = { }; #endif +#ifdef IMAGE_RMM +const mmap_region_t plat_qemu_mmap[] = { + MAP_DEVICE0, +#ifdef MAP_DEVICE1 + MAP_DEVICE1, +#endif +#ifdef MAP_DEVICE2 + MAP_DEVICE2, +#endif + {0} +}; +#endif + /******************************************************************************* * Returns QEMU platform specific memory map regions. ******************************************************************************/ @@ -160,7 +178,7 @@ int plat_get_mbedtls_heap(void **heap_addr, size_t *heap_size) */ #define PLAT_SPMC_SHMEM_DATASTORE_SIZE 64 * 1024 -uint8_t plat_spmc_shmem_datastore[PLAT_SPMC_SHMEM_DATASTORE_SIZE]; +uint8_t plat_spmc_shmem_datastore[PLAT_SPMC_SHMEM_DATASTORE_SIZE] __aligned(2 * sizeof(long)); int plat_spmc_shmem_datastore_get(uint8_t **datastore, size_t *size) { @@ -180,13 +198,137 @@ int plat_spmc_shmem_reclaim(struct ffa_mtd *desc) } #endif -#if defined(SPD_spmd) && (SPMC_AT_EL3 == 0) -/* - * A dummy implementation of the platform handler for Group0 secure interrupt. - */ +#if defined(SPD_spmd) int plat_spmd_handle_group0_interrupt(uint32_t intid) { + /* + * Currently, there are no sources of Group0 secure interrupt + * enabled for QEMU. + */ (void)intid; return -1; } -#endif /*defined(SPD_spmd) && (SPMC_AT_EL3 == 0)*/ +#endif /*defined(SPD_spmd)*/ + +#if ENABLE_RME +/* + * Get a pointer to the RMM-EL3 Shared buffer and return it + * through the pointer passed as parameter. + * + * This function returns the size of the shared buffer. + */ +size_t plat_rmmd_get_el3_rmm_shared_mem(uintptr_t *shared) +{ + *shared = (uintptr_t)RMM_SHARED_BASE; + + return (size_t)RMM_SHARED_SIZE; +} + +int plat_rmmd_load_manifest(struct rmm_manifest *manifest) +{ + uint64_t checksum; + uintptr_t base; + uint64_t size; + size_t num_banks = 1; + size_t num_consoles = 1; + struct ns_dram_bank *bank_ptr; + struct console_info *console_ptr; + + assert(manifest != NULL); + + manifest->version = RMMD_MANIFEST_VERSION; + manifest->padding = 0U; /* RES0 */ + manifest->plat_data = (uintptr_t)NULL; + manifest->plat_dram.num_banks = num_banks; + manifest->plat_console.num_consoles = num_consoles; + + /* + * Boot manifest structure illustration: + * + * +----------------------------------------+ + * | offset | field | comment | + * +----------+--------------+--------------+ + * | 0 | version | 0x00000003 | + * +----------+--------------+--------------+ + * | 4 | padding | 0x00000000 | + * +----------+--------------+--------------+ + * | 8 | plat_data | NULL | + * +----------+--------------+--------------+ + * | 16 | num_banks | | + * +----------+--------------+ | + * | 24 | banks | plat_dram | + * +----------+--------------+ | + * | 32 | checksum | | + * +----------+--------------+--------------+ + * | 40 | num_consoles | | + * +----------+--------------+ | + * | 48 | consoles | plat_console | + * +----------+--------------+ | + * | 56 | checksum | | + * +----------+--------------+--------------+ + * | 64 | base 0 | | + * +----------+--------------+ bank[0] | + * | 72 | size 0 | | + * +----------+--------------+--------------+ + * | 80 | base | | + * +----------+--------------+ | + * | 88 | map_pages | | + * +----------+--------------+ | + * | 96 | name | | + * +----------+--------------+ consoles[0] | + * | 104 | clk_in_hz | | + * +----------+--------------+ | + * | 112 | baud_rate | | + * +----------+--------------+ | + * | 120 | flags | | + * +----------+--------------+--------------+ + */ + bank_ptr = (struct ns_dram_bank *) + (((uintptr_t)manifest) + sizeof(*manifest)); + + console_ptr = (struct console_info *) + ((uintptr_t)bank_ptr + (num_banks * sizeof(*bank_ptr))); + + manifest->plat_dram.banks = bank_ptr; + manifest->plat_console.consoles = console_ptr; + + /* Ensure the manifest is not larger than the shared buffer */ + assert((sizeof(struct rmm_manifest) + + (sizeof(struct console_info) * num_consoles) + + (sizeof(struct ns_dram_bank) * num_banks)) <= RMM_SHARED_SIZE); + + /* Calculate checksum of plat_dram structure */ + checksum = num_banks + (uint64_t)bank_ptr; + + base = NS_DRAM0_BASE; + size = NS_DRAM0_SIZE; + bank_ptr[0].base = base; + bank_ptr[0].size = size; + checksum += base + size; + + /* Checksum must be 0 */ + manifest->plat_dram.checksum = ~checksum + 1UL; + + /* Calculate the checksum of the plat_consoles structure */ + checksum = num_consoles + (uint64_t)console_ptr; + + /* Zero out the console info struct */ + memset((void *)console_ptr, 0, sizeof(struct console_info) * num_consoles); + + console_ptr[0].map_pages = 1; + console_ptr[0].base = PLAT_QEMU_BOOT_UART_BASE; + console_ptr[0].clk_in_hz = PLAT_QEMU_BOOT_UART_CLK_IN_HZ; + console_ptr[0].baud_rate = PLAT_QEMU_CONSOLE_BAUDRATE; + + strlcpy(console_ptr[0].name, "pl011", sizeof(console_ptr[0].name)); + + /* Update checksum */ + checksum += console_ptr[0].base + console_ptr[0].map_pages + + console_ptr[0].clk_in_hz + console_ptr[0].baud_rate; + + /* Checksum must be 0 */ + manifest->plat_console.checksum = ~checksum + 1UL; + + return 0; +} +#endif /* ENABLE_RME */ diff --git a/plat/qemu/common/qemu_io_storage.c b/plat/qemu/common/qemu_io_storage.c index 4c61b146..59bba867 100644 --- a/plat/qemu/common/qemu_io_storage.c +++ b/plat/qemu/common/qemu_io_storage.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015-2022, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2015-2024, Arm Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -33,6 +33,7 @@ #define BL32_EXTRA1_IMAGE_NAME "bl32_extra1.bin" #define BL32_EXTRA2_IMAGE_NAME "bl32_extra2.bin" #define BL33_IMAGE_NAME "bl33.bin" +#define RMM_IMAGE_NAME "rmm.bin" #if TRUSTED_BOARD_BOOT #define TRUSTED_BOOT_FW_CERT_NAME "tb_fw.crt" @@ -96,6 +97,10 @@ static const io_uuid_spec_t bl33_uuid_spec = { .uuid = UUID_NON_TRUSTED_FIRMWARE_BL33, }; +static const io_uuid_spec_t rmm_uuid_spec = { + .uuid = UUID_REALM_MONITOR_MGMT_FIRMWARE, +}; + #if TRUSTED_BOARD_BOOT static const io_uuid_spec_t tb_fw_cert_uuid_spec = { .uuid = UUID_TRUSTED_BOOT_FW_CERT, @@ -163,6 +168,10 @@ static const io_file_spec_t sh_file_spec[] = { .path = BL33_IMAGE_NAME, .mode = FOPEN_MODE_RB }, + [RMM_IMAGE_ID] = { + .path = RMM_IMAGE_NAME, + .mode = FOPEN_MODE_RB + }, #if TRUSTED_BOARD_BOOT [TRUSTED_BOOT_FW_CERT_ID] = { .path = TRUSTED_BOOT_FW_CERT_NAME, @@ -289,6 +298,12 @@ static const struct plat_io_policy policies[] = { (uintptr_t)&bl33_uuid_spec, open_fip }, + [RMM_IMAGE_ID] = { + &fip_dev_handle, + (uintptr_t)&rmm_uuid_spec, + open_fip + }, + #if TRUSTED_BOARD_BOOT [TRUSTED_BOOT_FW_CERT_ID] = { &fip_dev_handle, diff --git a/plat/qemu/common/qemu_plat_attest_token.c b/plat/qemu/common/qemu_plat_attest_token.c new file mode 100644 index 00000000..7b54271e --- /dev/null +++ b/plat/qemu/common/qemu_plat_attest_token.c @@ -0,0 +1,232 @@ +/* + * Copyright (c) 2024, Arm Limited and Contributors. All rights reserved. + * Copyright (c) 2024, Linaro Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <errno.h> +#include <string.h> + +#include <plat/common/platform.h> + +/* + * This is the CBOR serialization of the CCA platform token described at + * https://git.trustedfirmware.org/TF-M/tf-m-tools/+/refs/heads/main/iat-verifier/tests/data/cca_example_platform_token.yaml + */ +static const uint8_t sample_platform_token[] = { + 0xd2, 0x84, 0x44, 0xa1, 0x01, 0x38, 0x22, 0xa0, + 0x59, 0x05, 0x81, 0xa9, 0x19, 0x01, 0x09, 0x78, + 0x23, 0x74, 0x61, 0x67, 0x3a, 0x61, 0x72, 0x6d, + 0x2e, 0x63, 0x6f, 0x6d, 0x2c, 0x32, 0x30, 0x32, + 0x33, 0x3a, 0x63, 0x63, 0x61, 0x5f, 0x70, 0x6c, + 0x61, 0x74, 0x66, 0x6f, 0x72, 0x6d, 0x23, 0x31, + 0x2e, 0x30, 0x2e, 0x30, 0x0a, 0x58, 0x20, 0x0d, + 0x22, 0xe0, 0x8a, 0x98, 0x46, 0x90, 0x58, 0x48, + 0x63, 0x18, 0x28, 0x34, 0x89, 0xbd, 0xb3, 0x6f, + 0x09, 0xdb, 0xef, 0xeb, 0x18, 0x64, 0xdf, 0x43, + 0x3f, 0xa6, 0xe5, 0x4e, 0xa2, 0xd7, 0x11, 0x19, + 0x09, 0x5c, 0x58, 0x20, 0x7f, 0x45, 0x4c, 0x46, + 0x02, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x3e, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x50, 0x58, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x19, 0x01, 0x00, 0x58, + 0x21, 0x01, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, + 0x01, 0x00, 0x0f, 0x0e, 0x0d, 0x0c, 0x0b, 0x0a, + 0x09, 0x08, 0x17, 0x16, 0x15, 0x14, 0x13, 0x12, + 0x11, 0x10, 0x1f, 0x1e, 0x1d, 0x1c, 0x1b, 0x1a, + 0x19, 0x18, 0x19, 0x09, 0x61, 0x44, 0xcf, 0xcf, + 0xcf, 0xcf, 0x19, 0x09, 0x5b, 0x19, 0x30, 0x03, + 0x19, 0x09, 0x62, 0x67, 0x73, 0x68, 0x61, 0x2d, + 0x32, 0x35, 0x36, 0x19, 0x09, 0x60, 0x78, 0x3a, + 0x68, 0x74, 0x74, 0x70, 0x73, 0x3a, 0x2f, 0x2f, + 0x76, 0x65, 0x72, 0x61, 0x69, 0x73, 0x6f, 0x6e, + 0x2e, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, + 0x2f, 0x2e, 0x77, 0x65, 0x6c, 0x6c, 0x2d, 0x6b, + 0x6e, 0x6f, 0x77, 0x6e, 0x2f, 0x76, 0x65, 0x72, + 0x61, 0x69, 0x73, 0x6f, 0x6e, 0x2f, 0x76, 0x65, + 0x72, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x19, 0x09, 0x5f, 0x8d, 0xa4, 0x01, + 0x69, 0x52, 0x53, 0x45, 0x5f, 0x42, 0x4c, 0x31, + 0x5f, 0x32, 0x05, 0x58, 0x20, 0x53, 0x78, 0x79, + 0x63, 0x07, 0x53, 0x5d, 0xf3, 0xec, 0x8d, 0x8b, + 0x15, 0xa2, 0xe2, 0xdc, 0x56, 0x41, 0x41, 0x9c, + 0x3d, 0x30, 0x60, 0xcf, 0xe3, 0x22, 0x38, 0xc0, + 0xfa, 0x97, 0x3f, 0x7a, 0xa3, 0x02, 0x58, 0x20, + 0x9a, 0x27, 0x1f, 0x2a, 0x91, 0x6b, 0x0b, 0x6e, + 0xe6, 0xce, 0xcb, 0x24, 0x26, 0xf0, 0xb3, 0x20, + 0x6e, 0xf0, 0x74, 0x57, 0x8b, 0xe5, 0x5d, 0x9b, + 0xc9, 0x4f, 0x6f, 0x3f, 0xe3, 0xab, 0x86, 0xaa, + 0x06, 0x67, 0x73, 0x68, 0x61, 0x2d, 0x32, 0x35, + 0x36, 0xa4, 0x01, 0x67, 0x52, 0x53, 0x45, 0x5f, + 0x42, 0x4c, 0x32, 0x05, 0x58, 0x20, 0x53, 0x78, + 0x79, 0x63, 0x07, 0x53, 0x5d, 0xf3, 0xec, 0x8d, + 0x8b, 0x15, 0xa2, 0xe2, 0xdc, 0x56, 0x41, 0x41, + 0x9c, 0x3d, 0x30, 0x60, 0xcf, 0xe3, 0x22, 0x38, + 0xc0, 0xfa, 0x97, 0x3f, 0x7a, 0xa3, 0x02, 0x58, + 0x20, 0x53, 0xc2, 0x34, 0xe5, 0xe8, 0x47, 0x2b, + 0x6a, 0xc5, 0x1c, 0x1a, 0xe1, 0xca, 0xb3, 0xfe, + 0x06, 0xfa, 0xd0, 0x53, 0xbe, 0xb8, 0xeb, 0xfd, + 0x89, 0x77, 0xb0, 0x10, 0x65, 0x5b, 0xfd, 0xd3, + 0xc3, 0x06, 0x67, 0x73, 0x68, 0x61, 0x2d, 0x32, + 0x35, 0x36, 0xa4, 0x01, 0x65, 0x52, 0x53, 0x45, + 0x5f, 0x53, 0x05, 0x58, 0x20, 0x53, 0x78, 0x79, + 0x63, 0x07, 0x53, 0x5d, 0xf3, 0xec, 0x8d, 0x8b, + 0x15, 0xa2, 0xe2, 0xdc, 0x56, 0x41, 0x41, 0x9c, + 0x3d, 0x30, 0x60, 0xcf, 0xe3, 0x22, 0x38, 0xc0, + 0xfa, 0x97, 0x3f, 0x7a, 0xa3, 0x02, 0x58, 0x20, + 0x11, 0x21, 0xcf, 0xcc, 0xd5, 0x91, 0x3f, 0x0a, + 0x63, 0xfe, 0xc4, 0x0a, 0x6f, 0xfd, 0x44, 0xea, + 0x64, 0xf9, 0xdc, 0x13, 0x5c, 0x66, 0x63, 0x4b, + 0xa0, 0x01, 0xd1, 0x0b, 0xcf, 0x43, 0x02, 0xa2, + 0x06, 0x67, 0x73, 0x68, 0x61, 0x2d, 0x32, 0x35, + 0x36, 0xa4, 0x01, 0x66, 0x41, 0x50, 0x5f, 0x42, + 0x4c, 0x31, 0x05, 0x58, 0x20, 0x53, 0x78, 0x79, + 0x63, 0x07, 0x53, 0x5d, 0xf3, 0xec, 0x8d, 0x8b, + 0x15, 0xa2, 0xe2, 0xdc, 0x56, 0x41, 0x41, 0x9c, + 0x3d, 0x30, 0x60, 0xcf, 0xe3, 0x22, 0x38, 0xc0, + 0xfa, 0x97, 0x3f, 0x7a, 0xa3, 0x02, 0x58, 0x20, + 0x15, 0x71, 0xb5, 0xec, 0x78, 0xbd, 0x68, 0x51, + 0x2b, 0xf7, 0x83, 0x0b, 0xb6, 0xa2, 0xa4, 0x4b, + 0x20, 0x47, 0xc7, 0xdf, 0x57, 0xbc, 0xe7, 0x9e, + 0xb8, 0xa1, 0xc0, 0xe5, 0xbe, 0xa0, 0xa5, 0x01, + 0x06, 0x67, 0x73, 0x68, 0x61, 0x2d, 0x32, 0x35, + 0x36, 0xa4, 0x01, 0x66, 0x41, 0x50, 0x5f, 0x42, + 0x4c, 0x32, 0x05, 0x58, 0x20, 0x53, 0x78, 0x79, + 0x63, 0x07, 0x53, 0x5d, 0xf3, 0xec, 0x8d, 0x8b, + 0x15, 0xa2, 0xe2, 0xdc, 0x56, 0x41, 0x41, 0x9c, + 0x3d, 0x30, 0x60, 0xcf, 0xe3, 0x22, 0x38, 0xc0, + 0xfa, 0x97, 0x3f, 0x7a, 0xa3, 0x02, 0x58, 0x20, + 0x10, 0x15, 0x9b, 0xaf, 0x26, 0x2b, 0x43, 0xa9, + 0x2d, 0x95, 0xdb, 0x59, 0xda, 0xe1, 0xf7, 0x2c, + 0x64, 0x51, 0x27, 0x30, 0x16, 0x61, 0xe0, 0xa3, + 0xce, 0x4e, 0x38, 0xb2, 0x95, 0xa9, 0x7c, 0x58, + 0x06, 0x67, 0x73, 0x68, 0x61, 0x2d, 0x32, 0x35, + 0x36, 0xa4, 0x01, 0x67, 0x53, 0x43, 0x50, 0x5f, + 0x42, 0x4c, 0x31, 0x05, 0x58, 0x20, 0x53, 0x78, + 0x79, 0x63, 0x07, 0x53, 0x5d, 0xf3, 0xec, 0x8d, + 0x8b, 0x15, 0xa2, 0xe2, 0xdc, 0x56, 0x41, 0x41, + 0x9c, 0x3d, 0x30, 0x60, 0xcf, 0xe3, 0x22, 0x38, + 0xc0, 0xfa, 0x97, 0x3f, 0x7a, 0xa3, 0x02, 0x58, + 0x20, 0x10, 0x12, 0x2e, 0x85, 0x6b, 0x3f, 0xcd, + 0x49, 0xf0, 0x63, 0x63, 0x63, 0x17, 0x47, 0x61, + 0x49, 0xcb, 0x73, 0x0a, 0x1a, 0xa1, 0xcf, 0xaa, + 0xd8, 0x18, 0x55, 0x2b, 0x72, 0xf5, 0x6d, 0x6f, + 0x68, 0x06, 0x67, 0x73, 0x68, 0x61, 0x2d, 0x32, + 0x35, 0x36, 0xa4, 0x01, 0x67, 0x53, 0x43, 0x50, + 0x5f, 0x42, 0x4c, 0x32, 0x05, 0x58, 0x20, 0xf1, + 0x4b, 0x49, 0x87, 0x90, 0x4b, 0xcb, 0x58, 0x14, + 0xe4, 0x45, 0x9a, 0x05, 0x7e, 0xd4, 0xd2, 0x0f, + 0x58, 0xa6, 0x33, 0x15, 0x22, 0x88, 0xa7, 0x61, + 0x21, 0x4d, 0xcd, 0x28, 0x78, 0x0b, 0x56, 0x02, + 0x58, 0x20, 0xaa, 0x67, 0xa1, 0x69, 0xb0, 0xbb, + 0xa2, 0x17, 0xaa, 0x0a, 0xa8, 0x8a, 0x65, 0x34, + 0x69, 0x20, 0xc8, 0x4c, 0x42, 0x44, 0x7c, 0x36, + 0xba, 0x5f, 0x7e, 0xa6, 0x5f, 0x42, 0x2c, 0x1f, + 0xe5, 0xd8, 0x06, 0x67, 0x73, 0x68, 0x61, 0x2d, + 0x32, 0x35, 0x36, 0xa4, 0x01, 0x67, 0x41, 0x50, + 0x5f, 0x42, 0x4c, 0x33, 0x31, 0x05, 0x58, 0x20, + 0x53, 0x78, 0x79, 0x63, 0x07, 0x53, 0x5d, 0xf3, + 0xec, 0x8d, 0x8b, 0x15, 0xa2, 0xe2, 0xdc, 0x56, + 0x41, 0x41, 0x9c, 0x3d, 0x30, 0x60, 0xcf, 0xe3, + 0x22, 0x38, 0xc0, 0xfa, 0x97, 0x3f, 0x7a, 0xa3, + 0x02, 0x58, 0x20, 0x2e, 0x6d, 0x31, 0xa5, 0x98, + 0x3a, 0x91, 0x25, 0x1b, 0xfa, 0xe5, 0xae, 0xfa, + 0x1c, 0x0a, 0x19, 0xd8, 0xba, 0x3c, 0xf6, 0x01, + 0xd0, 0xe8, 0xa7, 0x06, 0xb4, 0xcf, 0xa9, 0x66, + 0x1a, 0x6b, 0x8a, 0x06, 0x67, 0x73, 0x68, 0x61, + 0x2d, 0x32, 0x35, 0x36, 0xa4, 0x01, 0x63, 0x52, + 0x4d, 0x4d, 0x05, 0x58, 0x20, 0x53, 0x78, 0x79, + 0x63, 0x07, 0x53, 0x5d, 0xf3, 0xec, 0x8d, 0x8b, + 0x15, 0xa2, 0xe2, 0xdc, 0x56, 0x41, 0x41, 0x9c, + 0x3d, 0x30, 0x60, 0xcf, 0xe3, 0x22, 0x38, 0xc0, + 0xfa, 0x97, 0x3f, 0x7a, 0xa3, 0x02, 0x58, 0x20, + 0xa1, 0xfb, 0x50, 0xe6, 0xc8, 0x6f, 0xae, 0x16, + 0x79, 0xef, 0x33, 0x51, 0x29, 0x6f, 0xd6, 0x71, + 0x34, 0x11, 0xa0, 0x8c, 0xf8, 0xdd, 0x17, 0x90, + 0xa4, 0xfd, 0x05, 0xfa, 0xe8, 0x68, 0x81, 0x64, + 0x06, 0x67, 0x73, 0x68, 0x61, 0x2d, 0x32, 0x35, + 0x36, 0xa4, 0x01, 0x69, 0x48, 0x57, 0x5f, 0x43, + 0x4f, 0x4e, 0x46, 0x49, 0x47, 0x05, 0x58, 0x20, + 0x53, 0x78, 0x79, 0x63, 0x07, 0x53, 0x5d, 0xf3, + 0xec, 0x8d, 0x8b, 0x15, 0xa2, 0xe2, 0xdc, 0x56, + 0x41, 0x41, 0x9c, 0x3d, 0x30, 0x60, 0xcf, 0xe3, + 0x22, 0x38, 0xc0, 0xfa, 0x97, 0x3f, 0x7a, 0xa3, + 0x02, 0x58, 0x20, 0x1a, 0x25, 0x24, 0x02, 0x97, + 0x2f, 0x60, 0x57, 0xfa, 0x53, 0xcc, 0x17, 0x2b, + 0x52, 0xb9, 0xff, 0xca, 0x69, 0x8e, 0x18, 0x31, + 0x1f, 0xac, 0xd0, 0xf3, 0xb0, 0x6e, 0xca, 0xae, + 0xf7, 0x9e, 0x17, 0x06, 0x67, 0x73, 0x68, 0x61, + 0x2d, 0x32, 0x35, 0x36, 0xa4, 0x01, 0x69, 0x46, + 0x57, 0x5f, 0x43, 0x4f, 0x4e, 0x46, 0x49, 0x47, + 0x05, 0x58, 0x20, 0x53, 0x78, 0x79, 0x63, 0x07, + 0x53, 0x5d, 0xf3, 0xec, 0x8d, 0x8b, 0x15, 0xa2, + 0xe2, 0xdc, 0x56, 0x41, 0x41, 0x9c, 0x3d, 0x30, + 0x60, 0xcf, 0xe3, 0x22, 0x38, 0xc0, 0xfa, 0x97, + 0x3f, 0x7a, 0xa3, 0x02, 0x58, 0x20, 0x9a, 0x92, + 0xad, 0xbc, 0x0c, 0xee, 0x38, 0xef, 0x65, 0x8c, + 0x71, 0xce, 0x1b, 0x1b, 0xf8, 0xc6, 0x56, 0x68, + 0xf1, 0x66, 0xbf, 0xb2, 0x13, 0x64, 0x4c, 0x89, + 0x5c, 0xcb, 0x1a, 0xd0, 0x7a, 0x25, 0x06, 0x67, + 0x73, 0x68, 0x61, 0x2d, 0x32, 0x35, 0x36, 0xa4, + 0x01, 0x6c, 0x54, 0x42, 0x5f, 0x46, 0x57, 0x5f, + 0x43, 0x4f, 0x4e, 0x46, 0x49, 0x47, 0x05, 0x58, + 0x20, 0x53, 0x78, 0x79, 0x63, 0x07, 0x53, 0x5d, + 0xf3, 0xec, 0x8d, 0x8b, 0x15, 0xa2, 0xe2, 0xdc, + 0x56, 0x41, 0x41, 0x9c, 0x3d, 0x30, 0x60, 0xcf, + 0xe3, 0x22, 0x38, 0xc0, 0xfa, 0x97, 0x3f, 0x7a, + 0xa3, 0x02, 0x58, 0x20, 0x23, 0x89, 0x03, 0x18, + 0x0c, 0xc1, 0x04, 0xec, 0x2c, 0x5d, 0x8b, 0x3f, + 0x20, 0xc5, 0xbc, 0x61, 0xb3, 0x89, 0xec, 0x0a, + 0x96, 0x7d, 0xf8, 0xcc, 0x20, 0x8c, 0xdc, 0x7c, + 0xd4, 0x54, 0x17, 0x4f, 0x06, 0x67, 0x73, 0x68, + 0x61, 0x2d, 0x32, 0x35, 0x36, 0xa4, 0x01, 0x6d, + 0x53, 0x4f, 0x43, 0x5f, 0x46, 0x57, 0x5f, 0x43, + 0x4f, 0x4e, 0x46, 0x49, 0x47, 0x05, 0x58, 0x20, + 0x53, 0x78, 0x79, 0x63, 0x07, 0x53, 0x5d, 0xf3, + 0xec, 0x8d, 0x8b, 0x15, 0xa2, 0xe2, 0xdc, 0x56, + 0x41, 0x41, 0x9c, 0x3d, 0x30, 0x60, 0xcf, 0xe3, + 0x22, 0x38, 0xc0, 0xfa, 0x97, 0x3f, 0x7a, 0xa3, + 0x02, 0x58, 0x20, 0xe6, 0xc2, 0x1e, 0x8d, 0x26, + 0x0f, 0xe7, 0x18, 0x82, 0xde, 0xbd, 0xb3, 0x39, + 0xd2, 0x40, 0x2a, 0x2c, 0xa7, 0x64, 0x85, 0x29, + 0xbc, 0x23, 0x03, 0xf4, 0x86, 0x49, 0xbc, 0xe0, + 0x38, 0x00, 0x17, 0x06, 0x67, 0x73, 0x68, 0x61, + 0x2d, 0x32, 0x35, 0x36, 0x58, 0x60, 0x31, 0xd0, + 0x4d, 0x52, 0xcc, 0xde, 0x95, 0x2c, 0x1e, 0x32, + 0xcb, 0xa1, 0x81, 0x88, 0x5a, 0x40, 0xb8, 0xcc, + 0x38, 0xe0, 0x52, 0x8c, 0x1e, 0x89, 0x58, 0x98, + 0x07, 0x64, 0x2a, 0xa5, 0xe3, 0xf2, 0xbc, 0x37, + 0xf9, 0x53, 0x74, 0x50, 0x6b, 0xff, 0x4d, 0x2e, + 0x4b, 0xe7, 0x06, 0x3c, 0x4d, 0x72, 0x41, 0x92, + 0x70, 0xc7, 0x22, 0xe8, 0xd4, 0xd9, 0x3e, 0xe8, + 0xb6, 0xc9, 0xfa, 0xce, 0x3b, 0x43, 0xc9, 0x76, + 0x1a, 0x49, 0x94, 0x1a, 0xb6, 0xf3, 0x8f, 0xfd, + 0xff, 0x49, 0x6a, 0xd4, 0x63, 0xb4, 0xcb, 0xfa, + 0x11, 0xd8, 0x3e, 0x23, 0xe3, 0x1f, 0x7f, 0x62, + 0x32, 0x9d, 0xe3, 0x0c, 0x1c, 0xc8 +}; + +/* + * Get the hardcoded platform attestation token as QEMU does not support + * RSE. + */ +int plat_rmmd_get_cca_attest_token(uintptr_t buf, size_t *len, + uintptr_t hash, size_t hash_size, + size_t *remaining_len) +{ + const size_t token_size = sizeof(sample_platform_token); + (void)hash; + (void)hash_size; + + /* Shouldn't happen because RMM uses the whole 4kB shared buffer */ + if (*len < token_size) { + return -EINVAL; + } + + memcpy((void *)buf, sample_platform_token, token_size); + *len = token_size; + *remaining_len = 0; + + return 0; +} diff --git a/plat/qemu/common/qemu_pm.c b/plat/qemu/common/qemu_pm.c index c2b5091d..5f64d70c 100644 --- a/plat/qemu/common/qemu_pm.c +++ b/plat/qemu/common/qemu_pm.c @@ -101,22 +101,6 @@ static int qemu_validate_power_state(unsigned int power_state, return PSCI_E_SUCCESS; } -/******************************************************************************* - * Platform handler called to check the validity of the non secure - * entrypoint. - ******************************************************************************/ -static int qemu_validate_ns_entrypoint(uintptr_t entrypoint) -{ - /* - * Check if the non secure entrypoint lies within the non - * secure DRAM. - */ - if ((entrypoint >= NS_DRAM0_BASE) && - (entrypoint < (NS_DRAM0_BASE + NS_DRAM0_SIZE))) - return PSCI_E_SUCCESS; - return PSCI_E_INVALID_ADDRESS; -} - /******************************************************************************* * Platform handler called when a CPU is about to enter standby. ******************************************************************************/ @@ -241,7 +225,6 @@ static const plat_psci_ops_t plat_qemu_psci_pm_ops = { .system_off = qemu_system_off, .system_reset = qemu_system_reset, .validate_power_state = qemu_validate_power_state, - .validate_ns_entrypoint = qemu_validate_ns_entrypoint }; int plat_setup_psci_ops(uintptr_t sec_entrypoint, diff --git a/plat/qemu/common/qemu_realm_attest_key.c b/plat/qemu/common/qemu_realm_attest_key.c new file mode 100644 index 00000000..7da04d15 --- /dev/null +++ b/plat/qemu/common/qemu_realm_attest_key.c @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2024, Arm Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <errno.h> +#include <string.h> + +#include <plat/common/platform.h> + +static const uint8_t sample_delegated_key[] = { + 0x20, 0x11, 0xC7, 0xF0, 0x3C, 0xEE, 0x43, 0x25, 0x17, 0x6E, + 0x52, 0x4F, 0x03, 0x3C, 0x0C, 0xE1, 0xE2, 0x1A, 0x76, 0xE6, + 0xC1, 0xA4, 0xF0, 0xB8, 0x39, 0xAA, 0x1D, 0xF6, 0x1E, 0x0E, + 0x8A, 0x5C, 0x8A, 0x05, 0x74, 0x0F, 0x9B, 0x69, 0xEF, 0xA7, + 0xEB, 0x1A, 0x41, 0x85, 0xBD, 0x11, 0x7F, 0x68 +}; + +/* + * Get the hardcoded delegated realm attestation key as QEMU + * does not support RSE. + */ +int plat_rmmd_get_cca_realm_attest_key(uintptr_t buf, size_t *len, + unsigned int type) +{ + if (*len < sizeof(sample_delegated_key)) { + return -EINVAL; + } + + (void)memcpy((void *)buf, (const void *)sample_delegated_key, + sizeof(sample_delegated_key)); + *len = sizeof(sample_delegated_key); + + return 0; +} diff --git a/plat/qemu/common/trp/qemu_trp_setup.c b/plat/qemu/common/trp/qemu_trp_setup.c new file mode 100644 index 00000000..0b914ee0 --- /dev/null +++ b/plat/qemu/common/trp/qemu_trp_setup.c @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2024, Arm Limited. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <common/bl_common.h> +#include <platform_def.h> +#include <services/rmm_core_manifest.h> +#include <services/rmmd_svc.h> +#include <services/trp/platform_trp.h> +#include <trp_helpers.h> + +#include "../qemu_private.h" + +/******************************************************************************* + * Received from boot manifest and populated here + ******************************************************************************/ +extern uint32_t trp_boot_manifest_version; + +static int qemu_trp_process_manifest(struct rmm_manifest *manifest) +{ + /* padding field on the manifest must be RES0 */ + assert(manifest->padding == 0U); + + /* Verify the Boot Manifest Version. Only the Major is considered */ + if (RMMD_MANIFEST_VERSION_MAJOR != + RMMD_GET_MANIFEST_VERSION_MAJOR(manifest->version)) { + return E_RMM_BOOT_MANIFEST_VERSION_NOT_SUPPORTED; + } + + trp_boot_manifest_version = manifest->version; + flush_dcache_range((uintptr_t)manifest, sizeof(struct rmm_manifest)); + + return 0; +} + +void trp_early_platform_setup(struct rmm_manifest *manifest) +{ + int rc; + + rc = qemu_trp_process_manifest(manifest); + if (rc != 0) { + trp_boot_abort(rc); + } + + qemu_console_init(); +} diff --git a/plat/qemu/common/trp/trp-qemu-common.mk b/plat/qemu/common/trp/trp-qemu-common.mk new file mode 100644 index 00000000..081ba558 --- /dev/null +++ b/plat/qemu/common/trp/trp-qemu-common.mk @@ -0,0 +1,12 @@ +# +# Copyright (c) 2024, Arm Limited and Contributors. All rights reserved. +# +# SPDX-License-Identifier: BSD-3-Clause +# + +# TRP source files common to QEMU platforms +RMM_SOURCES += plat/qemu/common/trp/qemu_trp_setup.c \ + plat/common/aarch64/platform_mp_stack.S \ + plat/qemu/common/aarch64/plat_helpers.S + +INCLUDES += -Iinclude/services/trp diff --git a/plat/qemu/qemu/include/platform_def.h b/plat/qemu/qemu/include/platform_def.h index 903c809d..0c85b1ed 100644 --- a/plat/qemu/qemu/include/platform_def.h +++ b/plat/qemu/qemu/include/platform_def.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015-2023, Arm Limited and Contributors. All rights reserved. + * Copyright (c) 2015-2024, Arm Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -150,7 +150,7 @@ * Put BL3-1 at the top of the Trusted SRAM. BL31_BASE is calculated using the * current BL3-1 debug size plus a little space for growth. */ -#define BL31_BASE (BL31_LIMIT - 0x60000) +#define BL31_BASE (BL31_LIMIT - 0x70000) #define BL31_LIMIT (BL_RAM_BASE + BL_RAM_SIZE - FW_HANDOFF_SIZE) #define BL31_PROGBITS_LIMIT BL1_RW_BASE @@ -171,7 +171,8 @@ #define BL32_SRAM_BASE BL_RAM_BASE #define BL32_SRAM_LIMIT BL31_BASE #define BL32_DRAM_BASE SEC_DRAM_BASE -#define BL32_DRAM_LIMIT (SEC_DRAM_BASE + SEC_DRAM_SIZE) +#define BL32_DRAM_LIMIT (SEC_DRAM_BASE + SEC_DRAM_SIZE - \ + RME_GPT_DRAM_SIZE) #define SEC_SRAM_ID 0 #define SEC_DRAM_ID 1 @@ -183,7 +184,7 @@ # define BL32_LIMIT (BL32_SRAM_LIMIT - FW_HANDOFF_SIZE) #elif BL32_RAM_LOCATION_ID == SEC_DRAM_ID # define BL32_MEM_BASE SEC_DRAM_BASE -# define BL32_MEM_SIZE SEC_DRAM_SIZE +# define BL32_MEM_SIZE (SEC_DRAM_SIZE - RME_GPT_DRAM_SIZE) # define BL32_BASE BL32_DRAM_BASE # define BL32_LIMIT (BL32_DRAM_LIMIT - FW_HANDOFF_SIZE) #else @@ -199,7 +200,7 @@ #define PLAT_PHY_ADDR_SPACE_SIZE (1ULL << 32) #define PLAT_VIRT_ADDR_SPACE_SIZE (1ULL << 32) -#define MAX_MMAP_REGIONS (11 + MAX_MMAP_REGIONS_SPMC) +#define MAX_MMAP_REGIONS (13 + MAX_MMAP_REGIONS_SPMC) #define MAX_XLAT_TABLES (6 + MAX_XLAT_TABLES_SPMC) #define MAX_IO_DEVICES 4 #define MAX_IO_HANDLES 4 @@ -226,7 +227,7 @@ #define QEMU_FLASH1_SIZE 0x04000000 #define PLAT_QEMU_FIP_BASE 0x00040000 -#define PLAT_QEMU_FIP_MAX_SIZE 0x00400000 +#define PLAT_QEMU_FIP_MAX_SIZE (QEMU_FLASH0_SIZE - PLAT_QEMU_FIP_BASE) #define DEVICE0_BASE 0x08000000 #define DEVICE0_SIZE 0x01000000 @@ -300,11 +301,6 @@ #define PLAT_SDEI_NORMAL_PRI 0x70 #define PLAT_SDEI_SGI_PRIVATE QEMU_IRQ_SEC_SGI_0 -/* - * System counter - */ -#define SYS_COUNTER_FREQ_IN_TICKS ((1000 * 1000 * 1000) / 16) - /* * Maximum size of Event Log buffer used in Measured Boot Event Log driver */ @@ -338,4 +334,77 @@ #define MAX_MMAP_REGIONS_SPMC 0 #define MAX_XLAT_TABLES_SPMC 0 #endif + +#if ENABLE_RME + +/* + * Reserve some space at the end of secure DRAM for the Granule Protection + * Tables + */ +#define PLAT_QEMU_L0_GPT_BASE (PLAT_QEMU_L1_GPT_BASE - \ + (PLAT_QEMU_L0_GPT_SIZE + \ + PLAT_QEMU_GPT_BITLOCK_SIZE)) +#define PLAT_QEMU_L0_GPT_SIZE (2 * PAGE_SIZE) +/* Two pages so the L0 GPT is naturally aligned. */ +#define PLAT_QEMU_GPT_BITLOCK_SIZE (2 * PAGE_SIZE) + +#define PLAT_QEMU_L1_GPT_BASE (SEC_DRAM_BASE + SEC_DRAM_SIZE - \ + PLAT_QEMU_L1_GPT_SIZE) +#define PLAT_QEMU_L1_GPT_END (PLAT_QEMU_L1_GPT_BASE + \ + PLAT_QEMU_L1_GPT_SIZE - 1U) +#define PLAT_QEMU_L1_GPT_SIZE UL(0x00100000) /* 1MB */ + +#define RME_GPT_DRAM_BASE PLAT_QEMU_L0_GPT_BASE +#define RME_GPT_DRAM_SIZE (PLAT_QEMU_L1_GPT_SIZE + \ + PLAT_QEMU_L0_GPT_SIZE + \ + PLAT_QEMU_GPT_BITLOCK_SIZE) + +#ifndef __ASSEMBLER__ +/* L0 table greater than 4KB must be naturally aligned */ +CASSERT((PLAT_QEMU_L0_GPT_BASE & (PLAT_QEMU_L0_GPT_SIZE - 1)) == 0, + assert_l0_gpt_naturally_aligned); +#endif + +/* Reserved some DRAM space for RMM (24MB) */ +#define REALM_DRAM_BASE (NS_DRAM0_BASE + PLAT_QEMU_DT_MAX_SIZE) +#define REALM_DRAM_SIZE 0x01800000 + +#define PLAT_QEMU_RMM_SIZE (REALM_DRAM_SIZE - RMM_SHARED_SIZE) +#define PLAT_QEMU_RMM_SHARED_SIZE (PAGE_SIZE) /* 4KB */ + +#define RMM_BASE (REALM_DRAM_BASE) +#define RMM_LIMIT (RMM_BASE + PLAT_QEMU_RMM_SIZE) +#define RMM_SHARED_BASE (RMM_LIMIT) +#define RMM_SHARED_SIZE PLAT_QEMU_RMM_SHARED_SIZE + +#define MAP_GPT_L0_REGION MAP_REGION_FLAT( \ + PLAT_QEMU_L0_GPT_BASE, \ + PLAT_QEMU_L0_GPT_SIZE + \ + PLAT_QEMU_GPT_BITLOCK_SIZE, \ + MT_MEMORY | MT_RW | EL3_PAS) + +#define MAP_GPT_L1_REGION MAP_REGION_FLAT( \ + PLAT_QEMU_L1_GPT_BASE, \ + PLAT_QEMU_L1_GPT_SIZE, \ + MT_MEMORY | MT_RW | EL3_PAS) +/* + * We add the RMM_SHARED size to RMM mapping to map the region as a block. + * Else we end up requiring more pagetables in BL2 for ROMLIB build. + */ +#define MAP_RMM_DRAM MAP_REGION_FLAT( \ + RMM_BASE, \ + (PLAT_QEMU_RMM_SIZE + \ + RMM_SHARED_SIZE), \ + MT_MEMORY | MT_RW | MT_REALM) + +#define MAP_RMM_SHARED_MEM MAP_REGION_FLAT( \ + RMM_SHARED_BASE, \ + RMM_SHARED_SIZE, \ + MT_MEMORY | MT_RW | MT_REALM) +#else /* !ENABLE_RME */ + +#define RME_GPT_DRAM_SIZE 0 + +#endif /* ENABLE_RME */ + #endif /* PLATFORM_DEF_H */ diff --git a/plat/qemu/qemu/include/qemu_pas_def.h b/plat/qemu/qemu/include/qemu_pas_def.h new file mode 100644 index 00000000..bcbea210 --- /dev/null +++ b/plat/qemu/qemu/include/qemu_pas_def.h @@ -0,0 +1,108 @@ +/* + * Copyright (c) 2024, Arm Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ +#ifndef QEMU_PAS_DEF_H +#define QEMU_PAS_DEF_H + +#include <lib/gpt_rme/gpt_rme.h> +#include "platform_def.h" + +/***************************************************************************** + * PAS regions used to initialize the Granule Protection Table (GPT) + ****************************************************************************/ + +/* + * The PA space is initially mapped in the GPT as follows: + * + * =========================================================================== + * Base Addr | Size |L? GPT|PAS |Content |Comment + * =========================================================================== + * | 1GB |L0 GPT|ANY |Flash | + * 00000000 | | | |IO | + * --------------------------------------------------------------------------- + * 224MB | 1KB |L1 GPT|ANY |Secure RAM (EL3) | + * 0e000000 | | | | (shared) | + * --------------------------------------------------------------------------- + * | 1MB-1KB |L1 GPT|ROOT |Secure RAM (EL3) | + * 0e001000 | | | | | + * --------------------------------------------------------------------------- + * 225MB | 14MB |L1 GPT|SECURE|Secure RAM | + * 0e100000 | | | | (EL2, EL1) | + * --------------------------------------------------------------------------- + * | 2MB |L1 GPT|ROOT |L0 and L1 GPTs, | + * 0edfc000 | +16KB | | | bitlocks | + * --------------------------------------------------------------------------- + * 240MB | 800MB |L1 GPT|ANY |IO | + * 0f000000 | | | | | + * --------------------------------------------------------------------------- + * 1GB | 1MB |L1 GPT|NS |DRAM | + * 40000000 | | | | (device tree) | + * --------------------------------------------------------------------------- + * 1GB+1MB | 24MB |L1 GPT|REALM |DRAM (RMM) | + * 40100000 | | | | | + * --------------------------------------------------------------------------- + * 1GB+25MB | 3GB |L1 GPT|NS |DRAM (kernel) | Limit set by + * 41900000 | | | | | NS_DRAM0_SIZE + * --------------------------------------------------------------------------- + * 256GB | 512+GB |L0 GPT|ANY |IO | Floating. Higher + * 40000000000 | | | | | when RAM>256GB + * ---------------------------------------------------------------------------- + */ + +/* EL3 SRAM */ +#define QEMU_PAS_ROOT_BASE BL_RAM_BASE +#define QEMU_PAS_ROOT_SIZE BL_RAM_SIZE + +/* Secure DRAM */ +#define QEMU_PAS_SEC_BASE SEC_DRAM_BASE +#define QEMU_PAS_SEC_SIZE (SEC_DRAM_SIZE - RME_GPT_DRAM_SIZE) + +/* GPTs */ +#define QEMU_PAS_GPT_BASE RME_GPT_DRAM_BASE +#define QEMU_PAS_GPT_SIZE RME_GPT_DRAM_SIZE + +/* RMM */ +#define QEMU_PAS_RMM_BASE RMM_BASE +#define QEMU_PAS_RMM_SIZE PLAT_QEMU_RMM_SIZE + +/* Shared area between EL3 and RMM */ +#define QEMU_PAS_RMM_SHARED_BASE RMM_SHARED_BASE +#define QEMU_PAS_RMM_SHARED_SIZE RMM_SHARED_SIZE + +#define QEMU_PAS_NS0_BASE NS_DRAM0_BASE +#define QEMU_PAS_NS0_SIZE PLAT_QEMU_DT_MAX_SIZE +#define QEMU_PAS_NS1_BASE (REALM_DRAM_BASE + REALM_DRAM_SIZE) +#define QEMU_PAS_NS1_SIZE (NS_DRAM0_SIZE - \ + (QEMU_PAS_NS0_SIZE + REALM_DRAM_SIZE)) + +#define QEMU_PAS_ROOT GPT_MAP_REGION_GRANULE(QEMU_PAS_ROOT_BASE, \ + QEMU_PAS_ROOT_SIZE, \ + GPT_GPI_ROOT) + +#define QEMU_PAS_SECURE GPT_MAP_REGION_GRANULE(QEMU_PAS_SEC_BASE, \ + QEMU_PAS_SEC_SIZE, \ + GPT_GPI_SECURE) + +#define QEMU_PAS_GPTS GPT_MAP_REGION_GRANULE(QEMU_PAS_GPT_BASE, \ + QEMU_PAS_GPT_SIZE, \ + GPT_GPI_ROOT) + +#define QEMU_PAS_NS0 GPT_MAP_REGION_GRANULE(QEMU_PAS_NS0_BASE, \ + QEMU_PAS_NS0_SIZE, \ + GPT_GPI_NS) + +#define QEMU_PAS_NS1 GPT_MAP_REGION_GRANULE(QEMU_PAS_NS1_BASE, \ + QEMU_PAS_NS1_SIZE, \ + GPT_GPI_NS) + +#define QEMU_PAS_REALM GPT_MAP_REGION_GRANULE(QEMU_PAS_RMM_BASE, \ + QEMU_PAS_RMM_SIZE + \ + QEMU_PAS_RMM_SHARED_SIZE, \ + GPT_GPI_REALM) + +/* GPT Configuration options */ +#define PLATFORM_L0GPTSZ GPCCR_L0GPTSZ_30BITS + +#endif /* QEMU_PAS_DEF_H */ diff --git a/plat/qemu/qemu/platform.mk b/plat/qemu/qemu/platform.mk index e902c121..0d4cdb8f 100644 --- a/plat/qemu/qemu/platform.mk +++ b/plat/qemu/qemu/platform.mk @@ -1,5 +1,5 @@ # -# Copyright (c) 2013-2023, Arm Limited and Contributors. All rights reserved. +# Copyright (c) 2013-2024, Arm Limited and Contributors. All rights reserved. # # SPDX-License-Identifier: BSD-3-Clause # @@ -78,13 +78,13 @@ ifneq (${TRUSTED_BOARD_BOOT},0) certificates: $(ROT_KEY) - $(ROT_KEY): | $(BUILD_PLAT) - @echo " OPENSSL $@" - $(Q)${OPENSSL_BIN_PATH}/openssl genrsa 2048 > $@ 2>/dev/null + $(ROT_KEY): | $$(@D)/ + $(s)echo " OPENSSL $@" + $(q)${OPENSSL_BIN_PATH}/openssl genrsa 2048 > $@ 2>/dev/null - $(ROTPK_HASH): $(ROT_KEY) - @echo " OPENSSL $@" - $(Q)${OPENSSL_BIN_PATH}/openssl rsa -in $< -pubout -outform DER 2>/dev/null |\ + $(ROTPK_HASH): $(ROT_KEY) | $$(@D)/ + $(s)echo " OPENSSL $@" + $(q)${OPENSSL_BIN_PATH}/openssl rsa -in $< -pubout -outform DER 2>/dev/null |\ ${OPENSSL_BIN_PATH}/openssl dgst -sha256 -binary > $@ 2>/dev/null endif @@ -96,10 +96,6 @@ ifeq (${MEASURED_BOOT},1) $(info Including ${MEASURED_BOOT_MK}) include ${MEASURED_BOOT_MK} - ifneq (${MBOOT_EL_HASH_ALG}, sha256) - $(eval $(call add_define,TF_MBEDTLS_MBOOT_USE_SHA512)) - endif - BL2_SOURCES += plat/qemu/qemu/qemu_measured_boot.c \ plat/qemu/qemu/qemu_helpers.c \ ${EVENT_LOG_SOURCES} @@ -204,6 +200,10 @@ endif BL32_RAM_LOCATION := tdram ifeq (${BL32_RAM_LOCATION}, tsram) BL32_RAM_LOCATION_ID = SEC_SRAM_ID + ifeq (${ENABLE_RME},1) + # Avoid overlap between BL2 and BL32 to ease GPT partition + $(error "With RME, BL32 must use secure DRAM") + endif else ifeq (${BL32_RAM_LOCATION}, tdram) BL32_RAM_LOCATION_ID = SEC_DRAM_ID else @@ -222,14 +222,14 @@ ARM_PRELOADED_DTB_BASE := PLAT_QEMU_DT_BASE $(eval $(call add_define,ARM_PRELOADED_DTB_BASE)) qemu_fw.bios: bl1 fip - $(ECHO) " DD $@" - $(Q)cp ${BUILD_PLAT}/bl1.bin ${BUILD_PLAT}/$@ - $(Q)dd if=${BUILD_PLAT}/fip.bin of=${BUILD_PLAT}/$@ bs=64k seek=4 status=none + $(s)echo " DD $@" + $(q)cp ${BUILD_PLAT}/bl1.bin ${BUILD_PLAT}/$@ + $(q)dd if=${BUILD_PLAT}/fip.bin of=${BUILD_PLAT}/$@ bs=64k seek=4 status=none qemu_fw.rom: qemu_fw.bios - $(ECHO) " DD $@" - $(Q)cp ${BUILD_PLAT}/$^ ${BUILD_PLAT}/$@ - $(Q)dd if=/dev/zero of=${BUILD_PLAT}/$@ bs=1M seek=64 count=0 status=none + $(s)echo " DD $@" + $(q)cp ${BUILD_PLAT}/$^ ${BUILD_PLAT}/$@ + $(q)dd if=/dev/zero of=${BUILD_PLAT}/$@ bs=1M seek=64 count=0 status=none ifneq (${BL33},) all: qemu_fw.bios qemu_fw.rom diff --git a/plat/qemu/qemu/qemu_measured_boot.c b/plat/qemu/qemu/qemu_measured_boot.c index 077f7a48..76a4da17 100644 --- a/plat/qemu/qemu/qemu_measured_boot.c +++ b/plat/qemu/qemu/qemu_measured_boot.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, Arm Limited. All rights reserved. + * Copyright (c) 2022-2024, Arm Limited. All rights reserved. * Copyright (c) 2022-2023, Linaro. * * SPDX-License-Identifier: BSD-3-Clause @@ -8,6 +8,7 @@ #include <stdint.h> #include <drivers/measured_boot/event_log/event_log.h> +#include <drivers/measured_boot/metadata.h> #include <plat/common/common_def.h> #include <plat/common/platform.h> #include <tools_share/tbbr_oid.h> @@ -20,16 +21,16 @@ static uint64_t event_log_base; /* QEMU table with platform specific image IDs, names and PCRs */ static const event_log_metadata_t qemu_event_log_metadata[] = { - { BL31_IMAGE_ID, EVLOG_BL31_STRING, PCR_0 }, - { BL32_IMAGE_ID, EVLOG_BL32_STRING, PCR_0 }, - { BL32_EXTRA1_IMAGE_ID, EVLOG_BL32_EXTRA1_STRING, PCR_0 }, - { BL32_EXTRA2_IMAGE_ID, EVLOG_BL32_EXTRA2_STRING, PCR_0 }, - { BL33_IMAGE_ID, EVLOG_BL33_STRING, PCR_0 }, - { HW_CONFIG_ID, EVLOG_HW_CONFIG_STRING, PCR_0 }, - { NT_FW_CONFIG_ID, EVLOG_NT_FW_CONFIG_STRING, PCR_0 }, - { SCP_BL2_IMAGE_ID, EVLOG_SCP_BL2_STRING, PCR_0 }, - { SOC_FW_CONFIG_ID, EVLOG_SOC_FW_CONFIG_STRING, PCR_0 }, - { TOS_FW_CONFIG_ID, EVLOG_TOS_FW_CONFIG_STRING, PCR_0 }, + { BL31_IMAGE_ID, MBOOT_BL31_IMAGE_STRING, PCR_0 }, + { BL32_IMAGE_ID, MBOOT_BL32_IMAGE_STRING, PCR_0 }, + { BL32_EXTRA1_IMAGE_ID, MBOOT_BL32_EXTRA1_IMAGE_STRING, PCR_0 }, + { BL32_EXTRA2_IMAGE_ID, MBOOT_BL32_EXTRA2_IMAGE_STRING, PCR_0 }, + { BL33_IMAGE_ID, MBOOT_BL33_IMAGE_STRING, PCR_0 }, + { HW_CONFIG_ID, MBOOT_HW_CONFIG_STRING, PCR_0 }, + { NT_FW_CONFIG_ID, MBOOT_NT_FW_CONFIG_STRING, PCR_0 }, + { SCP_BL2_IMAGE_ID, MBOOT_SCP_BL2_IMAGE_STRING, PCR_0 }, + { SOC_FW_CONFIG_ID, MBOOT_SOC_FW_CONFIG_STRING, PCR_0 }, + { TOS_FW_CONFIG_ID, MBOOT_TOS_FW_CONFIG_STRING, PCR_0 }, { EVLOG_INVALID_ID, NULL, (unsigned int)(-1) } /* Terminator */ }; diff --git a/plat/qemu/qemu/trp/trp-qemu.mk b/plat/qemu/qemu/trp/trp-qemu.mk new file mode 100644 index 00000000..e0f530ef --- /dev/null +++ b/plat/qemu/qemu/trp/trp-qemu.mk @@ -0,0 +1,8 @@ +# +# Copyright (c) 2024, Arm Limited. All rights reserved. +# +# SPDX-License-Identifier: BSD-3-Clause +# + +include plat/qemu/common/trp/trp-qemu-common.mk + diff --git a/plat/qemu/qemu_sbsa/include/platform_def.h b/plat/qemu/qemu_sbsa/include/platform_def.h index 14030e35..d2300954 100644 --- a/plat/qemu/qemu_sbsa/include/platform_def.h +++ b/plat/qemu/qemu_sbsa/include/platform_def.h @@ -61,6 +61,11 @@ #define CACHE_WRITEBACK_SHIFT 6 #define CACHE_WRITEBACK_GRANULE (1 << CACHE_WRITEBACK_SHIFT) +/* + * Define the max number of memory nodes. + */ +#define PLAT_MAX_MEM_NODES 128 + /* * Partition memory into secure ROM, non-secure DRAM, secure "SRAM", * and secure DRAM. @@ -261,11 +266,6 @@ #define PLAT_QEMU_DT_BASE NS_DRAM0_BASE #define PLAT_QEMU_DT_MAX_SIZE 0x100000 -/* - * System counter - */ -#define SYS_COUNTER_FREQ_IN_TICKS ((1000 * 1000 * 1000) / 16) - #if SPM_MM #define PLAT_QEMU_SP_IMAGE_BASE BL_RAM_BASE #define PLAT_QEMU_SP_IMAGE_SIZE ULL(0x300000) diff --git a/plat/qemu/qemu_sbsa/sbsa_sip_svc.c b/plat/qemu/qemu_sbsa/sbsa_sip_svc.c index 05ebec47..83e66f3a 100644 --- a/plat/qemu/qemu_sbsa/sbsa_sip_svc.c +++ b/plat/qemu/qemu_sbsa/sbsa_sip_svc.c @@ -28,13 +28,233 @@ static int platform_version_minor; #define SIP_SVC_VERSION SIP_FUNCTION_ID(1) #define SIP_SVC_GET_GIC SIP_FUNCTION_ID(100) #define SIP_SVC_GET_GIC_ITS SIP_FUNCTION_ID(101) +#define SIP_SVC_GET_CPU_COUNT SIP_FUNCTION_ID(200) +#define SIP_SVC_GET_CPU_NODE SIP_FUNCTION_ID(201) +#define SIP_SVC_GET_CPU_TOPOLOGY SIP_FUNCTION_ID(202) +#define SIP_SVC_GET_MEMORY_NODE_COUNT SIP_FUNCTION_ID(300) +#define SIP_SVC_GET_MEMORY_NODE SIP_FUNCTION_ID(301) static uint64_t gic_its_addr; +typedef struct { + uint32_t nodeid; + uint32_t mpidr; +} cpu_data; + +typedef struct{ + uint32_t nodeid; + uint64_t addr_base; + uint64_t addr_size; +} memory_data; + +/* + * sockets: the number of sockets on sbsa-ref platform. + * clusters: the number of clusters in one socket. + * cores: the number of cores in one cluster. + * threads: the number of threads in one core. + */ +typedef struct { + uint32_t sockets; + uint32_t clusters; + uint32_t cores; + uint32_t threads; +} cpu_topology; + +static struct { + uint32_t num_cpus; + uint32_t num_memnodes; + cpu_data cpu[PLATFORM_CORE_COUNT]; + cpu_topology cpu_topo; + memory_data memory[PLAT_MAX_MEM_NODES]; +} dynamic_platform_info; + void sbsa_set_gic_bases(const uintptr_t gicd_base, const uintptr_t gicr_base); uintptr_t sbsa_get_gicd(void); uintptr_t sbsa_get_gicr(void); +/* + * QEMU provides us with minimal information about hardware platform using + * minimalistic DeviceTree. This is not a Linux DeviceTree. It is not even + * a firmware DeviceTree. + * + * It is information passed from QEMU to describe the information a hardware + * platform would have other mechanisms to discover at runtime, that are + * affected by the QEMU command line. + * + * Ultimately this device tree will be replaced by IPC calls to an emulated SCP. + * And when we do that, we won't then have to rewrite Normal world firmware to + * cope. + */ + +static void read_cpu_topology_from_dt(void *dtb) +{ + int node; + + /* + * QEMU gives us this DeviceTree node when we config: + * -smp 16,sockets=2,clusters=2,cores=2,threads=2 + * + * topology { + * threads = <0x02>; + * cores = <0x02>; + * clusters = <0x02>; + * sockets = <0x02>; + * }; + */ + + node = fdt_path_offset(dtb, "/cpus/topology"); + if (node > 0) { + dynamic_platform_info.cpu_topo.sockets = + fdt_read_uint32_default(dtb, node, "sockets", 0); + dynamic_platform_info.cpu_topo.clusters = + fdt_read_uint32_default(dtb, node, "clusters", 0); + dynamic_platform_info.cpu_topo.cores = + fdt_read_uint32_default(dtb, node, "cores", 0); + dynamic_platform_info.cpu_topo.threads = + fdt_read_uint32_default(dtb, node, "threads", 0); + } + + INFO("Cpu topology: sockets: %d, clusters: %d, cores: %d, threads: %d\n", + dynamic_platform_info.cpu_topo.sockets, + dynamic_platform_info.cpu_topo.clusters, + dynamic_platform_info.cpu_topo.cores, + dynamic_platform_info.cpu_topo.threads); +} + +void read_cpuinfo_from_dt(void *dtb) +{ + int node; + int prev; + int cpu = 0; + uintptr_t mpidr; + + /* + * QEMU gives us this DeviceTree node: + * numa-node-id entries are only when NUMA config is used + * + * cpus { + * #size-cells = <0x00>; + * #address-cells = <0x02>; + * + * cpu@0 { + * numa-node-id = <0x00>; + * reg = <0x00 0x00>; + * }; + * + * cpu@1 { + * numa-node-id = <0x03>; + * reg = <0x00 0x01>; + * }; + * }; + */ + node = fdt_path_offset(dtb, "/cpus"); + if (node < 0) { + ERROR("No information about cpus in DeviceTree.\n"); + panic(); + } + + /* + * QEMU numbers cpus from 0 and there can be /cpus/cpu-map present so we + * cannot use fdt_first_subnode() here + */ + node = fdt_path_offset(dtb, "/cpus/cpu@0"); + + while (node > 0) { + if (fdt_getprop(dtb, node, "reg", NULL)) { + fdt_get_reg_props_by_index(dtb, node, 0, &mpidr, NULL); + } else { + ERROR("Incomplete information for cpu %d in DeviceTree.\n", cpu); + panic(); + } + + dynamic_platform_info.cpu[cpu].mpidr = mpidr; + dynamic_platform_info.cpu[cpu].nodeid = + fdt_read_uint32_default(dtb, node, "numa-node-id", 0); + + INFO("CPU %d: node-id: %d, mpidr: %ld\n", cpu, + dynamic_platform_info.cpu[cpu].nodeid, mpidr); + + cpu++; + + prev = node; + node = fdt_next_subnode(dtb, prev); + } + + dynamic_platform_info.num_cpus = cpu; + INFO("Found %d cpus\n", dynamic_platform_info.num_cpus); + + read_cpu_topology_from_dt(dtb); +} + +void read_meminfo_from_dt(void *dtb) +{ + const fdt32_t *prop; + const char *type; + int prev, node; + int len; + uint32_t memnode = 0; + uint32_t higher_value, lower_value; + uint64_t cur_base, cur_size; + + /* + * QEMU gives us this DeviceTree node: + * + * memory@100c0000000 { + * numa-node-id = <0x01>; + * reg = <0x100 0xc0000000 0x00 0x40000000>; + * device_type = "memory"; + * }; + * + * memory@10000000000 { + * numa-node-id = <0x00>; + * reg = <0x100 0x00 0x00 0xc0000000>; + * device_type = "memory"; + * } + */ + + for (prev = 0;; prev = node) { + node = fdt_next_node(dtb, prev, NULL); + if (node < 0) { + break; + } + + type = fdt_getprop(dtb, node, "device_type", &len); + if (type && strncmp(type, "memory", len) == 0) { + dynamic_platform_info.memory[memnode].nodeid = + fdt_read_uint32_default(dtb, node, "numa-node-id", 0); + + /* + * Get the 'reg' property of this node and + * assume two 8 bytes for base and size. + */ + prop = fdt_getprop(dtb, node, "reg", &len); + if (prop != 0 && len == (2 * sizeof(int64_t))) { + higher_value = fdt32_to_cpu(*prop); + lower_value = fdt32_to_cpu(*(prop + 1)); + cur_base = (uint64_t)(lower_value | ((uint64_t)higher_value) << 32); + + higher_value = fdt32_to_cpu(*(prop + 2)); + lower_value = fdt32_to_cpu(*(prop + 3)); + cur_size = (uint64_t)(lower_value | ((uint64_t)higher_value) << 32); + + dynamic_platform_info.memory[memnode].addr_base = cur_base; + dynamic_platform_info.memory[memnode].addr_size = cur_size; + + INFO("RAM %d: node-id: %d, address: 0x%lx - 0x%lx\n", + memnode, + dynamic_platform_info.memory[memnode].nodeid, + dynamic_platform_info.memory[memnode].addr_base, + dynamic_platform_info.memory[memnode].addr_base + + dynamic_platform_info.memory[memnode].addr_size - 1); + } + + memnode++; + } + } + + dynamic_platform_info.num_memnodes = memnode; +} + void read_platform_config_from_dt(void *dtb) { int node; @@ -99,10 +319,10 @@ void read_platform_version(void *dtb) node = fdt_path_offset(dtb, "/"); if (node >= 0) { - platform_version_major = fdt32_ld(fdt_getprop(dtb, node, - "machine-version-major", NULL)); - platform_version_minor = fdt32_ld(fdt_getprop(dtb, node, - "machine-version-minor", NULL)); + platform_version_major = + fdt_read_uint32_default(dtb, node, "machine-version-major", 0); + platform_version_minor = + fdt_read_uint32_default(dtb, node, "machine-version-minor", 0); } } @@ -129,6 +349,8 @@ void sip_svc_init(void) INFO("Platform version: %d.%d\n", platform_version_major, platform_version_minor); read_platform_config_from_dt(dtb); + read_cpuinfo_from_dt(dtb); + read_meminfo_from_dt(dtb); } /* @@ -144,6 +366,7 @@ uintptr_t sbsa_sip_smc_handler(uint32_t smc_fid, u_register_t flags) { uint32_t ns; + uint64_t index; /* Determine which security state this SMC originated from */ ns = is_caller_non_secure(flags); @@ -163,6 +386,45 @@ uintptr_t sbsa_sip_smc_handler(uint32_t smc_fid, case SIP_SVC_GET_GIC_ITS: SMC_RET2(handle, NULL, gic_its_addr); + case SIP_SVC_GET_CPU_COUNT: + SMC_RET2(handle, NULL, dynamic_platform_info.num_cpus); + + case SIP_SVC_GET_CPU_NODE: + index = x1; + if (index < PLATFORM_CORE_COUNT) { + SMC_RET3(handle, NULL, + dynamic_platform_info.cpu[index].nodeid, + dynamic_platform_info.cpu[index].mpidr); + } else { + SMC_RET1(handle, SMC_ARCH_CALL_INVAL_PARAM); + } + + case SIP_SVC_GET_CPU_TOPOLOGY: + if (dynamic_platform_info.cpu_topo.cores > 0) { + SMC_RET5(handle, NULL, + dynamic_platform_info.cpu_topo.sockets, + dynamic_platform_info.cpu_topo.clusters, + dynamic_platform_info.cpu_topo.cores, + dynamic_platform_info.cpu_topo.threads); + } else { + /* we do not know topology so we report SMC as unknown */ + SMC_RET1(handle, SMC_UNK); + } + + case SIP_SVC_GET_MEMORY_NODE_COUNT: + SMC_RET2(handle, NULL, dynamic_platform_info.num_memnodes); + + case SIP_SVC_GET_MEMORY_NODE: + index = x1; + if (index < PLAT_MAX_MEM_NODES) { + SMC_RET4(handle, NULL, + dynamic_platform_info.memory[index].nodeid, + dynamic_platform_info.memory[index].addr_base, + dynamic_platform_info.memory[index].addr_size); + } else { + SMC_RET1(handle, SMC_ARCH_CALL_INVAL_PARAM); + } + default: ERROR("%s: unhandled SMC (0x%x) (function id: %d)\n", __func__, smc_fid, smc_fid - SIP_FUNCTION); diff --git a/plat/qti/common/src/aarch64/qti_kryo4_gold.S b/plat/qti/common/src/aarch64/qti_kryo4_gold.S index 9bcdf542..49b7cf01 100644 --- a/plat/qti/common/src/aarch64/qti_kryo4_gold.S +++ b/plat/qti/common/src/aarch64/qti_kryo4_gold.S @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015-2018, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2015-2024, Arm Limited and Contributors. All rights reserved. * Copyright (c) 2018-2020, The Linux Foundation. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause @@ -41,16 +41,6 @@ func qti_kryo4_gold_cluster_pwr_dwn ret endfunc qti_kryo4_gold_cluster_pwr_dwn -#if REPORT_ERRATA -/* - * Errata printing function for Kryo4 Gold. Must follow AAPCS. - */ -func qti_kryo4_gold_errata_report - /* TODO : Need to add support. Required only for debug bl31 image.*/ - ret -endfunc qti_kryo4_gold_errata_report -#endif - /* --------------------------------------------- * This function provides kryo4_gold specific * register information for crash reporting. diff --git a/plat/qti/common/src/aarch64/qti_kryo4_silver.S b/plat/qti/common/src/aarch64/qti_kryo4_silver.S index 36374b73..4a98912d 100644 --- a/plat/qti/common/src/aarch64/qti_kryo4_silver.S +++ b/plat/qti/common/src/aarch64/qti_kryo4_silver.S @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015-2018, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2015-2024, Arm Limited and Contributors. All rights reserved. * Copyright (c) 2018-2020, The Linux Foundation. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause @@ -35,17 +35,6 @@ func qti_kryo4_silver_cluster_pwr_dwn ret endfunc qti_kryo4_silver_cluster_pwr_dwn -#if REPORT_ERRATA -/* - * Errata printing function for Kryo4 Silver. Must follow AAPCS. - */ -func qti_kryo4_silver_errata_report - /* TODO : Need to add support. Required only for debug bl31 image.*/ - ret -endfunc qti_kryo4_silver_errata_report -#endif - - /* --------------------------------------------- * This function provides kryo4_silver specific * register information for crash reporting. diff --git a/plat/qti/common/src/aarch64/qti_kryo6_gold.S b/plat/qti/common/src/aarch64/qti_kryo6_gold.S index 577e7ff6..5f9463f6 100644 --- a/plat/qti/common/src/aarch64/qti_kryo6_gold.S +++ b/plat/qti/common/src/aarch64/qti_kryo6_gold.S @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015-2018, Arm Limited and Contributors. All rights reserved. + * Copyright (c) 2015-2024, Arm Limited and Contributors. All rights reserved. * Copyright (c) 2018-2021, The Linux Foundation. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause @@ -41,16 +41,6 @@ func qti_kryo6_gold_cluster_pwr_dwn ret endfunc qti_kryo6_gold_cluster_pwr_dwn -#if REPORT_ERRATA -/* - * Errata printing function for Kryo4 Gold. Must follow AAPCS. - */ -func qti_kryo6_gold_errata_report - /* TODO : Need to add support. Required only for debug bl31 image.*/ - ret -endfunc qti_kryo6_gold_errata_report -#endif - /* --------------------------------------------- * This function provides kryo4_gold specific * register information for crash reporting. diff --git a/plat/qti/common/src/aarch64/qti_kryo6_silver.S b/plat/qti/common/src/aarch64/qti_kryo6_silver.S index 6ad0bcae..4a54a64c 100644 --- a/plat/qti/common/src/aarch64/qti_kryo6_silver.S +++ b/plat/qti/common/src/aarch64/qti_kryo6_silver.S @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015-2018, Arm Limited and Contributors. All rights reserved. + * Copyright (c) 2015-2024, Arm Limited and Contributors. All rights reserved. * Copyright (c) 2018-2021, The Linux Foundation. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause @@ -35,17 +35,6 @@ func qti_kryo6_silver_cluster_pwr_dwn ret endfunc qti_kryo6_silver_cluster_pwr_dwn -#if REPORT_ERRATA -/* - * Errata printing function for Kryo4 Silver. Must follow AAPCS. - */ -func qti_kryo6_silver_errata_report - /* TODO : Need to add support. Required only for debug bl31 image.*/ - ret -endfunc qti_kryo6_silver_errata_report -#endif - - /* --------------------------------------------- * This function provides kryo4_silver specific * register information for crash reporting. diff --git a/plat/qti/qtiseclib/src/qtiseclib_cb_interface.c b/plat/qti/qtiseclib/src/qtiseclib_cb_interface.c index c4cd2597..804ad42f 100644 --- a/plat/qti/qtiseclib/src/qtiseclib_cb_interface.c +++ b/plat/qti/qtiseclib/src/qtiseclib_cb_interface.c @@ -142,10 +142,10 @@ void qtiseclib_cb_get_ns_ctx(qtiseclib_dbg_a64_ctxt_regs_type *qti_ns_ctx) qti_ns_ctx->elr_el3 = read_ctx_reg(get_el3state_ctx(ctx), CTX_ELR_EL3); qti_ns_ctx->spsr_el1 = - read_ctx_reg(get_el1_sysregs_ctx(ctx), CTX_SPSR_EL1); + read_el1_ctx_common(get_el1_sysregs_ctx(ctx), spsr_el1); qti_ns_ctx->elr_el1 = - read_ctx_reg(get_el1_sysregs_ctx(ctx), CTX_ELR_EL1); - qti_ns_ctx->sp_el1 = read_ctx_reg(get_el1_sysregs_ctx(ctx), CTX_SP_EL1); + read_el1_ctx_common(get_el1_sysregs_ctx(ctx), elr_el1); + qti_ns_ctx->sp_el1 = read_el1_ctx_common(get_el1_sysregs_ctx(ctx), sp_el1); qti_ns_ctx->x0 = read_ctx_reg(get_gpregs_ctx(ctx), CTX_GPREG_X0); qti_ns_ctx->x1 = read_ctx_reg(get_gpregs_ctx(ctx), CTX_GPREG_X1); diff --git a/plat/renesas/common/aarch64/plat_helpers.S b/plat/renesas/common/aarch64/plat_helpers.S index a7fdfa07..572620db 100644 --- a/plat/renesas/common/aarch64/plat_helpers.S +++ b/plat/renesas/common/aarch64/plat_helpers.S @@ -1,6 +1,6 @@ /* * Copyright (c) 2013-2023, Arm Limited and Contributors. All rights reserved. - * Copyright (c) 2015-2021, Renesas Electronics Corporation. All rights reserved. + * Copyright (c) 2015-2023, Renesas Electronics Corporation. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -172,15 +172,16 @@ func bl2_enter_bl31 * BL31 will initialize the address space according to its * own requirement. */ -#if RCAR_BL2_DCACHE == 1 /* Disable mmu and data cache */ bl disable_mmu_el3 +#if RCAR_BL2_DCACHE == 1 /* Data cache clean and invalidate */ mov x0, #DCCISW bl dcsw_op_all +#endif /* RCAR_BL2_DCACHE == 1 */ /* TLB invalidate all, EL3 */ tlbi alle3 -#endif /* RCAR_BL2_DCACHE == 1 */ + bl disable_mmu_icache_el3 /* Invalidate instruction cache */ ic iallu diff --git a/plat/renesas/common/aarch64/platform_common.c b/plat/renesas/common/aarch64/platform_common.c index 17ccb282..9e7d5260 100644 --- a/plat/renesas/common/aarch64/platform_common.c +++ b/plat/renesas/common/aarch64/platform_common.c @@ -1,6 +1,6 @@ /* * Copyright (c) 2013-2014, ARM Limited and Contributors. All rights reserved. - * Copyright (c) 2015-2017, Renesas Electronics Corporation. All rights reserved. + * Copyright (c) 2015-2023, Renesas Electronics Corporation. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -30,13 +30,19 @@ extern int32_t rcar_get_certificate(const int32_t name, uint32_t *cert); const uint8_t version_of_renesas[VERSION_OF_RENESAS_MAXLEN] __attribute__ ((__section__(".ro"))) = VERSION_OF_RENESAS; +#if (IMAGE_BL2) && (RCAR_BL2_DCACHE != 1) +#define RCAR_DCACHE MT_NON_CACHEABLE +#else +#define RCAR_DCACHE MT_MEMORY +#endif + #define MAP_SHARED_RAM MAP_REGION_FLAT(RCAR_SHARED_MEM_BASE, \ RCAR_SHARED_MEM_SIZE, \ MT_MEMORY | MT_RW | MT_SECURE) #define MAP_FLASH0 MAP_REGION_FLAT(FLASH0_BASE, \ FLASH0_SIZE, \ - MT_MEMORY | MT_RO | MT_SECURE) + RCAR_DCACHE | MT_RO | MT_SECURE) #define MAP_DRAM1_NS MAP_REGION_FLAT(DRAM1_NS_BASE, \ DRAM1_NS_SIZE, \ @@ -68,7 +74,7 @@ const uint8_t version_of_renesas[VERSION_OF_RENESAS_MAXLEN] #if IMAGE_BL2 #define MAP_DRAM0 MAP_REGION_FLAT(DRAM1_BASE, \ DRAM1_SIZE, \ - MT_MEMORY | MT_RW | MT_SECURE) + RCAR_DCACHE | MT_RW | MT_SECURE) #define MAP_REG0 MAP_REGION_FLAT(DEVICE_RCAR_BASE, \ DEVICE_RCAR_SIZE, \ @@ -76,7 +82,7 @@ const uint8_t version_of_renesas[VERSION_OF_RENESAS_MAXLEN] #define MAP_RAM0 MAP_REGION_FLAT(RCAR_SYSRAM_BASE, \ RCAR_SYSRAM_SIZE, \ - MT_MEMORY | MT_RW | MT_SECURE) + RCAR_DCACHE | MT_RW | MT_SECURE) #define MAP_REG1 MAP_REGION_FLAT(REG1_BASE, \ REG1_SIZE, \ @@ -84,7 +90,7 @@ const uint8_t version_of_renesas[VERSION_OF_RENESAS_MAXLEN] #define MAP_ROM MAP_REGION_FLAT(ROM0_BASE, \ ROM0_SIZE, \ - MT_MEMORY | MT_RO | MT_SECURE) + RCAR_DCACHE | MT_RO | MT_SECURE) #define MAP_REG2 MAP_REGION_FLAT(REG2_BASE, \ REG2_SIZE, \ @@ -92,7 +98,7 @@ const uint8_t version_of_renesas[VERSION_OF_RENESAS_MAXLEN] #define MAP_DRAM1 MAP_REGION_FLAT(DRAM_40BIT_BASE, \ DRAM_40BIT_SIZE, \ - MT_MEMORY | MT_RW | MT_SECURE) + RCAR_DCACHE | MT_RW | MT_SECURE) #endif #ifdef BL32_BASE @@ -152,9 +158,9 @@ void rcar_configure_mmu_el3(unsigned long total_base, unsigned long coh_limit) { mmap_add_region(total_base, total_base, total_size, - MT_MEMORY | MT_RW | MT_SECURE); + RCAR_DCACHE | MT_RW | MT_SECURE); mmap_add_region(ro_start, ro_start, ro_limit - ro_start, - MT_MEMORY | MT_RO | MT_SECURE); + RCAR_DCACHE | MT_RO | MT_SECURE); mmap_add_region(coh_start, coh_start, coh_limit - coh_start, MT_DEVICE | MT_RW | MT_SECURE); mmap_add(rcar_mmap); @@ -169,9 +175,9 @@ void rcar_configure_mmu_el3(unsigned long total_base, unsigned long ro_limit) { mmap_add_region(total_base, total_base, total_size, - MT_MEMORY | MT_RW | MT_SECURE); + RCAR_DCACHE | MT_RW | MT_SECURE); mmap_add_region(ro_start, ro_start, ro_limit - ro_start, - MT_MEMORY | MT_RO | MT_SECURE); + RCAR_DCACHE | MT_RO | MT_SECURE); mmap_add(rcar_mmap); init_xlat_tables(); diff --git a/plat/renesas/common/bl2_secure_setting.c b/plat/renesas/common/bl2_secure_setting.c index 2f8b0011..297b1a9e 100644 --- a/plat/renesas/common/bl2_secure_setting.c +++ b/plat/renesas/common/bl2_secure_setting.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015-2021, Renesas Electronics Corporation. All rights reserved. + * Copyright (c) 2015-2023, Renesas Electronics Corporation. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -107,8 +107,10 @@ static const struct { /* * Security group 0 attribute setting for master ports 3 * Security group 1 attribute setting for master ports 3 - * {SEC_GRP0CR3, 0x00000000U}, - * {SEC_GRP1CR3, 0x00000000U}, + */ + { SEC_GRP0CR3, 0x00003780U }, + { SEC_GRP1CR3, 0x00003780U }, + /* * Security group 0 attribute setting for slave ports 0 * Security group 1 attribute setting for slave ports 0 * {SEC_GRP0COND0, 0x00000000U}, @@ -259,10 +261,51 @@ static const struct { }; /* AXI settings */ -static const struct { +struct axi_t { uint32_t reg; uint32_t val; -} axi[] = { +}; + +static const struct axi_t axi[] = { + /* + * SRAM ptotection + * AXI sram protected area division + */ + {AXI_SPTDIVCR0, 0x0E0E6304U}, + {AXI_SPTDIVCR1, 0x0E0E6360U}, + {AXI_SPTDIVCR2, 0x0E0E6360U}, + {AXI_SPTDIVCR3, 0x0E0E6360U}, + {AXI_SPTDIVCR4, 0x0E0E6360U}, + {AXI_SPTDIVCR5, 0x0E0E6360U}, + {AXI_SPTDIVCR6, 0x0E0E6360U}, + {AXI_SPTDIVCR7, 0x0E0E6360U}, + {AXI_SPTDIVCR8, 0x0E0E6360U}, + {AXI_SPTDIVCR9, 0x0E0E6360U}, + {AXI_SPTDIVCR10, 0x0E0E6360U}, + {AXI_SPTDIVCR11, 0x0E0E6360U}, + {AXI_SPTDIVCR12, 0x0E0E6360U}, + {AXI_SPTDIVCR13, 0x0E0E6360U}, + {AXI_SPTDIVCR14, 0x0E0E6360U}, + /* AXI sram protected area setting */ + {AXI_SPTCR0, 0x0E000E0EU}, + {AXI_SPTCR1, 0x0E000000U}, + {AXI_SPTCR2, 0x0E000000U}, + {AXI_SPTCR3, 0x0E000000U}, + {AXI_SPTCR4, 0x0E000000U}, + {AXI_SPTCR5, 0x0E000000U}, + {AXI_SPTCR6, 0x0E000000U}, + {AXI_SPTCR7, 0x0E000000U}, + {AXI_SPTCR8, 0x0E000000U}, + {AXI_SPTCR9, 0x0E000000U}, + {AXI_SPTCR10, 0x0E000000U}, + {AXI_SPTCR11, 0x0E000000U}, + {AXI_SPTCR12, 0x0E000000U}, + {AXI_SPTCR13, 0x0E000000U}, + {AXI_SPTCR14, 0x0E000000U}, + {AXI_SPTCR15, 0x0E000000U} +}; + +static const struct axi_t axi_dram[] = { /* * DRAM protection * AXI dram protected area division @@ -299,41 +342,7 @@ static const struct { {AXI_DPTCR13, 0x0E000000U}, {AXI_DPTCR14, 0x0E000000U}, {AXI_DPTCR15, 0x0E000000U}, - /* - * SRAM ptotection - * AXI sram protected area division - */ - {AXI_SPTDIVCR0, 0x0E0E6304U}, - {AXI_SPTDIVCR1, 0x0E0E6360U}, - {AXI_SPTDIVCR2, 0x0E0E6360U}, - {AXI_SPTDIVCR3, 0x0E0E6360U}, - {AXI_SPTDIVCR4, 0x0E0E6360U}, - {AXI_SPTDIVCR5, 0x0E0E6360U}, - {AXI_SPTDIVCR6, 0x0E0E6360U}, - {AXI_SPTDIVCR7, 0x0E0E6360U}, - {AXI_SPTDIVCR8, 0x0E0E6360U}, - {AXI_SPTDIVCR9, 0x0E0E6360U}, - {AXI_SPTDIVCR10, 0x0E0E6360U}, - {AXI_SPTDIVCR11, 0x0E0E6360U}, - {AXI_SPTDIVCR12, 0x0E0E6360U}, - {AXI_SPTDIVCR13, 0x0E0E6360U}, - {AXI_SPTDIVCR14, 0x0E0E6360U}, /* AXI sram protected area setting */ - {AXI_SPTCR0, 0x0E000E0EU}, - {AXI_SPTCR1, 0x0E000000U}, - {AXI_SPTCR2, 0x0E000000U}, - {AXI_SPTCR3, 0x0E000000U}, - {AXI_SPTCR4, 0x0E000000U}, - {AXI_SPTCR5, 0x0E000000U}, - {AXI_SPTCR6, 0x0E000000U}, - {AXI_SPTCR7, 0x0E000000U}, - {AXI_SPTCR8, 0x0E000000U}, - {AXI_SPTCR9, 0x0E000000U}, - {AXI_SPTCR10, 0x0E000000U}, - {AXI_SPTCR11, 0x0E000000U}, - {AXI_SPTCR12, 0x0E000000U}, - {AXI_SPTCR13, 0x0E000000U}, - {AXI_SPTCR14, 0x0E000000U}, {AXI_SPTCR15, 0x0E000000U} }; @@ -345,7 +354,7 @@ static void lifec_security_setting(void) mmio_write_32(lifec[i].reg, lifec[i].val); } -/* SRAM/DRAM protection setting */ +/* SRAM protection setting */ static void axi_security_setting(void) { uint32_t i; @@ -354,6 +363,15 @@ static void axi_security_setting(void) mmio_write_32(axi[i].reg, axi[i].val); } +/* DRAM protection setting */ +void bl2_ram_security_setting_finish(void) +{ + uint32_t i; + + for (i = 0; i < ARRAY_SIZE(axi_dram); i++) + mmio_write_32(axi_dram[i].reg, axi_dram[i].val); +} + void bl2_secure_setting(void) { lifec_security_setting(); diff --git a/plat/renesas/common/include/platform_def.h b/plat/renesas/common/include/platform_def.h index ab071ec0..8178f3a7 100644 --- a/plat/renesas/common/include/platform_def.h +++ b/plat/renesas/common/include/platform_def.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015-2021, Renesas Electronics Corporation. All rights reserved. + * Copyright (c) 2015-2023, Renesas Electronics Corporation. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -144,7 +144,8 @@ ******************************************************************************/ #ifndef SPD_NONE #define BL32_BASE U(0x44100000) -#define BL32_LIMIT (BL32_BASE + U(0x200000)) +#define BL32_SIZE U(0x200000) +#define BL32_LIMIT (BL32_BASE + BL32_SIZE) #endif /******************************************************************************* @@ -152,7 +153,8 @@ ******************************************************************************/ #define BL33_BASE DRAM1_NS_BASE #define BL33_COMP_SIZE U(0x200000) -#define BL33_COMP_BASE (BL33_BASE - BL33_COMP_SIZE) +#define BL33_DECOMP_SIZE (BL33_COMP_SIZE * 32) +#define BL33_COMP_BASE (BL33_BASE + BL33_DECOMP_SIZE) /******************************************************************************* * Platform specific page table and MMU setup constants diff --git a/plat/renesas/common/include/rcar_def.h b/plat/renesas/common/include/rcar_def.h index 2cd26edb..86764442 100644 --- a/plat/renesas/common/include/rcar_def.h +++ b/plat/renesas/common/include/rcar_def.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015-2021, Renesas Electronics Corporation. All rights reserved. + * Copyright (c) 2015-2023, Renesas Electronics Corporation. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -31,7 +31,7 @@ #define DRAM_LIMIT ULL(0x0000010000000000) #define DRAM1_BASE U(0x40000000) #define DRAM1_SIZE U(0x80000000) -#define DRAM1_NS_BASE (DRAM1_BASE + U(0x10000000)) +#define DRAM1_NS_BASE (DRAM1_BASE + U(0x08000000)) #define DRAM1_NS_SIZE (DRAM1_SIZE - DRAM1_NS_BASE) #define DRAM_40BIT_BASE ULL(0x0400000000) #define DRAM_40BIT_SIZE ULL(0x0400000000) @@ -310,4 +310,31 @@ #define LOSSY_FMT2 LOSSY_FMT_YUV422INTLV #define LOSSY_ENA_DIS2 LOSSY_DISABLE +#define RCAR_CC63_BASE 0xE6600000U +#define CC63_TRNG_ISR_REG_ADDR 0x104U +#define CC63_TRNG_ISR_REG_EHR_VALID BIT_32(0) +#define CC63_TRNG_ISR_REG_AUTOCORR_ERR BIT_32(1) +#define CC63_TRNG_ICR_REG_ADDR 0x108U +#define CC63_TRNG_CONFIG_REG_ADDR 0x10CU +#define CC63_TRNG_CONFIG_REG_ROSC_MAX_LENGTH 3 +#define CC63_TRNG_VALID_REG_ADDR 0x110U +#define CC63_TRNG_VALID_REG_EHR_NOT_READY 0x0 +#define CC63_TRNG_EHR_DATA_ADDR_0_REG_ADDR 0x114U +#define CC63_TRNG_SOURCE_ENABLE_REG_ADDR 0x12CU +#define CC63_TRNG_SOURCE_ENABLE_REG_SET 0x1 +#define CC63_TRNG_SOURCE_ENABLE_REG_CLR 0x0 +#define CC63_TRNG_SAMPLE_CNT1_REG_ADDR 0x130U +#define CC63_TRNG_SAMPLE_CNT1_REG_SAMPLE_COUNT 100 +#define CC63_TRNG_DEBUG_CONTROL_REG_ADDR 0x138U +#define CC63_TRNG_DEBUG_CONTROL_REG_VNC_BYPASS BIT_32(1) +#define CC63_TRNG_DEBUG_CONTROL_REG_AUTOCORR_BYPASS BIT_32(3) +#define CC63_TRNG_DEBUG_CONTROL_REG_80090B \ + (CC63_TRNG_DEBUG_CONTROL_REG_VNC_BYPASS | \ + CC63_TRNG_DEBUG_CONTROL_REG_AUTOCORR_BYPASS) +#define CC63_TRNG_SW_RESET_REG_ADDR 0x140U +#define CC63_TRNG_SW_RESET_REG_SET 0x1 +#define CC63_TRNG_VERSION_REG_ADDR 0x1C0U +#define CC63_TRNG_CLK_ENABLE_REG_ADDR 0x1C4U +#define CC63_TRNG_CLK_ENABLE_REG_SET 0x1 + #endif /* RCAR_DEF_H */ diff --git a/plat/renesas/common/include/rcar_version.h b/plat/renesas/common/include/rcar_version.h index 5a0ca31c..777ec6aa 100644 --- a/plat/renesas/common/include/rcar_version.h +++ b/plat/renesas/common/include/rcar_version.h @@ -9,7 +9,7 @@ #include <arch_helpers.h> -#define VERSION_OF_RENESAS "3.0.3" +#define VERSION_OF_RENESAS "4.0.0" #define VERSION_OF_RENESAS_MAXLEN 128 extern const uint8_t version_of_renesas[VERSION_OF_RENESAS_MAXLEN]; diff --git a/plat/renesas/rcar/bl2_plat_setup.c b/plat/renesas/rcar/bl2_plat_setup.c index 81ee93e5..c5482403 100644 --- a/plat/renesas/rcar/bl2_plat_setup.c +++ b/plat/renesas/rcar/bl2_plat_setup.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018-2021, Renesas Electronics Corporation. All rights reserved. + * Copyright (c) 2018-2023, Renesas Electronics Corporation. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -48,10 +48,8 @@ #include "rcar_version.h" #include "rom_api.h" -#if RCAR_BL2_DCACHE == 1 /* - * Following symbols are only used during plat_arch_setup() only - * when RCAR_BL2_DCACHE is enabled. + * Following symbols are only used during plat_arch_setup() */ static const uint64_t BL2_RO_BASE = BL_CODE_BASE; static const uint64_t BL2_RO_LIMIT = BL_CODE_END; @@ -61,13 +59,12 @@ static const uint64_t BL2_COHERENT_RAM_BASE = BL_COHERENT_RAM_BASE; static const uint64_t BL2_COHERENT_RAM_LIMIT = BL_COHERENT_RAM_END; #endif -#endif - extern void plat_rcar_gic_driver_init(void); extern void plat_rcar_gic_init(void); extern void bl2_enter_bl31(const struct entry_point_info *bl_ep_info); extern void bl2_system_cpg_init(void); extern void bl2_secure_setting(void); +extern void bl2_ram_security_setting_finish(void); extern void bl2_cpg_init(void); extern void rcar_io_emmc_setup(void); extern void rcar_io_setup(void); @@ -371,10 +368,16 @@ mmu: rcar_swdt_release(); bl2_system_cpg_init(); -#if RCAR_BL2_DCACHE == 1 /* Disable data cache (clean and invalidate) */ disable_mmu_el3(); +#if RCAR_BL2_DCACHE == 1 + dcsw_op_all(DCCISW); #endif + tlbialle3(); + disable_mmu_icache_el3(); + plat_invalidate_icache(); + dsbsy(); + isb(); } static uint32_t is_ddr_backup_mode(void) @@ -417,44 +420,61 @@ void bl2_plat_preload_setup(void) } #endif -int bl2_plat_handle_pre_image_load(unsigned int image_id) +static uint64_t check_secure_load_area(uintptr_t base, uint32_t size, + uintptr_t dest, uint32_t len) { - u_register_t *boot_kind = (void *) BOOT_KIND_BASE; - bl_mem_params_node_t *bl_mem_params; + uintptr_t free_end, requested_end; - bl_mem_params = get_bl_mem_params_node(image_id); - -#if RCAR_GEN3_BL33_GZIP == 1 - if (image_id == BL33_IMAGE_ID) { - image_decompress_prepare(&bl_mem_params->image_info); + /* + * Handle corner cases first. + * + * The order of the 2 tests is important, because if there's no space + * left (i.e. free_size == 0) but we don't ask for any memory + * (i.e. size == 0) then we should report that the memory is free. + */ + if (len == 0U) { + WARN("BL2: load data size is zero\n"); + return 0; /* A zero-byte region is always free */ + } + if (size == 0U) { + goto err; } -#endif - - if (image_id != BL31_IMAGE_ID) - return 0; - - if (is_ddr_backup_mode() == RCAR_COLD_BOOT) - goto cold_boot; - - *boot_kind = RCAR_WARM_BOOT; - flush_dcache_range(BOOT_KIND_BASE, sizeof(*boot_kind)); - console_flush(); - bl2_plat_flush_bl31_params(); + /* + * Check that the end addresses don't overflow. + * If they do, consider that this memory region is not free, as this + * is an invalid scenario. + */ + if (check_uptr_overflow(base, size - 1U)) { + goto err; + } + free_end = base + (size - 1U); - /* will not return */ - bl2_enter_bl31(&bl_mem_params->ep_info); + if (check_uptr_overflow(dest, len - 1U)) { + goto err; + } + requested_end = dest + (len - 1U); -cold_boot: - *boot_kind = RCAR_COLD_BOOT; - flush_dcache_range(BOOT_KIND_BASE, sizeof(*boot_kind)); + /* + * Finally, check that the requested memory region lies within the free + * region. + */ + if ((dest < base) || (requested_end > free_end)) { + goto err; + } return 0; + +err: + ERROR("BL2: load data is outside the loadable area.\n"); + ERROR("BL2: dst=0x%lx, len=%d(0x%x)\n", dest, len, len); + return 1; } -static uint64_t rcar_get_dest_addr_from_cert(uint32_t certid, uintptr_t *dest) +static uint64_t rcar_get_dest_addr_from_cert(uint32_t certid, uintptr_t *dest, + uint32_t *len) { - uint32_t cert, len; + uint32_t cert; int ret; ret = rcar_get_certificate(certid, &cert); @@ -463,7 +483,104 @@ static uint64_t rcar_get_dest_addr_from_cert(uint32_t certid, uintptr_t *dest) return 1; } - rcar_read_certificate((uint64_t) cert, &len, dest); + rcar_read_certificate((uint64_t) cert, len, dest); + + return 0; +} + +int bl2_plat_handle_pre_image_load(unsigned int image_id) +{ + u_register_t *boot_kind = (void *) BOOT_KIND_BASE; + bl_mem_params_node_t *bl_mem_params; + uintptr_t dev_handle; + uintptr_t image_spec; + uintptr_t dest; + uint32_t len; + uint64_t ui64_ret; + int iret; + + bl_mem_params = get_bl_mem_params_node(image_id); + if (bl_mem_params == NULL) { + ERROR("BL2: Failed to get loading parameter.\n"); + return 1; + } + + switch (image_id) { + case BL31_IMAGE_ID: + if (is_ddr_backup_mode() == RCAR_COLD_BOOT) { + iret = plat_get_image_source(image_id, &dev_handle, + &image_spec); + if (iret != 0) { + return 1; + } + + ui64_ret = rcar_get_dest_addr_from_cert( + SOC_FW_CONTENT_CERT_ID, &dest, &len); + if (ui64_ret != 0U) { + return 1; + } + + ui64_ret = check_secure_load_area( + BL31_BASE, BL31_LIMIT - BL31_BASE, + dest, len); + if (ui64_ret != 0U) { + return 1; + } + + *boot_kind = RCAR_COLD_BOOT; + flush_dcache_range(BOOT_KIND_BASE, sizeof(*boot_kind)); + + bl_mem_params->image_info.image_base = dest; + bl_mem_params->image_info.image_size = len; + } else { + *boot_kind = RCAR_WARM_BOOT; + flush_dcache_range(BOOT_KIND_BASE, sizeof(*boot_kind)); + + console_flush(); + bl2_plat_flush_bl31_params(); + + /* will not return */ + bl2_enter_bl31(&bl_mem_params->ep_info); + } + + return 0; +#ifndef SPD_NONE + case BL32_IMAGE_ID: + ui64_ret = rcar_get_dest_addr_from_cert( + TRUSTED_OS_FW_CONTENT_CERT_ID, &dest, &len); + if (ui64_ret != 0U) { + return 1; + } + + ui64_ret = check_secure_load_area( + BL32_BASE, BL32_LIMIT - BL32_BASE, dest, len); + if (ui64_ret != 0U) { + return 1; + } + + bl_mem_params->image_info.image_base = dest; + bl_mem_params->image_info.image_size = len; + + return 0; +#endif + case BL33_IMAGE_ID: + /* case of image_id == BL33_IMAGE_ID */ + ui64_ret = rcar_get_dest_addr_from_cert( + NON_TRUSTED_FW_CONTENT_CERT_ID, + &dest, &len); + + if (ui64_ret != 0U) { + return 1; + } + +#if RCAR_GEN3_BL33_GZIP == 1 + image_decompress_prepare(&bl_mem_params->image_info); +#endif + + return 0; + default: + return 1; + } return 0; } @@ -472,8 +589,6 @@ int bl2_plat_handle_post_image_load(unsigned int image_id) { static bl2_to_bl31_params_mem_t *params; bl_mem_params_node_t *bl_mem_params; - uintptr_t dest; - int ret; if (!params) { params = (bl2_to_bl31_params_mem_t *) PARAMS_BASE; @@ -481,25 +596,23 @@ int bl2_plat_handle_post_image_load(unsigned int image_id) } bl_mem_params = get_bl_mem_params_node(image_id); + if (!bl_mem_params) { + ERROR("BL2: Failed to get loading parameter.\n"); + return 1; + } switch (image_id) { case BL31_IMAGE_ID: - ret = rcar_get_dest_addr_from_cert(SOC_FW_CONTENT_CERT_ID, - &dest); - if (!ret) - bl_mem_params->image_info.image_base = dest; - break; + bl_mem_params->ep_info.pc = bl_mem_params->image_info.image_base; + return 0; case BL32_IMAGE_ID: - ret = rcar_get_dest_addr_from_cert(TRUSTED_OS_FW_CONTENT_CERT_ID, - &dest); - if (!ret) - bl_mem_params->image_info.image_base = dest; - + bl_mem_params->ep_info.pc = bl_mem_params->image_info.image_base; memcpy(¶ms->bl32_ep_info, &bl_mem_params->ep_info, sizeof(entry_point_info_t)); - break; + return 0; case BL33_IMAGE_ID: #if RCAR_GEN3_BL33_GZIP == 1 + int ret; if ((mmio_read_32(BL33_COMP_BASE) & 0xffff) == 0x8b1f) { /* decompress gzip-compressed image */ ret = image_decompress(&bl_mem_params->image_info); @@ -514,7 +627,9 @@ int bl2_plat_handle_post_image_load(unsigned int image_id) #endif memcpy(¶ms->bl33_ep_info, &bl_mem_params->ep_info, sizeof(entry_point_info_t)); - break; + return 0; + default: + return 1; } return 0; @@ -641,6 +756,69 @@ err: #endif } +static void bl2_add_kaslr_seed(void) +{ + uint32_t cnt, isr, prr; + uint64_t seed; + int ret, node; + + /* SCEG is only available on H3/M3-W/M3-N */ + prr = mmio_read_32(RCAR_PRR); + switch (prr & PRR_PRODUCT_MASK) { + case PRR_PRODUCT_H3: + case PRR_PRODUCT_M3: + case PRR_PRODUCT_M3N: + break; + default: + return; + } + + mmio_write_32(RCAR_CC63_BASE + CC63_TRNG_SW_RESET_REG_ADDR, + CC63_TRNG_SW_RESET_REG_SET); + + do { + mmio_write_32(RCAR_CC63_BASE + CC63_TRNG_CLK_ENABLE_REG_ADDR, + CC63_TRNG_CLK_ENABLE_REG_SET); + mmio_write_32(RCAR_CC63_BASE + CC63_TRNG_SAMPLE_CNT1_REG_ADDR, + CC63_TRNG_SAMPLE_CNT1_REG_SAMPLE_COUNT); + cnt = mmio_read_32(RCAR_CC63_BASE + CC63_TRNG_SAMPLE_CNT1_REG_ADDR); + } while (cnt != CC63_TRNG_SAMPLE_CNT1_REG_SAMPLE_COUNT); + + mmio_write_32(RCAR_CC63_BASE + CC63_TRNG_CONFIG_REG_ADDR, + CC63_TRNG_CONFIG_REG_ROSC_MAX_LENGTH); + mmio_write_32(RCAR_CC63_BASE + CC63_TRNG_DEBUG_CONTROL_REG_ADDR, + CC63_TRNG_DEBUG_CONTROL_REG_80090B); + mmio_write_32(RCAR_CC63_BASE + CC63_TRNG_SOURCE_ENABLE_REG_ADDR, + CC63_TRNG_SOURCE_ENABLE_REG_SET); + + do { + isr = mmio_read_32(RCAR_CC63_BASE + CC63_TRNG_ISR_REG_ADDR); + if ((isr & CC63_TRNG_ISR_REG_AUTOCORR_ERR) != 0U) { + panic(); + } + } while ((isr & CC63_TRNG_ISR_REG_EHR_VALID) == 0U); + + mmio_write_32(RCAR_CC63_BASE + CC63_TRNG_ICR_REG_ADDR, UINT32_MAX); + seed = mmio_read_64(RCAR_CC63_BASE + CC63_TRNG_EHR_DATA_ADDR_0_REG_ADDR); + mmio_write_32(RCAR_CC63_BASE + CC63_TRNG_SOURCE_ENABLE_REG_ADDR, + CC63_TRNG_SOURCE_ENABLE_REG_CLR); + + node = ret = fdt_add_subnode(fdt, 0, "chosen"); + if (ret < 0) { + goto err; + } + + ret = fdt_setprop_u64(fdt, node, "kaslr-seed", seed); + if (ret < 0) { + goto err; + } + + return; +err: + NOTICE("BL2: Cannot add KASLR seed to FDT (ret=%i)\n", ret); + panic(); +} + static void bl2_add_dram_entry(uint64_t start, uint64_t size) { char nodename[32] = { 0 }; @@ -1100,6 +1278,9 @@ lcm_state: /* Print DRAM layout */ bl2_advertise_dram_size(product); + /* Add KASLR seed */ + bl2_add_kaslr_seed(); + if (boot_dev == MODEMR_BOOT_DEV_EMMC_25X1 || boot_dev == MODEMR_BOOT_DEV_EMMC_50X8) { if (rcar_emmc_init() != EMMC_SUCCESS) { @@ -1161,8 +1342,6 @@ lcm_state: void bl2_el3_plat_arch_setup(void) { -#if RCAR_BL2_DCACHE == 1 - NOTICE("BL2: D-Cache enable\n"); rcar_configure_mmu_el3(BL2_BASE, BL2_END - BL2_BASE, BL2_RO_BASE, BL2_RO_LIMIT @@ -1170,7 +1349,11 @@ void bl2_el3_plat_arch_setup(void) , BL2_COHERENT_RAM_BASE, BL2_COHERENT_RAM_LIMIT #endif ); -#endif +} + +void bl2_el3_plat_prepare_exit(void) +{ + bl2_ram_security_setting_finish(); } void bl2_platform_setup(void) diff --git a/plat/renesas/rcar/platform.mk b/plat/renesas/rcar/platform.mk index 670d4993..48139494 100644 --- a/plat/renesas/rcar/platform.mk +++ b/plat/renesas/rcar/platform.mk @@ -6,6 +6,8 @@ include plat/renesas/common/common.mk +ENABLE_STACK_PROTECTOR := strong + ifndef LSI $(error "Error: Unknown LSI. Please use LSI=<LSI name> to specify the LSI") else @@ -333,6 +335,10 @@ BL2_SOURCES += common/image_decompress.c \ $(ZLIB_SOURCES) endif +ifneq (${ENABLE_STACK_PROTECTOR},0) +BL_COMMON_SOURCES += plat/renesas/rcar/rcar_stack_protector.c +endif + ifeq (${RCAR_GEN3_ULCB},1) BL31_SOURCES += drivers/renesas/rcar/cpld/ulcb_cpld.c endif @@ -345,13 +351,13 @@ distclean realclean clean: clean_layout_tool clean_srecord LAYOUT_TOOLPATH ?= tools/renesas/rcar_layout_create clean_layout_tool: - @echo "clean layout tool" - ${Q}${MAKE} -C ${LAYOUT_TOOLPATH} clean + $(s)echo "clean layout tool" + $(q)${MAKE} -C ${LAYOUT_TOOLPATH} clean .PHONY: rcar_layout_tool rcar_layout_tool: - @echo "generating layout srecs" - ${Q}${MAKE} CPPFLAGS="-D=AARCH64" --no-print-directory -C ${LAYOUT_TOOLPATH} + $(s)echo "generating layout srecs" + $(q)${MAKE} CPPFLAGS="-D=AARCH64" --no-print-directory -C ${LAYOUT_TOOLPATH} # srecords SREC_PATH = ${BUILD_PLAT} @@ -359,13 +365,16 @@ BL2_ELF_SRC = ${SREC_PATH}/bl2/bl2.elf BL31_ELF_SRC = ${SREC_PATH}/bl31/bl31.elf clean_srecord: - @echo "clean bl2 and bl31 srecs" + $(s)echo "clean bl2 and bl31 srecs" rm -f ${SREC_PATH}/bl2.srec ${SREC_PATH}/bl31.srec -.PHONY: rcar_srecord -rcar_srecord: $(BL2_ELF_SRC) $(BL31_ELF_SRC) - @echo "generating srec: ${SREC_PATH}/bl2.srec" - $(Q)$(OC) -O srec --srec-forceS3 ${BL2_ELF_SRC} ${SREC_PATH}/bl2.srec - @echo "generating srec: ${SREC_PATH}/bl31.srec" - $(Q)$(OC) -O srec --srec-forceS3 ${BL31_ELF_SRC} ${SREC_PATH}/bl31.srec +$(SREC_PATH)/bl2.srec: $(BL2_ELF_SRC) + $(s)echo "generating srec: $(SREC_PATH)/bl2.srec" + $(q)$($(ARCH)-oc) -O srec --srec-forceS3 $(BL2_ELF_SRC) $(SREC_PATH)/bl2.srec + +$(SREC_PATH)/bl31.srec: $(BL31_ELF_SRC) + $(s)echo "generating srec: $(SREC_PATH)/bl31.srec" + $(q)$($(ARCH)-oc) -O srec --srec-forceS3 $(BL31_ELF_SRC) $(SREC_PATH)/bl31.srec +.PHONY: rcar_srecord +rcar_srecord: $(SREC_PATH)/bl2.srec $(SREC_PATH)/bl31.srec diff --git a/plat/renesas/rcar/rcar_stack_protector.c b/plat/renesas/rcar/rcar_stack_protector.c new file mode 100644 index 00000000..ecceef4b --- /dev/null +++ b/plat/renesas/rcar/rcar_stack_protector.c @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2021-2023, Renesas Electronics Corporation. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <stdint.h> + +#include <arch_helpers.h> +#include <common/debug.h> + +#define RANDOM_CANARY_VALUE ((u_register_t)0xDFF5FC8A720E205EULL) + +u_register_t plat_get_stack_protector_canary(void) +{ + u_register_t cnt; + u_register_t seed; + u_register_t mul; + u_register_t ret; + uintptr_t val1 = (uintptr_t)__builtin_return_address(0U); + uintptr_t val2 = (uintptr_t)__builtin_frame_address(0U); + + cnt = read_cntpct_el0(); + seed = (cnt ^ RANDOM_CANARY_VALUE) & ULONG_MAX; + ret = seed; + + if ((ULONG_MAX/val1) > seed) { + mul = (u_register_t)(val1 * seed); + if ((mul < ULONG_MAX) && + ((ULONG_MAX - (u_register_t)mul) > val2)) { + ret = mul + val2; + } + } + + return ret; +} diff --git a/plat/renesas/rzg/platform.mk b/plat/renesas/rzg/platform.mk index f37d7d0c..5da60529 100644 --- a/plat/renesas/rzg/platform.mk +++ b/plat/renesas/rzg/platform.mk @@ -249,13 +249,13 @@ distclean realclean clean: clean_layout_tool clean_srecord LAYOUT_TOOLPATH ?= tools/renesas/rzg_layout_create clean_layout_tool: - @echo "clean layout tool" - ${Q}${MAKE} -C ${LAYOUT_TOOLPATH} clean + $(s)echo "clean layout tool" + $(q)${MAKE} -C ${LAYOUT_TOOLPATH} clean .PHONY: rzg_layout_create rzg_layout_create: - @echo "generating layout srecs" - ${Q}${MAKE} CPPFLAGS="-D=AARCH64" --no-print-directory -C ${LAYOUT_TOOLPATH} + $(s)echo "generating layout srecs" + $(q)${MAKE} CPPFLAGS="-D=AARCH64" --no-print-directory -C ${LAYOUT_TOOLPATH} # srecords SREC_PATH = ${BUILD_PLAT} @@ -263,12 +263,16 @@ BL2_ELF_SRC = ${SREC_PATH}/bl2/bl2.elf BL31_ELF_SRC = ${SREC_PATH}/bl31/bl31.elf clean_srecord: - @echo "clean bl2 and bl31 srecs" + $(s)echo "clean bl2 and bl31 srecs" rm -f ${SREC_PATH}/bl2.srec ${SREC_PATH}/bl31.srec +$(SREC_PATH)/bl2.srec: $(BL2_ELF_SRC) + $(s)echo "generating srec: $(SREC_PATH)/bl2.srec" + $(q)$($(ARCH)-oc) -O srec --srec-forceS3 $(BL2_ELF_SRC) $(SREC_PATH)/bl2.srec + +$(SREC_PATH)/bl31.srec: $(BL31_ELF_SRC) + $(s)echo "generating srec: $(SREC_PATH)/bl31.srec" + $(q)$($(ARCH)-oc) -O srec --srec-forceS3 $(BL31_ELF_SRC) $(SREC_PATH)/bl31.srec + .PHONY: rzg_srecord -rzg_srecord: $(BL2_ELF_SRC) $(BL31_ELF_SRC) - @echo "generating srec: ${SREC_PATH}/bl2.srec" - $(Q)$(OC) -O srec --srec-forceS3 ${BL2_ELF_SRC} ${SREC_PATH}/bl2.srec - @echo "generating srec: ${SREC_PATH}/bl31.srec" - $(Q)$(OC) -O srec --srec-forceS3 ${BL31_ELF_SRC} ${SREC_PATH}/bl31.srec +rzg_srecord: $(SREC_PATH)/bl2.srec $(SREC_PATH)/bl31.srec diff --git a/plat/rockchip/common/aarch32/platform_common.c b/plat/rockchip/common/aarch32/platform_common.c index 9030951e..25ee964f 100644 --- a/plat/rockchip/common/aarch32/platform_common.c +++ b/plat/rockchip/common/aarch32/platform_common.c @@ -12,7 +12,7 @@ #include <common/bl_common.h> #include <common/debug.h> #include <lib/utils.h> -#include <lib/xlat_tables/xlat_tables.h> +#include <lib/xlat_tables/xlat_tables_compat.h> #include <plat_private.h> diff --git a/plat/rockchip/common/aarch64/plat_helpers.S b/plat/rockchip/common/aarch64/plat_helpers.S index c4c0dec3..9b8c9711 100644 --- a/plat/rockchip/common/aarch64/plat_helpers.S +++ b/plat/rockchip/common/aarch64/plat_helpers.S @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013-2016, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2013-2024, ARM Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -32,6 +32,17 @@ * */ func plat_reset_handler +#ifdef PLAT_RK_CPU_RESET_EARLY + mov x18, x30 + msr spsel, #0 + bl plat_set_my_stack + mov x0, x20 + mov x1, x21 + mov x2, x22 + mov x3, x23 + bl rockchip_cpu_reset_early + mov x30, x18 +#endif mrs x0, midr_el1 ubfx x0, x0, MIDR_PN_SHIFT, #12 cmp w0, #((CORTEX_A72_MIDR >> MIDR_PN_SHIFT) & MIDR_PN_MASK) @@ -150,7 +161,12 @@ endfunc platform_cpu_warmboot * Per-CPU Secure entry point - resume or power up * -------------------------------------------------------------------- */ + +#if USE_COHERENT_MEM .section .tzfw_coherent_mem, "a" +#else + .data +#endif .align 3 cpuson_entry_point: .rept PLATFORM_CORE_COUNT diff --git a/plat/rockchip/common/aarch64/platform_common.c b/plat/rockchip/common/aarch64/platform_common.c index 81e85206..df51d895 100644 --- a/plat/rockchip/common/aarch64/platform_common.c +++ b/plat/rockchip/common/aarch64/platform_common.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013-2016, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2013-2023, ARM Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -13,7 +13,7 @@ #include <common/debug.h> #include <drivers/arm/cci.h> #include <lib/utils.h> -#include <lib/xlat_tables/xlat_tables.h> +#include <lib/xlat_tables/xlat_tables_compat.h> #include <plat_private.h> @@ -42,9 +42,10 @@ static const int cci_map[] = { mmap_add_region(ro_start, ro_start, \ ro_limit - ro_start, \ MT_MEMORY | MT_RO | MT_SECURE); \ - mmap_add_region(coh_start, coh_start, \ - coh_limit - coh_start, \ - MT_DEVICE | MT_RW | MT_SECURE); \ + if ((coh_limit - coh_start) != 0) \ + mmap_add_region(coh_start, coh_start, \ + coh_limit - coh_start, \ + MT_DEVICE | MT_RW | MT_SECURE); \ mmap_add(plat_rk_mmap); \ rockchip_plat_mmu_el##_el(); \ init_xlat_tables(); \ diff --git a/plat/rockchip/common/bl31_plat_setup.c b/plat/rockchip/common/bl31_plat_setup.c index 59db3d85..62147226 100644 --- a/plat/rockchip/common/bl31_plat_setup.c +++ b/plat/rockchip/common/bl31_plat_setup.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016-2019, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2016-2023, ARM Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -93,10 +93,19 @@ void bl31_plat_arch_setup(void) { plat_cci_init(); plat_cci_enable(); +#if USE_COHERENT_MEM plat_configure_mmu_el3(BL_CODE_BASE, BL_COHERENT_RAM_END - BL_CODE_BASE, BL_CODE_BASE, BL_CODE_END, BL_COHERENT_RAM_BASE, BL_COHERENT_RAM_END); +#else + plat_configure_mmu_el3(BL31_START, + BL31_END - BL31_START, + BL_CODE_BASE, + BL_CODE_END, + 0, + 0); +#endif } diff --git a/plat/rockchip/common/include/plat_macros.S b/plat/rockchip/common/include/plat_macros.S index 691beeb4..548e3d90 100644 --- a/plat/rockchip/common/include/plat_macros.S +++ b/plat/rockchip/common/include/plat_macros.S @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2016, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2014-2024, ARM Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -23,8 +23,8 @@ icc_regs: /* Registers common to both GICv2 and GICv3 */ gicd_pend_reg: - .asciz "gicd_ispendr regs (Offsets 0x200 - 0x278)\n" \ - " Offset:\t\t\tvalue\n" + .ascii "gicd_ispendr regs (Offsets 0x200 - 0x278)\n" \ + " Offset:\t\t\tvalue\n\0" newline: .asciz "\n" spacer: diff --git a/plat/rockchip/common/include/plat_pm_helpers.h b/plat/rockchip/common/include/plat_pm_helpers.h new file mode 100644 index 00000000..2204a65d --- /dev/null +++ b/plat/rockchip/common/include/plat_pm_helpers.h @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2024, Rockchip, Inc. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef PLAT_PM_HELPERS_H +#define PLAT_PM_HELPERS_H + +#include <stdint.h> + +/** + * Use this macro to define a register region. + * start: start offset from the base address. + * end: end offset from the base address. + * stride: stride of registers in region. + * base: base address of registers in region. + * wmsk: write mask of registers in region. + */ +#define REG_REGION(_start, _end, _stride, _base, _wmsk) \ +{ \ + .start = (_base) + (_start), \ + .end = (_base) + (_end), \ + .stride = _stride, \ + .wmsk = _wmsk \ +} + +struct reg_region { + /* Start address of region */ + uint32_t start; + /* End address of region */ + uint32_t end; + /* Stride of registers in region */ + uint32_t stride; + /* Write mask of registers in region */ + uint32_t wmsk; + /* Buffer to save/restore registers in region */ + uint32_t *buf; +}; + +void rockchip_alloc_region_mem(struct reg_region *rgns, uint32_t rgn_num); +void rockchip_reg_rgn_save(struct reg_region *rgns, uint32_t rgn_num); +void rockchip_reg_rgn_restore(struct reg_region *rgns, uint32_t rgn_num); +void rockchip_reg_rgn_restore_reverse(struct reg_region *rgns, uint32_t rgn_num); +void rockchip_regs_dump(uint32_t base, + uint32_t start_offset, + uint32_t end_offset, + uint32_t stride); +void rockchip_dump_reg_rgns(struct reg_region *rgns, uint32_t rgn_num); + +#endif /* PLAT_PM_HELPERS_H */ diff --git a/plat/rockchip/common/include/plat_private.h b/plat/rockchip/common/include/plat_private.h index 990d1065..1e13a9e8 100644 --- a/plat/rockchip/common/include/plat_private.h +++ b/plat/rockchip/common/include/plat_private.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2019, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2014-2023, ARM Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -11,9 +11,9 @@ #include <stdint.h> -#include <lib/psci/psci.h> -#include <lib/xlat_tables/xlat_tables.h> #include <lib/mmio.h> +#include <lib/psci/psci.h> +#include <lib/xlat_tables/xlat_tables_compat.h> #include <plat_params.h> #define __sramdata __attribute__((section(".sram.data"))) @@ -49,7 +49,7 @@ extern uint32_t __sram_incbin_real_end; #endif #ifndef BITS_SHIFT -#define BITS_SHIFT(bits, shift) (bits << (shift)) +#define BITS_SHIFT(bits, shift) ((bits) << (shift)) #endif #ifndef BITS_WITH_WMASK @@ -135,13 +135,13 @@ extern const unsigned char rockchip_power_domain_tree_desc[]; extern void *pmu_cpuson_entrypoint; extern u_register_t cpuson_entry_point[PLATFORM_CORE_COUNT]; extern uint32_t cpuson_flags[PLATFORM_CORE_COUNT]; - extern const mmap_region_t plat_rk_mmap[]; uint32_t rockchip_get_uart_base(void); uint32_t rockchip_get_uart_baudrate(void); uint32_t rockchip_get_uart_clock(void); +void rockchip_init_scmi_server(void); #endif /* __ASSEMBLER__ */ /****************************************************************************** diff --git a/plat/rockchip/common/include/rockchip_sip_svc.h b/plat/rockchip/common/include/rockchip_sip_svc.h index 340d6538..8836f9b5 100644 --- a/plat/rockchip/common/include/rockchip_sip_svc.h +++ b/plat/rockchip/common/include/rockchip_sip_svc.h @@ -11,6 +11,7 @@ #define SIP_SVC_CALL_COUNT 0x8200ff00 #define SIP_SVC_UID 0x8200ff01 #define SIP_SVC_VERSION 0x8200ff03 +#define RK_SIP_SCMI_AGENT0 0x82000010 /* rockchip SiP Service Calls version numbers */ #define RK_SIP_SVC_VERSION_MAJOR 0x0 diff --git a/plat/rockchip/common/plat_pm_helpers.c b/plat/rockchip/common/plat_pm_helpers.c new file mode 100644 index 00000000..191b0cae --- /dev/null +++ b/plat/rockchip/common/plat_pm_helpers.c @@ -0,0 +1,213 @@ +/* + * Copyright (c) 2024, Rockchip, Inc. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <assert.h> +#include <errno.h> + +#include <arch_helpers.h> +#include <bl31/bl31.h> +#include <common/debug.h> +#include <drivers/console.h> +#include <drivers/delay_timer.h> +#include <lib/mmio.h> +#include <plat/common/platform.h> +#include <platform_def.h> + +#include <plat_pm_helpers.h> + +#define ROCKCHIP_PM_REG_REGION_MEM_LEN (ROCKCHIP_PM_REG_REGION_MEM_SIZE / sizeof(uint32_t)) + +/* REG region */ +#define RGN_LEN(_rgn) (((_rgn)->end - (_rgn)->start) / (_rgn)->stride + 1) + +#ifndef ROCKCHIP_PM_REG_REGION_MEM_SIZE +#define ROCKCHIP_PM_REG_REGION_MEM_SIZE 0 +#endif + +#ifdef ROCKCHIP_REG_RGN_MEM_BASE +static uint32_t *region_mem = (uint32_t *)ROCKCHIP_REG_RGN_MEM_BASE; +#else +static uint32_t region_mem[ROCKCHIP_PM_REG_REGION_MEM_LEN]; +#endif + +static int region_mem_idx; + +static int alloc_region_mem(uint32_t *buf, int max_len, + struct reg_region *rgns, uint32_t rgn_num) +{ + int i; + int total_len = 0, len = 0; + struct reg_region *r = rgns; + + assert(buf && rgns && rgn_num); + + for (i = 0; i < rgn_num; i++, r++) { + if (total_len < max_len) + r->buf = &buf[total_len]; + + len = RGN_LEN(r); + total_len += len; + } + + if (total_len > max_len) { + ERROR("%s The buffer remain length:%d is too small for region:0x%x, at least %d\n", + __func__, max_len, rgns[0].start, total_len); + panic(); + } + + return total_len; +} + +/** + * Alloc memory to reg_region->buf from region_mem. + * @rgns - struct reg_region array. + * @rgn_num - struct reg_region array length. + */ +void rockchip_alloc_region_mem(struct reg_region *rgns, uint32_t rgn_num) +{ + int max_len = 0, len; + + assert(rgns && rgn_num); + + max_len = ROCKCHIP_PM_REG_REGION_MEM_LEN - region_mem_idx; + + len = alloc_region_mem(region_mem + region_mem_idx, max_len, + rgns, rgn_num); + + region_mem_idx += len; +} + +/** + * Save (reg_region->start ~ reg_region->end) to reg_region->buf. + * @rgns - struct reg_region array. + * @rgn_num - struct reg_region array length. + */ +void rockchip_reg_rgn_save(struct reg_region *rgns, uint32_t rgn_num) +{ + struct reg_region *r; + uint32_t addr; + int i, j; + + assert(rgns && rgn_num); + + for (i = 0; i < rgn_num; i++) { + r = &rgns[i]; + for (j = 0, addr = r->start; addr <= r->end; addr += r->stride, j++) + r->buf[j] = mmio_read_32(addr); + } +} + +/** + * Restore reg_region->buf to (reg_region->start ~ reg_region->end). + * @rgns - struct reg_region array. + * @rgn_num - struct reg_region array length. + */ +void rockchip_reg_rgn_restore(struct reg_region *rgns, uint32_t rgn_num) +{ + struct reg_region *r; + uint32_t addr; + int i, j; + + assert(rgns && rgn_num); + + for (i = 0; i < rgn_num; i++) { + r = &rgns[i]; + for (j = 0, addr = r->start; addr <= r->end; addr += r->stride, j++) + mmio_write_32(addr, r->buf[j] | r->wmsk); + + dsb(); + } +} + +/** + * Restore reg_region->buf to (reg_region->start ~ reg_region->end) reversely. + * @rgns - struct reg_region array. + * @rgn_num - struct reg_region array length. + */ +void rockchip_reg_rgn_restore_reverse(struct reg_region *rgns, uint32_t rgn_num) +{ + struct reg_region *r; + uint32_t addr; + int i, j; + + assert(rgns && rgn_num); + + for (i = rgn_num - 1; i >= 0; i--) { + r = &rgns[i]; + j = RGN_LEN(r) - 1; + for (addr = r->end; addr >= r->start; addr -= r->stride, j--) + mmio_write_32(addr, r->buf[j] | r->wmsk); + + dsb(); + } +} + +static void rockchip_print_hex(uint32_t val) +{ + int i; + unsigned char tmp; + + putchar('0'); + putchar('x'); + for (i = 0; i < 8; val <<= 4, ++i) { + tmp = (val & 0xf0000000) >> 28; + if (tmp < 10) + putchar('0' + tmp); + else + putchar('a' + tmp - 10); + } +} + +/** + * Dump registers (base + start_offset ~ base + end_offset) + * @base - the base addr of the register. + * @start_offset - the start offset to dump. + * @end_offset - the end offset to dump. + * @stride - the stride of the registers. + */ +void rockchip_regs_dump(uint32_t base, + uint32_t start_offset, + uint32_t end_offset, + uint32_t stride) +{ + uint32_t i; + + for (i = start_offset; i <= end_offset; i += stride) { + if ((i - start_offset) % 16 == 0) { + putchar('\n'); + rockchip_print_hex(base + i); + putchar(':'); + putchar(' '); + putchar(' '); + putchar(' '); + putchar(' '); + } + rockchip_print_hex(mmio_read_32(base + i)); + putchar(' '); + putchar(' '); + putchar(' '); + putchar(' '); + } + putchar('\n'); +} + +/** + * Dump reg regions + * @rgns - struct reg_region array. + * @rgn_num - struct reg_region array length. + */ +void rockchip_dump_reg_rgns(struct reg_region *rgns, uint32_t rgn_num) +{ + struct reg_region *r; + int i; + + assert(rgns && rgn_num); + + for (i = 0; i < rgn_num; i++) { + r = &rgns[i]; + rockchip_regs_dump(0x0, r->start, r->end, r->stride); + } +} diff --git a/plat/rockchip/common/scmi/scmi.c b/plat/rockchip/common/scmi/scmi.c new file mode 100644 index 00000000..5c43c511 --- /dev/null +++ b/plat/rockchip/common/scmi/scmi.c @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2024, Rockchip, Inc. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <assert.h> +#include <string.h> + +#include <platform_def.h> + +#include <drivers/scmi-msg.h> +#include <drivers/scmi.h> +#include <lib/utils.h> +#include <lib/utils_def.h> + +#define MAX_PROTOCOL_IN_LIST 8U + +static const char vendor[] = "rockchip"; +static const char sub_vendor[] = ""; + +#pragma weak rockchip_scmi_protocol_table + +const uint8_t rockchip_scmi_protocol_table[1][MAX_PROTOCOL_IN_LIST] = { + { + SCMI_PROTOCOL_ID_CLOCK, + SCMI_PROTOCOL_ID_RESET_DOMAIN, + 0 + } +}; + +const char *plat_scmi_vendor_name(void) +{ + return vendor; +} + +const char *plat_scmi_sub_vendor_name(void) +{ + return sub_vendor; +} + +size_t plat_scmi_protocol_count(void) +{ + unsigned int count = 0U; + const uint8_t *protocol_list = rockchip_scmi_protocol_table[0]; + + while (protocol_list[count]) + count++; + + return count; +} + +const uint8_t *plat_scmi_protocol_list(unsigned int agent_id) +{ + assert(agent_id < ARRAY_SIZE(rockchip_scmi_protocol_table)); + + return rockchip_scmi_protocol_table[agent_id]; +} + +static struct scmi_msg_channel scmi_channel[] = { + [0] = { + .shm_addr = SMT_BUFFER0_BASE, + .shm_size = SMT_BUF_SLOT_SIZE, + }, + +#ifdef SMT_BUFFER1_BASE + [1] = { + .shm_addr = SMT_BUFFER1_BASE, + .shm_size = SMT_BUF_SLOT_SIZE, + }, +#endif +}; + +struct scmi_msg_channel *plat_scmi_get_channel(unsigned int agent_id) +{ + assert(agent_id < ARRAY_SIZE(scmi_channel)); + + return &scmi_channel[agent_id]; +} + +#pragma weak rockchip_init_scmi_server + +void rockchip_init_scmi_server(void) +{ + size_t i; + + for (i = 0U; i < ARRAY_SIZE(scmi_channel); i++) + scmi_smt_init_agent_channel(&scmi_channel[i]); +} diff --git a/plat/rockchip/common/scmi/scmi_clock.c b/plat/rockchip/common/scmi/scmi_clock.c new file mode 100644 index 00000000..d6d4b375 --- /dev/null +++ b/plat/rockchip/common/scmi/scmi_clock.c @@ -0,0 +1,157 @@ +/* + * Copyright (c) 2024, Rockchip, Inc. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <drivers/scmi-msg.h> +#include <drivers/scmi.h> + +#include "scmi_clock.h" + +#pragma weak rockchip_scmi_clock_count +#pragma weak rockchip_scmi_get_clock + +size_t rockchip_scmi_clock_count(unsigned int agent_id __unused) +{ + return 0; +} + +rk_scmi_clock_t *rockchip_scmi_get_clock(uint32_t agent_id __unused, + uint32_t scmi_id __unused) +{ + return NULL; +} + +size_t plat_scmi_clock_count(unsigned int agent_id) +{ + return rockchip_scmi_clock_count(agent_id); +} + +const char *plat_scmi_clock_get_name(unsigned int agent_id, + unsigned int scmi_id) +{ + rk_scmi_clock_t *clock; + + clock = rockchip_scmi_get_clock(agent_id, scmi_id); + if (clock == NULL) + return NULL; + + return clock->name; +} + +int32_t plat_scmi_clock_rates_array(unsigned int agent_id, + unsigned int scmi_id, + unsigned long *rates, + size_t *nb_elts, + uint32_t start_idx) +{ + uint32_t i; + unsigned long *rate_table; + rk_scmi_clock_t *clock; + + clock = rockchip_scmi_get_clock(agent_id, scmi_id); + if (clock == NULL) + return SCMI_NOT_FOUND; + + rate_table = clock->rate_table; + if (rate_table == NULL) + return SCMI_NOT_SUPPORTED; + + if (rates == 0) { + *nb_elts = clock->rate_cnt; + goto out; + } + + if (start_idx + *nb_elts > clock->rate_cnt) + return SCMI_OUT_OF_RANGE; + + for (i = 0; i < *nb_elts; i++) + rates[i] = rate_table[start_idx + i]; + +out: + return SCMI_SUCCESS; +} + +int32_t plat_scmi_clock_rates_by_step(unsigned int agent_id __unused, + unsigned int scmi_id __unused, + unsigned long *steps __unused) +{ + return SCMI_NOT_SUPPORTED; +} + +unsigned long plat_scmi_clock_get_rate(unsigned int agent_id, + unsigned int scmi_id) +{ + rk_scmi_clock_t *clock; + unsigned long rate = 0; + + clock = rockchip_scmi_get_clock(agent_id, scmi_id); + if (clock == NULL) + return 0; + + if (clock->clk_ops && clock->clk_ops->get_rate) + rate = clock->clk_ops->get_rate(clock); + + /* return cur_rate if no get_rate ops or get_rate return 0 */ + if (rate == 0) + rate = clock->cur_rate; + + return rate; +} + +int32_t plat_scmi_clock_set_rate(unsigned int agent_id, + unsigned int scmi_id, + unsigned long rate) +{ + rk_scmi_clock_t *clock; + int32_t status = 0; + + clock = rockchip_scmi_get_clock(agent_id, scmi_id); + if (clock == NULL) + return SCMI_NOT_FOUND; + + if (clock->clk_ops && clock->clk_ops->set_rate) { + status = clock->clk_ops->set_rate(clock, rate); + if (status == SCMI_SUCCESS) + clock->cur_rate = rate; + } else { + status = SCMI_NOT_SUPPORTED; + } + + return status; +} + +int32_t plat_scmi_clock_get_state(unsigned int agent_id, + unsigned int scmi_id) +{ + rk_scmi_clock_t *clock; + + clock = rockchip_scmi_get_clock(agent_id, scmi_id); + if (clock == NULL) + return 0; + + return clock->enable; +} + +int32_t plat_scmi_clock_set_state(unsigned int agent_id, + unsigned int scmi_id, + bool enable_not_disable) +{ + rk_scmi_clock_t *clock; + int32_t status = 0; + + clock = rockchip_scmi_get_clock(agent_id, scmi_id); + if (clock == NULL) + return SCMI_NOT_FOUND; + + if (clock->clk_ops && clock->clk_ops->set_status) { + status = clock->clk_ops->set_status(clock, enable_not_disable); + if (status == SCMI_SUCCESS) + clock->enable = enable_not_disable; + } else { + status = SCMI_NOT_SUPPORTED; + } + + return status; +} diff --git a/plat/rockchip/common/scmi/scmi_clock.h b/plat/rockchip/common/scmi/scmi_clock.h new file mode 100644 index 00000000..e640fe19 --- /dev/null +++ b/plat/rockchip/common/scmi/scmi_clock.h @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2024, Rockchip, Inc. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef RK_SCMI_CLOCK_H +#define RK_SCMI_CLOCK_H + +#include <stdint.h> + +#include <common.h> + +struct rk_scmi_clock; + +struct rk_clk_ops { + unsigned long (*get_rate)(struct rk_scmi_clock *clock); + int (*set_rate)(struct rk_scmi_clock *clock, unsigned long rate); + int (*set_status)(struct rk_scmi_clock *clock, bool status); +}; + +typedef struct rk_scmi_clock { + char name[SCMI_CLOCK_NAME_LENGTH_MAX]; + uint8_t enable; + int8_t is_security; + uint32_t id; + uint32_t rate_cnt; + uint64_t cur_rate; + uint32_t enable_count; + const struct rk_clk_ops *clk_ops; + unsigned long *rate_table; +} rk_scmi_clock_t; + +/* + * Return number of clock controllers for an agent + * @agent_id: SCMI agent ID + * Return number of clock controllers + */ +size_t rockchip_scmi_clock_count(unsigned int agent_id); + +/* + * Get rk_scmi_clock_t point + * @agent_id: SCMI agent ID + * @scmi_id: SCMI clock ID + * Return a rk_scmi_clock_t point + */ +rk_scmi_clock_t *rockchip_scmi_get_clock(uint32_t agent_id, + uint32_t scmi_id); + +#endif /* RK_SCMI_CLOCK_H */ diff --git a/plat/rockchip/common/scmi/scmi_rstd.c b/plat/rockchip/common/scmi/scmi_rstd.c new file mode 100644 index 00000000..35c5e0b2 --- /dev/null +++ b/plat/rockchip/common/scmi/scmi_rstd.c @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2024, Rockchip, Inc. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <drivers/scmi-msg.h> +#include <drivers/scmi.h> + +#include "scmi_rstd.h" + +#pragma weak rockchip_scmi_rstd_count +#pragma weak rockchip_scmi_get_rstd + +size_t rockchip_scmi_rstd_count(unsigned int agent_id __unused) +{ + return 0U; +} + +rk_scmi_rstd_t *rockchip_scmi_get_rstd(unsigned int agent_id __unused, + unsigned int scmi_id __unused) +{ + return NULL; +} + +size_t plat_scmi_rstd_count(unsigned int agent_id) +{ + return rockchip_scmi_rstd_count(agent_id); +} + +const char *plat_scmi_rstd_get_name(unsigned int agent_id, + unsigned int scmi_id) +{ + rk_scmi_rstd_t *rstd; + + rstd = rockchip_scmi_get_rstd(agent_id, scmi_id); + if (rstd == NULL) + return NULL; + + return rstd->name; +} + +int32_t plat_scmi_rstd_autonomous(unsigned int agent_id, + unsigned int scmi_id, + unsigned int state) +{ + rk_scmi_rstd_t *rstd; + + rstd = rockchip_scmi_get_rstd(agent_id, scmi_id); + if (rstd == NULL) + return SCMI_NOT_FOUND; + + if ((rstd->rstd_ops && rstd->rstd_ops->reset_auto) != 0) + return rstd->rstd_ops->reset_auto(rstd, state); + else + return SCMI_NOT_SUPPORTED; +} + +int32_t plat_scmi_rstd_set_state(unsigned int agent_id, + unsigned int scmi_id, + bool assert_not_deassert) +{ + rk_scmi_rstd_t *rstd; + + rstd = rockchip_scmi_get_rstd(agent_id, scmi_id); + if (rstd == NULL) + return SCMI_NOT_FOUND; + + if ((rstd->rstd_ops && rstd->rstd_ops->reset_explicit) != 0) + return rstd->rstd_ops->reset_explicit(rstd, + assert_not_deassert); + else + return SCMI_NOT_SUPPORTED; +} diff --git a/plat/rockchip/common/scmi/scmi_rstd.h b/plat/rockchip/common/scmi/scmi_rstd.h new file mode 100644 index 00000000..1af58813 --- /dev/null +++ b/plat/rockchip/common/scmi/scmi_rstd.h @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2024, Rockchip, Inc. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef RK_SCMI_RESET_DOMAIN_H +#define RK_SCMI_RESET_DOMAIN_H + +#include <stdint.h> + +#include <common.h> + +struct rk_scmi_rstd; + +struct rk_scmi_rstd_ops { + int (*reset_auto)(struct rk_scmi_rstd *rstd, uint32_t state); + int (*reset_explicit)(struct rk_scmi_rstd *rstd, bool assert_not_deassert); +}; + +typedef struct rk_scmi_rstd { + char name[SCMI_RESET_DOMAIN_ATTR_NAME_SZ]; + uint32_t id; + uint32_t attribute; + uint32_t latency; + struct rk_scmi_rstd_ops *rstd_ops; +} rk_scmi_rstd_t; + +/* + * Return number of reset domain for an agent + * @agent_id: SCMI agent ID + * Return number of reset domain + */ +size_t rockchip_scmi_rstd_count(unsigned int agent_id); + +/* + * Get rk_scmi_rstd_t point + * @agent_id: SCMI agent ID + * @scmi_id: SCMI rstd ID + * Return a rk_scmi_rstd_t point + */ +rk_scmi_rstd_t *rockchip_scmi_get_rstd(unsigned int agent_id, + unsigned int scmi_id); + +#endif /* RK_SCMI_RESET_DOMAIN_H */ diff --git a/plat/rockchip/rk3328/platform.mk b/plat/rockchip/rk3328/platform.mk index 5b4766d5..f96e18bb 100644 --- a/plat/rockchip/rk3328/platform.mk +++ b/plat/rockchip/rk3328/platform.mk @@ -65,6 +65,7 @@ include lib/libfdt/libfdt.mk # Enable workarounds for selected Cortex-A53 errata ERRATA_A53_855873 := 1 +ERRATA_A53_1530924 := 1 $(eval $(call add_define,PLAT_EXTRA_LD_SCRIPT)) $(eval $(call add_define,PLAT_SKIP_OPTEE_S_EL1_INT_REGISTER)) diff --git a/plat/rockchip/rk3399/drivers/m0/Makefile b/plat/rockchip/rk3399/drivers/m0/Makefile index 79e09f0e..32446efb 100644 --- a/plat/rockchip/rk3399/drivers/m0/Makefile +++ b/plat/rockchip/rk3399/drivers/m0/Makefile @@ -1,9 +1,12 @@ # -# Copyright (c) 2016, ARM Limited and Contributors. All rights reserved. +# Copyright (c) 2016-2024, Arm Limited and Contributors. All rights reserved. # # SPDX-License-Identifier: BSD-3-Clause # +include ../../../../../make_helpers/common.mk +include ../../../../../make_helpers/toolchain.mk + # Cross Compile M0_CROSS_COMPILE ?= arm-none-eabi- @@ -14,13 +17,6 @@ ARCH := cortex-m0 PLAT_M0 ?= rk3399m0 PLAT_M0_PMU ?= rk3399m0pmu -ifeq (${V},0) - Q=@ -else - Q= -endif -export Q - .SUFFIXES: INCLUDES += -Iinclude/ \ @@ -38,14 +34,6 @@ CFLAGS := -ffunction-sections -fdata-sections -fomit-frame-pointer -fno-common ASFLAGS := -Wa,--gdwarf-2 LDFLAGS := -Wl,--gc-sections -Wl,--build-id=none -# Cross tool -CC := ${M0_CROSS_COMPILE}gcc -CPP := ${M0_CROSS_COMPILE}cpp -AR := ${M0_CROSS_COMPILE}ar -OC := ${M0_CROSS_COMPILE}objcopy -OD := ${M0_CROSS_COMPILE}objdump -NM := ${M0_CROSS_COMPILE}nm - # NOTE: The line continuation '\' is required in the next define otherwise we # end up with a line-feed characer at the end of the last c filename. # Also bare this issue in mind if extending the list of supported filetypes. @@ -75,16 +63,16 @@ $(eval OBJ := $(1)/$(patsubst %.c,%.o,$(notdir $(2)))) -include $(patsubst %.o,%.d,$(OBJ)) $(OBJ) : $(2) - @echo " CC $$<" - $$(Q)$$(CC) $$(COMMON_FLAGS) $$(CFLAGS) $$(INCLUDES) -MMD -MT $$@ -c $$< -o $$@ + $(s)echo " CC $$<" + $$(q)$(rk3399-m0-cc) $$(COMMON_FLAGS) $$(CFLAGS) $$(INCLUDES) -MMD -MT $$@ -c $$< -o $$@ endef define MAKE_S $(eval OBJ := $(1)/$(patsubst %.S,%.o,$(notdir $(2)))) $(OBJ) : $(2) - @echo " AS $$<" - $$(Q)$$(CC) -x assembler-with-cpp $$(COMMON_FLAGS) $$(ASFLAGS) -c $$< -o $$@ + $(s)echo " AS $$<" + $$(q)$(rk3399-m0-cc) -x assembler-with-cpp $$(COMMON_FLAGS) $$(ASFLAGS) -c $$< -o $$@ endef define MAKE_OBJS @@ -105,20 +93,20 @@ all: $(BIN) $(BIN_PMU) .DEFAULT_GOAL := all $(LINKERFILE): $(LINKERFILE_SRC) - $(CC) $(COMMON_FLAGS) $(INCLUDES) -P -E -D__LINKER__ -MMD -MF $@.d -MT $@ -o $@ $< + $(rk3399-m0-cc) $(COMMON_FLAGS) $(INCLUDES) -P -E -D__LINKER__ -MMD -MF $@.d -MT $@ -o $@ $< -include $(LINKERFILE).d $(ELF) : $(OBJS) $(OBJS_COMMON) $(LINKERFILE) - @echo " LD $@" - $(Q)$(CC) -o $@ $(COMMON_FLAGS) $(LDFLAGS) -Wl,-Map=$(MAPFILE) -Wl,-T$(LINKERFILE) $(OBJS) $(OBJS_COMMON) + $(s)echo " LD $@" + $(q)$(rk3399-m0-cc) -o $@ $(COMMON_FLAGS) $(LDFLAGS) -Wl,-Map=$(MAPFILE) -Wl,-T$(LINKERFILE) $(OBJS) $(OBJS_COMMON) %.bin : %.elf - @echo " BIN $@" - $(Q)$(OC) -O binary $< $@ + $(s)echo " BIN $@" + $(q)$(rk3399-m0-oc) -O binary $< $@ $(ELF_PMU) : $(OBJS_COMMON) $(OBJS_PMU) $(LINKERFILE) - @echo " LD $@" - $(Q)$(CC) -o $@ $(COMMON_FLAGS) $(LDFLAGS) -Wl,-Map=$(MAPFILE_PMU) -Wl,-T$(LINKERFILE) $(OBJS_PMU) $(OBJS_COMMON) + $(s)echo " LD $@" + $(q)$(rk3399-m0-cc) -o $@ $(COMMON_FLAGS) $(LDFLAGS) -Wl,-Map=$(MAPFILE_PMU) -Wl,-T$(LINKERFILE) $(OBJS_PMU) $(OBJS_COMMON) $(eval $(call MAKE_OBJS,$(BUILD),$(SOURCES_COMMON),$(1))) $(eval $(call MAKE_OBJS,$(BUILD),$(SOURCES),$(1))) diff --git a/plat/rockchip/rk3399/drivers/pmu/pmu_fw.S b/plat/rockchip/rk3399/drivers/pmu/pmu_fw.S new file mode 100644 index 00000000..26f33131 --- /dev/null +++ b/plat/rockchip/rk3399/drivers/pmu/pmu_fw.S @@ -0,0 +1,21 @@ +/* + * Copyright (c) 2024, ARM Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +/* convoluted way to make sure that the define is pasted just the right way */ +.macro INCBIN file sym sec + .section \sec + .global \sym + .type \sym, @object + .align 4 +\sym : + .incbin \file + .size \sym , .-\sym + .global \sym\()_end +\sym\()_end : +.endm + +INCBIN ""RK3399M0FW"", "rk3399m0_bin", ".sram.incbin" +INCBIN ""RK3399M0PMUFW"", "rk3399m0pmu_bin", ".pmusram.incbin" diff --git a/plat/rockchip/rk3399/drivers/pmu/pmu_fw.c b/plat/rockchip/rk3399/drivers/pmu/pmu_fw.c deleted file mode 100644 index 25596b18..00000000 --- a/plat/rockchip/rk3399/drivers/pmu/pmu_fw.c +++ /dev/null @@ -1,22 +0,0 @@ -/* - * Copyright (c) 2016, ARM Limited and Contributors. All rights reserved. - * - * SPDX-License-Identifier: BSD-3-Clause - */ - -/* convoluted way to make sure that the define is pasted just the right way */ -#define INCBIN(file, sym, sec) \ - __asm__( \ - ".section " sec "\n" \ - ".global " sym "\n" \ - ".type " sym ", %object\n" \ - ".align 4\n" \ - sym ":\n" \ - ".incbin \"" file "\"\n" \ - ".size " sym ", .-" sym "\n" \ - ".global " sym "_end\n" \ - sym "_end:\n" \ - ) - -INCBIN(RK3399M0FW, "rk3399m0_bin", ".sram.incbin"); -INCBIN(RK3399M0PMUFW, "rk3399m0pmu_bin", ".pmusram.incbin"); diff --git a/plat/rockchip/rk3399/platform.mk b/plat/rockchip/rk3399/platform.mk index aba67c2f..9d0e2157 100644 --- a/plat/rockchip/rk3399/platform.mk +++ b/plat/rockchip/rk3399/platform.mk @@ -1,5 +1,5 @@ # -# Copyright (c) 2016-2020, ARM Limited and Contributors. All rights reserved. +# Copyright (c) 2016-2024, ARM Limited and Contributors. All rights reserved. # # SPDX-License-Identifier: BSD-3-Clause # @@ -61,7 +61,7 @@ BL31_SOURCES += ${RK_GIC_SOURCES} \ ${RK_PLAT_SOC}/plat_sip_calls.c \ ${RK_PLAT_SOC}/drivers/gpio/rk3399_gpio.c \ ${RK_PLAT_SOC}/drivers/pmu/pmu.c \ - ${RK_PLAT_SOC}/drivers/pmu/pmu_fw.c \ + ${RK_PLAT_SOC}/drivers/pmu/pmu_fw.S \ ${RK_PLAT_SOC}/drivers/pmu/m0_ctl.c \ ${RK_PLAT_SOC}/drivers/pwm/pwm.c \ ${RK_PLAT_SOC}/drivers/secure/secure.c \ @@ -102,11 +102,10 @@ endif # CCACHE_EXTRAFILES is needed because ccache doesn't handle .incbin export CCACHE_EXTRAFILES ${BUILD_PLAT}/bl31/pmu_fw.o: CCACHE_EXTRAFILES=$(RK3399M0FW):$(RK3399M0PMUFW) -${RK_PLAT_SOC}/drivers/pmu/pmu_fw.c: $(RK3399M0FW) +${RK_PLAT_SOC}/drivers/pmu/pmu_fw.S: $(RK3399M0FW) -$(eval $(call MAKE_PREREQ_DIR,${BUILD_M0},${BUILD_PLAT})) .PHONY: $(RK3399M0FW) -$(RK3399M0FW): | ${BUILD_M0} +$(RK3399M0FW): | $$(@D)/ $(MAKE) -C ${RK_PLAT_SOC}/drivers/m0 BUILD=$(abspath ${BUILD_PLAT}/m0) # Do not enable SVE diff --git a/plat/rockchip/rk3568/drivers/pmu/plat_pmu_macros.S b/plat/rockchip/rk3568/drivers/pmu/plat_pmu_macros.S new file mode 100644 index 00000000..8ddea0ee --- /dev/null +++ b/plat/rockchip/rk3568/drivers/pmu/plat_pmu_macros.S @@ -0,0 +1,18 @@ +/* + * Copyright (c) 2023, ARM Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <arch.h> +#include <asm_macros.S> + +#include <platform_def.h> + +.macro func_rockchip_clst_warmboot + /* Nothing to do for rk3568 */ +.endm + +.macro rockchip_clst_warmboot_data + /* Nothing to do for rk3568 */ +.endm diff --git a/plat/rockchip/rk3568/drivers/pmu/pmu.c b/plat/rockchip/rk3568/drivers/pmu/pmu.c new file mode 100644 index 00000000..970caec5 --- /dev/null +++ b/plat/rockchip/rk3568/drivers/pmu/pmu.c @@ -0,0 +1,543 @@ +/* + * Copyright (c) 2023, ARM Limited and Contributors. All rights reserved. + * + * The power management unit (PMU) is designed for controlling power resources. + * The PMU is dedicated for managing the power of the whole chip. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <assert.h> +#include <errno.h> + +#include <bakery_lock.h> +#include <cortex_a55.h> +#include <dsu_def.h> +#include <mmio.h> +#include <platform.h> +#include <platform_def.h> +#include <pmu.h> + +#include <cpus_on_fixed_addr.h> +#include <plat_private.h> +#include <soc.h> + +/* + * Use this macro to instantiate lock before it is used in below + * rockchip_pd_lock_xxx() macros + */ +DECLARE_BAKERY_LOCK(rockchip_pd_lock); + +static uint32_t grf_ddr_con3; +static struct psram_data_t *psram_sleep_cfg = + (struct psram_data_t *)&sys_sleep_flag_sram; + +/* + * These are wrapper macros to the powe domain Bakery Lock API. + */ +#define rockchip_pd_lock_init() bakery_lock_init(&rockchip_pd_lock) +#define rockchip_pd_lock_get() bakery_lock_get(&rockchip_pd_lock) +#define rockchip_pd_lock_rls() bakery_lock_release(&rockchip_pd_lock) + +void __dead2 rockchip_soc_sys_pd_pwr_dn_wfi(void) +{ + uint64_t ctrl; + + __asm__ volatile ("mrs %0, " __XSTRING(CORTEX_A55_CPUPWRCTLR_EL1) : "=r" (ctrl)); + ctrl |= 0x01; + __asm__ volatile ("msr " __XSTRING(CORTEX_A55_CPUPWRCTLR_EL1) ", %0" : : "r" (ctrl)); + isb(); + + while (1) + wfi(); +} + +static void pmu_pmic_sleep_mode_config(void) +{ + /* pmic sleep function selection + * 1'b0: From reset pulse generator, can reset external PMIC + * 1'b1: From pmu block, only support sleep function for external PMIC + */ + mmio_write_32(PMUGRF_BASE + PMU_GRF_SOC_CON(0), WRITE_MASK_SET(BIT(7))); + mmio_write_32(PMUGRF_BASE + PMU_GRF_GPIO0A_IOMUX_L, PMIC_SLEEP_FUN); +} + +static void pmu_wakeup_source_config(void) +{ + /* config wakeup source */ + mmio_write_32(PMU_BASE + PMU_WAKEUP_INT_CON, WRITE_MASK_SET(BIT(WAKEUP_GPIO0_INT_EN))); + + INFO("WAKEUP: PMU_WAKEUP_INT_CON:0x%x, reg: 0x%x\n", + mmio_read_32(PMU_BASE + PMU_WAKEUP_INT_CON), PMU_WAKEUP_INT_CON); +} + +static void pmu_pll_powerdown_config(void) +{ + uint32_t pll_id; + + /* PLL power down by PMU */ + pll_id = BIT(APLL_PD_ENA) | + BIT(CPLL_PD_ENA) | + BIT(GPLL_PD_ENA) | + BIT(MPLL_PD_ENA) | + BIT(NPLL_PD_ENA) | + BIT(HPLL_PD_ENA) | + BIT(PPLL_PD_ENA) | + BIT(VPLL_PD_ENA); + mmio_write_32(PMU_BASE + PMU_PLLPD_CON, WRITE_MASK_SET(pll_id)); + INFO("PLL: PMU_PLLPD_CON(0x%x):0x%x\n", + PMU_PLLPD_CON, mmio_read_32(PMU_BASE + PMU_PLLPD_CON)); +} + +static void pmu_stable_count_config(void) +{ + mmio_write_32(PMU_BASE + PMU_DSU_STABLE_CNT, 0x180); + mmio_write_32(PMU_BASE + PMU_PMIC_STABLE_CNT, 0x180); + mmio_write_32(PMU_BASE + PMU_OSC_STABLE_CNT, 0x180); + mmio_write_32(PMU_BASE + PMU_WAKEUP_RSTCLR_CNT, 0x180); + mmio_write_32(PMU_BASE + PMU_PLL_LOCK_CNT, 0x180); + mmio_write_32(PMU_BASE + PMU_DSU_PWRUP_CNT, 0x180); + mmio_write_32(PMU_BASE + PMU_DSU_PWRDN_CNT, 0x180); + mmio_write_32(PMU_BASE + PMU_GPU_VOLUP_CNT, 0x180); + mmio_write_32(PMU_BASE + PMU_GPU_VOLDN_CNT, 0x180); + mmio_write_32(PMU_BASE + PMU_WAKEUP_TIMEOUT_CNT, 0x180); + mmio_write_32(PMU_BASE + PMU_PWM_SWITCH_CNT, 0x180); + mmio_write_32(PMU_BASE + PMU_DBG_RST_CNT, 0x180); +} + +static void pmu_pd_powerdown_config(void) +{ + uint32_t pwr_gate_con, pwr_dwn_st, pmu_bus_idle_con0 = 0; + uint32_t pmu_bus_idle_con1; + + /* Pd power down by PMU */ + pwr_dwn_st = mmio_read_32(PMU_BASE + PMU_PWR_DWN_ST); + pwr_gate_con = ~pwr_dwn_st & 0x3ff; + + if (pwr_gate_con & BIT(PD_GPU_DWN_ENA)) { + pmu_bus_idle_con0 |= BIT(IDLE_REQ_GPU); + } + + if (pwr_gate_con & BIT(PD_NPU_DWN_ENA)) { + pmu_bus_idle_con0 |= BIT(IDLE_REQ_NPU); + } + + if (pwr_gate_con & BIT(PD_RKVENC_DWN_ENA)) { + pmu_bus_idle_con0 |= BIT(IDLE_REQ_RKVENC); + } + + if (pwr_gate_con & BIT(PD_RKVDEC_DWN_ENA)) { + pmu_bus_idle_con0 |= BIT(IDLE_REQ_RKVDEC); + } + + if (pwr_gate_con & BIT(PD_RGA_DWN_ENA)) { + pmu_bus_idle_con0 |= BIT(IDLE_REQ_RGA); + } + + if (pwr_gate_con & BIT(PD_VI_DWN_ENA)) { + pmu_bus_idle_con0 |= BIT(IDLE_REQ_VI); + } + + if (pwr_gate_con & BIT(PD_VO_DWN_ENA)) { + pmu_bus_idle_con0 |= BIT(IDLE_REQ_VO); + } + + if (pwr_gate_con & BIT(PD_PIPE_DWN_ENA)) { + pmu_bus_idle_con0 |= BIT(IDLE_REQ_PIPE); + } + + pmu_bus_idle_con0 |= BIT(IDLE_REQ_GIC_AUDIO) | + BIT(IDLE_REQ_MSCH) | + BIT(IDLE_REQ_PHP) | + BIT(IDLE_REQ_SECURE_FLASH) | + BIT(IDLE_REQ_PERIMID) | + BIT(IDLE_REQ_USB) | + BIT(IDLE_REQ_BUS); + + /* Enable power down PD by PMU automatically */ + pwr_gate_con |= (BIT(PD_GPU_DWN_ENA) | + BIT(PD_NPU_DWN_ENA) | + BIT(PD_VPU_DWN_ENA) | + BIT(PD_RKVENC_DWN_ENA) | + BIT(PD_RKVDEC_DWN_ENA) | + BIT(PD_RGA_DWN_ENA) | + BIT(PD_VI_DWN_ENA) | + BIT(PD_VO_DWN_ENA) | + BIT(PD_PIPE_DWN_ENA)) << 16; + + pmu_bus_idle_con1 = 0; + + mmio_write_32(PMU_BASE + PMU_PWR_GATE_CON, pwr_gate_con); + mmio_write_32(PMU_BASE + PMU_BUS_IDLE_CON0, WRITE_MASK_SET(pmu_bus_idle_con0)); + mmio_write_32(PMU_BASE + PMU_BUS_IDLE_CON1, WRITE_MASK_SET(pmu_bus_idle_con1)); + + /* When perform idle operation, + * corresponding clock can be opened or gated automatically + */ + mmio_write_32(PMU_BASE + PMU_NOC_AUTO_CON0, 0xffffffff); + mmio_write_32(PMU_BASE + PMU_NOC_AUTO_CON1, 0x00070007); + + mmio_write_32(PMU_BASE + PMU_VOL_GATE_SFTCON, WRITE_MASK_SET(BIT(VD_NPU_ENA))); + + mmio_write_32(PMU_BASE + PMU_PWR_CON, WRITE_MASK_CLR(BIT(PWRDN_BYPASS))); + mmio_write_32(PMU_BASE + PMU_PWR_CON, WRITE_MASK_CLR(BIT(BUS_BYPASS))); + + INFO("PD & BUS:PMU_PWR_DWN_ST(0x%x):0x%x\n", + PMU_PWR_DWN_ST, mmio_read_32(PMU_BASE + PMU_PWR_DWN_ST)); + INFO("PD & BUS:PMU_PWR_GATE_CON(0x%x):0x%x\n", + PMU_PWR_GATE_CON, mmio_read_32(PMU_BASE + PMU_PWR_GATE_CON)); + INFO("PD & BUS:PMU_BUS_IDLE_CON0(0x%x):0x%x\n", + PMU_BUS_IDLE_CON0, mmio_read_32(PMU_BASE + PMU_BUS_IDLE_CON0)); + INFO("PD & BUS:PMU_BUS_IDLE_CON1(0x%x):0x%x\n", + PMU_BUS_IDLE_CON1, mmio_read_32(PMU_BASE + PMU_BUS_IDLE_CON1)); + INFO("PD & BUS:PMU_PWR_CON(0x%x):0x%x\n", + PMU_PWR_CON, mmio_read_32(PMU_BASE + PMU_PWR_CON)); +} + +static void pmu_ddr_suspend_config(void) +{ + uint32_t pmu_ddr_pwr_con; + + pmu_ddr_pwr_con = BIT(DDR_SREF_ENA) | + BIT(DDRIO_RET_ENTER_ENA) | + BIT(DDRIO_RET_EXIT_ENA) | + BIT(DDRPHY_AUTO_GATING_ENA); + + mmio_write_32(PMU_BASE + PMU_DDR_PWR_CON, WRITE_MASK_SET(pmu_ddr_pwr_con)); + /* DPLL power down by PMU */ + mmio_write_32(PMU_BASE + PMU_PLLPD_CON, WRITE_MASK_SET(BIT(DPLL_PD_ENA))); + mmio_write_32(PMU_BASE + PMU_PWR_CON, WRITE_MASK_CLR(BIT(DDR_BYPASS))); + + grf_ddr_con3 = mmio_read_32(DDRGRF_BASE + GRF_DDR_CON3); + + mmio_write_32(DDRGRF_BASE + GRF_DDR_CON3, 0x00600020); + + pmu_ddr_pwr_con = mmio_read_32(PMU_BASE + PMU_DDR_PWR_CON); + + INFO("DDR: PMU_PLLPD_CON(0x%x):0x%x\n", + PMU_PLLPD_CON, mmio_read_32(PMU_BASE + PMU_PLLPD_CON)); + INFO("DDR: PMU_DDR_PWR_CON(0x%x):\t0x%x\n", + PMU_DDR_PWR_CON, pmu_ddr_pwr_con); + + if (pmu_ddr_pwr_con & BIT(DDR_SREF_ENA)) { + INFO("\t DDR_SREF_ENA\n"); + } + + if (pmu_ddr_pwr_con & BIT(DDRIO_RET_ENTER_ENA)) { + INFO("\t DDRIO_RET_ENTER_ENA\n"); + } + + if (pmu_ddr_pwr_con & BIT(DDRIO_RET_EXIT_ENA)) { + INFO("\t DDRIO_RET_EXIT_ENA\n"); + } + + if (pmu_ddr_pwr_con & BIT(DDRPHY_AUTO_GATING_ENA)) { + INFO("\t DDRPHY_AUTO_GATING_ENA\n"); + } +} + +static void pmu_dsu_suspend_config(void) +{ + uint32_t pmu_dsu_pwr_con; + + pmu_dsu_pwr_con = BIT(DSU_PWRDN_ENA); + + mmio_write_32(PMU_BASE + PMU_CLUSTER_IDLE_CON, 0x000f000f); + mmio_write_32(PMU_BASE + PMU_DSU_PWR_CON, WRITE_MASK_SET(pmu_dsu_pwr_con)); + mmio_write_32(PMU_BASE + PMU_PWR_CON, WRITE_MASK_CLR(BIT(DSU_BYPASS))); + dsu_pwr_dwn(); + + INFO("DSU: PMU_DSU_PWR_CON(0x%x): 0x%x\n", + PMU_DSU_PWR_CON, mmio_read_32(PMU_BASE + PMU_DSU_PWR_CON)); + INFO("DSU: PMU_CLUSTER_IDLE_CON(0x%x),: 0x%x\n", + PMU_CLUSTER_IDLE_CON, mmio_read_32(PMU_BASE + PMU_CLUSTER_IDLE_CON)); + INFO("DSU: PMU_PWR_CON(0x%x),: 0x%x\n", + PMU_PWR_CON, mmio_read_32(PMU_BASE + PMU_PWR_CON)); +} + +static void pmu_cpu_powerdown_config(void) +{ + uint32_t pmu_cluster_pwr_st, cpus_state, cpus_bypass; + + pmu_cluster_pwr_st = mmio_read_32(PMU_BASE + PMU_CLUSTER_PWR_ST); + cpus_state = pmu_cluster_pwr_st & 0x0f; + + cpus_bypass = cpus_state << CPU0_BYPASS; + + INFO("CPU: PMU_CLUSTER_PWR_ST(0x%x):0x%x\n", + PMU_CLUSTER_PWR_ST, mmio_read_32(PMU_BASE + PMU_CLUSTER_PWR_ST)); + mmio_write_32(PMU_BASE + PMU_PWR_CON, (0xf << (16 + CPU0_BYPASS)) | cpus_bypass); + + INFO("CPU: PMU_PWR_CON(0x%x), 0x%x\n", + PMU_PWR_CON, mmio_read_32(PMU_BASE + PMU_PWR_CON)); +} + +static void pvtm_32k_config(void) +{ + uint32_t pmu_cru_pwr_con; + uint32_t pvtm_freq_khz, pvtm_div; + + mmio_write_32(PMUCRU_BASE + PMUCRU_PMUGATE_CON01, 0x38000000); + mmio_write_32(PMUPVTM_BASE + PVTM_CON0, 0x00020002); + dsb(); + + mmio_write_32(PMUPVTM_BASE + PVTM_CON0, 0x001c0000); + + mmio_write_32(PMUPVTM_BASE + PVTM_CON1, PVTM_CALC_CNT); + dsb(); + + mmio_write_32(PMUPVTM_BASE + PVTM_CON0, 0x00010001); + dsb(); + + while (mmio_read_32(PMUPVTM_BASE + PVTM_STATUS1) < 30) { + ; + } + + dsb(); + while (!(mmio_read_32(PMUPVTM_BASE + PVTM_STATUS0) & 0x1)) { + ; + } + + pvtm_freq_khz = (mmio_read_32(PMUPVTM_BASE + PVTM_STATUS1) * 24000 + + PVTM_CALC_CNT / 2) / PVTM_CALC_CNT; + pvtm_div = (pvtm_freq_khz + 16) / 32; + + mmio_write_32(PMUGRF_BASE + PMU_GRF_DLL_CON0, pvtm_div); + + mmio_write_32(PMUCRU_BASE + PMUCRU_PMUCLKSEL_CON00, 0x00c00000); + + pmu_cru_pwr_con = BIT(ALIVE_32K_ENA) | BIT(OSC_DIS_ENA); + + mmio_write_32(PMU_BASE + PMU_WAKEUP_TIMEOUT_CNT, 32000 * 10); + + mmio_write_32(PMU_BASE + PMU_CRU_PWR_CON, WRITE_MASK_SET(pmu_cru_pwr_con)); + INFO("PVTM: PMU_CRU_PWR_CON(0x0%x): 0x%x\n", + PMU_CRU_PWR_CON, mmio_read_32(PMU_BASE + PMU_CRU_PWR_CON)); +} + +static void pmu_cru_suspendmode_config(void) +{ + uint32_t pmu_cru_pwr_con; + + pmu_cru_pwr_con = BIT(ALIVE_OSC_ENA); + + mmio_write_32(PMU_BASE + PMU_CRU_PWR_CON, WRITE_MASK_SET(pmu_cru_pwr_con)); + INFO("CRU: PMU_CRU_PWR_CON(0x0%x): 0x%x\n", + PMU_CRU_PWR_CON, mmio_read_32(PMU_BASE + PMU_CRU_PWR_CON)); +} + +static void pmu_suspend_cru_fsm(void) +{ + pmu_pmic_sleep_mode_config(); + + /* Global interrupt disable */ + mmio_write_32(PMU_BASE + PMU_INT_MASK_CON, CLB_INT_DISABLE); + mmio_write_32(PMU_BASE + PMU_PWR_CON, CPUS_BYPASS); + + pmu_stable_count_config(); + pmu_wakeup_source_config(); + mmio_write_32(PMU_BASE + PMU_WAKEUP_TIMEOUT_CNT, 0x5dc0 * 20000); + /* default cru config */ + mmio_write_32(PMU_BASE + PMU_CRU_PWR_CON, WRITE_MASK_SET(BIT(ALIVE_OSC_ENA))); + + pmu_cru_suspendmode_config(); + pmu_cpu_powerdown_config(); + pmu_pll_powerdown_config(); + pmu_pd_powerdown_config(); + pmu_ddr_suspend_config(); + pmu_dsu_suspend_config(); + pvtm_32k_config(); + mmio_write_32(PMU_BASE + PMU_PWR_CON, 0x00010001); +} + +static void pmu_reinit(void) +{ + mmio_write_32(DDRGRF_BASE + GRF_DDR_CON3, grf_ddr_con3 | 0xffff0000); + mmio_write_32(PMU_BASE + PMU_PWR_CON, 0xffff0000); + mmio_write_32(PMU_BASE + PMU_INT_MASK_CON, 0xffff0000); + mmio_write_32(PMU_BASE + PMU_WAKEUP_INT_CON, 0xffff0000); + mmio_write_32(PMU_BASE + PMU_BUS_IDLE_CON0, 0xffff0000); + mmio_write_32(PMU_BASE + PMU_DDR_PWR_CON, 0xffff0000); + mmio_write_32(PMU_BASE + PMU_BUS_IDLE_CON1, 0xffff0000); + + mmio_write_32(PMU_BASE + PMU_PWR_GATE_CON, 0xffff0000); + mmio_write_32(PMU_BASE + PMU_VOL_GATE_SFTCON, 0xffff0000); + mmio_write_32(PMU_BASE + PMU_CRU_PWR_CON, 0xffff0000); + + mmio_write_32(PMU_BASE + PMU_PLLPD_CON, 0xffff0000); + mmio_write_32(PMU_BASE + PMU_INFO_TX_CON, 0xffff0000); + mmio_write_32(PMU_BASE + PMU_DSU_PWR_CON, 0xffff0000); + mmio_write_32(PMU_BASE + PMU_CLUSTER_IDLE_CON, 0xffff0000); +} + +void rockchip_plat_mmu_el3(void) +{ +} + +int rockchip_soc_cores_pwr_dm_suspend(void) +{ + return 0; +} + +int rockchip_soc_cores_pwr_dm_resume(void) +{ + return 0; +} + +int rockchip_soc_sys_pwr_dm_suspend(void) +{ + psram_sleep_cfg->pm_flag = 0; + flush_dcache_range((uintptr_t)&(psram_sleep_cfg->pm_flag), + sizeof(uint32_t)); + pmu_suspend_cru_fsm(); + + return 0; +} + +int rockchip_soc_sys_pwr_dm_resume(void) +{ + pmu_reinit(); + plat_rockchip_gic_cpuif_enable(); + psram_sleep_cfg->pm_flag = PM_WARM_BOOT_BIT; + flush_dcache_range((uintptr_t)&(psram_sleep_cfg->pm_flag), + sizeof(uint32_t)); + + return 0; +} + +static int cpus_power_domain_off(uint32_t cpu_id, uint32_t pd_cfg) +{ + uint32_t apm_value, offset, idx; + + apm_value = BIT(core_pm_en) | BIT(core_pm_int_wakeup_glb_msk); + + if (pd_cfg == core_pwr_wfi_int) { + apm_value |= BIT(core_pm_int_wakeup_en); + } + + idx = cpu_id / 2; + offset = (cpu_id % 2) << 3; + + mmio_write_32(PMU_BASE + PMU_CPUAPM_CON(idx), + BITS_WITH_WMASK(apm_value, 0xf, offset)); + dsb(); + + return 0; +} + +static int cpus_power_domain_on(uint32_t cpu_id) +{ + uint32_t offset, idx; + + idx = cpu_id / 2; + offset = (cpu_id % 2) << 3; + + mmio_write_32(PMU_BASE + PMU_CPUAPM_CON(idx), + WMSK_BIT(core_pm_en + offset)); + mmio_write_32(PMU_BASE + PMU_CPUAPM_CON(idx), + BIT_WITH_WMSK(core_pm_sft_wakeup_en + offset)); + dsb(); + + return 0; +} + +int rockchip_soc_cores_pwr_dm_on(unsigned long mpidr, uint64_t entrypoint) +{ + uint32_t cpu_id = plat_core_pos_by_mpidr(mpidr); + + assert(cpu_id < PLATFORM_CORE_COUNT); + + cpuson_flags[cpu_id] = PMU_CPU_HOTPLUG; + cpuson_entry_point[cpu_id] = entrypoint; + flush_dcache_range((uintptr_t)cpuson_flags, sizeof(cpuson_flags)); + flush_dcache_range((uintptr_t)cpuson_entry_point, + sizeof(cpuson_entry_point)); + + cpus_power_domain_on(cpu_id); + return 0; +} + +int rockchip_soc_cores_pwr_dm_off(void) +{ + uint32_t cpu_id = plat_my_core_pos(); + + cpus_power_domain_off(cpu_id, + core_pwr_wfi); + return 0; +} + +int rockchip_soc_cores_pwr_dm_on_finish(void) +{ + uint32_t cpu_id = plat_my_core_pos(); + uint32_t offset, idx; + + /* Disable core_pm */ + idx = cpu_id / 2; + offset = (cpu_id % 2) << 3; + mmio_write_32(PMU_BASE + PMU_CPUAPM_CON(idx), + BITS_WITH_WMASK(0, 0xf, offset)); + + return 0; +} + +static void nonboot_cpus_off(void) +{ + uint32_t tmp; + + cpus_power_domain_off(1, 0); + cpus_power_domain_off(2, 0); + cpus_power_domain_off(3, 0); + + mmio_write_32(SYSSRAM_BASE + 0x04, 0xdeadbeaf); + mmio_write_32(SYSSRAM_BASE + 0x08, (uintptr_t)&rockchip_soc_sys_pd_pwr_dn_wfi); + sev(); + + do { + tmp = mmio_read_32(PMU_BASE + PMU_CLUSTER_PWR_ST); + } while ((tmp & 0xe) != 0xe); +} + +void plat_rockchip_pmu_init(void) +{ + uint32_t cpu; + + rockchip_pd_lock_init(); + nonboot_cpus_off(); + for (cpu = 0; cpu < PLATFORM_CORE_COUNT; cpu++) + cpuson_flags[cpu] = PMU_CPU_HOTPLUG; + + psram_sleep_cfg->ddr_data = (uint64_t)0; + psram_sleep_cfg->sp = PSRAM_SP_TOP; + psram_sleep_cfg->ddr_flag = 0x00; + psram_sleep_cfg->boot_mpidr = read_mpidr_el1() & 0xffff; + psram_sleep_cfg->pm_flag = PM_WARM_BOOT_BIT; + + /* + * When perform idle operation, corresponding clock can be + * opened or gated automatically. + */ + mmio_write_32(PMU_BASE + PMU_NOC_AUTO_CON0, 0xffffffff); + mmio_write_32(PMU_BASE + PMU_NOC_AUTO_CON1, 0x00070007); + + /* grf_con_pmic_sleep_sel + * pmic sleep function selection + * 1'b0: From reset pulse generator, can reset external PMIC + * 1'b1: From pmu block, only support sleep function for external PMIC + */ + mmio_write_32(PMUGRF_BASE + PMU_GRF_SOC_CON(0), 0x00800080); + + /* + * force jtag control + * 1'b0: CPU debug port IO mux is controlled by sdmmc_detect_en status + * 1'b0: CPU debug port IO mux IS controlled by GRF + */ + mmio_write_32(SGRF_BASE + 0x008, 0x00100000); + + /* + * remap + * 2'b00: Boot from boot-rom. + * 2'b01: Boot from pmu mem. + * 2'b10: Boot from sys mem. + */ + mmio_write_32(PMUSGRF_BASE + PMU_SGRF_SOC_CON1, 0x18000800); +} diff --git a/plat/rockchip/rk3568/drivers/pmu/pmu.h b/plat/rockchip/rk3568/drivers/pmu/pmu.h new file mode 100644 index 00000000..5821514b --- /dev/null +++ b/plat/rockchip/rk3568/drivers/pmu/pmu.h @@ -0,0 +1,304 @@ +/* + * Copyright (c) 2023, ARM Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef __PMU_H__ +#define __PMU_H__ + +#define PMU_VERSION 0x0000 +#define PMU_PWR_CON 0x0004 +#define PMU_MAIN_PWR_STATE 0x0008 +#define PMU_INT_MASK_CON 0x000C +#define PMU_WAKEUP_INT_CON 0x0010 +#define PMU_WAKEUP_INT_ST 0x0014 +#define PMU_WAKEUP_EDGE_CON 0x0018 +#define PMU_WAKEUP_EDGE_ST 0x001C +#define PMU_BUS_IDLE_CON0 0x0040 +#define PMU_BUS_IDLE_CON1 0x0044 +#define PMU_BUS_IDLE_SFTCON0 0x0050 +#define PMU_BUS_IDLE_SFTCON1 0x0054 +#define PMU_BUS_IDLE_ACK 0x0060 +#define PMU_BUS_IDLE_ST 0x0068 +#define PMU_NOC_AUTO_CON0 0x0070 +#define PMU_NOC_AUTO_CON1 0x0074 +#define PMU_DDR_PWR_CON 0x0080 +#define PMU_DDR_PWR_SFTCON 0x0084 +#define PMU_DDR_PWR_STATE 0x0088 +#define PMU_DDR_PWR_ST 0x008C +#define PMU_PWR_GATE_CON 0x0090 +#define PMU_PWR_GATE_STATE 0x0094 +#define PMU_PWR_DWN_ST 0x0098 +#define PMU_PWR_GATE_SFTCON 0x00A0 +#define PMU_VOL_GATE_SFTCON 0x00A8 +#define PMU_CRU_PWR_CON 0x00B0 +#define PMU_CRU_PWR_SFTCON 0x00B4 +#define PMU_CRU_PWR_STATE 0x00B8 +#define PMU_PLLPD_CON 0x00C0 +#define PMU_PLLPD_SFTCON 0x00C4 +#define PMU_INFO_TX_CON 0x00D0 +#define PMU_DSU_STABLE_CNT 0x0100 +#define PMU_PMIC_STABLE_CNT 0x0104 +#define PMU_OSC_STABLE_CNT 0x0108 +#define PMU_WAKEUP_RSTCLR_CNT 0x010C +#define PMU_PLL_LOCK_CNT 0x0110 +#define PMU_DSU_PWRUP_CNT 0x0118 +#define PMU_DSU_PWRDN_CNT 0x011C +#define PMU_GPU_VOLUP_CNT 0x0120 +#define PMU_GPU_VOLDN_CNT 0x0124 +#define PMU_WAKEUP_TIMEOUT_CNT 0x0128 +#define PMU_PWM_SWITCH_CNT 0x012C +#define PMU_DBG_RST_CNT 0x0130 +#define PMU_SYS_REG0 0x0180 +#define PMU_SYS_REG1 0x0184 +#define PMU_SYS_REG2 0x0188 +#define PMU_SYS_REG3 0x018C +#define PMU_SYS_REG4 0x0190 +#define PMU_SYS_REG5 0x0194 +#define PMU_SYS_REG6 0x0198 +#define PMU_SYS_REG7 0x019C +#define PMU_DSU_PWR_CON 0x0300 +#define PMU_DSU_PWR_SFTCON 0x0304 +#define PMU_DSU_AUTO_CON 0x0308 +#define PMU_DSU_PWR_STATE 0x030C +#define PMU_CPU_AUTO_PWR_CON0 0x0310 +#define PMU_CPU_AUTO_PWR_CON1 0x0314 +#define PMU_CPU_PWR_SFTCON 0x0318 +#define PMU_CLUSTER_PWR_ST 0x031C +#define PMU_CLUSTER_IDLE_CON 0x0320 +#define PMU_CLUSTER_IDLE_SFTCON 0x0324 +#define PMU_CLUSTER_IDLE_ACK 0x0328 +#define PMU_CLUSTER_IDLE_ST 0x032C +#define PMU_DBG_PWR_CON 0x0330 + +/* PMU_SGRF */ +#define PMU_SGRF_SOC_CON1 0x0004 +#define PMU_SGRF_FAST_BOOT_ADDR 0x0180 + +/* sys grf */ +#define GRF_CPU_STATUS0 0x0420 + +#define CRU_SOFTRST_CON00 0x0400 + +#define CORES_PM_DISABLE 0x0 +#define PD_CHECK_LOOP 500 +#define WFEI_CHECK_LOOP 500 + +#define PMUSGRF_SOC_CON(i) ((i) * 0x4) +/* Needed aligned 16 bytes for sp stack top */ +#define PSRAM_SP_TOP ((PMUSRAM_BASE + PMUSRAM_RSIZE) & ~0xf) +#define PMU_CPUAPM_CON(cpu) (0x0310 + (cpu) * 0x4) + +#define PMIC_SLEEP_FUN 0x07000100 +#define PMIC_SLEEP_GPIO 0x07000000 +#define GPIO_SWPORT_DR_L 0x0000 +#define GPIO_SWPORT_DR_H 0x0004 +#define GPIO_SWPORT_DDR_L 0x0008 +#define GPIO_SWPORT_DDR_H 0x000C +#define PMIC_SLEEP_HIGH_LEVEL 0x00040004 +#define PMIC_SLEEP_LOW_LEVEL 0x00040000 +#define PMIC_SLEEP_OUT 0x00040004 +#define CPUS_BYPASS 0x007e4f7e +#define CLB_INT_DISABLE 0x00010001 +#define WRITE_MASK_SET(value) ((value << 16) | value) +#define WRITE_MASK_CLR(value) ((value << 16)) + +enum pmu_cores_pm_by_wfi { + core_pm_en = 0, + core_pm_int_wakeup_en, + core_pm_int_wakeup_glb_msk, + core_pm_sft_wakeup_en, +}; + +/* The ways of cores power domain contorlling */ +enum cores_pm_ctr_mode { + core_pwr_pd = 0, + core_pwr_wfi = 1, + core_pwr_wfi_int = 2 +}; + +/* PMU_PWR_DWN_ST */ +enum pmu_pdid { + PD_GPU, + PD_NPU, + PD_VPU, + PD_RKVENC, + PD_RKVDEC, + PD_RGA, + PD_VI, + PD_VO, + PD_PIPE, + PD_CENTER, + PD_END +}; + +/* PMU_PWR_CON */ +enum pmu_pwr_con { + POWRMODE_EN, + DSU_BYPASS, + BUS_BYPASS = 4, + DDR_BYPASS, + PWRDN_BYPASS, + CRU_BYPASS, + CPU0_BYPASS, + CPU1_BYPASS, + CPU2_BYPASS, + CPU3_BYPASS, + PMU_SLEEP_LOW = 15, +}; + +/* PMU_CRU_PWR_CON */ +enum pmu_cru_pwr_con { + ALIVE_32K_ENA, + OSC_DIS_ENA, + WAKEUP_RST_ENA, + INPUT_CLAMP_ENA, + + ALIVE_OSC_ENA, + POWER_OFF_ENA, + PWM_SWITCH_ENA, + PWM_GPIO_IOE_ENA, + + PWM_SWITCH_IOUT, + PD_BUS_CLK_SRC_GATE_ENA, + PD_PERI_CLK_SRC_GATE_ENA, + PD_PMU_CLK_SRC_GATE_ENA, + + PMUMEM_CLK_SRC_GATE_ENA, + PWR_CON_END +}; + +/* PMU_PLLPD_CON */ +enum pmu_pllpd_con { + APLL_PD_ENA, + DPLL_PD_ENA, + CPLL_PD_ENA, + GPLL_PD_ENA, + MPLL_PD_ENA, + NPLL_PD_ENA, + HPLL_PD_ENA, + PPLL_PD_ENA, + VPLL_PD_ENA, + PLL_PD_END +}; + +/* PMU_DSU_PWR_CON */ +enum pmu_dsu_pwr_con { + DSU_PWRDN_ENA = 2, + DSU_PWROFF_ENA, + DSU_RET_ENA = 6, + CLUSTER_CLK_SRC_GATE_ENA, + DSU_PWR_CON_END +}; + +enum cpu_power_state { + CPU_POWER_ON, + CPU_POWER_OFF, + CPU_EMULATION_OFF, + CPU_RETENTION, + CPU_DEBUG +}; + +enum dsu_power_state { + DSU_POWER_ON, + CLUSTER_TRANSFER_IDLE, + DSU_POWER_DOWN, + DSU_OFF, + DSU_WAKEUP, + DSU_POWER_UP, + CLUSTER_TRANSFER_RESUME, + DSU_FUNCTION_RETENTION +}; + +enum pmu_wakeup_int_con { + WAKEUP_CPU0_INT_EN, + WAKEUP_CPU1_INT_EN, + WAKEUP_CPU2_INT_EN, + WAKEUP_CPU3_INT_EN, + WAKEUP_GPIO0_INT_EN, + WAKEUP_UART0_EN, + WAKEUP_SDMMC0_EN, + WAKEUP_SDMMC1_EN, + WAKEUP_SDMMC2_EN, + WAKEUP_USB_EN, + WAKEUP_PCIE_EN, + WAKEUP_VAD_EN, + WAKEUP_TIMER_EN, + WAKEUP_PWM0_EN, + WAKEUP_TIMEROUT_EN, + WAKEUP_MCU_SFT_EN, +}; + +enum pmu_wakeup_int_st { + WAKEUP_CPU0_INT_ST, + WAKEUP_CPU1_INT_ST, + WAKEUP_CPU2_INT_ST, + WAKEUP_CPU3_INT_ST, + WAKEUP_GPIO0_INT_ST, + WAKEUP_UART0_ST, + WAKEUP_SDMMC0_ST, + WAKEUP_SDMMC1_ST, + WAKEUP_SDMMC2_ST, + WAKEUP_USB_ST, + WAKEUP_PCIE_ST, + WAKEUP_VAD_ST, + WAKEUP_TIMER_ST, + WAKEUP_PWM0_ST, + WAKEUP_TIMEOUT_ST, + WAKEUP_SYS_INT_ST, +}; + +enum pmu_bus_idle_con0 { + IDLE_REQ_MSCH, + IDLE_REQ_GPU, + IDLE_REQ_NPU, + IDLE_REQ_VI, + IDLE_REQ_VO, + IDLE_REQ_RGA, + IDLE_REQ_VPU, + IDLE_REQ_RKVENC, + IDLE_REQ_RKVDEC, + IDLE_REQ_GIC_AUDIO, + IDLE_REQ_PHP, + IDLE_REQ_PIPE, + IDLE_REQ_SECURE_FLASH, + IDLE_REQ_PERIMID, + IDLE_REQ_USB, + IDLE_REQ_BUS, +}; + +enum pmu_bus_idle_con1 { + IDLE_REQ_TOP1, + IDLE_REQ_TOP2, + IDLE_REQ_PMU, +}; + +enum pmu_pwr_gate_con { + PD_GPU_DWN_ENA, + PD_NPU_DWN_ENA, + PD_VPU_DWN_ENA, + PD_RKVENC_DWN_ENA, + + PD_RKVDEC_DWN_ENA, + PD_RGA_DWN_ENA, + PD_VI_DWN_ENA, + PD_VO_DWN_ENA, + + PD_PIPE_DWN_ENA, + PD_CENTER_DWN_ENA, +}; + +enum pmu_ddr_pwr_con { + DDR_SREF_ENA, + DDRIO_RET_ENTER_ENA, + DDRIO_RET_EXIT_ENA = 2, + DDRPHY_AUTO_GATING_ENA = 4, +}; + +enum pmu_vol_gate_soft_con { + VD_GPU_ENA, + VD_NPU_ENA, +}; + +#endif /* __PMU_H__ */ diff --git a/plat/rockchip/rk3568/drivers/soc/soc.c b/plat/rockchip/rk3568/drivers/soc/soc.c new file mode 100644 index 00000000..2af38873 --- /dev/null +++ b/plat/rockchip/rk3568/drivers/soc/soc.c @@ -0,0 +1,103 @@ +/* + * Copyright (c) 2023, ARM Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <common/debug.h> +#include <lib/xlat_tables/xlat_tables_v2.h> +#include <mmio.h> +#include <platform_def.h> + +#include <soc.h> + +const mmap_region_t plat_rk_mmap[] = { + MAP_REGION_FLAT(RKFPGA_DEV_RNG0_BASE, RKFPGA_DEV_RNG0_SIZE, + MT_DEVICE | MT_RW | MT_SECURE), + MAP_REGION_FLAT(PMUSRAM_BASE, PMUSRAM_SIZE, + MT_MEMORY | MT_RW | MT_SECURE), + + { 0 } +}; + +/* The RockChip power domain tree descriptor */ +const unsigned char rockchip_power_domain_tree_desc[] = { + /* No of root nodes */ + PLATFORM_SYSTEM_COUNT, + /* No of children for the root node */ + PLATFORM_CLUSTER_COUNT, + /* No of children for the first cluster node */ + PLATFORM_CLUSTER0_CORE_COUNT, +}; + +static void secure_timer_init(void) +{ + mmio_write_32(STIMER0_CHN_BASE(1) + TIMER_CONTROL_REG, TIMER_DIS); + mmio_write_32(STIMER0_CHN_BASE(1) + TIMER_LOAD_COUNT0, 0xffffffff); + mmio_write_32(STIMER0_CHN_BASE(1) + TIMER_LOAD_COUNT1, 0xffffffff); + + /* auto reload & enable the timer */ + mmio_write_32(STIMER0_CHN_BASE(1) + TIMER_CONTROL_REG, TIMER_EN); +} + +static void sgrf_init(void) +{ + mmio_write_32(SGRF_BASE + SGRF_FIREWALL_SLV_CON(0), 0xffff0000); + mmio_write_32(SGRF_BASE + SGRF_FIREWALL_SLV_CON(1), 0xffff0000); + mmio_write_32(SGRF_BASE + SGRF_FIREWALL_SLV_CON(2), 0xffff0000); + mmio_write_32(SGRF_BASE + SGRF_FIREWALL_SLV_CON(3), 0xffff0000); + mmio_write_32(SGRF_BASE + SGRF_FIREWALL_SLV_CON(4), 0xffff0000); + mmio_write_32(SGRF_BASE + SGRF_FIREWALL_SLV_CON(5), 0xffff0000); + mmio_write_32(SGRF_BASE + SGRF_FIREWALL_SLV_CON(6), 0xffff0000); + mmio_write_32(SGRF_BASE + SGRF_FIREWALL_SLV_CON(7), 0xffff0000); + mmio_write_32(SGRF_BASE + SGRF_FIREWALL_SLV_CON(8), 0xffff0000); + + mmio_write_32(DDRSGRF_BASE + FIREWALL_DDR_FW_DDR_CON_REG, 0xffff0000); +} + +static void set_pll_slow_mode(uint32_t clk_pll) +{ + mmio_write_32(CRU_BASE + CRU_MODE_CON00, 0x03 << (16 + clk_pll * 2)); +} + +static void __dead2 soc_global_soft_reset(void) +{ + set_pll_slow_mode(CLK_CPLL); + set_pll_slow_mode(CLK_GPLL); + set_pll_slow_mode(CLK_NPLL); + set_pll_slow_mode(CLK_VPLL); + set_pll_slow_mode(CLK_USBPLL); + set_pll_slow_mode(CLK_APLL); + mmio_write_32(PMUCRU_BASE + PMUCRU_MODE_CON00, 0x000f0000); + + dsb(); + mmio_write_32(CRU_BASE + CRU_GLB_SRST_FST, GLB_SRST_FST_CFG_VAL); + /* + * Maybe the HW needs some times to reset the system, + * so we do not hope the core to excute valid codes. + */ + while (1) { + ; + } +} + +static void rockchip_system_reset_init(void) +{ + mmio_write_32(GRF_BASE + 0x0508, 0x00100010); + mmio_write_32(CRU_BASE + 0x00dc, 0x01030103); +} + +void __dead2 rockchip_soc_soft_reset(void) +{ + soc_global_soft_reset(); +} + +void plat_rockchip_soc_init(void) +{ + secure_timer_init(); + sgrf_init(); + rockchip_system_reset_init(); + NOTICE("BL31: Rockchip release version: v%d.%d\n", + MAJOR_VERSION, MINOR_VERSION); +} + diff --git a/plat/rockchip/rk3568/drivers/soc/soc.h b/plat/rockchip/rk3568/drivers/soc/soc.h new file mode 100644 index 00000000..41a2586a --- /dev/null +++ b/plat/rockchip/rk3568/drivers/soc/soc.h @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2023, ARM Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef __SOC_H__ +#define __SOC_H__ + +#define RKFPGA_DEV_RNG0_BASE 0xf8000000 +#define RKFPGA_DEV_RNG0_SIZE 0x07fff000 + +#define CRU_MODE_CON00 0x00c0 +#define PMUCRU_MODE_CON00 0x0080 + +#define CRU_GLB_SRST_FST 0x00d4 +#define GLB_SRST_FST_CFG_VAL 0xfdb9 + +#define PMU_GRF_GPIO0A_IOMUX_L 0x00 +#define PMU_GRF_SOC_CON(i) (0x0100 + i * 4) + +#define CRU_SOFTRST_CON 0x300 +#define CRU_SOFTRSTS_CON(n) (CRU_SOFTRST_CON + ((n) * 4)) +#define CRU_SOFTRSTS_CON_CNT 26 +#define GRF_DDR_CON3 0x000c +#define SGRF_FIREWALL_SLV_CON(i) (0x240 + i * 4) + +#define FIREWALL_DDR_FW_DDR_CON_REG 0x80 + + /* low 32 bits */ +#define TIMER_LOAD_COUNT0 0x00 +#define TIMER_LOAD_COUNT1 0x04 +#define TIMER_CURRENT_VALUE0 0x08 +#define TIMER_CURRENT_VALUE1 0x0c +#define TIMER_CONTROL_REG 0x10 +#define TIMER_INTSTATUS 0x18 +#define TIMER_DIS 0x0 +#define TIMER_EN 0x1 +#define STIMER0_CHN_BASE(n) (STIME_BASE + 0x20 * (n)) + +#define PMU_GRF_GPIO0B_IOMUX_L 0x0008 +#define PMUCRU_PMUCLKSEL_CON00 0x0100 +#define PMUPVTM_BASE 0xfdd80000 +#define PVTM_CON0 0x0004 +#define PVTM_CON1 0x0008 +#define PVTM_STATUS0 0x0080 +#define PVTM_STATUS1 0x0084 +#define PMUCRU_PMUGATE_CON01 0x0184 +#define PVTM_CALC_CNT 0x200 +#define PMU_GRF_DLL_CON0 0x0180 + +enum cru_mode_con00 { + CLK_APLL, + CLK_DPLL, + CLK_CPLL, + CLK_GPLL, + CLK_REVSERVED, + CLK_NPLL, + CLK_VPLL, + CLK_USBPLL, +}; + +#endif /* __SOC_H__ */ diff --git a/plat/rockchip/rk3568/include/plat.ld.S b/plat/rockchip/rk3568/include/plat.ld.S new file mode 100644 index 00000000..ddd584d4 --- /dev/null +++ b/plat/rockchip/rk3568/include/plat.ld.S @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2023, ARM Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ +#ifndef ROCKCHIP_PLAT_LD_S +#define ROCKCHIP_PLAT_LD_S + +MEMORY { + PMUSRAM (rwx): ORIGIN = PMUSRAM_BASE, LENGTH = PMUSRAM_RSIZE +} + +SECTIONS +{ + . = PMUSRAM_BASE; + + /* + * pmu_cpuson_entrypoint request address + * align 64K when resume, so put it in the + * start of pmusram + */ + .pmusram : { + ASSERT(. == ALIGN(64 * 1024), + ".pmusram.entry request 64K aligned."); + KEEP(*(.pmusram.entry)) + + __bl31_pmusram_text_start = .; + *(.pmusram.text) + *(.pmusram.rodata) + __bl31_pmusram_text_end = .; + __bl31_pmusram_data_start = .; + *(.pmusram.data) + __bl31_pmusram_data_end = .; + } >PMUSRAM +} +#endif /* ROCKCHIP_PLAT_LD_S */ diff --git a/plat/rockchip/rk3568/include/plat_sip_calls.h b/plat/rockchip/rk3568/include/plat_sip_calls.h new file mode 100644 index 00000000..6acb8762 --- /dev/null +++ b/plat/rockchip/rk3568/include/plat_sip_calls.h @@ -0,0 +1,12 @@ +/* + * Copyright (c) 2023, ARM Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef __PLAT_SIP_CALLS_H__ +#define __PLAT_SIP_CALLS_H__ + +#define RK_PLAT_SIP_NUM_CALLS 0 + +#endif /* __PLAT_SIP_CALLS_H__ */ diff --git a/plat/rockchip/rk3568/include/platform_def.h b/plat/rockchip/rk3568/include/platform_def.h new file mode 100644 index 00000000..19363a42 --- /dev/null +++ b/plat/rockchip/rk3568/include/platform_def.h @@ -0,0 +1,136 @@ +/* + * Copyright (c) 2023, ARM Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef __PLATFORM_DEF_H__ +#define __PLATFORM_DEF_H__ + +#include <arch.h> +#include <common_def.h> +#include <rk3568_def.h> + +#define DEBUG_XLAT_TABLE 0 + +/******************************************************************************* + * Platform binary types for linking + ******************************************************************************/ +#define PLATFORM_LINKER_FORMAT "elf64-littleaarch64" +#define PLATFORM_LINKER_ARCH aarch64 + +/******************************************************************************* + * Generic platform constants + ******************************************************************************/ + +/* Size of cacheable stacks */ +#if DEBUG_XLAT_TABLE +#define PLATFORM_STACK_SIZE 0x800 +#elif IMAGE_BL1 +#define PLATFORM_STACK_SIZE 0x440 +#elif IMAGE_BL2 +#define PLATFORM_STACK_SIZE 0x400 +#elif IMAGE_BL31 +#define PLATFORM_STACK_SIZE 0x800 +#elif IMAGE_BL32 +#define PLATFORM_STACK_SIZE 0x440 +#endif + +#define FIRMWARE_WELCOME_STR "Booting Trusted Firmware\n" + +#define PLATFORM_SYSTEM_COUNT 1 +#define PLATFORM_CLUSTER_COUNT 1 +#define PLATFORM_CLUSTER0_CORE_COUNT 4 + +#define PLATFORM_CLUSTER1_CORE_COUNT 0 +#define PLATFORM_CORE_COUNT (PLATFORM_CLUSTER1_CORE_COUNT + \ + PLATFORM_CLUSTER0_CORE_COUNT) + +#define PLATFORM_NUM_AFFS (PLATFORM_SYSTEM_COUNT + \ + PLATFORM_CLUSTER_COUNT + \ + PLATFORM_CORE_COUNT) + +#define PLAT_MAX_PWR_LVL MPIDR_AFFLVL2 + +#define PLAT_RK_CLST_TO_CPUID_SHIFT 8 + +/* + * This macro defines the deepest retention state possible. A higher state + * id will represent an invalid or a power down state. + */ +#define PLAT_MAX_RET_STATE 1 + +/* + * This macro defines the deepest power down states possible. Any state ID + * higher than this is invalid. + */ +#define PLAT_MAX_OFF_STATE 2 +/******************************************************************************* + * Platform memory map related constants + ******************************************************************************/ +/* TF txet, ro, rw, Size: 512KB */ +#define TZRAM_BASE (0x0) +#define TZRAM_SIZE (0x100000) + +/******************************************************************************* + * BL31 specific defines. + ******************************************************************************/ +/* + * Put BL3-1 at the top of the Trusted RAM + */ +#define BL31_BASE (TZRAM_BASE + 0x40000) +#define BL31_LIMIT (TZRAM_BASE + TZRAM_SIZE) + +/******************************************************************************* + * Platform specific page table and MMU setup constants + ******************************************************************************/ +#define PLAT_PHY_ADDR_SPACE_SIZE (1ULL << 32) +#define PLAT_VIRT_ADDR_SPACE_SIZE (1ULL << 32) + +#define ADDR_SPACE_SIZE (1ull << 32) +#define MAX_XLAT_TABLES 18 +#define MAX_MMAP_REGIONS 27 + +/******************************************************************************* + * Declarations and constants to access the mailboxes safely. Each mailbox is + * aligned on the biggest cache line size in the platform. This is known only + * to the platform as it might have a combination of integrated and external + * caches. Such alignment ensures that two maiboxes do not sit on the same cache + * line at any cache level. They could belong to different cpus/clusters & + * get written while being protected by different locks causing corruption of + * a valid mailbox address. + ******************************************************************************/ +#define CACHE_WRITEBACK_SHIFT 6 +#define CACHE_WRITEBACK_GRANULE (1 << CACHE_WRITEBACK_SHIFT) + +/* + * Define GICD and GICC and GICR base + */ +#define PLAT_RK_GICD_BASE PLAT_GICD_BASE +#define PLAT_RK_GICC_BASE PLAT_GICC_BASE +#define PLAT_RK_GICR_BASE PLAT_GICR_BASE + +/* + * Define a list of Group 1 Secure and Group 0 interrupts as per GICv3 + * terminology. On a GICv2 system or mode, the lists will be merged and treated + * as Group 0 interrupts. + */ + +#define PLAT_RK_GICV3_G1S_IRQS \ + INTR_PROP_DESC(RK_IRQ_SEC_PHY_TIMER, GIC_HIGHEST_SEC_PRIORITY, \ + INTR_GROUP1S, GIC_INTR_CFG_LEVEL) + +#define PLAT_RK_GICV3_G0_IRQS \ + INTR_PROP_DESC(RK_IRQ_SEC_SGI_6, GIC_HIGHEST_SEC_PRIORITY, \ + INTR_GROUP0, GIC_INTR_CFG_LEVEL) + +#define PLAT_RK_UART_BASE FPGA_UART_BASE +#define PLAT_RK_UART_CLOCK FPGA_UART_CLOCK +#define PLAT_RK_UART_BAUDRATE FPGA_BAUDRATE + +#define PLAT_RK_PRIMARY_CPU 0x0 + +#define ATAGS_PHYS_SIZE 0x2000 +#define ATAGS_PHYS_BASE (0x200000 - ATAGS_PHYS_SIZE)/* [2M-8K, 2M] */ + +#endif /* __PLATFORM_DEF_H__ */ diff --git a/plat/rockchip/rk3568/plat_sip_calls.c b/plat/rockchip/rk3568/plat_sip_calls.c new file mode 100644 index 00000000..b0f3a03d --- /dev/null +++ b/plat/rockchip/rk3568/plat_sip_calls.c @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2023, ARM Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <assert.h> +#include <errno.h> + +#include <common/debug.h> +#include <common/runtime_svc.h> +#include <lib/mmio.h> +#include <platform_def.h> + +#include <plat_sip_calls.h> +#include <rockchip_sip_svc.h> + +uintptr_t rockchip_plat_sip_handler(uint32_t smc_fid, + u_register_t x1, + u_register_t x2, + u_register_t x3, + u_register_t x4, + void *cookie, + void *handle, + u_register_t flags) +{ + ERROR("%s: unhandled SMC (0x%x)\n", __func__, smc_fid); + SMC_RET1(handle, SMC_UNK); +} diff --git a/plat/rockchip/rk3568/platform.mk b/plat/rockchip/rk3568/platform.mk new file mode 100644 index 00000000..1155ff84 --- /dev/null +++ b/plat/rockchip/rk3568/platform.mk @@ -0,0 +1,96 @@ +# +# Copyright (c) 2023, ARM Limited and Contributors. All rights reserved. +# +# SPDX-License-Identifier: BSD-3-Clause +# + +RK_PLAT := plat/rockchip +RK_PLAT_SOC := ${RK_PLAT}/${PLAT} +RK_PLAT_COMMON := ${RK_PLAT}/common + +DISABLE_BIN_GENERATION := 1 +GICV3_SUPPORT_GIC600 := 1 +include lib/coreboot/coreboot.mk +include lib/libfdt/libfdt.mk +include lib/xlat_tables_v2/xlat_tables.mk +# GIC-600 configuration +GICV3_IMPL := GIC600 +# Include GICv3 driver files +include drivers/arm/gic/v3/gicv3.mk + +PLAT_INCLUDES := -Iinclude/bl31 \ + -Iinclude/common \ + -Iinclude/drivers \ + -Iinclude/drivers/arm \ + -Iinclude/drivers/auth \ + -Iinclude/drivers/io \ + -Iinclude/drivers/ti/uart \ + -Iinclude/lib \ + -Iinclude/lib/cpus/${ARCH} \ + -Iinclude/lib/el3_runtime \ + -Iinclude/lib/pmf \ + -Iinclude/lib/psci \ + -Iinclude/plat/common \ + -Iinclude/services \ + -Iinclude/plat/common/ \ + -Idrivers/arm/gic/v3/ \ + -I${RK_PLAT_COMMON}/ \ + -I${RK_PLAT_COMMON}/pmusram/ \ + -I${RK_PLAT_COMMON}/include/ \ + -I${RK_PLAT_COMMON}/drivers/pmu/ \ + -I${RK_PLAT_COMMON}/drivers/parameter/ \ + -I${RK_PLAT_SOC}/ \ + -I${RK_PLAT_SOC}/drivers/pmu/ \ + -I${RK_PLAT_SOC}/drivers/soc/ \ + -I${RK_PLAT_SOC}/include/ + +RK_GIC_SOURCES := ${GICV3_SOURCES} \ + plat/common/plat_gicv3.c \ + ${RK_PLAT}/common/rockchip_gicv3.c + +PLAT_BL_COMMON_SOURCES := ${XLAT_TABLES_LIB_SRCS} \ + common/desc_image_load.c \ + plat/common/aarch64/crash_console_helpers.S \ + lib/bl_aux_params/bl_aux_params.c \ + plat/common/plat_psci_common.c + +ifneq (${ENABLE_STACK_PROTECTOR},0) +PLAT_BL_COMMON_SOURCES += ${RK_PLAT_COMMON}/rockchip_stack_protector.c +endif + +BL31_SOURCES += ${RK_GIC_SOURCES} \ + drivers/arm/cci/cci.c \ + lib/cpus/aarch64/cortex_a55.S \ + drivers/ti/uart/aarch64/16550_console.S \ + drivers/delay_timer/delay_timer.c \ + drivers/delay_timer/generic_delay_timer.c \ + $(LIBFDT_SRCS) \ + ${RK_PLAT_COMMON}/aarch64/plat_helpers.S \ + ${RK_PLAT_COMMON}/bl31_plat_setup.c \ + ${RK_PLAT_COMMON}/params_setup.c \ + ${RK_PLAT_COMMON}/plat_pm.c \ + ${RK_PLAT_COMMON}/plat_topology.c \ + ${RK_PLAT_COMMON}/rockchip_sip_svc.c \ + ${RK_PLAT_COMMON}/pmusram/cpus_on_fixed_addr.S \ + ${RK_PLAT_COMMON}/drivers/parameter/ddr_parameter.c \ + ${RK_PLAT_COMMON}/aarch64/platform_common.c \ + ${RK_PLAT_SOC}/drivers/soc/soc.c \ + ${RK_PLAT_SOC}/drivers/pmu/pmu.c \ + ${RK_PLAT_SOC}/plat_sip_calls.c + +ENABLE_PLAT_COMPAT := 0 +MULTI_CONSOLE_API := 1 +# System coherency is managed in hardware +HW_ASSISTED_COHERENCY := 1 +#Enable errata for cortex_a55 +ERRATA_A55_1530923 := 1 + +# When building for systems with hardware-assisted coherency, there's no need to +# use USE_COHERENT_MEM. Require that USE_COHERENT_MEM must be set to 0 too. +USE_COHERENT_MEM := 0 + +$(eval $(call add_define,PLAT_SKIP_OPTEE_S_EL1_INT_REGISTER)) +$(eval $(call add_define,PLAT_EXTRA_LD_SCRIPT)) + +# Do not enable SVE +ENABLE_SVE_FOR_NS := 0 diff --git a/plat/rockchip/rk3568/rk3568_def.h b/plat/rockchip/rk3568/rk3568_def.h new file mode 100644 index 00000000..0d1e5d1a --- /dev/null +++ b/plat/rockchip/rk3568/rk3568_def.h @@ -0,0 +1,104 @@ +/* + * Copyright (c) 2023, ARM Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef __PLAT_DEF_H__ +#define __PLAT_DEF_H__ + +#define MAJOR_VERSION (1) +#define MINOR_VERSION (0) + +#define SIZE_K(n) ((n) * 1024) + +/* Special value used to verify platform parameters from BL2 to BL3-1 */ +#define RK_BL31_PLAT_PARAM_VAL 0x0f1e2d3c4b5a6978ULL + +#define GIC600_BASE 0xfd400000 +#define GIC600_SIZE SIZE_K(64) + +#define PMUSGRF_BASE 0xfdc00000 +#define SYSSGRF_BASE 0xfdc10000 +#define PMUGRF_BASE 0xfdc20000 +#define CPUGRF_BASE 0xfdc30000 +#define DDRGRF_BASE 0xfdc40000 +#define PIPEGRF_BASE 0xfdc50000 +#define GRF_BASE 0xfdc60000 +#define PIPEPHY_GRF0 0xfdc70000 +#define PIPEPHY_GRF1 0xfdc80000 +#define PIPEPHY_GRF2 0xfdc90000 +#define USBPHY_U3_GRF 0xfdca0000 +#define USB2PHY_U2_GRF 0xfdca8000 +#define EDPPHY_GRF 0xfdcb0000 +#define SYSSRAM_BASE 0xfdcc0000 +#define PCIE30PHY_GRF 0xfdcb8000 +#define USBGRF_BASE 0xfdcf0000 + +#define PMUCRU_BASE 0xfdd00000 +#define SCRU_BASE 0xfdd10000 +#define SGRF_BASE 0xfdd18000 +#define STIME_BASE 0xfdd1c000 +#define CRU_BASE 0xfdd20000 +#define PMUSCRU_BASE 0xfdd30000 +#define I2C0_BASE 0xfdd40000 + +#define UART0_BASE 0xfdd50000 +#define GPIO0_BASE 0xfdd60000 +#define PMUPVTM_BASE 0xfdd80000 +#define PMU_BASE 0xfdd90000 +#define PMUSRAM_BASE 0xfdcd0000 +#define PMUSRAM_SIZE SIZE_K(128) +#define PMUSRAM_RSIZE SIZE_K(8) + +#define DDRSGRF_BASE 0xfe200000 +#define UART1_BASE 0xfe650000 +#define UART2_BASE 0xfe660000 +#define GPIO1_BASE 0xfe740000 +#define GPIO2_BASE 0xfe750000 +#define GPIO3_BASE 0xfe760000 +#define GPIO4_BASE 0xfe770000 + +#define REMAP_BASE 0xffff0000 +#define REMAP_SIZE SIZE_K(64) +/************************************************************************** + * UART related constants + **************************************************************************/ +#define FPGA_UART_BASE UART2_BASE +#define FPGA_BAUDRATE 1500000 +#define FPGA_UART_CLOCK 24000000 + +/****************************************************************************** + * System counter frequency related constants + ******************************************************************************/ +#define SYS_COUNTER_FREQ_IN_TICKS 24000000 +#define SYS_COUNTER_FREQ_IN_MHZ 24 + +/****************************************************************************** + * GIC-600 & interrupt handling related constants + ******************************************************************************/ + +/* Base rk_platform compatible GIC memory map */ +#define PLAT_GICD_BASE GIC600_BASE +#define PLAT_GICC_BASE 0 +#define PLAT_GICR_BASE (GIC600_BASE + 0x60000) + +/****************************************************************************** + * sgi, ppi + ******************************************************************************/ +#define RK_IRQ_SEC_PHY_TIMER 29 + +#define RK_IRQ_SEC_SGI_0 8 +#define RK_IRQ_SEC_SGI_1 9 +#define RK_IRQ_SEC_SGI_2 10 +#define RK_IRQ_SEC_SGI_3 11 +#define RK_IRQ_SEC_SGI_4 12 +#define RK_IRQ_SEC_SGI_5 13 +#define RK_IRQ_SEC_SGI_6 14 +#define RK_IRQ_SEC_SGI_7 15 + +#define SHARE_MEM_BASE 0x100000/* [1MB, 1MB+60K]*/ +#define SHARE_MEM_PAGE_NUM 15 +#define SHARE_MEM_SIZE SIZE_K(SHARE_MEM_PAGE_NUM * 4) + +#endif /* __PLAT_DEF_H__ */ diff --git a/plat/rockchip/rk3588/drivers/pmu/plat_pmu_macros.S b/plat/rockchip/rk3588/drivers/pmu/plat_pmu_macros.S new file mode 100644 index 00000000..c2788997 --- /dev/null +++ b/plat/rockchip/rk3588/drivers/pmu/plat_pmu_macros.S @@ -0,0 +1,21 @@ +/* + * Copyright (c) 2024, Rockchip, Inc. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <arch.h> +#include <asm_macros.S> +#include <platform_def.h> + +.globl clst_warmboot_data + +.macro func_rockchip_clst_warmboot +.endm + +.macro rockchip_clst_warmboot_data +clst_warmboot_data: + .rept PLATFORM_CLUSTER_COUNT + .word 0 + .endr +.endm diff --git a/plat/rockchip/rk3588/drivers/pmu/pm_pd_regs.c b/plat/rockchip/rk3588/drivers/pmu/pm_pd_regs.c new file mode 100644 index 00000000..78e85004 --- /dev/null +++ b/plat/rockchip/rk3588/drivers/pmu/pm_pd_regs.c @@ -0,0 +1,555 @@ +/* + * Copyright (c) 2024, Rockchip, Inc. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <assert.h> +#include <errno.h> + +#include <arch_helpers.h> +#include <bl31/bl31.h> +#include <common/debug.h> +#include <drivers/console.h> +#include <drivers/delay_timer.h> +#include <lib/mmio.h> +#include <platform.h> +#include <platform_def.h> +#include <pmu.h> + +#include <plat_pm_helpers.h> +#include <plat_private.h> +#include <pm_pd_regs.h> +#include <soc.h> + +#define WMSK_VAL 0xffff0000 + +static struct reg_region qos_reg_rgns[] = { + [QOS_ISP0_MWO] = REG_REGION(0x08, 0x18, 4, 0xfdf40500, 0), + [QOS_ISP0_MRO] = REG_REGION(0x08, 0x18, 4, 0xfdf40400, 0), + [QOS_ISP1_MWO] = REG_REGION(0x08, 0x18, 4, 0xfdf41000, 0), + [QOS_ISP1_MRO] = REG_REGION(0x08, 0x18, 4, 0xfdf41100, 0), + [QOS_VICAP_M0] = REG_REGION(0x08, 0x18, 4, 0xfdf40600, 0), + [QOS_VICAP_M1] = REG_REGION(0x08, 0x18, 4, 0xfdf40800, 0), + [QOS_FISHEYE0] = REG_REGION(0x08, 0x18, 4, 0xfdf40000, 0), + [QOS_FISHEYE1] = REG_REGION(0x08, 0x18, 4, 0xfdf40200, 0), + [QOS_VOP_M0] = REG_REGION(0x08, 0x18, 4, 0xfdf82000, 0), + [QOS_VOP_M1] = REG_REGION(0x08, 0x18, 4, 0xfdf82200, 0), + [QOS_RKVDEC0] = REG_REGION(0x08, 0x18, 4, 0xfdf62000, 0), + [QOS_RKVDEC1] = REG_REGION(0x08, 0x18, 4, 0xfdf63000, 0), + [QOS_AV1] = REG_REGION(0x08, 0x18, 4, 0xfdf64000, 0), + [QOS_RKVENC0_M0RO] = REG_REGION(0x08, 0x18, 4, 0xfdf60000, 0), + [QOS_RKVENC0_M1RO] = REG_REGION(0x08, 0x18, 4, 0xfdf60200, 0), + [QOS_RKVENC0_M2WO] = REG_REGION(0x08, 0x18, 4, 0xfdf60400, 0), + [QOS_RKVENC1_M0RO] = REG_REGION(0x08, 0x18, 4, 0xfdf61000, 0), + [QOS_RKVENC1_M1RO] = REG_REGION(0x08, 0x18, 4, 0xfdf61200, 0), + [QOS_RKVENC1_M2WO] = REG_REGION(0x08, 0x18, 4, 0xfdf61400, 0), + [QOS_DSU_M0] = REG_REGION(0x08, 0x18, 4, 0xfe008000, 0), + [QOS_DSU_M1] = REG_REGION(0x08, 0x18, 4, 0xfe008800, 0), + [QOS_DSU_MP] = REG_REGION(0x08, 0x18, 4, 0xfdf34200, 0), + [QOS_DEBUG] = REG_REGION(0x08, 0x18, 4, 0xfdf34400, 0), + [QOS_GPU_M0] = REG_REGION(0x08, 0x18, 4, 0xfdf35000, 0), + [QOS_GPU_M1] = REG_REGION(0x08, 0x18, 4, 0xfdf35200, 0), + [QOS_GPU_M2] = REG_REGION(0x08, 0x18, 4, 0xfdf35400, 0), + [QOS_GPU_M3] = REG_REGION(0x08, 0x18, 4, 0xfdf35600, 0), + [QOS_NPU1] = REG_REGION(0x08, 0x18, 4, 0xfdf70000, 0), + [QOS_NPU0_MRO] = REG_REGION(0x08, 0x18, 4, 0xfdf72200, 0), + [QOS_NPU2] = REG_REGION(0x08, 0x18, 4, 0xfdf71000, 0), + [QOS_NPU0_MWR] = REG_REGION(0x08, 0x18, 4, 0xfdf72000, 0), + [QOS_MCU_NPU] = REG_REGION(0x08, 0x18, 4, 0xfdf72400, 0), + [QOS_JPEG_DEC] = REG_REGION(0x08, 0x18, 4, 0xfdf66200, 0), + [QOS_JPEG_ENC0] = REG_REGION(0x08, 0x18, 4, 0xfdf66400, 0), + [QOS_JPEG_ENC1] = REG_REGION(0x08, 0x18, 4, 0xfdf66600, 0), + [QOS_JPEG_ENC2] = REG_REGION(0x08, 0x18, 4, 0xfdf66800, 0), + [QOS_JPEG_ENC3] = REG_REGION(0x08, 0x18, 4, 0xfdf66a00, 0), + [QOS_RGA2_MRO] = REG_REGION(0x08, 0x18, 4, 0xfdf66c00, 0), + [QOS_RGA2_MWO] = REG_REGION(0x08, 0x18, 4, 0xfdf66e00, 0), + [QOS_RGA3_0] = REG_REGION(0x08, 0x18, 4, 0xfdf67000, 0), + [QOS_RGA3_1] = REG_REGION(0x08, 0x18, 4, 0xfdf36000, 0), + [QOS_VDPU] = REG_REGION(0x08, 0x18, 4, 0xfdf67200, 0), + [QOS_IEP] = REG_REGION(0x08, 0x18, 4, 0xfdf66000, 0), + [QOS_HDCP0] = REG_REGION(0x08, 0x18, 4, 0xfdf80000, 0), + [QOS_HDCP1] = REG_REGION(0x08, 0x18, 4, 0xfdf81000, 0), + [QOS_HDMIRX] = REG_REGION(0x08, 0x18, 4, 0xfdf81200, 0), + [QOS_GIC600_M0] = REG_REGION(0x08, 0x18, 4, 0xfdf3a000, 0), + [QOS_GIC600_M1] = REG_REGION(0x08, 0x18, 4, 0xfdf3a200, 0), + [QOS_MMU600PCIE_TCU] = REG_REGION(0x08, 0x18, 4, 0xfdf3a400, 0), + [QOS_MMU600PHP_TBU] = REG_REGION(0x08, 0x18, 4, 0xfdf3a600, 0), + [QOS_MMU600PHP_TCU] = REG_REGION(0x08, 0x18, 4, 0xfdf3a800, 0), + [QOS_USB3_0] = REG_REGION(0x08, 0x18, 4, 0xfdf3e200, 0), + [QOS_USB3_1] = REG_REGION(0x08, 0x18, 4, 0xfdf3e000, 0), + [QOS_USBHOST_0] = REG_REGION(0x08, 0x18, 4, 0xfdf3e400, 0), + [QOS_USBHOST_1] = REG_REGION(0x08, 0x18, 4, 0xfdf3e600, 0), + [QOS_EMMC] = REG_REGION(0x08, 0x18, 4, 0xfdf38200, 0), + [QOS_FSPI] = REG_REGION(0x08, 0x18, 4, 0xfdf38000, 0), + [QOS_SDIO] = REG_REGION(0x08, 0x18, 4, 0xfdf39000, 0), + [QOS_DECOM] = REG_REGION(0x08, 0x18, 4, 0xfdf32000, 0), + [QOS_DMAC0] = REG_REGION(0x08, 0x18, 4, 0xfdf32200, 0), + [QOS_DMAC1] = REG_REGION(0x08, 0x18, 4, 0xfdf32400, 0), + [QOS_DMAC2] = REG_REGION(0x08, 0x18, 4, 0xfdf32600, 0), + [QOS_GIC600M] = REG_REGION(0x08, 0x18, 4, 0xfdf32800, 0), + [QOS_DMA2DDR] = REG_REGION(0x08, 0x18, 4, 0xfdf52000, 0), + [QOS_MCU_DDR] = REG_REGION(0x08, 0x18, 4, 0xfdf52200, 0), + [QOS_VAD] = REG_REGION(0x08, 0x18, 4, 0xfdf3b200, 0), + [QOS_MCU_PMU] = REG_REGION(0x08, 0x18, 4, 0xfdf3b000, 0), + [QOS_CRYPTOS] = REG_REGION(0x08, 0x18, 4, 0xfdf3d200, 0), + [QOS_CRYPTONS] = REG_REGION(0x08, 0x18, 4, 0xfdf3d000, 0), + [QOS_DCF] = REG_REGION(0x08, 0x18, 4, 0xfdf3d400, 0), + [QOS_SDMMC] = REG_REGION(0x08, 0x18, 4, 0xfdf3d800, 0), +}; + +static struct reg_region pd_crypto_reg_rgns[] = { + /* SECURE CRU */ + REG_REGION(0x300, 0x30c, 4, SCRU_BASE, WMSK_VAL), + REG_REGION(0x800, 0x80c, 4, SCRU_BASE, WMSK_VAL), + REG_REGION(0xa00, 0xa0c, 4, SCRU_BASE, WMSK_VAL), + REG_REGION(0xd00, 0xd20, 8, SCRU_BASE, 0), + REG_REGION(0xd04, 0xd24, 8, SCRU_BASE, WMSK_VAL), + + /* S TIMER0 6 channel */ + REG_REGION(0x00, 0x04, 4, STIMER0_BASE + 0x00, 0), + REG_REGION(0x10, 0x10, 4, STIMER0_BASE + 0x00, 0), + REG_REGION(0x00, 0x04, 4, STIMER0_BASE + 0x20, 0), + REG_REGION(0x10, 0x10, 4, STIMER0_BASE + 0x20, 0), + REG_REGION(0x00, 0x04, 4, STIMER0_BASE + 0x40, 0), + REG_REGION(0x10, 0x10, 4, STIMER0_BASE + 0x40, 0), + REG_REGION(0x00, 0x04, 4, STIMER0_BASE + 0x60, 0), + REG_REGION(0x10, 0x10, 4, STIMER0_BASE + 0x60, 0), + REG_REGION(0x00, 0x04, 4, STIMER0_BASE + 0x80, 0), + REG_REGION(0x10, 0x10, 4, STIMER0_BASE + 0x80, 0), + REG_REGION(0x00, 0x04, 4, STIMER0_BASE + 0xa0, 0), + REG_REGION(0x10, 0x10, 4, STIMER0_BASE + 0xa0, 0), + + /* S TIMER1 6 channel */ + REG_REGION(0x00, 0x04, 4, STIMER1_BASE + 0x00, 0), + REG_REGION(0x10, 0x10, 4, STIMER1_BASE + 0x00, 0), + REG_REGION(0x00, 0x04, 4, STIMER1_BASE + 0x20, 0), + REG_REGION(0x10, 0x10, 4, STIMER1_BASE + 0x20, 0), + REG_REGION(0x00, 0x04, 4, STIMER1_BASE + 0x40, 0), + REG_REGION(0x10, 0x10, 4, STIMER1_BASE + 0x40, 0), + REG_REGION(0x00, 0x04, 4, STIMER1_BASE + 0x60, 0), + REG_REGION(0x10, 0x10, 4, STIMER1_BASE + 0x60, 0), + REG_REGION(0x00, 0x04, 4, STIMER1_BASE + 0x80, 0), + REG_REGION(0x10, 0x10, 4, STIMER1_BASE + 0x80, 0), + REG_REGION(0x00, 0x04, 4, STIMER1_BASE + 0xa0, 0), + REG_REGION(0x10, 0x10, 4, STIMER1_BASE + 0xa0, 0), + + /* wdt_s */ + REG_REGION(0x04, 0x04, 4, WDT_S_BASE, 0), + REG_REGION(0x00, 0x00, 4, WDT_S_BASE, 0), +}; + +static struct reg_region pd_dsu_reg_rgns[] = { + /* dsucru */ + REG_REGION(0x040, 0x054, 4, DSUCRU_BASE, WMSK_VAL), + REG_REGION(0x300, 0x31c, 4, DSUCRU_BASE, WMSK_VAL), + REG_REGION(0x800, 0x80c, 4, DSUCRU_BASE, WMSK_VAL), + REG_REGION(0xa00, 0xa0c, 4, DSUCRU_BASE, WMSK_VAL), + REG_REGION(0xd00, 0xd20, 8, DSUCRU_BASE, 0), + REG_REGION(0xd04, 0xd24, 8, DSUCRU_BASE, WMSK_VAL), + REG_REGION(0xf00, 0xf00, 4, DSUCRU_BASE, WMSK_VAL), + REG_REGION(0xf10, 0xf1c, 4, DSUCRU_BASE, 0), + + /* bcore0cru */ + REG_REGION(0x000, 0x014, 4, BIGCORE0CRU_BASE, WMSK_VAL), + REG_REGION(0x300, 0x304, 4, BIGCORE0CRU_BASE, WMSK_VAL), + REG_REGION(0x800, 0x804, 4, BIGCORE0CRU_BASE, WMSK_VAL), + REG_REGION(0xa00, 0xa04, 4, BIGCORE0CRU_BASE, WMSK_VAL), + REG_REGION(0xcc0, 0xcc4, 4, BIGCORE0CRU_BASE, 0), + REG_REGION(0xd00, 0xd00, 4, BIGCORE0CRU_BASE, 0), + REG_REGION(0xd04, 0xd04, 4, BIGCORE0CRU_BASE, WMSK_VAL), + + /* bcore1cru */ + REG_REGION(0x020, 0x034, 4, BIGCORE1CRU_BASE, WMSK_VAL), + REG_REGION(0x300, 0x304, 4, BIGCORE1CRU_BASE, WMSK_VAL), + REG_REGION(0x800, 0x804, 4, BIGCORE1CRU_BASE, WMSK_VAL), + REG_REGION(0xa00, 0xa04, 4, BIGCORE1CRU_BASE, WMSK_VAL), + REG_REGION(0xcc0, 0xcc4, 4, BIGCORE1CRU_BASE, 0), + REG_REGION(0xd00, 0xd00, 4, BIGCORE1CRU_BASE, 0), + REG_REGION(0xd04, 0xd04, 4, BIGCORE1CRU_BASE, WMSK_VAL), + + /* dsugrf */ + REG_REGION(0x00, 0x18, 4, DSUGRF_BASE, WMSK_VAL), + REG_REGION(0x20, 0x20, 4, DSUGRF_BASE, WMSK_VAL), + REG_REGION(0x28, 0x30, 4, DSUGRF_BASE, WMSK_VAL), + REG_REGION(0x38, 0x38, 4, DSUGRF_BASE, WMSK_VAL), + + /* lcore_grf */ + REG_REGION(0x20, 0x20, 4, LITCOREGRF_BASE, WMSK_VAL), + REG_REGION(0x28, 0x30, 4, LITCOREGRF_BASE, WMSK_VAL), + + /* bcore0_grf */ + REG_REGION(0x20, 0x20, 4, BIGCORE0GRF_BASE, WMSK_VAL), + REG_REGION(0x28, 0x30, 4, BIGCORE0GRF_BASE, WMSK_VAL), + + /* bcore1_grf */ + REG_REGION(0x20, 0x20, 4, BIGCORE1GRF_BASE, WMSK_VAL), + REG_REGION(0x28, 0x28, 4, BIGCORE1GRF_BASE, WMSK_VAL), +}; + +static struct reg_region pd_php_reg_rgns[] = { + /* php_grf */ + REG_REGION(0x000, 0x008, 4, PHPGRF_BASE, WMSK_VAL), + REG_REGION(0x014, 0x024, 4, PHPGRF_BASE, WMSK_VAL), + REG_REGION(0x028, 0x02c, 4, PHPGRF_BASE, 0), + REG_REGION(0x030, 0x03c, 4, PHPGRF_BASE, WMSK_VAL), + REG_REGION(0x05c, 0x060, 4, PHPGRF_BASE, WMSK_VAL), + REG_REGION(0x064, 0x068, 4, PHPGRF_BASE, 0), + REG_REGION(0x070, 0x070, 4, PHPGRF_BASE, WMSK_VAL), + REG_REGION(0x074, 0x0d0, 4, PHPGRF_BASE, 0), + REG_REGION(0x0d4, 0x0d4, 4, PHPGRF_BASE, WMSK_VAL), + REG_REGION(0x0e0, 0x0e0, 4, PHPGRF_BASE, 0), + REG_REGION(0x0e4, 0x0ec, 4, PHPGRF_BASE, WMSK_VAL), + REG_REGION(0x100, 0x104, 4, PHPGRF_BASE, WMSK_VAL), + REG_REGION(0x10c, 0x130, 4, PHPGRF_BASE, 0), + REG_REGION(0x138, 0x138, 4, PHPGRF_BASE, WMSK_VAL), + REG_REGION(0x144, 0x168, 4, PHPGRF_BASE, 0), + REG_REGION(0x16c, 0x174, 4, PHPGRF_BASE, WMSK_VAL), + + /* php_cru */ + REG_REGION(0x200, 0x218, 4, PHP_CRU_BASE, WMSK_VAL), + REG_REGION(0x800, 0x800, 4, PHP_CRU_BASE, WMSK_VAL), + REG_REGION(0xa00, 0xa00, 4, PHP_CRU_BASE, WMSK_VAL), + + /* pcie3phy_grf_cmn_con0 */ + REG_REGION(0x00, 0x00, 4, PCIE3PHYGRF_BASE, WMSK_VAL), +}; + +void qos_save(void) +{ + uint32_t pmu_pd_st0 = mmio_read_32(PMU_BASE + PMU2_PWR_GATE_ST(0)); + + if ((pmu_pd_st0 & BIT(PD_GPU)) == 0) { + rockchip_reg_rgn_save(&qos_reg_rgns[QOS_GPU_M0], 1); + rockchip_reg_rgn_save(&qos_reg_rgns[QOS_GPU_M1], 1); + rockchip_reg_rgn_save(&qos_reg_rgns[QOS_GPU_M2], 1); + rockchip_reg_rgn_save(&qos_reg_rgns[QOS_GPU_M3], 1); + } + + if ((pmu_pd_st0 & BIT(PD_NPU1)) == 0) + rockchip_reg_rgn_save(&qos_reg_rgns[QOS_NPU1], 1); + if ((pmu_pd_st0 & BIT(PD_NPU2)) == 0) + rockchip_reg_rgn_save(&qos_reg_rgns[QOS_NPU2], 1); + if ((pmu_pd_st0 & BIT(PD_NPUTOP)) == 0) { + rockchip_reg_rgn_save(&qos_reg_rgns[QOS_NPU0_MRO], 1); + rockchip_reg_rgn_save(&qos_reg_rgns[QOS_NPU0_MWR], 1); + rockchip_reg_rgn_save(&qos_reg_rgns[QOS_MCU_NPU], 1); + } + + if ((pmu_pd_st0 & BIT(PD_RKVDEC1)) == 0) + rockchip_reg_rgn_save(&qos_reg_rgns[QOS_RKVDEC1], 1); + if ((pmu_pd_st0 & BIT(PD_RKVDEC0)) == 0) + rockchip_reg_rgn_save(&qos_reg_rgns[QOS_RKVDEC0], 1); + + if ((pmu_pd_st0 & BIT(PD_VENC1)) == 0) { + rockchip_reg_rgn_save(&qos_reg_rgns[QOS_RKVENC1_M0RO], 1); + rockchip_reg_rgn_save(&qos_reg_rgns[QOS_RKVENC1_M1RO], 1); + rockchip_reg_rgn_save(&qos_reg_rgns[QOS_RKVENC1_M2WO], 1); + } + if ((pmu_pd_st0 & BIT(PD_VENC0)) == 0) { + rockchip_reg_rgn_save(&qos_reg_rgns[QOS_RKVENC0_M0RO], 1); + rockchip_reg_rgn_save(&qos_reg_rgns[QOS_RKVENC0_M1RO], 1); + rockchip_reg_rgn_save(&qos_reg_rgns[QOS_RKVENC0_M2WO], 1); + } + + if ((pmu_pd_st0 & BIT(PD_RGA30)) == 0) + rockchip_reg_rgn_save(&qos_reg_rgns[QOS_RGA3_0], 1); + if ((pmu_pd_st0 & BIT(PD_AV1)) == 0) + rockchip_reg_rgn_save(&qos_reg_rgns[QOS_AV1], 1); + if ((pmu_pd_st0 & BIT(PD_VDPU)) == 0) { + rockchip_reg_rgn_save(&qos_reg_rgns[QOS_JPEG_DEC], 1); + rockchip_reg_rgn_save(&qos_reg_rgns[QOS_JPEG_ENC0], 1); + rockchip_reg_rgn_save(&qos_reg_rgns[QOS_JPEG_ENC1], 1); + rockchip_reg_rgn_save(&qos_reg_rgns[QOS_JPEG_ENC2], 1); + rockchip_reg_rgn_save(&qos_reg_rgns[QOS_JPEG_ENC3], 1); + rockchip_reg_rgn_save(&qos_reg_rgns[QOS_RGA2_MRO], 1); + rockchip_reg_rgn_save(&qos_reg_rgns[QOS_RGA2_MWO], 1); + rockchip_reg_rgn_save(&qos_reg_rgns[QOS_VDPU], 1); + rockchip_reg_rgn_save(&qos_reg_rgns[QOS_IEP], 1); + } + + if ((pmu_pd_st0 & BIT(PD_VO0)) == 0) + rockchip_reg_rgn_save(&qos_reg_rgns[QOS_HDCP0], 1); + if ((pmu_pd_st0 & BIT(PD_VO1)) == 0) { + rockchip_reg_rgn_save(&qos_reg_rgns[QOS_HDCP1], 1); + rockchip_reg_rgn_save(&qos_reg_rgns[QOS_HDMIRX], 1); + } + if ((pmu_pd_st0 & BIT(PD_VOP)) == 0) { + rockchip_reg_rgn_save(&qos_reg_rgns[QOS_VOP_M0], 1); + rockchip_reg_rgn_save(&qos_reg_rgns[QOS_VOP_M1], 1); + } + + if ((pmu_pd_st0 & BIT(PD_FEC)) == 0) { + rockchip_reg_rgn_save(&qos_reg_rgns[QOS_FISHEYE0], 1); + rockchip_reg_rgn_save(&qos_reg_rgns[QOS_FISHEYE1], 1); + } + if ((pmu_pd_st0 & BIT(PD_ISP1)) == 0) { + rockchip_reg_rgn_save(&qos_reg_rgns[QOS_ISP1_MWO], 1); + rockchip_reg_rgn_save(&qos_reg_rgns[QOS_ISP1_MRO], 1); + } + if ((pmu_pd_st0 & BIT(PD_VI)) == 0) { + rockchip_reg_rgn_save(&qos_reg_rgns[QOS_ISP0_MWO], 1); + rockchip_reg_rgn_save(&qos_reg_rgns[QOS_ISP0_MRO], 1); + rockchip_reg_rgn_save(&qos_reg_rgns[QOS_VICAP_M0], 1); + rockchip_reg_rgn_save(&qos_reg_rgns[QOS_VICAP_M1], 1); + } + + if ((pmu_pd_st0 & BIT(PD_RGA31)) == 0) + rockchip_reg_rgn_save(&qos_reg_rgns[QOS_RGA3_1], 1); + + if ((pmu_pd_st0 & BIT(PD_USB)) == 0) { + rockchip_reg_rgn_save(&qos_reg_rgns[QOS_USB3_0], 1); + rockchip_reg_rgn_save(&qos_reg_rgns[QOS_USB3_1], 1); + rockchip_reg_rgn_save(&qos_reg_rgns[QOS_USBHOST_0], 1); + rockchip_reg_rgn_save(&qos_reg_rgns[QOS_USBHOST_1], 1); + } + + if ((pmu_pd_st0 & BIT(PD_PHP)) == 0) { + rockchip_reg_rgn_save(&qos_reg_rgns[QOS_GIC600_M0], 1); + rockchip_reg_rgn_save(&qos_reg_rgns[QOS_GIC600_M1], 1); + rockchip_reg_rgn_save(&qos_reg_rgns[QOS_MMU600PCIE_TCU], 1); + rockchip_reg_rgn_save(&qos_reg_rgns[QOS_MMU600PHP_TBU], 1); + rockchip_reg_rgn_save(&qos_reg_rgns[QOS_MMU600PHP_TCU], 1); + } + + if ((pmu_pd_st0 & BIT(PD_SDIO)) == 0) + rockchip_reg_rgn_save(&qos_reg_rgns[QOS_SDIO], 1); + if ((pmu_pd_st0 & BIT(PD_NVM0)) == 0) { + rockchip_reg_rgn_save(&qos_reg_rgns[QOS_FSPI], 1); + rockchip_reg_rgn_save(&qos_reg_rgns[QOS_EMMC], 1); + } + + if ((pmu_pd_st0 & BIT(PD_SDMMC)) == 0) + rockchip_reg_rgn_save(&qos_reg_rgns[QOS_SDMMC], 1); + + if ((pmu_pd_st0 & BIT(PD_CRYPTO)) == 0) { + rockchip_reg_rgn_save(&qos_reg_rgns[QOS_CRYPTONS], 1); + rockchip_reg_rgn_save(&qos_reg_rgns[QOS_CRYPTOS], 1); + rockchip_reg_rgn_save(&qos_reg_rgns[QOS_DCF], 1); + } + + /* PD_DSU */ + rockchip_reg_rgn_save(&qos_reg_rgns[QOS_DSU_M0], 1); + rockchip_reg_rgn_save(&qos_reg_rgns[QOS_DSU_M1], 1); + rockchip_reg_rgn_save(&qos_reg_rgns[QOS_DSU_MP], 1); + rockchip_reg_rgn_save(&qos_reg_rgns[QOS_DEBUG], 1); +} + +void qos_restore(void) +{ + uint32_t pmu_pd_st0 = mmio_read_32(PMU_BASE + PMU2_PWR_GATE_ST(0)); + + if ((pmu_pd_st0 & BIT(PD_GPU)) == 0) { + rockchip_reg_rgn_restore(&qos_reg_rgns[QOS_GPU_M0], 1); + rockchip_reg_rgn_restore(&qos_reg_rgns[QOS_GPU_M1], 1); + rockchip_reg_rgn_restore(&qos_reg_rgns[QOS_GPU_M2], 1); + rockchip_reg_rgn_restore(&qos_reg_rgns[QOS_GPU_M3], 1); + } + + if ((pmu_pd_st0 & BIT(PD_NPU1)) == 0) + rockchip_reg_rgn_restore(&qos_reg_rgns[QOS_NPU1], 1); + if ((pmu_pd_st0 & BIT(PD_NPU2)) == 0) + rockchip_reg_rgn_restore(&qos_reg_rgns[QOS_NPU2], 1); + if ((pmu_pd_st0 & BIT(PD_NPUTOP)) == 0) { + rockchip_reg_rgn_restore(&qos_reg_rgns[QOS_NPU0_MRO], 1); + rockchip_reg_rgn_restore(&qos_reg_rgns[QOS_NPU0_MWR], 1); + rockchip_reg_rgn_restore(&qos_reg_rgns[QOS_MCU_NPU], 1); + } + + if ((pmu_pd_st0 & BIT(PD_RKVDEC1)) == 0) + rockchip_reg_rgn_restore(&qos_reg_rgns[QOS_RKVDEC1], 1); + if ((pmu_pd_st0 & BIT(PD_RKVDEC0)) == 0) + rockchip_reg_rgn_restore(&qos_reg_rgns[QOS_RKVDEC0], 1); + + if ((pmu_pd_st0 & BIT(PD_VENC1)) == 0) { + rockchip_reg_rgn_restore(&qos_reg_rgns[QOS_RKVENC1_M0RO], 1); + rockchip_reg_rgn_restore(&qos_reg_rgns[QOS_RKVENC1_M1RO], 1); + rockchip_reg_rgn_restore(&qos_reg_rgns[QOS_RKVENC1_M2WO], 1); + } + if ((pmu_pd_st0 & BIT(PD_VENC0)) == 0) { + rockchip_reg_rgn_restore(&qos_reg_rgns[QOS_RKVENC0_M0RO], 1); + rockchip_reg_rgn_restore(&qos_reg_rgns[QOS_RKVENC0_M1RO], 1); + rockchip_reg_rgn_restore(&qos_reg_rgns[QOS_RKVENC0_M2WO], 1); + } + + if ((pmu_pd_st0 & BIT(PD_RGA30)) == 0) + rockchip_reg_rgn_restore(&qos_reg_rgns[QOS_RGA3_0], 1); + if ((pmu_pd_st0 & BIT(PD_AV1)) == 0) + rockchip_reg_rgn_restore(&qos_reg_rgns[QOS_AV1], 1); + if ((pmu_pd_st0 & BIT(PD_VDPU)) == 0) { + rockchip_reg_rgn_restore(&qos_reg_rgns[QOS_JPEG_DEC], 1); + rockchip_reg_rgn_restore(&qos_reg_rgns[QOS_JPEG_ENC0], 1); + rockchip_reg_rgn_restore(&qos_reg_rgns[QOS_JPEG_ENC1], 1); + rockchip_reg_rgn_restore(&qos_reg_rgns[QOS_JPEG_ENC2], 1); + rockchip_reg_rgn_restore(&qos_reg_rgns[QOS_JPEG_ENC3], 1); + rockchip_reg_rgn_restore(&qos_reg_rgns[QOS_RGA2_MRO], 1); + rockchip_reg_rgn_restore(&qos_reg_rgns[QOS_RGA2_MWO], 1); + rockchip_reg_rgn_restore(&qos_reg_rgns[QOS_VDPU], 1); + rockchip_reg_rgn_restore(&qos_reg_rgns[QOS_IEP], 1); + } + + if ((pmu_pd_st0 & BIT(PD_VO0)) == 0) + rockchip_reg_rgn_restore(&qos_reg_rgns[QOS_HDCP0], 1); + if ((pmu_pd_st0 & BIT(PD_VO1)) == 0) { + rockchip_reg_rgn_restore(&qos_reg_rgns[QOS_HDCP1], 1); + rockchip_reg_rgn_restore(&qos_reg_rgns[QOS_HDMIRX], 1); + } + if ((pmu_pd_st0 & BIT(PD_VOP)) == 0) { + rockchip_reg_rgn_restore(&qos_reg_rgns[QOS_VOP_M0], 1); + rockchip_reg_rgn_restore(&qos_reg_rgns[QOS_VOP_M1], 1); + } + + if ((pmu_pd_st0 & BIT(PD_FEC)) == 0) { + rockchip_reg_rgn_restore(&qos_reg_rgns[QOS_FISHEYE0], 1); + rockchip_reg_rgn_restore(&qos_reg_rgns[QOS_FISHEYE1], 1); + } + if ((pmu_pd_st0 & BIT(PD_ISP1)) == 0) { + rockchip_reg_rgn_restore(&qos_reg_rgns[QOS_ISP1_MWO], 1); + rockchip_reg_rgn_restore(&qos_reg_rgns[QOS_ISP1_MRO], 1); + } + if ((pmu_pd_st0 & BIT(PD_VI)) == 0) { + rockchip_reg_rgn_restore(&qos_reg_rgns[QOS_ISP0_MWO], 1); + rockchip_reg_rgn_restore(&qos_reg_rgns[QOS_ISP0_MRO], 1); + rockchip_reg_rgn_restore(&qos_reg_rgns[QOS_VICAP_M0], 1); + rockchip_reg_rgn_restore(&qos_reg_rgns[QOS_VICAP_M1], 1); + } + + if ((pmu_pd_st0 & BIT(PD_RGA31)) == 0) + rockchip_reg_rgn_restore(&qos_reg_rgns[QOS_RGA3_1], 1); + + if ((pmu_pd_st0 & BIT(PD_USB)) == 0) { + rockchip_reg_rgn_restore(&qos_reg_rgns[QOS_USB3_0], 1); + rockchip_reg_rgn_restore(&qos_reg_rgns[QOS_USB3_1], 1); + rockchip_reg_rgn_restore(&qos_reg_rgns[QOS_USBHOST_0], 1); + rockchip_reg_rgn_restore(&qos_reg_rgns[QOS_USBHOST_1], 1); + } + + if ((pmu_pd_st0 & BIT(PD_PHP)) == 0) { + rockchip_reg_rgn_restore(&qos_reg_rgns[QOS_GIC600_M0], 1); + rockchip_reg_rgn_restore(&qos_reg_rgns[QOS_GIC600_M1], 1); + rockchip_reg_rgn_restore(&qos_reg_rgns[QOS_MMU600PCIE_TCU], 1); + rockchip_reg_rgn_restore(&qos_reg_rgns[QOS_MMU600PHP_TBU], 1); + rockchip_reg_rgn_restore(&qos_reg_rgns[QOS_MMU600PHP_TCU], 1); + } + + if ((pmu_pd_st0 & BIT(PD_SDIO)) == 0) + rockchip_reg_rgn_restore(&qos_reg_rgns[QOS_SDIO], 1); + if ((pmu_pd_st0 & BIT(PD_NVM0)) == 0) { + rockchip_reg_rgn_restore(&qos_reg_rgns[QOS_FSPI], 1); + rockchip_reg_rgn_restore(&qos_reg_rgns[QOS_EMMC], 1); + } + + if ((pmu_pd_st0 & BIT(PD_SDMMC)) == 0) + rockchip_reg_rgn_restore(&qos_reg_rgns[QOS_SDMMC], 1); + + if ((pmu_pd_st0 & BIT(PD_CRYPTO)) == 0) { + rockchip_reg_rgn_restore(&qos_reg_rgns[QOS_CRYPTONS], 1); + rockchip_reg_rgn_restore(&qos_reg_rgns[QOS_CRYPTOS], 1); + rockchip_reg_rgn_restore(&qos_reg_rgns[QOS_DCF], 1); + } + + /* PD_DSU */ + rockchip_reg_rgn_restore(&qos_reg_rgns[QOS_DSU_M0], 1); + rockchip_reg_rgn_restore(&qos_reg_rgns[QOS_DSU_M1], 1); + rockchip_reg_rgn_restore(&qos_reg_rgns[QOS_DSU_MP], 1); + rockchip_reg_rgn_restore(&qos_reg_rgns[QOS_DEBUG], 1); +} + +void pd_crypto_save(void) +{ + rockchip_reg_rgn_save(pd_crypto_reg_rgns, ARRAY_SIZE(pd_crypto_reg_rgns)); +} + +void pd_crypto_restore(void) +{ + rockchip_reg_rgn_restore(pd_crypto_reg_rgns, ARRAY_SIZE(pd_crypto_reg_rgns)); +} + +static uint32_t b0_cru_mode; +static uint32_t b1_cru_mode; +static uint32_t dsu_cru_mode; +static uint32_t bcore0_cru_sel_con2, bcore1_cru_sel_con2; + +void pd_dsu_core_save(void) +{ + b0_cru_mode = mmio_read_32(BIGCORE0CRU_BASE + 0x280); + b1_cru_mode = mmio_read_32(BIGCORE1CRU_BASE + 0x280); + dsu_cru_mode = mmio_read_32(DSUCRU_BASE + 0x280); + bcore0_cru_sel_con2 = mmio_read_32(BIGCORE0CRU_BASE + CRU_CLKSEL_CON(2)); + bcore1_cru_sel_con2 = mmio_read_32(BIGCORE1CRU_BASE + CRU_CLKSEL_CON(2)); + + rockchip_reg_rgn_save(pd_dsu_reg_rgns, ARRAY_SIZE(pd_dsu_reg_rgns)); +} + +void pd_dsu_core_restore(void) +{ + /* switch bcore0/1 pclk root to 24M */ + mmio_write_32(BIGCORE0CRU_BASE + CRU_CLKSEL_CON(2), + BITS_WITH_WMASK(2, 0x3, 0)); + mmio_write_32(BIGCORE1CRU_BASE + CRU_CLKSEL_CON(2), + BITS_WITH_WMASK(2, 0x3, 0)); + + /* slow mode */ + mmio_write_32(BIGCORE0CRU_BASE + 0x280, 0x00030000); + mmio_write_32(BIGCORE1CRU_BASE + 0x280, 0x00030000); + mmio_write_32(DSUCRU_BASE + 0x280, 0x00030000); + + rockchip_reg_rgn_restore(pd_dsu_reg_rgns, ARRAY_SIZE(pd_dsu_reg_rgns)); + + /* trigger dsu/lcore/bcore mem_cfg */ + mmio_write_32(DSUGRF_BASE + 0x18, BITS_WITH_WMASK(1, 0x1, 14)); + mmio_write_32(LITCOREGRF_BASE + 0x30, BITS_WITH_WMASK(1, 0x1, 5)); + mmio_write_32(BIGCORE0GRF_BASE + 0x30, BITS_WITH_WMASK(1, 0x1, 5)); + mmio_write_32(BIGCORE1GRF_BASE + 0x30, BITS_WITH_WMASK(1, 0x1, 5)); + udelay(1); + mmio_write_32(DSUGRF_BASE + 0x18, BITS_WITH_WMASK(0, 0x1, 14)); + mmio_write_32(LITCOREGRF_BASE + 0x30, BITS_WITH_WMASK(0, 0x1, 5)); + mmio_write_32(BIGCORE0GRF_BASE + 0x30, BITS_WITH_WMASK(0, 0x1, 5)); + mmio_write_32(BIGCORE1GRF_BASE + 0x30, BITS_WITH_WMASK(0, 0x1, 5)); + + /* wait lock */ + pm_pll_wait_lock(BIGCORE0CRU_BASE + 0x00); + pm_pll_wait_lock(BIGCORE1CRU_BASE + 0x20); + pm_pll_wait_lock(DSUCRU_BASE + 0x40); + + /* restore mode */ + mmio_write_32(BIGCORE0CRU_BASE + 0x280, WITH_16BITS_WMSK(b0_cru_mode)); + mmio_write_32(BIGCORE1CRU_BASE + 0x280, WITH_16BITS_WMSK(b1_cru_mode)); + mmio_write_32(DSUCRU_BASE + 0x280, WITH_16BITS_WMSK(dsu_cru_mode)); + + mmio_write_32(BIGCORE0CRU_BASE + CRU_CLKSEL_CON(2), + WITH_16BITS_WMSK(bcore0_cru_sel_con2)); + mmio_write_32(BIGCORE1CRU_BASE + CRU_CLKSEL_CON(2), + WITH_16BITS_WMSK(bcore1_cru_sel_con2)); +} + +static uint32_t php_ppll_con0; + +void pd_php_save(void) +{ + php_ppll_con0 = mmio_read_32(PHP_CRU_BASE + 0x200); + + /* php_ppll bypass */ + mmio_write_32(PHP_CRU_BASE + 0x200, BITS_WITH_WMASK(1u, 1u, 15)); + dsb(); + isb(); + rockchip_reg_rgn_save(pd_php_reg_rgns, ARRAY_SIZE(pd_php_reg_rgns)); +} + +void pd_php_restore(void) +{ + rockchip_reg_rgn_restore(pd_php_reg_rgns, ARRAY_SIZE(pd_php_reg_rgns)); + + pm_pll_wait_lock(PHP_CRU_BASE + 0x200); + + /* restore php_ppll bypass */ + mmio_write_32(PHP_CRU_BASE + 0x200, WITH_16BITS_WMSK(php_ppll_con0)); +} + +void pm_reg_rgns_init(void) +{ + rockchip_alloc_region_mem(qos_reg_rgns, ARRAY_SIZE(qos_reg_rgns)); + rockchip_alloc_region_mem(pd_crypto_reg_rgns, ARRAY_SIZE(pd_crypto_reg_rgns)); + rockchip_alloc_region_mem(pd_dsu_reg_rgns, ARRAY_SIZE(pd_dsu_reg_rgns)); + rockchip_alloc_region_mem(pd_php_reg_rgns, ARRAY_SIZE(pd_php_reg_rgns)); +} diff --git a/plat/rockchip/rk3588/drivers/pmu/pm_pd_regs.h b/plat/rockchip/rk3588/drivers/pmu/pm_pd_regs.h new file mode 100644 index 00000000..8baf69a7 --- /dev/null +++ b/plat/rockchip/rk3588/drivers/pmu/pm_pd_regs.h @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2024, Rockchip, Inc. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef PM_PD_REGS_H +#define PM_PD_REGS_H + +#include <stdint.h> + +void qos_save(void); +void qos_restore(void); +void pd_crypto_save(void); +void pd_crypto_restore(void); +void pd_dsu_core_save(void); +void pd_dsu_core_restore(void); +void pd_php_save(void); +void pd_php_restore(void); + +void pm_reg_rgns_init(void); + +#endif diff --git a/plat/rockchip/rk3588/drivers/pmu/pmu.c b/plat/rockchip/rk3588/drivers/pmu/pmu.c new file mode 100644 index 00000000..f693dbdf --- /dev/null +++ b/plat/rockchip/rk3588/drivers/pmu/pmu.c @@ -0,0 +1,1512 @@ +/* + * Copyright (c) 2024, Rockchip, Inc. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <assert.h> +#include <errno.h> + +#include <arch_helpers.h> +#include <bl31/bl31.h> +#include <common/debug.h> +#include <drivers/arm/gicv3.h> +#include <drivers/console.h> +#include <drivers/delay_timer.h> +#include <drivers/ti/uart/uart_16550.h> +#include <lib/mmio.h> +#include <plat/common/platform.h> +#include <platform_def.h> +#include <pmu.h> + +#include <cpus_on_fixed_addr.h> +#include <plat_pm_helpers.h> +#include <plat_private.h> +#include <pm_pd_regs.h> +#include <rk3588_clk.h> +#include <rockchip_sip_svc.h> +#include <secure.h> +#include <soc.h> + +#define PSRAM_SP_TOP ((PMUSRAM_BASE + PMUSRAM_RSIZE) & ~0xf) +#define NONBOOT_CPUS_OFF_LOOP (500000) + +#define DSUGRF_REG_CNT (0x78 / 4 + 1) +#define BCORE_GRF_REG_CNT (0x30 / 4 + 1) +#define LCORE_GRF_REG_CNT (0x30 / 4 + 1) + +#define CENTER_GRF_REG_CNT (0x20 / 4 + 1) + +static struct psram_data_t *psram_sleep_cfg = + (struct psram_data_t *)&sys_sleep_flag_sram; + +static int8_t pd_repair_map[] = { + [PD_GPU] = PD_RPR_GPU, + [PD_NPU] = -1, + [PD_VCODEC] = -1, + [PD_NPUTOP] = PD_RPR_NPUTOP, + [PD_NPU1] = PD_RPR_NPU1, + [PD_NPU2] = PD_RPR_NPU2, + [PD_VENC0] = PD_RPR_VENC0, + [PD_VENC1] = PD_RPR_VENC1, + [PD_RKVDEC0] = PD_RPR_RKVDEC0, + [PD_RKVDEC1] = PD_RPR_RKVDEC1, + [PD_VDPU] = PD_RPR_VDPU, + [PD_RGA30] = PD_RPR_RGA30, + [PD_AV1] = PD_RPR_AV1, + [PD_VI] = PD_RPR_VI, + [PD_FEC] = PD_RPR_FEC, + [PD_ISP1] = PD_RPR_ISP1, + [PD_RGA31] = PD_RPR_RGA31, + [PD_VOP] = PD_RPR_VOP, + [PD_VO0] = PD_RPR_VO0, + [PD_VO1] = PD_RPR_VO1, + [PD_AUDIO] = PD_RPR_AUDIO, + [PD_PHP] = PD_RPR_PHP, + [PD_GMAC] = PD_RPR_GMAC, + [PD_PCIE] = PD_RPR_PCIE, + [PD_NVM] = -1, + [PD_NVM0] = PD_RPR_NVM0, + [PD_SDIO] = PD_RPR_SDIO, + [PD_USB] = PD_RPR_USB, + [PD_SECURE] = -1, + [PD_SDMMC] = PD_RPR_SDMMC, + [PD_CRYPTO] = PD_RPR_CRYPTO, + [PD_CENTER] = PD_RPR_CENTER, + [PD_DDR01] = PD_RPR_DDR01, + [PD_DDR23] = PD_RPR_DDR23, +}; + +struct rk3588_sleep_ddr_data { + uint32_t gpio0a_iomux_l, gpio0a_iomux_h, gpio0b_iomux_l; + uint32_t pmu_pd_st0, bus_idle_st0, qch_pwr_st; + uint32_t pmu2_vol_gate_con[3], pmu2_submem_gate_sft_con0; + uint32_t pmu2_bisr_con0; + uint32_t cpll_con0; + uint32_t cru_mode_con, busscru_mode_con; + uint32_t bussgrf_soc_con7; + uint32_t pmu0grf_soc_con0, pmu0grf_soc_con1, pmu0grf_soc_con3; + uint32_t pmu1grf_soc_con2, pmu1grf_soc_con7, pmu1grf_soc_con8, pmu1grf_soc_con9; + uint32_t pmu0sgrf_soc_con1; + uint32_t pmu1sgrf_soc_con14; + uint32_t ddrgrf_chn_con0[4], ddrgrf_chn_con1[4], + ddrgrf_chn_con2[4], pmu1_ddr_pwr_sft_con[4]; + uint32_t pmu1cru_clksel_con1; +}; + +static struct rk3588_sleep_ddr_data ddr_data; + +struct rk3588_sleep_pmusram_data { + uint32_t dsusgrf_soc_con[DSUSGRF_SOC_CON_CNT], + dsusgrf_ddr_hash_con[DSUSGRF_DDR_HASH_CON_CNT]; + uint32_t dsu_ddr_fw_rgn_reg[FIREWALL_DSU_RGN_CNT], + dsu_ddr_fw_mst_reg[FIREWALL_DSU_MST_CNT], + dsu_ddr_fw_con_reg[FIREWALL_DSU_CON_CNT]; + uint32_t busioc_gpio0b_iomux_h; +}; + +static __pmusramdata struct rk3588_sleep_pmusram_data pmusram_data; + +static __pmusramfunc void dsu_restore_early(void) +{ + int i; + + /* dsusgrf */ + for (i = 0; i < DSUSGRF_SOC_CON_CNT; i++) + mmio_write_32(DSUSGRF_BASE + DSUSGRF_SOC_CON(i), + WITH_16BITS_WMSK(pmusram_data.dsusgrf_soc_con[i])); + + for (i = 0; i < DSUSGRF_DDR_HASH_CON_CNT; i++) + mmio_write_32(DSUSGRF_BASE + DSUSGRF_DDR_HASH_CON(i), + pmusram_data.dsusgrf_ddr_hash_con[i]); + + /* dsu ddr firewall */ + for (i = 0; i < FIREWALL_DSU_RGN_CNT; i++) + mmio_write_32(FIREWALL_DSU_BASE + FIREWALL_DSU_RGN(i), + pmusram_data.dsu_ddr_fw_rgn_reg[i]); + + for (i = 0; i < FIREWALL_DSU_MST_CNT; i++) + mmio_write_32(FIREWALL_DSU_BASE + FIREWALL_DSU_MST(i), + pmusram_data.dsu_ddr_fw_mst_reg[i]); + + for (i = 0; i < FIREWALL_DSU_CON_CNT; i++) + mmio_write_32(FIREWALL_DSU_BASE + FIREWALL_DSU_CON(i), + pmusram_data.dsu_ddr_fw_con_reg[i]); +} + +static __pmusramfunc void ddr_resume(void) +{ + /* check the crypto function had been enabled or not */ + if ((mmio_read_32(DSUSGRF_BASE + DSU_SGRF_SOC_CON(4)) & BIT(4)) != 0) { + /* enable the crypto function */ + mmio_write_32(DSUSGRF_BASE + DSU_SGRF_SOC_CON(4), BITS_WITH_WMASK(0, 0x1, 4)); + dsb(); + isb(); + + __asm__ volatile ("mov x0, #3\n" + "dsb sy\n" + "msr rmr_el3, x0\n" + "1:\n" + "isb\n" + "wfi\n" + "b 1b\n"); + } + + dsu_restore_early(); +} + +static void dsu_core_save(void) +{ + int i; + + /* dsusgrf */ + for (i = 0; i < DSUSGRF_SOC_CON_CNT; i++) + pmusram_data.dsusgrf_soc_con[i] = + mmio_read_32(DSUSGRF_BASE + DSUSGRF_SOC_CON(i)); + + for (i = 0; i < DSUSGRF_DDR_HASH_CON_CNT; i++) + pmusram_data.dsusgrf_ddr_hash_con[i] = + mmio_read_32(DSUSGRF_BASE + DSUSGRF_DDR_HASH_CON(i)); + + /* dsu ddr firewall */ + for (i = 0; i < FIREWALL_DSU_RGN_CNT; i++) + pmusram_data.dsu_ddr_fw_rgn_reg[i] = + mmio_read_32(FIREWALL_DSU_BASE + FIREWALL_DSU_RGN(i)); + + for (i = 0; i < FIREWALL_DSU_MST_CNT; i++) + pmusram_data.dsu_ddr_fw_mst_reg[i] = + mmio_read_32(FIREWALL_DSU_BASE + FIREWALL_DSU_MST(i)); + + for (i = 0; i < FIREWALL_DSU_CON_CNT; i++) + pmusram_data.dsu_ddr_fw_con_reg[i] = + mmio_read_32(FIREWALL_DSU_BASE + FIREWALL_DSU_CON(i)); + + pvtplls_suspend(); + pd_dsu_core_save(); +} + +static void dsu_core_restore(void) +{ + pd_dsu_core_restore(); + pvtplls_resume(); +} + +static uint32_t clk_save[CRU_CLKGATE_CON_CNT + PHPCRU_CLKGATE_CON_CNT + + SECURECRU_CLKGATE_CON_CNT + PMU1CRU_CLKGATE_CON_CNT]; + +void clk_gate_con_save(void) +{ + int i, j = 0; + + for (i = 0; i < CRU_CLKGATE_CON_CNT; i++, j++) + clk_save[j] = mmio_read_32(CRU_BASE + CRU_CLKGATE_CON(i)); + + clk_save[j] = mmio_read_32(PHP_CRU_BASE + PHPCRU_CLKGATE_CON); + + for (i = 0; i < SECURECRU_CLKGATE_CON_CNT; i++, j++) + clk_save[j] = mmio_read_32(SCRU_BASE + SECURECRU_CLKGATE_CON(i)); + + for (i = 0; i < PMU1CRU_CLKGATE_CON_CNT; i++, j++) + clk_save[j] = mmio_read_32(PMU1CRU_BASE + CRU_CLKGATE_CON(i)); +} + +void clk_gate_con_disable(void) +{ + int i; + + for (i = 0; i < CRU_CLKGATE_CON_CNT; i++) + mmio_write_32(CRU_BASE + CRU_CLKGATE_CON(i), 0xffff0000); + + mmio_write_32(PHP_CRU_BASE + PHPCRU_CLKGATE_CON, 0xffff0000); + + for (i = 0; i < SECURECRU_CLKGATE_CON_CNT; i++) + mmio_write_32(SCRU_BASE + SECURECRU_CLKGATE_CON(i), 0xffff0000); + + for (i = 0; i < PMU1CRU_CLKGATE_CON_CNT; i++) + mmio_write_32(PMU1CRU_BASE + CRU_CLKGATE_CON(i), 0xffff0000); +} + +void clk_gate_con_restore(void) +{ + int i, j = 0; + + for (i = 0; i < CRU_CLKGATE_CON_CNT; i++, j++) + mmio_write_32(CRU_BASE + CRU_CLKGATE_CON(i), + WITH_16BITS_WMSK(clk_save[j])); + + mmio_write_32(PHP_CRU_BASE + PHPCRU_CLKGATE_CON, + WITH_16BITS_WMSK(clk_save[j])); + + for (i = 0; i < SECURECRU_CLKGATE_CON_CNT; i++, j++) + mmio_write_32(SCRU_BASE + SECURECRU_CLKGATE_CON(i), + WITH_16BITS_WMSK(clk_save[j])); + + for (i = 0; i < PMU1CRU_CLKGATE_CON_CNT; i++, j++) + mmio_write_32(PMU1CRU_BASE + CRU_CLKGATE_CON(i), + WITH_16BITS_WMSK(clk_save[j])); +} + +static void pmu_bus_idle_req(uint32_t bus, uint32_t state) +{ + uint32_t wait_cnt = 0; + + mmio_write_32(PMU_BASE + PMU2_BUS_IDLE_SFTCON(bus / 16), + BITS_WITH_WMASK(state, 0x1, bus % 16)); + + while (pmu_bus_idle_st(bus) != state || + pmu_bus_idle_ack(bus) != state) { + if (++wait_cnt > BUS_IDLE_LOOP) + break; + udelay(1); + } + + if (wait_cnt > BUS_IDLE_LOOP) + WARN("%s: can't wait state %d for bus %d (0x%x)\n", + __func__, state, bus, + mmio_read_32(PMU_BASE + PMU2_BUS_IDLE_ST(bus / 32))); +} + +static void pmu_qch_pwr_ctlr(uint32_t msk, uint32_t state) +{ + uint32_t wait_cnt = 0; + + if (state != 0) + state = msk; + + mmio_write_32(PMU_BASE + PMU2_QCHANNEL_PWR_SFTCON, + BITS_WITH_WMASK(state, msk, 0)); + + while ((mmio_read_32(PMU_BASE + PMU2_QCHANNEL_STATUS) & msk) != state) { + if (++wait_cnt > QCH_PWR_LOOP) + break; + udelay(1); + } + + if (wait_cnt > BUS_IDLE_LOOP) + WARN("%s: can't wait qch:0x%x to state:0x%x (0x%x)\n", + __func__, msk, state, + mmio_read_32(PMU_BASE + PMU2_QCHANNEL_STATUS)); +} + +static inline uint32_t pmu_power_domain_chain_st(uint32_t pd) +{ + return mmio_read_32(PMU_BASE + PMU2_PWR_CHAIN1_ST(pd / 32)) & BIT(pd % 32) ? + pmu_pd_on : + pmu_pd_off; +} + +static inline uint32_t pmu_power_domain_mem_st(uint32_t pd) +{ + return mmio_read_32(PMU_BASE + PMU2_PWR_MEM_ST(pd / 32)) & BIT(pd % 32) ? + pmu_pd_off : + pmu_pd_on; +} + +static inline uint32_t pmu_power_domain_st(uint32_t pd) +{ + int8_t pd_repair = pd_repair_map[pd]; + + if (pd_repair >= 0) + return mmio_read_32(PMU_BASE + PMU2_BISR_STATUS(4)) & BIT(pd_repair) ? + pmu_pd_on : + pmu_pd_off; + else + return mmio_read_32(PMU_BASE + PMU2_PWR_GATE_ST(pd / 32)) & BIT(pd % 32) ? + pmu_pd_off : + pmu_pd_on; +} + +static int pmu_power_domain_pd_to_mem_st(uint32_t pd, uint32_t *pd_mem_st) +{ + uint32_t mem_st; + + switch (pd) { + case PD_NPUTOP: + mem_st = PD_NPU_TOP_MEM_ST; + break; + case PD_NPU1: + mem_st = PD_NPU1_MEM_ST; + break; + case PD_NPU2: + mem_st = PD_NPU2_MEM_ST; + break; + case PD_VENC0: + mem_st = PD_VENC0_MEM_ST; + break; + case PD_VENC1: + mem_st = PD_VENC1_MEM_ST; + break; + case PD_RKVDEC0: + mem_st = PD_RKVDEC0_MEM_ST; + break; + case PD_RKVDEC1: + mem_st = PD_RKVDEC1_MEM_ST; + break; + case PD_RGA30: + mem_st = PD_RGA30_MEM_ST; + break; + case PD_AV1: + mem_st = PD_AV1_MEM_ST; + break; + case PD_VI: + mem_st = PD_VI_MEM_ST; + break; + case PD_FEC: + mem_st = PD_FEC_MEM_ST; + break; + case PD_ISP1: + mem_st = PD_ISP1_MEM_ST; + break; + case PD_RGA31: + mem_st = PD_RGA31_MEM_ST; + break; + case PD_VOP: + mem_st = PD_VOP_MEM_ST; + break; + case PD_VO0: + mem_st = PD_VO0_MEM_ST; + break; + case PD_VO1: + mem_st = PD_VO1_MEM_ST; + break; + case PD_AUDIO: + mem_st = PD_AUDIO_MEM_ST; + break; + case PD_PHP: + mem_st = PD_PHP_MEM_ST; + break; + case PD_GMAC: + mem_st = PD_GMAC_MEM_ST; + break; + case PD_PCIE: + mem_st = PD_PCIE_MEM_ST; + break; + case PD_NVM0: + mem_st = PD_NVM0_MEM_ST; + break; + case PD_SDIO: + mem_st = PD_SDIO_MEM_ST; + break; + case PD_USB: + mem_st = PD_USB_MEM_ST; + break; + case PD_SDMMC: + mem_st = PD_SDMMC_MEM_ST; + break; + default: + return -EINVAL; + } + + *pd_mem_st = mem_st; + + return 0; +} + +static int pmu_power_domain_reset_mem(uint32_t pd, uint32_t pd_mem_st) +{ + uint32_t loop = 0; + int ret = 0; + + while (pmu_power_domain_chain_st(pd_mem_st) != pmu_pd_on) { + udelay(1); + loop++; + if (loop >= PD_CTR_LOOP) { + WARN("%s: %d chain up time out\n", __func__, pd); + ret = -EINVAL; + goto error; + } + } + + udelay(60); + + mmio_write_32(PMU_BASE + PMU2_MEMPWR_GATE_SFTCON(pd / 16), + BITS_WITH_WMASK(pmu_pd_off, 0x1, pd % 16)); + dsb(); + + loop = 0; + while (pmu_power_domain_mem_st(pd_mem_st) != pmu_pd_off) { + udelay(1); + loop++; + if (loop >= PD_CTR_LOOP) { + WARN("%s: %d mem down time out\n", __func__, pd); + ret = -EINVAL; + goto error; + } + } + + mmio_write_32(PMU_BASE + PMU2_MEMPWR_GATE_SFTCON(pd / 16), + BITS_WITH_WMASK(pmu_pd_on, 0x1, pd % 16)); + dsb(); + + loop = 0; + while (pmu_power_domain_mem_st(pd_mem_st) != pmu_pd_on) { + udelay(1); + loop++; + if (loop >= PD_CTR_LOOP) { + WARN("%s: %d mem up time out\n", __func__, pd); + ret = -EINVAL; + goto error; + } + } + + return 0; + +error: + return ret; +} + +static int pmu_power_domain_ctr(uint32_t pd, uint32_t pd_state) +{ + uint32_t loop = 0; + uint32_t is_mem_on = pmu_pd_off; + uint32_t pd_mem_st; + int ret = 0; + + if (pd_state == pmu_pd_on) { + ret = pmu_power_domain_pd_to_mem_st(pd, &pd_mem_st); + if (ret == 0) { + is_mem_on = pmu_power_domain_mem_st(pd_mem_st); + if (is_mem_on == pmu_pd_on) + WARN("%s: %d mem is up\n", __func__, pd); + } + } + + mmio_write_32(PMU_BASE + PMU2_PWR_GATE_SFTCON(pd / 16), + BITS_WITH_WMASK(pd_state, 0x1, pd % 16)); + dsb(); + + if (is_mem_on == pmu_pd_on) { + ret = pmu_power_domain_reset_mem(pd, pd_mem_st); + if (ret != 0) + goto out; + WARN("%s: %d mem reset ok\n", __func__, pd); + } + + while ((pmu_power_domain_st(pd) != pd_state) && (loop < PD_CTR_LOOP)) { + udelay(1); + loop++; + } + + if (pmu_power_domain_st(pd) != pd_state) { + WARN("%s: %d, %d, (0x%x, 0x%x) error!\n", __func__, pd, pd_state, + mmio_read_32(PMU_BASE + PMU2_PWR_GATE_ST(0)), + mmio_read_32(PMU_BASE + PMU2_BISR_STATUS(4))); + ret = -EINVAL; + } + +out: + return ret; +} + +static int pmu_set_power_domain(uint32_t pd_id, uint32_t pd_state) +{ + uint32_t state; + + if (pmu_power_domain_st(pd_id) == pd_state) + goto out; + + if (pd_state == pmu_pd_on) + pmu_power_domain_ctr(pd_id, pd_state); + + state = (pd_state == pmu_pd_off) ? bus_idle : bus_active; + + switch (pd_id) { + case PD_GPU: + pmu_bus_idle_req(BUS_ID_GPU, state); + break; + case PD_NPUTOP: + pmu_bus_idle_req(BUS_ID_NPUTOP, state); + break; + case PD_NPU1: + pmu_bus_idle_req(BUS_ID_NPU1, state); + break; + case PD_NPU2: + pmu_bus_idle_req(BUS_ID_NPU2, state); + break; + case PD_VENC0: + pmu_bus_idle_req(BUS_ID_RKVENC0, state); + break; + case PD_VENC1: + pmu_bus_idle_req(BUS_ID_RKVENC1, state); + break; + case PD_RKVDEC0: + pmu_bus_idle_req(BUS_ID_RKVDEC0, state); + break; + case PD_RKVDEC1: + pmu_bus_idle_req(BUS_ID_RKVDEC1, state); + break; + case PD_VDPU: + pmu_bus_idle_req(BUS_ID_VDPU, state); + break; + case PD_AV1: + pmu_bus_idle_req(BUS_ID_AV1, state); + break; + case PD_VI: + pmu_bus_idle_req(BUS_ID_VI, state); + break; + case PD_ISP1: + pmu_bus_idle_req(BUS_ID_ISP, state); + break; + case PD_RGA31: + pmu_bus_idle_req(BUS_ID_RGA31, state); + break; + case PD_VOP: + pmu_bus_idle_req(BUS_ID_VOP_CHANNEL, state); + pmu_bus_idle_req(BUS_ID_VOP, state); + break; + case PD_VO0: + pmu_bus_idle_req(BUS_ID_VO0, state); + break; + case PD_VO1: + pmu_bus_idle_req(BUS_ID_VO1, state); + break; + case PD_AUDIO: + pmu_bus_idle_req(BUS_ID_AUDIO, state); + break; + case PD_PHP: + pmu_bus_idle_req(BUS_ID_PHP, state); + break; + case PD_NVM: + pmu_bus_idle_req(BUS_ID_NVM, state); + break; + case PD_SDIO: + pmu_bus_idle_req(BUS_ID_SDIO, state); + break; + case PD_USB: + pmu_bus_idle_req(BUS_ID_USB, state); + break; + case PD_SECURE: + pmu_bus_idle_req(BUS_ID_SECURE, state); + break; + default: + break; + } + + if (pd_state == pmu_pd_off) + pmu_power_domain_ctr(pd_id, pd_state); + +out: + return 0; +} + +static void pmu_power_domains_suspend(void) +{ + ddr_data.qch_pwr_st = + mmio_read_32(PMU_BASE + PMU2_QCHANNEL_STATUS) & PMU2_QCH_PWR_MSK; + ddr_data.pmu_pd_st0 = mmio_read_32(PMU_BASE + PMU2_PWR_GATE_ST(0)); + ddr_data.bus_idle_st0 = mmio_read_32(PMU_BASE + PMU2_BUS_IDLE_ST(0)); + + qos_save(); + + if ((ddr_data.pmu_pd_st0 & BIT(PD_PHP)) == 0) + pd_php_save(); + + if ((ddr_data.pmu_pd_st0 & BIT(PD_CRYPTO)) == 0) + pd_crypto_save(); + + pmu_qch_pwr_ctlr(0x20, 1); + pmu_qch_pwr_ctlr(0x40, 1); + pmu_qch_pwr_ctlr(0x1, 1); + pmu_qch_pwr_ctlr(0x2, 1); + pmu_qch_pwr_ctlr(0x4, 1); + pmu_qch_pwr_ctlr(0x8, 1); + pmu_qch_pwr_ctlr(0x10, 1); + + pmu_bus_idle_req(BUS_ID_VO1USBTOP, bus_idle); + pmu_bus_idle_req(BUS_ID_SECURE_VO1USB_CHANNEL, bus_idle); + + pmu_bus_idle_req(BUS_ID_USB, bus_idle); + + pmu_set_power_domain(PD_GPU, pmu_pd_off); + + pmu_set_power_domain(PD_NPU1, pmu_pd_off); + pmu_set_power_domain(PD_NPU2, pmu_pd_off); + pmu_set_power_domain(PD_NPUTOP, pmu_pd_off); + pmu_set_power_domain(PD_NPU, pmu_pd_off); + + pmu_set_power_domain(PD_RKVDEC1, pmu_pd_off); + pmu_set_power_domain(PD_RKVDEC0, pmu_pd_off); + pmu_set_power_domain(PD_VENC1, pmu_pd_off); + pmu_set_power_domain(PD_VENC0, pmu_pd_off); + pmu_set_power_domain(PD_VCODEC, pmu_pd_off); + + pmu_set_power_domain(PD_RGA30, pmu_pd_off); + pmu_set_power_domain(PD_AV1, pmu_pd_off); + pmu_set_power_domain(PD_VDPU, pmu_pd_off); + + pmu_set_power_domain(PD_VO0, pmu_pd_off); + pmu_set_power_domain(PD_VO1, pmu_pd_off); + pmu_set_power_domain(PD_VOP, pmu_pd_off); + + pmu_set_power_domain(PD_FEC, pmu_pd_off); + pmu_set_power_domain(PD_ISP1, pmu_pd_off); + pmu_set_power_domain(PD_VI, pmu_pd_off); + + pmu_set_power_domain(PD_RGA31, pmu_pd_off); + + pmu_set_power_domain(PD_AUDIO, pmu_pd_off); + + pmu_set_power_domain(PD_GMAC, pmu_pd_off); + pmu_set_power_domain(PD_PCIE, pmu_pd_off); + pmu_set_power_domain(PD_PHP, pmu_pd_off); + + pmu_set_power_domain(PD_SDIO, pmu_pd_off); + + pmu_set_power_domain(PD_NVM0, pmu_pd_off); + pmu_set_power_domain(PD_NVM, pmu_pd_off); + + pmu_set_power_domain(PD_SDMMC, pmu_pd_off); + pmu_set_power_domain(PD_CRYPTO, pmu_pd_off); +} + +static void pmu_power_domains_resume(void) +{ + int i; + + pmu_set_power_domain(PD_CRYPTO, !!(ddr_data.pmu_pd_st0 & BIT(PD_CRYPTO))); + pmu_set_power_domain(PD_SDMMC, !!(ddr_data.pmu_pd_st0 & BIT(PD_SDMMC))); + + pmu_set_power_domain(PD_NVM, !!(ddr_data.pmu_pd_st0 & BIT(PD_NVM))); + pmu_set_power_domain(PD_NVM0, !!(ddr_data.pmu_pd_st0 & BIT(PD_NVM0))); + + pmu_set_power_domain(PD_SDIO, !!(ddr_data.pmu_pd_st0 & BIT(PD_SDIO))); + + pmu_set_power_domain(PD_PHP, !!(ddr_data.pmu_pd_st0 & BIT(PD_PHP))); + pmu_set_power_domain(PD_PCIE, !!(ddr_data.pmu_pd_st0 & BIT(PD_PCIE))); + pmu_set_power_domain(PD_GMAC, !!(ddr_data.pmu_pd_st0 & BIT(PD_GMAC))); + + pmu_set_power_domain(PD_AUDIO, !!(ddr_data.pmu_pd_st0 & BIT(PD_AUDIO))); + + pmu_set_power_domain(PD_USB, !!(ddr_data.pmu_pd_st0 & BIT(PD_USB))); + + pmu_set_power_domain(PD_RGA31, !!(ddr_data.pmu_pd_st0 & BIT(PD_RGA31))); + + pmu_set_power_domain(PD_VI, !!(ddr_data.pmu_pd_st0 & BIT(PD_VI))); + pmu_set_power_domain(PD_ISP1, !!(ddr_data.pmu_pd_st0 & BIT(PD_ISP1))); + pmu_set_power_domain(PD_FEC, !!(ddr_data.pmu_pd_st0 & BIT(PD_FEC))); + + pmu_set_power_domain(PD_VOP, !!(ddr_data.pmu_pd_st0 & BIT(PD_VOP))); + + pmu_set_power_domain(PD_VO1, !!(ddr_data.pmu_pd_st0 & BIT(PD_VO1))); + + pmu_set_power_domain(PD_VO0, !!(ddr_data.pmu_pd_st0 & BIT(PD_VO0))); + + pmu_set_power_domain(PD_VDPU, !!(ddr_data.pmu_pd_st0 & BIT(PD_VDPU))); + pmu_set_power_domain(PD_AV1, !!(ddr_data.pmu_pd_st0 & BIT(PD_AV1))); + pmu_set_power_domain(PD_RGA30, !!(ddr_data.pmu_pd_st0 & BIT(PD_RGA30))); + + pmu_set_power_domain(PD_VCODEC, !!(ddr_data.pmu_pd_st0 & BIT(PD_VCODEC))); + pmu_set_power_domain(PD_VENC0, !!(ddr_data.pmu_pd_st0 & BIT(PD_VENC0))); + pmu_set_power_domain(PD_VENC1, !!(ddr_data.pmu_pd_st0 & BIT(PD_VENC1))); + pmu_set_power_domain(PD_RKVDEC0, !!(ddr_data.pmu_pd_st0 & BIT(PD_RKVDEC0))); + pmu_set_power_domain(PD_RKVDEC1, !!(ddr_data.pmu_pd_st0 & BIT(PD_RKVDEC1))); + + pmu_set_power_domain(PD_NPU, !!(ddr_data.pmu_pd_st0 & BIT(PD_NPU))); + pmu_set_power_domain(PD_NPUTOP, !!(ddr_data.pmu_pd_st0 & BIT(PD_NPUTOP))); + pmu_set_power_domain(PD_NPU2, !!(ddr_data.pmu_pd_st0 & BIT(PD_NPU2))); + pmu_set_power_domain(PD_NPU1, !!(ddr_data.pmu_pd_st0 & BIT(PD_NPU1))); + + pmu_set_power_domain(PD_GPU, !!(ddr_data.pmu_pd_st0 & BIT(PD_GPU))); + + for (i = 0; i < 32; i++) + pmu_bus_idle_req(i, !!(ddr_data.bus_idle_st0 & BIT(i))); + + pmu_qch_pwr_ctlr(0x10, !!(ddr_data.qch_pwr_st & 0x10)); + pmu_qch_pwr_ctlr(0x8, !!(ddr_data.qch_pwr_st & 0x8)); + pmu_qch_pwr_ctlr(0x4, !!(ddr_data.qch_pwr_st & 0x4)); + pmu_qch_pwr_ctlr(0x2, !!(ddr_data.qch_pwr_st & 0x2)); + pmu_qch_pwr_ctlr(0x1, !!(ddr_data.qch_pwr_st & 0x1)); + pmu_qch_pwr_ctlr(0x40, !!(ddr_data.qch_pwr_st & 0x40)); + pmu_qch_pwr_ctlr(0x20, !!(ddr_data.qch_pwr_st & 0x20)); + + if ((ddr_data.pmu_pd_st0 & BIT(PD_CRYPTO)) == 0) + pd_crypto_restore(); + + if ((ddr_data.pmu_pd_st0 & BIT(PD_PHP)) == 0) + pd_php_restore(); + + qos_restore(); +} + +static int cpus_power_domain_on(uint32_t cpu_id) +{ + mmio_write_32(PMU_BASE + PMU2_CPU_AUTO_PWR_CON(cpu_id), + BITS_WITH_WMASK(0, 0x1, core_pm_en)); + mmio_write_32(PMU_BASE + PMU2_CPU_AUTO_PWR_CON(cpu_id), + BITS_WITH_WMASK(1, 0x1, core_pm_sft_wakeup_en)); + dsb(); + + return 0; +} + +static int cpus_power_domain_off(uint32_t cpu_id, uint32_t pd_cfg) +{ + uint32_t apm_value = BIT(core_pm_en); + + if (pd_cfg == core_pwr_wfi_int) + apm_value |= BIT(core_pm_int_wakeup_en); + + mmio_write_32(PMU_BASE + PMU2_CPU_AUTO_PWR_CON(cpu_id), + BITS_WITH_WMASK(apm_value, 0x3, 0)); + dsb(); + + return 0; +} + +static inline void cpus_pd_req_enter_wfi(void) +{ + /* CORTEX_A55_CPUACTLR_EL1 */ + __asm__ volatile ("msr DBGPRCR_EL1, xzr\n" + "mrs x0, S3_0_C15_C2_7\n" + "orr x0, x0, #0x1\n" + "msr S3_0_C15_C2_7, x0\n" + "wfi_loop:\n" + "isb\n" + "wfi\n" + "b wfi_loop\n"); +} + +static void nonboot_cpus_off(void) +{ + uint32_t boot_cpu, cpu, tmp; + uint32_t exp_st; + uint32_t bcore0_rst_msk = 0, bcore1_rst_msk = 0; + int wait_cnt; + + bcore0_rst_msk = CRU_BIGCPU02_RST_MSK | CRU_BIGCPU13_RST_MSK; + bcore1_rst_msk = CRU_BIGCPU02_RST_MSK | CRU_BIGCPU13_RST_MSK; + + mmio_write_32(BIGCORE0CRU_BASE + 0xa00, BITS_WITH_WMASK(0, bcore0_rst_msk, 0)); + mmio_write_32(BIGCORE1CRU_BASE + 0xa00, BITS_WITH_WMASK(0, bcore1_rst_msk, 0)); + + wait_cnt = NONBOOT_CPUS_OFF_LOOP; + exp_st = SYS_GRF_BIG_CPUS_WFE; + do { + wait_cnt--; + tmp = mmio_read_32(SYSGRF_BASE + SYS_GRF_SOC_STATUS(3)); + tmp &= SYS_GRF_BIG_CPUS_WFE; + } while (tmp != exp_st && wait_cnt); + + boot_cpu = plat_my_core_pos(); + + /* turn off noboot cpus */ + for (cpu = 0; cpu < PLATFORM_CORE_COUNT; cpu++) { + if (cpu == boot_cpu) + continue; + cpus_power_domain_off(cpu, core_pwr_wfi); + } + + mmio_write_32(SRAM_BASE + 0x08, (uintptr_t)&cpus_pd_req_enter_wfi); + mmio_write_32(SRAM_BASE + 0x04, 0xdeadbeaf); + + dsb(); + isb(); + + sev(); + + wait_cnt = NONBOOT_CPUS_OFF_LOOP; + do { + wait_cnt--; + tmp = mmio_read_32(PMU_BASE + PMU2_CLUSTER_ST); + tmp &= CLUSTER_STS_NONBOOT_CPUS_DWN; + } while (tmp != CLUSTER_STS_NONBOOT_CPUS_DWN && wait_cnt); + + if (tmp != CLUSTER_STS_NONBOOT_CPUS_DWN) + ERROR("nonboot cpus status(%x) error!\n", tmp); +} + +int rockchip_soc_cores_pwr_dm_on(unsigned long mpidr, + uint64_t entrypoint) +{ + uint32_t cpu_id = plat_core_pos_by_mpidr(mpidr); + + assert(cpu_id < PLATFORM_CORE_COUNT); + assert(cpuson_flags[cpu_id] == 0); + cpuson_flags[cpu_id] = PMU_CPU_HOTPLUG; + cpuson_entry_point[cpu_id] = entrypoint; + dsb(); + + flush_dcache_range((uintptr_t)cpuson_flags, sizeof(cpuson_flags)); + flush_dcache_range((uintptr_t)cpuson_entry_point, + sizeof(cpuson_entry_point)); + dsb(); + isb(); + + cpus_power_domain_on(cpu_id); + + return PSCI_E_SUCCESS; +} + +int rockchip_soc_cores_pwr_dm_on_finish(void) +{ + uint32_t cpu_id = plat_my_core_pos(); + + mmio_write_32(PMU_BASE + PMU2_CPU_AUTO_PWR_CON(cpu_id), + BITS_WITH_WMASK(0, 0xf, 0)); + + return PSCI_E_SUCCESS; +} + +int rockchip_soc_cores_pwr_dm_off(void) +{ + uint32_t cpu_id = plat_my_core_pos(); + + cpus_power_domain_off(cpu_id, core_pwr_wfi); + + return PSCI_E_SUCCESS; +} + +int rockchip_soc_cores_pwr_dm_suspend(void) +{ + uint32_t cpu_id = plat_my_core_pos(); + + assert(cpu_id < PLATFORM_CORE_COUNT); + + cpuson_flags[cpu_id] = PMU_CPU_AUTO_PWRDN; + cpuson_entry_point[cpu_id] = plat_get_sec_entrypoint(); + dsb(); + flush_dcache_range((uintptr_t)cpuson_flags, sizeof(cpuson_flags)); + flush_dcache_range((uintptr_t)cpuson_entry_point, + sizeof(cpuson_entry_point)); + dsb(); + isb(); + + cpus_power_domain_off(cpu_id, core_pwr_wfi_int); + + __asm__ volatile ("msr DBGPRCR_EL1, xzr\n" + "mrs x0, S3_0_C15_C2_7\n" + "orr x0, x0, #0x1\n" + "msr S3_0_C15_C2_7, x0\n"); + + return PSCI_E_SUCCESS; +} + +int rockchip_soc_cores_pwr_dm_resume(void) +{ + uint32_t cpu_id = plat_my_core_pos(); + + mmio_write_32(PMU_BASE + PMU2_CPU_AUTO_PWR_CON(cpu_id), + BITS_WITH_WMASK(0, 0x3, 0)); + + dsb(); + + return PSCI_E_SUCCESS; +} + +static void ddr_sleep_config(void) +{ + int i; + + if (pmu_power_domain_st(PD_DDR01) == 0) { + ddr_data.ddrgrf_chn_con0[0] = + mmio_read_32(DDR01GRF_BASE + DDRGRF_CHA_CON(0)); + ddr_data.ddrgrf_chn_con0[1] = + mmio_read_32(DDR01GRF_BASE + DDRGRF_CHB_CON(0)); + ddr_data.ddrgrf_chn_con1[0] = + mmio_read_32(DDR01GRF_BASE + DDRGRF_CHA_CON(1)); + ddr_data.ddrgrf_chn_con1[1] = + mmio_read_32(DDR01GRF_BASE + DDRGRF_CHB_CON(1)); + ddr_data.ddrgrf_chn_con2[0] = + mmio_read_32(DDR01GRF_BASE + DDRGRF_CHA_CON(2)); + ddr_data.ddrgrf_chn_con2[1] = + mmio_read_32(DDR01GRF_BASE + DDRGRF_CHB_CON(2)); + + mmio_write_32(DDR01GRF_BASE + DDRGRF_CHA_CON(2), 0x20002000); + mmio_write_32(DDR01GRF_BASE + DDRGRF_CHB_CON(2), 0x20002000); + mmio_write_32(DDR01GRF_BASE + DDRGRF_CHA_CON(2), 0x08000000); + mmio_write_32(DDR01GRF_BASE + DDRGRF_CHB_CON(2), 0x08000000); + mmio_write_32(DDR01GRF_BASE + DDRGRF_CHA_CON(0), 0x00200020); + mmio_write_32(DDR01GRF_BASE + DDRGRF_CHB_CON(0), 0x00200020); + mmio_write_32(DDR01GRF_BASE + DDRGRF_CHA_CON(1), 0x00400040); + mmio_write_32(DDR01GRF_BASE + DDRGRF_CHB_CON(1), 0x00400040); + } + + if (pmu_power_domain_st(PD_DDR23) == 0) { + ddr_data.ddrgrf_chn_con0[2] = + mmio_read_32(DDR23GRF_BASE + DDRGRF_CHA_CON(0)); + ddr_data.ddrgrf_chn_con0[3] = + mmio_read_32(DDR23GRF_BASE + DDRGRF_CHB_CON(0)); + ddr_data.ddrgrf_chn_con1[2] = + mmio_read_32(DDR23GRF_BASE + DDRGRF_CHA_CON(1)); + ddr_data.ddrgrf_chn_con1[3] = + mmio_read_32(DDR23GRF_BASE + DDRGRF_CHB_CON(1)); + ddr_data.ddrgrf_chn_con2[2] = + mmio_read_32(DDR23GRF_BASE + DDRGRF_CHA_CON(2)); + ddr_data.ddrgrf_chn_con2[3] = + mmio_read_32(DDR23GRF_BASE + DDRGRF_CHB_CON(2)); + + mmio_write_32(DDR23GRF_BASE + DDRGRF_CHA_CON(2), 0x20002000); + mmio_write_32(DDR23GRF_BASE + DDRGRF_CHB_CON(2), 0x20002000); + mmio_write_32(DDR23GRF_BASE + DDRGRF_CHA_CON(2), 0x08000000); + mmio_write_32(DDR23GRF_BASE + DDRGRF_CHB_CON(2), 0x08000000); + mmio_write_32(DDR23GRF_BASE + DDRGRF_CHA_CON(0), 0x00200020); + mmio_write_32(DDR23GRF_BASE + DDRGRF_CHB_CON(0), 0x00200020); + mmio_write_32(DDR23GRF_BASE + DDRGRF_CHA_CON(1), 0x00400040); + mmio_write_32(DDR23GRF_BASE + DDRGRF_CHB_CON(1), 0x00400040); + } + + for (i = 0; i < DDR_CHN_CNT; i++) { + ddr_data.pmu1_ddr_pwr_sft_con[i] = + mmio_read_32(PMU_BASE + PMU1_DDR_PWR_SFTCON(i)); + mmio_write_32(PMU_BASE + PMU1_DDR_PWR_SFTCON(i), 0x0fff0900); + } +} + +static void ddr_sleep_config_restore(void) +{ + int i; + + for (i = 0; i < DDR_CHN_CNT; i++) { + mmio_write_32(PMU_BASE + PMU1_DDR_PWR_SFTCON(i), + 0x0fff0000 | ddr_data.pmu1_ddr_pwr_sft_con[i]); + } + + if (pmu_power_domain_st(PD_DDR01) == 0) { + mmio_write_32(DDR01GRF_BASE + DDRGRF_CHA_CON(1), + 0x00400000 | ddr_data.ddrgrf_chn_con1[0]); + mmio_write_32(DDR01GRF_BASE + DDRGRF_CHB_CON(1), + 0x00400000 | ddr_data.ddrgrf_chn_con1[1]); + mmio_write_32(DDR01GRF_BASE + DDRGRF_CHA_CON(0), + 0x00200000 | ddr_data.ddrgrf_chn_con0[0]); + mmio_write_32(DDR01GRF_BASE + DDRGRF_CHB_CON(0), + 0x00200000 | ddr_data.ddrgrf_chn_con0[1]); + mmio_write_32(DDR01GRF_BASE + DDRGRF_CHA_CON(2), + 0x28000000 | ddr_data.ddrgrf_chn_con2[0]); + mmio_write_32(DDR01GRF_BASE + DDRGRF_CHB_CON(2), + 0x28000000 | ddr_data.ddrgrf_chn_con2[1]); + } + + if (pmu_power_domain_st(PD_DDR23) == 0) { + mmio_write_32(DDR23GRF_BASE + DDRGRF_CHA_CON(1), + 0x00400000 | ddr_data.ddrgrf_chn_con1[2]); + mmio_write_32(DDR23GRF_BASE + DDRGRF_CHB_CON(1), + 0x00400000 | ddr_data.ddrgrf_chn_con1[3]); + mmio_write_32(DDR23GRF_BASE + DDRGRF_CHA_CON(0), + 0x00200000 | ddr_data.ddrgrf_chn_con0[2]); + mmio_write_32(DDR23GRF_BASE + DDRGRF_CHB_CON(0), + 0x00200000 | ddr_data.ddrgrf_chn_con0[3]); + mmio_write_32(DDR23GRF_BASE + DDRGRF_CHA_CON(2), + 0x28000000 | ddr_data.ddrgrf_chn_con2[2]); + mmio_write_32(DDR23GRF_BASE + DDRGRF_CHB_CON(2), + 0x28000000 | ddr_data.ddrgrf_chn_con2[3]); + } +} + +static void pmu_sleep_config(void) +{ + uint32_t pmu1_pwr_con, pmu1_wkup_int_con, pmu1_cru_pwr_con; + uint32_t pmu1_ddr_pwr_con, pmu1_pll_pd_con[2] = {0}; + uint32_t pmu2_dsu_pwr_con, pmu2_core_pwr_con, pmu2_clst_idle_con; + uint32_t pmu2_bus_idle_con[3] = {0}, pmu2_pwr_gate_con[3] = {0}; + uint32_t pmu2_vol_gate_con[3] = {0}, pmu2_qch_pwr_con = 0; + int i; + + ddr_data.pmu1grf_soc_con7 = mmio_read_32(PMU1GRF_BASE + PMU1_GRF_SOC_CON(7)); + ddr_data.pmu1grf_soc_con8 = mmio_read_32(PMU1GRF_BASE + PMU1_GRF_SOC_CON(8)); + ddr_data.pmu1grf_soc_con9 = mmio_read_32(PMU1GRF_BASE + PMU1_GRF_SOC_CON(9)); + ddr_data.pmu1sgrf_soc_con14 = mmio_read_32(PMU1SGRF_BASE + PMU1_SGRF_SOC_CON(14)); + ddr_data.pmu0sgrf_soc_con1 = mmio_read_32(PMU0SGRF_BASE + PMU0_SGRF_SOC_CON(1)); + ddr_data.pmu0grf_soc_con1 = mmio_read_32(PMU0GRF_BASE + PMU0_GRF_SOC_CON(1)); + + ddr_data.pmu2_vol_gate_con[0] = mmio_read_32(PMU_BASE + PMU2_VOL_GATE_CON(0)); + ddr_data.pmu2_vol_gate_con[1] = mmio_read_32(PMU_BASE + PMU2_VOL_GATE_CON(1)); + ddr_data.pmu2_vol_gate_con[2] = mmio_read_32(PMU_BASE + PMU2_VOL_GATE_CON(2)); + + ddr_data.pmu2_submem_gate_sft_con0 = + mmio_read_32(PMU_BASE + PMU2_MEMPWR_MD_GATE_SFTCON(0)); + + /* save pmic_sleep iomux gpio0_a4 */ + ddr_data.gpio0a_iomux_l = mmio_read_32(PMU0IOC_BASE + 0); + ddr_data.gpio0a_iomux_h = mmio_read_32(PMU0IOC_BASE + 4); + ddr_data.pmu0grf_soc_con3 = mmio_read_32(PMU0GRF_BASE + PMU0_GRF_SOC_CON(3)); + + /* PMU1 repair disable */ + mmio_write_32(PMU0GRF_BASE + PMU0_GRF_SOC_CON(0), 0x00010000); + + /* set pmic_sleep iomux */ + mmio_write_32(PMU0IOC_BASE + 0, + BITS_WITH_WMASK(1, 0xf, 8) | + BITS_WITH_WMASK(1, 0xfu, 12)); + + /* set tsadc_shut_m0 pin iomux to gpio */ + mmio_write_32(PMU0IOC_BASE + 0, + BITS_WITH_WMASK(0, 0xf, 4)); + + /* set spi2_cs0/1 pin iomux to gpio */ + mmio_write_32(PMU0IOC_BASE + 8, + BITS_WITH_WMASK(0, 0xff, 0)); + + /* sleep 1~2 src select */ + mmio_write_32(PMU0GRF_BASE + PMU0_GRF_SOC_CON(3), + BITS_WITH_WMASK(0x8, 0xf, 0) | + BITS_WITH_WMASK(0x8, 0xf, 4) | + BITS_WITH_WMASK(0x0, 0x3, 8)); + + pmu1_wkup_int_con = BIT(WAKEUP_GPIO0_INT_EN) | + BIT(WAKEUP_CPU0_INT_EN); + + pmu1_pwr_con = BIT(powermode_en); + + pmu1_cru_pwr_con = + BIT(alive_osc_mode_en) | + BIT(power_off_en) | + BIT(pd_clk_src_gate_en); + + pmu1_ddr_pwr_con = 0; + + pmu2_dsu_pwr_con = + BIT(DSU_PWRDN_EN) | + BIT(DSU_PWROFF_EN); + + pmu2_core_pwr_con = BIT(CORE_PWRDN_EN); + + pmu2_clst_idle_con = + BIT(IDLE_REQ_BIGCORE0_EN) | + BIT(IDLE_REQ_BIGCORE1_EN) | + BIT(IDLE_REQ_DSU_EN) | + BIT(IDLE_REQ_LITDSU_EN) | + BIT(IDLE_REQ_ADB400_CORE_QCH_EN); + + pmu1_pll_pd_con[0] = + BIT(B0PLL_PD_EN) | + BIT(B1PLL_PD_EN) | + BIT(LPLL_PD_EN) | + BIT(V0PLL_PD_EN) | + BIT(AUPLL_PD_EN) | + BIT(GPLL_PD_EN) | + BIT(CPLL_PD_EN) | + BIT(NPLL_PD_EN); + + pmu1_pll_pd_con[1] = + BIT(PPLL_PD_EN) | + BIT(SPLL_PD_EN); + + pmu2_bus_idle_con[0] = 0; + + pmu2_bus_idle_con[1] = + BIT(BUS_ID_SECURE - 16) | + BIT(BUS_ID_SECURE_CENTER_CHANNEL - 16) | + BIT(BUS_ID_CENTER_CHANNEL - 16); + + pmu2_bus_idle_con[2] = + BIT(BUS_ID_MSCH - 32) | + BIT(BUS_ID_BUS - 32) | + BIT(BUS_ID_TOP - 32); + + pmu2_pwr_gate_con[0] = 0; + pmu2_pwr_gate_con[1] = BIT(PD_SECURE - 16); + pmu2_pwr_gate_con[2] = 0; + + pmu2_qch_pwr_con = 0; + + pmu2_vol_gate_con[0] = 0x7; + pmu2_vol_gate_con[2] = 0; + + mmio_write_32(PMU_BASE + PMU2_CORE_AUTO_PWR_CON(0), 0x00030000); + mmio_write_32(PMU_BASE + PMU2_CORE_AUTO_PWR_CON(1), 0x00030000); + mmio_write_32(PMU_BASE + PMU2_CORE_PWR_CON(0), + WITH_16BITS_WMSK(pmu2_core_pwr_con)); + mmio_write_32(PMU_BASE + PMU2_CORE_PWR_CON(1), + WITH_16BITS_WMSK(pmu2_core_pwr_con)); + mmio_write_32(PMU_BASE + PMU2_CLUSTER_IDLE_CON, + WITH_16BITS_WMSK(pmu2_clst_idle_con)); + mmio_write_32(PMU_BASE + PMU2_DSU_AUTO_PWR_CON, 0x00030000); + mmio_write_32(PMU_BASE + PMU2_DSU_PWR_CON, + WITH_16BITS_WMSK(pmu2_dsu_pwr_con)); + + mmio_write_32(PMU_BASE + PMU1_OSC_STABLE_CNT_THRESH, 24000); + mmio_write_32(PMU_BASE + PMU1_STABLE_CNT_THRESH, 24000); + mmio_write_32(PMU_BASE + PMU1_WAKEUP_RST_CLR_CNT_THRESH, 24000); + mmio_write_32(PMU_BASE + PMU1_PLL_LOCK_CNT_THRESH, 24000); + mmio_write_32(PMU_BASE + PMU1_PWM_SWITCH_CNT_THRESH, 24000); + mmio_write_32(PMU_BASE + PMU2_CORE0_STABLE_CNT_THRESH, 24000); + mmio_write_32(PMU_BASE + PMU2_CORE0_PWRUP_CNT_THRESH, 24000); + mmio_write_32(PMU_BASE + PMU2_CORE0_PWRDN_CNT_THRESH, 24000); + mmio_write_32(PMU_BASE + PMU2_CORE1_STABLE_CNT_THRESH, 24000); + mmio_write_32(PMU_BASE + PMU2_CORE1_PWRUP_CNT_THRESH, 24000); + mmio_write_32(PMU_BASE + PMU2_CORE1_PWRDN_CNT_THRESH, 24000); + mmio_write_32(PMU_BASE + PMU2_DSU_STABLE_CNT_THRESH, 24000); + mmio_write_32(PMU_BASE + PMU2_DSU_PWRUP_CNT_THRESH, 24000); + mmio_write_32(PMU_BASE + PMU2_DSU_PWRDN_CNT_THRESH, 24000); + + /* Config pmu power mode and pmu wakeup source */ + mmio_write_32(PMU_BASE + PMU1_INT_MASK_CON, + BITS_WITH_WMASK(1, 0x1, 0)); + + /* pmu1_pwr_con */ + mmio_write_32(PMU_BASE + PMU1_PWR_CON, + WITH_16BITS_WMSK(pmu1_pwr_con)); + + /* cru_pwr_con */ + mmio_write_32(PMU_BASE + PMU1_CRU_PWR_CON, + WITH_16BITS_WMSK(pmu1_cru_pwr_con)); + + /* wakeup source */ + mmio_write_32(PMU_BASE + PMU1_WAKEUP_INT_CON, pmu1_wkup_int_con); + + /* ddr pwr con */ + for (i = 0; i < DDR_CHN_CNT; i++) { + mmio_write_32(PMU_BASE + PMU1_DDR_PWR_CON(i), + WITH_16BITS_WMSK(pmu1_ddr_pwr_con)); + pmu2_bus_idle_con[1] |= + BIT(BUS_ID_MSCH0 - 16 + i); + } + + /* pll_pd */ + mmio_write_32(PMU_BASE + PMU1_PLLPD_CON(0), + WITH_16BITS_WMSK(pmu1_pll_pd_con[0])); + mmio_write_32(PMU_BASE + PMU1_PLLPD_CON(1), + WITH_16BITS_WMSK(pmu1_pll_pd_con[1])); + + /* bypass cpu1~7*/ + mmio_write_32(PMU_BASE + PMU2_PWR_CON1, 0x00ff00fe); + + /* bus idle */ + mmio_write_32(PMU_BASE + PMU2_BUS_IDLE_CON(0), + WITH_16BITS_WMSK(pmu2_bus_idle_con[0])); + mmio_write_32(PMU_BASE + PMU2_BUS_IDLE_CON(1), + WITH_16BITS_WMSK(pmu2_bus_idle_con[1])); + mmio_write_32(PMU_BASE + PMU2_BUS_IDLE_CON(2), + WITH_16BITS_WMSK(pmu2_bus_idle_con[2])); + mmio_write_32(PMU_BASE + PMU2_BUS_IDLE_CON(2), + 0xf000f000); + /* power gate */ + mmio_write_32(PMU_BASE + PMU2_PWR_GATE_CON(0), + WITH_16BITS_WMSK(pmu2_pwr_gate_con[0])); + mmio_write_32(PMU_BASE + PMU2_PWR_GATE_CON(1), + WITH_16BITS_WMSK(pmu2_pwr_gate_con[1])); + mmio_write_32(PMU_BASE + PMU2_PWR_GATE_CON(2), + WITH_16BITS_WMSK(pmu2_pwr_gate_con[2])); + /* vol gate */ + mmio_write_32(PMU_BASE + PMU2_VOL_GATE_CON(0), + BITS_WITH_WMASK(pmu2_vol_gate_con[0], 0x7, 0)); + mmio_write_32(PMU_BASE + PMU2_VOL_GATE_CON(1), 0); + mmio_write_32(PMU_BASE + PMU2_VOL_GATE_CON(2), + BITS_WITH_WMASK(pmu2_vol_gate_con[2], 0x3, 0)); + /* qch */ + mmio_write_32(PMU_BASE + PMU2_QCHANNEL_PWR_CON, + BITS_WITH_WMASK(pmu2_qch_pwr_con, 0x7f, 0)); + + mmio_write_32(PMU_BASE + PMU2_MEMPWR_MD_GATE_SFTCON(0), + 0x000f000f); +} + +static void pmu_sleep_restore(void) +{ + mmio_write_32(PMU1GRF_BASE + PMU1_GRF_SOC_CON(7), + WITH_16BITS_WMSK(ddr_data.pmu1grf_soc_con7)); + mmio_write_32(PMU1GRF_BASE + PMU1_GRF_SOC_CON(8), + WITH_16BITS_WMSK(ddr_data.pmu1grf_soc_con8)); + mmio_write_32(PMU1GRF_BASE + PMU1_GRF_SOC_CON(9), + WITH_16BITS_WMSK(ddr_data.pmu1grf_soc_con9)); + mmio_write_32(PMU1SGRF_BASE + PMU1_SGRF_SOC_CON(14), + WITH_16BITS_WMSK(ddr_data.pmu1sgrf_soc_con14)); + + mmio_write_32(PMU0SGRF_BASE + PMU0_SGRF_SOC_CON(1), + WITH_16BITS_WMSK(ddr_data.pmu0sgrf_soc_con1)); + mmio_write_32(PMU0GRF_BASE + PMU0_GRF_SOC_CON(1), + WITH_16BITS_WMSK(ddr_data.pmu0grf_soc_con1)); + + mmio_write_32(PMU_BASE + PMU2_CORE_PWR_CON(0), 0xffff0000); + mmio_write_32(PMU_BASE + PMU2_CORE_PWR_CON(1), 0xffff0000); + mmio_write_32(PMU_BASE + PMU2_CLUSTER_IDLE_CON, 0xffff0000); + mmio_write_32(PMU_BASE + PMU2_DSU_PWR_CON, 0xffff0000); + mmio_write_32(PMU_BASE + PMU2_PWR_CON1, 0xffff0000); + + /* Must clear PMU1_WAKEUP_INT_CON because the wakeup source + * in PMU1_WAKEUP_INT_CON will wakeup cpus in cpu_auto_pd state. + */ + mmio_write_32(PMU_BASE + PMU1_WAKEUP_INT_CON, 0); + mmio_write_32(PMU_BASE + PMU1_PWR_CON, 0xffff0000); + mmio_write_32(PMU_BASE + PMU1_INT_MASK_CON, 0x00010000); + mmio_write_32(PMU_BASE + PMU0_WAKEUP_INT_CON, 0x00010000); + mmio_write_32(PMU_BASE + PMU0_PWR_CON, 0xffff0000); + + mmio_write_32(PMU_BASE + PMU2_VOL_GATE_CON(0), + WITH_16BITS_WMSK(ddr_data.pmu2_vol_gate_con[0])); + mmio_write_32(PMU_BASE + PMU2_VOL_GATE_CON(1), + WITH_16BITS_WMSK(ddr_data.pmu2_vol_gate_con[1])); + mmio_write_32(PMU_BASE + PMU2_VOL_GATE_CON(2), + WITH_16BITS_WMSK(ddr_data.pmu2_vol_gate_con[2])); + + mmio_write_32(PMU_BASE + PMU2_MEMPWR_MD_GATE_SFTCON(0), + WITH_16BITS_WMSK(ddr_data.pmu2_submem_gate_sft_con0)); + + mmio_write_32(PMU0GRF_BASE + PMU0_GRF_SOC_CON(3), + WITH_16BITS_WMSK(ddr_data.pmu0grf_soc_con3)); + mmio_write_32(PMU1GRF_BASE + PMU1_GRF_SOC_CON(2), + WITH_16BITS_WMSK(ddr_data.pmu1grf_soc_con2)); + + mmio_write_32(PMU0IOC_BASE + 0x4, + WITH_16BITS_WMSK(ddr_data.gpio0a_iomux_h)); + mmio_write_32(PMU0IOC_BASE + 0, + WITH_16BITS_WMSK(ddr_data.gpio0a_iomux_l)); +} + +static void soc_sleep_config(void) +{ + ddr_data.gpio0b_iomux_l = mmio_read_32(PMU0IOC_BASE + 0x8); + + pmu_sleep_config(); + ddr_sleep_config(); +} + +static void soc_sleep_restore(void) +{ + ddr_sleep_config_restore(); + pmu_sleep_restore(); + + mmio_write_32(PMU0IOC_BASE + 0x8, WITH_16BITS_WMSK(ddr_data.gpio0b_iomux_l)); +} + +static void pm_pll_suspend(void) +{ + ddr_data.cru_mode_con = mmio_read_32(CRU_BASE + 0x280); + ddr_data.busscru_mode_con = mmio_read_32(BUSSCRU_BASE + 0x280); + ddr_data.pmu2_bisr_con0 = mmio_read_32(PMU_BASE + PMU2_BISR_CON(0)); + ddr_data.cpll_con0 = mmio_read_32(CRU_BASE + CRU_PLLS_CON(2, 0)); + ddr_data.pmu1cru_clksel_con1 = mmio_read_32(PMU1CRU_BASE + CRU_CLKSEL_CON(1)); + + /* disable bisr_init */ + mmio_write_32(PMU_BASE + PMU2_BISR_CON(0), BITS_WITH_WMASK(0, 0x1, 0)); + /* cpll bypass */ + mmio_write_32(CRU_BASE + CRU_PLLS_CON(2, 0), BITS_WITH_WMASK(1u, 1u, 15)); +} + +static void pm_pll_restore(void) +{ + pm_pll_wait_lock(CRU_BASE + CRU_PLLS_CON(2, 0)); + + mmio_write_32(CRU_BASE + 0x280, WITH_16BITS_WMSK(ddr_data.cru_mode_con)); + mmio_write_32(BUSSCRU_BASE + 0x280, WITH_16BITS_WMSK(ddr_data.busscru_mode_con)); + mmio_write_32(CRU_BASE + CRU_PLLS_CON(2, 0), WITH_16BITS_WMSK(ddr_data.cpll_con0)); + dsb(); + isb(); + mmio_write_32(PMU_BASE + PMU2_BISR_CON(0), WITH_16BITS_WMSK(ddr_data.pmu2_bisr_con0)); +} + +int rockchip_soc_sys_pwr_dm_suspend(void) +{ + clk_gate_con_save(); + clk_gate_con_disable(); + + psram_sleep_cfg->pm_flag &= ~PM_WARM_BOOT_BIT; + + pmu_power_domains_suspend(); + soc_sleep_config(); + dsu_core_save(); + pm_pll_suspend(); + + return 0; +} + +int rockchip_soc_sys_pwr_dm_resume(void) +{ + pm_pll_restore(); + dsu_core_restore(); + soc_sleep_restore(); + pmu_power_domains_resume(); + plat_rockchip_gic_cpuif_enable(); + + psram_sleep_cfg->pm_flag |= PM_WARM_BOOT_BIT; + + clk_gate_con_restore(); + + return 0; +} + +void __dead2 rockchip_soc_cores_pd_pwr_dn_wfi(const + psci_power_state_t *target_state) +{ + psci_power_down_wfi(); +} + +void __dead2 rockchip_soc_sys_pd_pwr_dn_wfi(void) +{ + cpus_pd_req_enter_wfi(); + psci_power_down_wfi(); +} + +void __dead2 rockchip_soc_soft_reset(void) +{ + /* pll slow mode */ + mmio_write_32(CRU_BASE + 0x280, 0x03ff0000); + mmio_write_32(BIGCORE0CRU_BASE + 0x280, 0x00030000); + mmio_write_32(BIGCORE0CRU_BASE + 0x300, 0x60000000); + mmio_write_32(BIGCORE0CRU_BASE + 0x304, 0x00600000); + mmio_write_32(BIGCORE1CRU_BASE + 0x280, 0x00030000); + mmio_write_32(BIGCORE1CRU_BASE + 0x300, 0x60000000); + mmio_write_32(BIGCORE1CRU_BASE + 0x304, 0x00600000); + mmio_write_32(DSUCRU_BASE + 0x280, 0x00030000); + mmio_write_32(DSUCRU_BASE + 0x318, 0x30600000); + mmio_write_32(DSUCRU_BASE + 0x31c, 0x30600000); + mmio_write_32(DSUCRU_BASE + 0x304, 0x00010000); + mmio_write_32(BUSSCRU_BASE + 0x280, 0x0003000); + dsb(); + isb(); + + mmio_write_32(CRU_BASE + CRU_GLB_SRST_FST, GLB_SRST_FST_CFG_VAL); + + /* + * Maybe the HW needs some times to reset the system, + * so we do not hope the core to execute valid codes. + */ + psci_power_down_wfi(); +} + +void __dead2 rockchip_soc_system_off(void) +{ + /* set pmic_sleep pin(gpio0_a2) to gpio mode */ + mmio_write_32(PMU0IOC_BASE + 0, BITS_WITH_WMASK(0, 0xf, 8)); + + /* config output */ + mmio_write_32(GPIO0_BASE + GPIO_SWPORT_DDR_L, + BITS_WITH_WMASK(1, 0x1, 2)); + + /* config output high level */ + mmio_write_32(GPIO0_BASE + GPIO_SWPORT_DR_L, + BITS_WITH_WMASK(1, 0x1, 2)); + dsb(); + + /* + * Maybe the HW needs some times to reset the system, + * so we do not hope the core to execute valid codes. + */ + psci_power_down_wfi(); +} + +static void rockchip_pmu_pd_init(void) +{ + mmio_write_32(PMU_BASE + PMU2_BISR_CON(1), 0xffffffff); + mmio_write_32(PMU_BASE + PMU2_BISR_CON(2), 0xffffffff); + mmio_write_32(PMU_BASE + PMU2_BISR_CON(3), 0xffffffff); + + pmu_set_power_domain(PD_PHP, pmu_pd_on); + pmu_set_power_domain(PD_PCIE, pmu_pd_on); + pmu_set_power_domain(PD_GMAC, pmu_pd_on); + pmu_set_power_domain(PD_SECURE, pmu_pd_on); + pmu_set_power_domain(PD_VOP, pmu_pd_on); + pmu_set_power_domain(PD_VO0, pmu_pd_on); + pmu_set_power_domain(PD_VO1, pmu_pd_on); +} + +#define PLL_LOCKED_TIMEOUT 600000U + +void pm_pll_wait_lock(uint32_t pll_base) +{ + int delay = PLL_LOCKED_TIMEOUT; + + if ((mmio_read_32(pll_base + CRU_PLL_CON(1)) & CRU_PLLCON1_PWRDOWN) != 0) + return; + + while (delay-- >= 0) { + if (mmio_read_32(pll_base + CRU_PLL_CON(6)) & + CRU_PLLCON6_LOCK_STATUS) + break; + udelay(1); + } + + if (delay <= 0) + ERROR("Can't wait pll(0x%x) lock\n", pll_base); +} + +void rockchip_plat_mmu_el3(void) +{ + /* Nothing todo */ +} + +void plat_rockchip_pmu_init(void) +{ + int cpu; + + for (cpu = 0; cpu < PLATFORM_CORE_COUNT; cpu++) + cpuson_flags[cpu] = 0; + + psram_sleep_cfg->sp = PSRAM_SP_TOP; + psram_sleep_cfg->ddr_func = (uint64_t)ddr_resume; + psram_sleep_cfg->ddr_data = 0; + psram_sleep_cfg->ddr_flag = 0; + psram_sleep_cfg->boot_mpidr = read_mpidr_el1() & 0xffff; + psram_sleep_cfg->pm_flag = PM_WARM_BOOT_BIT; + + nonboot_cpus_off(); + + /* + * When perform idle operation, corresponding clock can be + * opened or gated automatically. + */ + mmio_write_32(PMU_BASE + PMU2_BIU_AUTO_CON(0), 0xffffffff); + mmio_write_32(PMU_BASE + PMU2_BIU_AUTO_CON(1), 0xffffffff); + mmio_write_32(PMU_BASE + PMU2_BIU_AUTO_CON(2), 0x00070007); + + rockchip_pmu_pd_init(); + + /* grf_con_pmic_sleep_sel + * pmic sleep function selection + * 1'b0: From reset pulse generator, can reset external PMIC + * 1'b1: From pmu block, only support sleep function for external PMIC + */ + mmio_write_32(PMU0GRF_BASE + PMU0_GRF_SOC_CON(3), 0x03ff0000); + + /* pmusram remap to 0xffff0000 */ + mmio_write_32(PMU0SGRF_BASE + PMU0_SGRF_SOC_CON(2), 0x00030001); + + pm_reg_rgns_init(); +} + +static uint64_t boot_cpu_save[4]; +/* define in .data section */ +static uint32_t need_en_crypto = 1; + +void rockchip_cpu_reset_early(u_register_t arg0, u_register_t arg1, + u_register_t arg2, u_register_t arg3) +{ + if (need_en_crypto == 0) + return; + + /* check the crypto function had been enabled or not */ + if ((mmio_read_32(DSUSGRF_BASE + DSU_SGRF_SOC_CON(4)) & BIT(4)) != 0) { + /* save x0~x3 */ + boot_cpu_save[0] = arg0; + boot_cpu_save[1] = arg1; + boot_cpu_save[2] = arg2; + boot_cpu_save[3] = arg3; + + /* enable the crypto function */ + mmio_write_32(DSUSGRF_BASE + DSU_SGRF_SOC_CON(4), + BITS_WITH_WMASK(0, 0x1, 4)); + + /* remap pmusram to 0xffff0000 */ + mmio_write_32(PMU0SGRF_BASE + PMU0_SGRF_SOC_CON(2), 0x00030001); + psram_sleep_cfg->pm_flag = PM_WARM_BOOT_BIT; + cpuson_flags[0] = PMU_CPU_HOTPLUG; + cpuson_entry_point[0] = (uintptr_t)BL31_BASE; + dsb(); + + /* Must reset core0 to enable the crypto function. + * Core0 will boot from pmu_sram and jump to BL31_BASE. + */ + __asm__ volatile ("mov x0, #3\n" + "dsb sy\n" + "msr rmr_el3, x0\n" + "1:\n" + "isb\n" + "wfi\n" + "b 1b\n"); + } else { + need_en_crypto = 0; + + /* remap bootrom to 0xffff0000 */ + mmio_write_32(PMU0SGRF_BASE + PMU0_SGRF_SOC_CON(2), 0x00030000); + + /* + * the crypto function has been enabled, + * restore the x0~x3. + */ + __asm__ volatile ("ldr x20, [%0]\n" + "ldr x21, [%0, 0x8]\n" + "ldr x22, [%0, 0x10]\n" + "ldr x23, [%0, 0x18]\n" + : : "r" (&boot_cpu_save[0])); + } +} diff --git a/plat/rockchip/rk3588/drivers/pmu/pmu.h b/plat/rockchip/rk3588/drivers/pmu/pmu.h new file mode 100644 index 00000000..7d8288c5 --- /dev/null +++ b/plat/rockchip/rk3588/drivers/pmu/pmu.h @@ -0,0 +1,589 @@ +/* + * Copyright (c) 2024, Rockchip, Inc. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef __PMU_H__ +#define __PMU_H__ + +#include <lib/mmio.h> + +#define PMU0_PWR_CON 0x0000 +#define PMU0_WAKEUP_INT_CON 0x0008 +#define PMU0_WAKEUP_INT_ST 0x000c +#define PMU0_PMIC_STABLE_CNT_THRES 0x0010 +#define PMU0_WAKEUP_RST_CLR_CNT_THRES 0x0014 +#define PMU0_OSC_STABLE_CNT_THRES 0x0018 +#define PMU0_PWR_CHAIN_STABLE_CON 0x001c +#define PMU0_DDR_RET_CON(i) (0x0020 + (i) * 4) +#define PMU0_INFO_TX_CON 0x0030 + +#define PMU1_VERSION_ID 0x4000 +#define PMU1_PWR_CON 0x4004 +#define PMU1_PWR_FSM 0x4008 +#define PMU1_INT_MASK_CON 0x400c +#define PMU1_WAKEUP_INT_CON 0x4010 +#define PMU1_WAKEUP_INT_ST 0x4014 +#define PMU1_WAKEUP_EDGE_CON 0x4018 +#define PMU1_WAKEUP_EDGE_ST 0x401c +#define PMU1_DDR_PWR_CON(i) (0x4020 + (i) * 4) +#define PMU1_DDR_PWR_SFTCON(i) (0x4030 + (i) * 4) +#define PMU1_DDR_PWR_FSM 0x4040 +#define PMU1_DDR_PWR_ST 0x4044 +#define PMU1_CRU_PWR_CON 0x4050 +#define PMU1_CRU_PWR_SFTCON 0x4054 +#define PMU1_CRU_PWR_FSM 0x4058 +#define PMU1_PLLPD_CON(i) (0x4060 + (i) * 4) +#define PMU1_PLLPD_SFTCON(i) (0x4068 + (i) * 4) +#define PMU1_STABLE_CNT_THRESH 0x4080 +#define PMU1_OSC_STABLE_CNT_THRESH 0x4084 +#define PMU1_WAKEUP_RST_CLR_CNT_THRESH 0x4088 +#define PMU1_PLL_LOCK_CNT_THRESH 0x408c +#define PMU1_WAKEUP_TIMEOUT_THRESH 0x4094 +#define PMU1_PWM_SWITCH_CNT_THRESH 0x4098 +#define PMU1_SYS_REG(i) (0x4100 + (i) * 4) + +#define PMU2_PWR_CON1 0x8000 +#define PMU2_DSU_PWR_CON 0x8004 +#define PMU2_DSU_PWR_SFTCON 0x8008 +#define PMU2_DSU_AUTO_PWR_CON 0x800c +#define PMU2_CPU_AUTO_PWR_CON(i) (0x8010 + (i) * 4) +#define PMU2_CPU_PWR_SFTCON(i) (0x8030 + (i) * 4) +#define PMU2_CORE_PWR_CON(i) (0x8050 + (i) * 4) +#define PMU2_CORE_PWR_SFTCON(i) (0x8058 + (i) * 4) +#define PMU2_CORE_AUTO_PWR_CON(i) (0x8060 + (i) * 4) +#define PMU2_CLUSTER_NOC_AUTO_CON 0x8068 +#define PMU2_CLUSTER_DBG_PWR_CON 0x806c +#define PMU2_CLUSTER_IDLE_CON 0x8070 +#define PMU2_CLUSTER_IDLE_SFTCON 0x8074 +#define PMU2_CLUSTER_IDLE_ACK 0x8078 +#define PMU2_CLUSTER_IDLE_ST 0x807c +#define PMU2_CLUSTER_ST 0x8080 +#define PMU2_SCU_PWR_FSM_STATUS(i) (0x8084 + (i) * 4) +#define PMU2_CORE_PCHANNEL_STATUS(i) (0x808c + (i) * 4) +#define PMU2_CPU_PWR_CHAIN_STABLE_CON 0x8098 +#define PMU2_CLUSTER_MEMPWR_GATE_SFTCON 0x809c +#define PMU2_DSU_STABLE_CNT_THRESH 0x80b0 +#define PMU2_DSU_PWRUP_CNT_THRESH 0x80b4 +#define PMU2_DSU_PWRDN_CNT_THRESH 0x80b8 +#define PMU2_CORE0_STABLE_CNT_THRESH 0x80bc +#define PMU2_CORE0_PWRUP_CNT_THRESH 0x80c0 +#define PMU2_CORE0_PWRDN_CNT_THRESH 0x80c4 +#define PMU2_CORE1_STABLE_CNT_THRESH 0x80c8 +#define PMU2_CORE1_PWRUP_CNT_THRESH 0x80cc +#define PMU2_CORE1_PWRDN_CNT_THRESH 0x80d0 +#define PMU2_DBG_RST_CNT_THRESH(i) (0x80d4 + (i) * 4) +#define PMU2_BUS_IDLE_CON(i) (0x8100 + (i) * 4) +#define PMU2_BUS_IDLE_SFTCON(i) (0x810c + (i) * 4) +#define PMU2_BUS_IDLE_ACK(i) (0x8118 + (i) * 4) +#define PMU2_BUS_IDLE_ST(i) (0x8120 + (i) * 4) +#define PMU2_BIU_AUTO_CON(i) (0x8128 + (i) * 4) +#define PMU2_PWR_GATE_CON(i) (0x8140 + (i) * 4) +#define PMU2_PWR_GATE_SFTCON(i) (0x814c + (i) * 4) +#define PMU2_VOL_GATE_CON(i) (0x8158 + (i) * 4) +#define PMU2_PWR_UP_CHAIN_STABLE_CON(i) (0x8164 + (i) * 4) +#define PMU2_PWR_DWN_CHAIN_STABLE_CON(i)(0x8170 + (i) * 4) +#define PMU2_PWR_STABLE_CHAIN_CNT_THRES 0x817c +#define PMU2_PWR_GATE_ST(i) (0x8180 + (i) * 4) +#define PMU2_PWR_GATE_FSM 0x8188 +#define PMU2_VOL_GATE_FAST_CON 0x818c +#define PMU2_GPU_PWRUP_CNT 0x8190 +#define PMU2_GPU_PWRDN_CNT 0x8194 +#define PMU2_NPU_PWRUP_CNT 0x8198 +#define PMU2_NPU_PWRDN_CNT 0x819c +#define PMU2_MEMPWR_GATE_SFTCON(i) (0x81a0 + (i) * 4) +#define PMU2_MEMPWR_MD_GATE_SFTCON(i) (0x81b0 + (i) * 4) +#define PMU2_MEMPWR_MD_GATE_STATUS 0x81bc +#define PMU2_SUBMEM_PWR_ACK_BYPASS(i) (0x81c0 + (i) * 4) +#define PMU2_QCHANNEL_PWR_CON 0x81d0 +#define PMU2_QCHANNEL_PWR_SFTCON 0x81d4 +#define PMU2_QCHANNEL_STATUS 0x81d8 +#define PMU2_DEBUG_INFO_SEL 0x81e0 +#define PMU2_VOP_SUBPD_STATE 0x81e4 +#define PMU2_PWR_CHAIN0_ST(i) (0x81e8 + (i) * 4) +#define PMU2_PWR_CHAIN1_ST(i) (0x81f0 + (i) * 4) +#define PMU2_PWR_MEM_ST(i) (0x81f8 + (i) * 4) +#define PMU2_BISR_CON(i) (0x8200 + (i) * 4) +#define PMU2_BISR_STATUS(i) (0x8280 + (i) * 4) + +#define PMU2_QCH_PWR_MSK 0x7f + +#define PD_CTR_LOOP 500 +#define PD_CHECK_LOOP 500 +#define WFEI_CHECK_LOOP 500 +#define BUS_IDLE_LOOP 1000 +#define QCH_PWR_LOOP 5000 + +/* PMU1SCRU */ +#define PMU1SCRU_GATE_CON(i) (0x800 + (i) * 4) + +/* PMU_GRF */ +#define PMU0_GRF_SOC_CON(i) ((i) * 4) +#define PMU0_GRF_OS_REGS(i) (0x80 + ((i) - 8) * 4) +#define PMU1_GRF_SOC_CON(i) ((i) * 4) +#define PMU0_GRF_IO_RET_CON(i) (0x20 + (i) * 4) + +/* PMU_SGRF */ +#define PMU0_SGRF_SOC_CON(i) ((i) * 4) +#define PMU1_SGRF_SOC_CON(i) ((i) * 4) + +/* sys grf */ +#define GRF_CPU_STATUS0 0x0420 + +#define CORES_PM_DISABLE 0x0 +#define PD_CHECK_LOOP 500 +#define WFEI_CHECK_LOOP 500 + +/* The ways of cores power domain contorlling */ +enum cores_pm_ctr_mode { + core_pwr_pd = 0, + core_pwr_wfi = 1, + core_pwr_wfi_int = 2 +}; + +/* PMU0_PWR_CON */ +enum pmu0_pwr_con { + pmu0_powermode_en = 0, + pmu0_pmu1_pwr_bypass = 1, + pmu0_pmu1_bus_bypass = 2, + pmu0_wkup_bypass = 3, + pmu0_pmic_bypass = 4, + pmu0_reset_bypass = 5, + pmu0_freq_sw_bypass = 6, + pmu0_osc_dis_bypass = 7, + pmu0_pmu1_pwr_gt_en = 8, + pmu0_pmu1_pwr_gt_sft_en = 9, + pmu0_pmu1_mem_gt_sft_en = 10, + pmu0_pmu1_bus_idle_en = 11, + pmu0_pmu1_bus_idle_sft_en = 12, + pmu0_pmu1_biu_auto_en = 13, + pmu0_pwr_off_io_en = 14, +}; + +/* PMU1_PWR_CON */ +enum pmu1_pwr_con { + powermode_en = 0, + dsu_bypass = 1, + bus_bypass = 4, + ddr_bypass = 5, + pwrdn_bypass = 6, + cru_bypass = 7, + qch_bypass = 8, + core_bypass = 9, + cpu_sleep_wfi_dis = 12, +}; + +/* PMU1_DDR_PWR_CON */ +enum pmu1_ddr_pwr_con { + ddr_sref_en = 0, + ddr_sref_a_en = 1, + ddrio_ret_en = 2, + ddrio_ret_exit_en = 5, + ddrio_rstiov_en = 6, + ddrio_rstiov_exit_en = 7, + ddr_gating_a_en = 8, + ddr_gating_c_en = 9, + ddr_gating_p_en = 10, +}; + +/* PMU_CRU_PWR_CON */ +enum pmu1_cru_pwr_con { + alive_32k_en = 0, + osc_dis_en = 1, + wakeup_rst_en = 2, + input_clamp_en = 3, + alive_osc_mode_en = 4, + power_off_en = 5, + pwm_switch_en = 6, + pwm_gpio_ioe_en = 7, + pwm_switch_io = 8, + pd_clk_src_gate_en = 9, +}; + +/* PMU_PLLPD_CON */ +enum pmu1_pllpd_con { + B0PLL_PD_EN, + B1PLL_PD_EN, + LPLL_PD_EN, + D0APLL_PD_EN, + D0BPLL_PD_EN, + D1APLL_PD_EN, + D1BPLL_PD_EN, + D2APLL_PD_EN, + D2BPLL_PD_EN, + D3APLL_PD_EN, + D3BPLL_PD_EN, + V0PLL_PD_EN, + AUPLL_PD_EN, + GPLL_PD_EN, + CPLL_PD_EN, + NPLL_PD_EN, + PPLL_PD_EN = 0, + SPLL_PD_EN = 1, +}; + +enum pmu1_wakeup_int { + WAKEUP_CPU0_INT_EN, + WAKEUP_CPU1_INT_EN, + WAKEUP_CPU2_INT_EN, + WAKEUP_CPU3_INT_EN, + WAKEUP_CPU4_INT_EN, + WAKEUP_CPU5_INT_EN, + WAKEUP_CPU6_INT_EN, + WAKEUP_CPU7_INT_EN, + WAKEUP_GPIO0_INT_EN, + WAKEUP_SDMMC_EN, + WAKEUP_SDIO_EN, + WAKEUP_USBDEV_EN, + WAKEUP_UART0_EN, + WAKEUP_VAD_EN, + WAKEUP_TIMER_EN, + WAKEUP_SOC_INT_EN, + WAKEUP_TIMEROUT_EN, + WAKEUP_PMUMCU_CEC_EN = 20, +}; + +enum pmu2_dsu_auto_pwr_con { + dsu_pm_en = 0, + dsu_pm_int_wakeup_en = 1, + dsu_pm_sft_wakeup_en = 3, +}; + +enum pmu2_cpu_auto_pwr_con { + cpu_pm_en = 0, + cpu_pm_int_wakeup_en = 1, + cpu_pm_sft_wakeup_en = 3, +}; + +enum pmu2_core_auto_pwr_con { + core_pm_en = 0, + core_pm_int_wakeup_en = 1, + core_pm_int_wakeup_glb_msk = 2, + core_pm_sft_wakeup_en = 3, +}; + +enum pmu2_dsu_power_con { + DSU_PWRDN_EN, + DSU_PWROFF_EN, + BIT_FULL_EN, + DSU_RET_EN, + CLUSTER_CLK_SRC_GT_EN, +}; + +enum pmu2_core_power_con { + CORE_PWRDN_EN, + CORE_PWROFF_EN, + CORE_CPU_PWRDN_EN, + CORE_PWR_CNT_EN, +}; + +enum pmu2_cluster_idle_con { + IDLE_REQ_BIGCORE0_EN = 0, + IDLE_REQ_BIGCORE1_EN = 2, + IDLE_REQ_DSU_EN = 4, + IDLE_REQ_LITDSU_EN = 5, + IDLE_REQ_ADB400_CORE_QCH_EN = 6, +}; + +enum qos_id { + QOS_ISP0_MWO = 0, + QOS_ISP0_MRO = 1, + QOS_ISP1_MWO = 2, + QOS_ISP1_MRO = 3, + QOS_VICAP_M0 = 4, + QOS_VICAP_M1 = 5, + QOS_FISHEYE0 = 6, + QOS_FISHEYE1 = 7, + QOS_VOP_M0 = 8, + QOS_VOP_M1 = 9, + QOS_RKVDEC0 = 10, + QOS_RKVDEC1 = 11, + QOS_AV1 = 12, + QOS_RKVENC0_M0RO = 13, + QOS_RKVENC0_M1RO = 14, + QOS_RKVENC0_M2WO = 15, + QOS_RKVENC1_M0RO = 16, + QOS_RKVENC1_M1RO = 17, + QOS_RKVENC1_M2WO = 18, + QOS_DSU_M0 = 19, + QOS_DSU_M1 = 20, + QOS_DSU_MP = 21, + QOS_DEBUG = 22, + QOS_GPU_M0 = 23, + QOS_GPU_M1 = 24, + QOS_GPU_M2 = 25, + QOS_GPU_M3 = 26, + QOS_NPU1 = 27, + QOS_NPU0_MRO = 28, + QOS_NPU2 = 29, + QOS_NPU0_MWR = 30, + QOS_MCU_NPU = 31, + QOS_JPEG_DEC = 32, + QOS_JPEG_ENC0 = 33, + QOS_JPEG_ENC1 = 34, + QOS_JPEG_ENC2 = 35, + QOS_JPEG_ENC3 = 36, + QOS_RGA2_MRO = 37, + QOS_RGA2_MWO = 38, + QOS_RGA3_0 = 39, + QOS_RGA3_1 = 40, + QOS_VDPU = 41, + QOS_IEP = 42, + QOS_HDCP0 = 43, + QOS_HDCP1 = 44, + QOS_HDMIRX = 45, + QOS_GIC600_M0 = 46, + QOS_GIC600_M1 = 47, + QOS_MMU600PCIE_TCU = 48, + QOS_MMU600PHP_TBU = 49, + QOS_MMU600PHP_TCU = 50, + QOS_USB3_0 = 51, + QOS_USB3_1 = 52, + QOS_USBHOST_0 = 53, + QOS_USBHOST_1 = 54, + QOS_EMMC = 55, + QOS_FSPI = 56, + QOS_SDIO = 57, + QOS_DECOM = 58, + QOS_DMAC0 = 59, + QOS_DMAC1 = 60, + QOS_DMAC2 = 61, + QOS_GIC600M = 62, + QOS_DMA2DDR = 63, + QOS_MCU_DDR = 64, + QOS_VAD = 65, + QOS_MCU_PMU = 66, + QOS_CRYPTOS = 67, + QOS_CRYPTONS = 68, + QOS_DCF = 69, + QOS_SDMMC = 70, +}; + +enum pmu2_pdid { + PD_GPU = 0, + PD_NPU = 1, + PD_VCODEC = 2, + PD_NPUTOP = 3, + PD_NPU1 = 4, + PD_NPU2 = 5, + PD_VENC0 = 6, + PD_VENC1 = 7, + PD_RKVDEC0 = 8, + PD_RKVDEC1 = 9, + PD_VDPU = 10, + PD_RGA30 = 11, + PD_AV1 = 12, + PD_VI = 13, + PD_FEC = 14, + PD_ISP1 = 15, + PD_RGA31 = 16, + PD_VOP = 17, + PD_VO0 = 18, + PD_VO1 = 19, + PD_AUDIO = 20, + PD_PHP = 21, + PD_GMAC = 22, + PD_PCIE = 23, + PD_NVM = 24, + PD_NVM0 = 25, + PD_SDIO = 26, + PD_USB = 27, + PD_SECURE = 28, + PD_SDMMC = 29, + PD_CRYPTO = 30, + PD_CENTER = 31, + PD_DDR01 = 32, + PD_DDR23 = 33, +}; + +enum pmu2_pd_repair_id { + PD_RPR_PMU = 0, + PD_RPR_GPU = 1, + PD_RPR_NPUTOP = 2, + PD_RPR_NPU1 = 3, + PD_RPR_NPU2 = 4, + PD_RPR_VENC0 = 5, + PD_RPR_VENC1 = 6, + PD_RPR_RKVDEC0 = 7, + PD_RPR_RKVDEC1 = 8, + PD_RPR_VDPU = 9, + PD_RPR_RGA30 = 10, + PD_RPR_AV1 = 11, + PD_RPR_VI = 12, + PD_RPR_FEC = 13, + PD_RPR_ISP1 = 14, + PD_RPR_RGA31 = 15, + PD_RPR_VOP = 16, + PD_RPR_VO0 = 17, + PD_RPR_VO1 = 18, + PD_RPR_AUDIO = 19, + PD_RPR_PHP = 20, + PD_RPR_GMAC = 21, + PD_RPR_PCIE = 22, + PD_RPR_NVM0 = 23, + PD_RPR_SDIO = 24, + PD_RPR_USB = 25, + PD_RPR_SDMMC = 26, + PD_RPR_CRYPTO = 27, + PD_RPR_CENTER = 28, + PD_RPR_DDR01 = 29, + PD_RPR_DDR23 = 30, + PD_RPR_BUS = 31, +}; + +enum pmu2_bus_id { + BUS_ID_GPU = 0, + BUS_ID_NPUTOP = 1, + BUS_ID_NPU1 = 2, + BUS_ID_NPU2 = 3, + BUS_ID_RKVENC0 = 4, + BUS_ID_RKVENC1 = 5, + BUS_ID_RKVDEC0 = 6, + BUS_ID_RKVDEC1 = 7, + BUS_ID_VDPU = 8, + BUS_ID_AV1 = 9, + BUS_ID_VI = 10, + BUS_ID_ISP = 11, + BUS_ID_RGA31 = 12, + BUS_ID_VOP = 13, + BUS_ID_VOP_CHANNEL = 14, + BUS_ID_VO0 = 15, + BUS_ID_VO1 = 16, + BUS_ID_AUDIO = 17, + BUS_ID_NVM = 18, + BUS_ID_SDIO = 19, + BUS_ID_USB = 20, + BUS_ID_PHP = 21, + BUS_ID_VO1USBTOP = 22, + BUS_ID_SECURE = 23, + BUS_ID_SECURE_CENTER_CHANNEL = 24, + BUS_ID_SECURE_VO1USB_CHANNEL = 25, + BUS_ID_CENTER = 26, + BUS_ID_CENTER_CHANNEL = 27, + BUS_ID_MSCH0 = 28, + BUS_ID_MSCH1 = 29, + BUS_ID_MSCH2 = 30, + BUS_ID_MSCH3 = 31, + BUS_ID_MSCH = 32, + BUS_ID_BUS = 33, + BUS_ID_TOP = 34, +}; + +enum pmu2_mem_st { + PD_NPU_TOP_MEM_ST = 11, + PD_NPU1_MEM_ST = 12, + PD_NPU2_MEM_ST = 13, + PD_VENC0_MEM_ST = 14, + PD_VENC1_MEM_ST = 15, + PD_RKVDEC0_MEM_ST = 16, + PD_RKVDEC1_MEM_ST = 17, + PD_RGA30_MEM_ST = 19, + PD_AV1_MEM_ST = 20, + PD_VI_MEM_ST = 21, + PD_FEC_MEM_ST = 22, + PD_ISP1_MEM_ST = 23, + PD_RGA31_MEM_ST = 24, + PD_VOP_MEM_ST = 25, + PD_VO0_MEM_ST = 26, + PD_VO1_MEM_ST = 27, + PD_AUDIO_MEM_ST = 28, + PD_PHP_MEM_ST = 29, + PD_GMAC_MEM_ST = 30, + PD_PCIE_MEM_ST = 31, + PD_NVM0_MEM_ST = 33, + PD_SDIO_MEM_ST = 34, + PD_USB_MEM_ST = 35, + PD_SDMMC_MEM_ST = 37, +}; + +enum pmu2_qid { + QID_PHPMMU_TBU = 0, + QID_PHPMMU_TCU = 1, + QID_PCIEMMU_TBU0 = 2, + QID_PCIEMU_TCU = 3, + QID_PHP_GICITS = 4, + QID_BUS_GICITS0 = 5, + QID_BUS_GICITS1 = 6, +}; + +/* PMU_DSU_PWR_CON */ +enum pmu_dsu_pwr_con { + DSU_PWRDN_ENA = 2, + DSU_PWROFF_ENA, + DSU_RET_ENA = 6, + CLUSTER_CLK_SRC_GATE_ENA, + DSU_PWR_CON_END +}; + +enum cpu_power_state { + CPU_POWER_ON, + CPU_POWER_OFF, + CPU_EMULATION_OFF, + CPU_RETENTION, + CPU_DEBUG +}; + +enum dsu_power_state { + DSU_POWER_ON, + CLUSTER_TRANSFER_IDLE, + DSU_POWER_DOWN, + DSU_OFF, + DSU_WAKEUP, + DSU_POWER_UP, + CLUSTER_TRANSFER_RESUME, + DSU_FUNCTION_RETENTION +}; + +/* PMU2_CLUSTER_STS 0x8080 */ +enum pmu2_cluster_sts_bits { + pd_cpu0_dwn = 0, + pd_cpu1_dwn, + pd_cpu2_dwn, + pd_cpu3_dwn, + pd_cpu4_dwn, + pd_cpu5_dwn, + pd_cpu6_dwn, + pd_cpu7_dwn, + pd_core0_dwn, + pd_core1_dwn +}; + +#define CLUSTER_STS_NONBOOT_CPUS_DWN 0xfe + +enum cpu_off_trigger { + CPU_OFF_TRIGGER_WFE = 0, + CPU_OFF_TRIGGER_REQ_EML, + CPU_OFF_TRIGGER_REQ_WFI, + CPU_OFF_TRIGGER_REQ_WFI_NBT_CPU, + CPU_OFF_TRIGGER_REQ_WFI_NBT_CPU_SRAM +}; + +/***************************************************************************** + * power domain on or off + *****************************************************************************/ +enum pmu_pd_state { + pmu_pd_on = 0, + pmu_pd_off = 1 +}; + +enum bus_state { + bus_active, + bus_idle, +}; + +#define RK_CPU_STATUS_OFF 0 +#define RK_CPU_STATUS_ON 1 +#define RK_CPU_STATUS_BUSY -1 + +#define PD_CTR_LOOP 500 +#define MAX_WAIT_COUNT 500 + +#define pmu_bus_idle_st(id) \ + (!!(mmio_read_32(PMU_BASE + PMU2_BUS_IDLE_ST((id) / 32)) & BIT((id) % 32))) + +#define pmu_bus_idle_ack(id) \ + (!!(mmio_read_32(PMU_BASE + PMU2_BUS_IDLE_ACK((id) / 32)) & BIT((id) % 32))) + +void pm_pll_wait_lock(uint32_t pll_base); +#endif /* __PMU_H__ */ diff --git a/plat/rockchip/rk3588/drivers/scmi/rk3588_clk.c b/plat/rockchip/rk3588/drivers/scmi/rk3588_clk.c new file mode 100644 index 00000000..ab3af5f3 --- /dev/null +++ b/plat/rockchip/rk3588/drivers/scmi/rk3588_clk.c @@ -0,0 +1,2463 @@ +/* + * Copyright (c) 2024, Rockchip, Inc. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <assert.h> +#include <errno.h> + +#include <drivers/delay_timer.h> +#include <drivers/scmi.h> +#include <lib/mmio.h> +#include <platform_def.h> + +#include <plat_private.h> +#include "rk3588_clk.h" +#include <rockchip_sip_svc.h> +#include <scmi_clock.h> +#include <soc.h> + +enum pll_type_sel { + PLL_SEL_AUTO, /* all plls (normal pll or pvtpll) */ + PLL_SEL_PVT, + PLL_SEL_NOR, + PLL_SEL_AUTO_NOR /* all normal plls (apll/gpll/npll) */ +}; + +#define DIV_ROUND_UP(n, d) (((n) + (d) - 1) / (d)) + +#define RK3588_CPUL_PVTPLL_CON0_L 0x40 +#define RK3588_CPUL_PVTPLL_CON0_H 0x44 +#define RK3588_CPUL_PVTPLL_CON1 0x48 +#define RK3588_CPUL_PVTPLL_CON2 0x4c +#define RK3588_CPUB_PVTPLL_CON0_L 0x00 +#define RK3588_CPUB_PVTPLL_CON0_H 0x04 +#define RK3588_CPUB_PVTPLL_CON1 0x08 +#define RK3588_CPUB_PVTPLL_CON2 0x0c +#define RK3588_DSU_PVTPLL_CON0_L 0x60 +#define RK3588_DSU_PVTPLL_CON0_H 0x64 +#define RK3588_DSU_PVTPLL_CON1 0x70 +#define RK3588_DSU_PVTPLL_CON2 0x74 +#define RK3588_GPU_PVTPLL_CON0_L 0x00 +#define RK3588_GPU_PVTPLL_CON0_H 0x04 +#define RK3588_GPU_PVTPLL_CON1 0x08 +#define RK3588_GPU_PVTPLL_CON2 0x0c +#define RK3588_NPU_PVTPLL_CON0_L 0x0c +#define RK3588_NPU_PVTPLL_CON0_H 0x10 +#define RK3588_NPU_PVTPLL_CON1 0x14 +#define RK3588_NPU_PVTPLL_CON2 0x18 +#define RK3588_PVTPLL_MAX_LENGTH 0x3f + +#define GPLL_RATE 1188000000 +#define CPLL_RATE 1500000000 +#define SPLL_RATE 702000000 +#define AUPLL_RATE 786431952 +#define NPLL_RATE 850000000 + +#define MAX_RATE_TABLE 16 + +#define CLKDIV_6BITS_SHF(div, shift) BITS_WITH_WMASK(div, 0x3fU, shift) +#define CLKDIV_5BITS_SHF(div, shift) BITS_WITH_WMASK(div, 0x1fU, shift) +#define CLKDIV_4BITS_SHF(div, shift) BITS_WITH_WMASK(div, 0xfU, shift) +#define CLKDIV_3BITS_SHF(div, shift) BITS_WITH_WMASK(div, 0x7U, shift) +#define CLKDIV_2BITS_SHF(div, shift) BITS_WITH_WMASK(div, 0x3U, shift) +#define CLKDIV_1BITS_SHF(div, shift) BITS_WITH_WMASK(div, 0x1U, shift) + +#define CPU_PLL_PATH_SLOWMODE BITS_WITH_WMASK(0U, 0x3U, 0) +#define CPU_PLL_PATH_NORMAL BITS_WITH_WMASK(1U, 0x3U, 0) +#define CPU_PLL_PATH_DEEP_SLOW BITS_WITH_WMASK(2U, 0x3U, 0) + +#define CRU_PLL_POWER_DOWN BIT_WITH_WMSK(13) +#define CRU_PLL_POWER_UP WMSK_BIT(13) + +/* core_i: from gpll or apll */ +#define CLK_CORE_I_SEL_APLL WMSK_BIT(6) +#define CLK_CORE_I_SEL_GPLL BIT_WITH_WMSK(6) + +/* clk_core: + * from normal pll(core_i: gpll or apll) path or direct pass from apll + */ + +/* cpul clk path */ +#define CPUL_CLK_PATH_NOR_XIN BITS_WITH_WMASK(0U, 0x3U, 14) +#define CPUL_CLK_PATH_NOR_GPLL BITS_WITH_WMASK(1U, 0x3U, 14) +#define CPUL_CLK_PATH_NOR_LPLL BITS_WITH_WMASK(2U, 0x3U, 14) + +#define CPUL_CLK_PATH_LPLL (BITS_WITH_WMASK(0U, 0x3U, 5) | \ + BITS_WITH_WMASK(0U, 0x3U, 12)) +#define CPUL_CLK_PATH_DIR_LPLL (BITS_WITH_WMASK(0x1, 0x3U, 5) | \ + BITS_WITH_WMASK(1U, 0x3U, 12)) +#define CPUL_CLK_PATH_PVTPLL (BITS_WITH_WMASK(0x2, 0x3U, 5) | \ + BITS_WITH_WMASK(2U, 0x3U, 12)) + +#define CPUL_PVTPLL_PATH_DEEP_SLOW BITS_WITH_WMASK(0U, 0x1U, 14) +#define CPUL_PVTPLL_PATH_PVTPLL BITS_WITH_WMASK(1U, 0x1U, 14) + +/* cpub01 clk path */ +#define CPUB01_CLK_PATH_NOR_XIN BITS_WITH_WMASK(0U, 0x3U, 6) +#define CPUB01_CLK_PATH_NOR_GPLL BITS_WITH_WMASK(1U, 0x3U, 6) +#define CPUB01_CLK_PATH_NOR_B0PLL BITS_WITH_WMASK(2U, 0x3U, 6) + +#define CPUB01_CLK_PATH_B0PLL BITS_WITH_WMASK(0U, 0x3U, 13) +#define CPUB01_CLK_PATH_DIR_B0PLL BITS_WITH_WMASK(1U, 0x3U, 13) +#define CPUB01_CLK_PATH_B0_PVTPLL BITS_WITH_WMASK(2U, 0x3U, 13) + +#define CPUB01_CLK_PATH_B1PLL BITS_WITH_WMASK(0U, 0x3U, 5) +#define CPUB01_CLK_PATH_DIR_B1PLL BITS_WITH_WMASK(1U, 0x3U, 5) +#define CPUB01_CLK_PATH_B1_PVTPLL BITS_WITH_WMASK(2U, 0x3U, 5) + +#define CPUB01_PVTPLL_PATH_DEEP_SLOW BITS_WITH_WMASK(0U, 0x1U, 2) +#define CPUB01_PVTPLL_PATH_PVTPLL BITS_WITH_WMASK(1U, 0x1U, 2) + +#define CPUB_PCLK_PATH_100M BITS_WITH_WMASK(0U, 0x3U, 0) +#define CPUB_PCLK_PATH_50M BITS_WITH_WMASK(1U, 0x3U, 0) +#define CPUB_PCLK_PATH_24M BITS_WITH_WMASK(2U, 0x3U, 0) + +/* dsu clk path */ +#define SCLK_DSU_PATH_NOR_B0PLL BITS_WITH_WMASK(0U, 0x3U, 12) +#define SCLK_DSU_PATH_NOR_B1PLL BITS_WITH_WMASK(1U, 0x3U, 12) +#define SCLK_DSU_PATH_NOR_LPLL BITS_WITH_WMASK(2U, 0x3U, 12) +#define SCLK_DSU_PATH_NOR_GPLL BITS_WITH_WMASK(3U, 0x3U, 12) + +#define DSU_PVTPLL_PATH_DEEP_SLOW BITS_WITH_WMASK(0U, 0x1U, 15) +#define DSU_PVTPLL_PATH_PVTPLL BITS_WITH_WMASK(1U, 0x1U, 15) + +#define SCLK_DSU_PATH_NOR_PLL WMSK_BIT(0) +#define SCLK_DSU_PATH_PVTPLL BIT_WITH_WMSK(0) + +/* npu clk path */ +#define NPU_CLK_PATH_NOR_GPLL BITS_WITH_WMASK(0U, 0x7U, 7) +#define NPU_CLK_PATH_NOR_CPLL BITS_WITH_WMASK(1U, 0x7U, 7) +#define NPU_CLK_PATH_NOR_AUPLL BITS_WITH_WMASK(2U, 0x7U, 7) +#define NPU_CLK_PATH_NOR_NPLL BITS_WITH_WMASK(3U, 0x7U, 7) +#define NPU_CLK_PATH_NOR_SPLL BITS_WITH_WMASK(4U, 0x7U, 7) + +#define NPU_CLK_PATH_NOR_PLL WMSK_BIT(0) +#define NPU_CLK_PATH_PVTPLL BIT_WITH_WMSK(0) + +/* gpu clk path */ +#define GPU_CLK_PATH_NOR_GPLL BITS_WITH_WMASK(0U, 0x7U, 5) +#define GPU_CLK_PATH_NOR_CPLL BITS_WITH_WMASK(1U, 0x7U, 5) +#define GPU_CLK_PATH_NOR_AUPLL BITS_WITH_WMASK(2U, 0x7U, 5) +#define GPU_CLK_PATH_NOR_NPLL BITS_WITH_WMASK(3U, 0x7U, 5) +#define GPU_CLK_PATH_NOR_SPLL BITS_WITH_WMASK(4U, 0x7U, 5) +#define GPU_CLK_PATH_NOR_PLL WMSK_BIT(14) +#define GPU_CLK_PATH_PVTPLL BIT_WITH_WMSK(14) + +#define PVTPLL_NEED(type, length) (((type) == PLL_SEL_PVT || \ + (type) == PLL_SEL_AUTO) && \ + (length)) + +struct pvtpll_table { + unsigned int rate; + uint32_t length; + uint32_t ring_sel; +}; + +struct sys_clk_info_t { + struct pvtpll_table *cpul_table; + struct pvtpll_table *cpub01_table; + struct pvtpll_table *cpub23_table; + struct pvtpll_table *gpu_table; + struct pvtpll_table *npu_table; + unsigned int cpul_rate_count; + unsigned int cpub01_rate_count; + unsigned int cpub23_rate_count; + unsigned int gpu_rate_count; + unsigned int npu_rate_count; + unsigned long cpul_rate; + unsigned long dsu_rate; + unsigned long cpub01_rate; + unsigned long cpub23_rate; + unsigned long gpu_rate; + unsigned long npu_rate; +}; + +#define RK3588_SCMI_CLOCK(_id, _name, _data, _table, _cnt, _is_s) \ +{ \ + .id = _id, \ + .name = _name, \ + .clk_ops = _data, \ + .rate_table = _table, \ + .rate_cnt = _cnt, \ + .is_security = _is_s, \ +} + +#define ROCKCHIP_PVTPLL(_rate, _sel, _len) \ +{ \ + .rate = _rate##U, \ + .ring_sel = _sel, \ + .length = _len, \ +} + +static struct pvtpll_table rk3588_cpul_pvtpll_table[] = { + /* rate_hz, ring_sel, length */ + ROCKCHIP_PVTPLL(1800000000, 1, 15), + ROCKCHIP_PVTPLL(1704000000, 1, 15), + ROCKCHIP_PVTPLL(1608000000, 1, 15), + ROCKCHIP_PVTPLL(1416000000, 1, 15), + ROCKCHIP_PVTPLL(1200000000, 1, 17), + ROCKCHIP_PVTPLL(1008000000, 1, 22), + ROCKCHIP_PVTPLL(816000000, 1, 32), + ROCKCHIP_PVTPLL(600000000, 0, 0), + ROCKCHIP_PVTPLL(408000000, 0, 0), + { /* sentinel */ }, +}; + +static struct pvtpll_table rk3588_cpub0_pvtpll_table[] = { + /* rate_hz, ring_sel, length */ + ROCKCHIP_PVTPLL(2400000000, 1, 11), + ROCKCHIP_PVTPLL(2352000000, 1, 11), + ROCKCHIP_PVTPLL(2304000000, 1, 11), + ROCKCHIP_PVTPLL(2256000000, 1, 11), + ROCKCHIP_PVTPLL(2208000000, 1, 11), + ROCKCHIP_PVTPLL(2112000000, 1, 11), + ROCKCHIP_PVTPLL(2016000000, 1, 11), + ROCKCHIP_PVTPLL(1800000000, 1, 11), + ROCKCHIP_PVTPLL(1608000000, 1, 11), + ROCKCHIP_PVTPLL(1416000000, 1, 13), + ROCKCHIP_PVTPLL(1200000000, 1, 17), + ROCKCHIP_PVTPLL(1008000000, 1, 23), + ROCKCHIP_PVTPLL(816000000, 1, 33), + ROCKCHIP_PVTPLL(600000000, 0, 0), + ROCKCHIP_PVTPLL(408000000, 0, 0), + { /* sentinel */ }, +}; + +static struct +pvtpll_table rk3588_cpub1_pvtpll_table[ARRAY_SIZE(rk3588_cpub0_pvtpll_table)] = { 0 }; + +static struct pvtpll_table rk3588_gpu_pvtpll_table[] = { + /* rate_hz, ring_sel, length */ + ROCKCHIP_PVTPLL(1000000000, 1, 12), + ROCKCHIP_PVTPLL(900000000, 1, 12), + ROCKCHIP_PVTPLL(800000000, 1, 12), + ROCKCHIP_PVTPLL(700000000, 1, 13), + ROCKCHIP_PVTPLL(600000000, 1, 17), + ROCKCHIP_PVTPLL(500000000, 1, 25), + ROCKCHIP_PVTPLL(400000000, 1, 38), + ROCKCHIP_PVTPLL(300000000, 1, 55), + ROCKCHIP_PVTPLL(200000000, 0, 0), + { /* sentinel */ }, +}; + +static struct pvtpll_table rk3588_npu_pvtpll_table[] = { + /* rate_hz, ring_sel, length */ + ROCKCHIP_PVTPLL(1000000000, 1, 12), + ROCKCHIP_PVTPLL(900000000, 1, 12), + ROCKCHIP_PVTPLL(800000000, 1, 12), + ROCKCHIP_PVTPLL(700000000, 1, 13), + ROCKCHIP_PVTPLL(600000000, 1, 17), + ROCKCHIP_PVTPLL(500000000, 1, 25), + ROCKCHIP_PVTPLL(400000000, 1, 38), + ROCKCHIP_PVTPLL(300000000, 1, 55), + ROCKCHIP_PVTPLL(200000000, 0, 0), + { /* sentinel */ }, +}; + +static unsigned long rk3588_cpul_rates[] = { + 408000000, 600000000, 816000000, 1008000000, + 1200000000, 1416000000, 1608000000, 1800000063, +}; + +static unsigned long rk3588_cpub_rates[] = { + 408000000, 816000000, 1008000000, 1200000000, + 1416000000, 1608000000, 1800000000, 2016000000, + 2208000000, 2304000000, 2400000063 +}; + +static unsigned long rk3588_gpu_rates[] = { + 200000000, 300000000, 400000000, 500000000, + 600000000, 700000000, 800000000, 900000000, + 1000000063 +}; + +static unsigned long rk3588_sbus_rates[] = { + 24000000, 50000000, 100000000, 150000000, 200000000, + 250000000, 350000000, 700000000 +}; + +static unsigned long rk3588_sdmmc_rates[] = { + 400000, 24000000, 50000000, 100000000, 150000000, 200000000, + 300000000, 400000000, 600000000, 700000000 +}; + +static struct sys_clk_info_t sys_clk_info; +static int clk_scmi_dsu_set_rate(rk_scmi_clock_t *clock, unsigned long rate); + +static struct pvtpll_table *rkclk_get_pvtpll_config(struct pvtpll_table *table, + unsigned int count, + unsigned int freq_hz) +{ + int i; + + for (i = 0; i < count; i++) { + if (freq_hz == table[i].rate) + return &table[i]; + } + return NULL; +} + +static int clk_cpul_set_rate(unsigned long rate, enum pll_type_sel type) +{ + struct pvtpll_table *pvtpll; + int div; + + if (rate == 0) + return SCMI_INVALID_PARAMETERS; + + pvtpll = rkclk_get_pvtpll_config(sys_clk_info.cpul_table, + sys_clk_info.cpul_rate_count, rate); + if (pvtpll == NULL) + return SCMI_INVALID_PARAMETERS; + + /* set lpll */ + if (PVTPLL_NEED(type, pvtpll->length) != 0) { + /* set clock gating interval */ + mmio_write_32(LITCOREGRF_BASE + RK3588_CPUL_PVTPLL_CON2, + 0x00040000); + /* set ring sel */ + mmio_write_32(LITCOREGRF_BASE + RK3588_CPUL_PVTPLL_CON0_L, + 0x07000000 | (pvtpll->ring_sel << 8)); + /* set length */ + mmio_write_32(LITCOREGRF_BASE + RK3588_CPUL_PVTPLL_CON0_H, + 0x003f0000 | pvtpll->length); + /* set cal cnt = 24, T = 1us */ + mmio_write_32(LITCOREGRF_BASE + RK3588_CPUL_PVTPLL_CON1, + 0x18); + /* enable pvtpll */ + mmio_write_32(LITCOREGRF_BASE + RK3588_CPUL_PVTPLL_CON0_L, + 0x00020002); + /* start monitor */ + mmio_write_32(LITCOREGRF_BASE + RK3588_CPUL_PVTPLL_CON0_L, + 0x00010001); + /* set corel mux pvtpll */ + mmio_write_32(DSUCRU_BASE + CRU_CLKSEL_CON(7), + CPUL_PVTPLL_PATH_PVTPLL); + mmio_write_32(DSUCRU_BASE + CRU_CLKSEL_CON(6), + CPUL_CLK_PATH_PVTPLL); + mmio_write_32(DSUCRU_BASE + CRU_CLKSEL_CON(7), + CPUL_CLK_PATH_PVTPLL); + return 0; + } + + /* set clk corel div */ + div = DIV_ROUND_UP(GPLL_RATE, rate) - 1; + mmio_write_32(DSUCRU_BASE + CRU_CLKSEL_CON(6), + CLKDIV_5BITS_SHF(div, 0) | CLKDIV_5BITS_SHF(div, 7)); + mmio_write_32(DSUCRU_BASE + CRU_CLKSEL_CON(7), + CLKDIV_5BITS_SHF(div, 0) | CLKDIV_5BITS_SHF(div, 7)); + /* set corel mux gpll */ + mmio_write_32(DSUCRU_BASE + CRU_CLKSEL_CON(5), + CPUL_CLK_PATH_NOR_GPLL); + mmio_write_32(DSUCRU_BASE + CRU_CLKSEL_CON(6), + CPUL_CLK_PATH_LPLL); + mmio_write_32(DSUCRU_BASE + CRU_CLKSEL_CON(7), + CPUL_CLK_PATH_LPLL); + + return 0; +} + +static int clk_scmi_cpul_set_rate(rk_scmi_clock_t *clock, unsigned long rate) +{ + int ret; + + if (rate == 0) + return SCMI_INVALID_PARAMETERS; + + ret = clk_cpul_set_rate(rate, PLL_SEL_AUTO); + if (ret == 0) { + sys_clk_info.cpul_rate = rate; + ret = clk_scmi_dsu_set_rate(clock, rate); + } + + return ret; +} + +static unsigned long rk3588_lpll_get_rate(void) +{ + unsigned int m, p, s, k; + uint64_t rate64 = 24000000, postdiv; + int mode; + + mode = (mmio_read_32(DSUCRU_BASE + CRU_CLKSEL_CON(5)) >> 14) & + 0x3; + + if (mode == 0) + return rate64; + + m = (mmio_read_32(DSUCRU_BASE + CRU_PLL_CON(16)) >> + CRU_PLLCON0_M_SHIFT) & + CRU_PLLCON0_M_MASK; + p = (mmio_read_32(DSUCRU_BASE + CRU_PLL_CON(17)) >> + CRU_PLLCON1_P_SHIFT) & + CRU_PLLCON1_P_MASK; + s = (mmio_read_32(DSUCRU_BASE + CRU_PLL_CON(17)) >> + CRU_PLLCON1_S_SHIFT) & + CRU_PLLCON1_S_MASK; + k = (mmio_read_32(DSUCRU_BASE + CRU_PLL_CON(18)) >> + CRU_PLLCON2_K_SHIFT) & + CRU_PLLCON2_K_MASK; + + rate64 *= m; + rate64 = rate64 / p; + + if (k != 0) { + /* fractional mode */ + uint64_t frac_rate64 = 24000000 * k; + + postdiv = p * 65535; + frac_rate64 = frac_rate64 / postdiv; + rate64 += frac_rate64; + } + rate64 = rate64 >> s; + + return (unsigned long)rate64; +} + +static unsigned long clk_scmi_cpul_get_rate(rk_scmi_clock_t *clock) +{ + int src, div; + + src = mmio_read_32(DSUCRU_BASE + CRU_CLKSEL_CON(6)) & 0x0060; + src = src >> 5; + if (src == 2) { + return sys_clk_info.cpul_rate; + } else { + src = mmio_read_32(DSUCRU_BASE + CRU_CLKSEL_CON(5)) & 0xc000; + src = src >> 14; + div = mmio_read_32(DSUCRU_BASE + CRU_CLKSEL_CON(6)) & 0x1f; + switch (src) { + case 0: + return 24000000; + case 1: + /* Make the return rate is equal to the set rate */ + if (sys_clk_info.cpul_rate) + return sys_clk_info.cpul_rate; + else + return GPLL_RATE / (div + 1); + case 2: + return rk3588_lpll_get_rate(); + default: + return 0; + } + } +} + +static int clk_scmi_cpul_set_status(rk_scmi_clock_t *clock, bool status) +{ + return 0; +} + +static void clk_scmi_b0pll_disable(void) +{ + static bool is_b0pll_disabled; + + if (is_b0pll_disabled != 0) + return; + + /* set coreb01 mux gpll */ + mmio_write_32(BIGCORE0CRU_BASE + CRU_CLKSEL_CON(0), + CPUB01_CLK_PATH_NOR_GPLL); + /* pll enter slow mode */ + mmio_write_32(BIGCORE0CRU_BASE + CRU_MODE_CON0, CPU_PLL_PATH_SLOWMODE); + /* set pll power down */ + mmio_write_32(BIGCORE0CRU_BASE + CRU_PLL_CON(1), CRU_PLL_POWER_DOWN); + + is_b0pll_disabled = true; +} + +static int clk_cpub01_set_rate(unsigned long rate, enum pll_type_sel type) +{ + struct pvtpll_table *pvtpll; + int div; + + if (rate == 0) + return SCMI_INVALID_PARAMETERS; + + pvtpll = rkclk_get_pvtpll_config(sys_clk_info.cpub01_table, + sys_clk_info.cpub01_rate_count, rate); + if (pvtpll == NULL) + return SCMI_INVALID_PARAMETERS; + + /* set b0pll */ + if (PVTPLL_NEED(type, pvtpll->length)) { + /* set clock gating interval */ + mmio_write_32(BIGCORE0GRF_BASE + RK3588_CPUB_PVTPLL_CON2, + 0x00040000); + /* set ring sel */ + mmio_write_32(BIGCORE0GRF_BASE + RK3588_CPUB_PVTPLL_CON0_L, + 0x07000000 | (pvtpll->ring_sel << 8)); + /* set length */ + mmio_write_32(BIGCORE0GRF_BASE + RK3588_CPUB_PVTPLL_CON0_H, + 0x003f0000 | pvtpll->length); + /* set cal cnt = 24, T = 1us */ + mmio_write_32(BIGCORE0GRF_BASE + RK3588_CPUB_PVTPLL_CON1, + 0x18); + /* enable pvtpll */ + mmio_write_32(BIGCORE0GRF_BASE + RK3588_CPUB_PVTPLL_CON0_L, + 0x00020002); + /* start monitor */ + mmio_write_32(BIGCORE0GRF_BASE + RK3588_CPUB_PVTPLL_CON0_L, + 0x00010001); + /* set core mux pvtpll */ + mmio_write_32(BIGCORE0CRU_BASE + CRU_CLKSEL_CON(2), + CPUB01_PVTPLL_PATH_PVTPLL); + mmio_write_32(BIGCORE0CRU_BASE + CRU_CLKSEL_CON(0), + CPUB01_CLK_PATH_B0_PVTPLL); + mmio_write_32(BIGCORE0CRU_BASE + CRU_CLKSEL_CON(1), + CPUB01_CLK_PATH_B1_PVTPLL); + goto out; + } + + /* set clk coreb01 div */ + div = DIV_ROUND_UP(GPLL_RATE, rate) - 1; + mmio_write_32(BIGCORE0CRU_BASE + CRU_CLKSEL_CON(0), + CLKDIV_5BITS_SHF(div, 8)); + mmio_write_32(BIGCORE0CRU_BASE + CRU_CLKSEL_CON(1), + CLKDIV_5BITS_SHF(div, 0)); + /* set coreb01 mux gpll */ + mmio_write_32(BIGCORE0CRU_BASE + CRU_CLKSEL_CON(0), + CPUB01_CLK_PATH_NOR_GPLL); + mmio_write_32(BIGCORE0CRU_BASE + CRU_CLKSEL_CON(0), + CPUB01_CLK_PATH_B0PLL); + mmio_write_32(BIGCORE0CRU_BASE + CRU_CLKSEL_CON(1), + CPUB01_CLK_PATH_B1PLL); + +out: + clk_scmi_b0pll_disable(); + + return 0; +} + +static int clk_scmi_cpub01_set_rate(rk_scmi_clock_t *clock, unsigned long rate) +{ + int ret; + + if (rate == 0) + return SCMI_INVALID_PARAMETERS; + + ret = clk_cpub01_set_rate(rate, PLL_SEL_AUTO); + if (ret == 0) + sys_clk_info.cpub01_rate = rate; + + return ret; +} + +static unsigned long rk3588_b0pll_get_rate(void) +{ + unsigned int m, p, s, k; + uint64_t rate64 = 24000000, postdiv; + int mode; + + mode = (mmio_read_32(BIGCORE0CRU_BASE + CRU_CLKSEL_CON(0)) >> 6) & + 0x3; + + if (mode == 0) + return rate64; + + m = (mmio_read_32(BIGCORE0CRU_BASE + CRU_PLL_CON(0)) >> + CRU_PLLCON0_M_SHIFT) & + CRU_PLLCON0_M_MASK; + p = (mmio_read_32(BIGCORE0CRU_BASE + CRU_PLL_CON(1)) >> + CRU_PLLCON1_P_SHIFT) & + CRU_PLLCON1_P_MASK; + s = (mmio_read_32(BIGCORE0CRU_BASE + CRU_PLL_CON(1)) >> + CRU_PLLCON1_S_SHIFT) & + CRU_PLLCON1_S_MASK; + k = (mmio_read_32(BIGCORE0CRU_BASE + CRU_PLL_CON(2)) >> + CRU_PLLCON2_K_SHIFT) & + CRU_PLLCON2_K_MASK; + + rate64 *= m; + rate64 = rate64 / p; + + if (k != 0) { + /* fractional mode */ + uint64_t frac_rate64 = 24000000 * k; + + postdiv = p * 65535; + frac_rate64 = frac_rate64 / postdiv; + rate64 += frac_rate64; + } + rate64 = rate64 >> s; + + return (unsigned long)rate64; +} + +static unsigned long clk_scmi_cpub01_get_rate(rk_scmi_clock_t *clock) +{ + int value, src, div; + + value = mmio_read_32(BIGCORE0CRU_BASE + CRU_CLKSEL_CON(0)); + src = (value & 0x6000) >> 13; + if (src == 2) { + return sys_clk_info.cpub01_rate; + } else { + src = (value & 0x00c0) >> 6; + div = (value & 0x1f00) >> 8; + switch (src) { + case 0: + return 24000000; + case 1: + /* Make the return rate is equal to the set rate */ + if (sys_clk_info.cpub01_rate) + return sys_clk_info.cpub01_rate; + else + return GPLL_RATE / (div + 1); + case 2: + return rk3588_b0pll_get_rate(); + default: + return 0; + } + } +} + +static int clk_scmi_cpub01_set_status(rk_scmi_clock_t *clock, bool status) +{ + return 0; +} + +static void clk_scmi_b1pll_disable(void) +{ + static bool is_b1pll_disabled; + + if (is_b1pll_disabled != 0) + return; + + /* set coreb23 mux gpll */ + mmio_write_32(BIGCORE1CRU_BASE + CRU_CLKSEL_CON(0), + CPUB01_CLK_PATH_NOR_GPLL); + /* pll enter slow mode */ + mmio_write_32(BIGCORE1CRU_BASE + CRU_MODE_CON0, CPU_PLL_PATH_SLOWMODE); + /* set pll power down */ + mmio_write_32(BIGCORE1CRU_BASE + CRU_PLL_CON(9), CRU_PLL_POWER_DOWN); + + is_b1pll_disabled = true; +} + +static int clk_cpub23_set_rate(unsigned long rate, enum pll_type_sel type) +{ + struct pvtpll_table *pvtpll; + int div; + + if (rate == 0) + return SCMI_INVALID_PARAMETERS; + + pvtpll = rkclk_get_pvtpll_config(sys_clk_info.cpub23_table, + sys_clk_info.cpub23_rate_count, rate); + if (pvtpll == NULL) + return SCMI_INVALID_PARAMETERS; + + /* set b1pll */ + if (PVTPLL_NEED(type, pvtpll->length)) { + /* set clock gating interval */ + mmio_write_32(BIGCORE1GRF_BASE + RK3588_CPUB_PVTPLL_CON2, + 0x00040000); + /* set ring sel */ + mmio_write_32(BIGCORE1GRF_BASE + RK3588_CPUB_PVTPLL_CON0_L, + 0x07000000 | (pvtpll->ring_sel << 8)); + /* set length */ + mmio_write_32(BIGCORE1GRF_BASE + RK3588_CPUB_PVTPLL_CON0_H, + 0x003f0000 | pvtpll->length); + /* set cal cnt = 24, T = 1us */ + mmio_write_32(BIGCORE1GRF_BASE + RK3588_CPUB_PVTPLL_CON1, + 0x18); + /* enable pvtpll */ + mmio_write_32(BIGCORE1GRF_BASE + RK3588_CPUB_PVTPLL_CON0_L, + 0x00020002); + /* start monitor */ + mmio_write_32(BIGCORE1GRF_BASE + RK3588_CPUB_PVTPLL_CON0_L, + 0x00010001); + /* set core mux pvtpll */ + mmio_write_32(BIGCORE1CRU_BASE + CRU_CLKSEL_CON(2), + CPUB01_PVTPLL_PATH_PVTPLL); + mmio_write_32(BIGCORE1CRU_BASE + CRU_CLKSEL_CON(0), + CPUB01_CLK_PATH_B0_PVTPLL); + mmio_write_32(BIGCORE1CRU_BASE + CRU_CLKSEL_CON(1), + CPUB01_CLK_PATH_B1_PVTPLL); + goto out; + } + + /* set clk coreb23 div */ + div = DIV_ROUND_UP(GPLL_RATE, rate) - 1; + mmio_write_32(BIGCORE1CRU_BASE + CRU_CLKSEL_CON(0), + CLKDIV_5BITS_SHF(div, 8)); + mmio_write_32(BIGCORE1CRU_BASE + CRU_CLKSEL_CON(1), + CLKDIV_5BITS_SHF(div, 0)); + /* set coreb23 mux gpll */ + mmio_write_32(BIGCORE1CRU_BASE + CRU_CLKSEL_CON(0), + CPUB01_CLK_PATH_NOR_GPLL); + mmio_write_32(BIGCORE1CRU_BASE + CRU_CLKSEL_CON(0), + CPUB01_CLK_PATH_B0PLL); + mmio_write_32(BIGCORE1CRU_BASE + CRU_CLKSEL_CON(1), + CPUB01_CLK_PATH_B1PLL); + +out: + clk_scmi_b1pll_disable(); + + return 0; +} + +static int clk_scmi_cpub23_set_rate(rk_scmi_clock_t *clock, unsigned long rate) +{ + int ret; + + if (rate == 0) + return SCMI_INVALID_PARAMETERS; + + ret = clk_cpub23_set_rate(rate, PLL_SEL_AUTO); + if (ret == 0) + sys_clk_info.cpub23_rate = rate; + + return ret; +} + +static unsigned long rk3588_b1pll_get_rate(void) +{ + unsigned int m, p, s, k; + uint64_t rate64 = 24000000, postdiv; + int mode; + + mode = (mmio_read_32(BIGCORE1CRU_BASE + CRU_CLKSEL_CON(0)) >> 6) & + 0x3; + + if (mode == 0) + return rate64; + + m = (mmio_read_32(BIGCORE1CRU_BASE + CRU_PLL_CON(8)) >> + CRU_PLLCON0_M_SHIFT) & + CRU_PLLCON0_M_MASK; + p = (mmio_read_32(BIGCORE1CRU_BASE + CRU_PLL_CON(9)) >> + CRU_PLLCON1_P_SHIFT) & + CRU_PLLCON1_P_MASK; + s = (mmio_read_32(BIGCORE1CRU_BASE + CRU_PLL_CON(9)) >> + CRU_PLLCON1_S_SHIFT) & + CRU_PLLCON1_S_MASK; + k = (mmio_read_32(BIGCORE1CRU_BASE + CRU_PLL_CON(10)) >> + CRU_PLLCON2_K_SHIFT) & + CRU_PLLCON2_K_MASK; + + rate64 *= m; + rate64 = rate64 / p; + + if (k != 0) { + /* fractional mode */ + uint64_t frac_rate64 = 24000000 * k; + + postdiv = p * 65535; + frac_rate64 = frac_rate64 / postdiv; + rate64 += frac_rate64; + } + rate64 = rate64 >> s; + + return (unsigned long)rate64; +} + +static unsigned long clk_scmi_cpub23_get_rate(rk_scmi_clock_t *clock) +{ + int value, src, div; + + value = mmio_read_32(BIGCORE1CRU_BASE + CRU_CLKSEL_CON(0)); + src = (value & 0x6000) >> 13; + if (src == 2) { + return sys_clk_info.cpub23_rate; + } else { + src = (value & 0x00c0) >> 6; + div = (value & 0x1f00) >> 8; + switch (src) { + case 0: + return 24000000; + case 1: + /* Make the return rate is equal to the set rate */ + if (sys_clk_info.cpub23_rate) + return sys_clk_info.cpub23_rate; + else + return GPLL_RATE / (div + 1); + case 2: + return rk3588_b1pll_get_rate(); + default: + return 0; + } + } +} + +static int clk_scmi_cpub23_set_status(rk_scmi_clock_t *clock, bool status) +{ + return 0; +} + +static unsigned long clk_scmi_dsu_get_rate(rk_scmi_clock_t *clock) +{ + int src, div; + + src = mmio_read_32(DSUCRU_BASE + CRU_CLKSEL_CON(1)) & 0x1; + if (src != 0) { + return sys_clk_info.dsu_rate; + } else { + src = mmio_read_32(DSUCRU_BASE + CRU_CLKSEL_CON(0)) & 0x3000; + src = src >> 12; + div = mmio_read_32(DSUCRU_BASE + CRU_CLKSEL_CON(0)) & 0xf80; + div = div >> 7; + switch (src) { + case 0: + return rk3588_b0pll_get_rate() / (div + 1); + case 1: + return rk3588_b1pll_get_rate() / (div + 1); + case 2: + return rk3588_lpll_get_rate() / (div + 1); + case 3: + return GPLL_RATE / (div + 1); + default: + return 0; + } + } +} + +static void clk_scmi_lpll_disable(void) +{ + static bool is_lpll_disabled; + + if (is_lpll_disabled) + return; + + /* set corel mux gpll */ + mmio_write_32(DSUCRU_BASE + CRU_CLKSEL_CON(5), + CPUL_CLK_PATH_NOR_GPLL); + /* set dsu mux gpll */ + mmio_write_32(DSUCRU_BASE + CRU_CLKSEL_CON(0), + SCLK_DSU_PATH_NOR_GPLL); + /* pll enter slow mode */ + mmio_write_32(DSUCRU_BASE + CRU_MODE_CON0, CPU_PLL_PATH_SLOWMODE); + /* set pll power down */ + mmio_write_32(DSUCRU_BASE + CRU_PLL_CON(17), CRU_PLL_POWER_DOWN); + + is_lpll_disabled = true; +} + +static int clk_dsu_set_rate(unsigned long rate, enum pll_type_sel type) +{ + struct pvtpll_table *pvtpll; + int div; + + if (rate == 0) + return SCMI_INVALID_PARAMETERS; + + pvtpll = rkclk_get_pvtpll_config(sys_clk_info.cpul_table, + sys_clk_info.cpul_rate_count, rate); + if (pvtpll == NULL) + return SCMI_INVALID_PARAMETERS; + + /* set pvtpll */ + if (PVTPLL_NEED(type, pvtpll->length)) { + /* set clock gating interval */ + mmio_write_32(DSUGRF_BASE + RK3588_DSU_PVTPLL_CON2, + 0x00040000); + /* set ring sel */ + mmio_write_32(DSUGRF_BASE + RK3588_DSU_PVTPLL_CON0_L, + 0x07000000 | (pvtpll->ring_sel << 8)); + /* set length */ + mmio_write_32(DSUGRF_BASE + RK3588_DSU_PVTPLL_CON0_H, + 0x003f0000 | pvtpll->length); + /* set cal cnt = 24, T = 1us */ + mmio_write_32(DSUGRF_BASE + RK3588_DSU_PVTPLL_CON1, + 0x18); + /* enable pvtpll */ + mmio_write_32(DSUGRF_BASE + RK3588_DSU_PVTPLL_CON0_L, + 0x00020002); + /* start monitor */ + mmio_write_32(DSUGRF_BASE + RK3588_DSU_PVTPLL_CON0_L, + 0x00010001); + /* set dsu mux pvtpll */ + mmio_write_32(DSUCRU_BASE + CRU_CLKSEL_CON(7), + DSU_PVTPLL_PATH_PVTPLL); + mmio_write_32(DSUCRU_BASE + CRU_CLKSEL_CON(1), + SCLK_DSU_PATH_PVTPLL); + goto out; + } + /* set dsu div */ + div = DIV_ROUND_UP(GPLL_RATE, rate) - 1; + mmio_write_32(DSUCRU_BASE + CRU_CLKSEL_CON(0), + CLKDIV_5BITS_SHF(div, 7)); + /* set dsu mux gpll */ + mmio_write_32(DSUCRU_BASE + CRU_CLKSEL_CON(0), + SCLK_DSU_PATH_NOR_GPLL); + mmio_write_32(DSUCRU_BASE + CRU_CLKSEL_CON(1), + SCLK_DSU_PATH_NOR_PLL); + +out: + clk_scmi_lpll_disable(); + + return 0; +} + +static int clk_scmi_dsu_set_rate(rk_scmi_clock_t *clock, unsigned long rate) +{ + int ret; + + if (rate == 0) + return SCMI_INVALID_PARAMETERS; + + ret = clk_dsu_set_rate(rate, PLL_SEL_AUTO); + + if (ret == 0) + sys_clk_info.dsu_rate = rate; + return ret; +} + +static int clk_scmi_dsu_set_status(rk_scmi_clock_t *clock, bool status) +{ + return 0; +} + +static unsigned long clk_scmi_gpu_get_rate(rk_scmi_clock_t *clock) +{ + int div, src; + + if ((mmio_read_32(CRU_BASE + CRU_CLKSEL_CON(158)) & 0x4000) != 0) { + return sys_clk_info.gpu_rate; + } else { + div = mmio_read_32(CRU_BASE + CRU_CLKSEL_CON(158)) & 0x1f; + src = mmio_read_32(CRU_BASE + CRU_CLKSEL_CON(158)) & 0x00e0; + src = src >> 5; + switch (src) { + case 0: + /* Make the return rate is equal to the set rate */ + if (sys_clk_info.gpu_rate) + return sys_clk_info.gpu_rate; + else + return GPLL_RATE / (div + 1); + case 1: + return CPLL_RATE / (div + 1); + case 2: + return AUPLL_RATE / (div + 1); + case 3: + return NPLL_RATE / (div + 1); + case 4: + return SPLL_RATE / (div + 1); + default: + return 0; + } + } +} + +static int clk_gpu_set_rate(unsigned long rate, enum pll_type_sel type) +{ + struct pvtpll_table *pvtpll; + int div; + + pvtpll = rkclk_get_pvtpll_config(sys_clk_info.gpu_table, + sys_clk_info.gpu_rate_count, rate); + if (pvtpll == NULL) + return SCMI_INVALID_PARAMETERS; + + if (PVTPLL_NEED(type, pvtpll->length)) { + /* set clock gating interval */ + mmio_write_32(GPUGRF_BASE + RK3588_GPU_PVTPLL_CON2, + 0x00040000); + /* set ring sel */ + mmio_write_32(GPUGRF_BASE + RK3588_GPU_PVTPLL_CON0_L, + 0x07000000 | (pvtpll->ring_sel << 8)); + /* set length */ + mmio_write_32(GPUGRF_BASE + RK3588_GPU_PVTPLL_CON0_H, + 0x003f0000 | pvtpll->length); + /* set cal cnt = 24, T = 1us */ + mmio_write_32(GPUGRF_BASE + RK3588_GPU_PVTPLL_CON1, + 0x18); + /* enable pvtpll */ + mmio_write_32(GPUGRF_BASE + RK3588_GPU_PVTPLL_CON0_L, + 0x00020002); + /* start monitor */ + mmio_write_32(GPUGRF_BASE + RK3588_GPU_PVTPLL_CON0_L, + 0x00010001); + /* set gpu mux pvtpll */ + mmio_write_32(CRU_BASE + CRU_CLKSEL_CON(158), + GPU_CLK_PATH_PVTPLL); + return 0; + } + + /* set gpu div */ + div = DIV_ROUND_UP(GPLL_RATE, rate); + mmio_write_32(CRU_BASE + CRU_CLKSEL_CON(158), + CLKDIV_5BITS_SHF(div - 1, 0)); + /* set gpu mux gpll */ + mmio_write_32(CRU_BASE + CRU_CLKSEL_CON(158), + GPU_CLK_PATH_NOR_GPLL); + mmio_write_32(CRU_BASE + CRU_CLKSEL_CON(158), + GPU_CLK_PATH_NOR_PLL); + + return 0; +} + +static int clk_scmi_gpu_set_rate(rk_scmi_clock_t *clock, unsigned long rate) +{ + int ret; + + if (rate == 0) + return SCMI_INVALID_PARAMETERS; + + ret = clk_gpu_set_rate(rate, PLL_SEL_AUTO); + if (ret == 0) + sys_clk_info.gpu_rate = rate; + + return ret; +} + +static int clk_scmi_gpu_set_status(rk_scmi_clock_t *clock, bool status) +{ + return 0; +} + +static unsigned long clk_scmi_npu_get_rate(rk_scmi_clock_t *clock) +{ + int div, src; + + if ((mmio_read_32(CRU_BASE + CRU_CLKSEL_CON(74)) & 0x1) != 0) { + return sys_clk_info.npu_rate; + } else { + div = mmio_read_32(CRU_BASE + CRU_CLKSEL_CON(73)) & 0x007c; + div = div >> 2; + src = mmio_read_32(CRU_BASE + CRU_CLKSEL_CON(73)) & 0x0380; + src = src >> 7; + switch (src) { + case 0: + /* Make the return rate is equal to the set rate */ + if (sys_clk_info.npu_rate != 0) + return sys_clk_info.npu_rate; + else + return GPLL_RATE / (div + 1); + case 1: + return CPLL_RATE / (div + 1); + case 2: + return AUPLL_RATE / (div + 1); + case 3: + return NPLL_RATE / (div + 1); + case 4: + return SPLL_RATE / (div + 1); + default: + return 0; + } + } +} + +static int clk_npu_set_rate(unsigned long rate, enum pll_type_sel type) +{ + struct pvtpll_table *pvtpll; + int div; + + pvtpll = rkclk_get_pvtpll_config(sys_clk_info.npu_table, + sys_clk_info.npu_rate_count, rate); + if (pvtpll == NULL) + return SCMI_INVALID_PARAMETERS; + + if (PVTPLL_NEED(type, pvtpll->length)) { + /* set clock gating interval */ + mmio_write_32(NPUGRF_BASE + RK3588_NPU_PVTPLL_CON2, + 0x00040000); + /* set ring sel */ + mmio_write_32(NPUGRF_BASE + RK3588_NPU_PVTPLL_CON0_L, + 0x07000000 | (pvtpll->ring_sel << 8)); + /* set length */ + mmio_write_32(NPUGRF_BASE + RK3588_NPU_PVTPLL_CON0_H, + 0x003f0000 | pvtpll->length); + /* set cal cnt = 24, T = 1us */ + mmio_write_32(NPUGRF_BASE + RK3588_NPU_PVTPLL_CON1, + 0x18); + /* enable pvtpll */ + mmio_write_32(NPUGRF_BASE + RK3588_NPU_PVTPLL_CON0_L, + 0x00020002); + /* start monitor */ + mmio_write_32(NPUGRF_BASE + RK3588_NPU_PVTPLL_CON0_L, + 0x00010001); + /* set npu mux pvtpll */ + mmio_write_32(CRU_BASE + CRU_CLKSEL_CON(74), + NPU_CLK_PATH_PVTPLL); + return 0; + } + + /* set npu div */ + div = DIV_ROUND_UP(GPLL_RATE, rate); + mmio_write_32(CRU_BASE + CRU_CLKSEL_CON(73), + CLKDIV_5BITS_SHF(div - 1, 2)); + /* set npu mux gpll */ + mmio_write_32(CRU_BASE + CRU_CLKSEL_CON(73), + NPU_CLK_PATH_NOR_GPLL); + mmio_write_32(CRU_BASE + CRU_CLKSEL_CON(74), + NPU_CLK_PATH_NOR_PLL); + + return 0; +} + +static int clk_scmi_npu_set_rate(rk_scmi_clock_t *clock, unsigned long rate) +{ + int ret; + + if (rate == 0) + return SCMI_INVALID_PARAMETERS; + + ret = clk_npu_set_rate(rate, PLL_SEL_AUTO); + if (ret == 0) + sys_clk_info.npu_rate = rate; + + return ret; +} + +static int clk_scmi_npu_set_status(rk_scmi_clock_t *clock, bool status) +{ + return 0; +} + +static unsigned long clk_scmi_sbus_get_rate(rk_scmi_clock_t *clock) +{ + int div; + + if ((mmio_read_32(BUSSCRU_BASE + CRU_CLKSEL_CON(0)) & 0x0800) != 0) { + div = mmio_read_32(BUSSCRU_BASE + CRU_CLKSEL_CON(0)); + div = (div & 0x03e0) >> 5; + return SPLL_RATE / (div + 1); + } else { + return OSC_HZ; + } +} + +static int clk_scmi_sbus_set_rate(rk_scmi_clock_t *clock, unsigned long rate) +{ + int div; + + if (rate == OSC_HZ) { + mmio_write_32(BUSSCRU_BASE + CRU_CLKSEL_CON(0), + WMSK_BIT(11)); + return 0; + } + + div = DIV_ROUND_UP(SPLL_RATE, rate); + mmio_write_32(BUSSCRU_BASE + CRU_CLKSEL_CON(0), + CLKDIV_5BITS_SHF(div - 1, 5)); + mmio_write_32(BUSSCRU_BASE + CRU_CLKSEL_CON(0), + BIT_WITH_WMSK(11) | WMSK_BIT(10)); + return 0; +} + +static int clk_scmi_sbus_set_status(rk_scmi_clock_t *clock, bool status) +{ + return 0; +} + +static unsigned long clk_scmi_pclk_sbus_get_rate(rk_scmi_clock_t *clock) +{ + int div; + + div = mmio_read_32(BUSSCRU_BASE + CRU_CLKSEL_CON(0)); + div = div & 0x001f; + return SPLL_RATE / (div + 1); + +} + +static int clk_scmi_pclk_sbus_set_rate(rk_scmi_clock_t *clock, unsigned long rate) +{ + int div; + + div = DIV_ROUND_UP(SPLL_RATE, rate); + mmio_write_32(BUSSCRU_BASE + CRU_CLKSEL_CON(0), + CLKDIV_5BITS_SHF(div - 1, 0)); + return 0; +} + +static int clk_scmi_pclk_sbus_set_status(rk_scmi_clock_t *clock, bool status) +{ + return 0; +} + +static unsigned long clk_scmi_cclk_sdmmc_get_rate(rk_scmi_clock_t *clock) +{ + int div; + uint32_t src; + + src = mmio_read_32(SCRU_BASE + CRU_CLKSEL_CON(3)) & 0x3000; + src = src >> 12; + div = mmio_read_32(SCRU_BASE + CRU_CLKSEL_CON(3)) & 0x0fc0; + div = div >> 6; + if (src == 1) { + return SPLL_RATE / (div + 1); + } else if (src == 2) { + return OSC_HZ / (div + 1); + } else { + return GPLL_RATE / (div + 1); + } +} + +static int clk_scmi_cclk_sdmmc_set_rate(rk_scmi_clock_t *clock, unsigned long rate) +{ + int div; + + if ((OSC_HZ % rate) == 0) { + div = DIV_ROUND_UP(OSC_HZ, rate); + mmio_write_32(SCRU_BASE + CRU_CLKSEL_CON(3), + CLKDIV_6BITS_SHF(div - 1, 6) | + BITS_WITH_WMASK(2U, 0x3U, 12)); + } else if ((SPLL_RATE % rate) == 0) { + div = DIV_ROUND_UP(SPLL_RATE, rate); + mmio_write_32(SCRU_BASE + CRU_CLKSEL_CON(3), + CLKDIV_6BITS_SHF(div - 1, 6) | + BITS_WITH_WMASK(1U, 0x3U, 12)); + } else { + div = DIV_ROUND_UP(GPLL_RATE, rate); + mmio_write_32(SCRU_BASE + CRU_CLKSEL_CON(3), + CLKDIV_6BITS_SHF(div - 1, 6) | + BITS_WITH_WMASK(0U, 0x3U, 12)); + } + + return 0; +} + +static int clk_scmi_cclk_sdmmc_set_status(rk_scmi_clock_t *clock, bool status) +{ + mmio_write_32(SCRU_BASE + CRU_CLKGATE_CON(3), + BITS_WITH_WMASK(!status, 0x1U, 4)); + return 0; +} + +static unsigned long clk_scmi_dclk_sdmmc_get_rate(rk_scmi_clock_t *clock) +{ + int div; + uint32_t src; + + src = mmio_read_32(SCRU_BASE + CRU_CLKSEL_CON(3)) & 0x0020; + div = mmio_read_32(SCRU_BASE + CRU_CLKSEL_CON(3)) & 0x001f; + if (src != 0) { + return SPLL_RATE / (div + 1); + } else { + return GPLL_RATE / (div + 1); + } +} + +static int clk_scmi_dclk_sdmmc_set_rate(rk_scmi_clock_t *clock, unsigned long rate) +{ + int div; + + if ((SPLL_RATE % rate) == 0) { + div = DIV_ROUND_UP(SPLL_RATE, rate); + mmio_write_32(SCRU_BASE + CRU_CLKSEL_CON(3), + CLKDIV_5BITS_SHF(div - 1, 0) | + BITS_WITH_WMASK(1U, 0x1U, 5)); + } else { + div = DIV_ROUND_UP(GPLL_RATE, rate); + mmio_write_32(SCRU_BASE + CRU_CLKSEL_CON(3), + CLKDIV_5BITS_SHF(div - 1, 0) | + BITS_WITH_WMASK(0U, 0x1U, 5)); + } + return 0; +} + +static int clk_scmi_dclk_sdmmc_set_status(rk_scmi_clock_t *clock, bool status) +{ + mmio_write_32(SCRU_BASE + CRU_CLKGATE_CON(3), + BITS_WITH_WMASK(!status, 0x1U, 1)); + return 0; +} + +static unsigned long clk_scmi_aclk_secure_ns_get_rate(rk_scmi_clock_t *clock) +{ + uint32_t src; + + src = mmio_read_32(SCRU_BASE + CRU_CLKSEL_CON(1)) & 0x0003; + switch (src) { + case 0: + return 350 * MHz; + case 1: + return 200 * MHz; + case 2: + return 100 * MHz; + case 3: + return OSC_HZ; + default: + return 0; + } +} + +static int clk_scmi_aclk_secure_ns_set_rate(rk_scmi_clock_t *clock, unsigned long rate) +{ + uint32_t src; + + if (rate >= 350 * MHz) + src = 0; + else if (rate >= 200 * MHz) + src = 1; + else if (rate >= 100 * MHz) + src = 2; + else + src = 3; + + mmio_write_32(SCRU_BASE + CRU_CLKSEL_CON(1), + BITS_WITH_WMASK(src, 0x3U, 0)); + + return 0; +} + +static int clk_scmi_aclk_secure_ns_set_status(rk_scmi_clock_t *clock, bool status) +{ + return 0; +} + +static unsigned long clk_scmi_hclk_secure_ns_get_rate(rk_scmi_clock_t *clock) +{ + uint32_t src; + + src = mmio_read_32(SCRU_BASE + CRU_CLKSEL_CON(1)) & 0x000c; + src = src >> 2; + switch (src) { + case 0: + return 150 * MHz; + case 1: + return 100 * MHz; + case 2: + return 50 * MHz; + case 3: + return OSC_HZ; + default: + return 0; + } +} + +static int clk_scmi_hclk_secure_ns_set_rate(rk_scmi_clock_t *clock, unsigned long rate) +{ + uint32_t src; + + if (rate >= 150 * MHz) + src = 0; + else if (rate >= 100 * MHz) + src = 1; + else if (rate >= 50 * MHz) + src = 2; + else + src = 3; + + mmio_write_32(SCRU_BASE + CRU_CLKSEL_CON(1), + BITS_WITH_WMASK(src, 0x3U, 2)); + return 0; +} + +static int clk_scmi_hclk_secure_ns_set_status(rk_scmi_clock_t *clock, bool status) +{ + return 0; +} + +static unsigned long clk_scmi_tclk_wdt_get_rate(rk_scmi_clock_t *clock) +{ + return OSC_HZ; +} + +static int clk_scmi_tclk_wdt_set_status(rk_scmi_clock_t *clock, bool status) +{ + mmio_write_32(SCRU_BASE + CRU_CLKGATE_CON(2), + BITS_WITH_WMASK(!status, 0x1U, 0)); + return 0; +} + +static unsigned long clk_scmi_keyladder_core_get_rate(rk_scmi_clock_t *clock) +{ + uint32_t src; + + src = mmio_read_32(SCRU_BASE + CRU_CLKSEL_CON(2)) & 0x00c0; + src = src >> 6; + switch (src) { + case 0: + return 350 * MHz; + case 1: + return 233 * MHz; + case 2: + return 116 * MHz; + case 3: + return OSC_HZ; + default: + return 0; + } +} + +static int clk_scmi_keyladder_core_set_rate(rk_scmi_clock_t *clock, unsigned long rate) +{ + uint32_t src; + + if (rate >= 350 * MHz) + src = 0; + else if (rate >= 233 * MHz) + src = 1; + else if (rate >= 116 * MHz) + src = 2; + else + src = 3; + + mmio_write_32(SCRU_BASE + CRU_CLKSEL_CON(2), + BITS_WITH_WMASK(src, 0x3U, 6)); + return 0; +} + +static int clk_scmi_keyladder_core_set_status(rk_scmi_clock_t *clock, bool status) +{ + mmio_write_32(SCRU_BASE + CRU_CLKGATE_CON(1), + BITS_WITH_WMASK(!status, 0x1U, 9)); + return 0; +} + +static unsigned long clk_scmi_keyladder_rng_get_rate(rk_scmi_clock_t *clock) +{ + uint32_t src; + + src = mmio_read_32(SCRU_BASE + CRU_CLKSEL_CON(2)) & 0x0300; + src = src >> 8; + switch (src) { + case 0: + return 175 * MHz; + case 1: + return 116 * MHz; + case 2: + return 58 * MHz; + case 3: + return OSC_HZ; + default: + return 0; + } +} + +static int clk_scmi_keyladder_rng_set_rate(rk_scmi_clock_t *clock, unsigned long rate) +{ + uint32_t src; + + if (rate >= 175 * MHz) + src = 0; + else if (rate >= 116 * MHz) + src = 1; + else if (rate >= 58 * MHz) + src = 2; + else + src = 3; + + mmio_write_32(SCRU_BASE + CRU_CLKSEL_CON(2), + BITS_WITH_WMASK(src, 0x3U, 8)); + return 0; +} + +static int clk_scmi_keyladder_rng_set_status(rk_scmi_clock_t *clock, bool status) +{ + mmio_write_32(SCRU_BASE + CRU_CLKGATE_CON(1), + BITS_WITH_WMASK(!status, 0x1U, 10)); + return 0; +} + +static unsigned long clk_scmi_aclk_secure_s_get_rate(rk_scmi_clock_t *clock) +{ + uint32_t src; + + src = mmio_read_32(SCRU_BASE + CRU_CLKSEL_CON(1)) & 0x0030; + src = src >> 4; + switch (src) { + case 0: + return 350 * MHz; + case 1: + return 233 * MHz; + case 2: + return 116 * MHz; + case 3: + return OSC_HZ; + default: + return 0; + } +} + +static int clk_scmi_aclk_secure_s_set_rate(rk_scmi_clock_t *clock, unsigned long rate) +{ + uint32_t src; + + if (rate >= 350 * MHz) + src = 0; + else if (rate >= 233 * MHz) + src = 1; + else if (rate >= 116 * MHz) + src = 2; + else + src = 3; + + mmio_write_32(SCRU_BASE + CRU_CLKSEL_CON(1), + BITS_WITH_WMASK(src, 0x3U, 4)); + return 0; +} + +static int clk_scmi_aclk_secure_s_set_status(rk_scmi_clock_t *clock, bool status) +{ + return 0; +} + +static unsigned long clk_scmi_hclk_secure_s_get_rate(rk_scmi_clock_t *clock) +{ + uint32_t src; + + src = mmio_read_32(SCRU_BASE + CRU_CLKSEL_CON(1)) & 0x00c0; + src = src >> 6; + switch (src) { + case 0: + return 175 * MHz; + case 1: + return 116 * MHz; + case 2: + return 58 * MHz; + case 3: + return OSC_HZ; + default: + return 0; + } +} + +static int clk_scmi_hclk_secure_s_set_rate(rk_scmi_clock_t *clock, unsigned long rate) +{ + uint32_t src; + + if (rate >= 175 * MHz) + src = 0; + else if (rate >= 116 * MHz) + src = 1; + else if (rate >= 58 * MHz) + src = 2; + else + src = 3; + + mmio_write_32(SCRU_BASE + CRU_CLKSEL_CON(1), + BITS_WITH_WMASK(src, 0x3U, 6)); + return 0; +} + +static int clk_scmi_hclk_secure_s_set_status(rk_scmi_clock_t *clock, bool status) +{ + return 0; +} + +static unsigned long clk_scmi_pclk_secure_s_get_rate(rk_scmi_clock_t *clock) +{ + uint32_t src; + + src = mmio_read_32(SCRU_BASE + CRU_CLKSEL_CON(1)) & 0x0300; + src = src >> 8; + switch (src) { + case 0: + return 116 * MHz; + case 1: + return 58 * MHz; + case 2: + return OSC_HZ; + default: + return 0; + } +} + +static int clk_scmi_pclk_secure_s_set_rate(rk_scmi_clock_t *clock, unsigned long rate) +{ + uint32_t src; + + if (rate >= 116 * MHz) + src = 0; + else if (rate >= 58 * MHz) + src = 1; + else + src = 2; + + mmio_write_32(SCRU_BASE + CRU_CLKSEL_CON(1), + BITS_WITH_WMASK(src, 0x3U, 8)); + return 0; +} + +static int clk_scmi_pclk_secure_s_set_status(rk_scmi_clock_t *clock, bool status) +{ + return 0; +} + +static unsigned long clk_scmi_crypto_rng_get_rate(rk_scmi_clock_t *clock) +{ + uint32_t src; + + src = mmio_read_32(SCRU_BASE + CRU_CLKSEL_CON(1)) & 0xc000; + src = src >> 14; + switch (src) { + case 0: + return 175 * MHz; + case 1: + return 116 * MHz; + case 2: + return 58 * MHz; + case 3: + return OSC_HZ; + default: + return 0; + } +} + +static int clk_scmi_crypto_rng_set_rate(rk_scmi_clock_t *clock, unsigned long rate) +{ + uint32_t src; + + if (rate >= 175 * MHz) + src = 0; + else if (rate >= 116 * MHz) + src = 1; + else if (rate >= 58 * MHz) + src = 2; + else + src = 3; + + mmio_write_32(SCRU_BASE + CRU_CLKSEL_CON(1), + BITS_WITH_WMASK(src, 0x3U, 14)); + return 0; +} + +static int clk_scmi_crypto_rng_set_status(rk_scmi_clock_t *clock, bool status) +{ + mmio_write_32(SCRU_BASE + CRU_CLKGATE_CON(1), + BITS_WITH_WMASK(!status, 0x1U, 1)); + + return 0; +} + +static unsigned long clk_scmi_crypto_core_get_rate(rk_scmi_clock_t *clock) +{ + uint32_t src; + + src = mmio_read_32(SCRU_BASE + CRU_CLKSEL_CON(1)) & 0x0c00; + src = src >> 10; + switch (src) { + case 0: + return 350 * MHz; + case 1: + return 233 * MHz; + case 2: + return 116 * MHz; + case 3: + return OSC_HZ; + default: + return 0; + } +} + +static int clk_scmi_crypto_core_set_rate(rk_scmi_clock_t *clock, unsigned long rate) +{ + uint32_t src; + + if (rate >= 350 * MHz) + src = 0; + else if (rate >= 233 * MHz) + src = 1; + else if (rate >= 116 * MHz) + src = 2; + else + src = 3; + + mmio_write_32(SCRU_BASE + CRU_CLKSEL_CON(1), + BITS_WITH_WMASK(src, 0x3U, 10)); + return 0; +} + +static int clk_scmi_crypto_core_set_status(rk_scmi_clock_t *clock, bool status) +{ + mmio_write_32(SCRU_BASE + CRU_CLKGATE_CON(0), + BITS_WITH_WMASK(!status, 0x1U, 15)); + + return 0; +} + +static unsigned long clk_scmi_crypto_pka_get_rate(rk_scmi_clock_t *clock) +{ + uint32_t src; + + src = mmio_read_32(SCRU_BASE + CRU_CLKSEL_CON(1)) & 0x3000; + src = src >> 12; + switch (src) { + case 0: + return 350 * MHz; + case 1: + return 233 * MHz; + case 2: + return 116 * MHz; + case 3: + return OSC_HZ; + default: + return 0; + } +} + +static int clk_scmi_crypto_pka_set_rate(rk_scmi_clock_t *clock, unsigned long rate) +{ + uint32_t src; + + if (rate >= 350 * MHz) + src = 0; + else if (rate >= 233 * MHz) + src = 1; + else if (rate >= 116 * MHz) + src = 2; + else + src = 3; + + mmio_write_32(SCRU_BASE + CRU_CLKSEL_CON(1), + BITS_WITH_WMASK(src, 0x3U, 12)); + return 0; +} + +static int clk_scmi_crypto_pka_set_status(rk_scmi_clock_t *clock, bool status) +{ + mmio_write_32(SCRU_BASE + CRU_CLKGATE_CON(1), + BITS_WITH_WMASK(!status, 0x1U, 0)); + + return 0; +} + +static unsigned long clk_scmi_spll_get_rate(rk_scmi_clock_t *clock) +{ + uint32_t src; + + src = mmio_read_32(BUSSCRU_BASE + CRU_MODE_CON0) & 0x3; + switch (src) { + case 0: + return OSC_HZ; + case 1: + return 702 * MHz; + case 2: + return 32768; + default: + return 0; + } +} + +static int clk_scmi_spll_set_rate(rk_scmi_clock_t *clock, unsigned long rate) +{ + uint32_t src; + + if (rate >= 700 * MHz) + src = 1; + else + src = 0; + + mmio_write_32(BUSSCRU_BASE + CRU_MODE_CON0, + BITS_WITH_WMASK(0, 0x3U, 0)); + mmio_write_32(BUSSCRU_BASE + CRU_PLL_CON(137), + BITS_WITH_WMASK(2, 0x7U, 6)); + + mmio_write_32(BUSSCRU_BASE + CRU_MODE_CON0, + BITS_WITH_WMASK(src, 0x3U, 0)); + return 0; +} + +static int clk_scmi_spll_set_status(rk_scmi_clock_t *clock, bool status) +{ + return 0; +} + +static unsigned long clk_scmi_hclk_sd_get_rate(rk_scmi_clock_t *clock) +{ + return clk_scmi_hclk_secure_ns_get_rate(clock); +} + +static int clk_scmi_hclk_sd_set_status(rk_scmi_clock_t *clock, bool status) +{ + mmio_write_32(SCRU_BASE + CRU_CLKGATE_CON(3), + BITS_WITH_WMASK(!status, 0x1U, 2)); + return 0; +} + +static unsigned long clk_scmi_crypto_rng_s_get_rate(rk_scmi_clock_t *clock) +{ + uint32_t src; + + src = mmio_read_32(SCRU_BASE + CRU_CLKSEL_CON(2)) & 0x0030; + src = src >> 4; + switch (src) { + case 0: + return 175 * MHz; + case 1: + return 116 * MHz; + case 2: + return 58 * MHz; + case 3: + return OSC_HZ; + default: + return 0; + } +} + +static int clk_scmi_crypto_rng_s_set_rate(rk_scmi_clock_t *clock, unsigned long rate) +{ + uint32_t src; + + if (rate >= 175 * MHz) + src = 0; + else if (rate >= 116 * MHz) + src = 1; + else if (rate >= 58 * MHz) + src = 2; + else + src = 3; + + mmio_write_32(SCRU_BASE + CRU_CLKSEL_CON(2), + BITS_WITH_WMASK(src, 0x3U, 4)); + return 0; +} + +static int clk_scmi_crypto_rng_s_set_status(rk_scmi_clock_t *clock, bool status) +{ + mmio_write_32(SCRU_BASE + CRU_CLKGATE_CON(1), + BITS_WITH_WMASK(!status, 0x1U, 6)); + + return 0; +} + +static unsigned long clk_scmi_crypto_core_s_get_rate(rk_scmi_clock_t *clock) +{ + uint32_t src; + + src = mmio_read_32(SCRU_BASE + CRU_CLKSEL_CON(2)) & 0x3; + src = src >> 0; + switch (src) { + case 0: + return 350 * MHz; + case 1: + return 233 * MHz; + case 2: + return 116 * MHz; + case 3: + return OSC_HZ; + default: + return 0; + } +} + +static int clk_scmi_crypto_core_s_set_rate(rk_scmi_clock_t *clock, unsigned long rate) +{ + uint32_t src; + + if (rate >= 350 * MHz) + src = 0; + else if (rate >= 233 * MHz) + src = 1; + else if (rate >= 116 * MHz) + src = 2; + else + src = 3; + + mmio_write_32(SCRU_BASE + CRU_CLKSEL_CON(2), + BITS_WITH_WMASK(src, 0x3U, 0)); + return 0; +} + +static int clk_scmi_crypto_core_s_set_status(rk_scmi_clock_t *clock, bool status) +{ + mmio_write_32(SCRU_BASE + CRU_CLKGATE_CON(1), + BITS_WITH_WMASK(!status, 0x1U, 4)); + + return 0; +} + +static unsigned long clk_scmi_crypto_pka_s_get_rate(rk_scmi_clock_t *clock) +{ + uint32_t src; + + src = mmio_read_32(SCRU_BASE + CRU_CLKSEL_CON(2)) & 0x000c; + src = src >> 2; + switch (src) { + case 0: + return 350 * MHz; + case 1: + return 233 * MHz; + case 2: + return 116 * MHz; + case 3: + return OSC_HZ; + default: + return 0; + } +} + +static int clk_scmi_crypto_pka_s_set_rate(rk_scmi_clock_t *clock, unsigned long rate) +{ + uint32_t src; + + if (rate >= 350 * MHz) + src = 0; + else if (rate >= 233 * MHz) + src = 1; + else if (rate >= 116 * MHz) + src = 2; + else + src = 3; + + mmio_write_32(SCRU_BASE + CRU_CLKSEL_CON(2), + BITS_WITH_WMASK(src, 0x3U, 2)); + return 0; +} + +static int clk_scmi_crypto_pka_s_set_status(rk_scmi_clock_t *clock, bool status) +{ + mmio_write_32(SCRU_BASE + CRU_CLKGATE_CON(1), + BITS_WITH_WMASK(!status, 0x1U, 5)); + + return 0; +} + +static unsigned long clk_scmi_a_crypto_s_get_rate(rk_scmi_clock_t *clock) +{ + return clk_scmi_aclk_secure_s_get_rate(clock); +} + +static int clk_scmi_a_crypto_s_set_rate(rk_scmi_clock_t *clock, unsigned long rate) +{ + return clk_scmi_aclk_secure_s_set_rate(clock, rate); +} + +static int clk_scmi_a_crypto_s_set_status(rk_scmi_clock_t *clock, bool status) +{ + mmio_write_32(SCRU_BASE + CRU_CLKGATE_CON(1), + BITS_WITH_WMASK(!status, 0x1U, 7)); + + return 0; +} + +static unsigned long clk_scmi_h_crypto_s_get_rate(rk_scmi_clock_t *clock) +{ + return clk_scmi_hclk_secure_s_get_rate(clock); +} + +static int clk_scmi_h_crypto_s_set_rate(rk_scmi_clock_t *clock, unsigned long rate) +{ + return clk_scmi_hclk_secure_s_set_rate(clock, rate); +} + +static int clk_scmi_h_crypto_s_set_status(rk_scmi_clock_t *clock, bool status) +{ + mmio_write_32(SCRU_BASE + CRU_CLKGATE_CON(1), + BITS_WITH_WMASK(!status, 0x1U, 8)); + + return 0; +} + +static unsigned long clk_scmi_p_crypto_s_get_rate(rk_scmi_clock_t *clock) +{ + return clk_scmi_pclk_secure_s_get_rate(clock); +} + +static int clk_scmi_p_crypto_s_set_rate(rk_scmi_clock_t *clock, unsigned long rate) +{ + return clk_scmi_pclk_secure_s_set_rate(clock, rate); +} + +static int clk_scmi_p_crypto_s_set_status(rk_scmi_clock_t *clock, bool status) +{ + mmio_write_32(SCRU_BASE + CRU_CLKGATE_CON(2), + BITS_WITH_WMASK(!status, 0x1U, 13)); + + return 0; +} + +static unsigned long clk_scmi_a_keylad_s_get_rate(rk_scmi_clock_t *clock) +{ + return clk_scmi_aclk_secure_s_get_rate(clock); +} + +static int clk_scmi_a_keylad_s_set_rate(rk_scmi_clock_t *clock, unsigned long rate) +{ + return clk_scmi_aclk_secure_s_set_rate(clock, rate); +} + +static int clk_scmi_a_keylad_s_set_status(rk_scmi_clock_t *clock, bool status) +{ + mmio_write_32(SCRU_BASE + CRU_CLKGATE_CON(1), + BITS_WITH_WMASK(!status, 0x1U, 11)); + + return 0; +} + +static unsigned long clk_scmi_h_keylad_s_get_rate(rk_scmi_clock_t *clock) +{ + return clk_scmi_hclk_secure_s_get_rate(clock); +} + +static int clk_scmi_h_keylad_s_set_rate(rk_scmi_clock_t *clock, unsigned long rate) +{ + return clk_scmi_hclk_secure_s_set_rate(clock, rate); +} + +static int clk_scmi_h_keylad_s_set_status(rk_scmi_clock_t *clock, bool status) +{ + mmio_write_32(SCRU_BASE + CRU_CLKGATE_CON(1), + BITS_WITH_WMASK(!status, 0x1U, 12)); + + return 0; +} + +static unsigned long clk_scmi_p_keylad_s_get_rate(rk_scmi_clock_t *clock) +{ + return clk_scmi_pclk_secure_s_get_rate(clock); +} + +static int clk_scmi_p_keylad_s_set_rate(rk_scmi_clock_t *clock, unsigned long rate) +{ + return clk_scmi_pclk_secure_s_set_rate(clock, rate); +} + +static int clk_scmi_p_keylad_s_set_status(rk_scmi_clock_t *clock, bool status) +{ + mmio_write_32(SCRU_BASE + CRU_CLKGATE_CON(2), + BITS_WITH_WMASK(!status, 0x1U, 14)); + + return 0; +} + +static unsigned long clk_scmi_trng_s_get_rate(rk_scmi_clock_t *clock) +{ + return clk_scmi_hclk_secure_s_get_rate(clock); +} + +static int clk_scmi_trng_s_set_rate(rk_scmi_clock_t *clock, unsigned long rate) +{ + return clk_scmi_hclk_secure_s_set_rate(clock, rate); +} + +static int clk_scmi_trng_s_set_status(rk_scmi_clock_t *clock, bool status) +{ + mmio_write_32(SCRU_BASE + CRU_CLKGATE_CON(3), + BITS_WITH_WMASK(!status, 0x1U, 6)); + + return 0; +} + +static unsigned long clk_scmi_h_trng_s_get_rate(rk_scmi_clock_t *clock) +{ + return clk_scmi_hclk_secure_s_get_rate(clock); +} + +static int clk_scmi_h_trng_s_set_rate(rk_scmi_clock_t *clock, unsigned long rate) +{ + return clk_scmi_hclk_secure_s_set_rate(clock, rate); +} + +static int clk_scmi_h_trng_s_set_status(rk_scmi_clock_t *clock, bool status) +{ + mmio_write_32(SCRU_BASE + CRU_CLKGATE_CON(2), + BITS_WITH_WMASK(!status, 0x1U, 15)); + + return 0; +} + +static unsigned long clk_scmi_p_otpc_s_get_rate(rk_scmi_clock_t *clock) +{ + return clk_scmi_pclk_secure_s_get_rate(clock); +} + +static int clk_scmi_p_otpc_s_set_rate(rk_scmi_clock_t *clock, unsigned long rate) +{ + return clk_scmi_pclk_secure_s_set_rate(clock, rate); +} + +static int clk_scmi_p_otpc_s_set_status(rk_scmi_clock_t *clock, bool status) +{ + mmio_write_32(SCRU_BASE + CRU_CLKGATE_CON(1), + BITS_WITH_WMASK(!status, 0x1U, 13)); + + return 0; +} + +static unsigned long clk_scmi_otpc_s_get_rate(rk_scmi_clock_t *clock) +{ + return OSC_HZ; +} + +static int clk_scmi_otpc_s_set_status(rk_scmi_clock_t *clock, bool status) +{ + mmio_write_32(SCRU_BASE + CRU_CLKGATE_CON(1), + BITS_WITH_WMASK(!status, 0x1U, 14)); + return 0; +} + +static unsigned long clk_scmi_otp_phy_get_rate(rk_scmi_clock_t *clock) +{ + return OSC_HZ; +} + +static int clk_scmi_otp_phy_set_status(rk_scmi_clock_t *clock, bool status) +{ + mmio_write_32(CRU_BASE + CRU_CLKGATE_CON(18), + BITS_WITH_WMASK(!status, 0x1U, 13)); + return 0; +} + +static unsigned long clk_scmi_otpc_rd_get_rate(rk_scmi_clock_t *clock) +{ + return OSC_HZ; +} + +static int clk_scmi_otpc_rd_set_status(rk_scmi_clock_t *clock, bool status) +{ + mmio_write_32(CRU_BASE + CRU_CLKGATE_CON(18), + BITS_WITH_WMASK(!status, 0x1U, 12)); + return 0; +} + +static unsigned long clk_scmi_otpc_arb_get_rate(rk_scmi_clock_t *clock) +{ + return OSC_HZ; +} + +static int clk_scmi_otpc_arb_set_status(rk_scmi_clock_t *clock, bool status) +{ + mmio_write_32(CRU_BASE + CRU_CLKGATE_CON(18), + BITS_WITH_WMASK(!status, 0x1U, 11)); + return 0; +} + +static const struct rk_clk_ops clk_scmi_cpul_ops = { + .get_rate = clk_scmi_cpul_get_rate, + .set_rate = clk_scmi_cpul_set_rate, + .set_status = clk_scmi_cpul_set_status, +}; + +static const struct rk_clk_ops clk_scmi_dsu_ops = { + .get_rate = clk_scmi_dsu_get_rate, + .set_rate = clk_scmi_dsu_set_rate, + .set_status = clk_scmi_dsu_set_status, +}; + +static const struct rk_clk_ops clk_scmi_cpub01_ops = { + .get_rate = clk_scmi_cpub01_get_rate, + .set_rate = clk_scmi_cpub01_set_rate, + .set_status = clk_scmi_cpub01_set_status, +}; + +static const struct rk_clk_ops clk_scmi_cpub23_ops = { + .get_rate = clk_scmi_cpub23_get_rate, + .set_rate = clk_scmi_cpub23_set_rate, + .set_status = clk_scmi_cpub23_set_status, +}; + +static const struct rk_clk_ops clk_scmi_gpu_ops = { + .get_rate = clk_scmi_gpu_get_rate, + .set_rate = clk_scmi_gpu_set_rate, + .set_status = clk_scmi_gpu_set_status, +}; + +static const struct rk_clk_ops clk_scmi_npu_ops = { + .get_rate = clk_scmi_npu_get_rate, + .set_rate = clk_scmi_npu_set_rate, + .set_status = clk_scmi_npu_set_status, +}; + +static const struct rk_clk_ops clk_scmi_sbus_ops = { + .get_rate = clk_scmi_sbus_get_rate, + .set_rate = clk_scmi_sbus_set_rate, + .set_status = clk_scmi_sbus_set_status, +}; + +static const struct rk_clk_ops clk_scmi_pclk_sbus_ops = { + .get_rate = clk_scmi_pclk_sbus_get_rate, + .set_rate = clk_scmi_pclk_sbus_set_rate, + .set_status = clk_scmi_pclk_sbus_set_status, +}; + +static const struct rk_clk_ops clk_scmi_cclk_sdmmc_ops = { + .get_rate = clk_scmi_cclk_sdmmc_get_rate, + .set_rate = clk_scmi_cclk_sdmmc_set_rate, + .set_status = clk_scmi_cclk_sdmmc_set_status, +}; + +static const struct rk_clk_ops clk_scmi_dclk_sdmmc_ops = { + .get_rate = clk_scmi_dclk_sdmmc_get_rate, + .set_rate = clk_scmi_dclk_sdmmc_set_rate, + .set_status = clk_scmi_dclk_sdmmc_set_status, +}; + +static const struct rk_clk_ops clk_scmi_aclk_secure_ns_ops = { + .get_rate = clk_scmi_aclk_secure_ns_get_rate, + .set_rate = clk_scmi_aclk_secure_ns_set_rate, + .set_status = clk_scmi_aclk_secure_ns_set_status, +}; + +static const struct rk_clk_ops clk_scmi_hclk_secure_ns_ops = { + .get_rate = clk_scmi_hclk_secure_ns_get_rate, + .set_rate = clk_scmi_hclk_secure_ns_set_rate, + .set_status = clk_scmi_hclk_secure_ns_set_status, +}; + +static const struct rk_clk_ops clk_scmi_tclk_wdt_ops = { + .get_rate = clk_scmi_tclk_wdt_get_rate, + .set_status = clk_scmi_tclk_wdt_set_status, +}; + +static const struct rk_clk_ops clk_scmi_keyladder_core_ops = { + .get_rate = clk_scmi_keyladder_core_get_rate, + .set_rate = clk_scmi_keyladder_core_set_rate, + .set_status = clk_scmi_keyladder_core_set_status, +}; + +static const struct rk_clk_ops clk_scmi_keyladder_rng_ops = { + .get_rate = clk_scmi_keyladder_rng_get_rate, + .set_rate = clk_scmi_keyladder_rng_set_rate, + .set_status = clk_scmi_keyladder_rng_set_status, +}; + +static const struct rk_clk_ops clk_scmi_aclk_secure_s_ops = { + .get_rate = clk_scmi_aclk_secure_s_get_rate, + .set_rate = clk_scmi_aclk_secure_s_set_rate, + .set_status = clk_scmi_aclk_secure_s_set_status, +}; + +static const struct rk_clk_ops clk_scmi_hclk_secure_s_ops = { + .get_rate = clk_scmi_hclk_secure_s_get_rate, + .set_rate = clk_scmi_hclk_secure_s_set_rate, + .set_status = clk_scmi_hclk_secure_s_set_status, +}; + +static const struct rk_clk_ops clk_scmi_pclk_secure_s_ops = { + .get_rate = clk_scmi_pclk_secure_s_get_rate, + .set_rate = clk_scmi_pclk_secure_s_set_rate, + .set_status = clk_scmi_pclk_secure_s_set_status, +}; + +static const struct rk_clk_ops clk_scmi_crypto_rng_ops = { + .get_rate = clk_scmi_crypto_rng_get_rate, + .set_rate = clk_scmi_crypto_rng_set_rate, + .set_status = clk_scmi_crypto_rng_set_status, +}; + +static const struct rk_clk_ops clk_scmi_crypto_core_ops = { + .get_rate = clk_scmi_crypto_core_get_rate, + .set_rate = clk_scmi_crypto_core_set_rate, + .set_status = clk_scmi_crypto_core_set_status, +}; + +static const struct rk_clk_ops clk_scmi_crypto_pka_ops = { + .get_rate = clk_scmi_crypto_pka_get_rate, + .set_rate = clk_scmi_crypto_pka_set_rate, + .set_status = clk_scmi_crypto_pka_set_status, +}; + +static const struct rk_clk_ops clk_scmi_spll_ops = { + .get_rate = clk_scmi_spll_get_rate, + .set_rate = clk_scmi_spll_set_rate, + .set_status = clk_scmi_spll_set_status, +}; + +static const struct rk_clk_ops clk_scmi_hclk_sd_ops = { + .get_rate = clk_scmi_hclk_sd_get_rate, + .set_status = clk_scmi_hclk_sd_set_status, +}; + +static const struct rk_clk_ops clk_scmi_crypto_rng_s_ops = { + .get_rate = clk_scmi_crypto_rng_s_get_rate, + .set_rate = clk_scmi_crypto_rng_s_set_rate, + .set_status = clk_scmi_crypto_rng_s_set_status, +}; + +static const struct rk_clk_ops clk_scmi_crypto_core_s_ops = { + .get_rate = clk_scmi_crypto_core_s_get_rate, + .set_rate = clk_scmi_crypto_core_s_set_rate, + .set_status = clk_scmi_crypto_core_s_set_status, +}; + +static const struct rk_clk_ops clk_scmi_crypto_pka_s_ops = { + .get_rate = clk_scmi_crypto_pka_s_get_rate, + .set_rate = clk_scmi_crypto_pka_s_set_rate, + .set_status = clk_scmi_crypto_pka_s_set_status, +}; + +static const struct rk_clk_ops clk_scmi_a_crypto_s_ops = { + .get_rate = clk_scmi_a_crypto_s_get_rate, + .set_rate = clk_scmi_a_crypto_s_set_rate, + .set_status = clk_scmi_a_crypto_s_set_status, +}; + +static const struct rk_clk_ops clk_scmi_h_crypto_s_ops = { + .get_rate = clk_scmi_h_crypto_s_get_rate, + .set_rate = clk_scmi_h_crypto_s_set_rate, + .set_status = clk_scmi_h_crypto_s_set_status, +}; + +static const struct rk_clk_ops clk_scmi_p_crypto_s_ops = { + .get_rate = clk_scmi_p_crypto_s_get_rate, + .set_rate = clk_scmi_p_crypto_s_set_rate, + .set_status = clk_scmi_p_crypto_s_set_status, +}; + +static const struct rk_clk_ops clk_scmi_a_keylad_s_ops = { + .get_rate = clk_scmi_a_keylad_s_get_rate, + .set_rate = clk_scmi_a_keylad_s_set_rate, + .set_status = clk_scmi_a_keylad_s_set_status, +}; + +static const struct rk_clk_ops clk_scmi_h_keylad_s_ops = { + .get_rate = clk_scmi_h_keylad_s_get_rate, + .set_rate = clk_scmi_h_keylad_s_set_rate, + .set_status = clk_scmi_h_keylad_s_set_status, +}; + +static const struct rk_clk_ops clk_scmi_p_keylad_s_ops = { + .get_rate = clk_scmi_p_keylad_s_get_rate, + .set_rate = clk_scmi_p_keylad_s_set_rate, + .set_status = clk_scmi_p_keylad_s_set_status, +}; + +static const struct rk_clk_ops clk_scmi_trng_s_ops = { + .get_rate = clk_scmi_trng_s_get_rate, + .set_rate = clk_scmi_trng_s_set_rate, + .set_status = clk_scmi_trng_s_set_status, +}; + +static const struct rk_clk_ops clk_scmi_h_trng_s_ops = { + .get_rate = clk_scmi_h_trng_s_get_rate, + .set_rate = clk_scmi_h_trng_s_set_rate, + .set_status = clk_scmi_h_trng_s_set_status, +}; + +static const struct rk_clk_ops clk_scmi_p_otpc_s_ops = { + .get_rate = clk_scmi_p_otpc_s_get_rate, + .set_rate = clk_scmi_p_otpc_s_set_rate, + .set_status = clk_scmi_p_otpc_s_set_status, +}; + +static const struct rk_clk_ops clk_scmi_otpc_s_ops = { + .get_rate = clk_scmi_otpc_s_get_rate, + .set_status = clk_scmi_otpc_s_set_status, +}; + +static const struct rk_clk_ops clk_scmi_otp_phy_ops = { + .get_rate = clk_scmi_otp_phy_get_rate, + .set_status = clk_scmi_otp_phy_set_status, +}; + +static const struct rk_clk_ops clk_scmi_otpc_rd_ops = { + .get_rate = clk_scmi_otpc_rd_get_rate, + .set_status = clk_scmi_otpc_rd_set_status, +}; + +static const struct rk_clk_ops clk_scmi_otpc_arb_ops = { + .get_rate = clk_scmi_otpc_arb_get_rate, + .set_status = clk_scmi_otpc_arb_set_status, +}; + +rk_scmi_clock_t clock_table[] = { + RK3588_SCMI_CLOCK(SCMI_CLK_CPUL, "scmi_clk_cpul", &clk_scmi_cpul_ops, rk3588_cpul_rates, ARRAY_SIZE(rk3588_cpul_rates), false), + RK3588_SCMI_CLOCK(SCMI_CLK_DSU, "scmi_clk_dsu", &clk_scmi_dsu_ops, rk3588_cpul_rates, ARRAY_SIZE(rk3588_cpul_rates), false), + RK3588_SCMI_CLOCK(SCMI_CLK_CPUB01, "scmi_clk_cpub01", &clk_scmi_cpub01_ops, rk3588_cpub_rates, ARRAY_SIZE(rk3588_cpub_rates), false), + RK3588_SCMI_CLOCK(SCMI_CLK_CPUB23, "scmi_clk_cpub23", &clk_scmi_cpub23_ops, rk3588_cpub_rates, ARRAY_SIZE(rk3588_cpub_rates), false), + RK3588_SCMI_CLOCK(SCMI_CLK_DDR, "scmi_clk_ddr", NULL, NULL, 0, false), + RK3588_SCMI_CLOCK(SCMI_CLK_GPU, "scmi_clk_gpu", &clk_scmi_gpu_ops, rk3588_gpu_rates, ARRAY_SIZE(rk3588_gpu_rates), false), + RK3588_SCMI_CLOCK(SCMI_CLK_NPU, "scmi_clk_npu", &clk_scmi_npu_ops, rk3588_gpu_rates, ARRAY_SIZE(rk3588_gpu_rates), false), + RK3588_SCMI_CLOCK(SCMI_CLK_SBUS, "scmi_clk_sbus", &clk_scmi_sbus_ops, rk3588_sbus_rates, ARRAY_SIZE(rk3588_sbus_rates), true), + RK3588_SCMI_CLOCK(SCMI_PCLK_SBUS, "scmi_pclk_sbus", &clk_scmi_pclk_sbus_ops, rk3588_sbus_rates, ARRAY_SIZE(rk3588_sbus_rates), true), + RK3588_SCMI_CLOCK(SCMI_CCLK_SD, "scmi_cclk_sd", &clk_scmi_cclk_sdmmc_ops, rk3588_sdmmc_rates, ARRAY_SIZE(rk3588_sdmmc_rates), false), + RK3588_SCMI_CLOCK(SCMI_DCLK_SD, "scmi_dclk_sd", &clk_scmi_dclk_sdmmc_ops, rk3588_sdmmc_rates, ARRAY_SIZE(rk3588_sdmmc_rates), false), + RK3588_SCMI_CLOCK(SCMI_ACLK_SECURE_NS, "scmi_aclk_se_ns", &clk_scmi_aclk_secure_ns_ops, rk3588_sbus_rates, ARRAY_SIZE(rk3588_sbus_rates), false), + RK3588_SCMI_CLOCK(SCMI_HCLK_SECURE_NS, "scmi_hclk_se_ns", &clk_scmi_hclk_secure_ns_ops, rk3588_sbus_rates, ARRAY_SIZE(rk3588_sbus_rates), false), + RK3588_SCMI_CLOCK(SCMI_TCLK_WDT, "scmi_tclk_wdt", &clk_scmi_tclk_wdt_ops, NULL, 0, false), + RK3588_SCMI_CLOCK(SCMI_KEYLADDER_CORE, "scmi_keylad_c", &clk_scmi_keyladder_core_ops, rk3588_sbus_rates, ARRAY_SIZE(rk3588_sbus_rates), true), + RK3588_SCMI_CLOCK(SCMI_KEYLADDER_RNG, "scmi_keylad_r", &clk_scmi_keyladder_rng_ops, rk3588_sbus_rates, ARRAY_SIZE(rk3588_sbus_rates), true), + RK3588_SCMI_CLOCK(SCMI_ACLK_SECURE_S, "scmi_aclk_se_s", &clk_scmi_aclk_secure_s_ops, rk3588_sbus_rates, ARRAY_SIZE(rk3588_sbus_rates), true), + RK3588_SCMI_CLOCK(SCMI_HCLK_SECURE_S, "scmi_hclk_se_s", &clk_scmi_hclk_secure_s_ops, rk3588_sbus_rates, ARRAY_SIZE(rk3588_sbus_rates), true), + RK3588_SCMI_CLOCK(SCMI_PCLK_SECURE_S, "scmi_pclk_se_s", &clk_scmi_pclk_secure_s_ops, rk3588_sbus_rates, ARRAY_SIZE(rk3588_sbus_rates), true), + RK3588_SCMI_CLOCK(SCMI_CRYPTO_RNG, "scmi_crypto_r", &clk_scmi_crypto_rng_ops, rk3588_sbus_rates, ARRAY_SIZE(rk3588_sbus_rates), false), + RK3588_SCMI_CLOCK(SCMI_CRYPTO_CORE, "scmi_crypto_c", &clk_scmi_crypto_core_ops, rk3588_sbus_rates, ARRAY_SIZE(rk3588_sbus_rates), false), + RK3588_SCMI_CLOCK(SCMI_CRYPTO_PKA, "scmi_crypto_p", &clk_scmi_crypto_pka_ops, rk3588_sbus_rates, ARRAY_SIZE(rk3588_sbus_rates), false), + RK3588_SCMI_CLOCK(SCMI_SPLL, "scmi_spll", &clk_scmi_spll_ops, rk3588_sbus_rates, ARRAY_SIZE(rk3588_sbus_rates), false), + RK3588_SCMI_CLOCK(SCMI_HCLK_SD, "scmi_hclk_sd", &clk_scmi_hclk_sd_ops, NULL, 0, false), + RK3588_SCMI_CLOCK(SCMI_CRYPTO_RNG_S, "scmi_crypto_r_s", &clk_scmi_crypto_rng_s_ops, rk3588_sbus_rates, ARRAY_SIZE(rk3588_sbus_rates), true), + RK3588_SCMI_CLOCK(SCMI_CRYPTO_CORE_S, "scmi_crypto_c_s", &clk_scmi_crypto_core_s_ops, rk3588_sbus_rates, ARRAY_SIZE(rk3588_sbus_rates), true), + RK3588_SCMI_CLOCK(SCMI_CRYPTO_PKA_S, "scmi_crypto_p_s", &clk_scmi_crypto_pka_s_ops, rk3588_sbus_rates, ARRAY_SIZE(rk3588_sbus_rates), true), + RK3588_SCMI_CLOCK(SCMI_A_CRYPTO_S, "scmi_a_crypto_s", &clk_scmi_a_crypto_s_ops, rk3588_sbus_rates, ARRAY_SIZE(rk3588_sbus_rates), true), + RK3588_SCMI_CLOCK(SCMI_H_CRYPTO_S, "scmi_h_crypto_s", &clk_scmi_h_crypto_s_ops, rk3588_sbus_rates, ARRAY_SIZE(rk3588_sbus_rates), true), + RK3588_SCMI_CLOCK(SCMI_P_CRYPTO_S, "scmi_p_crypto_s", &clk_scmi_p_crypto_s_ops, rk3588_sbus_rates, ARRAY_SIZE(rk3588_sbus_rates), true), + RK3588_SCMI_CLOCK(SCMI_A_KEYLADDER_S, "scmi_a_keylad_s", &clk_scmi_a_keylad_s_ops, rk3588_sbus_rates, ARRAY_SIZE(rk3588_sbus_rates), true), + RK3588_SCMI_CLOCK(SCMI_H_KEYLADDER_S, "scmi_h_keylad_s", &clk_scmi_h_keylad_s_ops, rk3588_sbus_rates, ARRAY_SIZE(rk3588_sbus_rates), true), + RK3588_SCMI_CLOCK(SCMI_P_KEYLADDER_S, "scmi_p_keylad_s", &clk_scmi_p_keylad_s_ops, rk3588_sbus_rates, ARRAY_SIZE(rk3588_sbus_rates), true), + RK3588_SCMI_CLOCK(SCMI_TRNG_S, "scmi_trng_s", &clk_scmi_trng_s_ops, rk3588_sbus_rates, ARRAY_SIZE(rk3588_sbus_rates), true), + RK3588_SCMI_CLOCK(SCMI_H_TRNG_S, "scmi_h_trng_s", &clk_scmi_h_trng_s_ops, rk3588_sbus_rates, ARRAY_SIZE(rk3588_sbus_rates), true), + RK3588_SCMI_CLOCK(SCMI_P_OTPC_S, "scmi_p_otpc_s", &clk_scmi_p_otpc_s_ops, rk3588_sbus_rates, ARRAY_SIZE(rk3588_sbus_rates), true), + RK3588_SCMI_CLOCK(SCMI_OTPC_S, "scmi_otpc_s", &clk_scmi_otpc_s_ops, NULL, 0, true), + RK3588_SCMI_CLOCK(SCMI_OTP_PHY, "scmi_otp_phy", &clk_scmi_otp_phy_ops, NULL, 0, false), + RK3588_SCMI_CLOCK(SCMI_OTPC_AUTO_RD, "scmi_otpc_rd", &clk_scmi_otpc_rd_ops, NULL, 0, false), + RK3588_SCMI_CLOCK(SCMI_OTPC_ARB, "scmi_otpc_arb", &clk_scmi_otpc_arb_ops, NULL, 0, false), +}; + +size_t rockchip_scmi_clock_count(unsigned int agent_id __unused) +{ + return ARRAY_SIZE(clock_table); +} + +rk_scmi_clock_t *rockchip_scmi_get_clock(uint32_t agent_id __unused, + uint32_t clock_id) +{ + rk_scmi_clock_t *table = NULL; + + if (clock_id < ARRAY_SIZE(clock_table)) + table = &clock_table[clock_id]; + + if (table && !table->is_security) + return table; + else + return NULL; +} + +void pvtplls_suspend(void) +{ + clk_cpul_set_rate(408000000, PLL_SEL_NOR); + clk_dsu_set_rate(408000000, PLL_SEL_NOR); + clk_cpub01_set_rate(408000000, PLL_SEL_NOR); + clk_cpub23_set_rate(408000000, PLL_SEL_NOR); +} + +void pvtplls_resume(void) +{ + clk_cpul_set_rate(sys_clk_info.cpul_rate, PLL_SEL_AUTO); + clk_dsu_set_rate(sys_clk_info.dsu_rate, PLL_SEL_AUTO); + clk_cpub01_set_rate(sys_clk_info.cpub01_rate, PLL_SEL_AUTO); + clk_cpub23_set_rate(sys_clk_info.cpub23_rate, PLL_SEL_AUTO); +} + +void sys_reset_pvtplls_prepare(void) +{ + clk_gpu_set_rate(100000000, PLL_SEL_NOR); + clk_npu_set_rate(100000000, PLL_SEL_NOR); + clk_cpul_set_rate(408000000, PLL_SEL_NOR); + clk_cpub01_set_rate(408000000, PLL_SEL_NOR); + clk_cpub23_set_rate(408000000, PLL_SEL_NOR); + clk_dsu_set_rate(408000000, PLL_SEL_NOR); +} + +void rockchip_clock_init(void) +{ + /* set gpll src div to 0 for cpul */ + mmio_write_32(DSUCRU_BASE + CRU_CLKSEL_CON(5), CLKDIV_5BITS_SHF(0U, 9)); + /* set gpll src div to 0 for cpub01 */ + mmio_write_32(BIGCORE0CRU_BASE + CRU_CLKSEL_CON(0), + CLKDIV_5BITS_SHF(0U, 1)); + /* set gpll src div to 0 for cpu23 */ + mmio_write_32(BIGCORE1CRU_BASE + CRU_CLKSEL_CON(0), + CLKDIV_5BITS_SHF(0U, 1)); + + mmio_write_32(BIGCORE0CRU_BASE + CRU_CLKSEL_CON(2), + CPUB_PCLK_PATH_50M); + mmio_write_32(BIGCORE1CRU_BASE + CRU_CLKSEL_CON(2), + CPUB_PCLK_PATH_50M); + + mmio_write_32(DSUCRU_BASE + DSUCRU_CLKSEL_CON(4), + CLKDIV_5BITS_SHF(5U, 0)); + mmio_write_32(DSUCRU_BASE + DSUCRU_CLKSEL_CON(4), + BITS_WITH_WMASK(PCLK_DSU_ROOT_SEL_GPLL, + PCLK_DSU_ROOT_SEL_MASK, + PCLK_DSU_ROOT_SEL_SHIFT)); + + sys_clk_info.cpul_table = rk3588_cpul_pvtpll_table; + sys_clk_info.cpul_rate_count = ARRAY_SIZE(rk3588_cpul_pvtpll_table); + sys_clk_info.cpub01_table = rk3588_cpub0_pvtpll_table; + sys_clk_info.cpub01_rate_count = ARRAY_SIZE(rk3588_cpub0_pvtpll_table); + sys_clk_info.cpub23_table = rk3588_cpub1_pvtpll_table; + sys_clk_info.cpub23_rate_count = ARRAY_SIZE(rk3588_cpub1_pvtpll_table); + memcpy(sys_clk_info.cpub23_table, sys_clk_info.cpub01_table, + sys_clk_info.cpub01_rate_count * sizeof(*sys_clk_info.cpub01_table)); + sys_clk_info.gpu_table = rk3588_gpu_pvtpll_table; + sys_clk_info.gpu_rate_count = ARRAY_SIZE(rk3588_gpu_pvtpll_table); + sys_clk_info.npu_table = rk3588_npu_pvtpll_table; + sys_clk_info.npu_rate_count = ARRAY_SIZE(rk3588_npu_pvtpll_table); +} diff --git a/plat/rockchip/rk3588/drivers/scmi/rk3588_clk.h b/plat/rockchip/rk3588/drivers/scmi/rk3588_clk.h new file mode 100644 index 00000000..66fddaa3 --- /dev/null +++ b/plat/rockchip/rk3588/drivers/scmi/rk3588_clk.h @@ -0,0 +1,104 @@ +/* + * Copyright (c) 2024, Rockchip, Inc. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef __CLOCK_H__ +#define __CLOCK_H__ + +/* scmi-clocks indices */ + +#define SCMI_CLK_CPUL 0 +#define SCMI_CLK_DSU 1 +#define SCMI_CLK_CPUB01 2 +#define SCMI_CLK_CPUB23 3 +#define SCMI_CLK_DDR 4 +#define SCMI_CLK_GPU 5 +#define SCMI_CLK_NPU 6 +#define SCMI_CLK_SBUS 7 +#define SCMI_PCLK_SBUS 8 +#define SCMI_CCLK_SD 9 +#define SCMI_DCLK_SD 10 +#define SCMI_ACLK_SECURE_NS 11 +#define SCMI_HCLK_SECURE_NS 12 +#define SCMI_TCLK_WDT 13 +#define SCMI_KEYLADDER_CORE 14 +#define SCMI_KEYLADDER_RNG 15 +#define SCMI_ACLK_SECURE_S 16 +#define SCMI_HCLK_SECURE_S 17 +#define SCMI_PCLK_SECURE_S 18 +#define SCMI_CRYPTO_RNG 19 +#define SCMI_CRYPTO_CORE 20 +#define SCMI_CRYPTO_PKA 21 +#define SCMI_SPLL 22 +#define SCMI_HCLK_SD 23 +#define SCMI_CRYPTO_RNG_S 24 +#define SCMI_CRYPTO_CORE_S 25 +#define SCMI_CRYPTO_PKA_S 26 +#define SCMI_A_CRYPTO_S 27 +#define SCMI_H_CRYPTO_S 28 +#define SCMI_P_CRYPTO_S 29 +#define SCMI_A_KEYLADDER_S 30 +#define SCMI_H_KEYLADDER_S 31 +#define SCMI_P_KEYLADDER_S 32 +#define SCMI_TRNG_S 33 +#define SCMI_H_TRNG_S 34 +#define SCMI_P_OTPC_S 35 +#define SCMI_OTPC_S 36 +#define SCMI_OTP_PHY 37 +#define SCMI_OTPC_AUTO_RD 38 +#define SCMI_OTPC_ARB 39 + +/******** DSUCRU **************************************/ +#define DSUCRU_CLKSEL_CON(n) (0x0300 + (n) * 4) + +/********Name=DSUCRU_CLKSEL_CON04,Offset=0x310********/ +#define PCLK_DSU_ROOT_SEL_SHIFT 5 +#define PCLK_DSU_ROOT_SEL_MASK 0x3 +#define PCLK_DSU_ROOT_SEL_GPLL 0x3 + +/********Name=SECURE_SOFTRST_CON00,Offset=0xA00********/ +#define SRST_A_SECURE_NS_BIU 10 +#define SRST_H_SECURE_NS_BIU 11 +#define SRST_A_SECURE_S_BIU 12 +#define SRST_H_SECURE_S_BIU 13 +#define SRST_P_SECURE_S_BIU 14 +#define SRST_CRYPTO_CORE 15 +/********Name=SECURE_SOFTRST_CON01,Offset=0xA04********/ +#define SRST_CRYPTO_PKA 16 +#define SRST_CRYPTO_RNG 17 +#define SRST_A_CRYPTO 18 +#define SRST_H_CRYPTO 19 +#define SRST_KEYLADDER_CORE 25 +#define SRST_KEYLADDER_RNG 26 +#define SRST_A_KEYLADDER 27 +#define SRST_H_KEYLADDER 28 +#define SRST_P_OTPC_S 29 +#define SRST_OTPC_S 30 +#define SRST_WDT_S 31 +/********Name=SECURE_SOFTRST_CON02,Offset=0xA08********/ +#define SRST_T_WDT_S 32 +#define SRST_H_BOOTROM 33 +#define SRST_A_DCF 34 +#define SRST_P_DCF 35 +#define SRST_H_BOOTROM_NS 37 +#define SRST_P_KEYLADDER 46 +#define SRST_H_TRNG_S 47 +/********Name=SECURE_SOFTRST_CON03,Offset=0xA0C********/ +#define SRST_H_TRNG_NS 48 +#define SRST_D_SDMMC_BUFFER 49 +#define SRST_H_SDMMC 50 +#define SRST_H_SDMMC_BUFFER 51 +#define SRST_SDMMC 52 +#define SRST_P_TRNG_CHK 53 +#define SRST_TRNG_S 54 + +#define SRST_INVALID 55 + +void pvtplls_suspend(void); +void pvtplls_resume(void); + +void rockchip_clock_init(void); + +#endif diff --git a/plat/rockchip/rk3588/drivers/scmi/rk3588_rstd.c b/plat/rockchip/rk3588/drivers/scmi/rk3588_rstd.c new file mode 100644 index 00000000..50b99e72 --- /dev/null +++ b/plat/rockchip/rk3588/drivers/scmi/rk3588_rstd.c @@ -0,0 +1,96 @@ +/* + * Copyright (c) 2024, Rockchip, Inc. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <assert.h> +#include <errno.h> + +#include <drivers/delay_timer.h> +#include <drivers/scmi.h> +#include <lib/mmio.h> +#include <platform_def.h> + +#include <plat_private.h> +#include "rk3588_clk.h" +#include <scmi_rstd.h> +#include <soc.h> + +#define DEFAULT_RESET_DOM_ATTRIBUTE 0 + +#define RK3588_SCMI_RESET(_id, _name, _attribute, _ops) \ +{ \ + .id = _id, \ + .name = _name, \ + .attribute = _attribute, \ + .rstd_ops = _ops, \ +} + +static int rk3588_reset_explicit(rk_scmi_rstd_t *reset_domain, + bool assert_not_deassert) +{ + int bank = reset_domain->id / 16; + int offset = reset_domain->id % 16; + + mmio_write_32(SCRU_BASE + CRU_SOFTRST_CON(bank), + BITS_WITH_WMASK(assert_not_deassert, 0x1U, offset)); + return SCMI_SUCCESS; +} + +static struct rk_scmi_rstd_ops rk3588_reset_domain_ops = { + .reset_explicit = rk3588_reset_explicit, +}; + +static rk_scmi_rstd_t rk3588_reset_domain_table[] = { + RK3588_SCMI_RESET(SRST_CRYPTO_CORE, "scmi_sr_cy_core", DEFAULT_RESET_DOM_ATTRIBUTE, &rk3588_reset_domain_ops), + RK3588_SCMI_RESET(SRST_CRYPTO_PKA, "scmi_sr_cy_pka", DEFAULT_RESET_DOM_ATTRIBUTE, &rk3588_reset_domain_ops), + RK3588_SCMI_RESET(SRST_CRYPTO_RNG, "scmi_sr_cy_rng", DEFAULT_RESET_DOM_ATTRIBUTE, &rk3588_reset_domain_ops), + RK3588_SCMI_RESET(SRST_A_CRYPTO, "scmi_sr_a_cy", DEFAULT_RESET_DOM_ATTRIBUTE, &rk3588_reset_domain_ops), + RK3588_SCMI_RESET(SRST_H_CRYPTO, "scmi_sr_h_cy", DEFAULT_RESET_DOM_ATTRIBUTE, &rk3588_reset_domain_ops), + RK3588_SCMI_RESET(SRST_KEYLADDER_CORE, "scmi_sr_k_core", DEFAULT_RESET_DOM_ATTRIBUTE, &rk3588_reset_domain_ops), + RK3588_SCMI_RESET(SRST_KEYLADDER_RNG, "scmi_sr_k_rng", DEFAULT_RESET_DOM_ATTRIBUTE, &rk3588_reset_domain_ops), + RK3588_SCMI_RESET(SRST_P_OTPC_S, "scmi_sr_p_otp", DEFAULT_RESET_DOM_ATTRIBUTE, &rk3588_reset_domain_ops), + RK3588_SCMI_RESET(SRST_OTPC_S, "scmi_sr_otp", DEFAULT_RESET_DOM_ATTRIBUTE, &rk3588_reset_domain_ops), + RK3588_SCMI_RESET(SRST_WDT_S, "scmi_sr_wdt", DEFAULT_RESET_DOM_ATTRIBUTE, &rk3588_reset_domain_ops), + RK3588_SCMI_RESET(SRST_T_WDT_S, "scmi_sr_t_wdt", DEFAULT_RESET_DOM_ATTRIBUTE, &rk3588_reset_domain_ops), + RK3588_SCMI_RESET(SRST_H_BOOTROM, "scmi_sr_h_boot", DEFAULT_RESET_DOM_ATTRIBUTE, &rk3588_reset_domain_ops), + RK3588_SCMI_RESET(SRST_P_KEYLADDER, "scmi_sr_p_ky", DEFAULT_RESET_DOM_ATTRIBUTE, &rk3588_reset_domain_ops), + RK3588_SCMI_RESET(SRST_H_TRNG_S, "scmi_sr_h_trng", DEFAULT_RESET_DOM_ATTRIBUTE, &rk3588_reset_domain_ops), + RK3588_SCMI_RESET(SRST_H_TRNG_NS, "scmi_sr_t_trng", DEFAULT_RESET_DOM_ATTRIBUTE, &rk3588_reset_domain_ops), + RK3588_SCMI_RESET(SRST_D_SDMMC_BUFFER, "scmi_sr_d_sd", DEFAULT_RESET_DOM_ATTRIBUTE, &rk3588_reset_domain_ops), + RK3588_SCMI_RESET(SRST_H_SDMMC, "scmi_sr_h_sd", DEFAULT_RESET_DOM_ATTRIBUTE, &rk3588_reset_domain_ops), + RK3588_SCMI_RESET(SRST_H_SDMMC_BUFFER, "scmi_sr_h_sd_b", DEFAULT_RESET_DOM_ATTRIBUTE, &rk3588_reset_domain_ops), + RK3588_SCMI_RESET(SRST_SDMMC, "scmi_sr_sd", DEFAULT_RESET_DOM_ATTRIBUTE, &rk3588_reset_domain_ops), + RK3588_SCMI_RESET(SRST_P_TRNG_CHK, "scmi_sr_p_trng", DEFAULT_RESET_DOM_ATTRIBUTE, &rk3588_reset_domain_ops), + RK3588_SCMI_RESET(SRST_TRNG_S, "scmi_sr_trng", DEFAULT_RESET_DOM_ATTRIBUTE, &rk3588_reset_domain_ops), + RK3588_SCMI_RESET(SRST_INVALID, "scmi_sr_invalid", DEFAULT_RESET_DOM_ATTRIBUTE, NULL), +}; + +static rk_scmi_rstd_t * +rockchip_get_reset_domain_table(int id) +{ + rk_scmi_rstd_t *reset = rk3588_reset_domain_table; + int i = 0, cnt = ARRAY_SIZE(rk3588_reset_domain_table); + + for (i = 0; i < cnt; i++) { + if (reset->id == id) + return &rk3588_reset_domain_table[i]; + reset++; + } + + return &rk3588_reset_domain_table[cnt - 1]; +} + +rk_scmi_rstd_t *rockchip_scmi_get_rstd(unsigned int agent_id, + unsigned int scmi_id) + +{ + return rockchip_get_reset_domain_table(scmi_id); +} + +size_t rockchip_scmi_rstd_count(unsigned int agent_id) +{ + return SRST_TRNG_S; +} + diff --git a/plat/rockchip/rk3588/drivers/secure/secure.c b/plat/rockchip/rk3588/drivers/secure/secure.c new file mode 100644 index 00000000..fc9f2114 --- /dev/null +++ b/plat/rockchip/rk3588/drivers/secure/secure.c @@ -0,0 +1,188 @@ +/* + * Copyright (c) 2024, Rockchip, Inc. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <assert.h> +#include <lib/mmio.h> + +#include <platform_def.h> + +#include <secure.h> +#include <soc.h> + +static void secure_fw_master_init(void) +{ + uint32_t i; + + /* ddr_mcu can access all ddr-regions */ + mmio_write_32(FIREWALL_DDR_BASE + FIREWALL_DDR_MST(1), 0x0000ffff); + /* dcf/crypto_s can access all ddr-regions */ + mmio_write_32(FIREWALL_DDR_BASE + FIREWALL_DDR_MST(14), 0x00000000); + /* dsu_mp_sec can access all ddr-regions. + * DSU access memory [f000_0000~ff00_0000] through MP in firewall_ddr. + */ + mmio_write_32(FIREWALL_DDR_BASE + FIREWALL_DDR_MST(36), 0xffff0000); + + /* all other ns-master can't access all ddr-regions */ + for (i = 0; i < FIREWALL_DDR_MST_CNT; i++) { + if (i == 1 || i == 14 || i == 36) + continue; + + mmio_write_32(FIREWALL_DDR_BASE + FIREWALL_DDR_MST(i), 0xffffffff); + } + + /* mcu_pmu can access all sram-regions */ + mmio_write_32(FIREWALL_SYSMEM_BASE + FIREWALL_SYSMEM_MST(19), 0x000000ff); + /* dsu mp-sec can access all sram-regions */ + mmio_write_32(FIREWALL_SYSMEM_BASE + FIREWALL_SYSMEM_MST(38), 0x000000ff); + /* nsp_dsu2main_sec can access all sram-regions */ + mmio_write_32(FIREWALL_SYSMEM_BASE + FIREWALL_SYSMEM_MST(41), 0x00000000); + + /* all ns-master can't access all sram-regions */ + for (i = 0; i < FIREWALL_SYSMEM_MST_CNT; i++) { + if (i == 19 || i == 38 || i == 41) + continue; + + mmio_write_32(FIREWALL_SYSMEM_BASE + FIREWALL_SYSMEM_MST(i), + 0x00ff00ff); + } + + /* dsu-ns can't access all ddr-regions, dsu-s can access all ddr-regions */ + mmio_write_32(FIREWALL_DSU_BASE + FIREWALL_DSU_MST(0), 0xffffffff); + mmio_write_32(FIREWALL_DSU_BASE + FIREWALL_DSU_MST(1), 0x00000000); + dsb(); + isb(); +} + +/* unit: Mb */ +static void dsu_fw_rgn_config(uint64_t base_mb, uint64_t top_mb, int rgn_id) +{ + int i; + + if (rgn_id >= FIREWALL_DSU_RGN_CNT || rgn_id < 0) { + ERROR("%s regions-id:%d is invalid!\n", __func__, rgn_id); + panic(); + } + + mmio_write_32(FIREWALL_DSU_BASE + FIREWALL_DSU_RGN(rgn_id), + RG_MAP_SECURE(top_mb, base_mb)); + + for (i = 0; i < DDR_CHN_CNT; i++) + mmio_setbits_32(FIREWALL_DSU_BASE + FIREWALL_DSU_CON(i), + BIT(rgn_id)); +} + +/* unit: Mb */ +static void ddr_fw_rgn_config(uint64_t base_mb, uint64_t top_mb, int rgn_id) +{ + if (rgn_id >= FIREWALL_DDR_RGN_CNT || rgn_id < 0) { + ERROR("%s regions-id:%d is invalid!\n", __func__, rgn_id); + panic(); + } + + mmio_write_32(FIREWALL_DDR_BASE + FIREWALL_DDR_RGN(rgn_id), + RG_MAP_SECURE(top_mb, base_mb)); + + /* enable region */ + mmio_setbits_32(FIREWALL_DDR_BASE + FIREWALL_DDR_CON, + BIT(rgn_id)); +} + +/* Unit: Kb */ +static void sram_fw_rgn_config(uint64_t base_kb, uint64_t top_kb, int rgn_id) +{ + if (rgn_id >= FIREWALL_SYSMEM_RGN_CNT || rgn_id < 0) { + ERROR("%s regions-id:%d is invalid!\n", __func__, rgn_id); + panic(); + } + + mmio_write_32(FIREWALL_SYSMEM_BASE + FIREWALL_SYSMEM_RGN(rgn_id), + RG_MAP_SRAM_SECURE(top_kb, base_kb)); + + /* enable region */ + mmio_setbits_32(FIREWALL_SYSMEM_BASE + FIREWALL_SYSMEM_CON, BIT(rgn_id)); +} + +static void secure_region_init(void) +{ + uint32_t i; + + /* disable all region first except region0 */ + mmio_clrbits_32(FIREWALL_DDR_BASE + FIREWALL_DDR_CON, 0xfffe); + for (i = 0; i < FIREWALL_DSU_CON_CNT; i++) + mmio_clrbits_32(FIREWALL_DSU_BASE + FIREWALL_DSU_CON(i), 0xfffe); + mmio_clrbits_32(FIREWALL_SYSMEM_BASE + FIREWALL_SYSMEM_CON, 0xfe); + + secure_fw_master_init(); + + /* Use FW_DDR_RGN0_REG to config 0~1M space to secure */ + dsu_fw_rgn_config(0, 1, 0); + ddr_fw_rgn_config(0, 1, 0); + + /* Use FIREWALL_SYSMEM_RGN0 to config SRAM_ENTRY code(0~4k of sram) to secure */ + sram_fw_rgn_config(0, 4, 0); + /* For 0xffff0000~0xffffffff, use FIREWALL_SYSMEM_RGN7 to config + * 960~1024k of sram to secure. + */ + sram_fw_rgn_config(960, 1024, 7); +} + +void secure_timer_init(void) +{ + /* gpu's cntvalue comes from stimer1 channel_5 */ + mmio_write_32(STIMER1_CHN_BASE(5) + TIMER_CONTROL_REG, + TIMER_DIS); + + mmio_write_32(STIMER1_CHN_BASE(5) + TIMER_LOAD_COUNT0, 0xffffffff); + mmio_write_32(STIMER1_CHN_BASE(5) + TIMER_LOAD_COUNT1, 0xffffffff); + + /* auto reload & enable the timer */ + mmio_write_32(STIMER1_CHN_BASE(5) + TIMER_CONTROL_REG, + TIMER_EN | TIMER_FMODE); +} + +void sgrf_init(void) +{ + uint32_t i; + + secure_region_init(); + + /* config master ddr_mcu_prot|dcf_wr|dcf_rd as secure */ + mmio_write_32(BUSSGRF_BASE + SGRF_SOC_CON(14), 0x001f0011); + mmio_write_32(BUSSGRF_BASE + SGRF_SOC_CON(15), 0xffffffff); + mmio_write_32(BUSSGRF_BASE + SGRF_SOC_CON(16), 0x03ff03ff); + + /* config slave mailbox_mcu_ddr as secure */ + mmio_write_32(BUSSGRF_BASE + SGRF_FIREWALL_CON(4), 0xffff2000); + /* config slave int256mux4_mcu_ddr|int256mux4_mcu_pmu as secure */ + mmio_write_32(BUSSGRF_BASE + SGRF_FIREWALL_CON(5), 0xffff0060); + /* config slave ddrgrf*|dma2ddr|ddrphy*_cru|umctl* as secure */ + mmio_write_32(BUSSGRF_BASE + SGRF_FIREWALL_CON(24), 0xffff0fbf); + /* config slave ddrphy*|ddr_stanby*|ddr_mcu_timer|ddr_mcu_wdt as secure */ + mmio_write_32(BUSSGRF_BASE + SGRF_FIREWALL_CON(25), 0xffff03ff); + + /* config all other slave as ns */ + for (i = 0; i < SGRF_FIREWALL_CON_CNT; i++) { + if (i == 4 || i == 5 || i == 24 || i == 25) + continue; + + mmio_write_32(BUSSGRF_BASE + SGRF_FIREWALL_CON(i), 0xffff0000); + } + + /* config vad_hprot non-secure, pmu_mcu_hprot as secure */ + mmio_write_32(PMU1SGRF_BASE + PMU1SGRF_SOC_CON(0), 0x00180010); + /* config pmu1, pmu0, pmu_sram as secure */ + mmio_write_32(PMU1SGRF_BASE + PMU1SGRF_SOC_CON(1), 0xefbe6020); + /* config remap_pmu_mem, h_pmu_mem as secure */ + mmio_write_32(PMU1SGRF_BASE + PMU1SGRF_SOC_CON(2), 0x01f900c0); + + /* disable dp encryption */ + mmio_write_32(BUSSGRF_BASE + SGRF_SOC_CON(13), 0x00180018); + + /* select grf config for pcie ats */ + mmio_write_32(BUSSGRF_BASE + SGRF_SOC_CON(17), 0x11111111); + mmio_write_32(BUSSGRF_BASE + SGRF_SOC_CON(18), 0x11111111); + mmio_write_32(BUSSGRF_BASE + SGRF_SOC_CON(19), 0x00110011); +} diff --git a/plat/rockchip/rk3588/drivers/secure/secure.h b/plat/rockchip/rk3588/drivers/secure/secure.h new file mode 100644 index 00000000..d9c234fd --- /dev/null +++ b/plat/rockchip/rk3588/drivers/secure/secure.h @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2024, Rockchip, Inc. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef SECURE_H +#define SECURE_H + +/* DSUSGRF */ +#define DSU_SGRF_SOC_CON(i) ((i) * 4) +#define DSUSGRF_SOC_CON(i) ((i) * 4) +#define DSUSGRF_SOC_CON_CNT 13 +#define DSUSGRF_DDR_HASH_CON(i) (0x240 + (i) * 4) +#define DSUSGRF_DDR_HASH_CON_CNT 8 + +/* PMUSGRF */ +#define PMU1SGRF_SOC_CON(n) ((n) * 4) + +/* SGRF */ +#define SGRF_SOC_CON(i) ((i) * 4) +#define SGRF_FIREWALL_CON(i) (0x240 + (i) * 4) +#define SGRF_FIREWALL_CON_CNT 32 + +/* ddr firewall */ +#define FIREWALL_DDR_RGN(i) ((i) * 0x4) +#define FIREWALL_DDR_RGN_CNT 16 +#define FIREWALL_DDR_MST(i) (0x40 + (i) * 0x4) +#define FIREWALL_DDR_MST_CNT 42 +#define FIREWALL_DDR_CON 0xf0 + +#define FIREWALL_SYSMEM_RGN(i) ((i) * 0x4) +#define FIREWALL_SYSMEM_RGN_CNT 8 +#define FIREWALL_SYSMEM_MST(i) (0x40 + (i) * 0x4) +#define FIREWALL_SYSMEM_MST_CNT 43 +#define FIREWALL_SYSMEM_CON 0xf0 + +#define FIREWALL_DSU_RGN(i) ((i) * 0x4) +#define FIREWALL_DSU_RGN_CNT 16 +#define FIREWALL_DSU_MST(i) (0x40 + (i) * 0x4) +#define FIREWALL_DSU_MST_CNT 2 +#define FIREWALL_DSU_CON(i) (0xf0 + (i) * 4) +#define FIREWALL_DSU_CON_CNT 4 + +#define PLAT_MAX_DDR_CAPACITY_MB 0x8000 /* for 32Gb */ +#define RG_MAP_SECURE(top, base) \ + (((((top) - 1) & 0x7fff) << 16) | ((base) & 0x7fff)) +#define RG_MAP_SRAM_SECURE(top_kb, base_kb) \ + (((((top_kb) / 4 - 1) & 0xff) << 16) | ((base_kb) / 4 & 0xff)) + +void secure_timer_init(void); +void sgrf_init(void); + +#endif /* SECURE_H */ diff --git a/plat/rockchip/rk3588/drivers/soc/soc.c b/plat/rockchip/rk3588/drivers/soc/soc.c new file mode 100644 index 00000000..6db81eeb --- /dev/null +++ b/plat/rockchip/rk3588/drivers/soc/soc.c @@ -0,0 +1,99 @@ +/* + * Copyright (c) 2024, Rockchip, Inc. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <assert.h> +#include <errno.h> + +#include <arch_helpers.h> +#include <bl31/bl31.h> +#include <common/debug.h> +#include <drivers/console.h> +#include <drivers/delay_timer.h> +#include <lib/mmio.h> +#include <lib/xlat_tables/xlat_tables_compat.h> +#include <platform.h> +#include <platform_def.h> +#include <pmu.h> + +#include <plat_private.h> +#include <rk3588_clk.h> +#include <secure.h> +#include <soc.h> + +#define RK3588_DEV_RNG0_BASE 0xf0000000 +#define RK3588_DEV_RNG0_SIZE 0x0ffff000 + +const mmap_region_t plat_rk_mmap[] = { + MAP_REGION_FLAT(RK3588_DEV_RNG0_BASE, RK3588_DEV_RNG0_SIZE, + MT_DEVICE | MT_RW | MT_SECURE), + MAP_REGION_FLAT(DDR_SHARE_MEM, DDR_SHARE_SIZE, + MT_DEVICE | MT_RW | MT_NS), + { 0 } +}; + +/* The RockChip power domain tree descriptor */ +const unsigned char rockchip_power_domain_tree_desc[] = { + /* No of root nodes */ + PLATFORM_SYSTEM_COUNT, + /* No of children for the root node */ + PLATFORM_CLUSTER_COUNT, + /* No of children for the first cluster node */ + PLATFORM_CLUSTER0_CORE_COUNT, + /* No of children for the second cluster node */ + PLATFORM_CLUSTER1_CORE_COUNT +}; + +void timer_hp_init(void) +{ + if ((mmio_read_32(TIMER_HP_BASE + TIMER_HP_CTRL) & 0x1) != 0) + return; + + mmio_write_32(TIMER_HP_BASE + TIMER_HP_CTRL, 0x0); + dsb(); + mmio_write_32(TIMER_HP_BASE + TIMER_HP_LOAD_COUNT0, 0xffffffff); + mmio_write_32(TIMER_HP_BASE + TIMER_HP_LOAD_COUNT1, 0xffffffff); + mmio_write_32(TIMER_HP_BASE + TIMER_HP_INT_EN, 0); + dsb(); + mmio_write_32(TIMER_HP_BASE + TIMER_HP_CTRL, 0x1); +} + +static void system_reset_init(void) +{ + /* enable wdt_ns0~4 trigger global reset and select first reset. + * enable tsadc trigger global reset and select first reset. + * enable global reset and wdt trigger pmu reset. + * select first reset trigger pmu reset.s + */ + mmio_write_32(CRU_BASE + CRU_GLB_RST_CON, 0xffdf); + + /* enable wdt_s, wdt_ns reset */ + mmio_write_32(BUSSGRF_BASE + SGRF_SOC_CON(2), 0x0c000c00); + + /* reset width = 0xffff */ + mmio_write_32(PMU1GRF_BASE + PMU1GRF_SOC_CON(1), 0xffffffff); + + /* enable first/tsadc/wdt reset output */ + mmio_write_32(PMU1SGRF_BASE + PMU1SGRF_SOC_CON(0), 0x00070007); + + /* pmu1_grf pmu1_ioc hold */ + mmio_write_32(PMU1GRF_BASE + PMU1GRF_SOC_CON(7), 0x30003000); + + /* pmu1sgrf hold */ + mmio_write_32(PMU1SGRF_BASE + PMU1SGRF_SOC_CON(14), 0x00200020); + + /* select tsadc_shut_m0 ionmux*/ + mmio_write_32(PMU0IOC_BASE + 0x0, 0x00f00020); +} + +void plat_rockchip_soc_init(void) +{ + rockchip_clock_init(); + secure_timer_init(); + timer_hp_init(); + system_reset_init(); + sgrf_init(); + rockchip_init_scmi_server(); +} diff --git a/plat/rockchip/rk3588/drivers/soc/soc.h b/plat/rockchip/rk3588/drivers/soc/soc.h new file mode 100644 index 00000000..9af179a4 --- /dev/null +++ b/plat/rockchip/rk3588/drivers/soc/soc.h @@ -0,0 +1,198 @@ +/* + * Copyright (c) 2024, Rockchip, Inc. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef __SOC_H__ +#define __SOC_H__ + +enum pll_id { + APLL_ID, + DPLL_ID, + GPLL_ID, + CPLL_ID, + NPLL_ID, + VPLL_ID, +}; + +enum pmu_pll_id { + PPLL_ID = 0, + HPLL_ID +}; + +enum cru_mode_con00 { + CLK_APLL, + CLK_DPLL, + CLK_CPLL, + CLK_GPLL, + CLK_REVSERVED, + CLK_NPLL, + CLK_VPLL, + CLK_USBPLL, +}; + +#define KHz 1000 +#define MHz (1000 * KHz) +#define OSC_HZ (24 * MHz) + +/* CRU */ +#define GLB_SRST_FST_CFG_VAL 0xfdb9 + +#define CRU_PLLS_CON(pll_id, i) (0x160 + (pll_id) * 0x20 + (i) * 0x4) +#define CRU_PLL_CON(i) ((i) * 0x4) +#define CRU_MODE_CON0 0x280 +#define CRU_CLKSEL_CON(i) ((i) * 0x4 + 0x300) +#define CRU_CLKGATE_CON(i) ((i) * 0x4 + 0x800) +#define CRU_CLKGATE_CON_CNT 78 +#define CRU_SOFTRST_CON(i) ((i) * 0x4 + 0xa00) +#define CRU_GLB_CNT_TH 0xc00 +#define CRU_GLB_SRST_FST 0xc08 +#define CRU_GLB_SRST_SND 0xc0c +#define CRU_GLB_RST_CON 0xc10 +#define CRU_GLB_RST_ST 0xc04 +#define CRU_SDIO_CON0 0xc24 +#define CRU_SDIO_CON1 0xc28 +#define CRU_SDMMC_CON0 0xc30 +#define CRU_SDMMC_CON1 0xc34 +#define CRU_AUTOCS_CON0(id) (0xd00 + (id) * 8) +#define CRU_AUTOCS_CON1(id) (0xd04 + (id) * 8) + +#define CRU_AUTOCS_ID_CNT 74 + +#define CRU_PLLCON0_M_MASK 0x3ff +#define CRU_PLLCON0_M_SHIFT 0 +#define CRU_PLLCON1_P_MASK 0x3f +#define CRU_PLLCON1_P_SHIFT 0 +#define CRU_PLLCON1_S_MASK 0x7 +#define CRU_PLLCON1_S_SHIFT 6 +#define CRU_PLLCON2_K_MASK 0xffff +#define CRU_PLLCON2_K_SHIFT 0 +#define CRU_PLLCON1_PWRDOWN BIT(13) +#define CRU_PLLCON6_LOCK_STATUS BIT(15) + +#define CRU_BIGCPU02_RST_MSK 0x30 +#define CRU_BIGCPU13_RST_MSK 0x300 + +#define PHPCRU_CLKGATE_CON 0x800 +#define PHPCRU_CLKGATE_CON_CNT 1 + +#define SECURECRU_CLKGATE_CON(i) ((i) * 0x4 + 0x800) +#define SECURECRU_CLKGATE_CON_CNT 4 + +#define PMU1CRU_CLKGATE_CON_CNT 6 + +/* CENTER GRF */ +#define CENTER_GRF_CON(i) ((i) * 4) + +/* PMU1GRF */ +#define PMU1GRF_SOC_CON(n) ((n) * 4) +#define PMU1GRF_SOC_ST 0x60 +#define PMU1GRF_OS_REG(n) (0x200 + ((n) * 4)) + +#define PMU_MCU_HALT BIT(7) +#define PMU_MCU_SLEEP BIT(9) +#define PMU_MCU_DEEPSLEEP BIT(10) +#define PMU_MCU_STOP_MSK \ + (PMU_MCU_HALT | PMU_MCU_SLEEP | PMU_MCU_DEEPSLEEP) + +/* SYSGRF */ +#define SYS_GRF_NOC_CON(n) (0x100 + (n) * 4) +#define SYS_GRF_SOC_CON(n) (0x300 + (n) * 4) +#define SYS_GRF_SOC_STATUS(n) (0x380 + (n) * 4) + +#define SYS_GRF_LITTLE_CPUS_WFE 0xf +#define SYS_GRF_CORE0_CPUS_WFE 0x30 +#define SYS_GRF_CORE1_CPUS_WFE 0xc0 +#define SYS_GRF_BIG_CPUS_WFE 0xf0 +#define SYS_GRF_LITTLE_CPUS_WFI 0xf00 +#define SYS_GRF_CORE0_CPUS_WFI 0x3000 +#define SYS_GRF_CORE1_CPUS_WFI 0xc000 + +/* pvtm */ +#define PVTM_CON(i) (0x4 + (i) * 4) +#define PVTM_INTEN 0x70 +#define PVTM_INTSTS 0x74 +#define PVTM_STATUS(i) (0x80 + (i) * 4) +#define PVTM_CALC_CNT 0x200 + +enum pvtm_con0 { + pvtm_start = 0, + pvtm_osc_en = 1, + pvtm_osc_sel = 2, + pvtm_rnd_seed_en = 5, +}; + +/* timer */ +#define TIMER_LOAD_COUNT0 0x00 +#define TIMER_LOAD_COUNT1 0x04 +#define TIMER_CURRENT_VALUE0 0x08 +#define TIMER_CURRENT_VALUE1 0x0c +#define TIMER_CONTROL_REG 0x10 +#define TIMER_INTSTATUS 0x18 + +#define TIMER_DIS 0x0 +#define TIMER_EN 0x1 + +#define TIMER_FMODE (0x0 << 1) +#define TIMER_RMODE (0x1 << 1) + +#define STIMER0_CHN_BASE(n) (STIMER0_BASE + 0x20 * (n)) +#define STIMER1_CHN_BASE(n) (STIMER1_BASE + 0x20 * (n)) + +/* cpu timer */ +#define TIMER_HP_REVISION 0x0 +#define TIMER_HP_CTRL 0x4 +#define TIMER_HP_INT_EN 0x8 +#define TIMER_HP_T24_GCD 0xc +#define TIMER_HP_T32_GCD 0x10 +#define TIMER_HP_LOAD_COUNT0 0x14 +#define TIMER_HP_LOAD_COUNT1 0x18 +#define TIMER_HP_T24_DELAT_COUNT0 0x1c +#define TIMER_HP_T24_DELAT_COUNT1 0x20 +#define TIMER_HP_CURR_32K_VALUE0 0x24 +#define TIMER_HP_CURR_32K_VALUE1 0x28 +#define TIMER_HP_CURR_TIMER_VALUE0 0x2c +#define TIMER_HP_CURR_TIMER_VALUE1 0x30 +#define TIMER_HP_T24_32BEGIN0 0x34 +#define TIMER_HP_T24_32BEGIN1 0x38 +#define TIMER_HP_T32_24END0 0x3c +#define TIMER_HP_T32_24END1 0x40 +#define TIMER_HP_BEGIN_END_VALID 0x44 +#define TIMER_HP_SYNC_REQ 0x48 +#define TIMER_HP_INTR_STATUS 0x4c + + /* GPIO */ +#define GPIO_SWPORT_DR_L 0x0000 +#define GPIO_SWPORT_DR_H 0x0004 +#define GPIO_SWPORT_DDR_L 0x0008 +#define GPIO_SWPORT_DDR_H 0x000c +#define GPIO_INT_EN_L 0x0010 +#define GPIO_INT_EN_H 0x0014 +#define GPIO_INT_MASK_L 0x0018 +#define GPIO_INT_MASK_H 0x001c +#define GPIO_INT_TYPE_L 0x0020 +#define GPIO_INT_TYPE_H 0x0024 +#define GPIO_INT_POLARITY_L 0x0028 +#define GPIO_INT_POLARITY_H 0x002c +#define GPIO_INT_BOTHEDGE_L 0x0030 +#define GPIO_INT_BOTHEDGE_H 0x0034 +#define GPIO_DEBOUNCE_L 0x0038 +#define GPIO_DEBOUNCE_H 0x003c +#define GPIO_DBCLK_DIV_EN_L 0x0040 +#define GPIO_DBCLK_DIV_EN_H 0x0044 +#define GPIO_DBCLK_DIV_CON 0x0048 +#define GPIO_INT_STATUS 0x0050 +#define GPIO_INT_RAWSTATUS 0x0058 +#define GPIO_PORT_EOI_L 0x0060 +#define GPIO_PORT_EOI_H 0x0064 +#define GPIO_EXT_PORT 0x0070 +#define GPIO_VER_ID 0x0078 + +/* DDRGRF */ +#define DDRGRF_CHA_CON(i) ((i) * 4) +#define DDRGRF_CHB_CON(i) (0x30 + (i) * 4) + +#define DDR_CHN_CNT 4 + +#endif /* __SOC_H__ */ diff --git a/plat/rockchip/rk3588/include/plat.ld.S b/plat/rockchip/rk3588/include/plat.ld.S new file mode 100644 index 00000000..e3ea9ccf --- /dev/null +++ b/plat/rockchip/rk3588/include/plat.ld.S @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2024, Rockchip, Inc. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef ROCKCHIP_PLAT_LD_S +#define ROCKCHIP_PLAT_LD_S + +MEMORY { + PMUSRAM (rwx): ORIGIN = PMUSRAM_BASE, LENGTH = PMUSRAM_RSIZE +} + +SECTIONS +{ + . = PMUSRAM_BASE; + + /* + * pmu_cpuson_entrypoint request address + * align 64K when resume, so put it in the + * start of pmusram + */ + .text_pmusram : { + ASSERT(. == ALIGN(64 * 1024), + ".pmusram.entry request 64K aligned."); + KEEP(*(.pmusram.entry)) + __bl31_pmusram_text_start = .; + *(.pmusram.text) + *(.pmusram.rodata) + . = ALIGN(PAGE_SIZE); + __bl31_pmusram_text_end = .; + __bl31_pmusram_data_start = .; + *(.pmusram.data) + . = ALIGN(PAGE_SIZE); + __bl31_pmusram_data_end = .; + + ASSERT(__bl31_pmusram_data_end <= PMUSRAM_BASE + PMUSRAM_RSIZE, + ".pmusram has exceeded its limit."); + } >PMUSRAM +} + +#endif /* ROCKCHIP_PLAT_LD_S */ diff --git a/plat/rockchip/rk3588/include/plat_sip_calls.h b/plat/rockchip/rk3588/include/plat_sip_calls.h new file mode 100644 index 00000000..bc4455f9 --- /dev/null +++ b/plat/rockchip/rk3588/include/plat_sip_calls.h @@ -0,0 +1,12 @@ +/* + * Copyright (c) 2024, Rockchip, Inc. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef __PLAT_SIP_CALLS_H__ +#define __PLAT_SIP_CALLS_H__ + +#define RK_PLAT_SIP_NUM_CALLS 0 + +#endif /* __PLAT_SIP_CALLS_H__ */ diff --git a/plat/rockchip/rk3588/include/platform_def.h b/plat/rockchip/rk3588/include/platform_def.h new file mode 100644 index 00000000..5946af0b --- /dev/null +++ b/plat/rockchip/rk3588/include/platform_def.h @@ -0,0 +1,119 @@ +/* + * Copyright (c) 2024, Rockchip, Inc. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef __PLATFORM_DEF_H__ +#define __PLATFORM_DEF_H__ + +#include <arch.h> +#include <plat/common/common_def.h> + +#include <rk3588_def.h> + +#define DEBUG_XLAT_TABLE 0 + +/******************************************************************************* + * Platform binary types for linking + ******************************************************************************/ +#define PLATFORM_LINKER_FORMAT "elf64-littleaarch64" +#define PLATFORM_LINKER_ARCH aarch64 + +/******************************************************************************* + * Generic platform constants + ******************************************************************************/ + +/* Size of cacheable stacks */ +#if DEBUG_XLAT_TABLE +#define PLATFORM_STACK_SIZE 0x800 +#elif IMAGE_BL1 +#define PLATFORM_STACK_SIZE 0x440 +#elif IMAGE_BL2 +#define PLATFORM_STACK_SIZE 0x400 +#elif IMAGE_BL31 +#define PLATFORM_STACK_SIZE 0x800 +#elif IMAGE_BL32 +#define PLATFORM_STACK_SIZE 0x440 +#endif + +#define FIRMWARE_WELCOME_STR "Booting Trusted Firmware\n" + +#define PLATFORM_SYSTEM_COUNT 1 +#define PLATFORM_CLUSTER_COUNT 1 +#define PLATFORM_CLUSTER0_CORE_COUNT 8 +#define PLATFORM_CLUSTER1_CORE_COUNT 0 +#define PLATFORM_CORE_COUNT (PLATFORM_CLUSTER1_CORE_COUNT + \ + PLATFORM_CLUSTER0_CORE_COUNT) + +#define PLATFORM_NUM_AFFS (PLATFORM_SYSTEM_COUNT + \ + PLATFORM_CLUSTER_COUNT + \ + PLATFORM_CORE_COUNT) + +#define PLAT_MAX_PWR_LVL MPIDR_AFFLVL2 + +#define PLAT_RK_CLST_TO_CPUID_SHIFT 8 + +/* + * This macro defines the deepest retention state possible. A higher state + * id will represent an invalid or a power down state. + */ +#define PLAT_MAX_RET_STATE 1 + +/* + * This macro defines the deepest power down states possible. Any state ID + * higher than this is invalid. + */ +#define PLAT_MAX_OFF_STATE 2 +/******************************************************************************* + * Platform memory map related constants + ******************************************************************************/ +/* TF txet, ro, rw, Size: 512KB */ +#define TZRAM_BASE (0x0) +#define TZRAM_SIZE (0x100000) + +/******************************************************************************* + * BL31 specific defines. + ******************************************************************************/ +/* + * Put BL3-1 at the top of the Trusted RAM + */ +#define BL31_BASE (TZRAM_BASE + 0x40000) +#define BL31_LIMIT (TZRAM_BASE + TZRAM_SIZE) + +/******************************************************************************* + * Platform specific page table and MMU setup constants + ******************************************************************************/ +#define PLAT_PHY_ADDR_SPACE_SIZE (1ULL << 32) +#define PLAT_VIRT_ADDR_SPACE_SIZE (1ULL << 32) + +#define ADDR_SPACE_SIZE (1ULL << 32) +#define MAX_XLAT_TABLES 18 +#define MAX_MMAP_REGIONS 27 + +/******************************************************************************* + * Declarations and constants to access the mailboxes safely. Each mailbox is + * aligned on the biggest cache line size in the platform. This is known only + * to the platform as it might have a combination of integrated and external + * caches. Such alignment ensures that two maiboxes do not sit on the same cache + * line at any cache level. They could belong to different cpus/clusters & + * get written while being protected by different locks causing corruption of + * a valid mailbox address. + ******************************************************************************/ +#define CACHE_WRITEBACK_SHIFT 6 +#define CACHE_WRITEBACK_GRANULE (1 << CACHE_WRITEBACK_SHIFT) + +/* + * Define GICD and GICC and GICR base + */ +#define PLAT_RK_GICD_BASE PLAT_GICD_BASE +#define PLAT_RK_GICC_BASE PLAT_GICC_BASE +#define PLAT_RK_GICR_BASE PLAT_GICR_BASE + +#define PLAT_RK_UART_BASE RK_DBG_UART_BASE +#define PLAT_RK_UART_CLOCK RK_DBG_UART_CLOCK +#define PLAT_RK_UART_BAUDRATE RK_DBG_UART_BAUDRATE + +#define PLAT_RK_PRIMARY_CPU 0x0 + +#endif /* __PLATFORM_DEF_H__ */ diff --git a/plat/rockchip/rk3588/plat_sip_calls.c b/plat/rockchip/rk3588/plat_sip_calls.c new file mode 100644 index 00000000..496e8d75 --- /dev/null +++ b/plat/rockchip/rk3588/plat_sip_calls.c @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2024, Rockchip, Inc. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <common/debug.h> +#include <common/runtime_svc.h> +#include <drivers/scmi-msg.h> + +#include <plat_sip_calls.h> +#include <rockchip_sip_svc.h> + +uintptr_t rockchip_plat_sip_handler(uint32_t smc_fid, + u_register_t x1, + u_register_t x2, + u_register_t x3, + u_register_t x4, + void *cookie, + void *handle, + u_register_t flags) +{ + switch (smc_fid) { + case RK_SIP_SCMI_AGENT0: + scmi_smt_fastcall_smc_entry(0); + SMC_RET1(handle, 0); + + default: + ERROR("%s: unhandled SMC (0x%x)\n", __func__, smc_fid); + SMC_RET1(handle, SMC_UNK); + } +} diff --git a/plat/rockchip/rk3588/platform.mk b/plat/rockchip/rk3588/platform.mk new file mode 100644 index 00000000..2fadb5a5 --- /dev/null +++ b/plat/rockchip/rk3588/platform.mk @@ -0,0 +1,98 @@ +# +# Copyright (c) 2024, Rockchip, Inc. All rights reserved. +# +# SPDX-License-Identifier: BSD-3-Clause +# + +RK_PLAT := plat/rockchip +RK_PLAT_SOC := ${RK_PLAT}/${PLAT} +RK_PLAT_COMMON := ${RK_PLAT}/common + +DISABLE_BIN_GENERATION := 1 +include lib/libfdt/libfdt.mk +include lib/xlat_tables_v2/xlat_tables.mk + +# GIC-600 configuration +GICV3_IMPL := GIC600 +GICV3_SUPPORT_GIC600 := 1 + +# Include GICv3 driver files +include drivers/arm/gic/v3/gicv3.mk + +PLAT_INCLUDES := -Iinclude/plat/common \ + -Idrivers/arm/gic/v3/ \ + -Idrivers/scmi-msg/ \ + -I${RK_PLAT_COMMON}/ \ + -I${RK_PLAT_COMMON}/drivers/pmu/ \ + -I${RK_PLAT_COMMON}/drivers/parameter/ \ + -I${RK_PLAT_COMMON}/include/ \ + -I${RK_PLAT_COMMON}/pmusram/ \ + -I${RK_PLAT_COMMON}/scmi/ \ + -I${RK_PLAT_SOC}/ \ + -I${RK_PLAT_SOC}/drivers/pmu/ \ + -I${RK_PLAT_SOC}/drivers/scmi/ \ + -I${RK_PLAT_SOC}/drivers/secure/ \ + -I${RK_PLAT_SOC}/drivers/soc/ \ + -I${RK_PLAT_SOC}/include/ + +RK_GIC_SOURCES := ${GICV3_SOURCES} \ + plat/common/plat_gicv3.c \ + ${RK_PLAT}/common/rockchip_gicv3.c + +PLAT_BL_COMMON_SOURCES := ${XLAT_TABLES_LIB_SRCS} \ + common/desc_image_load.c \ + plat/common/aarch64/crash_console_helpers.S \ + lib/bl_aux_params/bl_aux_params.c \ + plat/common/plat_psci_common.c + +ifneq (${ENABLE_STACK_PROTECTOR},0) +PLAT_BL_COMMON_SOURCES += ${RK_PLAT_COMMON}/rockchip_stack_protector.c +endif + +BL31_SOURCES += ${RK_GIC_SOURCES} \ + drivers/ti/uart/aarch64/16550_console.S \ + drivers/delay_timer/delay_timer.c \ + drivers/delay_timer/generic_delay_timer.c \ + drivers/scmi-msg/base.c \ + drivers/scmi-msg/clock.c \ + drivers/scmi-msg/entry.c \ + drivers/scmi-msg/reset_domain.c \ + drivers/scmi-msg/smt.c \ + lib/cpus/aarch64/cortex_a55.S \ + lib/cpus/aarch64/cortex_a76.S \ + ${RK_PLAT_COMMON}/aarch64/plat_helpers.S \ + ${RK_PLAT_COMMON}/aarch64/platform_common.c \ + ${RK_PLAT_COMMON}/bl31_plat_setup.c \ + ${RK_PLAT_COMMON}/plat_pm.c \ + ${RK_PLAT_COMMON}/plat_pm_helpers.c \ + ${RK_PLAT_COMMON}/plat_topology.c \ + ${RK_PLAT_COMMON}/params_setup.c \ + ${RK_PLAT_COMMON}/pmusram/cpus_on_fixed_addr.S \ + ${RK_PLAT_COMMON}/rockchip_sip_svc.c \ + ${RK_PLAT_COMMON}/scmi/scmi.c \ + ${RK_PLAT_COMMON}/scmi/scmi_clock.c \ + ${RK_PLAT_COMMON}/scmi/scmi_rstd.c \ + ${RK_PLAT_SOC}/plat_sip_calls.c \ + ${RK_PLAT_SOC}/drivers/secure/secure.c \ + ${RK_PLAT_SOC}/drivers/soc/soc.c \ + ${RK_PLAT_SOC}/drivers/pmu/pmu.c \ + ${RK_PLAT_SOC}/drivers/pmu/pm_pd_regs.c \ + ${RK_PLAT_SOC}/drivers/scmi/rk3588_clk.c \ + ${RK_PLAT_SOC}/drivers/scmi/rk3588_rstd.c + +CTX_INCLUDE_AARCH32_REGS := 0 +ENABLE_PLAT_COMPAT := 0 +MULTI_CONSOLE_API := 1 +ERRATA_A55_1530923 := 1 + +# System coherency is managed in hardware +HW_ASSISTED_COHERENCY := 1 + +# When building for systems with hardware-assisted coherency, there's no need to +# use USE_COHERENT_MEM. Require that USE_COHERENT_MEM must be set to 0 too. +USE_COHERENT_MEM := 0 + +ENABLE_SPE_FOR_LOWER_ELS := 0 + +$(eval $(call add_define,PLAT_EXTRA_LD_SCRIPT)) +$(eval $(call add_define,PLAT_RK_CPU_RESET_EARLY)) diff --git a/plat/rockchip/rk3588/rk3588_def.h b/plat/rockchip/rk3588/rk3588_def.h new file mode 100644 index 00000000..412495a8 --- /dev/null +++ b/plat/rockchip/rk3588/rk3588_def.h @@ -0,0 +1,224 @@ +/* + * Copyright (c) 2024, Rockchip, Inc. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef __PLAT_DEF_H__ +#define __PLAT_DEF_H__ + +#define SIZE_K(n) ((n) * 1024) + +#define WITH_16BITS_WMSK(bits) (0xffff0000 | (bits)) + +/* Special value used to verify platform parameters from BL2 to BL3-1 */ +#define RK_BL31_PLAT_PARAM_VAL 0x0f1e2d3c4b5a6978ULL + +#define UMCTL0_BASE 0xf7000000 +#define UMCTL1_BASE 0xf8000000 +#define UMCTL2_BASE 0xf9000000 +#define UMCTL3_BASE 0xfa000000 + +#define GIC600_BASE 0xfe600000 +#define GIC600_SIZE SIZE_K(64) + +#define DAPLITE_BASE 0xfd100000 +#define PMU0SGRF_BASE 0xfd580000 +#define PMU1SGRF_BASE 0xfd582000 +#define BUSSGRF_BASE 0xfd586000 +#define DSUSGRF_BASE 0xfd587000 +#define PMU0GRF_BASE 0xfd588000 +#define PMU1GRF_BASE 0xfd58a000 + +#define SYSGRF_BASE 0xfd58c000 +#define BIGCORE0GRF_BASE 0xfd590000 +#define BIGCORE1GRF_BASE 0xfd592000 +#define LITCOREGRF_BASE 0xfd594000 +#define DSUGRF_BASE 0xfd598000 +#define DDR01GRF_BASE 0xfd59c000 +#define DDR23GRF_BASE 0xfd59d000 +#define CENTERGRF_BASE 0xfd59e000 +#define GPUGRF_BASE 0xfd5a0000 +#define NPUGRF_BASE 0xfd5a2000 +#define USBGRF_BASE 0xfd5ac000 +#define PHPGRF_BASE 0xfd5b0000 +#define PCIE3PHYGRF_BASE 0xfd5b8000 +#define USB2PHY0_GRF_BASE 0xfd5d0000 +#define USB2PHY1_GRF_BASE 0xfd5d4000 +#define USB2PHY2_GRF_BASE 0xfd5d8000 +#define USB2PHY3_GRF_BASE 0xfd5dc000 + +#define PMU0IOC_BASE 0xfd5f0000 +#define PMU1IOC_BASE 0xfd5f4000 +#define BUSIOC_BASE 0xfd5f8000 +#define VCCIO1_4_IOC_BASE 0xfd5f9000 +#define VCCIO3_5_IOC_BASE 0xfd5fa000 +#define VCCIO2_IOC_BASE 0xfd5fb000 +#define VCCIO6_IOC_BASE 0xfd5fc000 + +#define SRAM_BASE 0xff000000 +#define PMUSRAM_BASE 0xff100000 +#define PMUSRAM_SIZE SIZE_K(128) +#define PMUSRAM_RSIZE SIZE_K(64) + +#define CRU_BASE 0xfd7c0000 +#define PHP_CRU_BASE 0xfd7c8000 +#define SCRU_BASE 0xfd7d0000 +#define BUSSCRU_BASE 0xfd7d8000 +#define PMU1SCRU_BASE 0xfd7e0000 +#define PMU1CRU_BASE 0xfd7f0000 + +#define DDR0CRU_BASE 0xfd800000 +#define DDR1CRU_BASE 0xfd804000 +#define DDR2CRU_BASE 0xfd808000 +#define DDR3CRU_BASE 0xfd80c000 + +#define BIGCORE0CRU_BASE 0xfd810000 +#define BIGCORE1CRU_BASE 0xfd812000 +#define LITCRU_BASE 0xfd814000 +#define DSUCRU_BASE 0xfd818000 + +#define I2C0_BASE 0xfd880000 +#define UART0_BASE 0xfd890000 +#define GPIO0_BASE 0xfd8a0000 +#define PWM0_BASE 0xfd8b0000 +#define PMUPVTM_BASE 0xfd8c0000 +#define TIMER_HP_BASE 0xfd8c8000 +#define PMU0_BASE 0xfd8d0000 +#define PMU1_BASE 0xfd8d4000 +#define PMU2_BASE 0xfd8d8000 +#define PMU_BASE PMU0_BASE +#define PMUWDT_BASE 0xfd8e0000 +#define PMUTIMER_BASE 0xfd8f0000 +#define OSC_CHK_BASE 0xfd9b0000 +#define VOP_BASE 0xfdd90000 +#define HDMIRX_BASE 0xfdee0000 + +#define MSCH0_BASE 0xfe000000 +#define MSCH1_BASE 0xfe002000 +#define MSCH2_BASE 0xfe004000 +#define MSCH3_BASE 0xfe006000 +#define FIREWALL_DSU_BASE 0xfe010000 +#define FIREWALL_DDR_BASE 0xfe030000 +#define FIREWALL_SYSMEM_BASE 0xfe038000 +#define DDRPHY0_BASE 0xfe0c0000 +#define DDRPHY1_BASE 0xfe0d0000 +#define DDRPHY2_BASE 0xfe0e0000 +#define DDRPHY3_BASE 0xfe0f0000 +#define TIMER_DDR_BASE 0xfe118000 +#define KEYLADDER_BASE 0xfe380000 +#define CRYPTO_S_BASE 0xfe390000 +#define OTP_S_BASE 0xfe3a0000 +#define DCF_BASE 0xfe3c0000 +#define STIMER0_BASE 0xfe3d0000 +#define WDT_S_BASE 0xfe3e0000 +#define CRYPTO_S_BY_KEYLAD_BASE 0xfe420000 +#define NSTIMER0_BASE 0xfeae0000 +#define NSTIMER1_BASE 0xfeae8000 +#define WDT_NS_BASE 0xfeaf0000 + +#define UART1_BASE 0xfeb40000 +#define UART2_BASE 0xfeb50000 +#define UART3_BASE 0xfeb60000 +#define UART4_BASE 0xfeb70000 +#define UART5_BASE 0xfeb80000 +#define UART6_BASE 0xfeb90000 +#define UART7_BASE 0xfeba0000 +#define UART8_BASE 0xfebb0000 +#define UART9_BASE 0xfebc0000 + +#define GPIO1_BASE 0xfec20000 +#define GPIO2_BASE 0xfec30000 +#define GPIO3_BASE 0xfec40000 +#define GPIO4_BASE 0xfec50000 + +#define MAILBOX1_BASE 0xfec70000 +#define OTP_NS_BASE 0xfecc0000 +#define INTMUX0_DDR_BASE 0Xfecf8000 +#define INTMUX1_DDR_BASE 0Xfecfc000 +#define STIMER1_BASE 0xfed30000 + +/************************************************************************** + * sys sram allocation + **************************************************************************/ +#define SRAM_ENTRY_BASE SRAM_BASE +#define SRAM_PMUM0_SHMEM_BASE (SRAM_ENTRY_BASE + SIZE_K(3)) +#define SRAM_LD_BASE (SRAM_ENTRY_BASE + SIZE_K(4)) +#define SRAM_LD_SIZE SIZE_K(64) + +#define SRAM_LD_SP (SRAM_LD_BASE + SRAM_LD_SIZE -\ + 128) + +/************************************************************************** + * share mem region allocation: 1M~2M + **************************************************************************/ +#define DDR_SHARE_MEM SIZE_K(1024) +#define DDR_SHARE_SIZE SIZE_K(64) + +#define SHARE_MEM_BASE DDR_SHARE_MEM +#define SHARE_MEM_PAGE_NUM 15 +#define SHARE_MEM_SIZE SIZE_K(SHARE_MEM_PAGE_NUM * 4) + +#define SCMI_SHARE_MEM_BASE (SHARE_MEM_BASE + SHARE_MEM_SIZE) +#define SCMI_SHARE_MEM_SIZE SIZE_K(4) + +#define SMT_BUFFER_BASE SCMI_SHARE_MEM_BASE +#define SMT_BUFFER0_BASE SMT_BUFFER_BASE + +/************************************************************************** + * UART related constants + **************************************************************************/ +#define RK_DBG_UART_BASE UART2_BASE +#define RK_DBG_UART_BAUDRATE 1500000 +#define RK_DBG_UART_CLOCK 24000000 + +/****************************************************************************** + * System counter frequency related constants + ******************************************************************************/ +#define SYS_COUNTER_FREQ_IN_TICKS 24000000 +#define SYS_COUNTER_FREQ_IN_MHZ 24 + +/****************************************************************************** + * GIC-600 & interrupt handling related constants + ******************************************************************************/ + +/* Base rk_platform compatible GIC memory map */ +#define PLAT_GICD_BASE GIC600_BASE +#define PLAT_GICC_BASE 0 +#define PLAT_GICR_BASE (GIC600_BASE + 0x80000) +#define PLAT_GICITS0_BASE 0xfe640000 +#define PLAT_GICITS1_BASE 0xfe660000 + +/****************************************************************************** + * sgi, ppi + ******************************************************************************/ +#define RK_IRQ_SEC_SGI_0 8 +#define RK_IRQ_SEC_SGI_1 9 +#define RK_IRQ_SEC_SGI_2 10 +#define RK_IRQ_SEC_SGI_3 11 +#define RK_IRQ_SEC_SGI_4 12 +#define RK_IRQ_SEC_SGI_5 13 +#define RK_IRQ_SEC_SGI_6 14 +#define RK_IRQ_SEC_SGI_7 15 +#define RK_IRQ_SEC_PHY_TIMER 29 + +/* + * Define a list of Group 1 Secure and Group 0 interrupts as per GICv3 + * terminology. On a GICv2 system or mode, the lists will be merged and treated + * as Group 0 interrupts. + */ + +#define PLAT_RK_GICV3_G1S_IRQS \ + INTR_PROP_DESC(RK_IRQ_SEC_PHY_TIMER, GIC_HIGHEST_SEC_PRIORITY, \ + INTR_GROUP1S, GIC_INTR_CFG_LEVEL) + +#define PLAT_RK_GICV3_G0_IRQS \ + INTR_PROP_DESC(RK_IRQ_SEC_SGI_6, GIC_HIGHEST_SEC_PRIORITY, \ + INTR_GROUP0, GIC_INTR_CFG_LEVEL) + +/****************************************************************************** + * pm reg region memory + ******************************************************************************/ +#define ROCKCHIP_PM_REG_REGION_MEM_SIZE SIZE_K(4) + +#endif /* __PLAT_DEF_H__ */ diff --git a/plat/rpi/rpi4/aarch64/armstub8_header.S b/plat/rpi/common/aarch64/armstub8_header.S similarity index 89% rename from plat/rpi/rpi4/aarch64/armstub8_header.S rename to plat/rpi/common/aarch64/armstub8_header.S index 246358d0..dc1e54e1 100644 --- a/plat/rpi/rpi4/aarch64/armstub8_header.S +++ b/plat/rpi/common/aarch64/armstub8_header.S @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2019-2024, Arm Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ diff --git a/plat/rpi/common/aarch64/plat_helpers.S b/plat/rpi/common/aarch64/plat_helpers.S index f045e211..18873af7 100644 --- a/plat/rpi/common/aarch64/plat_helpers.S +++ b/plat/rpi/common/aarch64/plat_helpers.S @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015-2020, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2015-2024, Arm Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -27,10 +27,19 @@ * * This function uses the plat_rpi3_calc_core_pos() * definition to get the index of the calling CPU. + * + * When MT is set, lowest affinity represents the thread ID. + * Since we only support one thread per core, discard this field + * so cluster and core IDs go back into Aff1 and Aff0 respectively. + * The upper bits are also affected, but plat_rpi3_calc_core_pos() + * does not use them. * ----------------------------------------------------- */ func plat_my_core_pos mrs x0, mpidr_el1 + tst x0, #MPIDR_MT_MASK + lsr x1, x0, #MPIDR_AFFINITY_BITS + csel x0, x1, x0, ne b plat_rpi3_calc_core_pos endfunc plat_my_core_pos @@ -164,10 +173,16 @@ endfunc platform_mem_init * --------------------------------------------- */ func plat_crash_console_init - mov_imm x0, PLAT_RPI_MINI_UART_BASE + mov_imm x0, PLAT_RPI_CRASH_UART_BASE +#if PLAT_RPI_CRASH_UART_BASE == PLAT_RPI_PL011_UART_BASE + mov_imm x1, RPI4_PL011_UART_CLOCK + mov_imm x2, PLAT_RPI_UART_BAUDRATE + b console_pl011_core_init +#else mov x1, xzr mov x2, xzr b console_16550_core_init +#endif endfunc plat_crash_console_init /* --------------------------------------------- @@ -178,8 +193,12 @@ endfunc plat_crash_console_init * --------------------------------------------- */ func plat_crash_console_putc - mov_imm x1, PLAT_RPI_MINI_UART_BASE + mov_imm x1, PLAT_RPI_CRASH_UART_BASE +#if PLAT_RPI_CRASH_UART_BASE == PLAT_RPI_PL011_UART_BASE + b console_pl011_core_putc +#else b console_16550_core_putc +#endif endfunc plat_crash_console_putc /* --------------------------------------------- @@ -191,8 +210,12 @@ endfunc plat_crash_console_putc * --------------------------------------------- */ func plat_crash_console_flush - mov_imm x0, PLAT_RPI_MINI_UART_BASE + mov_imm x0, PLAT_RPI_CRASH_UART_BASE +#if PLAT_RPI_CRASH_UART_BASE == PLAT_RPI_PL011_UART_BASE + b console_pl011_core_flush +#else b console_16550_core_flush +#endif endfunc plat_crash_console_flush /* --------------------------------------------- diff --git a/plat/rpi/rpi4/include/plat_macros.S b/plat/rpi/common/include/plat_macros.S similarity index 87% rename from plat/rpi/rpi4/include/plat_macros.S rename to plat/rpi/common/include/plat_macros.S index 6007d031..576d0ffc 100644 --- a/plat/rpi/rpi4/include/plat_macros.S +++ b/plat/rpi/common/include/plat_macros.S @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016-2019, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2016-2024, Arm Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ diff --git a/plat/rpi/common/include/rpi_shared.h b/plat/rpi/common/include/rpi_shared.h index ddf239eb..8562c3d5 100644 --- a/plat/rpi/common/include/rpi_shared.h +++ b/plat/rpi/common/include/rpi_shared.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015-2019, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2015-2024, Arm Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -7,14 +7,20 @@ #ifndef RPI_SHARED_H #define RPI_SHARED_H +#include <stddef.h> #include <stdint.h> +#include <drivers/console.h> + /******************************************************************************* * Function and variable prototypes ******************************************************************************/ -/* Utility functions */ +/* Serial console functions */ void rpi3_console_init(void); +int rpi3_register_used_uart(console_t *console); + +/* Utility functions */ void rpi3_setup_page_tables(uintptr_t total_base, size_t total_size, uintptr_t code_start, uintptr_t code_limit, uintptr_t rodata_start, uintptr_t rodata_limit @@ -23,6 +29,8 @@ void rpi3_setup_page_tables(uintptr_t total_base, size_t total_size, #endif ); +uintptr_t rpi4_get_dtb_address(void); + /* Optional functions required in the Raspberry Pi 3 port */ unsigned int plat_rpi3_calc_core_pos(u_register_t mpidr); @@ -38,4 +46,10 @@ int rpi3_vc_hardware_get_board_revision(uint32_t *revision); int plat_rpi_get_model(void); +/******************************************************************************* + * Platform implemented functions + ******************************************************************************/ + +void plat_rpi_bl31_custom_setup(void); + #endif /* RPI3_PRIVATE_H */ diff --git a/plat/rpi/common/rpi3_common.c b/plat/rpi/common/rpi3_common.c index ef88bf10..89764969 100644 --- a/plat/rpi/common/rpi3_common.c +++ b/plat/rpi/common/rpi3_common.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015-2019, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2015-2024, Arm Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -13,9 +13,6 @@ #include <common/debug.h> #include <bl31/interrupt_mgmt.h> #include <drivers/console.h> -#include <drivers/rpi3/gpio/rpi3_gpio.h> -#include <drivers/ti/uart/uart_16550.h> -#include <drivers/arm/pl011.h> #include <lib/xlat_tables/xlat_tables_v2.h> #include <rpi_hw.h> @@ -106,12 +103,6 @@ static const mmap_region_t plat_rpi3_mmap[] = { ******************************************************************************/ static console_t rpi3_console; - -static bool rpi3_use_mini_uart(void) -{ - return rpi3_gpio_get_select(14) == RPI3_GPIO_FUNC_ALT5; -} - void rpi3_console_init(void) { int console_scope = CONSOLE_FLAG_BOOT; @@ -120,18 +111,7 @@ void rpi3_console_init(void) if (RPI3_RUNTIME_UART != -1) console_scope |= CONSOLE_FLAG_RUNTIME; - rpi3_gpio_init(); - - if (rpi3_use_mini_uart()) - rc = console_16550_register(PLAT_RPI_MINI_UART_BASE, - 0, - PLAT_RPI_UART_BAUDRATE, - &rpi3_console); - else - rc = console_pl011_register(PLAT_RPI_PL011_UART_BASE, - PLAT_RPI_PL011_UART_CLOCK, - PLAT_RPI_UART_BAUDRATE, - &rpi3_console); + rc = rpi3_register_used_uart(&rpi3_console); if (rc == 0) { /* diff --git a/plat/rpi/common/rpi3_console_dual.c b/plat/rpi/common/rpi3_console_dual.c new file mode 100644 index 00000000..15ee3e70 --- /dev/null +++ b/plat/rpi/common/rpi3_console_dual.c @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2015-2024, Arm Limited and Contributors. All rights reserved. + * Copyright (c) 2024, Mario Bălănică <mariobalanica02@gmail.com> + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <drivers/arm/pl011.h> +#include <drivers/console.h> +#include <drivers/rpi3/gpio/rpi3_gpio.h> +#include <drivers/ti/uart/uart_16550.h> +#include <platform_def.h> + +#include <rpi_shared.h> + +static bool rpi3_use_mini_uart(void) +{ + return rpi3_gpio_get_select(14) == RPI3_GPIO_FUNC_ALT5; +} + +int rpi3_register_used_uart(console_t *console) +{ + rpi3_gpio_init(); + + if (rpi3_use_mini_uart()) + return console_16550_register(PLAT_RPI_MINI_UART_BASE, + 0, + PLAT_RPI_UART_BAUDRATE, + console); + else + return console_pl011_register(PLAT_RPI_PL011_UART_BASE, + PLAT_RPI_PL011_UART_CLOCK, + PLAT_RPI_UART_BAUDRATE, + console); +} diff --git a/plat/rpi/common/rpi3_console_pl011.c b/plat/rpi/common/rpi3_console_pl011.c new file mode 100644 index 00000000..6ab72097 --- /dev/null +++ b/plat/rpi/common/rpi3_console_pl011.c @@ -0,0 +1,20 @@ +/* + * Copyright (c) 2015-2024, Arm Limited and Contributors. All rights reserved. + * Copyright (c) 2024, Mario Bălănică <mariobalanica02@gmail.com> + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <drivers/arm/pl011.h> +#include <drivers/console.h> +#include <platform_def.h> + +#include <rpi_shared.h> + +int rpi3_register_used_uart(console_t *console) +{ + return console_pl011_register(PLAT_RPI_PL011_UART_BASE, + PLAT_RPI_PL011_UART_CLOCK, + PLAT_RPI_UART_BAUDRATE, + console); +} diff --git a/plat/rpi/common/rpi3_pm.c b/plat/rpi/common/rpi3_pm.c index d98ac66f..456e1603 100644 --- a/plat/rpi/common/rpi3_pm.c +++ b/plat/rpi/common/rpi3_pm.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015-2018, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2015-2024, Arm Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -21,6 +21,22 @@ #include <drivers/arm/gicv2.h> #endif +/* Registers on top of RPI3_PM_BASE. */ +#define RPI3_PM_RSTC_OFFSET ULL(0x0000001C) +#define RPI3_PM_RSTS_OFFSET ULL(0x00000020) +#define RPI3_PM_WDOG_OFFSET ULL(0x00000024) +/* Watchdog constants */ +#define RPI3_PM_PASSWORD U(0x5A000000) +#define RPI3_PM_RSTC_WRCFG_MASK U(0x00000030) +#define RPI3_PM_RSTC_WRCFG_FULL_RESET U(0x00000020) +/* + * The RSTS register is used by the VideoCore firmware when booting the + * Raspberry Pi to know which partition to boot from. The partition value is + * formed by bits 0, 2, 4, 6, 8 and 10. Partition 63 is used by said firmware + * to indicate halt. + */ +#define RPI3_PM_RSTS_WRCFG_HALT U(0x00000555) + /* Make composite power state parameter till power level 0 */ #if PSCI_EXTENDED_STATE_ID diff --git a/plat/rpi/common/rpi3_topology.c b/plat/rpi/common/rpi3_topology.c index 3747287c..5fef777c 100644 --- a/plat/rpi/common/rpi3_topology.c +++ b/plat/rpi/common/rpi3_topology.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015-2019, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2015-2024, Arm Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -39,12 +39,27 @@ int plat_core_pos_by_mpidr(u_register_t mpidr) unsigned int cluster_id, cpu_id; mpidr &= MPIDR_AFFINITY_MASK; + + /* + * When MT is set, lowest affinity represents the thread ID. + * Since we only support one thread per core, discard this field + * so cluster and core IDs go back into Aff1 and Aff0 respectively. + * The upper bits are also affected, but plat_rpi3_calc_core_pos() + * does not use them. + */ + if ((read_mpidr() & MPIDR_MT_MASK) != 0) { + if (MPIDR_AFFLVL0_VAL(mpidr) != 0) { + return -1; + } + mpidr >>= MPIDR_AFFINITY_BITS; + } + if (mpidr & ~(MPIDR_CLUSTER_MASK | MPIDR_CPU_MASK)) { return -1; } - cluster_id = (mpidr >> MPIDR_AFF1_SHIFT) & MPIDR_AFFLVL_MASK; - cpu_id = (mpidr >> MPIDR_AFF0_SHIFT) & MPIDR_AFFLVL_MASK; + cluster_id = MPIDR_AFFLVL1_VAL(mpidr); + cpu_id = MPIDR_AFFLVL0_VAL(mpidr); if (cluster_id >= PLATFORM_CLUSTER_COUNT) { return -1; diff --git a/plat/rpi/rpi4/rpi4_bl31_setup.c b/plat/rpi/common/rpi4_bl31_setup.c similarity index 67% rename from plat/rpi/rpi4/rpi4_bl31_setup.c rename to plat/rpi/common/rpi4_bl31_setup.c index 2fb4d3df..a7228fd4 100644 --- a/plat/rpi/rpi4/rpi4_bl31_setup.c +++ b/plat/rpi/common/rpi4_bl31_setup.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015-2019, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2015-2024, Arm Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -8,21 +8,15 @@ #include <inttypes.h> #include <stdint.h> -#include <libfdt.h> - -#include <platform_def.h> #include <arch_helpers.h> #include <common/bl_common.h> +#include <drivers/arm/gicv2.h> #include <lib/mmio.h> #include <lib/xlat_tables/xlat_mmu_helpers.h> #include <lib/xlat_tables/xlat_tables_defs.h> #include <lib/xlat_tables/xlat_tables_v2.h> #include <plat/common/platform.h> -#include <common/fdt_fixup.h> -#include <common/fdt_wrappers.h> -#include <libfdt.h> - -#include <drivers/arm/gicv2.h> +#include <platform_def.h> #include <rpi_shared.h> @@ -85,7 +79,7 @@ uintptr_t plat_get_ns_image_entrypoint(void) #endif } -static uintptr_t rpi4_get_dtb_address(void) +uintptr_t rpi4_get_dtb_address(void) { #ifdef RPI3_PRELOADED_DTB_BASE return RPI3_PRELOADED_DTB_BASE; @@ -151,7 +145,7 @@ void bl31_early_platform_setup2(u_register_t arg0, u_register_t arg1, * r1 = machine type number, optional in DT-only platforms (~0 if so) * r2 = Physical address of the device tree blob */ - VERBOSE("rpi4: Preparing to boot 32-bit Linux kernel\n"); + VERBOSE("rpi: Preparing to boot 32-bit Linux kernel\n"); bl33_image_ep_info.args.arg0 = 0U; bl33_image_ep_info.args.arg1 = ~0U; bl33_image_ep_info.args.arg2 = rpi4_get_dtb_address(); @@ -162,7 +156,7 @@ void bl31_early_platform_setup2(u_register_t arg0, u_register_t arg1, * tree blob (DTB) in x0, while x1-x3 are reserved for future use and * must be 0. */ - VERBOSE("rpi4: Preparing to boot 64-bit Linux kernel\n"); + VERBOSE("rpi: Preparing to boot 64-bit Linux kernel\n"); bl33_image_ep_info.args.arg0 = rpi4_get_dtb_address(); bl33_image_ep_info.args.arg1 = 0ULL; bl33_image_ep_info.args.arg2 = 0ULL; @@ -203,102 +197,13 @@ void bl31_plat_arch_setup(void) enable_mmu_el3(0); } -/* - * Remove the FDT /memreserve/ entry that covers the region at the very - * beginning of memory (if that exists). This is where the secondaries - * originally spin, but we pull them out there. - * Having overlapping /reserved-memory and /memreserve/ regions confuses - * the Linux kernel, so we need to get rid of this one. - */ -static void remove_spintable_memreserve(void *dtb) -{ - uint64_t addr, size; - int regions = fdt_num_mem_rsv(dtb); - int i; - - for (i = 0; i < regions; i++) { - if (fdt_get_mem_rsv(dtb, i, &addr, &size) != 0) { - return; - } - if (size == 0U) { - return; - } - /* We only look for the region at the beginning of DRAM. */ - if (addr != 0U) { - continue; - } - /* - * Currently the region in the existing DTs is exactly 4K - * in size. Should this value ever change, there is probably - * a reason for that, so inform the user about this. - */ - if (size == 4096U) { - fdt_del_mem_rsv(dtb, i); - return; - } - WARN("Keeping unknown /memreserve/ region at 0, size: %" PRId64 "\n", - size); - } -} - -static void rpi4_prepare_dtb(void) -{ - void *dtb = (void *)rpi4_get_dtb_address(); - uint32_t gic_int_prop[3]; - int ret, offs; - - /* Return if no device tree is detected */ - if (fdt_check_header(dtb) != 0) - return; - - ret = fdt_open_into(dtb, dtb, 0x100000); - if (ret < 0) { - ERROR("Invalid Device Tree at %p: error %d\n", dtb, ret); - return; - } - - if (dt_add_psci_node(dtb)) { - ERROR("Failed to add PSCI Device Tree node\n"); - return; - } - - if (dt_add_psci_cpu_enable_methods(dtb)) { - ERROR("Failed to add PSCI cpu enable methods in Device Tree\n"); - return; - } - - /* - * Remove the original reserved region (used for the spintable), and - * replace it with a region describing the whole of Trusted Firmware. - */ - remove_spintable_memreserve(dtb); - if (fdt_add_reserved_memory(dtb, "atf@0", 0, 0x80000)) - WARN("Failed to add reserved memory nodes to DT.\n"); - - offs = fdt_node_offset_by_compatible(dtb, 0, "arm,gic-400"); - gic_int_prop[0] = cpu_to_fdt32(1); // PPI - gic_int_prop[1] = cpu_to_fdt32(9); // PPI #9 - gic_int_prop[2] = cpu_to_fdt32(0x0f04); // all cores, level high - fdt_setprop(dtb, offs, "interrupts", gic_int_prop, 12); - - offs = fdt_path_offset(dtb, "/chosen"); - fdt_setprop_string(dtb, offs, "stdout-path", "serial0"); - - ret = fdt_pack(dtb); - if (ret < 0) - ERROR("Failed to pack Device Tree at %p: error %d\n", dtb, ret); - - clean_dcache_range((uintptr_t)dtb, fdt_blob_size(dtb)); - INFO("Changed device tree to advertise PSCI.\n"); -} - void bl31_platform_setup(void) { - rpi4_prepare_dtb(); - /* Configure the interrupt controller */ gicv2_driver_init(&rpi4_gic_data); gicv2_distif_init(); gicv2_pcpu_distif_init(); gicv2_cpuif_enable(); + + plat_rpi_bl31_custom_setup(); } diff --git a/plat/rpi/rpi4/rpi4_pci_svc.c b/plat/rpi/common/rpi_pci_svc.c similarity index 76% rename from plat/rpi/rpi4/rpi4_pci_svc.c rename to plat/rpi/common/rpi_pci_svc.c index e4ef5c1a..c22f6d89 100644 --- a/plat/rpi/rpi4/rpi4_pci_svc.c +++ b/plat/rpi/common/rpi_pci_svc.c @@ -1,9 +1,10 @@ /* - * Copyright (c) 2021, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2021-2024, Arm Limited and Contributors. All rights reserved. + * Copyright (c) 2024, Mario Bălănică <mariobalanica02@gmail.com> * * SPDX-License-Identifier: BSD-3-Clause * - * The RPi4 has a single nonstandard PCI config region. It is broken into two + * The RPi has a single nonstandard PCI config region. It is broken into two * pieces, the root port config registers and a window to a single device's * config space which can move between devices. There isn't (yet) an * authoritative public document on this since the available BCM2711 reference @@ -29,62 +30,63 @@ #include <lib/mmio.h> -static spinlock_t pci_lock; - -#define PCIE_REG_BASE U(RPI_IO_BASE + 0x01500000) #define PCIE_MISC_PCIE_STATUS 0x4068 #define PCIE_EXT_CFG_INDEX 0x9000 -/* A small window pointing at the ECAM of the device selected by CFG_INDEX */ #define PCIE_EXT_CFG_DATA 0x8000 +#define PCIE_EXT_CFG_BDF_SHIFT 12 + #define INVALID_PCI_ADDR 0xFFFFFFFF -#define PCIE_EXT_BUS_SHIFT 20 -#define PCIE_EXT_DEV_SHIFT 15 -#define PCIE_EXT_FUN_SHIFT 12 +static spinlock_t pci_lock; +static uint64_t pcie_rc_bases[] = { RPI_PCIE_RC_BASES }; static uint64_t pci_segment_lib_get_base(uint32_t address, uint32_t offset) { - uint64_t base; - uint32_t bus, dev, fun; - uint32_t status; + uint64_t base; + uint32_t seg, bus, dev, fun; - base = PCIE_REG_BASE; + seg = PCI_ADDR_SEG(address); - offset &= PCI_OFFSET_MASK; /* Pick off the 4k register offset */ + if (seg >= ARRAY_SIZE(pcie_rc_bases)) { + return INVALID_PCI_ADDR; + } /* The root port is at the base of the PCIe register space */ - if (address != 0U) { - /* - * The current device must be at CFG_DATA, a 4K window mapped, - * via CFG_INDEX, to the device we are accessing. At the same - * time we must avoid accesses to certain areas of the cfg - * space via CFG_DATA. Detect those accesses and report that - * the address is invalid. - */ - base += PCIE_EXT_CFG_DATA; - bus = PCI_ADDR_BUS(address); - dev = PCI_ADDR_DEV(address); - fun = PCI_ADDR_FUN(address); - address = (bus << PCIE_EXT_BUS_SHIFT) | - (dev << PCIE_EXT_DEV_SHIFT) | - (fun << PCIE_EXT_FUN_SHIFT); - - /* Allow only dev = 0 on root port and bus 1 */ - if ((bus < 2U) && (dev > 0U)) { - return INVALID_PCI_ADDR; - } + base = pcie_rc_bases[seg]; + + bus = PCI_ADDR_BUS(address); + dev = PCI_ADDR_DEV(address); + fun = PCI_ADDR_FUN(address); - /* Assure link up before reading bus 1 */ - status = mmio_read_32(PCIE_REG_BASE + PCIE_MISC_PCIE_STATUS); - if ((status & 0x30) != 0x30) { + /* There can only be the root port on bus 0 */ + if ((bus == 0U) && ((dev > 0U) || (fun > 0U))) { + return INVALID_PCI_ADDR; + } + + /* There can only be one device on bus 1 */ + if ((bus == 1U) && (dev > 0U)) { + return INVALID_PCI_ADDR; + } + + if (bus > 0) { +#if RPI_PCIE_ECAM_SERROR_QUIRK + uint32_t status = mmio_read_32(base + PCIE_MISC_PCIE_STATUS); + + /* Assure link up before accessing downstream of root port */ + if ((status & 0x30) == 0U) { return INVALID_PCI_ADDR; } - - /* Adjust which device the CFG_DATA window is pointing at */ - mmio_write_32(PCIE_REG_BASE + PCIE_EXT_CFG_INDEX, address); +#endif + /* + * Device function is mapped at CFG_DATA, a 4 KB window + * movable by writing its B/D/F location to CFG_INDEX. + */ + mmio_write_32(base + PCIE_EXT_CFG_INDEX, address << PCIE_EXT_CFG_BDF_SHIFT); + base += PCIE_EXT_CFG_DATA; } - return base + offset; + + return base + (offset & PCI_OFFSET_MASK); } /** @@ -130,7 +132,7 @@ uint32_t pci_read_config(uint32_t addr, uint32_t off, uint32_t sz, uint32_t *val *val = mmio_read_32(base); break; default: /* should be unreachable */ - *val = 0; + *val = 0U; ret = SMC_PCI_CALL_INVAL_PARAM; } } @@ -204,9 +206,12 @@ uint32_t pci_write_config(uint32_t addr, uint32_t off, uint32_t sz, uint32_t val uint32_t pci_get_bus_for_seg(uint32_t seg, uint32_t *bus_range, uint32_t *nseg) { uint32_t ret = SMC_PCI_CALL_SUCCESS; - *nseg = 0U; /* only a single segment */ - if (seg == 0U) { - *bus_range = 0xFF00; /* start 0, end 255 */ + uint32_t rc_count = ARRAY_SIZE(pcie_rc_bases); + + *nseg = (seg < rc_count - 1U) ? seg + 1U : 0U; + + if (seg < rc_count) { + *bus_range = 0U + (0xFF << 8); /* start 0, end 255 */ } else { *bus_range = 0U; ret = SMC_PCI_CALL_NOT_IMPL; diff --git a/plat/rpi/rpi3/include/plat_macros.S b/plat/rpi/rpi3/include/plat_macros.S deleted file mode 100644 index c0c39679..00000000 --- a/plat/rpi/rpi3/include/plat_macros.S +++ /dev/null @@ -1,20 +0,0 @@ -/* - * Copyright (c) 2016-2018, ARM Limited and Contributors. All rights reserved. - * - * SPDX-License-Identifier: BSD-3-Clause - */ -#ifndef PLAT_MACROS_S -#define PLAT_MACROS_S - - /* --------------------------------------------- - * The below required platform porting macro - * prints out relevant platform registers - * whenever an unhandled exception is taken in - * BL31. - * Clobbers: x0 - x10, x16, x17, sp - * --------------------------------------------- - */ - .macro plat_crash_print_regs - .endm - -#endif /* PLAT_MACROS_S */ diff --git a/plat/rpi/rpi3/include/platform_def.h b/plat/rpi/rpi3/include/platform_def.h index f44d1f52..757c64ad 100644 --- a/plat/rpi/rpi3/include/platform_def.h +++ b/plat/rpi/rpi3/include/platform_def.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015-2018, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2015-2024, Arm Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -254,6 +254,7 @@ #define PLAT_RPI_PL011_UART_BASE RPI3_PL011_UART_BASE #define PLAT_RPI_PL011_UART_CLOCK RPI3_PL011_UART_CLOCK #define PLAT_RPI_UART_BAUDRATE ULL(115200) +#define PLAT_RPI_CRASH_UART_BASE PLAT_RPI_MINI_UART_BASE /* * System counter diff --git a/plat/rpi/rpi3/include/rpi_hw.h b/plat/rpi/rpi3/include/rpi_hw.h index 2aecab37..dec59633 100644 --- a/plat/rpi/rpi3/include/rpi_hw.h +++ b/plat/rpi/rpi3/include/rpi_hw.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016-2018, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2016-2024, Arm Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -21,60 +21,18 @@ */ #define RPI3_MBOX_OFFSET ULL(0x0000B880) #define RPI3_MBOX_BASE (RPI_IO_BASE + RPI3_MBOX_OFFSET) -/* VideoCore -> ARM */ -#define RPI3_MBOX0_READ_OFFSET ULL(0x00000000) -#define RPI3_MBOX0_PEEK_OFFSET ULL(0x00000010) -#define RPI3_MBOX0_SENDER_OFFSET ULL(0x00000014) -#define RPI3_MBOX0_STATUS_OFFSET ULL(0x00000018) -#define RPI3_MBOX0_CONFIG_OFFSET ULL(0x0000001C) -/* ARM -> VideoCore */ -#define RPI3_MBOX1_WRITE_OFFSET ULL(0x00000020) -#define RPI3_MBOX1_PEEK_OFFSET ULL(0x00000030) -#define RPI3_MBOX1_SENDER_OFFSET ULL(0x00000034) -#define RPI3_MBOX1_STATUS_OFFSET ULL(0x00000038) -#define RPI3_MBOX1_CONFIG_OFFSET ULL(0x0000003C) -/* Mailbox status constants */ -#define RPI3_MBOX_STATUS_FULL_MASK U(0x80000000) /* Set if full */ -#define RPI3_MBOX_STATUS_EMPTY_MASK U(0x40000000) /* Set if empty */ /* * Power management, reset controller, watchdog. */ #define RPI3_IO_PM_OFFSET ULL(0x00100000) #define RPI3_PM_BASE (RPI_IO_BASE + RPI3_IO_PM_OFFSET) -/* Registers on top of RPI3_PM_BASE. */ -#define RPI3_PM_RSTC_OFFSET ULL(0x0000001C) -#define RPI3_PM_RSTS_OFFSET ULL(0x00000020) -#define RPI3_PM_WDOG_OFFSET ULL(0x00000024) -/* Watchdog constants */ -#define RPI3_PM_PASSWORD U(0x5A000000) -#define RPI3_PM_RSTC_WRCFG_MASK U(0x00000030) -#define RPI3_PM_RSTC_WRCFG_FULL_RESET U(0x00000020) -/* - * The RSTS register is used by the VideoCore firmware when booting the - * Raspberry Pi to know which partition to boot from. The partition value is - * formed by bits 0, 2, 4, 6, 8 and 10. Partition 63 is used by said firmware - * to indicate halt. - */ -#define RPI3_PM_RSTS_WRCFG_HALT U(0x00000555) /* * Hardware random number generator. */ #define RPI3_IO_RNG_OFFSET ULL(0x00104000) #define RPI3_RNG_BASE (RPI_IO_BASE + RPI3_IO_RNG_OFFSET) -#define RPI3_RNG_CTRL_OFFSET ULL(0x00000000) -#define RPI3_RNG_STATUS_OFFSET ULL(0x00000004) -#define RPI3_RNG_DATA_OFFSET ULL(0x00000008) -#define RPI3_RNG_INT_MASK_OFFSET ULL(0x00000010) -/* Enable/disable RNG */ -#define RPI3_RNG_CTRL_ENABLE U(0x1) -#define RPI3_RNG_CTRL_DISABLE U(0x0) -/* Number of currently available words */ -#define RPI3_RNG_STATUS_NUM_WORDS_SHIFT U(24) -#define RPI3_RNG_STATUS_NUM_WORDS_MASK U(0xFF) -/* Value to mask interrupts caused by the RNG */ -#define RPI3_RNG_INT_MASK_DISABLE U(0x1) /* * Serial ports: diff --git a/plat/rpi/rpi3/platform.mk b/plat/rpi/rpi3/platform.mk index 06393e40..fc51bec6 100644 --- a/plat/rpi/rpi3/platform.mk +++ b/plat/rpi/rpi3/platform.mk @@ -1,5 +1,5 @@ # -# Copyright (c) 2013-2023, Arm Limited and Contributors. All rights reserved. +# Copyright (c) 2013-2024, Arm Limited and Contributors. All rights reserved. # # SPDX-License-Identifier: BSD-3-Clause # @@ -17,11 +17,13 @@ PLAT_BL_COMMON_SOURCES := drivers/ti/uart/aarch64/16550_console.S \ drivers/rpi3/gpio/rpi3_gpio.c \ plat/rpi/common/aarch64/plat_helpers.S \ plat/rpi/common/rpi3_common.c \ + plat/rpi/common/rpi3_console_dual.c \ ${XLAT_TABLES_LIB_SRCS} BL1_SOURCES += drivers/io/io_fip.c \ drivers/io/io_memmap.c \ drivers/io/io_storage.c \ + drivers/delay_timer/generic_delay_timer.c \ lib/cpus/aarch64/cortex_a53.S \ plat/common/aarch64/platform_mp_stack.S \ plat/rpi/rpi3/rpi3_bl1_setup.c \ @@ -52,9 +54,9 @@ BL31_SOURCES += lib/cpus/aarch64/cortex_a53.S \ ${LIBFDT_SRCS} # Tune compiler for Cortex-A53 -ifeq ($(notdir $(CC)),armclang) +ifeq ($($(ARCH)-cc-id),arm-clang) TF_CFLAGS_aarch64 += -mcpu=cortex-a53 -else ifneq ($(findstring clang,$(notdir $(CC))),) +else ifneq ($(filter %-clang,$($(ARCH)-cc-id)),) TF_CFLAGS_aarch64 += -mcpu=cortex-a53 else TF_CFLAGS_aarch64 += -mtune=cortex-a53 @@ -72,13 +74,13 @@ all: armstub # This target concatenates BL1 and the FIP so that the base addresses match the # ones defined in the memory map armstub: bl1 fip - @echo " CAT $@" - ${Q}cp ${BUILD_PLAT}/bl1.bin ${RPI3_BL1_PAD_BIN} - ${Q}truncate --size=131072 ${RPI3_BL1_PAD_BIN} - ${Q}cat ${RPI3_BL1_PAD_BIN} ${BUILD_PLAT}/fip.bin > ${RPI3_ARMSTUB8_BIN} - @${ECHO_BLANK_LINE} - @echo "Built $@ successfully" - @${ECHO_BLANK_LINE} + $(s)echo " CAT $@" + $(q)cp ${BUILD_PLAT}/bl1.bin ${RPI3_BL1_PAD_BIN} + $(q)truncate --size=131072 ${RPI3_BL1_PAD_BIN} + $(q)cat ${RPI3_BL1_PAD_BIN} ${BUILD_PLAT}/fip.bin > ${RPI3_ARMSTUB8_BIN} + $(s)echo + $(s)echo "Built $@ successfully" + $(s)echo # Build config flags # ------------------ @@ -211,12 +213,12 @@ ifneq (${TRUSTED_BOARD_BOOT},0) certificates: $(ROT_KEY) - $(ROT_KEY): | $(BUILD_PLAT) - @echo " OPENSSL $@" - $(Q)${OPENSSL_BIN_PATH}/openssl genrsa 2048 > $@ 2>/dev/null + $(ROT_KEY): | $$(@D)/ + $(s)echo " OPENSSL $@" + $(q)${OPENSSL_BIN_PATH}/openssl genrsa 2048 > $@ 2>/dev/null - $(ROTPK_HASH): $(ROT_KEY) - @echo " OPENSSL $@" - $(Q)${OPENSSL_BIN_PATH}/openssl rsa -in $< -pubout -outform DER 2>/dev/null |\ + $(ROTPK_HASH): $(ROT_KEY) | $$(@D)/ + $(s)echo " OPENSSL $@" + $(q)${OPENSSL_BIN_PATH}/openssl rsa -in $< -pubout -outform DER 2>/dev/null |\ ${OPENSSL_BIN_PATH}/openssl dgst -sha256 -binary > $@ 2>/dev/null endif diff --git a/plat/rpi/rpi3/rpi3_bl1_setup.c b/plat/rpi/rpi3/rpi3_bl1_setup.c index 3ac30e0f..6c8fb2d0 100644 --- a/plat/rpi/rpi3/rpi3_bl1_setup.c +++ b/plat/rpi/rpi3/rpi3_bl1_setup.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015-2019, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2015-2024, ARM Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -13,6 +13,8 @@ #include <lib/mmio.h> #include <lib/xlat_tables/xlat_mmu_helpers.h> #include <lib/xlat_tables/xlat_tables_defs.h> +#include <drivers/generic_delay_timer.h> +#include <plat/common/platform.h> #include <rpi_shared.h> @@ -37,6 +39,15 @@ void bl1_early_platform_setup(void) /* Initialize the console to provide early debug support */ rpi3_console_init(); + /* + * Write the System Timer Frequency to CNTFRQ manually, this + * is required to use the delay_timer functionality. + */ + write_cntfrq_el0(plat_get_syscnt_freq2()); + + /* Enable arch timer */ + generic_delay_timer_init(); + /* Allow BL1 to see the whole Trusted RAM */ bl1_tzram_layout.total_base = BL_RAM_BASE; bl1_tzram_layout.total_size = BL_RAM_SIZE; diff --git a/plat/rpi/rpi4/include/platform_def.h b/plat/rpi/rpi4/include/platform_def.h index 6787ebfe..b72aedcf 100644 --- a/plat/rpi/rpi4/include/platform_def.h +++ b/plat/rpi/rpi4/include/platform_def.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015-2019, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2015-2024, Arm Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -131,6 +131,7 @@ #define PLAT_RPI_PL011_UART_BASE RPI4_PL011_UART_BASE #define PLAT_RPI_PL011_UART_CLOCK RPI4_PL011_UART_CLOCK #define PLAT_RPI_UART_BAUDRATE ULL(115200) +#define PLAT_RPI_CRASH_UART_BASE PLAT_RPI_MINI_UART_BASE /* * System counter diff --git a/plat/rpi/rpi4/include/rpi_hw.h b/plat/rpi/rpi4/include/rpi_hw.h index 0430d464..53ce0f8b 100644 --- a/plat/rpi/rpi4/include/rpi_hw.h +++ b/plat/rpi/rpi4/include/rpi_hw.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016-2021, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2016-2024, Arm Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -23,60 +23,18 @@ */ #define RPI3_MBOX_OFFSET ULL(0x0000B880) #define RPI3_MBOX_BASE (RPI_LEGACY_BASE + RPI3_MBOX_OFFSET) -/* VideoCore -> ARM */ -#define RPI3_MBOX0_READ_OFFSET ULL(0x00000000) -#define RPI3_MBOX0_PEEK_OFFSET ULL(0x00000010) -#define RPI3_MBOX0_SENDER_OFFSET ULL(0x00000014) -#define RPI3_MBOX0_STATUS_OFFSET ULL(0x00000018) -#define RPI3_MBOX0_CONFIG_OFFSET ULL(0x0000001C) -/* ARM -> VideoCore */ -#define RPI3_MBOX1_WRITE_OFFSET ULL(0x00000020) -#define RPI3_MBOX1_PEEK_OFFSET ULL(0x00000030) -#define RPI3_MBOX1_SENDER_OFFSET ULL(0x00000034) -#define RPI3_MBOX1_STATUS_OFFSET ULL(0x00000038) -#define RPI3_MBOX1_CONFIG_OFFSET ULL(0x0000003C) -/* Mailbox status constants */ -#define RPI3_MBOX_STATUS_FULL_MASK U(0x80000000) /* Set if full */ -#define RPI3_MBOX_STATUS_EMPTY_MASK U(0x40000000) /* Set if empty */ /* * Power management, reset controller, watchdog. */ #define RPI3_IO_PM_OFFSET ULL(0x00100000) #define RPI3_PM_BASE (RPI_LEGACY_BASE + RPI3_IO_PM_OFFSET) -/* Registers on top of RPI3_PM_BASE. */ -#define RPI3_PM_RSTC_OFFSET ULL(0x0000001C) -#define RPI3_PM_RSTS_OFFSET ULL(0x00000020) -#define RPI3_PM_WDOG_OFFSET ULL(0x00000024) -/* Watchdog constants */ -#define RPI3_PM_PASSWORD U(0x5A000000) -#define RPI3_PM_RSTC_WRCFG_MASK U(0x00000030) -#define RPI3_PM_RSTC_WRCFG_FULL_RESET U(0x00000020) -/* - * The RSTS register is used by the VideoCore firmware when booting the - * Raspberry Pi to know which partition to boot from. The partition value is - * formed by bits 0, 2, 4, 6, 8 and 10. Partition 63 is used by said firmware - * to indicate halt. - */ -#define RPI3_PM_RSTS_WRCFG_HALT U(0x00000555) /* * Hardware random number generator. */ #define RPI3_IO_RNG_OFFSET ULL(0x00104000) #define RPI3_RNG_BASE (RPI_LEGACY_BASE + RPI3_IO_RNG_OFFSET) -#define RPI3_RNG_CTRL_OFFSET ULL(0x00000000) -#define RPI3_RNG_STATUS_OFFSET ULL(0x00000004) -#define RPI3_RNG_DATA_OFFSET ULL(0x00000008) -#define RPI3_RNG_INT_MASK_OFFSET ULL(0x00000010) -/* Enable/disable RNG */ -#define RPI3_RNG_CTRL_ENABLE U(0x1) -#define RPI3_RNG_CTRL_DISABLE U(0x0) -/* Number of currently available words */ -#define RPI3_RNG_STATUS_NUM_WORDS_SHIFT U(24) -#define RPI3_RNG_STATUS_NUM_WORDS_MASK U(0xFF) -/* Value to mask interrupts caused by the RNG */ -#define RPI3_RNG_INT_MASK_DISABLE U(0x1) /* * Serial ports: @@ -111,4 +69,11 @@ #define RPI4_LOCAL_CONTROL_BASE_ADDRESS ULL(0xff800000) #define RPI4_LOCAL_CONTROL_PRESCALER ULL(0xff800008) +/* + * PCI Express + */ +#define RPI_PCIE_RC_BASES (RPI_IO_BASE + ULL(0x01500000)) + +#define RPI_PCIE_ECAM_SERROR_QUIRK 1 + #endif /* RPI_HW_H */ diff --git a/plat/rpi/rpi4/platform.mk b/plat/rpi/rpi4/platform.mk index 528eb1d2..c39a5877 100644 --- a/plat/rpi/rpi4/platform.mk +++ b/plat/rpi/rpi4/platform.mk @@ -1,5 +1,5 @@ # -# Copyright (c) 2013-2021, ARM Limited and Contributors. All rights reserved. +# Copyright (c) 2013-2024, Arm Limited and Contributors. All rights reserved. # # SPDX-License-Identifier: BSD-3-Clause # @@ -15,20 +15,23 @@ PLAT_INCLUDES := -Iplat/rpi/common/include \ PLAT_BL_COMMON_SOURCES := drivers/ti/uart/aarch64/16550_console.S \ drivers/arm/pl011/aarch64/pl011_console.S \ plat/rpi/common/rpi3_common.c \ + plat/rpi/common/rpi3_console_dual.c \ ${XLAT_TABLES_LIB_SRCS} BL31_SOURCES += lib/cpus/aarch64/cortex_a72.S \ plat/rpi/common/aarch64/plat_helpers.S \ - plat/rpi/rpi4/aarch64/armstub8_header.S \ + plat/rpi/common/aarch64/armstub8_header.S \ drivers/delay_timer/delay_timer.c \ drivers/gpio/gpio.c \ drivers/rpi3/gpio/rpi3_gpio.c \ plat/common/plat_gicv2.c \ - plat/rpi/rpi4/rpi4_bl31_setup.c \ + plat/rpi/common/rpi4_bl31_setup.c \ + plat/rpi/rpi4/rpi4_setup.c \ plat/rpi/common/rpi3_pm.c \ plat/common/plat_psci_common.c \ plat/rpi/common/rpi3_topology.c \ common/fdt_fixup.c \ + common/fdt_wrappers.c \ ${LIBFDT_SRCS} \ ${GICV2_SOURCES} @@ -39,9 +42,9 @@ RESET_TO_BL31 := 1 COLD_BOOT_SINGLE_CPU := 0 # Tune compiler for Cortex-A72 -ifeq ($(notdir $(CC)),armclang) +ifeq ($($(ARCH)-cc-id),arm-clang) TF_CFLAGS_aarch64 += -mcpu=cortex-a72 -else ifneq ($(findstring clang,$(notdir $(CC))),) +else ifneq ($(filter %-clang,$($(ARCH)-cc-id)),) TF_CFLAGS_aarch64 += -mcpu=cortex-a72 else TF_CFLAGS_aarch64 += -mtune=cortex-a72 @@ -111,6 +114,5 @@ PLAT_BL_COMMON_SOURCES += drivers/rpi3/rng/rpi3_rng.c \ endif ifeq ($(SMC_PCI_SUPPORT), 1) -BL31_SOURCES += plat/rpi/rpi4/rpi4_pci_svc.c +BL31_SOURCES += plat/rpi/common/rpi_pci_svc.c endif - diff --git a/plat/rpi/rpi4/rpi4_setup.c b/plat/rpi/rpi4/rpi4_setup.c new file mode 100644 index 00000000..82200b97 --- /dev/null +++ b/plat/rpi/rpi4/rpi4_setup.c @@ -0,0 +1,109 @@ +/* + * Copyright (c) 2016-2024, Arm Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <assert.h> +#include <inttypes.h> +#include <stdint.h> + +#include <arch_helpers.h> +#include <common/fdt_fixup.h> +#include <common/fdt_wrappers.h> + +#include <rpi_shared.h> + +/* + * Remove the FDT /memreserve/ entry that covers the region at the very + * beginning of memory (if that exists). This is where the secondaries + * originally spin, but we pull them out there. + * Having overlapping /reserved-memory and /memreserve/ regions confuses + * the Linux kernel, so we need to get rid of this one. + */ +static void remove_spintable_memreserve(void *dtb) +{ + uint64_t addr, size; + int regions = fdt_num_mem_rsv(dtb); + int i; + + for (i = 0; i < regions; i++) { + if (fdt_get_mem_rsv(dtb, i, &addr, &size) != 0) { + return; + } + if (size == 0U) { + return; + } + /* We only look for the region at the beginning of DRAM. */ + if (addr != 0U) { + continue; + } + /* + * Currently the region in the existing DTs is exactly 4K + * in size. Should this value ever change, there is probably + * a reason for that, so inform the user about this. + */ + if (size == 4096U) { + fdt_del_mem_rsv(dtb, i); + return; + } + WARN("Keeping unknown /memreserve/ region at 0, size: %" PRId64 "\n", + size); + } +} + +static void rpi4_prepare_dtb(void) +{ + void *dtb = (void *)rpi4_get_dtb_address(); + uint32_t gic_int_prop[3]; + int ret, offs; + + /* Return if no device tree is detected */ + if (fdt_check_header(dtb) != 0) + return; + + ret = fdt_open_into(dtb, dtb, 0x100000); + if (ret < 0) { + ERROR("Invalid Device Tree at %p: error %d\n", dtb, ret); + return; + } + + if (dt_add_psci_node(dtb)) { + ERROR("Failed to add PSCI Device Tree node\n"); + return; + } + + if (dt_add_psci_cpu_enable_methods(dtb)) { + ERROR("Failed to add PSCI cpu enable methods in Device Tree\n"); + return; + } + + /* + * Remove the original reserved region (used for the spintable), and + * replace it with a region describing the whole of Trusted Firmware. + */ + remove_spintable_memreserve(dtb); + if (fdt_add_reserved_memory(dtb, "atf@0", 0, 0x80000)) + WARN("Failed to add reserved memory nodes to DT.\n"); + + offs = fdt_node_offset_by_compatible(dtb, 0, "arm,gic-400"); + gic_int_prop[0] = cpu_to_fdt32(1); // PPI + gic_int_prop[1] = cpu_to_fdt32(9); // PPI #9 + gic_int_prop[2] = cpu_to_fdt32(0x0f04); // all cores, level high + fdt_setprop(dtb, offs, "interrupts", gic_int_prop, 12); + + offs = fdt_path_offset(dtb, "/chosen"); + fdt_setprop_string(dtb, offs, "stdout-path", "serial0"); + + ret = fdt_pack(dtb); + if (ret < 0) + ERROR("Failed to pack Device Tree at %p: error %d\n", dtb, ret); + + clean_dcache_range((uintptr_t)dtb, fdt_blob_size(dtb)); + INFO("Changed device tree to advertise PSCI.\n"); +} + +void plat_rpi_bl31_custom_setup(void) +{ + rpi4_prepare_dtb(); +} diff --git a/plat/rpi/rpi5/include/plat.ld.S b/plat/rpi/rpi5/include/plat.ld.S new file mode 100644 index 00000000..961c630c --- /dev/null +++ b/plat/rpi/rpi5/include/plat.ld.S @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2019-2024, Arm Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + * + * Stub linker script to provide the armstub8.bin header before the actual + * code. If the GPU firmware finds a magic value at offset 240 in + * armstub8.bin, it will put the DTB and kernel load address in subsequent + * words. We can then read those values to find the proper NS entry point + * and find our DTB more flexibly. + */ + +MEMORY { + PRERAM (rwx): ORIGIN = 0, LENGTH = 4096 +} + +SECTIONS +{ + .armstub8 . : { + *armstub8_header.o(.text*) + KEEP(*(.armstub8)) + } >PRERAM +} diff --git a/plat/rpi/rpi5/include/platform_def.h b/plat/rpi/rpi5/include/platform_def.h new file mode 100644 index 00000000..a4c2f5b9 --- /dev/null +++ b/plat/rpi/rpi5/include/platform_def.h @@ -0,0 +1,141 @@ +/* + * Copyright (c) 2015-2024, Arm Limited and Contributors. All rights reserved. + * Copyright (c) 2024, Mario Bălănică <mariobalanica02@gmail.com> + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef PLATFORM_DEF_H +#define PLATFORM_DEF_H + +#include <arch.h> +#include <common/tbbr/tbbr_img_def.h> +#include <lib/utils_def.h> +#include <plat/common/common_def.h> + +#include "rpi_hw.h" + +/* Special value used to verify platform parameters from BL2 to BL31 */ +#define RPI3_BL31_PLAT_PARAM_VAL ULL(0x0F1E2D3C4B5A6978) + +#define PLATFORM_STACK_SIZE ULL(0x1000) + +#define PLATFORM_MAX_CPUS_PER_CLUSTER U(4) +#define PLATFORM_CLUSTER_COUNT U(1) +#define PLATFORM_CLUSTER0_CORE_COUNT PLATFORM_MAX_CPUS_PER_CLUSTER +#define PLATFORM_CORE_COUNT PLATFORM_CLUSTER0_CORE_COUNT + +#define RPI_PRIMARY_CPU U(0) + +#define PLAT_MAX_PWR_LVL MPIDR_AFFLVL1 +#define PLAT_NUM_PWR_DOMAINS (PLATFORM_CLUSTER_COUNT + \ + PLATFORM_CORE_COUNT) + +#define PLAT_MAX_RET_STATE U(1) +#define PLAT_MAX_OFF_STATE U(2) + +/* Local power state for power domains in Run state. */ +#define PLAT_LOCAL_STATE_RUN U(0) +/* Local power state for retention. Valid only for CPU power domains */ +#define PLAT_LOCAL_STATE_RET U(1) +/* + * Local power state for OFF/power-down. Valid for CPU and cluster power + * domains. + */ +#define PLAT_LOCAL_STATE_OFF U(2) + +/* + * Macros used to parse state information from State-ID if it is using the + * recommended encoding for State-ID. + */ +#define PLAT_LOCAL_PSTATE_WIDTH U(4) +#define PLAT_LOCAL_PSTATE_MASK ((U(1) << PLAT_LOCAL_PSTATE_WIDTH) - 1) + +/* + * Some data must be aligned on the biggest cache line size in the platform. + * This is known only to the platform as it might have a combination of + * integrated and external caches. + */ +#define CACHE_WRITEBACK_SHIFT U(6) +#define CACHE_WRITEBACK_GRANULE (U(1) << CACHE_WRITEBACK_SHIFT) + +/* + * I/O registers. + */ +#define DEVICE0_BASE RPI_IO_BASE +#define DEVICE0_SIZE RPI_IO_SIZE + +/* + * Mailbox to control the secondary cores. All secondary cores are held in a + * wait loop in cold boot. To release them perform the following steps (plus + * any additional barriers that may be needed): + * + * uint64_t *entrypoint = (uint64_t *)PLAT_RPI3_TM_ENTRYPOINT; + * *entrypoint = ADDRESS_TO_JUMP_TO; + * + * uint64_t *mbox_entry = (uint64_t *)PLAT_RPI3_TM_HOLD_BASE; + * mbox_entry[cpu_id] = PLAT_RPI3_TM_HOLD_STATE_GO; + * + * sev(); + */ +/* The secure entry point to be used on warm reset by all CPUs. */ +#define PLAT_RPI3_TM_ENTRYPOINT 0x100 +#define PLAT_RPI3_TM_ENTRYPOINT_SIZE ULL(8) + +/* Hold entries for each CPU. */ +#define PLAT_RPI3_TM_HOLD_BASE (PLAT_RPI3_TM_ENTRYPOINT + \ + PLAT_RPI3_TM_ENTRYPOINT_SIZE) +#define PLAT_RPI3_TM_HOLD_ENTRY_SIZE ULL(8) +#define PLAT_RPI3_TM_HOLD_SIZE (PLAT_RPI3_TM_HOLD_ENTRY_SIZE * \ + PLATFORM_CORE_COUNT) + +#define PLAT_RPI3_TRUSTED_MAILBOX_SIZE (PLAT_RPI3_TM_ENTRYPOINT_SIZE + \ + PLAT_RPI3_TM_HOLD_SIZE) + +#define PLAT_RPI3_TM_HOLD_STATE_WAIT ULL(0) +#define PLAT_RPI3_TM_HOLD_STATE_GO ULL(1) +#define PLAT_RPI3_TM_HOLD_STATE_BSP_OFF ULL(2) + +/* + * BL31 specific defines. + * + * Put BL31 at the top of the Trusted SRAM. BL31_BASE is calculated using the + * current BL31 debug size plus a little space for growth. + */ +#define PLAT_MAX_BL31_SIZE ULL(0x80000) + +#define BL31_BASE ULL(0x1000) +#define BL31_LIMIT ULL(0x80000) +#define BL31_PROGBITS_LIMIT ULL(0x80000) + +#define SEC_SRAM_ID 0 +#define SEC_DRAM_ID 1 + +/* + * Other memory-related defines. + */ +#define PLAT_PHY_ADDR_SPACE_SIZE (ULL(1) << 40) +#define PLAT_VIRT_ADDR_SPACE_SIZE (ULL(1) << 40) + +#define MAX_MMAP_REGIONS 8 +#define MAX_XLAT_TABLES 4 + +#define MAX_IO_DEVICES U(3) +#define MAX_IO_HANDLES U(4) + +#define MAX_IO_BLOCK_DEVICES U(1) + +/* + * Serial-related constants. + */ +#define PLAT_RPI_PL011_UART_BASE RPI4_PL011_UART_BASE +#define PLAT_RPI_PL011_UART_CLOCK RPI4_PL011_UART_CLOCK +#define PLAT_RPI_UART_BAUDRATE ULL(115200) +#define PLAT_RPI_CRASH_UART_BASE PLAT_RPI_PL011_UART_BASE + +/* + * System counter + */ +#define SYS_COUNTER_FREQ_IN_TICKS ULL(54000000) + +#endif /* PLATFORM_DEF_H */ diff --git a/plat/rpi/rpi5/include/rpi_hw.h b/plat/rpi/rpi5/include/rpi_hw.h new file mode 100644 index 00000000..a7376765 --- /dev/null +++ b/plat/rpi/rpi5/include/rpi_hw.h @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2016-2024, Arm Limited and Contributors. All rights reserved. + * Copyright (c) 2024, Mario Bălănică <mariobalanica02@gmail.com> + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef RPI_HW_H +#define RPI_HW_H + +#include <lib/utils_def.h> + +/* + * Peripherals + */ + +#define RPI_IO_BASE ULL(0x1000000000) +#define RPI_IO_SIZE ULL(0x1000000000) + +/* + * ARM <-> VideoCore mailboxes + */ +#define RPI3_MBOX_BASE (RPI_IO_BASE + ULL(0x7c013880)) + +/* + * Power management, reset controller, watchdog. + */ +#define RPI3_PM_BASE (RPI_IO_BASE + ULL(0x7d200000)) + +/* + * Hardware random number generator. + */ +#define RPI3_RNG_BASE (RPI_IO_BASE + ULL(0x7d208000)) + +/* + * PL011 system serial port + */ +#define RPI4_PL011_UART_BASE (RPI_IO_BASE + ULL(0x7d001000)) +#define RPI4_PL011_UART_CLOCK ULL(44000000) + +/* + * GIC interrupt controller + */ +#define RPI_HAVE_GIC +#define RPI4_GIC_GICD_BASE (RPI_IO_BASE + ULL(0x7fff9000)) +#define RPI4_GIC_GICC_BASE (RPI_IO_BASE + ULL(0x7fffa000)) + +#define RPI4_LOCAL_CONTROL_BASE_ADDRESS (RPI_IO_BASE + ULL(0x7c280000)) +#define RPI4_LOCAL_CONTROL_PRESCALER (RPI_IO_BASE + ULL(0x7c280008)) + +/* + * PCI Express + */ +#define RPI_PCIE_RC_BASES RPI_IO_BASE + ULL(0x00100000), \ + RPI_IO_BASE + ULL(0x00110000), \ + RPI_IO_BASE + ULL(0x00120000) + +#endif /* RPI_HW_H */ diff --git a/plat/rpi/rpi5/platform.mk b/plat/rpi/rpi5/platform.mk new file mode 100644 index 00000000..70c5add2 --- /dev/null +++ b/plat/rpi/rpi5/platform.mk @@ -0,0 +1,115 @@ +# +# Copyright (c) 2015-2024, Arm Limited and Contributors. All rights reserved. +# Copyright (c) 2024, Mario Bălănică <mariobalanica02@gmail.com> +# +# SPDX-License-Identifier: BSD-3-Clause +# + +include lib/xlat_tables_v2/xlat_tables.mk + +include drivers/arm/gic/v2/gicv2.mk + +PLAT_INCLUDES := -Iplat/rpi/common/include \ + -Iplat/rpi/rpi5/include + +PLAT_BL_COMMON_SOURCES := drivers/arm/pl011/aarch64/pl011_console.S \ + plat/rpi/common/rpi3_common.c \ + plat/rpi/common/rpi3_console_pl011.c \ + ${XLAT_TABLES_LIB_SRCS} + +BL31_SOURCES += lib/cpus/aarch64/cortex_a76.S \ + plat/rpi/common/aarch64/plat_helpers.S \ + plat/rpi/common/aarch64/armstub8_header.S \ + drivers/delay_timer/delay_timer.c \ + plat/common/plat_gicv2.c \ + plat/rpi/common/rpi4_bl31_setup.c \ + plat/rpi/rpi5/rpi5_setup.c \ + plat/rpi/common/rpi3_pm.c \ + plat/common/plat_psci_common.c \ + plat/rpi/common/rpi3_topology.c \ + ${GICV2_SOURCES} + +# For now we only support BL31, using the kernel loaded by the GPU firmware. +RESET_TO_BL31 := 1 + +# All CPUs enter armstub8.bin. +COLD_BOOT_SINGLE_CPU := 0 + +# Tune compiler for Cortex-A76 +ifeq ($(notdir $(CC)),armclang) + TF_CFLAGS_aarch64 += -mcpu=cortex-a76 +else ifneq ($(findstring clang,$(notdir $(CC))),) + TF_CFLAGS_aarch64 += -mcpu=cortex-a76 +else + TF_CFLAGS_aarch64 += -mtune=cortex-a76 +endif + +# Add support for platform supplied linker script for BL31 build +$(eval $(call add_define,PLAT_EXTRA_LD_SCRIPT)) + +# Enable all errata workarounds for Cortex-A76 r4p1 +ERRATA_A76_1946160 := 1 +ERRATA_A76_2743102 := 1 + +# Add new default target when compiling this platform +all: bl31 + +# Build config flags +# ------------------ + +# Disable stack protector by default +ENABLE_STACK_PROTECTOR := 0 + +# Have different sections for code and rodata +SEPARATE_CODE_AND_RODATA := 1 + +# Hardware-managed coherency +HW_ASSISTED_COHERENCY := 1 +USE_COHERENT_MEM := 0 + +# Cortex-A76 is 64-bit only +CTX_INCLUDE_AARCH32_REGS := 0 + +# Platform build flags +# -------------------- + +# There is not much else than a Linux kernel to load at the moment. +RPI3_DIRECT_LINUX_BOOT := 1 + +# BL33 images can only be AArch64 on this platform. +RPI3_BL33_IN_AARCH32 := 0 + +# UART to use at runtime. -1 means the runtime UART is disabled. +# Any other value means the default UART will be used. +RPI3_RUNTIME_UART := 0 + +# Use normal memory mapping for ROM, FIP, SRAM and DRAM +RPI3_USE_UEFI_MAP := 0 + +# SMCCC PCI support (should be enabled for ACPI builds) +SMC_PCI_SUPPORT := 0 + +# Process platform flags +# ---------------------- + +$(eval $(call add_define,RPI3_BL33_IN_AARCH32)) +$(eval $(call add_define,RPI3_DIRECT_LINUX_BOOT)) +ifdef RPI3_PRELOADED_DTB_BASE +$(eval $(call add_define,RPI3_PRELOADED_DTB_BASE)) +endif +$(eval $(call add_define,RPI3_RUNTIME_UART)) +$(eval $(call add_define,RPI3_USE_UEFI_MAP)) +$(eval $(call add_define,SMC_PCI_SUPPORT)) + +ifeq (${ARCH},aarch32) + $(error Error: AArch32 not supported on rpi5) +endif + +ifneq ($(ENABLE_STACK_PROTECTOR), 0) +PLAT_BL_COMMON_SOURCES += drivers/rpi3/rng/rpi3_rng.c \ + plat/rpi/common/rpi3_stack_protector.c +endif + +ifeq ($(SMC_PCI_SUPPORT), 1) +BL31_SOURCES += plat/rpi/common/rpi_pci_svc.c +endif diff --git a/plat/rpi/rpi5/rpi5_setup.c b/plat/rpi/rpi5/rpi5_setup.c new file mode 100644 index 00000000..de82300d --- /dev/null +++ b/plat/rpi/rpi5/rpi5_setup.c @@ -0,0 +1,12 @@ +/* + * Copyright (c) 2024, Mario Bălănică <mariobalanica02@gmail.com> + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <rpi_shared.h> + +void plat_rpi_bl31_custom_setup(void) +{ + /* Nothing to do here yet. */ +} diff --git a/plat/socionext/synquacer/platform.mk b/plat/socionext/synquacer/platform.mk index a6d9bef7..e4ae87bd 100644 --- a/plat/socionext/synquacer/platform.mk +++ b/plat/socionext/synquacer/platform.mk @@ -1,5 +1,5 @@ # -# Copyright (c) 2018-2023, ARM Limited and Contributors. All rights reserved. +# Copyright (c) 2018-2024, Arm Limited and Contributors. All rights reserved. # # SPDX-License-Identifier: BSD-3-Clause # @@ -71,13 +71,13 @@ $(eval $(call add_define_val,ROTPK_HASH,'"$(ROTPK_HASH)"')) $(BUILD_PLAT)/bl2/sq_rotpk.o: $(ROTPK_HASH) certificates: $(ROT_KEY) -$(ROT_KEY): | $(BUILD_PLAT) - @echo " OPENSSL $@" - $(Q)${OPENSSL_BIN_PATH}/openssl genrsa 2048 > $@ 2>/dev/null +$(ROT_KEY): | $$(@D)/ + $(s)echo " OPENSSL $@" + $(q)${OPENSSL_BIN_PATH}/openssl genrsa 2048 > $@ 2>/dev/null -$(ROTPK_HASH): $(ROT_KEY) - @echo " OPENSSL $@" - $(Q)${OPENSSL_BIN_PATH}/openssl rsa -in $< -pubout -outform DER 2>/dev/null |\ +$(ROTPK_HASH): $(ROT_KEY) | $$(@D)/ + $(s)echo " OPENSSL $@" + $(q)${OPENSSL_BIN_PATH}/openssl rsa -in $< -pubout -outform DER 2>/dev/null |\ ${OPENSSL_BIN_PATH}/openssl dgst -sha256 -binary > $@ 2>/dev/null endif # TRUSTED_BOARD_BOOT diff --git a/plat/socionext/synquacer/sq_bl31_setup.c b/plat/socionext/synquacer/sq_bl31_setup.c index 967437b2..e46d8779 100644 --- a/plat/socionext/synquacer/sq_bl31_setup.c +++ b/plat/socionext/synquacer/sq_bl31_setup.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018-2022, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2018-2024, ARM Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ diff --git a/plat/socionext/uniphier/platform.mk b/plat/socionext/uniphier/platform.mk index d466aa1c..21d95cf1 100644 --- a/plat/socionext/uniphier/platform.mk +++ b/plat/socionext/uniphier/platform.mk @@ -1,5 +1,5 @@ # -# Copyright (c) 2017-2023, ARM Limited and Contributors. All rights reserved. +# Copyright (c) 2017-2024, Arm Limited and Contributors. All rights reserved. # # SPDX-License-Identifier: BSD-3-Clause # @@ -105,13 +105,13 @@ $(eval $(call add_define_val,ROTPK_HASH,'"$(ROTPK_HASH)"')) $(BUILD_PLAT)/bl2/uniphier_rotpk.o: $(ROTPK_HASH) certificates: $(ROT_KEY) -$(ROT_KEY): | $(BUILD_PLAT) - @echo " OPENSSL $@" - $(Q)${OPENSSL_BIN_PATH}/openssl genrsa 2048 > $@ 2>/dev/null +$(ROT_KEY): | $$(@D)/ + $(s)echo " OPENSSL $@" + $(q)${OPENSSL_BIN_PATH}/openssl genrsa 2048 > $@ 2>/dev/null -$(ROTPK_HASH): $(ROT_KEY) - @echo " OPENSSL $@" - $(Q)${OPENSSL_BIN_PATH}/openssl rsa -in $< -pubout -outform DER 2>/dev/null |\ +$(ROTPK_HASH): $(ROT_KEY) | $$(@D)/ + $(s)echo " OPENSSL $@" + $(q)${OPENSSL_BIN_PATH}/openssl rsa -in $< -pubout -outform DER 2>/dev/null |\ ${OPENSSL_BIN_PATH}/openssl dgst -sha256 -binary > $@ 2>/dev/null endif @@ -136,5 +136,5 @@ endif .PHONY: bl2_gzip bl2_gzip: $(BUILD_PLAT)/bl2.bin.gz %.gz: % - @echo " GZIP $@" - $(Q)gzip -n -f -9 $< --stdout > $@ + $(s)echo " GZIP $@" + $(q)gzip -n -f -9 $< --stdout > $@ diff --git a/plat/st/common/bl2_io_storage.c b/plat/st/common/bl2_io_storage.c index 86795d71..c17ac7eb 100644 --- a/plat/st/common/bl2_io_storage.c +++ b/plat/st/common/bl2_io_storage.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015-2023, Arm Limited and Contributors. All rights reserved. + * Copyright (c) 2015-2024, Arm Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -47,6 +47,7 @@ uintptr_t fip_dev_handle; uintptr_t storage_dev_handle; static const io_dev_connector_t *fip_dev_con; +static uint32_t nand_block_sz __maybe_unused; #ifndef DECRYPTION_SUPPORT_none static const io_dev_connector_t *enc_dev_con; @@ -310,11 +311,55 @@ static void boot_spi_nor(boot_api_context_t *boot_context) } #endif /* STM32MP_SPI_NOR */ +#if STM32MP_RAW_NAND || STM32MP_SPI_NAND +/* + * This function returns 0 if it can find an alternate + * image to be loaded or a negative errno otherwise. + */ +static int try_nand_backup_partitions(unsigned int image_id) +{ + static unsigned int backup_id; + static unsigned int backup_block_nb; + + /* Check if NAND storage used */ + if (nand_block_sz == 0U) { + return -ENODEV; + } + + if (backup_id != image_id) { + backup_block_nb = PLATFORM_MTD_MAX_PART_SIZE / nand_block_sz; + backup_id = image_id; + } + + if (backup_block_nb-- == 0U) { + return -ENOSPC; + } + +#if PSA_FWU_SUPPORT + if (((image_block_spec.offset < STM32MP_NAND_FIP_B_OFFSET) && + ((image_block_spec.offset + nand_block_sz) >= STM32MP_NAND_FIP_B_OFFSET)) || + (image_block_spec.offset + nand_block_sz >= STM32MP_NAND_FIP_B_MAX_OFFSET)) { + return 0; + } +#endif + + image_block_spec.offset += nand_block_sz; + + return 0; +} + +static const struct plat_try_images_ops try_img_ops = { + .next_instance = try_nand_backup_partitions, +}; +#endif /* STM32MP_RAW_NAND || STM32MP_SPI_NAND */ + #if STM32MP_RAW_NAND static void boot_fmc2_nand(boot_api_context_t *boot_context) { int io_result __maybe_unused; + plat_setup_try_img_ops(&try_img_ops); + io_result = stm32_fmc2_init(); assert(io_result == 0); @@ -326,6 +371,8 @@ static void boot_fmc2_nand(boot_api_context_t *boot_context) io_result = io_dev_open(nand_dev_con, (uintptr_t)&nand_dev_spec, &storage_dev_handle); assert(io_result == 0); + + nand_block_sz = nand_dev_spec.erase_size; } #endif /* STM32MP_RAW_NAND */ @@ -334,6 +381,8 @@ static void boot_spi_nand(boot_api_context_t *boot_context) { int io_result __maybe_unused; + plat_setup_try_img_ops(&try_img_ops); + io_result = stm32_qspi_init(); assert(io_result == 0); @@ -345,6 +394,8 @@ static void boot_spi_nand(boot_api_context_t *boot_context) (uintptr_t)&spi_nand_dev_spec, &storage_dev_handle); assert(io_result == 0); + + nand_block_sz = spi_nand_dev_spec.erase_size; } #endif /* STM32MP_SPI_NAND */ @@ -493,12 +544,10 @@ int bl2_plat_handle_pre_image_load(unsigned int image_id) */ #if !PSA_FWU_SUPPORT const partition_entry_t *entry; - const struct efi_guid img_type_guid = STM32MP_FIP_GUID; - uuid_t img_type_uuid; + const struct efi_guid fip_guid = STM32MP_FIP_GUID; - guidcpy(&img_type_uuid, &img_type_guid); partition_init(GPT_IMAGE_ID); - entry = get_partition_entry_by_type(&img_type_uuid); + entry = get_partition_entry_by_type(&fip_guid); if (entry == NULL) { entry = get_partition_entry(FIP_IMAGE_NAME); if (entry == NULL) { @@ -532,7 +581,14 @@ int bl2_plat_handle_pre_image_load(unsigned int image_id) #if STM32MP_SPI_NAND case BOOT_API_CTX_BOOT_INTERFACE_SEL_FLASH_NAND_SPI: #endif +/* + * With FWU Multi Bank feature enabled, the selection of + * the image to boot will be done by fwu_init calling the + * platform hook, plat_fwu_set_images_source. + */ +#if !PSA_FWU_SUPPORT image_block_spec.offset = STM32MP_NAND_FIP_OFFSET; +#endif break; #endif @@ -598,7 +654,7 @@ int plat_get_image_source(unsigned int image_id, uintptr_t *dev_handle, return rc; } -#if (STM32MP_SDMMC || STM32MP_EMMC || STM32MP_SPI_NOR) && PSA_FWU_SUPPORT +#if PSA_FWU_SUPPORT /* * In each boot in non-trial mode, we set the BKP register to * FWU_MAX_TRIAL_REBOOT, and return the active_index from metadata. @@ -613,8 +669,6 @@ int plat_get_image_source(unsigned int image_id, uintptr_t *dev_handle, * - we already boot FWU_MAX_TRIAL_REBOOT times in trial mode. * we select the previous_active_index. */ -#define INVALID_BOOT_IDX 0xFFFFFFFFU - uint32_t plat_fwu_get_boot_idx(void) { /* @@ -622,32 +676,38 @@ uint32_t plat_fwu_get_boot_idx(void) * even if this function is called several times. */ static uint32_t boot_idx = INVALID_BOOT_IDX; - const struct fwu_metadata *data; - - data = fwu_get_metadata(); if (boot_idx == INVALID_BOOT_IDX) { + const struct fwu_metadata *data = fwu_get_metadata(); + boot_idx = data->active_index; - if (fwu_is_trial_run_state()) { + + if (data->bank_state[boot_idx] == FWU_BANK_STATE_VALID) { if (stm32_get_and_dec_fwu_trial_boot_cnt() == 0U) { WARN("Trial FWU fails %u times\n", FWU_MAX_TRIAL_REBOOT); - boot_idx = data->previous_active_index; + boot_idx = fwu_get_alternate_boot_bank(); } - } else { + } else if (data->bank_state[boot_idx] == + FWU_BANK_STATE_ACCEPTED) { stm32_set_max_fwu_trial_boot_cnt(); + } else { + ERROR("The active bank(%u) of the platform is in Invalid State.\n", + boot_idx); + boot_idx = fwu_get_alternate_boot_bank(); + stm32_clear_fwu_trial_boot_cnt(); } } return boot_idx; } -static void *stm32_get_image_spec(const uuid_t *img_type_uuid) +static void *stm32_get_image_spec(const struct efi_guid *img_type_guid) { unsigned int i; for (i = 0U; i < MAX_NUMBER_IDS; i++) { - if ((guidcmp(&policies[i].img_type_guid, img_type_uuid)) == 0) { + if ((guidcmp(&policies[i].img_type_guid, img_type_guid)) == 0) { return (void *)policies[i].image_spec; } } @@ -660,20 +720,23 @@ void plat_fwu_set_images_source(const struct fwu_metadata *metadata) unsigned int i; uint32_t boot_idx; const partition_entry_t *entry __maybe_unused; - const uuid_t *img_type_uuid; - const uuid_t *img_uuid __maybe_unused; + const struct fwu_image_entry *img_entry; + const void *img_type_guid; + const void *img_guid; io_block_spec_t *image_spec; const uint16_t boot_itf = stm32mp_get_boot_itf_selected(); boot_idx = plat_fwu_get_boot_idx(); assert(boot_idx < NR_OF_FW_BANKS); + VERBOSE("Selecting to boot from bank %u\n", boot_idx); + img_entry = (void *)&metadata->fw_desc.img_entry; for (i = 0U; i < NR_OF_IMAGES_IN_FW_BANK; i++) { - img_type_uuid = &metadata->img_entry[i].img_type_uuid; + img_type_guid = &img_entry[i].img_type_guid; - img_uuid = &metadata->img_entry[i].img_props[boot_idx].img_uuid; + img_guid = &img_entry[i].img_bank_info[boot_idx].img_guid; - image_spec = stm32_get_image_spec(img_type_uuid); + image_spec = stm32_get_image_spec(img_type_guid); if (image_spec == NULL) { ERROR("Unable to get image spec for the image in the metadata\n"); panic(); @@ -683,7 +746,7 @@ void plat_fwu_set_images_source(const struct fwu_metadata *metadata) #if (STM32MP_SDMMC || STM32MP_EMMC) case BOOT_API_CTX_BOOT_INTERFACE_SEL_FLASH_SD: case BOOT_API_CTX_BOOT_INTERFACE_SEL_FLASH_EMMC: - entry = get_partition_entry_by_uuid(img_uuid); + entry = get_partition_entry_by_guid(img_guid); if (entry == NULL) { ERROR("No partition with the uuid mentioned in metadata\n"); panic(); @@ -695,15 +758,28 @@ void plat_fwu_set_images_source(const struct fwu_metadata *metadata) #endif #if STM32MP_SPI_NOR case BOOT_API_CTX_BOOT_INTERFACE_SEL_FLASH_NOR_SPI: - if (guidcmp(img_uuid, &STM32MP_NOR_FIP_A_GUID) == 0) { + if (guidcmp(img_guid, &STM32MP_NOR_FIP_A_GUID) == 0) { image_spec->offset = STM32MP_NOR_FIP_A_OFFSET; - } else if (guidcmp(img_uuid, &STM32MP_NOR_FIP_B_GUID) == 0) { + } else if (guidcmp(img_guid, &STM32MP_NOR_FIP_B_GUID) == 0) { image_spec->offset = STM32MP_NOR_FIP_B_OFFSET; } else { ERROR("Invalid uuid mentioned in metadata\n"); panic(); } break; +#endif +#if (STM32MP_RAW_NAND || STM32MP_SPI_NAND) + case BOOT_API_CTX_BOOT_INTERFACE_SEL_FLASH_NAND_FMC: + case BOOT_API_CTX_BOOT_INTERFACE_SEL_FLASH_NAND_SPI: + if (guidcmp(img_guid, &STM32MP_NAND_FIP_A_GUID) == 0) { + image_spec->offset = STM32MP_NAND_FIP_A_OFFSET; + } else if (guidcmp(img_guid, &STM32MP_NAND_FIP_B_GUID) == 0) { + image_spec->offset = STM32MP_NAND_FIP_B_OFFSET; + } else { + ERROR("Invalid uuid mentioned in metadata\n"); + panic(); + } + break; #endif default: panic(); @@ -712,9 +788,9 @@ void plat_fwu_set_images_source(const struct fwu_metadata *metadata) } } -static int plat_set_image_source(unsigned int image_id, - uintptr_t *handle, - uintptr_t *image_spec) +static int set_metadata_image_source(unsigned int image_id, + uintptr_t *handle, + uintptr_t *image_spec) { struct plat_io_policy *policy; io_block_spec_t *spec __maybe_unused; @@ -757,6 +833,19 @@ static int plat_set_image_source(unsigned int image_id, spec->length = sizeof(struct fwu_metadata); break; #endif + +#if (STM32MP_RAW_NAND || STM32MP_SPI_NAND) + case BOOT_API_CTX_BOOT_INTERFACE_SEL_FLASH_NAND_FMC: + case BOOT_API_CTX_BOOT_INTERFACE_SEL_FLASH_NAND_SPI: + if (image_id == FWU_METADATA_IMAGE_ID) { + spec->offset = STM32MP_NAND_METADATA1_OFFSET; + } else { + spec->offset = STM32MP_NAND_METADATA2_OFFSET; + } + + spec->length = sizeof(struct fwu_metadata); + break; +#endif default: panic(); break; @@ -775,6 +864,6 @@ int plat_fwu_set_metadata_image_source(unsigned int image_id, assert((image_id == FWU_METADATA_IMAGE_ID) || (image_id == BKUP_FWU_METADATA_IMAGE_ID)); - return plat_set_image_source(image_id, handle, image_spec); + return set_metadata_image_source(image_id, handle, image_spec); } -#endif /* (STM32MP_SDMMC || STM32MP_EMMC || STM32MP_SPI_NOR) && PSA_FWU_SUPPORT */ +#endif /* PSA_FWU_SUPPORT */ diff --git a/plat/st/common/common.mk b/plat/st/common/common.mk index 7f93961d..7395a367 100644 --- a/plat/st/common/common.mk +++ b/plat/st/common/common.mk @@ -1,12 +1,11 @@ # -# Copyright (c) 2023, STMicroelectronics - All Rights Reserved +# Copyright (c) 2023-2024, STMicroelectronics - All Rights Reserved # # SPDX-License-Identifier: BSD-3-Clause # RESET_TO_BL2 := 1 -STM32MP_EARLY_CONSOLE ?= 0 STM32MP_RECONFIGURE_CONSOLE ?= 0 STM32MP_UART_BAUDRATE ?= 115200 @@ -29,6 +28,24 @@ STM32_HEADER_BL2_BINARY_TYPE := 0x10 TF_CFLAGS += -Wsign-compare TF_CFLAGS += -Wformat-signedness +# Number of TF-A copies in the device +STM32_TF_A_COPIES := 2 + +# PLAT_PARTITION_MAX_ENTRIES must take care of STM32_TF-A_COPIES and other partitions +PLAT_PARTITION_MAX_ENTRIES := $(shell echo $$(($(STM32_TF_A_COPIES) + $(STM32_EXTRA_PARTS)))) + +ifeq (${PSA_FWU_SUPPORT},1) +# Number of banks of updatable firmware +NR_OF_FW_BANKS := 2 +NR_OF_IMAGES_IN_FW_BANK := 1 + +FWU_MAX_PART = $(shell echo $$(($(STM32_TF_A_COPIES) + 2 + $(NR_OF_FW_BANKS)))) +ifeq ($(shell test $(FWU_MAX_PART) -gt $(PLAT_PARTITION_MAX_ENTRIES); echo $$?),0) +$(error "Required partition number is $(FWU_MAX_PART) where PLAT_PARTITION_MAX_ENTRIES is only \ +$(PLAT_PARTITION_MAX_ENTRIES)") +endif +endif + # Boot devices STM32MP_EMMC ?= 0 STM32MP_SDMMC ?= 0 @@ -43,7 +60,7 @@ STM32MP_EMMC_BOOT ?= 0 STM32MP_UART_PROGRAMMER ?= 0 STM32MP_USB_PROGRAMMER ?= 0 -$(eval DTC_V = $(shell $(DTC) -v | awk '{print $$NF}')) +$(eval DTC_V = $(shell $($(ARCH)-dtc) -v | awk '{print $$NF}')) $(eval DTC_VERSION = $(shell printf "%d" $(shell echo ${DTC_V} | cut -d- -f1 | sed "s/\./0/g" | grep -o "[0-9]*"))) DTC_CPPFLAGS += ${INCLUDES} DTC_FLAGS += -Wno-unit_address_vs_reg @@ -82,7 +99,6 @@ endif $(eval $(call assert_booleans,\ $(sort \ PLAT_XLAT_TABLES_DYNAMIC \ - STM32MP_EARLY_CONSOLE \ STM32MP_EMMC \ STM32MP_EMMC_BOOT \ STM32MP_RAW_NAND \ @@ -104,7 +120,6 @@ $(eval $(call add_defines,\ $(sort \ PLAT_XLAT_TABLES_DYNAMIC \ STM32_TF_VERSION \ - STM32MP_EARLY_CONSOLE \ STM32MP_EMMC \ STM32MP_EMMC_BOOT \ STM32MP_RAW_NAND \ @@ -123,6 +138,9 @@ PLAT_INCLUDES += -Iplat/st/common/include/ include lib/fconf/fconf.mk include lib/libfdt/libfdt.mk include lib/zlib/zlib.mk +ifeq (${PSA_FWU_SUPPORT},1) +include drivers/fwu/fwu.mk +endif PLAT_BL_COMMON_SOURCES += common/uuid.c \ plat/st/common/stm32mp_common.c @@ -183,12 +201,10 @@ ifneq (${MBEDTLS_DIR},) MBEDTLS_MAJOR=$(shell grep -hP "define MBEDTLS_VERSION_MAJOR" \ ${MBEDTLS_DIR}/include/mbedtls/*.h | grep -oe '\([0-9.]*\)') -ifeq (${MBEDTLS_MAJOR}, 2) -MBEDTLS_CONFIG_FILE ?= "<stm32mp_mbedtls_config-2.h>" -endif - ifeq (${MBEDTLS_MAJOR}, 3) MBEDTLS_CONFIG_FILE ?= "<stm32mp_mbedtls_config-3.h>" +else +$(error Error: TF-A only supports MbedTLS versions > 3.x) endif endif diff --git a/plat/st/common/common_rules.mk b/plat/st/common/common_rules.mk index f39caab8..9070a16a 100644 --- a/plat/st/common/common_rules.mk +++ b/plat/st/common/common_rules.mk @@ -1,5 +1,5 @@ # -# Copyright (c) 2023, STMicroelectronics - All Rights Reserved +# Copyright (c) 2023-2024, STMicroelectronics - All Rights Reserved # # SPDX-License-Identifier: BSD-3-Clause # @@ -15,7 +15,7 @@ distclean realclean clean: clean_stm32image bl2: check_boot_device check_boot_device: - @if [ ${STM32MP_EMMC} != 1 ] && \ + $(q)if [ ${STM32MP_EMMC} != 1 ] && \ [ ${STM32MP_SDMMC} != 1 ] && \ [ ${STM32MP_RAW_NAND} != 1 ] && \ [ ${STM32MP_SPI_NAND} != 1 ] && \ @@ -29,55 +29,55 @@ check_boot_device: stm32image: ${STM32IMAGE} ${STM32IMAGE}: ${STM32IMAGE_SRC} - ${Q}${MAKE} CPPFLAGS="" --no-print-directory -C ${STM32IMAGEPATH} + $(q)${MAKE} CPPFLAGS="" --no-print-directory -C ${STM32IMAGEPATH} clean_stm32image: - ${Q}${MAKE} --no-print-directory -C ${STM32IMAGEPATH} clean + $(q)${MAKE} --no-print-directory -C ${STM32IMAGEPATH} clean check_dtc_version: - @if [ ${DTC_VERSION} -lt 10407 ]; then \ + $(q)if [ ${DTC_VERSION} -lt 10407 ]; then \ echo "dtc version too old (${DTC_V}), you need at least version 1.4.7"; \ false; \ fi # Create DTB file for BL2 -${BUILD_PLAT}/fdts/%-bl2.dts: fdts/%.dts fdts/${BL2_DTSI} | ${BUILD_PLAT} fdt_dirs - @echo '#include "$(patsubst fdts/%,%,$<)"' > $@ - @echo '#include "${BL2_DTSI}"' >> $@ +${BUILD_PLAT}/fdts/%-bl2.dts: fdts/%.dts fdts/${BL2_DTSI} | $$(@D)/ + $(q)echo '#include "$(patsubst fdts/%,%,$<)"' > $@ + $(q)echo '#include "${BL2_DTSI}"' >> $@ ${BUILD_PLAT}/fdts/%-bl2.dtb: ${BUILD_PLAT}/fdts/%-bl2.dts ${BUILD_PLAT}/$(PLAT)-%.o: ${BUILD_PLAT}/fdts/%-bl2.dtb $(STM32_BINARY_MAPPING) bl2 - @echo " AS $${PLAT}.S" - ${Q}${AS} ${ASFLAGS} ${TF_CFLAGS} \ + $(s)echo " AS $${PLAT}.S" + $(q)$($(ARCH)-as) -x assembler-with-cpp $(TF_CFLAGS_$(ARCH)) ${ASFLAGS} ${TF_CFLAGS} \ -DDTB_BIN_PATH=\"$<\" \ -c $(word 2,$^) -o $@ $(eval $(call MAKE_LD,${STM32_TF_LINKERFILE},$(STM32_LD_FILE),bl2)) tf-a-%.elf: $(PLAT)-%.o ${STM32_TF_LINKERFILE} - @echo " LDS $<" -ifneq ($(findstring gcc,$(notdir $(LD))),) - ${Q}${LD} -o $@ $(subst --,-Wl$(comma)--,${STM32_TF_ELF_LDFLAGS}) -nostartfiles -Wl,-Map=$(@:.elf=.map) -Wl,-dT ${STM32_TF_LINKERFILE} $< + $(s)echo " LDS $<" +ifeq ($($(ARCH)-ld-id),gnu-gcc) + $(q)$($(ARCH)-ld) -o $@ $(subst --,-Wl$(comma)--,${STM32_TF_ELF_LDFLAGS}) -nostartfiles -no-pie -Wl,-Map=$(@:.elf=.map) -Wl,-dT ${STM32_TF_LINKERFILE} $< else - ${Q}${LD} -o $@ ${STM32_TF_ELF_LDFLAGS} -Map=$(@:.elf=.map) --script ${STM32_TF_LINKERFILE} $< + $(q)$($(ARCH)-ld) -o $@ ${STM32_TF_ELF_LDFLAGS} -no-pie -Map=$(@:.elf=.map) --script ${STM32_TF_LINKERFILE} $< endif tf-a-%.bin: tf-a-%.elf - ${Q}${OC} -O binary $< $@ - @echo - @echo "Built $@ successfully" - @echo + $(q)$($(ARCH)-oc) -O binary $< $@ + $(s)echo + $(s)echo "Built $@ successfully" + $(s)echo tf-a-%.stm32: tf-a-%.bin ${STM32_DEPS} - @echo - @echo "Generate $@" + $(s)echo + $(s)echo "Generate $@" $(eval LOADADDR = $(shell cat $(@:.stm32=.map) | grep '^RAM' | awk '{print $$2}')) $(eval ENTRY = $(shell cat $(@:.stm32=.map) | grep "__BL2_IMAGE_START" | awk '{print $$1}')) - ${Q}${STM32IMAGE} -s $< -d $@ \ + $(q)${STM32IMAGE} -s $< -d $@ \ -l $(LOADADDR) -e ${ENTRY} \ -v ${STM32_TF_VERSION} \ -m ${STM32_HEADER_VERSION_MAJOR} \ -n ${STM32_HEADER_VERSION_MINOR} \ -b ${STM32_HEADER_BL2_BINARY_TYPE} - @echo + $(s)echo diff --git a/plat/st/stm32mp1/include/plat_def_fip_uuid.h b/plat/st/common/include/plat_def_fip_uuid.h similarity index 59% rename from plat/st/stm32mp1/include/plat_def_fip_uuid.h rename to plat/st/common/include/plat_def_fip_uuid.h index e5fbc2df..096fd952 100644 --- a/plat/st/stm32mp1/include/plat_def_fip_uuid.h +++ b/plat/st/common/include/plat_def_fip_uuid.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021-2022, STMicroelectronics - All Rights Reserved + * Copyright (c) 2021-2024, STMicroelectronics - All Rights Reserved * * SPDX-License-Identifier: BSD-3-Clause */ @@ -7,6 +7,10 @@ #ifndef PLAT_DEF_FIP_UUID_H #define PLAT_DEF_FIP_UUID_H +#define UUID_DDR_FW \ + {{0xb1, 0x12, 0x49, 0xbe}, {0x92, 0xdd}, {0x4b, 0x10}, 0x86, 0x7c, \ + {0x2c, 0x6a, 0x4b, 0x47, 0xa7, 0xfb} } + #define UUID_STM32MP_CONFIG_CERT \ {{0x50, 0x1d, 0x8d, 0xd2}, {0x8b, 0xce}, {0x49, 0xa5}, 0x84, 0xeb, \ {0x55, 0x9a, 0x9f, 0x2e, 0xae, 0xaf} } diff --git a/plat/st/common/include/stm32mp_common.h b/plat/st/common/include/stm32mp_common.h index e334f225..f65301ff 100644 --- a/plat/st/common/include/stm32mp_common.h +++ b/plat/st/common/include/stm32mp_common.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2018-2023, STMicroelectronics - All Rights Reserved + * Copyright (C) 2018-2024, STMicroelectronics - All Rights Reserved * * SPDX-License-Identifier: BSD-3-Clause */ @@ -14,17 +14,26 @@ #define JEDEC_ST_BKID U(0x0) #define JEDEC_ST_MFID U(0x20) +#define STM32MP_CHIP_SEC_CLOSED U(0x34D9CCC5) +#define STM32MP_CHIP_SEC_OPEN U(0xA764D182) + /* FWU configuration (max supported value is 15) */ #define FWU_MAX_TRIAL_REBOOT U(3) +/* Define maximum page size for NAND devices */ +#define PLATFORM_MTD_MAX_PAGE_SIZE U(0x1000) + +/* Needed by STM32CubeProgrammer support */ +#define DWL_BUFFER_SIZE U(0x01000000) + /* Functions to save and get boot context address given by ROM code */ void stm32mp_save_boot_ctx_address(uintptr_t address); uintptr_t stm32mp_get_boot_ctx_address(void); uint16_t stm32mp_get_boot_itf_selected(void); bool stm32mp_is_single_core(void); -bool stm32mp_is_closed_device(void); bool stm32mp_is_auth_supported(void); +uint32_t stm32mp_check_closed_device(void); /* Return the base address of the DDR controller */ uintptr_t stm32mp_ddrctrl_base(void); @@ -68,13 +77,7 @@ uintptr_t get_uart_address(uint32_t instance_nb); /* Setup the UART console */ int stm32mp_uart_console_setup(void); -#if STM32MP_EARLY_CONSOLE -void stm32mp_setup_early_console(void); -#else -static inline void stm32mp_setup_early_console(void) -{ -} -#endif +bool stm32mp_is_wakeup_from_standby(void); /* * Platform util functions for the GPIO driver @@ -119,6 +122,10 @@ void stm32mp_io_setup(void); int stm32mp_map_ddr_non_cacheable(void); int stm32mp_unmap_ddr(void); +/* Functions to map RETRAM, and unmap it */ +int stm32mp_map_retram(void); +int stm32mp_unmap_retram(void); + /* Function to save boot info */ void stm32_save_boot_info(boot_api_context_t *boot_context); /* Function to get boot peripheral info */ @@ -130,9 +137,11 @@ uintptr_t stm32_get_bkpr_boot_mode_addr(void); void stm32_display_board_info(uint32_t board_id); #if PSA_FWU_SUPPORT -void stm32mp1_fwu_set_boot_idx(void); +uintptr_t stm32_get_bkpr_fwu_info_addr(void); +void stm32_fwu_set_boot_idx(void); uint32_t stm32_get_and_dec_fwu_trial_boot_cnt(void); void stm32_set_max_fwu_trial_boot_cnt(void); +void stm32_clear_fwu_trial_boot_cnt(void); #endif /* PSA_FWU_SUPPORT */ #endif /* STM32MP_COMMON_H */ diff --git a/plat/st/common/include/stm32mp_mbedtls_config-2.h b/plat/st/common/include/stm32mp_mbedtls_config-2.h deleted file mode 100644 index 66ff3465..00000000 --- a/plat/st/common/include/stm32mp_mbedtls_config-2.h +++ /dev/null @@ -1,119 +0,0 @@ -/* - * Copyright (c) 2022-2023, STMicroelectronics - All Rights Reserved - * - * SPDX-License-Identifier: BSD-3-Clause - */ -#ifndef MBEDTLS_CONFIG_H -#define MBEDTLS_CONFIG_H - -/* - * Key algorithms currently supported on mbed TLS libraries - */ -#define TF_MBEDTLS_USE_RSA 0 -#define TF_MBEDTLS_USE_ECDSA 1 - -/* - * Hash algorithms currently supported on mbed TLS libraries - */ -#define TF_MBEDTLS_SHA256 1 -#define TF_MBEDTLS_SHA384 2 -#define TF_MBEDTLS_SHA512 3 - -/* - * Configuration file to build mbed TLS with the required features for - * Trusted Boot - */ - -#define MBEDTLS_PLATFORM_MEMORY -#define MBEDTLS_PLATFORM_NO_STD_FUNCTIONS -/* Prevent mbed TLS from using snprintf so that it can use tf_snprintf. */ -#define MBEDTLS_PLATFORM_SNPRINTF_ALT - -#define MBEDTLS_PKCS1_V21 - -#define MBEDTLS_X509_ALLOW_UNSUPPORTED_CRITICAL_EXTENSION -#define MBEDTLS_X509_CHECK_KEY_USAGE -#define MBEDTLS_X509_CHECK_EXTENDED_KEY_USAGE - -#define MBEDTLS_ASN1_PARSE_C -#define MBEDTLS_ASN1_WRITE_C - -#define MBEDTLS_BASE64_C -#define MBEDTLS_BIGNUM_C - -#define MBEDTLS_ERROR_C -#define MBEDTLS_MD_C - -#define MBEDTLS_MEMORY_BUFFER_ALLOC_C -#define MBEDTLS_OID_C - -#define MBEDTLS_PK_C -#define MBEDTLS_PK_PARSE_C -#define MBEDTLS_PK_WRITE_C - -#define MBEDTLS_PLATFORM_C - -#if TF_MBEDTLS_USE_ECDSA -#define MBEDTLS_ECDSA_C -#define MBEDTLS_ECP_C -#define MBEDTLS_ECP_DP_SECP256R1_ENABLED -#define MBEDTLS_ECP_NO_INTERNAL_RNG -#endif -#if TF_MBEDTLS_USE_RSA -#define MBEDTLS_RSA_C -#define MBEDTLS_X509_RSASSA_PSS_SUPPORT -#endif - -#define MBEDTLS_SHA256_C -#if (TF_MBEDTLS_HASH_ALG_ID != TF_MBEDTLS_SHA256) -#define MBEDTLS_SHA512_C -#endif - -#define MBEDTLS_VERSION_C - -#define MBEDTLS_X509_USE_C -#define MBEDTLS_X509_CRT_PARSE_C - -#if TF_MBEDTLS_USE_AES_GCM -#define MBEDTLS_AES_C -#define MBEDTLS_CIPHER_C -#define MBEDTLS_GCM_C -#endif - -/* MPI / BIGNUM options */ -#define MBEDTLS_MPI_WINDOW_SIZE 2 - -#if TF_MBEDTLS_USE_RSA -#if TF_MBEDTLS_KEY_SIZE <= 2048 -#define MBEDTLS_MPI_MAX_SIZE 256 -#else -#define MBEDTLS_MPI_MAX_SIZE 512 -#endif -#else -#define MBEDTLS_MPI_MAX_SIZE 256 -#endif - -/* Memory buffer allocator options */ -#define MBEDTLS_MEMORY_ALIGN_MULTIPLE 8 - -/* - * Prevent the use of 128-bit division which - * creates dependency on external libraries. - */ -#define MBEDTLS_NO_UDBL_DIVISION - -#ifndef __ASSEMBLER__ -/* System headers required to build mbed TLS with the current configuration */ -#include <stdlib.h> -#include <mbedtls/check_config.h> -#endif - -/* - * Mbed TLS heap size is smal as we only use the asn1 - * parsing functions - * digest, signature and crypto algorithm are done by - * other library. - */ - -#define TF_MBEDTLS_HEAP_SIZE U(5120) -#endif /* MBEDTLS_CONFIG_H */ diff --git a/plat/st/common/include/stm32mp_mbedtls_config-3.h b/plat/st/common/include/stm32mp_mbedtls_config-3.h index a812671b..2dbf0689 100644 --- a/plat/st/common/include/stm32mp_mbedtls_config-3.h +++ b/plat/st/common/include/stm32mp_mbedtls_config-3.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022-2023, STMicroelectronics - All Rights Reserved + * Copyright (c) 2022-2024, STMicroelectronics - All Rights Reserved * * SPDX-License-Identifier: BSD-3-Clause */ @@ -102,7 +102,6 @@ #ifndef __ASSEMBLER__ /* System headers required to build mbed TLS with the current configuration */ #include <stdlib.h> -#include <mbedtls/check_config.h> #endif /* diff --git a/plat/st/common/stm32mp_common.c b/plat/st/common/stm32mp_common.c index 2163aaf1..d2f87844 100644 --- a/plat/st/common/stm32mp_common.c +++ b/plat/st/common/stm32mp_common.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015-2023, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2015-2024, Arm Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -55,6 +55,12 @@ #define BOOT_INST_MASK GENMASK_32(11, 8) #define BOOT_INST_SHIFT 8 +/* Layout for fwu update information. */ +#define FWU_INFO_IDX_MSK GENMASK(3, 0) +#define FWU_INFO_IDX_OFF U(0) +#define FWU_INFO_CNT_MSK GENMASK(7, 4) +#define FWU_INFO_CNT_OFF U(4) + static console_t console; uintptr_t plat_get_ns_image_entrypoint(void) @@ -168,9 +174,9 @@ int stm32_get_otp_value_from_idx(const uint32_t otp_idx, uint32_t *otp_val) assert(otp_val != NULL); #if defined(IMAGE_BL2) - ret = bsec_shadow_read_otp(otp_val, otp_idx); -#elif defined(IMAGE_BL32) - ret = bsec_read_otp(otp_val, otp_idx); + ret = stm32_otp_shadow_read(otp_val, otp_idx); +#elif defined(IMAGE_BL31) || defined(IMAGE_BL32) + ret = stm32_otp_read(otp_val, otp_idx); #else #error "Not supported" #endif @@ -269,8 +275,8 @@ int stm32mp_uart_console_setup(void) return 0; } -#if STM32MP_EARLY_CONSOLE -void stm32mp_setup_early_console(void) +#if EARLY_CONSOLE +void plat_setup_early_console(void) { #if defined(IMAGE_BL2) || STM32MP_RECONFIGURE_CONSOLE plat_crash_console_init(); @@ -278,7 +284,7 @@ void stm32mp_setup_early_console(void) set_console(STM32MP_DEBUG_USART_BASE, STM32MP_DEBUG_USART_CLK_FRQ); NOTICE("Early console setup\n"); } -#endif /* STM32MP_EARLY_CONSOLE */ +#endif /* EARLY_CONSOLE */ /***************************************************************************** * plat_is_smccc_feature_available() - This function checks whether SMCCC @@ -378,3 +384,53 @@ void stm32_get_boot_interface(uint32_t *interface, uint32_t *instance) *interface = (itf & BOOT_ITF_MASK) >> BOOT_ITF_SHIFT; *instance = (itf & BOOT_INST_MASK) >> BOOT_INST_SHIFT; } + +#if PSA_FWU_SUPPORT +void stm32_fwu_set_boot_idx(void) +{ + clk_enable(TAMP_BKP_REG_CLK); + mmio_clrsetbits_32(stm32_get_bkpr_fwu_info_addr(), + FWU_INFO_IDX_MSK, + (plat_fwu_get_boot_idx() << FWU_INFO_IDX_OFF) & + FWU_INFO_IDX_MSK); + clk_disable(TAMP_BKP_REG_CLK); +} + +uint32_t stm32_get_and_dec_fwu_trial_boot_cnt(void) +{ + uintptr_t bkpr_fwu_cnt = stm32_get_bkpr_fwu_info_addr(); + uint32_t try_cnt; + + clk_enable(TAMP_BKP_REG_CLK); + try_cnt = (mmio_read_32(bkpr_fwu_cnt) & FWU_INFO_CNT_MSK) >> FWU_INFO_CNT_OFF; + + assert(try_cnt <= FWU_MAX_TRIAL_REBOOT); + + if (try_cnt != 0U) { + mmio_clrsetbits_32(bkpr_fwu_cnt, FWU_INFO_CNT_MSK, + (try_cnt - 1U) << FWU_INFO_CNT_OFF); + } + clk_disable(TAMP_BKP_REG_CLK); + + return try_cnt; +} + +void stm32_set_max_fwu_trial_boot_cnt(void) +{ + uintptr_t bkpr_fwu_cnt = stm32_get_bkpr_fwu_info_addr(); + + clk_enable(TAMP_BKP_REG_CLK); + mmio_clrsetbits_32(bkpr_fwu_cnt, FWU_INFO_CNT_MSK, + (FWU_MAX_TRIAL_REBOOT << FWU_INFO_CNT_OFF) & FWU_INFO_CNT_MSK); + clk_disable(TAMP_BKP_REG_CLK); +} + +void stm32_clear_fwu_trial_boot_cnt(void) +{ + uintptr_t bkpr_fwu_cnt = stm32_get_bkpr_fwu_info_addr(); + + clk_enable(TAMP_BKP_REG_CLK); + mmio_clrbits_32(bkpr_fwu_cnt, FWU_INFO_CNT_MSK); + clk_disable(TAMP_BKP_REG_CLK); +} +#endif /* PSA_FWU_SUPPORT */ diff --git a/plat/st/common/stm32mp_crypto_lib.c b/plat/st/common/stm32mp_crypto_lib.c index e282115d..7223022e 100644 --- a/plat/st/common/stm32mp_crypto_lib.c +++ b/plat/st/common/stm32mp_crypto_lib.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022-2023, STMicroelectronics - All Rights Reserved + * Copyright (c) 2022-2024, STMicroelectronics - All Rights Reserved * * SPDX-License-Identifier: BSD-3-Clause */ @@ -11,7 +11,6 @@ #include <common/debug.h> #include <drivers/auth/crypto_mod.h> #include <drivers/io/io_storage.h> -#include <drivers/st/bsec.h> #include <drivers/st/stm32_hash.h> #include <drivers/st/stm32_pka.h> #include <drivers/st/stm32_rng.h> @@ -58,7 +57,8 @@ static void crypto_lib_init(void) panic(); } - if (stm32mp_is_closed_device() || stm32mp_is_auth_supported()) { + if ((stm32mp_check_closed_device() == STM32MP_CHIP_SEC_CLOSED) || + stm32mp_is_auth_supported()) { #if STM32MP_CRYPTO_ROM_LIB boot_context = (boot_api_context_t *)stm32mp_get_boot_ctx_address(); auth_ops.verify_signature = boot_context->bootrom_ecdsa_verify_signature; @@ -322,7 +322,8 @@ static int crypto_verify_signature(void *data_ptr, unsigned int data_len, size_t bignum_len = sizeof(sig) / 2U; unsigned int seq_num = 0U; - if (!stm32mp_is_closed_device() && !stm32mp_is_auth_supported()) { + if ((stm32mp_check_closed_device() == STM32MP_CHIP_SEC_OPEN) && + !stm32mp_is_auth_supported()) { return CRYPTO_SUCCESS; } diff --git a/plat/st/common/stm32mp_dt.c b/plat/st/common/stm32mp_dt.c index 1cbf51ba..282f53fa 100644 --- a/plat/st/common/stm32mp_dt.c +++ b/plat/st/common/stm32mp_dt.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2023, Arm Limited and Contributors. All rights reserved. + * Copyright (c) 2017-2024, Arm Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -243,7 +243,11 @@ size_t dt_get_ddr_size(void) return 0U; } +#ifdef __aarch64__ + size = (size_t)fdt_read_uint64_default(fdt, node, "st,mem-size", 0ULL); +#else /* __aarch64__ */ size = (size_t)fdt_read_uint32_default(fdt, node, "st,mem-size", 0U); +#endif /* __aarch64__ */ flush_dcache_range((uintptr_t)&size, sizeof(size_t)); diff --git a/plat/st/common/stm32mp_fconf_io.c b/plat/st/common/stm32mp_fconf_io.c index 5514c09c..644275e3 100644 --- a/plat/st/common/stm32mp_fconf_io.c +++ b/plat/st/common/stm32mp_fconf_io.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021-2023, STMicroelectronics - All Rights Reserved + * Copyright (c) 2021-2024, STMicroelectronics - All Rights Reserved * * SPDX-License-Identifier: BSD-3-Clause */ @@ -27,12 +27,12 @@ static io_block_spec_t gpt_block_spec = { }; #endif -#if (STM32MP_SDMMC || STM32MP_EMMC || STM32MP_SPI_NOR) && PSA_FWU_SUPPORT +#if PSA_FWU_SUPPORT static io_block_spec_t metadata_block_spec = { .offset = 0, /* To be filled at runtime */ .length = 0, /* To be filled at runtime */ }; -#endif /* (STM32MP_SDMMC || STM32MP_EMMC || STM32MP_SPI_NOR) && PSA_FWU_SUPPORT */ +#endif /* PSA_FWU_SUPPORT */ /* By default, STM32 platforms load images from the FIP */ struct plat_io_policy policies[MAX_NUMBER_IDS] = { @@ -58,7 +58,7 @@ struct plat_io_policy policies[MAX_NUMBER_IDS] = { .check = open_storage }, #endif -#if (STM32MP_SDMMC || STM32MP_EMMC || STM32MP_SPI_NOR) && PSA_FWU_SUPPORT +#if PSA_FWU_SUPPORT [FWU_METADATA_IMAGE_ID] = { .dev_handle = &storage_dev_handle, .image_spec = (uintptr_t)&metadata_block_spec, @@ -71,19 +71,33 @@ struct plat_io_policy policies[MAX_NUMBER_IDS] = { .img_type_guid = NULL_GUID, .check = open_storage }, -#endif /* (STM32MP_SDMMC || STM32MP_EMMC || STM32MP_SPI_NOR) && PSA_FWU_SUPPORT */ +#endif /* PSA_FWU_SUPPORT */ }; #define DEFAULT_UUID_NUMBER U(7) +#ifdef __aarch64__ +#define BL31_UUID_NUMBER U(2) +#else +#define BL31_UUID_NUMBER U(0) +#endif + #if TRUSTED_BOARD_BOOT #define TBBR_UUID_NUMBER U(6) #else #define TBBR_UUID_NUMBER U(0) #endif +#if STM32MP_DDR_FIP_IO_STORAGE +#define DDR_FW_UUID_NUMBER U(1) +#else +#define DDR_FW_UUID_NUMBER U(0) +#endif + #define FCONF_ST_IO_UUID_NUMBER (DEFAULT_UUID_NUMBER + \ - TBBR_UUID_NUMBER) + BL31_UUID_NUMBER + \ + TBBR_UUID_NUMBER + \ + DDR_FW_UUID_NUMBER) static io_uuid_spec_t fconf_stm32mp_uuids[FCONF_ST_IO_UUID_NUMBER]; static OBJECT_POOL_ARRAY(fconf_stm32mp_uuids_pool, fconf_stm32mp_uuids); @@ -95,7 +109,14 @@ struct policies_load_info { /* image id to property name table */ static const struct policies_load_info load_info[FCONF_ST_IO_UUID_NUMBER] = { +#if STM32MP_DDR_FIP_IO_STORAGE + {DDR_FW_ID, "ddr_fw_uuid"}, +#endif {FW_CONFIG_ID, "fw_cfg_uuid"}, +#ifdef __aarch64__ + {BL31_IMAGE_ID, "bl31_uuid"}, + {SOC_FW_CONFIG_ID, "soc_fw_cfg_uuid"}, +#endif {BL32_IMAGE_ID, "bl32_uuid"}, {BL32_EXTRA1_IMAGE_ID, "bl32_extra1_uuid"}, {BL32_EXTRA2_IMAGE_ID, "bl32_extra2_uuid"}, diff --git a/plat/st/common/stm32mp_gic.c b/plat/st/common/stm32mp_gic.c index d02b635a..a4cc4a5e 100644 --- a/plat/st/common/stm32mp_gic.c +++ b/plat/st/common/stm32mp_gic.c @@ -1,11 +1,12 @@ /* - * Copyright (c) 2016-2023, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2016-2024, Arm Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ #include <common/bl_common.h> #include <common/debug.h> +#include <common/fdt_wrappers.h> #include <drivers/arm/gicv2.h> #include <dt-bindings/interrupt-controller/arm-gic.h> #include <lib/utils.h> @@ -45,25 +46,29 @@ void stm32mp_gic_init(void) int node; void *fdt; const fdt32_t *cuint; - struct dt_node_info dt_gic; + uintptr_t addr; + int err; if (fdt_get_address(&fdt) == 0) { panic(); } - node = dt_get_node(&dt_gic, -1, "arm,cortex-a7-gic"); + node = fdt_node_offset_by_compatible(fdt, -1, "arm,cortex-a7-gic"); if (node < 0) { panic(); } - platform_gic_data.gicd_base = dt_gic.base; - - cuint = fdt_getprop(fdt, node, "reg", NULL); - if (cuint == NULL) { + err = fdt_get_reg_props_by_index(fdt, node, 0, &addr, NULL); + if (err < 0) { panic(); } + platform_gic_data.gicd_base = addr; - platform_gic_data.gicc_base = fdt32_to_cpu(*(cuint + 2)); + err = fdt_get_reg_props_by_index(fdt, node, 1, &addr, NULL); + if (err < 0) { + panic(); + } + platform_gic_data.gicc_base = addr; cuint = fdt_getprop(fdt, node, "#interrupt-cells", NULL); if (cuint == NULL) { diff --git a/plat/st/common/stm32mp_trusted_boot.c b/plat/st/common/stm32mp_trusted_boot.c index 6d89290e..d40fc558 100644 --- a/plat/st/common/stm32mp_trusted_boot.c +++ b/plat/st/common/stm32mp_trusted_boot.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, STMicroelectronics - All Rights Reserved + * Copyright (c) 2022-2024, STMicroelectronics - All Rights Reserved * * SPDX-License-Identifier: BSD-3-Clause */ @@ -67,14 +67,14 @@ static int copy_hash_from_otp(const char *otp_name, uint8_t *hash, size_t len) * Check if key hash values in OTP are 0 or 0xFFFFFFFFF * programmed : Invalid Key */ - if (!stm32mp_is_closed_device() && !valid) { + if ((stm32mp_check_closed_device() == STM32MP_CHIP_SEC_OPEN) && !valid) { if ((tmp != 0U) && (tmp != 0xFFFFFFFFU) && (tmp != first)) { valid = true; } } } - if (!stm32mp_is_closed_device() && !valid) { + if ((stm32mp_check_closed_device() == STM32MP_CHIP_SEC_OPEN) && !valid) { return 0; } @@ -163,7 +163,7 @@ int plat_get_rotpk_info(void *cookie, void **key_ptr, unsigned int *key_len, *key_ptr = &root_pk_hash; *flags = ROTPK_IS_HASH; - if ((res == 0) && !stm32mp_is_closed_device()) { + if ((res == 0) && (stm32mp_check_closed_device() == STM32MP_CHIP_SEC_OPEN)) { *flags |= ROTPK_NOT_DEPLOYED; } diff --git a/plat/st/stm32mp1/bl2_plat_setup.c b/plat/st/stm32mp1/bl2_plat_setup.c index eeabd09d..9da311e2 100644 --- a/plat/st/stm32mp1/bl2_plat_setup.c +++ b/plat/st/stm32mp1/bl2_plat_setup.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015-2023, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2015-2024, Arm Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -142,8 +142,6 @@ void bl2_el3_early_platform_setup(u_register_t arg0, u_register_t arg2 __unused, u_register_t arg3 __unused) { - stm32mp_setup_early_console(); - stm32mp_save_boot_ctx_address(arg0); } @@ -255,11 +253,6 @@ void bl2_el3_plat_arch_setup(void) mmio_clrbits_32(rcc_base + RCC_BDCR, RCC_BDCR_VSWRST); } -#if STM32MP15 - /* Disable MCKPROT */ - mmio_clrbits_32(rcc_base + RCC_TZCR, RCC_TZCR_MCKPROT); -#endif - /* * Set minimum reset pulse duration to 31ms for discrete power * supplied boards. @@ -318,7 +311,7 @@ void bl2_el3_plat_arch_setup(void) skip_console_init: #if !TRUSTED_BOARD_BOOT - if (stm32mp_is_closed_device()) { + if (stm32mp_check_closed_device() == STM32MP_CHIP_SEC_CLOSED) { /* Closed chip mandates authentication */ ERROR("Secure chip: TRUSTED_BOARD_BOOT must be enabled\n"); panic(); @@ -338,7 +331,7 @@ skip_console_init: print_pmic_info_and_debug(); } - stm32mp1_syscfg_init(); + stm32mp_syscfg_init(); if (stm32_iwdg_init() < 0) { panic(); @@ -347,7 +340,7 @@ skip_console_init: stm32_iwdg_refresh(); if (bsec_read_debug_conf() != 0U) { - if (stm32mp_is_closed_device()) { + if (stm32mp_check_closed_device() == STM32MP_CHIP_SEC_CLOSED) { #if DEBUG WARN("\n%s", debug_msg); #else @@ -367,10 +360,12 @@ skip_console_init: print_reset_reason(); #if STM32MP15 - update_monotonic_counter(); + if (stm32mp_check_closed_device() == STM32MP_CHIP_SEC_CLOSED) { + update_monotonic_counter(); + } #endif - stm32mp1_syscfg_enable_io_compensation_finish(); + stm32mp_syscfg_enable_io_compensation_finish(); fconf_populate("TB_FW", STM32MP_DTB_BASE); @@ -445,8 +440,7 @@ int bl2_plat_handle_post_image_load(unsigned int image_id) paged_mem_params = get_bl_mem_params_node(BL32_EXTRA2_IMAGE_ID); if (paged_mem_params != NULL) { paged_mem_params->image_info.image_base = STM32MP_DDR_BASE + - (dt_get_ddr_size() - STM32MP_DDR_S_SIZE - - STM32MP_DDR_SHMEM_SIZE); + (dt_get_ddr_size() - STM32MP_DDR_S_SIZE); paged_mem_params->image_info.image_max_size = STM32MP_DDR_S_SIZE; } @@ -467,7 +461,8 @@ int bl2_plat_handle_post_image_load(unsigned int image_id) break; case BL32_IMAGE_ID: - if (optee_header_is_valid(bl_mem_params->image_info.image_base)) { + if ((bl_mem_params->image_info.image_base != 0UL) && + (optee_header_is_valid(bl_mem_params->image_info.image_base))) { image_info_t *paged_image_info = NULL; /* BL32 is OP-TEE header */ @@ -513,7 +508,7 @@ int bl2_plat_handle_post_image_load(unsigned int image_id) assert(bl32_mem_params != NULL); bl32_mem_params->ep_info.lr_svc = bl_mem_params->ep_info.pc; #if PSA_FWU_SUPPORT - stm32mp1_fwu_set_boot_idx(); + stm32_fwu_set_boot_idx(); #endif /* PSA_FWU_SUPPORT */ break; diff --git a/plat/st/stm32mp1/cert_create_tbbr.mk b/plat/st/stm32mp1/cert_create_tbbr.mk index 5b1a3ed9..fb9e5ecf 100644 --- a/plat/st/stm32mp1/cert_create_tbbr.mk +++ b/plat/st/stm32mp1/cert_create_tbbr.mk @@ -11,9 +11,8 @@ $(eval $(call add_define,PDEF_CERTS)) PLAT_INCLUDE += -I${PLAT_DIR}include src/stm32mp1_tbb_cert.o: ${PLAT_DIR}stm32mp1_tbb_cert.c - ${Q}${HOSTCC} -c ${HOSTCCFLAGS} ${INC_DIR} $< -o $@ + $(q)$(host-cc) -c ${HOSTCCFLAGS} ${INC_DIR} $< -o $@ PLAT_OBJECTS = src/stm32mp1_tbb_cert.o OBJECTS += $(PLAT_OBJECTS) - diff --git a/plat/st/stm32mp1/include/platform_def.h b/plat/st/stm32mp1/include/platform_def.h index 75c8219a..7e6d91fe 100644 --- a/plat/st/stm32mp1/include/platform_def.h +++ b/plat/st/stm32mp1/include/platform_def.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015-2023, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2015-2024, ARM Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -77,20 +77,6 @@ ******************************************************************************/ #define BL33_BASE STM32MP_BL33_BASE -/* - * Load address of BL33 for this platform port - */ -#define PLAT_STM32MP_NS_IMAGE_OFFSET BL33_BASE - -/* Needed by STM32CubeProgrammer support */ -#define DWL_BUFFER_SIZE U(0x01000000) - -/* - * SSBL offset in case it's stored in eMMC boot partition. - * We can fix it to 256K because TF-A size can't be bigger than SRAM - */ -#define PLAT_EMMC_BOOT_SSBL_OFFSET U(0x40000) - /******************************************************************************* * DTB specific defines. ******************************************************************************/ diff --git a/plat/st/stm32mp1/include/stm32mp1_private.h b/plat/st/stm32mp1/include/stm32mp1_private.h index 4a522555..55227fb3 100644 --- a/plat/st/stm32mp1/include/stm32mp1_private.h +++ b/plat/st/stm32mp1/include/stm32mp1_private.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015-2023, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2015-2024, Arm Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -14,22 +14,49 @@ void configure_mmu(void); void stm32mp1_arch_security_setup(void); void stm32mp1_security_setup(void); -void stm32mp1_syscfg_init(void); -void stm32mp1_syscfg_enable_io_compensation_start(void); -void stm32mp1_syscfg_enable_io_compensation_finish(void); -void stm32mp1_syscfg_disable_io_compensation(void); -uint32_t stm32mp1_syscfg_get_chip_version(void); -uint32_t stm32mp1_syscfg_get_chip_dev_id(void); +void stm32mp_syscfg_init(void); +void stm32mp_syscfg_enable_io_compensation_start(void); +void stm32mp_syscfg_enable_io_compensation_finish(void); +void stm32mp_syscfg_disable_io_compensation(void); +uint32_t stm32mp_syscfg_get_chip_version(void); +uint32_t stm32mp_syscfg_get_chip_dev_id(void); #if STM32MP13 -void stm32mp1_syscfg_boot_mode_enable(void); -void stm32mp1_syscfg_boot_mode_disable(void); +void stm32mp_syscfg_boot_mode_enable(void); +void stm32mp_syscfg_boot_mode_disable(void); #endif #if STM32MP15 -static inline void stm32mp1_syscfg_boot_mode_enable(void){} -static inline void stm32mp1_syscfg_boot_mode_disable(void){} +static inline void stm32mp_syscfg_boot_mode_enable(void){} +static inline void stm32mp_syscfg_boot_mode_disable(void){} #endif void stm32mp1_deconfigure_uart_pins(void); void stm32mp1_init_scmi_server(void); + +/* Wrappers for OTP / BSEC functions */ +static inline uint32_t stm32_otp_read(uint32_t *val, uint32_t otp) +{ + return bsec_read_otp(val, otp); +} + +static inline uint32_t stm32_otp_shadow_read(uint32_t *val, uint32_t otp) +{ + return bsec_shadow_read_otp(val, otp); +} + +static inline uint32_t stm32_otp_write(uint32_t val, uint32_t otp) +{ + return bsec_write_otp(val, otp); +} + +static inline uint32_t stm32_otp_set_sr_lock(uint32_t otp) +{ + return bsec_set_sr_lock(otp); +} + +static inline uint32_t stm32_otp_read_sw_lock(uint32_t otp, bool *value) +{ + return bsec_read_sw_lock(otp, value); +} + #endif /* STM32MP1_PRIVATE_H */ diff --git a/plat/st/stm32mp1/plat_ddr.c b/plat/st/stm32mp1/plat_ddr.c new file mode 100644 index 00000000..a6a0cdb9 --- /dev/null +++ b/plat/st/stm32mp1/plat_ddr.c @@ -0,0 +1,135 @@ +/* + * Copyright (C) 2024, STMicroelectronics - All Rights Reserved + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <errno.h> + +#include <drivers/st/regulator.h> +#include <drivers/st/stm32mp_ddr.h> +#include <drivers/st/stm32mp_pmic.h> + +/* configure the STPMIC1 regulators on STMicroelectronics boards */ +static int pmic_ddr_power_init(enum ddr_type ddr_type) +{ + int status; + uint16_t buck3_min_mv __maybe_unused; + struct rdev *buck2, *buck3 __maybe_unused, *vref; + struct rdev *ldo3 __maybe_unused; + + buck2 = regulator_get_by_name("buck2"); + if (buck2 == NULL) { + return -ENOENT; + } + +#if STM32MP15 + ldo3 = regulator_get_by_name("ldo3"); + if (ldo3 == NULL) { + return -ENOENT; + } +#endif + + vref = regulator_get_by_name("vref_ddr"); + if (vref == NULL) { + return -ENOENT; + } + + switch (ddr_type) { + case STM32MP_DDR3: +#if STM32MP15 + status = regulator_set_flag(ldo3, REGUL_SINK_SOURCE); + if (status != 0) { + return status; + } +#endif + + status = regulator_set_min_voltage(buck2); + if (status != 0) { + return status; + } + + status = regulator_enable(buck2); + if (status != 0) { + return status; + } + + status = regulator_enable(vref); + if (status != 0) { + return status; + } + +#if STM32MP15 + status = regulator_enable(ldo3); + if (status != 0) { + return status; + } +#endif + break; + + case STM32MP_LPDDR2: + case STM32MP_LPDDR3: +#if STM32MP15 + /* + * Set LDO3 to 1.8V according BUCK3 voltage + * => bypass mode if BUCK3 = 1.8V + * => normal mode if BUCK3 != 1.8V + */ + buck3 = regulator_get_by_name("buck3"); + if (buck3 == NULL) { + return -ENOENT; + } + + regulator_get_range(buck3, &buck3_min_mv, NULL); + + if (buck3_min_mv != 1800) { + status = regulator_set_min_voltage(ldo3); + if (status != 0) { + return status; + } + } else { + status = regulator_set_flag(ldo3, REGUL_ENABLE_BYPASS); + if (status != 0) { + return status; + } + } +#endif + + status = regulator_set_min_voltage(buck2); + if (status != 0) { + return status; + } + +#if STM32MP15 + status = regulator_enable(ldo3); + if (status != 0) { + return status; + } +#endif + + status = regulator_enable(buck2); + if (status != 0) { + return status; + } + + status = regulator_enable(vref); + if (status != 0) { + return status; + } + break; + + default: + break; + }; + + return 0; +} + +int stm32mp_board_ddr_power_init(enum ddr_type ddr_type) +{ + if (dt_pmic_status() > 0) { + return pmic_ddr_power_init(ddr_type); + } + + return 0; +} diff --git a/plat/st/stm32mp1/platform.mk b/plat/st/stm32mp1/platform.mk index ddc52892..3d377389 100644 --- a/plat/st/stm32mp1/platform.mk +++ b/plat/st/stm32mp1/platform.mk @@ -1,9 +1,13 @@ # -# Copyright (c) 2015-2023, Arm Limited and Contributors. All rights reserved. +# Copyright (c) 2015-2024, Arm Limited and Contributors. All rights reserved. # # SPDX-License-Identifier: BSD-3-Clause # +# Extra partitions used to find FIP, contains: +# metadata (2) and the FIP partitions (default is 2). +STM32_EXTRA_PARTS := 4 + include plat/st/common/common.mk ARM_CORTEX_A7 := yes @@ -60,11 +64,6 @@ STM32MP_DDR_32BIT_INTERFACE:= 1 # STM32 image header version v1.0 STM32_HEADER_VERSION_MAJOR:= 1 STM32_HEADER_VERSION_MINOR:= 0 - -# Add OP-TEE reserved shared memory area in mapping -STM32MP15_OPTEE_RSV_SHM := 0 -$(eval $(call add_defines,STM32MP15_OPTEE_RSV_SHM)) - STM32MP_CRYPTO_ROM_LIB := 1 # Decryption support @@ -85,25 +84,6 @@ endif WORKAROUND_CVE_2017_5715:= 0 WORKAROUND_CVE_2022_23960:= 0 -# Number of TF-A copies in the device -STM32_TF_A_COPIES := 2 - -# PLAT_PARTITION_MAX_ENTRIES must take care of STM32_TF-A_COPIES and other partitions -# such as metadata (2) to find all the FIP partitions (default is 2). -PLAT_PARTITION_MAX_ENTRIES := $(shell echo $$(($(STM32_TF_A_COPIES) + 4))) - -ifeq (${PSA_FWU_SUPPORT},1) -# Number of banks of updatable firmware -NR_OF_FW_BANKS := 2 -NR_OF_IMAGES_IN_FW_BANK := 1 - -FWU_MAX_PART = $(shell echo $$(($(STM32_TF_A_COPIES) + 2 + $(NR_OF_FW_BANKS)))) -ifeq ($(shell test $(FWU_MAX_PART) -gt $(PLAT_PARTITION_MAX_ENTRIES); echo $$?),0) -$(error "Required partition number is $(FWU_MAX_PART) where PLAT_PARTITION_MAX_ENTRIES is only \ -$(PLAT_PARTITION_MAX_ENTRIES)") -endif -endif - ifeq ($(STM32MP13),1) STM32_HASH_VER := 4 STM32_RNG_VER := 4 @@ -233,10 +213,6 @@ endif BL2_SOURCES += plat/st/stm32mp1/plat_bl2_mem_params_desc.c \ plat/st/stm32mp1/stm32mp1_fconf_firewall.c -ifeq (${PSA_FWU_SUPPORT},1) -include drivers/fwu/fwu.mk -endif - BL2_SOURCES += drivers/st/crypto/stm32_hash.c \ plat/st/stm32mp1/bl2_plat_setup.c @@ -277,13 +253,15 @@ endif BL2_SOURCES += drivers/st/ddr/stm32mp1_ddr.c \ drivers/st/ddr/stm32mp1_ram.c +BL2_SOURCES += plat/st/stm32mp1/plat_ddr.c + ifeq ($(AARCH32_SP),sp_min) # Create DTB file for BL32 -${BUILD_PLAT}/fdts/%-bl32.dts: fdts/%.dts fdts/${BL32_DTSI} | ${BUILD_PLAT} fdt_dirs - @echo '#include "$(patsubst fdts/%,%,$<)"' > $@ - @echo '#include "${BL32_DTSI}"' >> $@ +${BUILD_PLAT}/fdts/%-bl32.dts: fdts/%.dts fdts/${BL32_DTSI} | $$(@D)/ + $(q)echo '#include "$(patsubst fdts/%,%,$<)"' > $@ + $(q)echo '#include "${BL32_DTSI}"' >> $@ -${BUILD_PLAT}/fdts/%-bl32.dtb: ${BUILD_PLAT}/fdts/%-bl32.dts +${BUILD_PLAT}/fdts/%-bl32.dtb: ${BUILD_PLAT}/fdts/%-bl32.dts | $$(@D)/ endif include plat/st/common/common_rules.mk diff --git a/plat/st/stm32mp1/services/bsec_svc.c b/plat/st/stm32mp1/services/bsec_svc.c index 1fb44b48..7cc00138 100644 --- a/plat/st/stm32mp1/services/bsec_svc.c +++ b/plat/st/stm32mp1/services/bsec_svc.c @@ -1,15 +1,15 @@ /* - * Copyright (c) 2016-2022, STMicroelectronics - All Rights Reserved + * Copyright (c) 2016-2024, STMicroelectronics - All Rights Reserved * * SPDX-License-Identifier: BSD-3-Clause */ -#include <platform_def.h> #include <common/debug.h> #include <drivers/st/bsec.h> #include <drivers/st/bsec2_reg.h> +#include <platform_def.h> #include <stm32mp1_smc.h> #include "bsec_svc.h" @@ -39,12 +39,7 @@ uint32_t bsec_main(uint32_t x1, uint32_t x2, uint32_t x3, break; } - result = bsec_shadow_register(x2); - if (result != BSEC_OK) { - break; - } - - result = bsec_read_otp(ret_otp_value, x2); + result = bsec_shadow_read_otp(ret_otp_value, x2); if (result != BSEC_OK) { break; } diff --git a/plat/st/stm32mp1/sp_min/sp_min_setup.c b/plat/st/stm32mp1/sp_min/sp_min_setup.c index b46f4af4..7bfe6ba5 100644 --- a/plat/st/stm32mp1/sp_min/sp_min_setup.c +++ b/plat/st/stm32mp1/sp_min/sp_min_setup.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015-2023, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2015-2024, ARM Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -116,8 +116,6 @@ void sp_min_early_platform_setup2(u_register_t arg0, u_register_t arg1, bl_params_t *params_from_bl2 = (bl_params_t *)arg0; uintptr_t dt_addr = arg1; - stm32mp_setup_early_console(); - /* Imprecise aborts can be masked in NonSecure */ write_scr(read_scr() | SCR_AW_BIT); @@ -182,6 +180,9 @@ void sp_min_platform_setup(void) stm32mp_gic_init(); + /* Disable MCU subsystem protection */ + stm32mp1_clk_mcuss_protect(false); + if (stm32_iwdg_init() < 0) { panic(); } diff --git a/plat/st/stm32mp1/stm32mp1_def.h b/plat/st/stm32mp1/stm32mp1_def.h index 6530957c..824563fe 100644 --- a/plat/st/stm32mp1/stm32mp1_def.h +++ b/plat/st/stm32mp1/stm32mp1_def.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015-2023, Arm Limited and Contributors. All rights reserved. + * Copyright (c) 2015-2024, Arm Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -10,6 +10,7 @@ #include <common/tbbr/tbbr_img_def.h> #include <drivers/st/stm32mp1_rcc.h> #include <dt-bindings/clock/stm32mp1-clks.h> +#include <dt-bindings/gpio/stm32-gpio.h> #include <dt-bindings/reset/stm32mp1-resets.h> #include <lib/utils_def.h> #include <lib/xlat_tables/xlat_tables_defs.h> @@ -186,9 +187,6 @@ enum ddr_type { #endif #define STM32MP_BL33_MAX_SIZE U(0x400000) -/* Define maximum page size for NAND devices */ -#define PLATFORM_MTD_MAX_PAGE_SIZE U(0x1000) - /* Define location for the MTD scratch buffer */ #if STM32MP13 #define STM32MP_MTD_BUFFER (SRAM1_BASE + \ @@ -234,21 +232,7 @@ enum ddr_type { #endif #define GPIO_BANK_OFFSET U(0x1000) -/* Bank IDs used in GPIO driver API */ -#define GPIO_BANK_A U(0) -#define GPIO_BANK_B U(1) -#define GPIO_BANK_C U(2) -#define GPIO_BANK_D U(3) -#define GPIO_BANK_E U(4) -#define GPIO_BANK_F U(5) -#define GPIO_BANK_G U(6) -#define GPIO_BANK_H U(7) -#define GPIO_BANK_I U(8) #if STM32MP15 -#define GPIO_BANK_J U(9) -#define GPIO_BANK_K U(10) -#define GPIO_BANK_Z U(25) - #define STM32MP_GPIOZ_PIN_MAX_COUNT 8 #endif @@ -426,24 +410,24 @@ enum ddr_type { #define OTP_MAX_SIZE (STM32MP1_OTP_MAX_ID + 1U) /* OTP labels */ -#define CFG0_OTP "cfg0_otp" +#define CFG0_OTP "cfg0-otp" #define PART_NUMBER_OTP "part-number-otp" #if STM32MP15 -#define PACKAGE_OTP "package_otp" +#define PACKAGE_OTP "package-otp" #endif -#define HW2_OTP "hw2_otp" +#define HW2_OTP "hw2-otp" #if STM32MP13 -#define NAND_OTP "cfg9_otp" -#define NAND2_OTP "cfg10_otp" +#define NAND_OTP "cfg9-otp" +#define NAND2_OTP "cfg10-otp" #endif #if STM32MP15 -#define NAND_OTP "nand_otp" +#define NAND_OTP "nand-otp" #endif -#define MONOTONIC_OTP "monotonic_otp" -#define UID_OTP "uid_otp" -#define PKH_OTP "pkh_otp" -#define ENCKEY_OTP "enckey_otp" -#define BOARD_ID_OTP "board_id" +#define MONOTONIC_OTP "monotonic-otp" +#define UID_OTP "uid-otp" +#define PKH_OTP "pkh-otp" +#define ENCKEY_OTP "oem-enc-key" +#define BOARD_ID_OTP "board-id" /* OTP mask */ /* CFG0 */ @@ -636,6 +620,11 @@ static inline uintptr_t tamp_bkpr(uint32_t idx) /* 2 FIXED */ #define PLAT_NB_FIXED_REGUS U(2) +/******************************************************************************* + * STM32MP1 CLOCKS + ******************************************************************************/ +#define PLL1_NOMINAL_FREQ_IN_KHZ U(650000) /* 650MHz */ + /******************************************************************************* * Device Tree defines ******************************************************************************/ diff --git a/plat/st/stm32mp1/stm32mp1_fip_def.h b/plat/st/stm32mp1/stm32mp1_fip_def.h index e37e2e65..165f1523 100644 --- a/plat/st/stm32mp1/stm32mp1_fip_def.h +++ b/plat/st/stm32mp1/stm32mp1_fip_def.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2021-2023, STMicroelectronics - All Rights Reserved + * Copyright (C) 2021-2024, STMicroelectronics - All Rights Reserved * * SPDX-License-Identifier: BSD-3-Clause */ @@ -7,13 +7,7 @@ #ifndef STM32MP1_FIP_DEF_H #define STM32MP1_FIP_DEF_H -#if STM32MP15_OPTEE_RSV_SHM -#define STM32MP_DDR_S_SIZE U(0x01E00000) /* 30 MB */ -#define STM32MP_DDR_SHMEM_SIZE U(0x00200000) /* 2 MB */ -#else #define STM32MP_DDR_S_SIZE U(0x02000000) /* 32 MB */ -#define STM32MP_DDR_SHMEM_SIZE U(0) /* empty */ -#endif #if TRUSTED_BOARD_BOOT && !STM32MP_USE_EXTERNAL_HEAP #if STM32MP15 diff --git a/plat/st/stm32mp1/stm32mp1_pm.c b/plat/st/stm32mp1/stm32mp1_pm.c index ff2218fd..97e1ac61 100644 --- a/plat/st/stm32mp1/stm32mp1_pm.c +++ b/plat/st/stm32mp1/stm32mp1_pm.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015-2023, Arm Limited and Contributors. All rights reserved. + * Copyright (c) 2015-2024, Arm Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -13,6 +13,7 @@ #include <drivers/arm/gic_common.h> #include <drivers/arm/gicv2.h> #include <drivers/clk.h> +#include <drivers/st/stm32mp_reset.h> #include <dt-bindings/clock/stm32mp1-clks.h> #include <lib/mmio.h> #include <lib/psci/psci.h> @@ -149,13 +150,7 @@ static void __dead2 stm32_system_off(void) static void __dead2 stm32_system_reset(void) { - mmio_setbits_32(stm32mp_rcc_base() + RCC_MP_GRSTCSETR, - RCC_MP_GRSTCSETR_MPSYSRST); - - /* Loop in case system reset is not immediately caught */ - for ( ; ; ) { - ; - } + stm32mp_system_reset(); } static int stm32_validate_power_state(unsigned int power_state, diff --git a/plat/st/stm32mp1/stm32mp1_private.c b/plat/st/stm32mp1/stm32mp1_private.c index ea35055a..45446dc6 100644 --- a/plat/st/stm32mp1/stm32mp1_private.c +++ b/plat/st/stm32mp1/stm32mp1_private.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015-2023, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2015-2024, Arm Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -29,10 +29,6 @@ * (so it should be in Zone 2). */ #define TAMP_BOOT_FWU_INFO_REG_ID U(10) -#define TAMP_BOOT_FWU_INFO_IDX_MSK GENMASK(3, 0) -#define TAMP_BOOT_FWU_INFO_IDX_OFF U(0) -#define TAMP_BOOT_FWU_INFO_CNT_MSK GENMASK(7, 4) -#define TAMP_BOOT_FWU_INFO_CNT_OFF U(4) #if defined(IMAGE_BL2) #define MAP_SEC_SYSRAM MAP_REGION_FLAT(STM32MP_SYSRAM_BASE, \ @@ -115,14 +111,14 @@ void configure_mmu(void) uintptr_t stm32_get_gpio_bank_base(unsigned int bank) { #if STM32MP13 - assert((GPIO_BANK_A == 0) && (bank <= GPIO_BANK_I)); + assert(bank <= GPIO_BANK_I); #endif #if STM32MP15 if (bank == GPIO_BANK_Z) { return GPIOZ_BASE; } - assert((GPIO_BANK_A == 0) && (bank <= GPIO_BANK_K)); + assert(bank <= GPIO_BANK_K); #endif return GPIOA_BASE + (bank * GPIO_BANK_OFFSET); @@ -131,14 +127,14 @@ uintptr_t stm32_get_gpio_bank_base(unsigned int bank) uint32_t stm32_get_gpio_bank_offset(unsigned int bank) { #if STM32MP13 - assert((GPIO_BANK_A == 0) && (bank <= GPIO_BANK_I)); + assert(bank <= GPIO_BANK_I); #endif #if STM32MP15 if (bank == GPIO_BANK_Z) { return 0; } - assert((GPIO_BANK_A == 0) && (bank <= GPIO_BANK_K)); + assert(bank <= GPIO_BANK_K); #endif return bank * GPIO_BANK_OFFSET; @@ -161,14 +157,14 @@ bool stm32_gpio_is_secure_at_reset(unsigned int bank) unsigned long stm32_get_gpio_bank_clock(unsigned int bank) { #if STM32MP13 - assert((GPIO_BANK_A == 0) && (bank <= GPIO_BANK_I)); + assert(bank <= GPIO_BANK_I); #endif #if STM32MP15 if (bank == GPIO_BANK_Z) { return GPIOZ; } - assert((GPIO_BANK_A == 0) && (bank <= GPIO_BANK_K)); + assert(bank <= GPIO_BANK_K); #endif return GPIOA + (bank - GPIO_BANK_A); @@ -284,7 +280,7 @@ void stm32mp1_deconfigure_uart_pins(void) uint32_t stm32mp_get_chip_version(void) { #if STM32MP13 - return stm32mp1_syscfg_get_chip_version(); + return stm32mp_syscfg_get_chip_version(); #endif #if STM32MP15 uint32_t version = 0U; @@ -301,7 +297,7 @@ uint32_t stm32mp_get_chip_version(void) uint32_t stm32mp_get_chip_dev_id(void) { #if STM32MP13 - return stm32mp1_syscfg_get_chip_dev_id(); + return stm32mp_syscfg_get_chip_dev_id(); #endif #if STM32MP15 uint32_t dev_id; @@ -531,12 +527,12 @@ bool stm32mp_is_single_core(void) } /* Return true when device is in closed state */ -bool stm32mp_is_closed_device(void) +uint32_t stm32mp_check_closed_device(void) { uint32_t value; if (stm32_get_otp_value(CFG0_OTP, &value) != 0) { - return true; + return STM32MP_CHIP_SEC_CLOSED; } #if STM32MP13 @@ -544,17 +540,22 @@ bool stm32mp_is_closed_device(void) switch (value) { case CFG0_OPEN_DEVICE: - return false; + return STM32MP_CHIP_SEC_OPEN; case CFG0_CLOSED_DEVICE: case CFG0_CLOSED_DEVICE_NO_BOUNDARY_SCAN: case CFG0_CLOSED_DEVICE_NO_JTAG: - return true; + return STM32MP_CHIP_SEC_CLOSED; default: panic(); } #endif #if STM32MP15 - return (value & CFG0_CLOSED_DEVICE) == CFG0_CLOSED_DEVICE; + if ((value & CFG0_CLOSED_DEVICE) == CFG0_CLOSED_DEVICE) { + return STM32MP_CHIP_SEC_CLOSED; + } else { + return STM32MP_CHIP_SEC_OPEN; + } + #endif } @@ -663,50 +664,20 @@ uint32_t stm32_iwdg_shadow_update(uint32_t iwdg_inst, uint32_t flags) } #endif -uintptr_t stm32_get_bkpr_boot_mode_addr(void) -{ - return tamp_bkpr(TAMP_BOOT_MODE_BACKUP_REG_ID); -} - -#if PSA_FWU_SUPPORT -void stm32mp1_fwu_set_boot_idx(void) +bool stm32mp_is_wakeup_from_standby(void) { - clk_enable(RTCAPB); - mmio_clrsetbits_32(tamp_bkpr(TAMP_BOOT_FWU_INFO_REG_ID), - TAMP_BOOT_FWU_INFO_IDX_MSK, - (plat_fwu_get_boot_idx() << TAMP_BOOT_FWU_INFO_IDX_OFF) & - TAMP_BOOT_FWU_INFO_IDX_MSK); - clk_disable(RTCAPB); + /* TODO add source code to determine if platform is waking up from standby mode */ + return false; } -uint32_t stm32_get_and_dec_fwu_trial_boot_cnt(void) +uintptr_t stm32_get_bkpr_boot_mode_addr(void) { - uintptr_t bkpr_fwu_cnt = tamp_bkpr(TAMP_BOOT_FWU_INFO_REG_ID); - uint32_t try_cnt; - - clk_enable(RTCAPB); - try_cnt = (mmio_read_32(bkpr_fwu_cnt) & TAMP_BOOT_FWU_INFO_CNT_MSK) >> - TAMP_BOOT_FWU_INFO_CNT_OFF; - - assert(try_cnt <= FWU_MAX_TRIAL_REBOOT); - - if (try_cnt != 0U) { - mmio_clrsetbits_32(bkpr_fwu_cnt, TAMP_BOOT_FWU_INFO_CNT_MSK, - (try_cnt - 1U) << TAMP_BOOT_FWU_INFO_CNT_OFF); - } - clk_disable(RTCAPB); - - return try_cnt; + return tamp_bkpr(TAMP_BOOT_MODE_BACKUP_REG_ID); } -void stm32_set_max_fwu_trial_boot_cnt(void) +#if PSA_FWU_SUPPORT +uintptr_t stm32_get_bkpr_fwu_info_addr(void) { - uintptr_t bkpr_fwu_cnt = tamp_bkpr(TAMP_BOOT_FWU_INFO_REG_ID); - - clk_enable(RTCAPB); - mmio_clrsetbits_32(bkpr_fwu_cnt, TAMP_BOOT_FWU_INFO_CNT_MSK, - (FWU_MAX_TRIAL_REBOOT << TAMP_BOOT_FWU_INFO_CNT_OFF) & - TAMP_BOOT_FWU_INFO_CNT_MSK); - clk_disable(RTCAPB); + return tamp_bkpr(TAMP_BOOT_FWU_INFO_REG_ID); } #endif /* PSA_FWU_SUPPORT */ diff --git a/plat/st/stm32mp1/stm32mp1_syscfg.c b/plat/st/stm32mp1/stm32mp1_syscfg.c index 75dd7093..199bdc9c 100644 --- a/plat/st/stm32mp1/stm32mp1_syscfg.c +++ b/plat/st/stm32mp1/stm32mp1_syscfg.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019-2022, STMicroelectronics - All Rights Reserved + * Copyright (c) 2019-2024, STMicroelectronics - All Rights Reserved * * SPDX-License-Identifier: BSD-3-Clause */ @@ -261,7 +261,7 @@ static void enable_high_speed_mode_low_voltage(void) #endif } -static void stm32mp1_syscfg_set_hslv(void) +static void stm32mp_syscfg_set_hslv(void) { uint32_t otp_value; uint32_t vdd_voltage; @@ -310,7 +310,7 @@ static void stm32mp1_syscfg_set_hslv(void) } } -void stm32mp1_syscfg_init(void) +void stm32mp_syscfg_init(void) { #if STM32MP15 uint32_t bootr; @@ -328,12 +328,12 @@ void stm32mp1_syscfg_init(void) bootr << SYSCFG_BOOTR_BOOTPD_SHIFT); #endif - stm32mp1_syscfg_set_hslv(); + stm32mp_syscfg_set_hslv(); - stm32mp1_syscfg_enable_io_compensation_start(); + stm32mp_syscfg_enable_io_compensation_start(); } -void stm32mp1_syscfg_enable_io_compensation_start(void) +void stm32mp_syscfg_enable_io_compensation_start(void) { /* * Activate automatic I/O compensation. @@ -353,7 +353,7 @@ void stm32mp1_syscfg_enable_io_compensation_start(void) #endif } -void stm32mp1_syscfg_enable_io_compensation_finish(void) +void stm32mp_syscfg_enable_io_compensation_finish(void) { enable_io_comp_cell_finish(SYSCFG_CMPCR); #if STM32MP13 @@ -362,7 +362,7 @@ void stm32mp1_syscfg_enable_io_compensation_finish(void) #endif } -void stm32mp1_syscfg_disable_io_compensation(void) +void stm32mp_syscfg_disable_io_compensation(void) { clk_enable(SYSCFG); @@ -385,7 +385,7 @@ void stm32mp1_syscfg_disable_io_compensation(void) * @brief Get silicon revision from SYSCFG registers. * @retval chip version (REV_ID). */ -uint32_t stm32mp1_syscfg_get_chip_version(void) +uint32_t stm32mp_syscfg_get_chip_version(void) { return (mmio_read_32(SYSCFG_BASE + SYSCFG_IDC) & SYSCFG_IDC_REV_ID_MASK) >> SYSCFG_IDC_REV_ID_SHIFT; @@ -395,18 +395,18 @@ uint32_t stm32mp1_syscfg_get_chip_version(void) * @brief Get device ID from SYSCFG registers. * @retval device ID (DEV_ID). */ -uint32_t stm32mp1_syscfg_get_chip_dev_id(void) +uint32_t stm32mp_syscfg_get_chip_dev_id(void) { return mmio_read_32(SYSCFG_BASE + SYSCFG_IDC) & SYSCFG_IDC_DEV_ID_MASK; } #if STM32MP13 -void stm32mp1_syscfg_boot_mode_enable(void) +void stm32mp_syscfg_boot_mode_enable(void) { mmio_setbits_32(SYSCFG_BASE + SYSCFG_BOOTCR, SYSCFG_BOOTCR_BMEN); } -void stm32mp1_syscfg_boot_mode_disable(void) +void stm32mp_syscfg_boot_mode_disable(void) { mmio_clrbits_32(SYSCFG_BASE + SYSCFG_BOOTCR, SYSCFG_BOOTCR_BMEN); } diff --git a/plat/st/stm32mp2/aarch64/stm32mp2_helper.S b/plat/st/stm32mp2/aarch64/stm32mp2_helper.S index 66333ad7..0df3e088 100644 --- a/plat/st/stm32mp2/aarch64/stm32mp2_helper.S +++ b/plat/st/stm32mp2/aarch64/stm32mp2_helper.S @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023, STMicroelectronics - All Rights Reserved + * Copyright (c) 2023-2024, STMicroelectronics - All Rights Reserved * * SPDX-License-Identifier: BSD-3-Clause */ @@ -14,6 +14,7 @@ .globl platform_mem_init .globl plat_secondary_cold_boot_setup .globl plat_is_my_cpu_primary + .globl plat_my_core_pos .globl plat_crash_console_init .globl plat_crash_console_flush .globl plat_crash_console_putc @@ -32,9 +33,14 @@ endfunc platform_mem_init */ func plat_secondary_cold_boot_setup dsb sy +1: wfi - /* This shouldn't be reached */ - b . + /* + * This shouldn't be reached, but when a debugger halts the + * secondary core it causes exit from wfi. + * Put back the core in wfi. + */ + b 1b endfunc plat_secondary_cold_boot_setup /* ---------------------------------------------- @@ -50,6 +56,31 @@ func plat_is_my_cpu_primary ret endfunc plat_is_my_cpu_primary + /* ----------------------------------------------------------- + * unsigned int plat_stm32mp_get_core_pos(u_register_t mpidr) + * Helper function to calculate the core position. + * With this function: CorePos = (ClusterId * 4) + + * CoreId + * ----------------------------------------------------------- + */ +func plat_stm32mp_get_core_pos + and x1, x0, #MPIDR_CPU_MASK + and x0, x0, #MPIDR_CLUSTER_MASK + add x0, x1, x0, LSR #6 + ret +endfunc plat_stm32mp_get_core_pos + + /* ----------------------------------------------------- + * unsigned int plat_my_core_pos(void) + * This function uses the plat_stm32mp_get_core_pos() + * definition to get the index of the calling CPU. + * ----------------------------------------------------- + */ +func plat_my_core_pos + mrs x0, mpidr_el1 + b plat_stm32mp_get_core_pos +endfunc plat_my_core_pos + /* --------------------------------------------- * int plat_crash_console_init(void) * @@ -65,13 +96,13 @@ func plat_crash_console_init str x0, [x1] 1: ldr x0, [x1] - ands x2, x0, x2 + tst x0, #DEBUG_UART_RST_BIT beq 1b - bic x2, x2, #DEBUG_UART_RST_BIT - str x2, [x1] + bic x0, x0, #DEBUG_UART_RST_BIT + str x0, [x1] 2: ldr x0, [x1] - ands x2, x0, x2 + tst x0, #DEBUG_UART_RST_BIT bne 2b /* Enable GPIOs for UART TX */ mov_imm x1, (RCC_BASE + DEBUG_UART_TX_GPIO_BANK_CLK_REG) diff --git a/plat/st/stm32mp2/bl2_plat_setup.c b/plat/st/stm32mp2/bl2_plat_setup.c index 08057568..2fabc418 100644 --- a/plat/st/stm32mp2/bl2_plat_setup.c +++ b/plat/st/stm32mp2/bl2_plat_setup.c @@ -1,26 +1,394 @@ /* - * Copyright (c) 2023, STMicroelectronics - All Rights Reserved + * Copyright (c) 2023-2024, STMicroelectronics - All Rights Reserved * * SPDX-License-Identifier: BSD-3-Clause */ +#include <assert.h> #include <cdefs.h> +#include <errno.h> #include <stdint.h> +#include <common/debug.h> +#include <common/desc_image_load.h> +#include <drivers/clk.h> +#include <drivers/mmc.h> +#include <drivers/st/regulator_fixed.h> +#include <drivers/st/stm32mp2_ddr_helpers.h> +#include <drivers/st/stm32mp2_ram.h> +#include <drivers/st/stm32mp_pmic2.h> +#include <drivers/st/stm32mp_risab_regs.h> +#include <lib/fconf/fconf.h> +#include <lib/fconf/fconf_dyn_cfg_getter.h> +#include <lib/mmio.h> +#include <lib/optee_utils.h> +#include <lib/xlat_tables/xlat_tables_v2.h> +#include <plat/common/platform.h> + +#include <platform_def.h> #include <stm32mp_common.h> +#include <stm32mp_dt.h> + +#define BOOT_CTX_ADDR 0x0e000020UL + +static void print_reset_reason(void) +{ + uint32_t rstsr = mmio_read_32(stm32mp_rcc_base() + RCC_C1BOOTRSTSCLRR); + + if (rstsr == 0U) { + WARN("Reset reason unknown\n"); + return; + } + + INFO("Reset reason (0x%x):\n", rstsr); + + if ((rstsr & RCC_C1BOOTRSTSCLRR_PADRSTF) == 0U) { + if ((rstsr & RCC_C1BOOTRSTSCLRR_STBYC1RSTF) != 0U) { + INFO("System exits from Standby for CA35\n"); + return; + } + + if ((rstsr & RCC_C1BOOTRSTSCLRR_D1STBYRSTF) != 0U) { + INFO("D1 domain exits from DStandby\n"); + return; + } + } + + if ((rstsr & RCC_C1BOOTRSTSCLRR_PORRSTF) != 0U) { + INFO(" Power-on Reset (rst_por)\n"); + return; + } + + if ((rstsr & RCC_C1BOOTRSTSCLRR_BORRSTF) != 0U) { + INFO(" Brownout Reset (rst_bor)\n"); + return; + } + + if ((rstsr & RCC_C1BOOTRSTSSETR_SYSC2RSTF) != 0U) { + INFO(" System reset (SYSRST) by M33\n"); + return; + } + + if ((rstsr & RCC_C1BOOTRSTSSETR_SYSC1RSTF) != 0U) { + INFO(" System reset (SYSRST) by A35\n"); + return; + } + + if ((rstsr & RCC_C1BOOTRSTSCLRR_HCSSRSTF) != 0U) { + INFO(" Clock failure on HSE\n"); + return; + } + + if ((rstsr & RCC_C1BOOTRSTSCLRR_IWDG1SYSRSTF) != 0U) { + INFO(" IWDG1 system reset (rst_iwdg1)\n"); + return; + } + + if ((rstsr & RCC_C1BOOTRSTSCLRR_IWDG2SYSRSTF) != 0U) { + INFO(" IWDG2 system reset (rst_iwdg2)\n"); + return; + } + + if ((rstsr & RCC_C1BOOTRSTSCLRR_IWDG3SYSRSTF) != 0U) { + INFO(" IWDG3 system reset (rst_iwdg3)\n"); + return; + } + + if ((rstsr & RCC_C1BOOTRSTSCLRR_IWDG4SYSRSTF) != 0U) { + INFO(" IWDG4 system reset (rst_iwdg4)\n"); + return; + } + + if ((rstsr & RCC_C1BOOTRSTSCLRR_IWDG5SYSRSTF) != 0U) { + INFO(" IWDG5 system reset (rst_iwdg5)\n"); + return; + } + + if ((rstsr & RCC_C1BOOTRSTSCLRR_C1P1RSTF) != 0U) { + INFO(" A35 processor core 1 reset\n"); + return; + } + + if ((rstsr & RCC_C1BOOTRSTSCLRR_PADRSTF) != 0U) { + INFO(" Pad Reset from NRST\n"); + return; + } + + if ((rstsr & RCC_C1BOOTRSTSCLRR_VCORERSTF) != 0U) { + INFO(" Reset due to a failure of VDD_CORE\n"); + return; + } + + if ((rstsr & RCC_C1BOOTRSTSCLRR_C1RSTF) != 0U) { + INFO(" A35 processor reset\n"); + return; + } + + ERROR(" Unidentified reset reason\n"); +} void bl2_el3_early_platform_setup(u_register_t arg0 __unused, u_register_t arg1 __unused, u_register_t arg2 __unused, u_register_t arg3 __unused) { - stm32mp_setup_early_console(); + stm32mp_save_boot_ctx_address(BOOT_CTX_ADDR); } void bl2_platform_setup(void) { + int ret; + + ret = stm32mp2_ddr_probe(); + if (ret != 0) { + ERROR("DDR probe: error %d\n", ret); + panic(); + } + + /* Map DDR for binary load, now with cacheable attribute */ + ret = mmap_add_dynamic_region(STM32MP_DDR_BASE, STM32MP_DDR_BASE, + STM32MP_DDR_MAX_SIZE, MT_MEMORY | MT_RW | MT_SECURE); + if (ret < 0) { + ERROR("DDR mapping: error %d\n", ret); + panic(); + } +} + +static void reset_backup_domain(void) +{ + uintptr_t pwr_base = stm32mp_pwr_base(); + uintptr_t rcc_base = stm32mp_rcc_base(); + + /* + * Disable the backup domain write protection. + * The protection is enable at each reset by hardware + * and must be disabled by software. + */ + mmio_setbits_32(pwr_base + PWR_BDCR1, PWR_BDCR1_DBD3P); + + while ((mmio_read_32(pwr_base + PWR_BDCR1) & PWR_BDCR1_DBD3P) == 0U) { + ; + } + + /* Reset backup domain on cold boot cases */ + if ((mmio_read_32(rcc_base + RCC_BDCR) & RCC_BDCR_RTCCKEN) == 0U) { + mmio_setbits_32(rcc_base + RCC_BDCR, RCC_BDCR_VSWRST); + + while ((mmio_read_32(rcc_base + RCC_BDCR) & RCC_BDCR_VSWRST) == 0U) { + ; + } + + mmio_clrbits_32(rcc_base + RCC_BDCR, RCC_BDCR_VSWRST); + } } void bl2_el3_plat_arch_setup(void) { + const char *board_model; + boot_api_context_t *boot_context = + (boot_api_context_t *)stm32mp_get_boot_ctx_address(); + + if (stm32_otp_probe() != 0U) { + EARLY_ERROR("OTP probe failed\n"); + panic(); + } + + mmap_add_region(BL_CODE_BASE, BL_CODE_BASE, + BL_CODE_END - BL_CODE_BASE, + MT_CODE | MT_SECURE); + + configure_mmu(); + + if (dt_open_and_check(STM32MP_DTB_BASE) < 0) { + panic(); + } + + reset_backup_domain(); + + /* + * Initialize DDR sub-system clock. This needs to be done before enabling DDR PLL (PLL2), + * and so before stm32mp2_clk_init(). + */ + ddr_sub_system_clk_init(); + + if (stm32mp2_clk_init() < 0) { + panic(); + } + +#if STM32MP_DDR_FIP_IO_STORAGE + /* + * RISAB3 setup (dedicated for SRAM1) + * + * Allow secure read/writes data accesses to non-secure + * blocks or pages, all RISAB registers are writable. + * DDR firmwares are saved there before being loaded in DDRPHY memory. + */ + mmio_write_32(RISAB3_BASE + RISAB_CR, RISAB_CR_SRWIAD); +#endif + + stm32_save_boot_info(boot_context); + + if (stm32mp_uart_console_setup() != 0) { + goto skip_console_init; + } + + stm32mp_print_cpuinfo(); + + board_model = dt_get_board_model(); + if (board_model != NULL) { + NOTICE("Model: %s\n", board_model); + } + + stm32mp_print_boardinfo(); + + print_reset_reason(); + +skip_console_init: + if (fixed_regulator_register() != 0) { + panic(); + } + + if (dt_pmic_status() > 0) { + initialize_pmic(); + } + + fconf_populate("TB_FW", STM32MP_DTB_BASE); + + /* + * RISAB5 setup (dedicated for RETRAM) + * + * Allow secure read/writes data accesses to non-secure + * blocks or pages, all RISAB registers are writable. + * DDR retention registers are saved there and restored + * when exiting standby low power state. + */ + mmio_write_32(RISAB5_BASE + RISAB_CR, RISAB_CR_SRWIAD); + + stm32mp_io_setup(); +} + +/******************************************************************************* + * This function can be used by the platforms to update/use image + * information for given `image_id`. + ******************************************************************************/ +int bl2_plat_handle_post_image_load(unsigned int image_id) +{ + int err = 0; + bl_mem_params_node_t *bl_mem_params = get_bl_mem_params_node(image_id); + bl_mem_params_node_t *pager_mem_params; + const struct dyn_cfg_dtb_info_t *config_info; + unsigned int i; + const unsigned int image_ids[] = { + BL31_IMAGE_ID, + SOC_FW_CONFIG_ID, + BL32_IMAGE_ID, + BL33_IMAGE_ID, + HW_CONFIG_ID, + }; + + assert(bl_mem_params != NULL); + +#if STM32MP_SDMMC || STM32MP_EMMC + /* + * Invalidate remaining data read from MMC but not flushed by load_image_flush(). + * We take the worst case which is 2 MMC blocks. + */ + if ((image_id != FW_CONFIG_ID) && + ((bl_mem_params->image_info.h.attr & IMAGE_ATTRIB_SKIP_LOADING) == 0U)) { + inv_dcache_range(bl_mem_params->image_info.image_base + + bl_mem_params->image_info.image_size, + 2U * MMC_BLOCK_SIZE); + } +#endif /* STM32MP_SDMMC || STM32MP_EMMC */ + + switch (image_id) { + case FW_CONFIG_ID: + /* Set global DTB info for fixed fw_config information */ + set_config_info(STM32MP_FW_CONFIG_BASE, ~0UL, STM32MP_FW_CONFIG_MAX_SIZE, + FW_CONFIG_ID); + fconf_populate("FW_CONFIG", STM32MP_FW_CONFIG_BASE); + + /* Iterate through all the fw config IDs */ + for (i = 0U; i < ARRAY_SIZE(image_ids); i++) { + bl_mem_params = get_bl_mem_params_node(image_ids[i]); + assert(bl_mem_params != NULL); + + config_info = FCONF_GET_PROPERTY(dyn_cfg, dtb, image_ids[i]); + if (config_info == NULL) { + continue; + } + + bl_mem_params->image_info.image_base = config_info->config_addr; + bl_mem_params->image_info.image_max_size = config_info->config_max_size; + + bl_mem_params->image_info.h.attr &= ~IMAGE_ATTRIB_SKIP_LOADING; + + switch (image_ids[i]) { + case BL31_IMAGE_ID: + bl_mem_params->ep_info.pc = config_info->config_addr; + break; + + case BL32_IMAGE_ID: + bl_mem_params->ep_info.pc = config_info->config_addr; + + /* In case of OPTEE, initialize address space with tos_fw addr */ + pager_mem_params = get_bl_mem_params_node(BL32_EXTRA1_IMAGE_ID); + if (pager_mem_params != NULL) { + pager_mem_params->image_info.image_base = + config_info->config_addr; + pager_mem_params->image_info.image_max_size = + config_info->config_max_size; + } + break; + + case BL33_IMAGE_ID: + bl_mem_params->ep_info.pc = config_info->config_addr; + break; + + case HW_CONFIG_ID: + case SOC_FW_CONFIG_ID: + break; + + default: + return -EINVAL; + } + } + + /* + * After this step, the BL2 device tree area will be overwritten + * with BL31 binary, no other data should be read from BL2 DT. + */ + + break; + + case BL32_IMAGE_ID: + if ((bl_mem_params->image_info.image_base != 0UL) && + (optee_header_is_valid(bl_mem_params->image_info.image_base))) { + /* BL32 is OP-TEE header */ + bl_mem_params->ep_info.pc = bl_mem_params->image_info.image_base; + pager_mem_params = get_bl_mem_params_node(BL32_EXTRA1_IMAGE_ID); + assert(pager_mem_params != NULL); + + err = parse_optee_header(&bl_mem_params->ep_info, + &pager_mem_params->image_info, + NULL); + if (err != 0) { + ERROR("OPTEE header parse error.\n"); + panic(); + } + + /* Set optee boot info from parsed header data */ + bl_mem_params->ep_info.args.arg0 = 0U; /* Unused */ + bl_mem_params->ep_info.args.arg1 = 0U; /* Unused */ + bl_mem_params->ep_info.args.arg2 = 0U; /* No DT supported */ + } + break; + + case BL33_IMAGE_ID: + default: + /* Do nothing in default case */ + break; + } + + return err; } diff --git a/plat/st/stm32mp2/bl31_plat_setup.c b/plat/st/stm32mp2/bl31_plat_setup.c new file mode 100644 index 00000000..a7a3721b --- /dev/null +++ b/plat/st/stm32mp2/bl31_plat_setup.c @@ -0,0 +1,159 @@ +/* + * Copyright (c) 2023-2024, STMicroelectronics - All Rights Reserved + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <assert.h> +#include <stdint.h> + +#include <common/bl_common.h> +#include <drivers/generic_delay_timer.h> +#include <drivers/st/stm32_console.h> +#include <lib/xlat_tables/xlat_tables_v2.h> +#include <plat/common/platform.h> + +#include <platform_def.h> + +static entry_point_info_t bl32_image_ep_info; +static entry_point_info_t bl33_image_ep_info; + +void bl31_early_platform_setup2(u_register_t arg0, u_register_t arg1, + u_register_t arg2, u_register_t arg3) +{ + bl_params_t *params_from_bl2; + int ret; + + /* + * Invalidate remaining data from second half of SYSRAM (used by BL2) as this area will + * be later used as non-secure. + */ + inv_dcache_range(STM32MP_SYSRAM_BASE + STM32MP_SYSRAM_SIZE / 2U, + STM32MP_SYSRAM_SIZE / 2U); + + mmap_add_region(BL_CODE_BASE, BL_CODE_BASE, + BL_CODE_END - BL_CODE_BASE, + MT_CODE | MT_SECURE); + + /* + * Map soc_fw_config device tree with secure property, i.e. default region. + * DDR region definitions will be finalized at BL32 level. + */ + mmap_add_region(arg1, arg1, STM32MP_SOC_FW_CONFIG_MAX_SIZE, MT_RO_DATA | MT_SECURE); + +#if USE_COHERENT_MEM + /* Map coherent memory */ + mmap_add_region(BL_COHERENT_RAM_BASE, BL_COHERENT_RAM_BASE, + BL_COHERENT_RAM_END - BL_COHERENT_RAM_BASE, + MT_DEVICE | MT_RW | MT_SECURE); +#endif + + configure_mmu(); + + ret = dt_open_and_check(arg1); + if (ret < 0) { + EARLY_ERROR("%s: failed to open DT (%d)\n", __func__, ret); + panic(); + } + + ret = stm32mp2_clk_init(); + if (ret < 0) { + EARLY_ERROR("%s: failed init clocks (%d)\n", __func__, ret); + panic(); + } + + generic_delay_timer_init(); + + (void)stm32mp_uart_console_setup(); + + /* + * Map upper SYSRAM where bl_params_t are stored in BL2 + */ + ret = mmap_add_dynamic_region(STM32MP_SYSRAM_BASE + STM32MP_SYSRAM_SIZE / 2U, + STM32MP_SYSRAM_BASE + STM32MP_SYSRAM_SIZE / 2U, + STM32MP_SYSRAM_SIZE / 2U, MT_RO_DATA | MT_SECURE); + if (ret < 0) { + ERROR("BL2 params area mapping: %d\n", ret); + panic(); + } + + assert(arg0 != 0UL); + params_from_bl2 = (bl_params_t *)arg0; + assert(params_from_bl2 != NULL); + assert(params_from_bl2->h.type == PARAM_BL_PARAMS); + assert(params_from_bl2->h.version >= VERSION_2); + + bl_params_node_t *bl_params = params_from_bl2->head; + + while (bl_params != NULL) { + /* + * Copy BL33 entry point information. + * They are stored in Secure RAM, in BL2's address space. + */ + if (bl_params->image_id == BL33_IMAGE_ID) { + bl33_image_ep_info = *bl_params->ep_info; + /* + * Check if hw_configuration is given to BL32 and + * share it to BL33 + */ + if (arg2 != 0U) { + bl33_image_ep_info.args.arg0 = 0U; + bl33_image_ep_info.args.arg1 = 0U; + bl33_image_ep_info.args.arg2 = arg2; + } + } + + if (bl_params->image_id == BL32_IMAGE_ID) { + bl32_image_ep_info = *bl_params->ep_info; + + if (arg2 != 0U) { + bl32_image_ep_info.args.arg3 = arg2; + } + } + + bl_params = bl_params->next_params_info; + } + + ret = mmap_remove_dynamic_region(STM32MP_SYSRAM_BASE + STM32MP_SYSRAM_SIZE / 2U, + STM32MP_SYSRAM_SIZE / 2U); + if (ret < 0) { + ERROR("BL2 params area unmapping: %d\n", ret); + panic(); + } +} + +void bl31_plat_arch_setup(void) +{ + stm32mp_gic_init(); +} + +void bl31_platform_setup(void) +{ +} + +entry_point_info_t *bl31_plat_get_next_image_ep_info(unsigned int type) +{ + entry_point_info_t *next_image_info = NULL; + + assert(sec_state_is_valid(type)); + + switch (type) { + case NON_SECURE: + next_image_info = &bl33_image_ep_info; + break; + + case SECURE: + next_image_info = &bl32_image_ep_info; + break; + + default: + break; + } + + /* None of the next images on ST platforms can have 0x0 as the entrypoint */ + if ((next_image_info == NULL) || (next_image_info->pc == 0UL)) { + return NULL; + } + + return next_image_info; +} diff --git a/plat/st/stm32mp2/include/boot_api.h b/plat/st/stm32mp2/include/boot_api.h index d3bed763..580a65b5 100644 --- a/plat/st/stm32mp2/include/boot_api.h +++ b/plat/st/stm32mp2/include/boot_api.h @@ -86,7 +86,7 @@ /* Image Header related definitions */ /* Definition of header version */ -#define BOOT_API_HEADER_VERSION 0x00020000U +#define BOOT_API_HEADER_VERSION 0x00020200U /* * Magic number used to detect header in memory diff --git a/plat/st/stm32mp2/include/plat_tbbr_img_def.h b/plat/st/stm32mp2/include/plat_tbbr_img_def.h new file mode 100644 index 00000000..830bf88b --- /dev/null +++ b/plat/st/stm32mp2/include/plat_tbbr_img_def.h @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2024, STMicroelectronics - All Rights Reserved + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef PLAT_TBBR_IMG_DEF_H +#define PLAT_TBBR_IMG_DEF_H + +#include <export/common/tbbr/tbbr_img_def_exp.h> + +/* Undef the existing values */ +#undef BKUP_FWU_METADATA_IMAGE_ID +#undef FWU_METADATA_IMAGE_ID +#undef FW_CONFIG_ID +#undef ENC_IMAGE_ID +#undef GPT_IMAGE_ID +#undef NT_FW_CONFIG_ID +#undef SOC_FW_CONFIG_ID +#undef TB_FW_CONFIG_ID +#undef HW_CONFIG_ID +#undef TRUSTED_BOOT_FW_CERT_ID +#undef SOC_FW_CONTENT_CERT_ID +#undef BL32_EXTRA1_IMAGE_ID +#undef TOS_FW_CONFIG_ID + +/* Define the STM32MP2 used ID */ +#define FW_CONFIG_ID U(1) +#define HW_CONFIG_ID U(2) +#define ENC_IMAGE_ID U(6) +#define BL32_EXTRA1_IMAGE_ID U(8) +#define FWU_METADATA_IMAGE_ID U(12) +#define BKUP_FWU_METADATA_IMAGE_ID U(13) +#define TOS_FW_CONFIG_ID U(16) +#define NT_FW_CONFIG_ID U(18) +#define SOC_FW_CONFIG_ID U(19) +#define TB_FW_CONFIG_ID U(20) +#define TRUSTED_BOOT_FW_CERT_ID U(21) +#define SOC_FW_CONTENT_CERT_ID U(23) +#define STM32MP_CONFIG_CERT_ID U(24) +#define GPT_IMAGE_ID U(25) + +#if STM32MP_DDR_FIP_IO_STORAGE +#define DDR_FW_ID U(26) +/* Increase the MAX_NUMBER_IDS to match the authentication pool required */ +#define MAX_NUMBER_IDS U(27) + +#else +/* Increase the MAX_NUMBER_IDS to match the authentication pool required */ +#define MAX_NUMBER_IDS U(26) + +#endif + +#endif /* PLAT_TBBR_IMG_DEF_H */ + diff --git a/plat/st/stm32mp2/include/platform_def.h b/plat/st/stm32mp2/include/platform_def.h index 404c384f..e720c02c 100644 --- a/plat/st/stm32mp2/include/platform_def.h +++ b/plat/st/stm32mp2/include/platform_def.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023, STMicroelectronics - All Rights Reserved + * Copyright (c) 2023-2024, STMicroelectronics - All Rights Reserved * * SPDX-License-Identifier: BSD-3-Clause */ @@ -8,6 +8,7 @@ #define PLATFORM_DEF_H #include <arch.h> +#include <drivers/arm/gic_common.h> #include <lib/utils_def.h> #include <plat/common/common_def.h> @@ -32,9 +33,9 @@ #define PLATFORM_CORE_COUNT U(2) #define PLATFORM_MAX_CPUS_PER_CLUSTER U(2) -#define PLAT_MAX_PWR_LVL U(5) -#define PLAT_MAX_CPU_SUSPEND_PWR_LVL U(5) -#define PLAT_NUM_PWR_DOMAINS U(7) +#define PLAT_MAX_PWR_LVL U(1) +#define PLAT_MIN_SUSPEND_PWR_LVL U(2) +#define PLAT_NUM_PWR_DOMAINS U(6) /* Local power state for power domains in Run state. */ #define STM32MP_LOCAL_STATE_RUN U(0) @@ -61,6 +62,20 @@ #define BL2_LIMIT (STM32MP_BL2_BASE + \ STM32MP_BL2_SIZE) +#define BL2_RO_BASE STM32MP_BL2_RO_BASE +#define BL2_RO_LIMIT (STM32MP_BL2_RO_BASE + \ + STM32MP_BL2_RO_SIZE) + +#define BL2_RW_BASE STM32MP_BL2_RW_BASE +#define BL2_RW_LIMIT (STM32MP_BL2_RW_BASE + \ + STM32MP_BL2_RW_SIZE) + +/******************************************************************************* + * BL31 specific defines. + ******************************************************************************/ +#define BL31_BASE 0 +#define BL31_LIMIT (STM32MP_SEC_SYSRAM_SIZE / 2) + /******************************************************************************* * BL33 specific defines. ******************************************************************************/ @@ -84,4 +99,59 @@ #define CACHE_WRITEBACK_SHIFT 6 #define CACHE_WRITEBACK_GRANULE (U(1) << CACHE_WRITEBACK_SHIFT) +/* + * Secure Interrupt: based on the standard ARM mapping + */ +#define ARM_IRQ_SEC_PHY_TIMER U(29) + +#define ARM_IRQ_NON_SEC_SGI_0 U(0) + +#define ARM_IRQ_SEC_SGI_0 U(8) +#define ARM_IRQ_SEC_SGI_1 U(9) +#define ARM_IRQ_SEC_SGI_2 U(10) +#define ARM_IRQ_SEC_SGI_3 U(11) +#define ARM_IRQ_SEC_SGI_4 U(12) +#define ARM_IRQ_SEC_SGI_5 U(13) +#define ARM_IRQ_SEC_SGI_6 U(14) +#define ARM_IRQ_SEC_SGI_7 U(15) + +/* Platform IRQ Priority */ +#define STM32MP_IRQ_SEC_SPI_PRIO U(0x10) + +/* + * Define a list of Group 1 Secure and Group 0 interrupts as per GICv3 + * terminology. On a GICv2 system or mode, the lists will be merged and treated + * as Group 0 interrupts. + */ +#define PLATFORM_G1S_PROPS(grp) \ + INTR_PROP_DESC(ARM_IRQ_SEC_PHY_TIMER, \ + GIC_HIGHEST_SEC_PRIORITY, \ + (grp), GIC_INTR_CFG_LEVEL), \ + INTR_PROP_DESC(ARM_IRQ_SEC_SGI_1, \ + GIC_HIGHEST_SEC_PRIORITY, \ + (grp), GIC_INTR_CFG_EDGE), \ + INTR_PROP_DESC(ARM_IRQ_SEC_SGI_2, \ + GIC_HIGHEST_SEC_PRIORITY, \ + (grp), GIC_INTR_CFG_EDGE), \ + INTR_PROP_DESC(ARM_IRQ_SEC_SGI_3, \ + GIC_HIGHEST_SEC_PRIORITY, \ + (grp), GIC_INTR_CFG_EDGE), \ + INTR_PROP_DESC(ARM_IRQ_SEC_SGI_4, \ + GIC_HIGHEST_SEC_PRIORITY, \ + (grp), GIC_INTR_CFG_EDGE), \ + INTR_PROP_DESC(ARM_IRQ_SEC_SGI_5, \ + GIC_HIGHEST_SEC_PRIORITY, \ + (grp), GIC_INTR_CFG_EDGE), \ + INTR_PROP_DESC(ARM_IRQ_SEC_SGI_7, \ + GIC_HIGHEST_SEC_PRIORITY, \ + (grp), GIC_INTR_CFG_EDGE) + +#define PLATFORM_G0_PROPS(grp) \ + INTR_PROP_DESC(ARM_IRQ_SEC_SGI_0, \ + GIC_HIGHEST_SEC_PRIORITY, \ + (grp), GIC_INTR_CFG_EDGE), \ + INTR_PROP_DESC(ARM_IRQ_SEC_SGI_6, \ + GIC_HIGHEST_SEC_PRIORITY, \ + (grp), GIC_INTR_CFG_EDGE) + #endif /* PLATFORM_DEF_H */ diff --git a/plat/st/stm32mp2/include/stm32mp2_private.h b/plat/st/stm32mp2/include/stm32mp2_private.h new file mode 100644 index 00000000..4bb8c523 --- /dev/null +++ b/plat/st/stm32mp2/include/stm32mp2_private.h @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2024, STMicroelectronics - All Rights Reserved + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef STM32MP2_PRIVATE_H +#define STM32MP2_PRIVATE_H + +void configure_mmu(void); + +uint32_t stm32mp_syscfg_get_chip_dev_id(void); + +/* Get DDRDBG peripheral IO memory base address */ +uintptr_t stm32_ddrdbg_get_base(void); + +/* Wrappers for OTP / BSEC functions */ +static inline uint32_t stm32_otp_probe(void) +{ + return bsec_probe(); +} + +static inline uint32_t stm32_otp_read(uint32_t *val, uint32_t otp) +{ + return bsec_read_otp(val, otp); +} + +static inline uint32_t stm32_otp_shadow_read(uint32_t *val, uint32_t otp) +{ + return bsec_shadow_read_otp(val, otp); +} + +static inline uint32_t stm32_otp_write(uint32_t val, uint32_t otp) +{ + return bsec_write_otp(val, otp); +} + +static inline uint32_t stm32_otp_set_sr_lock(uint32_t otp) +{ + return bsec_set_sr_lock(otp); +} + +static inline uint32_t stm32_otp_read_sw_lock(uint32_t otp, bool *value) +{ + return bsec_read_sw_lock(otp, value); +} + +static inline bool stm32_otp_is_closed_device(void) +{ + return bsec_mode_is_closed_device(); +} + +#endif /* STM32MP2_PRIVATE_H */ diff --git a/plat/st/stm32mp2/plat_bl2_mem_params_desc.c b/plat/st/stm32mp2/plat_bl2_mem_params_desc.c index 630cc84d..8ca582ec 100644 --- a/plat/st/stm32mp2/plat_bl2_mem_params_desc.c +++ b/plat/st/stm32mp2/plat_bl2_mem_params_desc.c @@ -1,10 +1,20 @@ /* - * Copyright (c) 2023, STMicroelectronics - All Rights Reserved + * Copyright (c) 2023-2024, STMicroelectronics - All Rights Reserved * * SPDX-License-Identifier: BSD-3-Clause */ +#include <common/bl_common.h> #include <common/desc_image_load.h> +#include <plat/common/platform.h> + +#include <platform_def.h> + +#if STM32MP_BL33_EL1 +#define BL33_MODE MODE_EL1 +#else +#define BL33_MODE MODE_EL2 +#endif /******************************************************************************* * Following descriptor provides BL image/ep information that gets used @@ -15,6 +25,133 @@ * the next executable image id. ******************************************************************************/ static bl_mem_params_node_t bl2_mem_params_descs[] = { +#if STM32MP_DDR_FIP_IO_STORAGE + /* Fill FW_DDR related information if it exists */ + { + .image_id = DDR_FW_ID, + SET_STATIC_PARAM_HEAD(ep_info, PARAM_IMAGE_BINARY, + VERSION_2, entry_point_info_t, + SECURE | NON_EXECUTABLE), + SET_STATIC_PARAM_HEAD(image_info, PARAM_IMAGE_BINARY, + VERSION_2, image_info_t, + 0), + + .image_info.image_base = STM32MP_DDR_FW_BASE, + .image_info.image_max_size = STM32MP_DDR_FW_MAX_SIZE, + + .next_handoff_image_id = INVALID_IMAGE_ID, + }, +#endif + + /* Fill FW_CONFIG related information if it exists */ + { + .image_id = FW_CONFIG_ID, + SET_STATIC_PARAM_HEAD(ep_info, PARAM_IMAGE_BINARY, + VERSION_2, entry_point_info_t, + SECURE | NON_EXECUTABLE), + SET_STATIC_PARAM_HEAD(image_info, PARAM_IMAGE_BINARY, + VERSION_2, image_info_t, + IMAGE_ATTRIB_PLAT_SETUP), + + .image_info.image_base = STM32MP_FW_CONFIG_BASE, + .image_info.image_max_size = STM32MP_FW_CONFIG_MAX_SIZE, + + .next_handoff_image_id = INVALID_IMAGE_ID, + }, + + /* Fill BL31 related information */ + { + .image_id = BL31_IMAGE_ID, + + SET_STATIC_PARAM_HEAD(ep_info, PARAM_EP, + VERSION_2, entry_point_info_t, + SECURE | EXECUTABLE | EP_FIRST_EXE), + + .ep_info.spsr = SPSR_64(MODE_EL3, MODE_SP_ELX, DISABLE_ALL_EXCEPTIONS), + + SET_STATIC_PARAM_HEAD(image_info, PARAM_EP, + VERSION_2, image_info_t, + IMAGE_ATTRIB_SKIP_LOADING), + + .next_handoff_image_id = BL32_IMAGE_ID, + }, + + /* Fill SoC FW config related information */ + { + .image_id = SOC_FW_CONFIG_ID, + + SET_STATIC_PARAM_HEAD(ep_info, PARAM_IMAGE_BINARY, + VERSION_2, entry_point_info_t, + SECURE | NON_EXECUTABLE), + + SET_STATIC_PARAM_HEAD(image_info, PARAM_IMAGE_BINARY, + VERSION_2, image_info_t, + IMAGE_ATTRIB_SKIP_LOADING), + + .next_handoff_image_id = INVALID_IMAGE_ID, + }, + + /* Fill BL32 related information */ + { + .image_id = BL32_IMAGE_ID, + + SET_STATIC_PARAM_HEAD(ep_info, PARAM_EP, + VERSION_2, entry_point_info_t, + SECURE | EXECUTABLE), + + SET_STATIC_PARAM_HEAD(image_info, PARAM_EP, + VERSION_2, image_info_t, + IMAGE_ATTRIB_SKIP_LOADING), + + .next_handoff_image_id = BL33_IMAGE_ID, + }, + + /* Fill BL32 external 1 image related information */ + { + .image_id = BL32_EXTRA1_IMAGE_ID, + + SET_STATIC_PARAM_HEAD(ep_info, PARAM_EP, + VERSION_2, entry_point_info_t, + SECURE | NON_EXECUTABLE), + + SET_STATIC_PARAM_HEAD(image_info, PARAM_EP, + VERSION_2, image_info_t, + IMAGE_ATTRIB_SKIP_LOADING), + + .next_handoff_image_id = INVALID_IMAGE_ID, + }, + + /* Fill HW_CONFIG related information if it exists */ + { + .image_id = HW_CONFIG_ID, + + SET_STATIC_PARAM_HEAD(ep_info, PARAM_IMAGE_BINARY, + VERSION_2, entry_point_info_t, + NON_SECURE | NON_EXECUTABLE), + + SET_STATIC_PARAM_HEAD(image_info, PARAM_IMAGE_BINARY, + VERSION_2, image_info_t, + IMAGE_ATTRIB_SKIP_LOADING), + + .next_handoff_image_id = INVALID_IMAGE_ID, + }, + + /* Fill BL33 related information */ + { + .image_id = BL33_IMAGE_ID, + + SET_STATIC_PARAM_HEAD(ep_info, PARAM_EP, + VERSION_2, entry_point_info_t, + NON_SECURE | EXECUTABLE), + + .ep_info.spsr = SPSR_64(BL33_MODE, MODE_SP_ELX, DISABLE_ALL_EXCEPTIONS), + + SET_STATIC_PARAM_HEAD(image_info, PARAM_EP, + VERSION_2, image_info_t, + IMAGE_ATTRIB_SKIP_LOADING), + + .next_handoff_image_id = INVALID_IMAGE_ID, + } }; REGISTER_BL_IMAGE_DESCS(bl2_mem_params_descs) diff --git a/plat/st/stm32mp2/plat_ddr.c b/plat/st/stm32mp2/plat_ddr.c new file mode 100644 index 00000000..5302e45b --- /dev/null +++ b/plat/st/stm32mp2/plat_ddr.c @@ -0,0 +1,217 @@ +/* + * Copyright (C) 2023-2024, STMicroelectronics - All Rights Reserved + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <assert.h> +#include <errno.h> +#include <stdint.h> + +#include <common/fdt_wrappers.h> + +#include <drivers/delay_timer.h> +#include <drivers/st/regulator.h> +#include <drivers/st/stm32mp_ddr.h> + +#include <libfdt.h> + +#include <platform_def.h> + +#if STM32MP_DDR3_TYPE +struct ddr3_supply { + struct rdev *vdd; + struct rdev *vref; + struct rdev *vtt; +}; + +static void ddr3_supply_read(void *fdt, int node, struct ddr3_supply *supply) +{ + supply->vdd = regulator_get_by_supply_name(fdt, node, "vdd"); + supply->vref = regulator_get_by_supply_name(fdt, node, "vref"); + supply->vtt = regulator_get_by_supply_name(fdt, node, "vtt"); +} + +static int ddr_power_init(void *fdt, int node) +{ + int status; + struct ddr3_supply supply; + + ddr3_supply_read(fdt, node, &supply); + if ((supply.vdd == NULL) || (supply.vref == NULL) || (supply.vtt == NULL)) { + return -ENOENT; + } + + /* + * DDR3 power on sequence is: + * enable VREF_DDR, VTT_DDR, VPP_DDR + */ + status = regulator_set_min_voltage(supply.vdd); + if (status != 0) { + return status; + } + + status = regulator_enable(supply.vdd); + if (status != 0) { + return status; + } + + status = regulator_enable(supply.vref); + if (status != 0) { + return status; + } + + return regulator_enable(supply.vtt); +} +#endif /* STM32MP_DDR3_TYPE */ + +#if STM32MP_DDR4_TYPE +struct ddr4_supply { + struct rdev *vdd; + struct rdev *vref; + struct rdev *vtt; + struct rdev *vpp; +}; + +static void ddr4_supply_read(void *fdt, int node, struct ddr4_supply *supply) +{ + supply->vpp = regulator_get_by_supply_name(fdt, node, "vpp"); + supply->vdd = regulator_get_by_supply_name(fdt, node, "vdd"); + supply->vref = regulator_get_by_supply_name(fdt, node, "vref"); + supply->vtt = regulator_get_by_supply_name(fdt, node, "vtt"); +} + +static int ddr_power_init(void *fdt, int node) +{ + int status; + struct ddr4_supply supply; + + ddr4_supply_read(fdt, node, &supply); + if ((supply.vpp == NULL) || (supply.vdd == NULL) || (supply.vref == NULL) || + (supply.vtt == NULL)) { + return -ENOENT; + } + + /* + * DDR4 power on sequence is: + * enable VPP_DDR + * enable VREF_DDR, VTT_DDR, VPP_DDR + */ + status = regulator_set_min_voltage(supply.vpp); + if (status != 0) { + return status; + } + + status = regulator_set_min_voltage(supply.vdd); + if (status != 0) { + return status; + } + + status = regulator_enable(supply.vpp); + if (status != 0) { + return status; + } + + status = regulator_enable(supply.vdd); + if (status != 0) { + return status; + } + + status = regulator_enable(supply.vref); + if (status != 0) { + return status; + } + + return regulator_enable(supply.vtt); +} +#endif /* STM32MP_DDR4_TYPE */ + +#if STM32MP_LPDDR4_TYPE +struct lpddr4_supply { + struct rdev *vdd1; + struct rdev *vdd2; + struct rdev *vddq; +}; + +static void lpddr4_supply_read(void *fdt, int node, struct lpddr4_supply *supply) +{ + supply->vdd1 = regulator_get_by_supply_name(fdt, node, "vdd1"); + supply->vdd2 = regulator_get_by_supply_name(fdt, node, "vdd2"); + supply->vddq = regulator_get_by_supply_name(fdt, node, "vddq"); +} + +static int ddr_power_init(void *fdt, int node) +{ + int status; + struct lpddr4_supply supply; + + lpddr4_supply_read(fdt, node, &supply); + if ((supply.vdd1 == NULL) || (supply.vdd2 == NULL) || (supply.vddq == NULL)) { + return -ENOENT; + } + + /* + * LPDDR4 power on sequence is: + * enable VDD1_DDR + * enable VDD2_DDR + * enable VDDQ_DDR + */ + status = regulator_set_min_voltage(supply.vdd1); + if (status != 0) { + return status; + } + + status = regulator_set_min_voltage(supply.vdd2); + if (status != 0) { + return status; + } + + status = regulator_set_min_voltage(supply.vddq); + if (status != 0) { + return status; + } + + status = regulator_enable(supply.vdd1); + if (status != 0) { + return status; + } + + status = regulator_enable(supply.vdd2); + if (status != 0) { + return status; + } + + return regulator_enable(supply.vddq); +} +#endif /* STM32MP_LPDDR4_TYPE */ + +int stm32mp_board_ddr_power_init(enum ddr_type ddr_type) +{ + void *fdt = NULL; + int node; + + VERBOSE("DDR power init, ddr_type = %u\n", ddr_type); + +#if STM32MP_DDR3_TYPE + assert(ddr_type == STM32MP_DDR3); +#elif STM32MP_DDR4_TYPE + assert(ddr_type == STM32MP_DDR4); +#elif STM32MP_LPDDR4_TYPE + assert(ddr_type == STM32MP_LPDDR4); +#else + ERROR("DDR type (%u) not supported\n", ddr_type); + panic(); +#endif + + if (fdt_get_address(&fdt) == 0) { + return -FDT_ERR_NOTFOUND; + } + + node = fdt_node_offset_by_compatible(fdt, -1, DT_DDR_COMPAT); + if (node < 0) { + ERROR("%s: Cannot read DDR node in DT\n", __func__); + return -EINVAL; + } + + return ddr_power_init(fdt, node); +} diff --git a/plat/st/stm32mp2/platform.mk b/plat/st/stm32mp2/platform.mk index 6ea4638c..f4616562 100644 --- a/plat/st/stm32mp2/platform.mk +++ b/plat/st/stm32mp2/platform.mk @@ -1,14 +1,28 @@ # -# Copyright (c) 2023, STMicroelectronics - All Rights Reserved +# Copyright (c) 2023-2024, STMicroelectronics - All Rights Reserved # # SPDX-License-Identifier: BSD-3-Clause # +# Extra partitions used to find FIP, contains: +# metadata (2) and fsbl-m (2) and the FIP partitions (default is 2). +STM32_EXTRA_PARTS := 6 + include plat/st/common/common.mk CRASH_REPORTING := 1 ENABLE_PIE := 1 PROGRAMMABLE_RESET_ADDRESS := 1 +BL2_IN_XIP_MEM := 1 + +STM32MP_BL33_EL1 ?= 1 +ifeq ($(STM32MP_BL33_EL1),1) +INIT_UNUSED_NS_EL2 := 1 +endif + +# Disable features unsupported in ARMv8.0 +ENABLE_SPE_FOR_NS := 0 +ENABLE_SVE_FOR_NS := 0 # Default Device tree DTB_FILE_NAME ?= stm32mp257f-ev1.dtb @@ -19,34 +33,193 @@ STM32MP25 := 1 STM32_HEADER_VERSION_MAJOR := 2 STM32_HEADER_VERSION_MINOR := 2 -# Number of TF-A copies in the device -STM32_TF_A_COPIES := 2 +# Set load address for serial boot devices +DWL_BUFFER_BASE ?= 0x87000000 + +# DDR types +STM32MP_DDR3_TYPE ?= 0 +STM32MP_DDR4_TYPE ?= 0 +STM32MP_LPDDR4_TYPE ?= 0 +ifeq (${STM32MP_DDR3_TYPE},1) +DDR_TYPE := ddr3 +endif +ifeq (${STM32MP_DDR4_TYPE},1) +DDR_TYPE := ddr4 +endif +ifeq (${STM32MP_LPDDR4_TYPE},1) +DDR_TYPE := lpddr4 +endif -# PLAT_PARTITION_MAX_ENTRIES must take care of STM32_TF-A_COPIES and other partitions -# such as metadata (2) and fsbl-m (2) to find all the FIP partitions (default is 2). -PLAT_PARTITION_MAX_ENTRIES := $(shell echo $$(($(STM32_TF_A_COPIES) + 6))) +# DDR features +STM32MP_DDR_DUAL_AXI_PORT := 1 +STM32MP_DDR_FIP_IO_STORAGE := 1 # Device tree BL2_DTSI := stm32mp25-bl2.dtsi FDT_SOURCES := $(addprefix ${BUILD_PLAT}/fdts/, $(patsubst %.dtb,%-bl2.dts,$(DTB_FILE_NAME))) +BL31_DTSI := stm32mp25-bl31.dtsi +FDT_SOURCES += $(addprefix ${BUILD_PLAT}/fdts/, $(patsubst %.dtb,%-bl31.dts,$(DTB_FILE_NAME))) # Macros and rules to build TF binary STM32_TF_STM32 := $(addprefix ${BUILD_PLAT}/tf-a-, $(patsubst %.dtb,%.stm32,$(DTB_FILE_NAME))) STM32_LD_FILE := plat/st/stm32mp2/${ARCH}/stm32mp2.ld.S STM32_BINARY_MAPPING := plat/st/stm32mp2/${ARCH}/stm32mp2.S +STM32MP_FW_CONFIG_NAME := $(patsubst %.dtb,%-fw-config.dtb,$(DTB_FILE_NAME)) +STM32MP_FW_CONFIG := ${BUILD_PLAT}/fdts/$(STM32MP_FW_CONFIG_NAME) +STM32MP_SOC_FW_CONFIG := $(addprefix ${BUILD_PLAT}/fdts/, $(patsubst %.dtb,%-bl31.dtb,$(DTB_FILE_NAME))) +ifeq (${STM32MP_DDR_FIP_IO_STORAGE},1) +STM32MP_DDR_FW_PATH ?= drivers/st/ddr/phy/firmware/bin/stm32mp2 +STM32MP_DDR_FW_NAME := ${DDR_TYPE}_pmu_train.bin +STM32MP_DDR_FW := ${STM32MP_DDR_FW_PATH}/${STM32MP_DDR_FW_NAME} +endif +FDT_SOURCES += $(addprefix fdts/, $(patsubst %.dtb,%.dts,$(STM32MP_FW_CONFIG_NAME))) + +# Add the FW_CONFIG to FIP and specify the same to certtool +$(eval $(call TOOL_ADD_PAYLOAD,${STM32MP_FW_CONFIG},--fw-config)) + +# Add the SOC_FW_CONFIG to FIP and specify the same to certtool +$(eval $(call TOOL_ADD_IMG_PAYLOAD,STM32MP_SOC_FW_CONFIG,$(STM32MP_SOC_FW_CONFIG),--soc-fw-config,$(patsubst %.dtb,%.dts,$(STM32MP_SOC_FW_CONFIG)))) + +ifeq (${STM32MP_DDR_FIP_IO_STORAGE},1) +# Add the FW_DDR to FIP and specify the same to certtool +$(eval $(call TOOL_ADD_IMG,STM32MP_DDR_FW,--ddr-fw)) +endif + +# Enable flags for C files +$(eval $(call assert_booleans,\ + $(sort \ + STM32MP_DDR_DUAL_AXI_PORT \ + STM32MP_DDR_FIP_IO_STORAGE \ + STM32MP_DDR3_TYPE \ + STM32MP_DDR4_TYPE \ + STM32MP_LPDDR4_TYPE \ + STM32MP25 \ + STM32MP_BL33_EL1 \ +))) + +$(eval $(call assert_numerics,\ + $(sort \ + PLAT_PARTITION_MAX_ENTRIES \ + STM32_HEADER_VERSION_MAJOR \ + STM32_TF_A_COPIES \ +))) + +$(eval $(call add_defines,\ + $(sort \ + DWL_BUFFER_BASE \ + PLAT_DEF_FIP_UUID \ + PLAT_PARTITION_MAX_ENTRIES \ + PLAT_TBBR_IMG_DEF \ + STM32_TF_A_COPIES \ + STM32MP_DDR_DUAL_AXI_PORT \ + STM32MP_DDR_FIP_IO_STORAGE \ + STM32MP_DDR3_TYPE \ + STM32MP_DDR4_TYPE \ + STM32MP_LPDDR4_TYPE \ + STM32MP25 \ + STM32MP_BL33_EL1 \ +))) + # STM32MP2x is based on Cortex-A35, which is Armv8.0, and does not support BTI # Disable mbranch-protection to avoid adding useless code TF_CFLAGS += -mbranch-protection=none # Include paths and source files PLAT_INCLUDES += -Iplat/st/stm32mp2/include/ +PLAT_INCLUDES += -Idrivers/st/ddr/phy/phyinit/include/ +PLAT_INCLUDES += -Idrivers/st/ddr/phy/firmware/include/ PLAT_BL_COMMON_SOURCES += lib/cpus/${ARCH}/cortex_a35.S PLAT_BL_COMMON_SOURCES += drivers/st/uart/${ARCH}/stm32_console.S PLAT_BL_COMMON_SOURCES += plat/st/stm32mp2/${ARCH}/stm32mp2_helper.S +PLAT_BL_COMMON_SOURCES += drivers/st/pmic/stm32mp_pmic2.c \ + drivers/st/pmic/stpmic2.c \ + +PLAT_BL_COMMON_SOURCES += drivers/st/i2c/stm32_i2c.c + +PLAT_BL_COMMON_SOURCES += plat/st/stm32mp2/stm32mp2_private.c + +PLAT_BL_COMMON_SOURCES += drivers/st/bsec/bsec3.c \ + drivers/st/reset/stm32mp2_reset.c \ + plat/st/stm32mp2/stm32mp2_syscfg.c + +PLAT_BL_COMMON_SOURCES += drivers/st/clk/clk-stm32-core.c \ + drivers/st/clk/clk-stm32mp2.c + BL2_SOURCES += plat/st/stm32mp2/plat_bl2_mem_params_desc.c -BL2_SOURCES += plat/st/stm32mp2/bl2_plat_setup.c + +BL2_SOURCES += plat/st/stm32mp2/bl2_plat_setup.c \ + plat/st/stm32mp2/plat_ddr.c + +ifneq ($(filter 1,${STM32MP_EMMC} ${STM32MP_SDMMC}),) +BL2_SOURCES += drivers/st/mmc/stm32_sdmmc2.c +endif + +ifeq (${STM32MP_USB_PROGRAMMER},1) +BL2_SOURCES += plat/st/stm32mp2/stm32mp2_usb_dfu.c +endif + +BL2_SOURCES += drivers/st/ddr/stm32mp2_ddr.c \ + drivers/st/ddr/stm32mp2_ddr_helpers.c \ + drivers/st/ddr/stm32mp2_ram.c + +BL2_SOURCES += drivers/st/ddr/phy/phyinit/src/ddrphy_phyinit_c_initphyconfig.c \ + drivers/st/ddr/phy/phyinit/src/ddrphy_phyinit_calcmb.c \ + drivers/st/ddr/phy/phyinit/src/ddrphy_phyinit_i_loadpieimage.c \ + drivers/st/ddr/phy/phyinit/src/ddrphy_phyinit_initstruct.c \ + drivers/st/ddr/phy/phyinit/src/ddrphy_phyinit_isdbytedisabled.c \ + drivers/st/ddr/phy/phyinit/src/ddrphy_phyinit_loadpieprodcode.c \ + drivers/st/ddr/phy/phyinit/src/ddrphy_phyinit_mapdrvstren.c \ + drivers/st/ddr/phy/phyinit/src/ddrphy_phyinit_progcsrskiptrain.c \ + drivers/st/ddr/phy/phyinit/src/ddrphy_phyinit_reginterface.c \ + drivers/st/ddr/phy/phyinit/src/ddrphy_phyinit_restore_sequence.c \ + drivers/st/ddr/phy/phyinit/src/ddrphy_phyinit_sequence.c \ + drivers/st/ddr/phy/phyinit/src/ddrphy_phyinit_softsetmb.c \ + drivers/st/ddr/phy/phyinit/usercustom/ddrphy_phyinit_usercustom_custompretrain.c \ + drivers/st/ddr/phy/phyinit/usercustom/ddrphy_phyinit_usercustom_saveretregs.c + +BL2_SOURCES += drivers/st/ddr/phy/phyinit/src/ddrphy_phyinit_d_loadimem.c \ + drivers/st/ddr/phy/phyinit/src/ddrphy_phyinit_f_loaddmem.c \ + drivers/st/ddr/phy/phyinit/src/ddrphy_phyinit_g_execfw.c \ + drivers/st/ddr/phy/phyinit/src/ddrphy_phyinit_writeoutmem.c \ + drivers/st/ddr/phy/phyinit/usercustom/ddrphy_phyinit_usercustom_g_waitfwdone.c + +# BL31 sources +BL31_SOURCES += ${FDT_WRAPPERS_SOURCES} + +BL31_SOURCES += plat/st/stm32mp2/bl31_plat_setup.c \ + plat/st/stm32mp2/stm32mp2_pm.c \ + plat/st/stm32mp2/stm32mp2_topology.c +# Generic GIC v2 +include drivers/arm/gic/v2/gicv2.mk + +BL31_SOURCES += ${GICV2_SOURCES} \ + plat/common/plat_gicv2.c \ + plat/st/common/stm32mp_gic.c + +# Generic PSCI +BL31_SOURCES += plat/common/plat_psci_common.c + +# Compilation rules +.PHONY: check_ddr_type +.SUFFIXES: + +bl2: check_ddr_type + +check_ddr_type: + $(eval DDR_TYPE = $(shell echo $$(($(STM32MP_DDR3_TYPE) + \ + $(STM32MP_DDR4_TYPE) + \ + $(STM32MP_LPDDR4_TYPE))))) + @if [ ${DDR_TYPE} != 1 ]; then \ + echo "One and only one DDR type must be defined"; \ + false; \ + fi + +# Create DTB file for BL31 +${BUILD_PLAT}/fdts/%-bl31.dts: fdts/%.dts fdts/${BL31_DTSI} | $$(@D)/ + @echo '#include "$(patsubst fdts/%,%,$<)"' > $@ + @echo '#include "${BL31_DTSI}"' >> $@ include plat/st/common/common_rules.mk diff --git a/plat/st/stm32mp2/stm32mp2_def.h b/plat/st/stm32mp2/stm32mp2_def.h index 66514fcf..b4415021 100644 --- a/plat/st/stm32mp2/stm32mp2_def.h +++ b/plat/st/stm32mp2/stm32mp2_def.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023, STMicroelectronics - All Rights Reserved + * Copyright (c) 2023-2024, STMicroelectronics - All Rights Reserved * * SPDX-License-Identifier: BSD-3-Clause */ @@ -12,24 +12,70 @@ #include <drivers/st/bsec.h> #endif #include <drivers/st/stm32mp25_rcc.h> +#ifndef __ASSEMBLER__ +#include <drivers/st/stm32mp2_clk.h> +#endif +#include <drivers/st/stm32mp2_pwr.h> #include <dt-bindings/clock/stm32mp25-clks.h> #include <dt-bindings/clock/stm32mp25-clksrc.h> +#include <dt-bindings/gpio/stm32-gpio.h> #include <dt-bindings/reset/stm32mp25-resets.h> #ifndef __ASSEMBLER__ #include <boot_api.h> +#include <stm32mp2_private.h> #include <stm32mp_common.h> #include <stm32mp_dt.h> #include <stm32mp_shared_resources.h> #endif +/******************************************************************************* + * CHIP ID + ******************************************************************************/ +#define STM32MP2_CHIP_ID U(0x505) + +#define STM32MP251A_PART_NB U(0x400B3E6D) +#define STM32MP251C_PART_NB U(0x000B306D) +#define STM32MP251D_PART_NB U(0xC00B3E6D) +#define STM32MP251F_PART_NB U(0x800B306D) +#define STM32MP253A_PART_NB U(0x400B3E0C) +#define STM32MP253C_PART_NB U(0x000B300C) +#define STM32MP253D_PART_NB U(0xC00B3E0C) +#define STM32MP253F_PART_NB U(0x800B300C) +#define STM32MP255A_PART_NB U(0x40082E00) +#define STM32MP255C_PART_NB U(0x00082000) +#define STM32MP255D_PART_NB U(0xC0082E00) +#define STM32MP255F_PART_NB U(0x80082000) +#define STM32MP257A_PART_NB U(0x40002E00) +#define STM32MP257C_PART_NB U(0x00002000) +#define STM32MP257D_PART_NB U(0xC0002E00) +#define STM32MP257F_PART_NB U(0x80002000) + +#define STM32MP2_REV_A U(0x08) +#define STM32MP2_REV_B U(0x10) +#define STM32MP2_REV_X U(0x12) +#define STM32MP2_REV_Y U(0x11) +#define STM32MP2_REV_Z U(0x09) + +/******************************************************************************* + * PACKAGE ID + ******************************************************************************/ +#define STM32MP25_PKG_CUSTOM U(0) +#define STM32MP25_PKG_AL_VFBGA361 U(1) +#define STM32MP25_PKG_AK_VFBGA424 U(3) +#define STM32MP25_PKG_AI_TFBGA436 U(5) +#define STM32MP25_PKG_UNKNOWN U(7) + /******************************************************************************* * STM32MP2 memory map related constants ******************************************************************************/ #define STM32MP_SYSRAM_BASE U(0x0E000000) #define STM32MP_SYSRAM_SIZE U(0x00040000) +#define SRAM1_BASE U(0x0E040000) +#define SRAM1_SIZE_FOR_TFA U(0x00010000) +#define RETRAM_BASE U(0x0E080000) +#define RETRAM_SIZE U(0x00020000) -#define STM32MP_SEC_SYSRAM_BASE STM32MP_SYSRAM_BASE #define STM32MP_SEC_SYSRAM_SIZE STM32MP_SYSRAM_SIZE /* DDR configuration */ @@ -47,28 +93,44 @@ enum ddr_type { /* Section used inside TF binaries */ #define STM32MP_PARAM_LOAD_SIZE U(0x00002400) /* 9 KB for param */ -/* 512 Octets reserved for header */ +/* 512 Bytes reserved for header */ #define STM32MP_HEADER_SIZE U(0x00000200) -#define STM32MP_HEADER_BASE (STM32MP_SEC_SYSRAM_BASE + \ +#define STM32MP_HEADER_BASE (STM32MP_SYSRAM_BASE + \ STM32MP_PARAM_LOAD_SIZE) /* round_up(STM32MP_PARAM_LOAD_SIZE + STM32MP_HEADER_SIZE, PAGE_SIZE) */ #define STM32MP_HEADER_RESERVED_SIZE U(0x3000) -#define STM32MP_BINARY_BASE (STM32MP_SEC_SYSRAM_BASE + \ +#define STM32MP_BINARY_BASE (STM32MP_SYSRAM_BASE + \ STM32MP_PARAM_LOAD_SIZE + \ STM32MP_HEADER_SIZE) -#define STM32MP_BINARY_SIZE (STM32MP_SEC_SYSRAM_SIZE - \ +#define STM32MP_BINARY_SIZE (STM32MP_SYSRAM_SIZE - \ (STM32MP_PARAM_LOAD_SIZE + \ STM32MP_HEADER_SIZE)) -#define STM32MP_BL2_SIZE U(0x0002A000) /* 168 KB for BL2 */ +#define STM32MP_BL2_RO_SIZE U(0x00020000) /* 128 KB */ +#define STM32MP_BL2_SIZE U(0x00029000) /* 164 KB for BL2 */ -#define STM32MP_BL2_BASE (STM32MP_SEC_SYSRAM_BASE + \ - STM32MP_SEC_SYSRAM_SIZE - \ +/* Allocate remaining sysram to BL31 Binary only */ +#define STM32MP_BL31_SIZE (STM32MP_SEC_SYSRAM_SIZE - \ STM32MP_BL2_SIZE) +#define BL31_PROGBITS_LIMIT STM32MP_BL31_SIZE + +#define STM32MP_BL2_BASE (STM32MP_SYSRAM_BASE + \ + STM32MP_SYSRAM_SIZE - \ + STM32MP_BL2_SIZE) + +#define STM32MP_BL2_RO_BASE STM32MP_BL2_BASE + +#define STM32MP_BL2_RW_BASE (STM32MP_BL2_RO_BASE + \ + STM32MP_BL2_RO_SIZE) + +#define STM32MP_BL2_RW_SIZE (STM32MP_SYSRAM_BASE + \ + STM32MP_SYSRAM_SIZE - \ + STM32MP_BL2_RW_BASE) + /* BL2 and BL32/sp_min require 4 tables */ #define MAX_XLAT_TABLES U(4) /* 16 KB for mapping */ @@ -76,16 +138,45 @@ enum ddr_type { * MAX_MMAP_REGIONS is usually: * BL stm32mp2_mmap size + mmap regions in *_plat_arch_setup */ +#if defined(IMAGE_BL31) +#define MAX_MMAP_REGIONS 7 +#else #define MAX_MMAP_REGIONS 6 +#endif /* DTB initialization value */ -#define STM32MP_BL2_DTB_SIZE U(0x00005000) /* 20 KB for DTB */ +#define STM32MP_BL2_DTB_SIZE U(0x00006000) /* 24 KB for DTB */ #define STM32MP_BL2_DTB_BASE (STM32MP_BL2_BASE - \ STM32MP_BL2_DTB_SIZE) +#if defined(IMAGE_BL2) +#define STM32MP_DTB_SIZE STM32MP_BL2_DTB_SIZE +#define STM32MP_DTB_BASE STM32MP_BL2_DTB_BASE +#endif + +#if STM32MP_DDR_FIP_IO_STORAGE +#define STM32MP_DDR_FW_BASE SRAM1_BASE +#define STM32MP_DDR_FW_DMEM_OFFSET U(0x400) +#define STM32MP_DDR_FW_IMEM_OFFSET U(0x800) +#define STM32MP_DDR_FW_MAX_SIZE U(0x8800) +#endif + +#define STM32MP_FW_CONFIG_MAX_SIZE PAGE_SIZE +#define STM32MP_FW_CONFIG_BASE STM32MP_SYSRAM_BASE + #define STM32MP_BL33_BASE (STM32MP_DDR_BASE + U(0x04000000)) #define STM32MP_BL33_MAX_SIZE U(0x400000) +#define STM32MP_HW_CONFIG_BASE (STM32MP_BL33_BASE + \ + STM32MP_BL33_MAX_SIZE) +#define STM32MP_HW_CONFIG_MAX_SIZE U(0x40000) +#define STM32MP_SOC_FW_CONFIG_MAX_SIZE U(0x10000) /* 64kB for BL31 DT */ + +/******************************************************************************* + * STM32MP2 device/io map related constants (used for MMU) + ******************************************************************************/ +#define STM32MP_DEVICE_BASE U(0x40000000) +#define STM32MP_DEVICE_SIZE U(0x40000000) /******************************************************************************* * STM32MP2 RCC @@ -156,6 +247,97 @@ enum ddr_type { #define STM32MP_SDMMC2_BASE U(0x48230000) #define STM32MP_SDMMC3_BASE U(0x48240000) +/******************************************************************************* + * STM32MP2 BSEC / OTP + ******************************************************************************/ +/* + * 367 available OTPs, the other are masked + * - ECIES key: 368 to 375 (only readable by bootrom) + * - HWKEY: 376 to 383 (never reloadable or readable) + */ +#define STM32MP2_OTP_MAX_ID U(0x16F) +#define STM32MP2_MID_OTP_START U(0x80) +#define STM32MP2_UPPER_OTP_START U(0x100) + +/* OTP labels */ +#define PART_NUMBER_OTP "part-number-otp" +#define REVISION_OTP "rev_otp" +#define PACKAGE_OTP "package-otp" +#define HCONF1_OTP "otp124" +#define NAND_OTP "otp16" +#define NAND2_OTP "otp20" +#define BOARD_ID_OTP "board-id" +#define UID_OTP "uid-otp" +#define LIFECYCLE2_OTP "otp18" +#define PKH_OTP "otp144" +#define ENCKEY_OTP "otp260" + +/* OTP mask */ +/* PACKAGE */ +#define PACKAGE_OTP_PKG_MASK GENMASK_32(2, 0) +#define PACKAGE_OTP_PKG_SHIFT U(0) + +/* IWDG OTP */ +#define HCONF1_OTP_IWDG_HW_POS U(0) +#define HCONF1_OTP_IWDG_FZ_STOP_POS U(1) +#define HCONF1_OTP_IWDG_FZ_STANDBY_POS U(2) + +/* NAND OTP */ +/* NAND parameter storage flag */ +#define NAND_PARAM_STORED_IN_OTP BIT_32(31) + +/* NAND page size in bytes */ +#define NAND_PAGE_SIZE_MASK GENMASK_32(30, 29) +#define NAND_PAGE_SIZE_SHIFT U(29) +#define NAND_PAGE_SIZE_2K U(0) +#define NAND_PAGE_SIZE_4K U(1) +#define NAND_PAGE_SIZE_8K U(2) + +/* NAND block size in pages */ +#define NAND_BLOCK_SIZE_MASK GENMASK_32(28, 27) +#define NAND_BLOCK_SIZE_SHIFT U(27) +#define NAND_BLOCK_SIZE_64_PAGES U(0) +#define NAND_BLOCK_SIZE_128_PAGES U(1) +#define NAND_BLOCK_SIZE_256_PAGES U(2) + +/* NAND number of block (in unit of 256 blocks) */ +#define NAND_BLOCK_NB_MASK GENMASK_32(26, 19) +#define NAND_BLOCK_NB_SHIFT U(19) +#define NAND_BLOCK_NB_UNIT U(256) + +/* NAND bus width in bits */ +#define NAND_WIDTH_MASK BIT_32(18) +#define NAND_WIDTH_SHIFT U(18) + +/* NAND number of ECC bits per 512 bytes */ +#define NAND_ECC_BIT_NB_MASK GENMASK_32(17, 15) +#define NAND_ECC_BIT_NB_SHIFT U(15) +#define NAND_ECC_BIT_NB_UNSET U(0) +#define NAND_ECC_BIT_NB_1_BITS U(1) +#define NAND_ECC_BIT_NB_4_BITS U(2) +#define NAND_ECC_BIT_NB_8_BITS U(3) +#define NAND_ECC_ON_DIE U(4) + +/* NAND number of planes */ +#define NAND_PLANE_BIT_NB_MASK BIT_32(14) + +/* NAND2 OTP */ +#define NAND2_PAGE_SIZE_SHIFT U(16) + +/* NAND2 config distribution */ +#define NAND2_CONFIG_DISTRIB BIT_32(0) +#define NAND2_PNAND_NAND2_SNAND_NAND1 U(0) +#define NAND2_PNAND_NAND1_SNAND_NAND2 U(1) + +/* MONOTONIC OTP */ +#define MAX_MONOTONIC_VALUE U(32) + +/* UID OTP */ +#define UID_WORD_NB U(3) + +/* Lifecycle OTP */ +#define SECURE_BOOT_CLOSED_SECURE GENMASK_32(3, 0) + /******************************************************************************* * STM32MP2 TAMP ******************************************************************************/ @@ -200,6 +382,17 @@ static inline uintptr_t tamp_bkpr(uint32_t idx) #define STGEN_BASE U(0x48080000) #define SYSCFG_BASE U(0x44230000) +/******************************************************************************* + * STM32MP RIF + ******************************************************************************/ +#define RISAB3_BASE U(0x42110000) +#define RISAB5_BASE U(0x42130000) + +/******************************************************************************* + * STM32MP CA35SSC + ******************************************************************************/ +#define A35SSC_BASE U(0x48800000) + /******************************************************************************* * REGULATORS ******************************************************************************/ @@ -217,6 +410,7 @@ static inline uintptr_t tamp_bkpr(uint32_t idx) #define DT_DDR_COMPAT "st,stm32mp2-ddr" #define DT_PWR_COMPAT "st,stm32mp25-pwr" #define DT_RCC_CLK_COMPAT "st,stm32mp25-rcc" +#define DT_SDMMC2_COMPAT "st,stm32mp25-sdmmc2" #define DT_UART_COMPAT "st,stm32h7-uart" #endif /* STM32MP2_DEF_H */ diff --git a/plat/st/stm32mp2/stm32mp2_pm.c b/plat/st/stm32mp2/stm32mp2_pm.c new file mode 100644 index 00000000..5bb381d0 --- /dev/null +++ b/plat/st/stm32mp2/stm32mp2_pm.c @@ -0,0 +1,124 @@ +/* + * Copyright (c) 2024, STMicroelectronics - All Rights Reserved + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <assert.h> +#include <errno.h> + +#include <arch_helpers.h> +#include <common/debug.h> +#include <drivers/arm/gic_common.h> +#include <drivers/arm/gicv2.h> +#include <drivers/st/stm32mp_reset.h> +#include <lib/mmio.h> +#include <lib/psci/psci.h> +#include <plat/common/platform.h> + +#include <platform_def.h> + +static uintptr_t stm32_sec_entrypoint; + +static void stm32_cpu_standby(plat_local_state_t cpu_state) +{ +} + +static int stm32_pwr_domain_on(u_register_t mpidr) +{ + return PSCI_E_INTERN_FAIL; +} + +static void stm32_pwr_domain_off(const psci_power_state_t *target_state) +{ + /* Nothing to do */ +} + +static void stm32_pwr_domain_suspend(const psci_power_state_t *target_state) +{ + /* Nothing to do, power domain is not disabled */ +} + +static void stm32_pwr_domain_on_finish(const psci_power_state_t *target_state) +{ +} + +/******************************************************************************* + * STM32MP2 handler called when a power domain has just been powered on after + * having been suspended earlier. The target_state encodes the low power state + * that each level has woken up from. + ******************************************************************************/ +static void stm32_pwr_domain_suspend_finish(const psci_power_state_t + *target_state) +{ + /* Nothing to do, power domain is not disabled */ +} + +static void __dead2 stm32_pwr_domain_pwr_down_wfi(const psci_power_state_t + *target_state) +{ + ERROR("stm32mp2 Power Down WFI: operation not handled.\n"); + panic(); +} + +static void __dead2 stm32_system_off(void) +{ + ERROR("stm32mp2 System Off: operation not handled.\n"); + panic(); +} + +static void __dead2 stm32_system_reset(void) +{ + stm32mp_system_reset(); +} + +static int stm32_validate_power_state(unsigned int power_state, + psci_power_state_t *req_state) +{ + return PSCI_E_INVALID_PARAMS; +} + +static int stm32_validate_ns_entrypoint(uintptr_t entrypoint) +{ + /* The non-secure entry point must be in DDR */ + if (entrypoint < STM32MP_DDR_BASE) { + return PSCI_E_INVALID_ADDRESS; + } + + return PSCI_E_SUCCESS; +} + +static void stm32_get_sys_suspend_power_state(psci_power_state_t *req_state) +{ +} + +/******************************************************************************* + * Export the platform handlers. The ARM Standard platform layer will take care + * of registering the handlers with PSCI. + ******************************************************************************/ +static const plat_psci_ops_t stm32_psci_ops = { + .cpu_standby = stm32_cpu_standby, + .pwr_domain_on = stm32_pwr_domain_on, + .pwr_domain_off = stm32_pwr_domain_off, + .pwr_domain_suspend = stm32_pwr_domain_suspend, + .pwr_domain_on_finish = stm32_pwr_domain_on_finish, + .pwr_domain_suspend_finish = stm32_pwr_domain_suspend_finish, + .pwr_domain_pwr_down_wfi = stm32_pwr_domain_pwr_down_wfi, + .system_off = stm32_system_off, + .system_reset = stm32_system_reset, + .validate_power_state = stm32_validate_power_state, + .validate_ns_entrypoint = stm32_validate_ns_entrypoint, + .get_sys_suspend_power_state = stm32_get_sys_suspend_power_state, +}; + +/******************************************************************************* + * Export the platform specific power ops. + ******************************************************************************/ +int plat_setup_psci_ops(uintptr_t sec_entrypoint, + const plat_psci_ops_t **psci_ops) +{ + stm32_sec_entrypoint = sec_entrypoint; + *psci_ops = &stm32_psci_ops; + + return 0; +} diff --git a/plat/st/stm32mp2/stm32mp2_private.c b/plat/st/stm32mp2/stm32mp2_private.c new file mode 100644 index 00000000..5be4c5a3 --- /dev/null +++ b/plat/st/stm32mp2/stm32mp2_private.c @@ -0,0 +1,334 @@ +/* + * Copyright (c) 2023-2024, STMicroelectronics - All Rights Reserved + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <assert.h> + +#include <lib/xlat_tables/xlat_tables_v2.h> + +#include <platform_def.h> + +#define BKPR_BOOT_MODE 96U + +#if defined(IMAGE_BL31) +/* BL31 only uses the first half of the SYSRAM */ +#define MAP_SYSRAM MAP_REGION_FLAT(STM32MP_SYSRAM_BASE, \ + STM32MP_SYSRAM_SIZE / 2U, \ + MT_MEMORY | \ + MT_RW | \ + MT_SECURE | \ + MT_EXECUTE_NEVER) +#else +#define MAP_SYSRAM MAP_REGION_FLAT(STM32MP_SYSRAM_BASE, \ + STM32MP_SYSRAM_SIZE, \ + MT_MEMORY | \ + MT_RW | \ + MT_SECURE | \ + MT_EXECUTE_NEVER) +#endif + +#if STM32MP_DDR_FIP_IO_STORAGE +#define MAP_SRAM1 MAP_REGION_FLAT(SRAM1_BASE, \ + SRAM1_SIZE_FOR_TFA, \ + MT_MEMORY | \ + MT_RW | \ + MT_SECURE | \ + MT_EXECUTE_NEVER) +#endif + +#define MAP_DEVICE MAP_REGION_FLAT(STM32MP_DEVICE_BASE, \ + STM32MP_DEVICE_SIZE, \ + MT_DEVICE | \ + MT_RW | \ + MT_SECURE | \ + MT_EXECUTE_NEVER) + +#if defined(IMAGE_BL2) +static const mmap_region_t stm32mp2_mmap[] = { + MAP_SYSRAM, +#if STM32MP_DDR_FIP_IO_STORAGE + MAP_SRAM1, +#endif + MAP_DEVICE, + {0} +}; +#endif +#if defined(IMAGE_BL31) +static const mmap_region_t stm32mp2_mmap[] = { + MAP_SYSRAM, + MAP_DEVICE, + {0} +}; +#endif + +void configure_mmu(void) +{ + mmap_add(stm32mp2_mmap); + init_xlat_tables(); + + enable_mmu_el3(0); +} + +int stm32mp_map_retram(void) +{ + return mmap_add_dynamic_region(RETRAM_BASE, RETRAM_BASE, + RETRAM_SIZE, + MT_RW | MT_SECURE); +} + +int stm32mp_unmap_retram(void) +{ + return mmap_remove_dynamic_region(RETRAM_BASE, + RETRAM_SIZE); +} + +uintptr_t stm32_get_gpio_bank_base(unsigned int bank) +{ + if (bank == GPIO_BANK_Z) { + return GPIOZ_BASE; + } + + assert(bank <= GPIO_BANK_K); + + return GPIOA_BASE + (bank * GPIO_BANK_OFFSET); +} + +uint32_t stm32_get_gpio_bank_offset(unsigned int bank) +{ + if (bank == GPIO_BANK_Z) { + return 0; + } + + assert(bank <= GPIO_BANK_K); + + return bank * GPIO_BANK_OFFSET; +} + +unsigned long stm32_get_gpio_bank_clock(unsigned int bank) +{ + if (bank == GPIO_BANK_Z) { + return CK_BUS_GPIOZ; + } + + assert(bank <= GPIO_BANK_K); + + return CK_BUS_GPIOA + (bank - GPIO_BANK_A); +} + +#if STM32MP_UART_PROGRAMMER || !defined(IMAGE_BL2) +/* + * UART Management + */ +static const uintptr_t stm32mp2_uart_addresses[STM32MP_NB_OF_UART] = { + USART1_BASE, + USART2_BASE, + USART3_BASE, + UART4_BASE, + UART5_BASE, + USART6_BASE, + UART7_BASE, + UART8_BASE, + UART9_BASE, +}; + +uintptr_t get_uart_address(uint32_t instance_nb) +{ + if ((instance_nb == 0U) || + (instance_nb > STM32MP_NB_OF_UART)) { + return 0U; + } + + return stm32mp2_uart_addresses[instance_nb - 1U]; +} +#endif + +uint32_t stm32mp_get_chip_version(void) +{ + static uint32_t rev; + + if (rev != 0U) { + return rev; + } + + if (stm32_get_otp_value(REVISION_OTP, &rev) != 0) { + panic(); + } + + return rev; +} + +uint32_t stm32mp_get_chip_dev_id(void) +{ + return stm32mp_syscfg_get_chip_dev_id(); +} + +static uint32_t get_part_number(void) +{ + static uint32_t part_number; + + if (part_number != 0U) { + return part_number; + } + + if (stm32_get_otp_value(PART_NUMBER_OTP, &part_number) != 0) { + panic(); + } + + return part_number; +} + +static uint32_t get_cpu_package(void) +{ + static uint32_t package = UINT32_MAX; + + if (package == UINT32_MAX) { + if (stm32_get_otp_value(PACKAGE_OTP, &package) != 0) { + panic(); + } + } + + return (package & PACKAGE_OTP_PKG_MASK) >> PACKAGE_OTP_PKG_SHIFT; +} + +void stm32mp_get_soc_name(char name[STM32_SOC_NAME_SIZE]) +{ + char *cpu_s, *cpu_r, *pkg; + + /* MPUs Part Numbers */ + switch (get_part_number()) { + case STM32MP251A_PART_NB: + cpu_s = "251A"; + break; + case STM32MP251C_PART_NB: + cpu_s = "251C"; + break; + case STM32MP251D_PART_NB: + cpu_s = "251D"; + break; + case STM32MP251F_PART_NB: + cpu_s = "251F"; + break; + case STM32MP253A_PART_NB: + cpu_s = "253A"; + break; + case STM32MP253C_PART_NB: + cpu_s = "253C"; + break; + case STM32MP253D_PART_NB: + cpu_s = "253D"; + break; + case STM32MP253F_PART_NB: + cpu_s = "253F"; + break; + case STM32MP255A_PART_NB: + cpu_s = "255A"; + break; + case STM32MP255C_PART_NB: + cpu_s = "255C"; + break; + case STM32MP255D_PART_NB: + cpu_s = "255D"; + break; + case STM32MP255F_PART_NB: + cpu_s = "255F"; + break; + case STM32MP257A_PART_NB: + cpu_s = "257A"; + break; + case STM32MP257C_PART_NB: + cpu_s = "257C"; + break; + case STM32MP257D_PART_NB: + cpu_s = "257D"; + break; + case STM32MP257F_PART_NB: + cpu_s = "257F"; + break; + default: + cpu_s = "????"; + break; + } + + /* Package */ + switch (get_cpu_package()) { + case STM32MP25_PKG_CUSTOM: + pkg = "XX"; + break; + case STM32MP25_PKG_AL_VFBGA361: + pkg = "AL"; + break; + case STM32MP25_PKG_AK_VFBGA424: + pkg = "AK"; + break; + case STM32MP25_PKG_AI_TFBGA436: + pkg = "AI"; + break; + default: + pkg = "??"; + break; + } + + /* REVISION */ + switch (stm32mp_get_chip_version()) { + case STM32MP2_REV_A: + cpu_r = "A"; + break; + case STM32MP2_REV_B: + cpu_r = "B"; + break; + case STM32MP2_REV_X: + cpu_r = "X"; + break; + case STM32MP2_REV_Y: + cpu_r = "Y"; + break; + case STM32MP2_REV_Z: + cpu_r = "Z"; + break; + default: + cpu_r = "?"; + break; + } + + snprintf(name, STM32_SOC_NAME_SIZE, + "STM32MP%s%s Rev.%s", cpu_s, pkg, cpu_r); +} + +void stm32mp_print_cpuinfo(void) +{ + char name[STM32_SOC_NAME_SIZE]; + + stm32mp_get_soc_name(name); + NOTICE("CPU: %s\n", name); +} + +void stm32mp_print_boardinfo(void) +{ + uint32_t board_id = 0U; + + if (stm32_get_otp_value(BOARD_ID_OTP, &board_id) != 0) { + return; + } + + if (board_id != 0U) { + stm32_display_board_info(board_id); + } +} + +bool stm32mp_is_wakeup_from_standby(void) +{ + /* TODO add source code to determine if platform is waking up from standby mode */ + return false; +} + +uintptr_t stm32_get_bkpr_boot_mode_addr(void) +{ + return tamp_bkpr(BKPR_BOOT_MODE); +} + +uintptr_t stm32_ddrdbg_get_base(void) +{ + return DDRDBG_BASE; +} diff --git a/plat/st/stm32mp2/stm32mp2_syscfg.c b/plat/st/stm32mp2/stm32mp2_syscfg.c new file mode 100644 index 00000000..46c75a68 --- /dev/null +++ b/plat/st/stm32mp2/stm32mp2_syscfg.c @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2024, STMicroelectronics - All Rights Reserved + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <common/debug.h> +#include <lib/mmio.h> +#include <lib/utils_def.h> + +#include <platform_def.h> +#include <stm32mp2_private.h> + +/* + * SYSCFG register offsets (base relative) + */ +#define SYSCFG_DEVICEID 0x6400U + +/* + * SYSCFG_DEVICEID Register + */ +#define SYSCFG_DEVICEID_DEV_ID_MASK GENMASK_32(11, 0) + +/* + * @brief Get device ID from SYSCFG registers. + * @retval device ID (DEV_ID). + */ +uint32_t stm32mp_syscfg_get_chip_dev_id(void) +{ + return mmio_read_32(SYSCFG_BASE + SYSCFG_DEVICEID) & SYSCFG_DEVICEID_DEV_ID_MASK; +} diff --git a/plat/st/stm32mp2/stm32mp2_topology.c b/plat/st/stm32mp2/stm32mp2_topology.c new file mode 100644 index 00000000..cc2d58c0 --- /dev/null +++ b/plat/st/stm32mp2/stm32mp2_topology.c @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2024, STMicroelectronics - All Rights Reserved + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <lib/psci/psci.h> +#include <plat/common/platform.h> + +#include <platform_def.h> + +/* 1 cluster, all cores into */ +static const unsigned char stm32mp2_power_domain_tree_desc[] = { + PLATFORM_CLUSTER_COUNT, + PLATFORM_CORE_COUNT, +}; + +/* This function returns the platform topology */ +const unsigned char *plat_get_power_domain_tree_desc(void) +{ + return stm32mp2_power_domain_tree_desc; +} + +/******************************************************************************* + * This function implements a part of the critical interface between the psci + * generic layer and the platform that allows the former to query the platform + * to convert an MPIDR to a unique linear index. An error code (-1) is returned + * in case the MPIDR is invalid. + ******************************************************************************/ +int plat_core_pos_by_mpidr(u_register_t mpidr) +{ + unsigned int cluster_id, cpu_id; + u_register_t mpidr_copy = mpidr; + + mpidr_copy &= MPIDR_AFFINITY_MASK; + + if ((mpidr_copy & ~(MPIDR_CLUSTER_MASK | MPIDR_CPU_MASK)) != 0U) { + return -1; + } + + cluster_id = (mpidr_copy >> MPIDR_AFF1_SHIFT) & MPIDR_AFFLVL_MASK; + cpu_id = (mpidr_copy >> MPIDR_AFF0_SHIFT) & MPIDR_AFFLVL_MASK; + + if (cluster_id >= PLATFORM_CLUSTER_COUNT) { + return -1; + } + + /* + * Validate cpu_id by checking whether it represents a CPU in one + * of the two clusters present on the platform. + */ + if (cpu_id >= PLATFORM_CORE_COUNT) { + return -1; + } + + return (int)cpu_id; +} diff --git a/plat/st/stm32mp2/stm32mp2_usb_dfu.c b/plat/st/stm32mp2/stm32mp2_usb_dfu.c new file mode 100644 index 00000000..e9679640 --- /dev/null +++ b/plat/st/stm32mp2/stm32mp2_usb_dfu.c @@ -0,0 +1,21 @@ +/* + * Copyright (c) 2024, STMicroelectronics - All Rights Reserved + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <stddef.h> + +#include <drivers/usb_device.h> + +#include <usb_dfu.h> + +struct usb_handle *usb_dfu_plat_init(void) +{ + return NULL; +} + +uint8_t usb_dfu_get_phase(uint8_t alt) +{ + return 0; +} diff --git a/plat/ti/k3/common/drivers/sec_proxy/sec_proxy.c b/plat/ti/k3/common/drivers/sec_proxy/sec_proxy.c index 1bed229b..fb27336e 100644 --- a/plat/ti/k3/common/drivers/sec_proxy/sec_proxy.c +++ b/plat/ti/k3/common/drivers/sec_proxy/sec_proxy.c @@ -320,7 +320,7 @@ int k3_sec_proxy_recv(enum k3_sec_proxy_chan_id id, struct k3_sec_proxy_msg *msg i = msg->len - trail_bytes; while (trail_bytes--) { - msg->buf[i] = data_trail & 0xff; + msg->buf[i++] = data_trail & 0xff; data_trail >>= 8; } } diff --git a/plat/ti/k3/common/drivers/ti_sci/ti_sci.c b/plat/ti/k3/common/drivers/ti_sci/ti_sci.c index d04d8059..5c6ba6b3 100644 --- a/plat/ti/k3/common/drivers/ti_sci/ti_sci.c +++ b/plat/ti/k3/common/drivers/ti_sci/ti_sci.c @@ -2,7 +2,7 @@ * Texas Instruments System Control Interface Driver * Based on Linux and U-Boot implementation * - * Copyright (C) 2018-2022 Texas Instruments Incorporated - https://www.ti.com/ + * Copyright (C) 2018-2024 Texas Instruments Incorporated - https://www.ti.com/ * * SPDX-License-Identifier: BSD-3-Clause */ @@ -185,17 +185,20 @@ unlock: * * Updates the SCI information in the internal data structure. * + * @version: Structure containing the version info + * * Return: 0 if all goes well, else appropriate error message */ -int ti_sci_get_revision(struct ti_sci_msg_resp_version *rev_info) +int ti_sci_get_revision(struct ti_sci_msg_version *version) { + struct ti_sci_msg_resp_version rev_info; struct ti_sci_msg_hdr hdr; struct ti_sci_xfer xfer; int ret; ret = ti_sci_setup_one_xfer(TI_SCI_MSG_VERSION, 0x0, &hdr, sizeof(hdr), - rev_info, sizeof(*rev_info), + &rev_info, sizeof(rev_info), &xfer); if (ret) { ERROR("Message alloc failed (%d)\n", ret); @@ -208,6 +211,14 @@ int ti_sci_get_revision(struct ti_sci_msg_resp_version *rev_info) return ret; } + memcpy(version->firmware_description, rev_info.firmware_description, + sizeof(rev_info.firmware_description)); + version->abi_major = rev_info.abi_major; + version->abi_minor = rev_info.abi_minor; + version->firmware_revision = rev_info.firmware_revision; + version->sub_version = rev_info.sub_version; + version->patch_version = rev_info.patch_version; + return 0; } @@ -1731,25 +1742,39 @@ int ti_sci_enter_sleep(uint8_t proc_id, } /** - * ti_sci_init() - Basic initialization + * ti_sci_lpm_get_next_sys_mode() - Get next LPM system mode + * + * @next_mode: pointer to a variable that will store the next mode * * Return: 0 if all goes well, else appropriate error message */ -int ti_sci_init(void) +int ti_sci_lpm_get_next_sys_mode(uint8_t *next_mode) { - struct ti_sci_msg_resp_version rev_info; + struct ti_sci_msg_req_lpm_get_next_sys_mode req; + struct ti_sci_msg_resp_lpm_get_next_sys_mode resp; + struct ti_sci_xfer xfer; int ret; - ret = ti_sci_get_revision(&rev_info); - if (ret) { - ERROR("Unable to communicate with control firmware (%d)\n", ret); + if (next_mode == NULL) { + return -EINVAL; + } + + ret = ti_sci_setup_one_xfer(TI_SCI_MSG_LPM_GET_NEXT_SYS_MODE, 0, + &req, sizeof(req), + &resp, sizeof(resp), + &xfer); + if (ret != 0) { + ERROR("Message alloc failed (%d)\n", ret); + return ret; + } + + ret = ti_sci_do_xfer(&xfer); + if (ret != 0) { + ERROR("Transfer send failed (%d)\n", ret); return ret; } - INFO("SYSFW ABI: %d.%d (firmware rev 0x%04x '%s')\n", - rev_info.abi_major, rev_info.abi_minor, - rev_info.firmware_revision, - rev_info.firmware_description); + *next_mode = resp.mode; return 0; } diff --git a/plat/ti/k3/common/drivers/ti_sci/ti_sci.h b/plat/ti/k3/common/drivers/ti_sci/ti_sci.h index c702a711..06d1f8d6 100644 --- a/plat/ti/k3/common/drivers/ti_sci/ti_sci.h +++ b/plat/ti/k3/common/drivers/ti_sci/ti_sci.h @@ -2,7 +2,7 @@ * Texas Instruments System Control Interface API * Based on Linux and U-Boot implementation * - * Copyright (C) 2018-2022 Texas Instruments Incorporated - https://www.ti.com/ + * Copyright (C) 2018-2024 Texas Instruments Incorporated - https://www.ti.com/ * * SPDX-License-Identifier: BSD-3-Clause */ @@ -13,6 +13,41 @@ #include <stdint.h> #include <stdbool.h> +/** + * User exported structures. + * + * The structures in ti_sci_protocol.h are used by the internal drivers. + * These are the structures that are exported for outside use and populated + * by the internal drivers. + * + * struct ti_sci_msg_version - Structure containing version info + * + * @firmware_description: String describing the firmware + * @firmware_revision: Firmware revision + * @abi_major: Major version of the ABI that firmware supports + * @abi_minor: Minor version of the ABI that firmware supports + * @sub_version: Sub-version number of the firmware + * @patch_version: Patch-version number of the firmware. + */ +struct ti_sci_msg_version { +#define FIRMWARE_DESCRIPTION_LENGTH 32 + char firmware_description[FIRMWARE_DESCRIPTION_LENGTH]; + uint16_t firmware_revision; + uint8_t abi_major; + uint8_t abi_minor; + uint8_t sub_version; + uint8_t patch_version; +}; + +/** + * General Message + * + * ti_sci_get_revision - Get the revision of the SCI entity + * @version: Structure containing the version info + * + **/ +int ti_sci_get_revision(struct ti_sci_msg_version *version); + /** * Device control operations * @@ -217,6 +252,11 @@ int ti_sci_proc_wait_boot_status_no_wait(uint8_t proc_id, * @mode: Low power mode to enter. * @core_resume_addr: Address that core should be resumed from * after low power transition. + * - ti_sci_lpm_get_next_sys_mode - Get next LPM system mode + * + * @next_mode: pointer to a variable that will store the next mode + * + * Return: 0 if all goes well, else appropriate error message * * NOTE: for all these functions, the following are generic in nature: * Returns 0 for successful request, else returns corresponding error message. @@ -224,12 +264,6 @@ int ti_sci_proc_wait_boot_status_no_wait(uint8_t proc_id, int ti_sci_enter_sleep(uint8_t proc_id, uint8_t mode, uint64_t core_resume_addr); - -/** - * ti_sci_init() - Basic initialization - * - * Return: 0 if all goes good, else appropriate error message. - */ -int ti_sci_init(void); +int ti_sci_lpm_get_next_sys_mode(uint8_t *next_mode); #endif /* TI_SCI_H */ diff --git a/plat/ti/k3/common/drivers/ti_sci/ti_sci_protocol.h b/plat/ti/k3/common/drivers/ti_sci/ti_sci_protocol.h index 7f1c3683..cc71eac6 100644 --- a/plat/ti/k3/common/drivers/ti_sci/ti_sci_protocol.h +++ b/plat/ti/k3/common/drivers/ti_sci/ti_sci_protocol.h @@ -31,6 +31,7 @@ /* Low Power Mode Requests */ #define TI_SCI_MSG_ENTER_SLEEP 0x0301 +#define TI_SCI_MSG_LPM_GET_NEXT_SYS_MODE 0x030d /* Clock requests */ #define TI_SCI_MSG_SET_CLOCK_STATE 0x0100 @@ -133,6 +134,7 @@ struct ti_sci_msg_req_reboot { * MSG_FLAG_CAPS_LPM_MCU_ONLY: MCU only LPM * MSG_FLAG_CAPS_LPM_STANDBY: Standby LPM * MSG_FLAG_CAPS_LPM_PARTIAL_IO: Partial IO in LPM + * MSG_FLAG_CAPS_LPM_DM_MANAGED: LPM can be managed by DM * * Response to a generic message with message type TI_SCI_MSG_QUERY_FW_CAPS * providing currently available SOC/firmware capabilities. SoC that don't @@ -145,6 +147,7 @@ struct ti_sci_msg_resp_query_fw_caps { #define MSG_FLAG_CAPS_LPM_MCU_ONLY TI_SCI_MSG_FLAG(2) #define MSG_FLAG_CAPS_LPM_STANDBY TI_SCI_MSG_FLAG(3) #define MSG_FLAG_CAPS_LPM_PARTIAL_IO TI_SCI_MSG_FLAG(4) +#define MSG_FLAG_CAPS_LPM_DM_MANAGED TI_SCI_MSG_FLAG(5) uint64_t fw_caps; } __packed; @@ -764,10 +767,35 @@ struct ti_sci_msg_req_wait_proc_boot_status { */ struct ti_sci_msg_req_enter_sleep { struct ti_sci_msg_hdr hdr; +#define MSG_VALUE_SLEEP_MODE_DEEP_SLEEP 0x0 uint8_t mode; uint8_t processor_id; uint32_t core_resume_lo; uint32_t core_resume_hi; } __packed; +/** + * struct ti_sci_msg_req_lpm_get_next_sys_mode - Request for TI_SCI_MSG_LPM_GET_NEXT_SYS_MODE. + * + * @hdr Generic Header + * + * This message is used to enquire DM for selected system wide low power mode. + */ +struct ti_sci_msg_req_lpm_get_next_sys_mode { + struct ti_sci_msg_hdr hdr; +} __packed; + +/** + * struct ti_sci_msg_resp_lpm_get_next_sys_mode - Response for TI_SCI_MSG_LPM_GET_NEXT_SYS_MODE. + * + * @hdr Generic Header + * @mode The selected system wide low power mode. + * + * Note: If the mode selection is not yet locked, this API returns "not selected" mode. + */ +struct ti_sci_msg_resp_lpm_get_next_sys_mode { + struct ti_sci_msg_hdr hdr; + uint8_t mode; +} __packed; + #endif /* TI_SCI_PROTOCOL_H */ diff --git a/plat/ti/k3/common/k3_bl31_setup.c b/plat/ti/k3/common/k3_bl31_setup.c index c5f60fee..63fe0206 100644 --- a/plat/ti/k3/common/k3_bl31_setup.c +++ b/plat/ti/k3/common/k3_bl31_setup.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2018, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2017-2024, Arm Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -119,10 +119,44 @@ void bl31_plat_arch_setup(void) void bl31_platform_setup(void) { + struct ti_sci_msg_version version; + int ret; + k3_gic_driver_init(K3_GIC_BASE); k3_gic_init(); - ti_sci_init(); + ret = ti_sci_get_revision(&version); + if (ret) { + ERROR("Unable to communicate with the control firmware (%d)\n", ret); + return; + } + + INFO("SYSFW ABI: %d.%d (firmware rev 0x%04x '%s')\n", + version.abi_major, version.abi_minor, + version.firmware_revision, + version.firmware_description); + + /* + * Older firmware have a timing issue with DM that crashes few TF-A + * lite devices while trying to make calls to DM. Since there is no way + * to detect what current DM version we are running - we rely on the + * corresponding TIFS versioning to handle this check and ensure that + * the platform boots up + * + * Upgrading to TIFS version 9.1.7 along with the corresponding DM from + * ti-linux-firmware will enable this functionality. + */ + if (version.firmware_revision > 9 || + (version.firmware_revision == 9 && version.sub_version > 1) || + (version.firmware_revision == 9 && version.sub_version == 1 && + version.patch_version >= 7) + ) { + if (ti_sci_device_get(PLAT_BOARD_DEVICE_ID)) { + WARN("Unable to take system power reference\n"); + } + } else { + NOTICE("Upgrade Firmwares for Power off functionality\n"); + } } void platform_mem_init(void) diff --git a/plat/ti/k3/common/k3_psci.c b/plat/ti/k3/common/k3_psci.c index e8d73dbd..df49f489 100644 --- a/plat/ti/k3/common/k3_psci.c +++ b/plat/ti/k3/common/k3_psci.c @@ -234,7 +234,7 @@ static int k3_validate_power_state(unsigned int power_state, return PSCI_E_SUCCESS; } -static void k3_pwr_domain_suspend(const psci_power_state_t *target_state) +static void k3_pwr_domain_suspend_to_mode(const psci_power_state_t *target_state, uint8_t mode) { unsigned int core, proc_id; @@ -247,7 +247,25 @@ static void k3_pwr_domain_suspend(const psci_power_state_t *target_state) k3_pwr_domain_off(target_state); - ti_sci_enter_sleep(proc_id, 0, k3_sec_entrypoint); + ti_sci_enter_sleep(proc_id, mode, k3_sec_entrypoint); +} + +static void k3_pwr_domain_suspend_dm_managed(const psci_power_state_t *target_state) +{ + uint8_t mode = MSG_VALUE_SLEEP_MODE_DEEP_SLEEP; + int ret; + + ret = ti_sci_lpm_get_next_sys_mode(&mode); + if (ret != 0) { + ERROR("Failed to fetch next system mode\n"); + } + + k3_pwr_domain_suspend_to_mode(target_state, mode); +} + +static void k3_pwr_domain_suspend(const psci_power_state_t *target_state) +{ + k3_pwr_domain_suspend_to_mode(target_state, MSG_VALUE_SLEEP_MODE_DEEP_SLEEP); } static void k3_pwr_domain_suspend_finish(const psci_power_state_t *target_state) @@ -301,6 +319,8 @@ int plat_setup_psci_ops(uintptr_t sec_entrypoint, k3_plat_psci_ops.pwr_domain_suspend = NULL; k3_plat_psci_ops.pwr_domain_suspend_finish = NULL; k3_plat_psci_ops.get_sys_suspend_power_state = NULL; + } else if (fw_caps & MSG_FLAG_CAPS_LPM_DM_MANAGED) { + k3_plat_psci_ops.pwr_domain_suspend = k3_pwr_domain_suspend_dm_managed; } *psci_ops = &k3_plat_psci_ops; diff --git a/plat/ti/k3/common/plat_common.mk b/plat/ti/k3/common/plat_common.mk index 23efa319..8db732cb 100644 --- a/plat/ti/k3/common/plat_common.mk +++ b/plat/ti/k3/common/plat_common.mk @@ -28,6 +28,8 @@ ERRATA_A72_1319367 := 1 CRASH_REPORTING := 1 +NS_TIMER_SWITCH := 0 + # Split out RO data into a non-executable section SEPARATE_CODE_AND_RODATA := 1 diff --git a/plat/xilinx/common/include/plat_clkfunc.h b/plat/xilinx/common/include/plat_clkfunc.h new file mode 100644 index 00000000..a182f915 --- /dev/null +++ b/plat/xilinx/common/include/plat_clkfunc.h @@ -0,0 +1,13 @@ +/* + * Copyright (c) 2023-2024, Advanced Micro Devices, Inc. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + * + */ + +#ifndef PLAT_CLKFUNC_H +#define PLAT_CLKFUNC_H + +void set_cnt_freq(void); + +#endif /* PLAT_CLKFUNC_H */ diff --git a/plat/xilinx/common/include/plat_common.h b/plat/xilinx/common/include/plat_common.h index 676baa2c..2958868f 100644 --- a/plat/xilinx/common/include/plat_common.h +++ b/plat/xilinx/common/include/plat_common.h @@ -14,4 +14,16 @@ (typeof(_mask))(((_reg) & (_mask)) >> __bf_shf(_mask)); \ }) +/******************************************************************************* + * interrupt handling related constants + ******************************************************************************/ +#define ARM_IRQ_SEC_SGI_0 8U +#define ARM_IRQ_SEC_SGI_1 9U +#define ARM_IRQ_SEC_SGI_2 10U +#define ARM_IRQ_SEC_SGI_3 11U +#define ARM_IRQ_SEC_SGI_4 12U +#define ARM_IRQ_SEC_SGI_5 13U +#define ARM_IRQ_SEC_SGI_6 14U +#define ARM_IRQ_SEC_SGI_7 15U + #endif /* PLAT_COMMON_H */ diff --git a/plat/xilinx/common/include/plat_console.h b/plat/xilinx/common/include/plat_console.h index 0f8320e0..fa6021d4 100644 --- a/plat/xilinx/common/include/plat_console.h +++ b/plat/xilinx/common/include/plat_console.h @@ -8,18 +8,30 @@ #define PLAT_DT_UART_H #define DT_UART_DCC_COMPAT "arm,dcc" +#define DT_UART_CAD_COMPAT "xlnx,zynqmp-uart" +#define DT_UART_PL011_COMPAT "arm,pl011" -#if defined(PLAT_zynqmp) -#define DT_UART_COMPAT "xlnx,zynqmp-uart" -#else -#define DT_UART_COMPAT "arm,pl011" -#endif +/* Default console type is either CADENCE0 or CADENCE1 or PL011_0 or PL011_1 + * Debug console type is DCC + */ +#define CONSOLE_NONE 0 +#define CONSOLE_CDNS 1 +#define CONSOLE_PL011 2 +#define CONSOLE_DCC 3 + +typedef struct console_hd { + uint32_t clk; + uint32_t baud_rate; + uintptr_t base; + uint32_t console_scope; + uint8_t console_type; +} console_holder; typedef struct dt_uart_info_s { char compatible[30]; uintptr_t base; uint32_t baud_rate; - int32_t status; + uint8_t console_type; } dt_uart_info_t; void setup_console(void); diff --git a/plat/xilinx/common/include/plat_fdt.h b/plat/xilinx/common/include/plat_fdt.h index a1ee1e19..47a678c7 100644 --- a/plat/xilinx/common/include/plat_fdt.h +++ b/plat/xilinx/common/include/plat_fdt.h @@ -9,4 +9,8 @@ void prepare_dtb(void); +#if defined(XILINX_OF_BOARD_DTB_ADDR) +int32_t is_valid_dtb(void *fdt); +#endif + #endif /* PLAT_FDT_H */ diff --git a/plat/xilinx/common/include/plat_xfer_list.h b/plat/xilinx/common/include/plat_xfer_list.h new file mode 100644 index 00000000..cc79a2c6 --- /dev/null +++ b/plat/xilinx/common/include/plat_xfer_list.h @@ -0,0 +1,15 @@ +/* + * Copyright (c) 2023-2024, Advanced Micro Devices, Inc. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef PLAT_XFER_LIST_H +#define PLAT_XFER_LIST_H + +#include <lib/transfer_list.h> + +int32_t transfer_list_populate_ep_info(entry_point_info_t *bl32, + entry_point_info_t *bl33); + +#endif /* PLAT_XFER_LIST_H */ diff --git a/plat/xilinx/common/include/pm_api_sys.h b/plat/xilinx/common/include/pm_api_sys.h index 3fcb62f7..029bb438 100644 --- a/plat/xilinx/common/include/pm_api_sys.h +++ b/plat/xilinx/common/include/pm_api_sys.h @@ -1,6 +1,6 @@ /* * Copyright (c) 2019-2022, Xilinx, Inc. All rights reserved. - * Copyright (c) 2022-2023, Advanced Micro Devices, Inc. All rights reserved. + * Copyright (c) 2022-2024, Advanced Micro Devices, Inc. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -53,9 +53,6 @@ enum pm_ret_status pm_force_powerdown(uint32_t target, uint8_t ack, uint32_t flag); enum pm_ret_status pm_system_shutdown(uint32_t type, uint32_t subtype, uint32_t flag); -enum pm_ret_status pm_api_ioctl(uint32_t device_id, uint32_t ioctl_id, - uint32_t arg1, uint32_t arg2, uint32_t arg3, - uint32_t *value, uint32_t flag); enum pm_ret_status pm_query_data(uint32_t qid, uint32_t arg1, uint32_t arg2, uint32_t arg3, uint32_t *data, uint32_t flag); uint32_t pm_get_shutdown_scope(void); @@ -67,6 +64,7 @@ enum pm_ret_status pm_register_notifier(uint32_t device_id, uint32_t event, uint32_t wake, uint32_t enable, uint32_t flag); enum pm_ret_status pm_get_chipid(uint32_t *value); +enum pm_ret_status eemi_feature_check(uint32_t api_id, uint32_t *ret_payload); /* * Assigning of argument values into array elements. @@ -100,4 +98,9 @@ enum pm_ret_status pm_get_chipid(uint32_t *value); PM_PACK_PAYLOAD5(pl, (mid), (flag), (arg0), (arg1), (arg2), (arg3), (arg4)); \ } +#define PM_PACK_PAYLOAD7(pl, mid, flag, arg0, arg1, arg2, arg3, arg4, arg5, arg6) { \ + pl[6] = (uint32_t)(arg6); \ + PM_PACK_PAYLOAD6(pl, (mid), (flag), (arg0), (arg1), (arg2), (arg3), (arg4), (arg5)); \ +} + #endif /* PM_API_SYS_H */ diff --git a/plat/xilinx/common/include/pm_client.h b/plat/xilinx/common/include/pm_client.h index a87923ff..e9c36c3b 100644 --- a/plat/xilinx/common/include/pm_client.h +++ b/plat/xilinx/common/include/pm_client.h @@ -1,7 +1,7 @@ /* * Copyright (c) 2013-2019, Arm Limited and Contributors. All rights reserved. * Copyright (c) 2020-2022, Xilinx, Inc. All rights reserved. - * Copyright (c) 2022-2023, Advanced Micro Devices, Inc. All rights reserved. + * Copyright (c) 2022-2024, Advanced Micro Devices, Inc. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -31,7 +31,6 @@ extern const struct pm_proc *primary_proc; #if defined(PLAT_zynqmp) enum pm_ret_status pm_set_suspend_mode(uint32_t mode); -const struct pm_proc *pm_get_proc_by_node(enum pm_node_id nid); #endif /* PLAT_zynqmp */ #endif /* PM_CLIENT_H */ diff --git a/plat/xilinx/common/include/pm_common.h b/plat/xilinx/common/include/pm_common.h index c0308ab6..68d1db29 100644 --- a/plat/xilinx/common/include/pm_common.h +++ b/plat/xilinx/common/include/pm_common.h @@ -1,6 +1,6 @@ /* * Copyright (c) 2013-2018, Arm Limited and Contributors. All rights reserved. - * Copyright (c) 2022-2023, Advanced Micro Devices, Inc. All rights reserved. + * Copyright (c) 2022-2024, Advanced Micro Devices, Inc. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -24,8 +24,9 @@ #define CRC_ORDER 16U #define CRC_POLYNOM 0x8005U #else -#define PAYLOAD_ARG_CNT 6U +#define PAYLOAD_ARG_CNT 7U #endif +#define RET_PAYLOAD_ARG_CNT 6U #define PAYLOAD_ARG_SIZE 4U /* size in bytes */ #define TZ_VERSION_MAJOR 1 diff --git a/plat/xilinx/common/include/pm_defs.h b/plat/xilinx/common/include/pm_defs.h index 9cdb0ba0..99206114 100644 --- a/plat/xilinx/common/include/pm_defs.h +++ b/plat/xilinx/common/include/pm_defs.h @@ -1,6 +1,6 @@ /* * Copyright (c) 2019-2022, Xilinx, Inc. All rights reserved. - * Copyright (c) 2022-2023, Advanced Micro Devices, Inc. All rights reserved. + * Copyright (c) 2022-2024, Advanced Micro Devices, Inc. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -18,6 +18,7 @@ /* State arguments of the self suspend */ #define PM_STATE_CPU_IDLE 0x0U +#define PM_STATE_CPU_OFF 0x1U #define PM_STATE_SUSPEND_TO_RAM 0xFU #define MAX_LATENCY (~0U) @@ -34,6 +35,7 @@ (uint32_t)XPM_NODESUBCL_DEV_PERIPH, \ (uint32_t)XPM_NODETYPE_DEV_PERIPH, (IDX)) +#define TF_A_FEATURE_CHECK 0xa00U #define PM_GET_CALLBACK_DATA 0xa01U #define PM_GET_TRUSTZONE_VERSION 0xa03U #define TF_A_PM_REGISTER_SGI 0xa04U @@ -94,8 +96,8 @@ enum { IOCTL_GET_LAST_RESET_REASON = 23, /* AI engine NPI ISR clear */ IOCTL_AIE_ISR_CLEAR = 24, - /* Register SGI to TF-A */ - IOCTL_SET_SGI = 25, + IOCTL_UFS_TXRX_CFGRDY_GET = 40, + IOCTL_UFS_SRAM_CSR_SEL = 41, }; /** diff --git a/plat/xilinx/common/include/pm_svc_main.h b/plat/xilinx/common/include/pm_svc_main.h index 4cf77276..000f198e 100644 --- a/plat/xilinx/common/include/pm_svc_main.h +++ b/plat/xilinx/common/include/pm_svc_main.h @@ -1,6 +1,6 @@ /* * Copyright (c) 2019-2022, Xilinx, Inc. All rights reserved. - * Copyright (c) 2022-2023, Advanced Micro Devices, Inc. All rights reserved. + * Copyright (c) 2022-2024, Advanced Micro Devices, Inc. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -10,6 +10,10 @@ #include <pm_common.h> +extern bool pwrdwn_req_received; + +#define PASS_THROUGH_FW_CMD_ID U(0xfff) + /******************************************************************************/ /** * SECURE_REDUNDANT_CALL() - Adds redundancy to the function call. This is to @@ -30,6 +34,7 @@ status_tmp = function(__VA_ARGS__); \ } +void request_cpu_pwrdwn(void); int32_t pm_setup(void); uint64_t pm_smc_handler(uint32_t smc_fid, uint64_t x1, uint64_t x2, uint64_t x3, uint64_t x4, const void *cookie, void *handle, diff --git a/plat/xilinx/common/ipi.c b/plat/xilinx/common/ipi.c index 399d283d..d7c70f32 100644 --- a/plat/xilinx/common/ipi.c +++ b/plat/xilinx/common/ipi.c @@ -70,7 +70,7 @@ static inline int is_ipi_mb_within_range(uint32_t local, uint32_t remote) { int ret = 1; - if (remote >= ipi_total || local >= ipi_total) { + if ((remote >= ipi_total) || (local >= ipi_total)) { ret = 0; } @@ -144,11 +144,11 @@ int ipi_mb_enquire_status(uint32_t local, uint32_t remote) uint32_t status; status = mmio_read_32(IPI_REG_BASE(local) + IPI_OBR_OFFSET); - if (status & IPI_BIT_MASK(remote)) { + if ((status & IPI_BIT_MASK(remote)) != 0U) { ret |= IPI_MB_STATUS_SEND_PENDING; } status = mmio_read_32(IPI_REG_BASE(local) + IPI_ISR_OFFSET); - if (status & IPI_BIT_MASK(remote)) { + if ((status & IPI_BIT_MASK(remote)) != 0U) { ret |= IPI_MB_STATUS_RECV_PENDING; } @@ -170,11 +170,11 @@ void ipi_mb_notify(uint32_t local, uint32_t remote, uint32_t is_blocking) mmio_write_32(IPI_REG_BASE(local) + IPI_TRIG_OFFSET, IPI_BIT_MASK(remote)); - if (is_blocking) { + if (is_blocking != 0U) { do { status = mmio_read_32(IPI_REG_BASE(local) + IPI_OBR_OFFSET); - } while (status & IPI_BIT_MASK(remote)); + } while ((status & IPI_BIT_MASK(remote)) != 0U); } } diff --git a/plat/xilinx/common/ipi_mailbox_service/ipi_mailbox_svc.c b/plat/xilinx/common/ipi_mailbox_service/ipi_mailbox_svc.c index 434cd888..9a0149be 100644 --- a/plat/xilinx/common/ipi_mailbox_service/ipi_mailbox_svc.c +++ b/plat/xilinx/common/ipi_mailbox_service/ipi_mailbox_svc.c @@ -70,6 +70,9 @@ uint64_t ipi_smc_handler(uint32_t smc_fid, uint64_t x1, uint64_t x2, uint64_t x3, uint64_t x4, const void *cookie, void *handle, uint64_t flags) { + (void) x4; + (void) flags; + (void) cookie; int32_t ret; uint32_t ipi_local_id; uint32_t ipi_remote_id; @@ -91,7 +94,7 @@ uint64_t ipi_smc_handler(uint32_t smc_fid, uint64_t x1, uint64_t x2, /* Validate IPI mailbox access */ ret = ipi_mb_validate(ipi_local_id, ipi_remote_id, is_secure); - if (ret) + if (ret != 0) SMC_RET1(handle, ret); switch (GET_SMC_NUM(smc_fid)) { @@ -103,11 +106,11 @@ uint64_t ipi_smc_handler(uint32_t smc_fid, uint64_t x1, uint64_t x2, SMC_RET1(handle, 0); case IPI_MAILBOX_STATUS_ENQUIRY: { - int32_t disable_irq; + int32_t disable_interrupt; - disable_irq = (x3 & IPI_SMC_ENQUIRY_DIRQ_MASK) ? 1 : 0; + disable_interrupt = (x3 & IPI_SMC_ENQUIRY_DIRQ_MASK) ? 1 : 0; ret = ipi_mb_enquire_status(ipi_local_id, ipi_remote_id); - if ((ret & IPI_MB_STATUS_RECV_PENDING) && disable_irq) + if ((ret & IPI_MB_STATUS_RECV_PENDING) && disable_interrupt) ipi_mb_disable_irq(ipi_local_id, ipi_remote_id); SMC_RET1(handle, ret); } @@ -121,11 +124,11 @@ uint64_t ipi_smc_handler(uint32_t smc_fid, uint64_t x1, uint64_t x2, } case IPI_MAILBOX_ACK: { - int32_t enable_irq; + int32_t enable_interrupt; - enable_irq = (x3 & IPI_SMC_ACK_EIRQ_MASK) ? 1 : 0; + enable_interrupt = (x3 & IPI_SMC_ACK_EIRQ_MASK) ? 1 : 0; ipi_mb_ack(ipi_local_id, ipi_remote_id); - if (enable_irq) + if (enable_interrupt != 0) ipi_mb_enable_irq(ipi_local_id, ipi_remote_id); SMC_RET1(handle, 0); } diff --git a/plat/xilinx/common/plat_clkfunc.c b/plat/xilinx/common/plat_clkfunc.c new file mode 100644 index 00000000..f7910de6 --- /dev/null +++ b/plat/xilinx/common/plat_clkfunc.c @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2023-2024, Advanced Micro Devices, Inc. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ +#include <common/debug.h> +#include <lib/mmio.h> +#include <plat/common/platform.h> + +#include <platform_def.h> +#include <plat_clkfunc.h> +#include <plat_private.h> + +uint32_t plat_get_syscnt_freq2(void) +{ + uint32_t counter_freq = 0; + uint32_t ret = 0; + + counter_freq = mmio_read_32(IOU_SCNTRS_BASE + + IOU_SCNTRS_BASE_FREQ_OFFSET); + if (counter_freq != 0U) { + ret = counter_freq; + } else { + INFO("Indicates counter frequency %dHz setting to %dHz\n", + counter_freq, cpu_clock); + ret = cpu_clock; + } + + return ret; +} + +void set_cnt_freq(void) +{ + uint64_t counter_freq; + + /* Configure counter frequency */ + counter_freq = read_cntfrq_el0(); + if (counter_freq == 0U) { + write_cntfrq_el0(plat_get_syscnt_freq2()); + } +} diff --git a/plat/xilinx/common/plat_console.c b/plat/xilinx/common/plat_console.c index 0c0e74b9..681226f9 100644 --- a/plat/xilinx/common/plat_console.c +++ b/plat/xilinx/common/plat_console.c @@ -18,13 +18,69 @@ #include <drivers/console.h> #include <libfdt.h> #include <plat_console.h> +#include <plat_fdt.h> #include <platform_def.h> #include <plat_private.h> -static console_t console; +#if !(CONSOLE_IS(none)) +static console_t boot_console; +static console_holder boot_hd_console; +#if defined(CONSOLE_RUNTIME) +static console_t runtime_console; +static console_holder rt_hd_console; +#endif + +#if ((CONSOLE_IS(dtb) || RT_CONSOLE_IS(dtb)) && defined(XILINX_OF_BOARD_DTB_ADDR)) && \ + (!defined(PLAT_zynqmp) || (defined(PLAT_zynqmp) && \ + !IS_TFA_IN_OCM(BL31_BASE))) +static dt_uart_info_t dt_uart_info; +#endif -#if (defined(XILINX_OF_BOARD_DTB_ADDR) && !IS_TFA_IN_OCM(BL31_BASE)) +/** + * register_console() - Registers the uart with console list. + * @consoleh: Console holder structure with UART base address, + * UART clock, UART buad rate, flags & console type + * @console: Pointer to the console information structure. + */ +static void register_console(const console_holder *consoleh, console_t *console) +{ + int32_t rc = 0; + + switch (consoleh->console_type) { +#if defined(PLAT_zynqmp) + case CONSOLE_CDNS: + rc = console_cdns_register(consoleh->base, + consoleh->clk, + consoleh->baud_rate, + console); + break; +#else + case CONSOLE_PL011: + rc = console_pl011_register(consoleh->base, + consoleh->clk, + consoleh->baud_rate, + console); + break; +#endif + case CONSOLE_DCC: + rc = console_dcc_register(console); + break; + default: + INFO("Invalid console type\n"); + break; + } + + if (rc == 0) { + panic(); + } + + console_set_scope(console, consoleh->console_scope); +} + +#if ((CONSOLE_IS(dtb) || RT_CONSOLE_IS(dtb)) && defined(XILINX_OF_BOARD_DTB_ADDR)) && \ + (!defined(PLAT_zynqmp) || (defined(PLAT_zynqmp) && \ + !IS_TFA_IN_OCM(BL31_BASE))) /** * get_baudrate() - Get the baudrate form DTB. * @dtb: Address of the Device Tree Blob (DTB). @@ -102,34 +158,56 @@ static uint32_t get_node_status(void *dtb, int node) * @node: Node address in the device tree. * @dtb: Address of the Device Tree Blob(DTB). * - * Return: On success, it returns 1; on failure, it returns an 0. + * Return: On success, it returns 0; on failure, it returns -1 or -FDT_ERR_NOTFOUND. */ -static uint32_t fdt_add_uart_info(dt_uart_info_t *info, int node, void *dtb) +static int32_t fdt_add_uart_info(dt_uart_info_t *info, int node, void *dtb) { uintptr_t base_addr; const char *com; - uint32_t ret = 0; + int32_t ret = 0; + uint32_t status; com = fdt_getprop(dtb, node, "compatible", NULL); if (com != NULL) { strlcpy(info->compatible, com, sizeof(info->compatible)); } else { ERROR("Compatible property not found in DTB node\n"); - ret = -FDT_ERR_NOTFOUND; + ret = -FDT_ERR_NOTFOUND; goto error; } - ret = fdt_get_reg_props_by_index(dtb, node, 0, &base_addr, NULL); - if (ret >= 0) { - info->base = base_addr; - } else { - ERROR("Failed to retrieve base address. Error code: %d\n", ret); - ret = -FDT_ERR_NOTFOUND; + status = get_node_status(dtb, node); + if (status == 0) { + ERROR("Uart node is disabled in DTB\n"); + ret = -FDT_ERR_NOTFOUND; goto error; } - info->status = get_node_status(dtb, node); - info->baud_rate = get_baudrate(dtb); + if (strncmp(info->compatible, DT_UART_DCC_COMPAT, strlen(DT_UART_DCC_COMPAT)) != 0) { + ret = fdt_get_reg_props_by_index(dtb, node, 0, &base_addr, NULL); + if (ret >= 0) { + info->base = base_addr; + } else { + ERROR("Failed to retrieve base address. Error code: %d\n", ret); + ret = -FDT_ERR_NOTFOUND; + goto error; + } + + info->baud_rate = get_baudrate(dtb); + + if (strncmp(info->compatible, DT_UART_CAD_COMPAT, + strlen(DT_UART_CAD_COMPAT)) == 0) { + info->console_type = CONSOLE_CDNS; + } else if (strncmp(info->compatible, DT_UART_PL011_COMPAT, + strlen(DT_UART_PL011_COMPAT)) == 0) { + info->console_type = CONSOLE_PL011; + } else { + ERROR("Incompatible uart node in DTB\n"); + ret = -FDT_ERR_NOTFOUND; + } + } else { + info->console_type = CONSOLE_DCC; + } error: return ret; @@ -143,204 +221,93 @@ error: */ static int fdt_get_uart_info(dt_uart_info_t *info) { - int node, ret = 0; + int node = 0, ret = 0; void *dtb = (void *)XILINX_OF_BOARD_DTB_ADDR; - if (fdt_check_header(dtb) != 0) { - ERROR("Can't read DT at %p\n", dtb); - ret = -FDT_ERR_NOTFOUND; - goto error; - } - - ret = fdt_open_into(dtb, dtb, XILINX_OF_BOARD_DTB_MAX_SIZE); + ret = is_valid_dtb(dtb); if (ret < 0) { ERROR("Invalid Device Tree at %p: error %d\n", dtb, ret); - ret = -FDT_ERR_NOTFOUND; goto error; } node = fdt_get_stdout_node_offset(dtb); if (node < 0) { ERROR("DT get stdout node failed : %d\n", node); - ret = -FDT_ERR_NOTFOUND; goto error; } ret = fdt_add_uart_info(info, node, dtb); if (ret < 0) { ERROR("Failed to add DT UART info: %d\n", ret); - ret = -FDT_ERR_NOTFOUND; goto error; } error: return ret; } - -/** - * check_fdt_uart_info() - Check early uart info with DTB uart info. - * @info: Pointer to the UART information structure. - * - * Return: On success, it returns 0; on failure, it returns an error+reason. - */ -static int check_fdt_uart_info(dt_uart_info_t *info) -{ - uint32_t ret = 0; - - if (info->status == 0) { - ret = -ENODEV; - goto error; - } - - if ((info->base == console.base) && - (info->baud_rate == UART_BAUDRATE) && !CONSOLE_IS(dcc)) { - ret = -ENODEV; - goto error; - } - -error: - return ret; -} - -/** - * console_boot_end() - Unregister the console_t instance form the console list. - * @boot_console: Pointer to the console information structure. - */ -static void console_boot_end(console_t *boot_console) -{ - if (CONSOLE_IS(dcc)) { - console_dcc_unregister(); - } else { - console_flush(); - (void)console_unregister(boot_console); - } -} - -/** - * setup_runtime_console() - Registers the runtime uart with console list. - * @clock: UART clock. - * @info: Pointer to the UART information structure. - */ -static void setup_runtime_console(uint32_t clock, dt_uart_info_t *info) -{ - static console_t bl31_runtime_console; - uint32_t rc; - -#if defined(PLAT_zynqmp) - rc = console_cdns_register(info->base, - clock, - info->baud_rate, - &bl31_runtime_console); -#else - rc = console_pl011_register(info->base, - clock, - info->baud_rate, - &bl31_runtime_console); #endif - if (rc == 0) { - panic(); - } - - console_set_scope(&bl31_runtime_console, - CONSOLE_FLAG_BOOT | CONSOLE_FLAG_RUNTIME | - CONSOLE_FLAG_CRASH); -} - -/** - * runtime_console_init() - Initializes the run time console information. - * @uart_info: Pointer to the UART information structure. - * @bl31_boot_console: Pointer to the console information structure. - * @clock: UART clock. - * - * Return: On success, it returns 0; on failure, it returns an error+reason; - */ -static int32_t runtime_console_init(dt_uart_info_t *uart_info, - console_t *bl31_boot_console, - uint32_t clock) +void setup_console(void) { - int32_t rc = 0; - - /* Parse UART information from Device Tree Blob (DTB) */ - rc = fdt_get_uart_info(uart_info); - if (rc < 0) { - rc = -FDT_ERR_NOTFOUND; - } - - if (strncmp(uart_info->compatible, DT_UART_COMPAT, - strlen(DT_UART_COMPAT)) == 0) { - - if (check_fdt_uart_info(uart_info) == 0) { - setup_runtime_console(clock, uart_info); - console_boot_end(bl31_boot_console); - INFO("Runtime console setup\n"); - } else { - INFO("Early console and DTB console are same\n"); - } - } else if (strncmp(uart_info->compatible, DT_UART_DCC_COMPAT, - strlen(DT_UART_DCC_COMPAT)) == 0) { - rc = console_dcc_register(); - if (rc == 0) { - panic(); + /* This is hardcoded console setup just in case that DTB console fails */ + boot_hd_console.base = (uintptr_t)UART_BASE; + boot_hd_console.baud_rate = (uint32_t)UART_BAUDRATE; + boot_hd_console.clk = get_uart_clk(); + boot_hd_console.console_scope = CONSOLE_FLAG_BOOT | CONSOLE_FLAG_CRASH; + boot_hd_console.console_type = UART_TYPE; + + /* For DT code decoding uncomment console registration below */ + /* register_console(&boot_hd_console, &boot_console); */ + +#if ((CONSOLE_IS(dtb) || RT_CONSOLE_IS(dtb)) && defined(XILINX_OF_BOARD_DTB_ADDR)) && \ + (!defined(PLAT_zynqmp) || (defined(PLAT_zynqmp) && \ + !IS_TFA_IN_OCM(BL31_BASE))) + /* Parse DTB console for UART information */ + if (fdt_get_uart_info(&dt_uart_info) == 0) { + if (CONSOLE_IS(dtb)) { + boot_hd_console.base = dt_uart_info.base; + boot_hd_console.baud_rate = dt_uart_info.baud_rate; + boot_hd_console.console_type = dt_uart_info.console_type; } - console_boot_end(bl31_boot_console); } else { - WARN("BL31: No console device found in DT.\n"); + ERROR("Failed to initialize DT console or console node is disabled\n"); } - - return rc; -} #endif -void setup_console(void) -{ - uint32_t rc; - uint32_t uart_clk = get_uart_clk(); + /* Initialize the boot console */ + register_console(&boot_hd_console, &boot_console); -#if defined(PLAT_zynqmp) - if (CONSOLE_IS(cadence) || (CONSOLE_IS(cadence1))) { - rc = console_cdns_register(UART_BASE, - uart_clk, - UART_BAUDRATE, - &console); - if (rc == 0) { - panic(); - } + INFO("BL31: Early console setup\n"); - console_set_scope(&console, CONSOLE_FLAG_BOOT | - CONSOLE_FLAG_RUNTIME | CONSOLE_FLAG_CRASH); - } +#ifdef CONSOLE_RUNTIME +#if (RT_CONSOLE_IS(dtb) && defined(XILINX_OF_BOARD_DTB_ADDR)) && \ + (!defined(PLAT_zynqmp) || (defined(PLAT_zynqmp) && \ + !IS_TFA_IN_OCM(BL31_BASE))) + rt_hd_console.base = dt_uart_info.base; + rt_hd_console.baud_rate = dt_uart_info.baud_rate; + rt_hd_console.console_type = dt_uart_info.console_type; #else - if (CONSOLE_IS(pl011) || (CONSOLE_IS(pl011_1))) { - /* Initialize the console to provide early debug support */ - rc = console_pl011_register((uint32_t)UART_BASE, - uart_clk, - (uint32_t)UART_BAUDRATE, - &console); - if (rc == 0) { - panic(); - } - - console_set_scope(&console, CONSOLE_FLAG_BOOT | - CONSOLE_FLAG_RUNTIME | CONSOLE_FLAG_CRASH); - } + rt_hd_console.base = (uintptr_t)RT_UART_BASE; + rt_hd_console.baud_rate = (uint32_t)UART_BAUDRATE; + rt_hd_console.console_type = RT_UART_TYPE; #endif - if (CONSOLE_IS(dcc)) { - /* Initialize the dcc console for debug */ - rc = console_dcc_register(); - if (rc == 0) { - panic(); - } - } - INFO("BL31: Early console setup\n"); -#if (defined(XILINX_OF_BOARD_DTB_ADDR) && !IS_TFA_IN_OCM(BL31_BASE)) - static dt_uart_info_t uart_info = {0}; + if ((rt_hd_console.console_type == boot_hd_console.console_type) && + (rt_hd_console.base == boot_hd_console.base)) { + console_set_scope(&boot_console, + CONSOLE_FLAG_BOOT | CONSOLE_FLAG_CRASH | CONSOLE_FLAG_RUNTIME); + INFO("Successfully initialized runtime console\n"); + } else { + rt_hd_console.clk = get_uart_clk(); + rt_hd_console.console_scope = CONSOLE_FLAG_RUNTIME; - /* Initialize the runtime console using UART information from the DTB */ - rc = runtime_console_init(&uart_info, &console, uart_clk); - if (rc < 0) { - ERROR("Failed to initialize runtime console: %d\n", rc); + register_console(&rt_hd_console, &runtime_console); + INFO("Successfully initialized new runtime console\n"); } #endif } +#else +void setup_console(void) +{ +} +#endif diff --git a/plat/xilinx/common/plat_fdt.c b/plat/xilinx/common/plat_fdt.c index de5d1a1f..4ad7b2d0 100644 --- a/plat/xilinx/common/plat_fdt.c +++ b/plat/xilinx/common/plat_fdt.c @@ -13,6 +13,91 @@ #include <plat_fdt.h> #include <platform_def.h> +#if defined(XILINX_OF_BOARD_DTB_ADDR) + +#define FIT_CONFS_PATH "/configurations" + +static uint8_t is_fit_image(void *dtb) +{ + int64_t confs_noffset; + uint8_t status = 0; + + confs_noffset = fdt_path_offset(dtb, FIT_CONFS_PATH); + /*confs_noffset is only present on FIT image */ + if (confs_noffset < 0) { + status = 0; + } else { + status = 1; + } + + return status; +} + +int32_t is_valid_dtb(void *fdt) +{ + int32_t ret = 0; + + if (fdt_check_header(fdt) != 0) { + ERROR("Can't read DT at %p\n", fdt); + ret = -FDT_ERR_NOTFOUND; + goto error; + } + + ret = fdt_open_into(fdt, fdt, XILINX_OF_BOARD_DTB_MAX_SIZE); + if (ret < 0) { + ERROR("Invalid Device Tree at %p: error %d\n", fdt, ret); + ret = -FDT_ERR_NOTFOUND; + goto error; + } + + if (is_fit_image(fdt) != 0U) { + WARN("FIT image detected, TF-A will not update DTB for DDR address space\n"); + ret = -FDT_ERR_NOTFOUND; + } +error: + return ret; +} + +static int add_mmap_dynamic_region(unsigned long long base_pa, uintptr_t base_va, + size_t size, unsigned int attr) +{ + int ret = 0; +#if defined(PLAT_XLAT_TABLES_DYNAMIC) + ret = mmap_add_dynamic_region(base_pa, base_va, size, attr); + if (ret != 0) { + WARN("Failed to add dynamic region for dtb: error %d\n", + ret); + } +#endif + return ret; +} + +static int remove_mmap_dynamic_region(uintptr_t base_va, size_t size) +{ + int ret = 0; +#if defined(PLAT_XLAT_TABLES_DYNAMIC) + ret = mmap_remove_dynamic_region(base_va, size); + if (ret != 0) { + WARN("Failed to remove dynamic region for dtb:error %d\n", + ret); + } +#endif + return ret; +} +#endif + +#if defined(XILINX_OF_BOARD_DTB_ADDR) +static int check_fdt_reserved_memory(void *dtb, const char *node_name) +{ + int offset = fdt_path_offset(dtb, "/reserved-memory"); + + if (offset >= 0) { + offset = fdt_subnode_offset(dtb, offset, node_name); + } + return offset; +} +#endif + void prepare_dtb(void) { #if defined(XILINX_OF_BOARD_DTB_ADDR) @@ -24,75 +109,51 @@ void prepare_dtb(void) if (!IS_TFA_IN_OCM(BL31_BASE)) { -#if defined(PLAT_XLAT_TABLES_DYNAMIC) - map_ret = mmap_add_dynamic_region((unsigned long long)dtb, - (uintptr_t)dtb, - XILINX_OF_BOARD_DTB_MAX_SIZE, - MT_MEMORY | MT_RW | MT_NS); - if (map_ret != 0) { - WARN("Failed to add dynamic region for dtb: error %d\n", - map_ret); - } -#endif - - if (!map_ret) { + map_ret = add_mmap_dynamic_region((unsigned long long)dtb, + (uintptr_t)dtb, + XILINX_OF_BOARD_DTB_MAX_SIZE, + MT_MEMORY | MT_RW | MT_NS); + if (map_ret == 0) { /* Return if no device tree is detected */ - if (fdt_check_header(dtb) != 0) { - NOTICE("Can't read DT at %p\n", dtb); - } else { - ret = fdt_open_into(dtb, dtb, XILINX_OF_BOARD_DTB_MAX_SIZE); - - if (ret < 0) { - ERROR("Invalid Device Tree at %p: error %d\n", - dtb, ret); - } else { - - if (dt_add_psci_node(dtb)) { - WARN("Failed to add PSCI Device Tree node\n"); - } + if (is_valid_dtb(dtb) == 0) { + if (dt_add_psci_node(dtb)) { + WARN("Failed to add PSCI Device Tree node\n"); + } - if (dt_add_psci_cpu_enable_methods(dtb)) { - WARN("Failed to add PSCI cpu enable methods in DT\n"); - } + if (dt_add_psci_cpu_enable_methods(dtb)) { + WARN("Failed to add PSCI cpu enable methods in DT\n"); + } + /* Check reserved memory set in DT*/ + ret = check_fdt_reserved_memory(dtb, "tf-a"); + if (ret < 0) { /* Reserve memory used by Trusted Firmware. */ - ret = fdt_add_reserved_memory(dtb, - "tf-a", - BL31_BASE, - BL31_LIMIT - - - BL31_BASE); + ret = fdt_add_reserved_memory(dtb, "tf-a", + BL31_BASE, + BL31_LIMIT - BL31_BASE); if (ret < 0) { WARN("Failed to add reserved memory nodes for BL31 to DT.\n"); } - ret = fdt_pack(dtb); - if (ret < 0) { - WARN("Failed to pack dtb at %p: error %d\n", - dtb, ret); - } - flush_dcache_range((uintptr_t)dtb, - fdt_blob_size(dtb)); - - INFO("Changed device tree to advertise PSCI and reserved memories.\n"); - + } else { + WARN("Reserved memory pre-exists in DT.\n"); } - } - } + ret = fdt_pack(dtb); + if (ret < 0) { + WARN("Failed to pack dtb at %p: error %d\n", dtb, ret); + } + flush_dcache_range((uintptr_t)dtb, fdt_blob_size(dtb)); + INFO("Changed device tree to advertise PSCI and reserved memories.\n"); + } -#if defined(PLAT_XLAT_TABLES_DYNAMIC) - if (!map_ret) { - ret = mmap_remove_dynamic_region((uintptr_t)dtb, - XILINX_OF_BOARD_DTB_MAX_SIZE); + ret = remove_mmap_dynamic_region((uintptr_t)dtb, + XILINX_OF_BOARD_DTB_MAX_SIZE); if (ret != 0) { - WARN("Failed to remove dynamic region for dtb:error %d\n", - ret); + WARN("Failed to remove mmap dynamic regions.\n"); } } -#endif } - #endif } diff --git a/plat/xilinx/common/plat_startup.c b/plat/xilinx/common/plat_startup.c index 5beb7658..149ba2dc 100644 --- a/plat/xilinx/common/plat_startup.c +++ b/plat/xilinx/common/plat_startup.c @@ -237,8 +237,8 @@ enum xbl_handoff xbl_handover(entry_point_info_t *bl32, } target_secure = get_xbl_ss(&HandoffParams->partition[i]); - if (target_secure == XBL_FLAGS_SECURE && - target_el == XBL_FLAGS_EL2) { + if ((target_secure == XBL_FLAGS_SECURE) && + (target_el == XBL_FLAGS_EL2)) { WARN("BL31: invalid security state (%i) for exception level (%i)\n", target_secure, target_el); continue; @@ -284,7 +284,7 @@ enum xbl_handoff xbl_handover(entry_point_info_t *bl32, } VERBOSE("Setting up %s entry point to:%" PRIx64 ", el:%x\n", - target_secure == XBL_FLAGS_SECURE ? "BL32" : "BL33", + (target_secure == XBL_FLAGS_SECURE) ? "BL32" : "BL33", HandoffParams->partition[i].entry_point, target_el); image->pc = HandoffParams->partition[i].entry_point; diff --git a/plat/xilinx/common/plat_xfer_list.c b/plat/xilinx/common/plat_xfer_list.c new file mode 100644 index 00000000..eae7ce4e --- /dev/null +++ b/plat/xilinx/common/plat_xfer_list.c @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2023-2024, Advanced Micro Devices, Inc. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ +#include <stddef.h> +#include <arch_helpers.h> +#include <common/debug.h> +#include <lib/transfer_list.h> + +/* + * FIXME: This address should come from firmware before TF-A runs + * Having this to make sure the transfer list functionality works + */ +#define FW_HANDOFF_BASE U(0x1200000) +#define FW_HANDOFF_SIZE U(0x600000) + +static struct transfer_list_header *tl_hdr; + +int32_t transfer_list_populate_ep_info(entry_point_info_t *bl32, + entry_point_info_t *bl33) +{ + struct transfer_list_entry *te = NULL; + struct entry_point_info *ep; + int32_t ret; + + tl_hdr = (struct transfer_list_header *)FW_HANDOFF_BASE; + ret = transfer_list_check_header(tl_hdr); + if ((ret == TL_OPS_ALL) || (ret == TL_OPS_RO)) { + transfer_list_dump(tl_hdr); + while ((te = transfer_list_next(tl_hdr, te)) != NULL) { + ep = transfer_list_entry_data(te); + if (te->tag_id == TL_TAG_EXEC_EP_INFO64) { + switch (GET_SECURITY_STATE(ep->h.attr)) { + case NON_SECURE: + *bl33 = *ep; + continue; + case SECURE: + *bl32 = *ep; + continue; + default: + ERROR("Unrecognized Image Security State %lu\n", + GET_SECURITY_STATE(ep->h.attr)); + ret = TL_OPS_NON; + } + } + } + } + return ret; +} diff --git a/plat/xilinx/common/pm_service/pm_api_sys.c b/plat/xilinx/common/pm_service/pm_api_sys.c index ffc39bbe..e9c5f138 100644 --- a/plat/xilinx/common/pm_service/pm_api_sys.c +++ b/plat/xilinx/common/pm_service/pm_api_sys.c @@ -1,6 +1,6 @@ /* * Copyright (c) 2019-2022, Xilinx, Inc. All rights reserved. - * Copyright (c) 2022-2023, Advanced Micro Devices, Inc. All rights reserved. + * Copyright (c) 2022-2024, Advanced Micro Devices, Inc. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -50,7 +50,7 @@ void pm_client_set_wakeup_sources(uint32_t node_id) { uint32_t reg_num, device_id; uint8_t pm_wakeup_nodes_set[XPM_NODEIDX_DEV_MAX] = {0U}; - uint32_t isenabler1 = PLAT_GICD_BASE_VALUE + GICD_ISENABLER + 4U; + uint32_t isenabler1 = PLAT_ARM_GICD_BASE + GICD_ISENABLER + 4U; zeromem(&pm_wakeup_nodes_set, (u_register_t)sizeof(pm_wakeup_nodes_set)); @@ -122,7 +122,7 @@ enum pm_ret_status pm_handle_eemi_call(uint32_t flag, uint32_t x0, uint32_t x1, } PM_PACK_PAYLOAD6(payload, module_id, flag, x0, x1, x2, x3, x4, x5); - return pm_ipi_send_sync(primary_proc, payload, (uint32_t *)result, PAYLOAD_ARG_CNT); + return pm_ipi_send_sync(primary_proc, payload, (uint32_t *)result, RET_PAYLOAD_ARG_CNT); } /** @@ -286,112 +286,6 @@ enum pm_ret_status pm_get_callbackdata(uint32_t *data, size_t count, uint32_t fl return ret; } -/** - * pm_pll_set_param() - Set PLL parameter. - * @clk_id: PLL clock ID. - * @param: PLL parameter ID. - * @value: Value to set for PLL parameter. - * @flag: 0 - Call from secure source. - * 1 - Call from non-secure source. - * - * This API is deprecated and maintained here for backward compatibility. - * New use of this API should be avoided for versal platform. - * This API and its use cases will be removed for versal platform. - * - * Return: Returns status, either success or error+reason. - * - */ -enum pm_ret_status pm_pll_set_param(uint32_t clk_id, uint32_t param, - uint32_t value, uint32_t flag) -{ - uint32_t payload[PAYLOAD_ARG_CNT]; - - /* Send request to the PMC */ - PM_PACK_PAYLOAD4(payload, LIBPM_MODULE_ID, flag, PM_PLL_SET_PARAMETER, - clk_id, param, value); - - return pm_ipi_send_sync(primary_proc, payload, NULL, 0); -} - -/** - * pm_pll_get_param() - Get PLL parameter value. - * @clk_id: PLL clock ID. - * @param: PLL parameter ID. - * @value: Buffer to store PLL parameter value. - * @flag: 0 - Call from secure source. - * 1 - Call from non-secure source. - * - * This API is deprecated and maintained here for backward compatibility. - * New use of this API should be avoided for versal platform. - * This API and its use cases will be removed for versal platform. - * - * Return: Returns status, either success or error+reason. - * - */ -enum pm_ret_status pm_pll_get_param(uint32_t clk_id, uint32_t param, - uint32_t *value, uint32_t flag) -{ - uint32_t payload[PAYLOAD_ARG_CNT]; - - /* Send request to the PMC */ - PM_PACK_PAYLOAD3(payload, LIBPM_MODULE_ID, flag, PM_PLL_GET_PARAMETER, - clk_id, param); - - return pm_ipi_send_sync(primary_proc, payload, value, 1); -} - -/** - * pm_pll_set_mode() - Set PLL mode. - * @clk_id: PLL clock ID. - * @mode: PLL mode. - * @flag: 0 - Call from secure source. - * 1 - Call from non-secure source. - * - * This API is deprecated and maintained here for backward compatibility. - * New use of this API should be avoided for versal platform. - * This API and its use cases will be removed for versal platform. - * - * Return: Returns status, either success or error+reason. - * - */ -enum pm_ret_status pm_pll_set_mode(uint32_t clk_id, uint32_t mode, - uint32_t flag) -{ - uint32_t payload[PAYLOAD_ARG_CNT]; - - /* Send request to the PMC */ - PM_PACK_PAYLOAD3(payload, LIBPM_MODULE_ID, flag, PM_PLL_SET_MODE, - clk_id, mode); - - return pm_ipi_send_sync(primary_proc, payload, NULL, 0); -} - -/** - * pm_pll_get_mode() - Get PLL mode. - * @clk_id: PLL clock ID. - * @mode: Buffer to store PLL mode. - * @flag: 0 - Call from secure source. - * 1 - Call from non-secure source. - * - * This API is deprecated and maintained here for backward compatibility. - * New use of this API should be avoided for versal platform. - * This API and its use cases will be removed for versal platform. - * - * Return: Returns status, either success or error+reason. - * - */ -enum pm_ret_status pm_pll_get_mode(uint32_t clk_id, uint32_t *mode, - uint32_t flag) -{ - uint32_t payload[PAYLOAD_ARG_CNT]; - - /* Send request to the PMC */ - PM_PACK_PAYLOAD2(payload, LIBPM_MODULE_ID, flag, PM_PLL_GET_MODE, - clk_id); - - return pm_ipi_send_sync(primary_proc, payload, mode, 1); -} - /** * pm_force_powerdown() - PM call to request for another PU or subsystem to * be powered down forcefully. @@ -448,131 +342,58 @@ enum pm_ret_status pm_system_shutdown(uint32_t type, uint32_t subtype, } /** - * pm_query_data() - PM API for querying firmware data. - * @qid: The type of data to query. - * @arg1: Argument 1 to requested query data call. - * @arg2: Argument 2 to requested query data call. - * @arg3: Argument 3 to requested query data call. - * @data: Returned output data. - * @flag: 0 - Call from secure source. - * 1 - Call from non-secure source. - * - * This API is deprecated and maintained here for backward compatibility. - * New use of this API should be avoided for versal platform. - * This API and its use cases will be removed for versal platform. + * pm_set_wakeup_source() - PM call to specify the wakeup source while + * suspended. + * @target: Device id of the targeted PU or subsystem + * @wkup_device: Device id of the wakeup peripheral + * @enable: Enable or disable the specified peripheral as wake source + * @flag: 0 - Call from secure source + * 1 - Call from non-secure source * - * Return: 0 if success else non-zero error code of type - * enum pm_ret_status. + * Return: Returns status, either success or error+reason. * */ -enum pm_ret_status pm_query_data(uint32_t qid, uint32_t arg1, uint32_t arg2, - uint32_t arg3, uint32_t *data, uint32_t flag) +enum pm_ret_status pm_set_wakeup_source(uint32_t target, uint32_t wkup_device, + uint8_t enable, uint32_t flag) { - uint32_t ret; - uint32_t version[PAYLOAD_ARG_CNT] = {0}; uint32_t payload[PAYLOAD_ARG_CNT]; - uint32_t fw_api_version; - /* Send request to the PMC */ - PM_PACK_PAYLOAD5(payload, LIBPM_MODULE_ID, flag, PM_QUERY_DATA, qid, - arg1, arg2, arg3); - - ret = pm_feature_check((uint32_t)PM_QUERY_DATA, &version[0], flag); - if (ret == PM_RET_SUCCESS) { - fw_api_version = version[0] & 0xFFFFU; - if ((fw_api_version == 2U) && - ((qid == XPM_QID_CLOCK_GET_NAME) || - (qid == XPM_QID_PINCTRL_GET_FUNCTION_NAME))) { - ret = pm_ipi_send_sync(primary_proc, payload, data, PAYLOAD_ARG_CNT); - if (ret == PM_RET_SUCCESS) { - ret = data[0]; - data[0] = data[1]; - data[1] = data[2]; - data[2] = data[3]; - } - } else { - ret = pm_ipi_send_sync(primary_proc, payload, data, PAYLOAD_ARG_CNT); - } - } - return ret; + PM_PACK_PAYLOAD4(payload, LIBPM_MODULE_ID, flag, PM_SET_WAKEUP_SOURCE, + target, wkup_device, enable); + return pm_ipi_send_sync(primary_proc, payload, NULL, 0); } + /** - * pm_api_ioctl() - PM IOCTL API for device control and configs. - * @device_id: Device ID. - * @ioctl_id: ID of the requested IOCTL. - * @arg1: Argument 1 to requested IOCTL call. - * @arg2: Argument 2 to requested IOCTL call. - * @arg3: Argument 3 to requested IOCTL call. - * @value: Returned output value. - * @flag: 0 - Call from secure source. - * 1 - Call from non-secure source. - * - * This API is deprecated and maintained here for backward compatibility. - * New use of this API should be avoided for versal platform. - * This API and its use cases will be removed for versal platform. - * - * This function calls IOCTL to firmware for device control and configuration. - * - * Return: Returns status, either 0 on success or non-zero error code - * of type enum pm_ret_status. + * eemi_feature_check() - Returns the supported API version if supported. + * @api_id: API ID to check. + * @ret_payload: pointer to array of PAYLOAD_ARG_CNT number of + * words Returned supported API version * + * Return: Returns status, either success or error+reason. */ -enum pm_ret_status pm_api_ioctl(uint32_t device_id, uint32_t ioctl_id, - uint32_t arg1, uint32_t arg2, uint32_t arg3, - uint32_t *value, uint32_t flag) +enum pm_ret_status eemi_feature_check(uint32_t api_id, uint32_t *ret_payload) { enum pm_ret_status ret; - switch (ioctl_id) { - case IOCTL_SET_PLL_FRAC_MODE: - ret = pm_pll_set_mode(arg1, arg2, flag); - break; - case IOCTL_GET_PLL_FRAC_MODE: - ret = pm_pll_get_mode(arg1, value, flag); - break; - case IOCTL_SET_PLL_FRAC_DATA: - ret = pm_pll_set_param(arg1, (uint32_t)PM_PLL_PARAM_DATA, arg2, flag); - break; - case IOCTL_GET_PLL_FRAC_DATA: - ret = pm_pll_get_param(arg1, (uint32_t)PM_PLL_PARAM_DATA, value, flag); + /* Return version of API which are implemented in TF-A only */ + switch (api_id) { + case PM_GET_CALLBACK_DATA: + case PM_GET_TRUSTZONE_VERSION: + ret_payload[0] = PM_API_VERSION_2; + ret = PM_RET_SUCCESS; break; - case IOCTL_SET_SGI: - /* Get the sgi number */ - ret = pm_register_sgi(arg1, arg2); - if (ret != 0) { - return PM_RET_ERROR_ARGS; - } + case TF_A_PM_REGISTER_SGI: + case TF_A_FEATURE_CHECK: + ret_payload[0] = PM_API_BASE_VERSION; ret = PM_RET_SUCCESS; break; default: - return PM_RET_ERROR_NOTSUPPORTED; + ret = PM_RET_ERROR_NO_FEATURE; } return ret; } -/** - * pm_set_wakeup_source() - PM call to specify the wakeup source while - * suspended. - * @target: Device id of the targeted PU or subsystem - * @wkup_device: Device id of the wakeup peripheral - * @enable: Enable or disable the specified peripheral as wake source - * @flag: 0 - Call from secure source - * 1 - Call from non-secure source - * - * Return: Returns status, either success or error+reason. - * - */ -enum pm_ret_status pm_set_wakeup_source(uint32_t target, uint32_t wkup_device, - uint8_t enable, uint32_t flag) -{ - uint32_t payload[PAYLOAD_ARG_CNT]; - - PM_PACK_PAYLOAD4(payload, LIBPM_MODULE_ID, flag, PM_SET_WAKEUP_SOURCE, - target, wkup_device, enable); - return pm_ipi_send_sync(primary_proc, payload, NULL, 0); -} - /** * pm_feature_check() - Returns the supported API version if supported. * @api_id: API ID to check. @@ -616,7 +437,7 @@ enum pm_ret_status pm_feature_check(uint32_t api_id, uint32_t *ret_payload, PM_PACK_PAYLOAD2(payload, LIBPM_MODULE_ID, flag, PM_FEATURE_CHECK, api_id); - return pm_ipi_send_sync(primary_proc, payload, ret_payload, PAYLOAD_ARG_CNT); + return pm_ipi_send_sync(primary_proc, payload, ret_payload, RET_PAYLOAD_ARG_CNT); } /** diff --git a/plat/xilinx/common/pm_service/pm_ipi.c b/plat/xilinx/common/pm_service/pm_ipi.c index 56567dd6..c3872fc0 100644 --- a/plat/xilinx/common/pm_service/pm_ipi.c +++ b/plat/xilinx/common/pm_service/pm_ipi.c @@ -1,7 +1,7 @@ /* * Copyright (c) 2013-2020, Arm Limited and Contributors. All rights reserved. * Copyright (c) 2019-2022, Xilinx, Inc. All rights reserved. - * Copyright (c) 2022-2023, Advanced Micro Devices, Inc. All rights reserved. + * Copyright (c) 2022-2024, Advanced Micro Devices, Inc. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -169,9 +169,7 @@ static enum pm_ret_status pm_ipi_buff_read(const struct pm_proc *proc, size_t i; enum pm_ret_status ret; #if IPI_CRC_CHECK - uint32_t *payload_ptr = value; - size_t j; - uint32_t response_payload[PAYLOAD_ARG_CNT]; + uint32_t crc; #endif uintptr_t buffer_base = proc->ipi->buffer_base + IPI_BUFFER_TARGET_REMOTE_OFFSET + @@ -184,27 +182,20 @@ static enum pm_ret_status pm_ipi_buff_read(const struct pm_proc *proc, * buf-2: unused * buf-3: unused */ - for (i = 1; i <= count; i++) { - *value = mmio_read_32(buffer_base + (i * PAYLOAD_ARG_SIZE)); - value++; + for (i = 0U; i < count; i++) { + value[i] = mmio_read_32(buffer_base + ((i + 1U) * PAYLOAD_ARG_SIZE)); } ret = mmio_read_32(buffer_base); #if IPI_CRC_CHECK - for (j = 0; j < PAYLOAD_ARG_CNT; j++) { - response_payload[j] = mmio_read_32(buffer_base + - (j * PAYLOAD_ARG_SIZE)); - } - - if (response_payload[PAYLOAD_CRC_POS] != - calculate_crc(response_payload, IPI_W0_TO_W6_SIZE)) { - NOTICE("ERROR in CRC response payload value:0x%x\n", - response_payload[PAYLOAD_CRC_POS]); + crc = mmio_read_32(buffer_base + (PAYLOAD_CRC_POS * PAYLOAD_ARG_SIZE)); + if (crc != calculate_crc((uint32_t *)buffer_base, IPI_W0_TO_W6_SIZE)) { + NOTICE("ERROR in CRC response payload value:0x%x\n", crc); ret = PM_RET_ERROR_INVALID_CRC; /* Payload data is invalid as CRC validation failed * Clear the payload to avoid leakage of data to upper layers */ - memset(payload_ptr, 0, count); + memset(value, 0, count); } #endif @@ -226,39 +217,31 @@ static enum pm_ret_status pm_ipi_buff_read(const struct pm_proc *proc, enum pm_ret_status pm_ipi_buff_read_callb(uint32_t *value, size_t count) { size_t i; + size_t local_count = count; #if IPI_CRC_CHECK - uint32_t *payload_ptr = value; - size_t j; - unsigned int response_payload[PAYLOAD_ARG_CNT] = {0}; + uint32_t crc; #endif uintptr_t buffer_base = IPI_BUFFER_REMOTE_BASE + IPI_BUFFER_TARGET_LOCAL_OFFSET + IPI_BUFFER_REQ_OFFSET; enum pm_ret_status ret = PM_RET_SUCCESS; - if (count > IPI_BUFFER_MAX_WORDS) { - count = IPI_BUFFER_MAX_WORDS; + if (local_count > IPI_BUFFER_MAX_WORDS) { + local_count = IPI_BUFFER_MAX_WORDS; } - for (i = 0; i <= count; i++) { - *value = mmio_read_32(buffer_base + (i * PAYLOAD_ARG_SIZE)); - value++; + for (i = 0; i < count; i++) { + value[i] = mmio_read_32(buffer_base + (i * PAYLOAD_ARG_SIZE)); } #if IPI_CRC_CHECK - for (j = 0; j < PAYLOAD_ARG_CNT; j++) { - response_payload[j] = mmio_read_32(buffer_base + - (j * PAYLOAD_ARG_SIZE)); - } - - if (response_payload[PAYLOAD_CRC_POS] != - calculate_crc(response_payload, IPI_W0_TO_W6_SIZE)) { - NOTICE("ERROR in CRC response payload value:0x%x\n", - response_payload[PAYLOAD_CRC_POS]); + crc = mmio_read_32(buffer_base + (PAYLOAD_CRC_POS * PAYLOAD_ARG_SIZE)); + if (crc != calculate_crc((uint32_t *)buffer_base, IPI_W0_TO_W6_SIZE)) { + NOTICE("ERROR in CRC response payload value:0x%x\n", crc); ret = PM_RET_ERROR_INVALID_CRC; /* Payload data is invalid as CRC validation failed * Clear the payload to avoid leakage of data to upper layers */ - memset(payload_ptr, 0, count); + memset(value, 0, local_count); } #endif return ret; diff --git a/plat/xilinx/common/pm_service/pm_svc_main.c b/plat/xilinx/common/pm_service/pm_svc_main.c index 1e5808cf..afb9a967 100644 --- a/plat/xilinx/common/pm_service/pm_svc_main.c +++ b/plat/xilinx/common/pm_service/pm_svc_main.c @@ -1,6 +1,6 @@ /* * Copyright (c) 2019-2022, Xilinx, Inc. All rights reserved. - * Copyright (c) 2022-2023, Advanced Micro Devices, Inc. All rights reserved. + * Copyright (c) 2022-2024, Advanced Micro Devices, Inc. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -17,6 +17,8 @@ #include <common/runtime_svc.h> #include <drivers/arm/gicv3.h> +#include <lib/psci/psci.h> +#include <plat/arm/common/plat_arm.h> #include <plat/common/platform.h> #include <plat_private.h> @@ -31,33 +33,131 @@ #define INVALID_SGI 0xFFU #define PM_INIT_SUSPEND_CB (30U) #define PM_NOTIFY_CB (32U) +#define EVENT_CPU_PWRDWN (4U) +#define MBOX_SGI_SHARED_IPI (7U) + +/** + * upper_32_bits - return bits 32-63 of a number + * @n: the number we're accessing + */ +#define upper_32_bits(n) ((uint32_t)((n) >> 32U)) + +/** + * lower_32_bits - return bits 0-31 of a number + * @n: the number we're accessing + */ +#define lower_32_bits(n) ((uint32_t)((n) & 0xffffffffU)) + +/** + * EXTRACT_SMC_ARGS - extracts 32-bit payloads from 64-bit SMC arguments + * @pm_arg: array of 32-bit payloads + * @x: array of 64-bit SMC arguments + */ +#define EXTRACT_ARGS(pm_arg, x) \ + for (uint32_t i = 0U; i < (PAYLOAD_ARG_CNT - 1U); i++) { \ + if ((i % 2U) != 0U) { \ + pm_arg[i] = lower_32_bits(x[(i / 2U) + 1U]); \ + } else { \ + pm_arg[i] = upper_32_bits(x[i / 2U]); \ + } \ + } + +/* 1 sec of wait timeout for secondary core down */ +#define PWRDWN_WAIT_TIMEOUT (1000U) DEFINE_RENAME_SYSREG_RW_FUNCS(icc_asgi1r_el1, S3_0_C12_C11_6) /* pm_up = true - UP, pm_up = false - DOWN */ static bool pm_up; static uint32_t sgi = (uint32_t)INVALID_SGI; +bool pwrdwn_req_received; static void notify_os(void) { - int32_t cpu; - uint32_t reg; + plat_ic_raise_ns_sgi(sgi, read_mpidr_el1()); +} - cpu = plat_my_core_pos() + 1U; +static uint64_t cpu_pwrdwn_req_handler(uint32_t id, uint32_t flags, + void *handle, void *cookie) +{ + (void)id; + (void)flags; + (void)handle; + (void)cookie; + uint32_t cpu_id = plat_my_core_pos(); + + VERBOSE("Powering down CPU %d\n", cpu_id); + + /* Deactivate CPU power down SGI */ + plat_ic_end_of_interrupt(CPU_PWR_DOWN_REQ_INTR); - reg = (cpu | (sgi << XSCUGIC_SGIR_EL1_INITID_SHIFT)); - write_icc_asgi1r_el1(reg); + return psci_cpu_off(); +} + +/** + * raise_pwr_down_interrupt() - Callback function to raise SGI. + * @mpidr: MPIDR for the target CPU. + * + * Raise SGI interrupt to trigger the CPU power down sequence on all the + * online secondary cores. + */ +static void raise_pwr_down_interrupt(u_register_t mpidr) +{ + plat_ic_raise_el3_sgi(CPU_PWR_DOWN_REQ_INTR, mpidr); +} + +void request_cpu_pwrdwn(void) +{ + enum pm_ret_status ret; + + VERBOSE("CPU power down request received\n"); + + /* Send powerdown request to online secondary core(s) */ + ret = psci_stop_other_cores(PWRDWN_WAIT_TIMEOUT, raise_pwr_down_interrupt); + if (ret != PSCI_E_SUCCESS) { + ERROR("Failed to powerdown secondary core(s)\n"); + } + + /* Clear IPI IRQ */ + pm_ipi_irq_clear(primary_proc); + + /* Deactivate IPI IRQ */ + plat_ic_end_of_interrupt(PLAT_VERSAL_IPI_IRQ); } static uint64_t ipi_fiq_handler(uint32_t id, uint32_t flags, void *handle, void *cookie) { + (void)flags; + (void)handle; + (void)cookie; uint32_t payload[4] = {0}; enum pm_ret_status ret; + int ipi_status, i; VERBOSE("Received IPI FIQ from firmware\n"); + console_flush(); (void)plat_ic_acknowledge_interrupt(); + /* Check status register for each IPI except PMC */ + for (i = IPI_ID_APU; i <= IPI_ID_5; i++) { + ipi_status = ipi_mb_enquire_status(IPI_ID_APU, i); + + /* If any agent other than PMC has generated IPI FIQ then send SGI to mbox driver */ + if (ipi_status & IPI_MB_STATUS_RECV_PENDING) { + plat_ic_raise_ns_sgi(MBOX_SGI_SHARED_IPI, read_mpidr_el1()); + break; + } + } + + /* If PMC has not generated interrupt then end ISR */ + ipi_status = ipi_mb_enquire_status(IPI_ID_APU, IPI_ID_PMC); + if ((ipi_status & IPI_MB_STATUS_RECV_PENDING) == 0) { + plat_ic_end_of_interrupt(id); + return 0; + } + + /* Handle PMC case */ ret = pm_get_callbackdata(payload, ARRAY_SIZE(payload), 0, 0); if (ret != PM_RET_SUCCESS) { payload[0] = ret; @@ -65,9 +165,26 @@ static uint64_t ipi_fiq_handler(uint32_t id, uint32_t flags, void *handle, switch (payload[0]) { case PM_INIT_SUSPEND_CB: + if (sgi != INVALID_SGI) { + notify_os(); + } + break; case PM_NOTIFY_CB: if (sgi != INVALID_SGI) { + if (payload[2] == EVENT_CPU_PWRDWN) { + if (pwrdwn_req_received) { + pwrdwn_req_received = false; + request_cpu_pwrdwn(); + (void)psci_cpu_off(); + break; + } else { + pwrdwn_req_received = true; + } + } notify_os(); + } else if (payload[2] == EVENT_CPU_PWRDWN) { + request_cpu_pwrdwn(); + (void)psci_cpu_off(); } break; case PM_RET_ERROR_INVALID_CRC: @@ -139,6 +256,12 @@ int32_t pm_setup(void) pm_ipi_init(primary_proc); pm_up = true; + /* register SGI handler for CPU power down request */ + ret = request_intr_type_el3(CPU_PWR_DOWN_REQ_INTR, cpu_pwrdwn_req_handler); + if (ret != 0) { + WARN("BL31: registering SGI interrupt failed\n"); + } + /* * Enable IPI IRQ * assume the rich OS is OK to handle callback IRQs now. @@ -153,6 +276,14 @@ int32_t pm_setup(void) } gicd_write_irouter(gicv3_driver_data->gicd_base, PLAT_VERSAL_IPI_IRQ, MODE); + + /* Register for idle callback during force power down/restart */ + ret = pm_register_notifier(primary_proc->node_id, EVENT_CPU_PWRDWN, + 0x0U, 0x1U, SECURE_FLAG); + if (ret != 0) { + WARN("BL31: registering idle callback for restart/force power down failed\n"); + } + return ret; } @@ -178,33 +309,9 @@ static uintptr_t eemi_for_compatibility(uint32_t api_id, uint32_t *pm_arg, switch (api_id) { - case (uint32_t)PM_IOCTL: - { - uint32_t value = 0U; - - ret = pm_api_ioctl(pm_arg[0], pm_arg[1], pm_arg[2], - pm_arg[3], pm_arg[4], - &value, security_flag); - if (ret == PM_RET_ERROR_NOTSUPPORTED) - return (uintptr_t)0; - - SMC_RET1(handle, (uint64_t)ret | ((uint64_t)value) << 32U); - } - - case (uint32_t)PM_QUERY_DATA: - { - uint32_t data[PAYLOAD_ARG_CNT] = { 0 }; - - ret = pm_query_data(pm_arg[0], pm_arg[1], pm_arg[2], - pm_arg[3], data, security_flag); - - SMC_RET2(handle, (uint64_t)ret | ((uint64_t)data[0] << 32U), - (uint64_t)data[1] | ((uint64_t)data[2] << 32U)); - } - case (uint32_t)PM_FEATURE_CHECK: { - uint32_t result[PAYLOAD_ARG_CNT] = {0U}; + uint32_t result[RET_PAYLOAD_ARG_CNT] = {0U}; ret = pm_feature_check(pm_arg[0], result, security_flag); SMC_RET2(handle, (uint64_t)ret | ((uint64_t)result[0] << 32U), @@ -293,6 +400,15 @@ static uintptr_t TF_A_specific_handler(uint32_t api_id, uint32_t *pm_arg, { switch (api_id) { + case TF_A_FEATURE_CHECK: + { + enum pm_ret_status ret; + uint32_t result[PAYLOAD_ARG_CNT] = {0U}; + + ret = eemi_feature_check(pm_arg[0], result); + SMC_RET1(handle, (uint64_t)ret | ((uint64_t)result[0] << 32U)); + } + case TF_A_PM_REGISTER_SGI: { int32_t ret; @@ -350,7 +466,7 @@ static uintptr_t eemi_handler(uint32_t api_id, uint32_t *pm_arg, void *handle, uint32_t security_flag) { enum pm_ret_status ret; - uint32_t buf[PAYLOAD_ARG_CNT] = {0}; + uint32_t buf[RET_PAYLOAD_ARG_CNT] = {0}; ret = pm_handle_eemi_call(security_flag, api_id, pm_arg[0], pm_arg[1], pm_arg[2], pm_arg[3], pm_arg[4], @@ -362,9 +478,9 @@ static uintptr_t eemi_handler(uint32_t api_id, uint32_t *pm_arg, * than other eemi calls. */ if (api_id == (uint32_t)PM_QUERY_DATA) { - if ((pm_arg[0] == XPM_QID_CLOCK_GET_NAME || - pm_arg[0] == XPM_QID_PINCTRL_GET_FUNCTION_NAME) && - ret == PM_RET_SUCCESS) { + if (((pm_arg[0] == XPM_QID_CLOCK_GET_NAME) || + (pm_arg[0] == XPM_QID_PINCTRL_GET_FUNCTION_NAME)) && + (ret == PM_RET_SUCCESS)) { SMC_RET2(handle, (uint64_t)buf[0] | ((uint64_t)buf[1] << 32U), (uint64_t)buf[2] | ((uint64_t)buf[3] << 32U)); } @@ -374,6 +490,45 @@ static uintptr_t eemi_handler(uint32_t api_id, uint32_t *pm_arg, (uint64_t)buf[1] | ((uint64_t)buf[2] << 32U)); } +/** + * eemi_api_handler() - Prepare EEMI payload and perform IPI transaction. + * @api_id: identifier for the API being called. + * @pm_arg: pointer to the argument data for the API call. + * @handle: Pointer to caller's context structure. + * @security_flag: SECURE_FLAG or NON_SECURE_FLAG. + * + * EEMI - Embedded Energy Management Interface is AMD-Xilinx proprietary + * protocol to allow communication between power management controller and + * different processing clusters. + * + * This handler prepares EEMI protocol payload received from kernel and performs + * IPI transaction. + * + * Return: If EEMI API found then, uintptr_t type address, else 0 + */ +static uintptr_t eemi_api_handler(uint32_t api_id, const uint32_t *pm_arg, + void *handle, uint32_t security_flag) +{ + enum pm_ret_status ret; + uint32_t buf[RET_PAYLOAD_ARG_CNT] = {0U}; + uint32_t payload[PAYLOAD_ARG_CNT] = {0U}; + uint32_t module_id; + + module_id = (api_id & MODULE_ID_MASK) >> 8U; + + PM_PACK_PAYLOAD7(payload, module_id, security_flag, api_id, + pm_arg[0], pm_arg[1], pm_arg[2], pm_arg[3], + pm_arg[4], pm_arg[5]); + + ret = pm_ipi_send_sync(primary_proc, payload, (uint32_t *)buf, + RET_PAYLOAD_ARG_CNT); + + SMC_RET4(handle, (uint64_t)ret | ((uint64_t)buf[0] << 32U), + (uint64_t)buf[1] | ((uint64_t)buf[2] << 32U), + (uint64_t)buf[3] | ((uint64_t)buf[4] << 32U), + (uint64_t)buf[5]); +} + /** * pm_smc_handler() - SMC handler for PM-API calls coming from EL1/EL2. * @smc_fid: Function Identifier. @@ -398,11 +553,13 @@ static uintptr_t eemi_handler(uint32_t api_id, uint32_t *pm_arg, uint64_t pm_smc_handler(uint32_t smc_fid, uint64_t x1, uint64_t x2, uint64_t x3, uint64_t x4, const void *cookie, void *handle, uint64_t flags) { + (void)cookie; uintptr_t ret; uint32_t pm_arg[PAYLOAD_ARG_CNT] = {0}; uint32_t security_flag = NON_SECURE_FLAG; uint32_t api_id; bool status = false, status_tmp = false; + const uint64_t x[4] = {x1, x2, x3, x4}; /* Handle case where PM wasn't initialized properly */ if (pm_up == false) { @@ -420,6 +577,14 @@ uint64_t pm_smc_handler(uint32_t smc_fid, uint64_t x1, uint64_t x2, uint64_t x3, security_flag = SECURE_FLAG; } + if ((smc_fid & FUNCID_NUM_MASK) == PASS_THROUGH_FW_CMD_ID) { + api_id = lower_32_bits(x[0]); + + EXTRACT_ARGS(pm_arg, x); + + return eemi_api_handler(api_id, pm_arg, handle, security_flag); + } + pm_arg[0] = (uint32_t)x1; pm_arg[1] = (uint32_t)(x1 >> 32U); pm_arg[2] = (uint32_t)x2; diff --git a/plat/xilinx/common/versal.c b/plat/xilinx/common/versal.c index 3ea022cf..b37dc76c 100644 --- a/plat/xilinx/common/versal.c +++ b/plat/xilinx/common/versal.c @@ -7,6 +7,7 @@ #include <common/debug.h> #include <lib/mmio.h> #include <lib/smccc.h> +#include <plat/common/platform.h> #include <services/arm_arch_svc.h> #include <plat_private.h> diff --git a/plat/xilinx/versal/aarch64/versal_common.c b/plat/xilinx/versal/aarch64/versal_common.c index aba190de..4236d8a9 100644 --- a/plat/xilinx/versal/aarch64/versal_common.c +++ b/plat/xilinx/versal/aarch64/versal_common.c @@ -1,12 +1,11 @@ /* * Copyright (c) 2018-2020, Arm Limited and Contributors. All rights reserved. - * Copyright (c) 2022-2023, Advanced Micro Devices, Inc. All rights reserved. + * Copyright (c) 2022-2024, Advanced Micro Devices, Inc. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ #include <common/debug.h> -#include <drivers/generic_delay_timer.h> #include <lib/mmio.h> #include <lib/xlat_tables/xlat_tables_v2.h> #include <plat/common/platform.h> @@ -18,6 +17,7 @@ #include <versal_def.h> uint32_t platform_id, platform_version; +uint32_t cpu_clock; /* * Table of regions to map using the MMU. @@ -38,24 +38,10 @@ const mmap_region_t *plat_get_mmap(void) return plat_versal_mmap; } -static void versal_print_platform_name(void) -{ - NOTICE("TF-A running on %s\n", PLATFORM_NAME); -} - void versal_config_setup(void) { /* Configure IPI data for versal */ versal_ipi_config_table_init(); - - versal_print_platform_name(); - - generic_delay_timer_init(); -} - -uint32_t plat_get_syscnt_freq2(void) -{ - return VERSAL_CPU_CLOCK; } void board_detection(void) @@ -72,9 +58,54 @@ void board_detection(void) platform_id = FIELD_GET(PLATFORM_MASK, plat_info[1]); platform_version = FIELD_GET(PLATFORM_VERSION_MASK, plat_info[1]); + + if (platform_id == VERSAL_COSIM) { + platform_id = VERSAL_QEMU; + } +} + +const char *board_name_decode(void) +{ + const char *platform; + + switch (platform_id) { + case VERSAL_SPP: + platform = "IPP"; + break; + case VERSAL_EMU: + platform = "EMU"; + break; + case VERSAL_QEMU: + platform = "QEMU"; + break; + case VERSAL_SILICON: + platform = "SILICON"; + break; + default: + platform = "unknown"; + } + + return platform; } uint32_t get_uart_clk(void) { - return UART_CLOCK; + uint32_t uart_clock; + + switch (platform_id) { + case VERSAL_SPP: + uart_clock = 25000000; + break; + case VERSAL_EMU: + uart_clock = 212000; + break; + case VERSAL_QEMU: + case VERSAL_SILICON: + uart_clock = 100000000; + break; + default: + panic(); + } + + return uart_clock; } diff --git a/plat/xilinx/versal/bl31_versal_setup.c b/plat/xilinx/versal/bl31_versal_setup.c index cd105c61..819a55b7 100644 --- a/plat/xilinx/versal/bl31_versal_setup.c +++ b/plat/xilinx/versal/bl31_versal_setup.c @@ -1,7 +1,7 @@ /* - * Copyright (c) 2018-2021, Arm Limited and Contributors. All rights reserved. + * Copyright (c) 2018-2024, Arm Limited and Contributors. All rights reserved. * Copyright (c) 2018-2022, Xilinx, Inc. All rights reserved. - * Copyright (c) 2022-2023, Advanced Micro Devices, Inc. All rights reserved. + * Copyright (c) 2022-2024, Advanced Micro Devices, Inc. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -12,11 +12,13 @@ #include <bl31/bl31.h> #include <common/bl_common.h> #include <common/debug.h> +#include <drivers/generic_delay_timer.h> #include <lib/mmio.h> #include <lib/xlat_tables/xlat_tables_v2.h> #include <plat/common/platform.h> #include <plat_arm.h> #include <plat_console.h> +#include <plat_clkfunc.h> #include <plat_fdt.h> #include <plat_private.h> @@ -67,18 +69,14 @@ static inline void bl31_set_default_config(void) void bl31_early_platform_setup2(u_register_t arg0, u_register_t arg1, u_register_t arg2, u_register_t arg3) { + (void)arg0; + (void)arg1; + (void)arg2; + (void)arg3; uint64_t tfa_handoff_addr; uint32_t payload[PAYLOAD_ARG_CNT], max_size = HANDOFF_PARAMS_MAX_SIZE; enum pm_ret_status ret_status; - uint64_t addr[HANDOFF_PARAMS_MAX_SIZE]; - - setup_console(); - - /* Initialize the platform config for future decision making */ - versal_config_setup(); - - /* Get platform related information */ - board_detection(); + const uint64_t addr[HANDOFF_PARAMS_MAX_SIZE]; /* * Do initial security configuration to allow DRAM/device access. On @@ -86,6 +84,32 @@ void bl31_early_platform_setup2(u_register_t arg0, u_register_t arg1, * other platforms might have more programmable security devices * present. */ + versal_config_setup(); + + /* Initialize the platform config for future decision making */ + board_detection(); + + switch (platform_id) { + case VERSAL_SPP: + cpu_clock = 2720000; + break; + case VERSAL_EMU: + cpu_clock = 212000; + break; + case VERSAL_QEMU: + case VERSAL_SILICON: + cpu_clock = 100000000; + break; + default: + panic(); + } + set_cnt_freq(); + + generic_delay_timer_init(); + + setup_console(); + + NOTICE("TF-A running on %s %d\n", board_name_decode(), platform_version); /* Populate common information for BL32 and BL33 */ SET_PARAM_HEAD(&bl32_image_ep_info, PARAM_EP, VERSION_1, 0); @@ -107,7 +131,7 @@ void bl31_early_platform_setup2(u_register_t arg0, u_register_t arg1, enum xbl_handoff ret = xbl_handover(&bl32_image_ep_info, &bl33_image_ep_info, tfa_handoff_addr); - if (ret == XBL_HANDOFF_NO_STRUCT || ret == XBL_HANDOFF_INVAL_STRUCT) { + if ((ret == XBL_HANDOFF_NO_STRUCT) || (ret == XBL_HANDOFF_INVAL_STRUCT)) { bl31_set_default_config(); } else if (ret == XBL_HANDOFF_TOO_MANY_PARTS) { ERROR("BL31: Error too many partitions %u\n", ret); @@ -115,19 +139,6 @@ void bl31_early_platform_setup2(u_register_t arg0, u_register_t arg1, panic(); } else { INFO("BL31: PLM to TF-A handover success %u\n", ret); - - /* - * The BL32 load address is indicated as 0x0 in the handoff - * parameters, which is different from the default/user-provided - * load address of 0x60000000 but the flags are correctly - * configured. Consequently, in this scenario, set the PC - * to the requested BL32_BASE address. - */ - - /* TODO: Remove the following check once this is fixed from PLM */ - if (bl32_image_ep_info.pc == 0 && bl32_image_ep_info.spsr != 0) { - bl32_image_ep_info.pc = (uintptr_t)BL32_BASE; - } } NOTICE("BL31: Secure code at 0x%lx\n", bl32_image_ep_info.pc); @@ -142,7 +153,7 @@ int request_intr_type_el3(uint32_t id, interrupt_type_handler_t handler) uint32_t i; /* Validate 'handler' and 'id' parameters */ - if (handler == NULL || index >= MAX_INTR_EL3) { + if ((handler == NULL) || (index >= MAX_INTR_EL3)) { return -EINVAL; } @@ -164,6 +175,7 @@ int request_intr_type_el3(uint32_t id, interrupt_type_handler_t handler) static uint64_t rdo_el3_interrupt_handler(uint32_t id, uint32_t flags, void *handle, void *cookie) { + (void)id; uint32_t intr_id; uint32_t i; interrupt_type_handler_t handler = NULL; @@ -203,8 +215,6 @@ void bl31_plat_runtime_setup(void) if (rc != 0) { panic(); } - - console_switch_state(CONSOLE_FLAG_RUNTIME); } /* diff --git a/plat/xilinx/versal/include/plat_macros.S b/plat/xilinx/versal/include/plat_macros.S index 41193a51..38f47f66 100644 --- a/plat/xilinx/versal/include/plat_macros.S +++ b/plat/xilinx/versal/include/plat_macros.S @@ -103,8 +103,8 @@ exit_print_gic_regs: * --------------------------------------------- */ .macro plat_crash_print_regs - mov_imm x17, PLAT_GICD_BASE_VALUE - mov_imm x16, PLAT_GICR_BASE_VALUE + mov_imm x17, PLAT_ARM_GICD_BASE + mov_imm x16, PLAT_ARM_GICR_BASE versal_print_gic_regs .endm diff --git a/plat/xilinx/versal/include/plat_private.h b/plat/xilinx/versal/include/plat_private.h index a4210cd1..658dc9eb 100644 --- a/plat/xilinx/versal/include/plat_private.h +++ b/plat/xilinx/versal/include/plat_private.h @@ -1,7 +1,7 @@ /* * Copyright (c) 2018-2019, Arm Limited and Contributors. All rights reserved. * Copyright (c) 2018-2022, Xilinx, Inc. All rights reserved. - * Copyright (c) 2022-2023, Advanced Micro Devices, Inc. All rights reserved. + * Copyright (c) 2022-2024, Advanced Micro Devices, Inc. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -22,9 +22,11 @@ void versal_config_setup(void); const mmap_region_t *plat_get_mmap(void); -extern uint32_t platform_id, platform_version; +extern uint32_t cpu_clock, platform_id, platform_version; void board_detection(void); +const char *board_name_decode(void); + void plat_versal_gic_driver_init(void); void plat_versal_gic_init(void); void plat_versal_gic_cpuif_enable(void); @@ -32,6 +34,8 @@ void plat_versal_gic_cpuif_disable(void); void plat_versal_gic_pcpu_init(void); void plat_versal_gic_save(void); void plat_versal_gic_resume(void); +void plat_versal_gic_redistif_on(void); +void plat_versal_gic_redistif_off(void); uint32_t versal_calc_core_pos(u_register_t mpidr); /* diff --git a/plat/xilinx/versal/include/platform_def.h b/plat/xilinx/versal/include/platform_def.h index 286a706c..8cf8de0d 100644 --- a/plat/xilinx/versal/include/platform_def.h +++ b/plat/xilinx/versal/include/platform_def.h @@ -9,6 +9,7 @@ #define PLATFORM_DEF_H #include <arch.h> +#include <plat_common.h> #include "versal_def.h" /******************************************************************************* @@ -74,8 +75,17 @@ /******************************************************************************* * Platform specific page table and MMU setup constants ******************************************************************************/ -#define PLAT_PHY_ADDR_SPACE_SIZE (1ull << 32) -#define PLAT_VIRT_ADDR_SPACE_SIZE (1ull << 32) + +#if (BL31_BASE >= (1ULL << 32U)) +/* Address range in High DDR and HBM memory range */ +#define PLAT_ADDR_SPACE_SHIFT U(42) +#else +/* Address range in OCM and Low DDR memory range */ +#define PLAT_ADDR_SPACE_SHIFT U(32) +#endif + +#define PLAT_PHY_ADDR_SPACE_SIZE (1ull << PLAT_ADDR_SPACE_SHIFT) +#define PLAT_VIRT_ADDR_SPACE_SIZE (1ull << PLAT_ADDR_SPACE_SHIFT) #define XILINX_OF_BOARD_DTB_MAX_SIZE U(0x200000) @@ -103,8 +113,8 @@ #define CACHE_WRITEBACK_SHIFT 6 #define CACHE_WRITEBACK_GRANULE (1 << CACHE_WRITEBACK_SHIFT) -#define PLAT_GICD_BASE_VALUE U(0xF9000000) -#define PLAT_GICR_BASE_VALUE U(0xF9080000) +#define PLAT_ARM_GICD_BASE U(0xF9000000) +#define PLAT_ARM_GICR_BASE U(0xF9080000) /* * Define a list of Group 1 Secure and Group 0 interrupts as per GICv3 @@ -122,6 +132,8 @@ #define PLAT_VERSAL_G0_IRQ_PROPS(grp) \ INTR_PROP_DESC(PLAT_VERSAL_IPI_IRQ, GIC_HIGHEST_SEC_PRIORITY, grp, \ GIC_INTR_CFG_EDGE), \ + INTR_PROP_DESC(CPU_PWR_DOWN_REQ_INTR, GIC_HIGHEST_SEC_PRIORITY, grp, \ + GIC_INTR_CFG_EDGE) #define IRQ_MAX 142U diff --git a/plat/xilinx/versal/include/versal_def.h b/plat/xilinx/versal/include/versal_def.h index 92c0ba6c..3a1c127f 100644 --- a/plat/xilinx/versal/include/versal_def.h +++ b/plat/xilinx/versal/include/versal_def.h @@ -1,7 +1,7 @@ /* * Copyright (c) 2018-2022, Arm Limited and Contributors. All rights reserved. * Copyright (c) 2019-2022, Xilinx, Inc. All rights reserved. - * Copyright (c) 2022-2023, Advanced Micro Devices, Inc. All rights reserved. + * Copyright (c) 2022-2024, Advanced Micro Devices, Inc. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -18,20 +18,30 @@ /* number of interrupt handlers. increase as required */ #define MAX_INTR_EL3 2 /* List all consoles */ +#define VERSAL_CONSOLE_ID_none 0 #define VERSAL_CONSOLE_ID_pl011 1 #define VERSAL_CONSOLE_ID_pl011_0 1 #define VERSAL_CONSOLE_ID_pl011_1 2 #define VERSAL_CONSOLE_ID_dcc 3 +#define VERSAL_CONSOLE_ID_dtb 4 #define CONSOLE_IS(con) (VERSAL_CONSOLE_ID_ ## con == VERSAL_CONSOLE) -/* List all supported platforms */ -#define VERSAL_PLATFORM_ID_versal_virt 1 -#define VERSAL_PLATFORM_ID_spp_itr6 2 -#define VERSAL_PLATFORM_ID_emu_itr6 3 -#define VERSAL_PLATFORM_ID_silicon 4 +/* Runtime console */ +#define RT_CONSOLE_ID_pl011 1 +#define RT_CONSOLE_ID_pl011_0 1 +#define RT_CONSOLE_ID_pl011_1 2 +#define RT_CONSOLE_ID_dcc 3 +#define RT_CONSOLE_ID_dtb 4 -#define VERSAL_PLATFORM_IS(con) (VERSAL_PLATFORM_ID_ ## con == VERSAL_PLATFORM) +#define RT_CONSOLE_IS(con) (RT_CONSOLE_ID_ ## con == CONSOLE_RUNTIME) + +/* List of platforms */ +#define VERSAL_SILICON U(0) +#define VERSAL_SPP U(1) +#define VERSAL_EMU U(2) +#define VERSAL_QEMU U(3) +#define VERSAL_COSIM U(7) /* Firmware Image Package */ #define VERSAL_PRIMARY_CPU 0 @@ -64,38 +74,41 @@ #define VERSAL_UART0_BASE 0xFF000000 #define VERSAL_UART1_BASE 0xFF010000 -#if CONSOLE_IS(pl011) || CONSOLE_IS(dcc) +#if CONSOLE_IS(pl011) || CONSOLE_IS(dtb) # define UART_BASE VERSAL_UART0_BASE +# define UART_TYPE CONSOLE_PL011 #elif CONSOLE_IS(pl011_1) # define UART_BASE VERSAL_UART1_BASE +# define UART_TYPE CONSOLE_PL011 +#elif CONSOLE_IS(dcc) +# define UART_BASE 0x0 +# define UART_TYPE CONSOLE_DCC +#elif CONSOLE_IS(none) +# define UART_TYPE CONSOLE_NONE #else # error "invalid VERSAL_CONSOLE" #endif +/* Runtime console */ +#if defined(CONSOLE_RUNTIME) +#if RT_CONSOLE_IS(pl011) || RT_CONSOLE_IS(dtb) +# define RT_UART_BASE VERSAL_UART0_BASE +# define RT_UART_TYPE CONSOLE_PL011 +#elif RT_CONSOLE_IS(pl011_1) +# define RT_UART_BASE VERSAL_UART1_BASE +# define RT_UART_TYPE CONSOLE_PL011 +#elif RT_CONSOLE_IS(dcc) +# define RT_UART_BASE 0x0 +# define RT_UART_TYPE CONSOLE_DCC +#else +# error "invalid CONSOLE_RUNTIME" +#endif +#endif + /******************************************************************************* * Platform related constants ******************************************************************************/ -#if VERSAL_PLATFORM_IS(versal_virt) -# define PLATFORM_NAME "Versal Virt" -# define UART_CLOCK 25000000 -# define UART_BAUDRATE 115200 -# define VERSAL_CPU_CLOCK 2720000 -#elif VERSAL_PLATFORM_IS(silicon) -# define PLATFORM_NAME "Versal Silicon" -# define UART_CLOCK 100000000 -# define UART_BAUDRATE 115200 -# define VERSAL_CPU_CLOCK 100000000 -#elif VERSAL_PLATFORM_IS(spp_itr6) -# define PLATFORM_NAME "SPP ITR6" -# define UART_CLOCK 25000000 -# define UART_BAUDRATE 115200 -# define VERSAL_CPU_CLOCK 2720000 -#elif VERSAL_PLATFORM_IS(emu_itr6) -# define PLATFORM_NAME "EMU ITR6" -# define UART_CLOCK 212000 -# define UART_BAUDRATE 9600 -# define VERSAL_CPU_CLOCK 212000 -#endif +#define UART_BAUDRATE 115200 /* Access control register defines */ #define ACTLR_EL3_L2ACTLR_BIT (1 << 6) @@ -111,6 +124,10 @@ #define CRF_RST_APU_ACPU_RESET (1 << 0) #define CRF_RST_APU_ACPU_PWRON_RESET (1 << 10) +/* IOU SCNTRS */ +#define IOU_SCNTRS_BASE U(0xFF140000) +#define IOU_SCNTRS_BASE_FREQ_OFFSET U(0x20) + /* APU registers and bitfields */ #define FPD_APU_BASE 0xFD5C0000U #define FPD_APU_CONFIG_0 (FPD_APU_BASE + 0x20U) diff --git a/plat/xilinx/versal/plat_psci.c b/plat/xilinx/versal/plat_psci.c index 56d98f79..3fc6dbd3 100644 --- a/plat/xilinx/versal/plat_psci.c +++ b/plat/xilinx/versal/plat_psci.c @@ -1,6 +1,6 @@ /* * Copyright (c) 2018-2021, Arm Limited and Contributors. All rights reserved. - * Copyright (c) 2022-2023, Advanced Micro Devices, Inc. All rights reserved. + * Copyright (c) 2022-2024, Advanced Micro Devices, Inc. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -14,10 +14,13 @@ #include <plat/common/platform.h> #include <plat_arm.h> +#include "drivers/delay_timer.h" #include <plat_private.h> #include "pm_api_sys.h" #include "pm_client.h" #include <pm_common.h> +#include "pm_ipi.h" +#include "pm_svc_main.h" static uintptr_t versal_sec_entry; @@ -33,6 +36,9 @@ static int32_t versal_pwr_domain_on(u_register_t mpidr) } proc = pm_get_proc((uint32_t)cpu_id); + if (proc == NULL) { + return PSCI_E_INTERN_FAIL; + } /* Send request to PMC to wake up selected ACPU core */ (void)pm_req_wakeup(proc->node_id, (versal_sec_entry & 0xFFFFFFFFU) | 0x1U, @@ -56,6 +62,10 @@ static void versal_pwr_domain_suspend(const psci_power_state_t *target_state) uint32_t cpu_id = plat_my_core_pos(); const struct pm_proc *proc = pm_get_proc(cpu_id); + if (proc == NULL) { + return; + } + for (size_t i = 0U; i <= PLAT_MAX_PWR_LVL; i++) { VERBOSE("%s: target_state->pwr_domain_state[%lu]=%x\n", __func__, i, target_state->pwr_domain_state[i]); @@ -67,7 +77,7 @@ static void versal_pwr_domain_suspend(const psci_power_state_t *target_state) plat_versal_gic_save(); } - state = target_state->pwr_domain_state[1] > PLAT_MAX_RET_STATE ? + state = (target_state->pwr_domain_state[1] > PLAT_MAX_RET_STATE) ? PM_STATE_SUSPEND_TO_RAM : PM_STATE_CPU_IDLE; /* Send request to PMC to suspend this core */ @@ -93,6 +103,10 @@ static void versal_pwr_domain_suspend_finish( uint32_t cpu_id = plat_my_core_pos(); const struct pm_proc *proc = pm_get_proc(cpu_id); + if (proc == NULL) { + return; + } + for (size_t i = 0U; i <= PLAT_MAX_PWR_LVL; i++) { VERBOSE("%s: target_state->pwr_domain_state[%lu]=%x\n", __func__, i, target_state->pwr_domain_state[i]); @@ -112,7 +126,7 @@ static void versal_pwr_domain_suspend_finish( plat_versal_gic_cpuif_enable(); } -void versal_pwr_domain_on_finish(const psci_power_state_t *target_state) +static void versal_pwr_domain_on_finish(const psci_power_state_t *target_state) { /* Enable the gic cpu interface */ plat_versal_gic_pcpu_init(); @@ -132,7 +146,7 @@ static void __dead2 versal_system_off(void) (void)pm_system_shutdown(XPM_SHUTDOWN_TYPE_SHUTDOWN, pm_get_shutdown_scope(), SECURE_FLAG); - while (1) { + while (true) { wfi(); } } @@ -145,11 +159,33 @@ static void __dead2 versal_system_off(void) */ static void __dead2 versal_system_reset(void) { - /* Send the system reset request to the PMC */ - (void)pm_system_shutdown(XPM_SHUTDOWN_TYPE_RESET, - pm_get_shutdown_scope(), SECURE_FLAG); + uint32_t ret, timeout = 10000U; - while (1) { + request_cpu_pwrdwn(); + + /* + * Send the system reset request to the firmware if power down request + * is not received from firmware. + */ + if (!pwrdwn_req_received) { + (void)pm_system_shutdown(XPM_SHUTDOWN_TYPE_RESET, + pm_get_shutdown_scope(), SECURE_FLAG); + + /* + * Wait for system shutdown request completed and idle callback + * not received. + */ + do { + ret = ipi_mb_enquire_status(primary_proc->ipi->local_ipi_id, + primary_proc->ipi->remote_ipi_id); + udelay(100); + timeout--; + } while ((ret != IPI_MB_STATUS_RECV_PENDING) && (timeout > 0U)); + } + + (void)psci_cpu_off(); + + while (true) { wfi(); } } @@ -161,9 +197,14 @@ static void __dead2 versal_system_reset(void) */ static void versal_pwr_domain_off(const psci_power_state_t *target_state) { + uint32_t ret, fw_api_version, version_type[RET_PAYLOAD_ARG_CNT] = {0U}; uint32_t cpu_id = plat_my_core_pos(); const struct pm_proc *proc = pm_get_proc(cpu_id); + if (proc == NULL) { + return; + } + for (size_t i = 0U; i <= PLAT_MAX_PWR_LVL; i++) { VERBOSE("%s: target_state->pwr_domain_state[%lu]=%x\n", __func__, i, target_state->pwr_domain_state[i]); @@ -180,8 +221,17 @@ static void versal_pwr_domain_off(const psci_power_state_t *target_state) * invoking CPU_on function, during which resume address will * be set. */ - (void)pm_self_suspend(proc->node_id, MAX_LATENCY, PM_STATE_CPU_IDLE, 0, - SECURE_FLAG); + ret = pm_feature_check((uint32_t)PM_SELF_SUSPEND, &version_type[0], SECURE_FLAG); + if (ret == PM_RET_SUCCESS) { + fw_api_version = version_type[0] & 0xFFFFU; + if (fw_api_version >= 3U) { + (void)pm_self_suspend(proc->node_id, MAX_LATENCY, PM_STATE_CPU_OFF, 0, + SECURE_FLAG); + } else { + (void)pm_self_suspend(proc->node_id, MAX_LATENCY, PM_STATE_CPU_IDLE, 0, + SECURE_FLAG); + } + } } /** @@ -200,7 +250,7 @@ static int32_t versal_validate_power_state(uint32_t power_state, uint32_t pstate = psci_get_pstate_type(power_state); - assert(req_state); + assert(req_state != NULL); /* Sanity check the requested state */ if (pstate == PSTATE_TYPE_STANDBY) { diff --git a/plat/xilinx/versal/platform.mk b/plat/xilinx/versal/platform.mk index 7c53daad..7c15be02 100644 --- a/plat/xilinx/versal/platform.mk +++ b/plat/xilinx/versal/platform.mk @@ -1,5 +1,5 @@ # Copyright (c) 2018-2021, Arm Limited and Contributors. All rights reserved. -# Copyright (c) 2022-2023, Advanced Micro Devices, Inc. All rights reserved. +# Copyright (c) 2022-2024, Advanced Micro Devices, Inc. All rights reserved. # # SPDX-License-Identifier: BSD-3-Clause @@ -11,6 +11,8 @@ override RESET_TO_BL31 := 1 PL011_GENERIC_UART := 1 IPI_CRC_CHECK := 0 HARDEN_SLS_ALL := 0 +CPU_PWRDWN_SGI ?= 6 +$(eval $(call add_define_val,CPU_PWR_DOWN_REQ_INTR,ARM_IRQ_SEC_SGI_${CPU_PWRDWN_SGI})) # A72 Erratum for SoC ERRATA_A72_859971 := 1 @@ -20,7 +22,7 @@ ifdef VERSAL_ATF_MEM_BASE $(eval $(call add_define,VERSAL_ATF_MEM_BASE)) ifndef VERSAL_ATF_MEM_SIZE - $(error "VERSAL_ATF_BASE defined without VERSAL_ATF_SIZE") + $(error "VERSAL_ATF_MEM_BASE defined without VERSAL_ATF_MEM_SIZE") endif $(eval $(call add_define,VERSAL_ATF_MEM_SIZE)) @@ -33,7 +35,7 @@ ifdef VERSAL_BL32_MEM_BASE $(eval $(call add_define,VERSAL_BL32_MEM_BASE)) ifndef VERSAL_BL32_MEM_SIZE - $(error "VERSAL_BL32_BASE defined without VERSAL_BL32_SIZE") + $(error "VERSAL_BL32_MEM_BASE defined without VERSAL_BL32_MEM_SIZE") endif $(eval $(call add_define,VERSAL_BL32_MEM_SIZE)) endif @@ -42,8 +44,9 @@ ifdef IPI_CRC_CHECK $(eval $(call add_define,IPI_CRC_CHECK)) endif -VERSAL_PLATFORM ?= silicon -$(eval $(call add_define_val,VERSAL_PLATFORM,VERSAL_PLATFORM_ID_${VERSAL_PLATFORM})) +ifdef VERSAL_PLATFORM + $(warning "VERSAL_PLATFORM has been deprecated") +endif ifdef XILINX_OF_BOARD_DTB_ADDR $(eval $(call add_define,XILINX_OF_BOARD_DTB_ADDR)) @@ -82,13 +85,27 @@ PLAT_BL_COMMON_SOURCES := drivers/arm/dcc/dcc_console.c \ ${XLAT_TABLES_LIB_SRCS} VERSAL_CONSOLE ?= pl011 -ifeq (${VERSAL_CONSOLE}, $(filter ${VERSAL_CONSOLE},pl011 pl011_0 pl011_1 dcc)) +ifeq (${VERSAL_CONSOLE}, $(filter ${VERSAL_CONSOLE},pl011 pl011_0 pl011_1 dcc dtb none)) else $(error "Please define VERSAL_CONSOLE") endif $(eval $(call add_define_val,VERSAL_CONSOLE,VERSAL_CONSOLE_ID_${VERSAL_CONSOLE})) +# Runtime console in default console in DEBUG build +ifeq ($(DEBUG), 1) +CONSOLE_RUNTIME ?= pl011 +endif + +# Runtime console +ifdef CONSOLE_RUNTIME +ifeq (${CONSOLE_RUNTIME}, $(filter ${CONSOLE_RUNTIME},pl011 pl011_0 pl011_1 dcc dtb)) +$(eval $(call add_define_val,CONSOLE_RUNTIME,RT_CONSOLE_ID_${CONSOLE_RUNTIME})) +else +$(error "Please define CONSOLE_RUNTIME") +endif +endif + BL31_SOURCES += drivers/arm/cci/cci.c \ lib/cpus/aarch64/cortex_a72.S \ common/fdt_wrappers.c \ @@ -96,6 +113,7 @@ BL31_SOURCES += drivers/arm/cci/cci.c \ plat/xilinx/common/ipi.c \ plat/xilinx/common/plat_fdt.c \ plat/xilinx/common/plat_console.c \ + plat/xilinx/common/plat_clkfunc.c \ plat/xilinx/common/plat_startup.c \ plat/xilinx/common/ipi_mailbox_service/ipi_mailbox_svc.c \ plat/xilinx/common/pm_service/pm_ipi.c \ @@ -116,3 +134,9 @@ BL31_SOURCES += drivers/arm/cci/cci.c \ ifeq ($(HARDEN_SLS_ALL), 1) TF_CFLAGS_aarch64 += -mharden-sls=all endif + +ifeq (${ERRATA_ABI_SUPPORT}, 1) +# enable the cpu macros for errata abi interface +CORTEX_A72_H_INC := 1 +$(eval $(call add_define, CORTEX_A72_H_INC)) +endif diff --git a/plat/xilinx/versal/pm_service/pm_client.c b/plat/xilinx/versal/pm_service/pm_client.c index ccbfe770..3e441538 100644 --- a/plat/xilinx/versal/pm_service/pm_client.c +++ b/plat/xilinx/versal/pm_service/pm_client.c @@ -1,6 +1,6 @@ /* * Copyright (c) 2019-2022, Xilinx, Inc. All rights reserved. - * Copyright (c) 2022-2023, Advanced Micro Devices, Inc. All rights reserved. + * Copyright (c) 2022-2024, Advanced Micro Devices, Inc. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -155,6 +155,9 @@ enum pm_device_node_idx irq_to_pm_node_idx(uint32_t irq) case 74: dev_idx = XPM_NODEIDX_DEV_USB_0; break; + case 122: + dev_idx = XPM_NODEIDX_DEV_GPIO_PMC; + break; case 126: case 127: dev_idx = XPM_NODEIDX_DEV_SDIO_0; diff --git a/plat/xilinx/versal/sip_svc_setup.c b/plat/xilinx/versal/sip_svc_setup.c index b30254d5..3027946b 100644 --- a/plat/xilinx/versal/sip_svc_setup.c +++ b/plat/xilinx/versal/sip_svc_setup.c @@ -1,6 +1,6 @@ /* * Copyright (c) 2018-2021, Arm Limited and Contributors. All rights reserved. - * Copyright (c) 2022-2023, Advanced Micro Devices, Inc. All rights reserved. + * Copyright (c) 2022-2024, Advanced Micro Devices, Inc. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -17,13 +17,12 @@ #include "pm_svc_main.h" /* SMC function IDs for SiP Service queries */ -#define VERSAL_SIP_SVC_CALL_COUNT U(0x8200ff00) #define VERSAL_SIP_SVC_UID U(0x8200ff01) #define VERSAL_SIP_SVC_VERSION U(0x8200ff03) /* SiP Service Calls version numbers */ #define SIP_SVC_VERSION_MAJOR U(0) -#define SIP_SVC_VERSION_MINOR U(1) +#define SIP_SVC_VERSION_MINOR U(2) /* These macros are used to identify PM calls from the SMC function ID */ #define SIP_FID_MASK GENMASK(23, 16) @@ -69,19 +68,19 @@ static int32_t sip_svc_setup(void) * * Return: Unused. */ -uintptr_t sip_svc_smc_handler(uint32_t smc_fid, - u_register_t x1, - u_register_t x2, - u_register_t x3, - u_register_t x4, - void *cookie, - void *handle, - u_register_t flags) +static uintptr_t sip_svc_smc_handler(uint32_t smc_fid, + u_register_t x1, + u_register_t x2, + u_register_t x3, + u_register_t x4, + void *cookie, + void *handle, + u_register_t flags) { VERBOSE("SMCID: 0x%08x, x1: 0x%016" PRIx64 ", x2: 0x%016" PRIx64 ", x3: 0x%016" PRIx64 ", x4: 0x%016" PRIx64 "\n", smc_fid, x1, x2, x3, x4); - if (smc_fid & SIP_FID_MASK) { + if ((smc_fid & SIP_FID_MASK) != 0U) { WARN("SMC out of SiP assinged range: 0x%x\n", smc_fid); SMC_RET1(handle, SMC_UNK); } @@ -100,10 +99,6 @@ uintptr_t sip_svc_smc_handler(uint32_t smc_fid, /* Let PM SMC handler deal with PM-related requests */ switch (smc_fid) { - case VERSAL_SIP_SVC_CALL_COUNT: - /* PM functions + default functions */ - SMC_RET1(handle, 2); - case VERSAL_SIP_SVC_UID: SMC_UUID_RET(handle, versal_sip_uuid); diff --git a/plat/xilinx/versal/versal_gicv3.c b/plat/xilinx/versal/versal_gicv3.c index 197d047f..1750d351 100644 --- a/plat/xilinx/versal/versal_gicv3.c +++ b/plat/xilinx/versal/versal_gicv3.c @@ -62,8 +62,8 @@ static uint32_t versal_gicv3_mpidr_hash(u_register_t mpidr) } static const gicv3_driver_data_t versal_gic_data __unused = { - .gicd_base = PLAT_GICD_BASE_VALUE, - .gicr_base = PLAT_GICR_BASE_VALUE, + .gicd_base = PLAT_ARM_GICD_BASE, + .gicr_base = PLAT_ARM_GICR_BASE, .interrupt_props = versal_interrupt_props, .interrupt_props_num = ARRAY_SIZE(versal_interrupt_props), .rdistif_num = PLATFORM_CORE_COUNT, diff --git a/plat/xilinx/versal_net/aarch64/versal_net_common.c b/plat/xilinx/versal_net/aarch64/versal_net_common.c index 69c5c87d..0dd01946 100644 --- a/plat/xilinx/versal_net/aarch64/versal_net_common.c +++ b/plat/xilinx/versal_net/aarch64/versal_net_common.c @@ -60,11 +60,11 @@ char *board_name_decode(void) void board_detection(void) { - uint32_t version; + uint32_t version_type; - version = mmio_read_32(PMC_TAP_VERSION); - platform_id = FIELD_GET(PLATFORM_MASK, version); - platform_version = FIELD_GET(PLATFORM_VERSION_MASK, version); + version_type = mmio_read_32(PMC_TAP_VERSION); + platform_id = FIELD_GET(PLATFORM_MASK, version_type); + platform_version = FIELD_GET(PLATFORM_VERSION_MASK, version_type); if (platform_id == VERSAL_NET_QEMU_COSIM) { platform_id = VERSAL_NET_QEMU; @@ -113,12 +113,22 @@ uint32_t get_uart_clk(void) } void versal_net_config_setup(void) +{ + generic_delay_timer_init(); + +#if (TFA_NO_PM == 0) + /* Configure IPI data for versal_net */ + versal_net_ipi_config_table_init(); +#endif +} + +void syscnt_freq_config_setup(void) { uint32_t val; uintptr_t crl_base, iou_scntrs_base, psx_base; crl_base = VERSAL_NET_CRL; - iou_scntrs_base = VERSAL_NET_IOU_SCNTRS; + iou_scntrs_base = IOU_SCNTRS_BASE; psx_base = PSX_CRF; /* Reset for system timestamp generator in FPX */ @@ -133,20 +143,9 @@ void versal_net_config_setup(void) mmio_write_32(crl_base + VERSAL_NET_CRL_RST_TIMESTAMP_OFFSET, 0); /* Program freq register in System counter and enable system counter. */ - mmio_write_32(iou_scntrs_base + VERSAL_NET_IOU_SCNTRS_BASE_FREQ_OFFSET, + mmio_write_32(iou_scntrs_base + IOU_SCNTRS_BASE_FREQ_OFFSET, cpu_clock); - mmio_write_32(iou_scntrs_base + VERSAL_NET_IOU_SCNTRS_COUNTER_CONTROL_REG_OFFSET, - VERSAL_NET_IOU_SCNTRS_CONTROL_EN); - - generic_delay_timer_init(); - -#if (TFA_NO_PM == 0) - /* Configure IPI data for versal_net */ - versal_net_ipi_config_table_init(); -#endif + mmio_write_32(iou_scntrs_base + IOU_SCNTRS_COUNTER_CONTROL_REG_OFFSET, + IOU_SCNTRS_CONTROL_EN); } -uint32_t plat_get_syscnt_freq2(void) -{ - return cpu_clock; -} diff --git a/plat/xilinx/versal_net/aarch64/versal_net_helpers.S b/plat/xilinx/versal_net/aarch64/versal_net_helpers.S index dab87179..1ae879f3 100644 --- a/plat/xilinx/versal_net/aarch64/versal_net_helpers.S +++ b/plat/xilinx/versal_net/aarch64/versal_net_helpers.S @@ -12,6 +12,7 @@ #include <platform_def.h> + .globl plat_arm_calc_core_pos .globl plat_secondary_cold_boot_setup .globl plat_is_my_cpu_primary .globl platform_mem_init @@ -58,6 +59,16 @@ func plat_my_core_pos b plat_core_pos_by_mpidr endfunc plat_my_core_pos + /* ----------------------------------------------------- + * unsigned int plat_arm_calc_core_pos(u_register_t mpidr) + * This function uses the plat_core_pos_by_mpidr() + * definition to get the index of the calling CPU. + * ----------------------------------------------------- + */ +func plat_arm_calc_core_pos + b plat_core_pos_by_mpidr +endfunc plat_arm_calc_core_pos + /* --------------------------------------------------------------------- * We don't need to carry out any memory initialization on Versal NET * platform. The Secure RAM is accessible straight away. diff --git a/plat/xilinx/versal_net/bl31_versal_net_setup.c b/plat/xilinx/versal_net/bl31_versal_net_setup.c index 56ef27b1..cf2368a0 100644 --- a/plat/xilinx/versal_net/bl31_versal_net_setup.c +++ b/plat/xilinx/versal_net/bl31_versal_net_setup.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018-2020, Arm Limited and Contributors. All rights reserved. + * Copyright (c) 2018-2024, Arm Limited and Contributors. All rights reserved. * Copyright (c) 2018-2022, Xilinx, Inc. All rights reserved. * Copyright (c) 2022-2023, Advanced Micro Devices, Inc. All rights reserved. * @@ -17,6 +17,7 @@ #include <plat/common/platform.h> #include <plat_arm.h> #include <plat_console.h> +#include <plat_clkfunc.h> #include <plat_fdt.h> #include <plat_private.h> @@ -58,6 +59,20 @@ static inline void bl31_set_default_config(void) DISABLE_ALL_EXCEPTIONS); } +/* Define read and write function for clusterbusqos register */ +DEFINE_RENAME_SYSREG_RW_FUNCS(cluster_bus_qos, S3_0_C15_C4_4) + +static void versal_net_setup_qos(void) +{ + int ret; + + ret = read_cluster_bus_qos(); + INFO("BL31: default cluster bus qos: 0x%x\n", ret); + write_cluster_bus_qos(0); + ret = read_cluster_bus_qos(); + INFO("BL31: cluster bus qos written: 0x%x\n", ret); +} + /* * Perform any BL31 specific platform actions. Here is an opportunity to copy * parameters passed by the calling EL (S-EL1 in BL2 & S-EL3 in BL1) before they @@ -67,6 +82,11 @@ static inline void bl31_set_default_config(void) void bl31_early_platform_setup2(u_register_t arg0, u_register_t arg1, u_register_t arg2, u_register_t arg3) { + (void)arg0; + (void)arg1; + (void)arg2; + (void)arg3; + #if !(TFA_NO_PM) uint64_t tfa_handoff_addr, buff[HANDOFF_PARAMS_MAX_SIZE] = {0}; uint32_t payload[PAYLOAD_ARG_CNT], max_size = HANDOFF_PARAMS_MAX_SIZE; @@ -93,11 +113,17 @@ void bl31_early_platform_setup2(u_register_t arg0, u_register_t arg1, panic(); } + syscnt_freq_config_setup(); + + set_cnt_freq(); + setup_console(); NOTICE("TF-A running on %s %d.%d\n", board_name_decode(), platform_version / 10U, platform_version % 10U); + versal_net_setup_qos(); + /* Initialize the platform config for future decision making */ versal_net_config_setup(); @@ -132,18 +158,6 @@ void bl31_early_platform_setup2(u_register_t arg0, u_register_t arg1, INFO("BL31: PLM to TF-A handover success\n"); - /* - * The BL32 load address is indicated as 0x0 in the handoff - * parameters, which is different from the default/user-provided - * load address of 0x60000000 but the flags are correctly - * configured. Consequently, in this scenario, set the PC - * to the requested BL32_BASE address. - */ - - /* TODO: Remove the following check once this is fixed from PLM */ - if (bl32_image_ep_info.pc == 0 && bl32_image_ep_info.spsr != 0) { - bl32_image_ep_info.pc = (uintptr_t)BL32_BASE; - } } else { INFO("BL31: setting up default configs\n"); @@ -165,7 +179,7 @@ int request_intr_type_el3(uint32_t id, interrupt_type_handler_t handler) uint32_t i; /* Validate 'handler' and 'id' parameters */ - if (handler == NULL || index >= MAX_INTR_EL3) { + if ((handler == NULL) || (index >= MAX_INTR_EL3)) { return -EINVAL; } @@ -200,7 +214,7 @@ static uint64_t rdo_el3_interrupt_handler(uint32_t id, uint32_t flags, } if (handler != NULL) { - handler(intr_id, flags, handle, cookie); + (void)handler(intr_id, flags, handle, cookie); } return 0; @@ -211,8 +225,8 @@ void bl31_platform_setup(void) prepare_dtb(); /* Initialize the gic cpu and distributor interfaces */ - plat_versal_net_gic_driver_init(); - plat_versal_net_gic_init(); + plat_arm_gic_driver_init(); + plat_arm_gic_init(); } void bl31_plat_runtime_setup(void) @@ -226,8 +240,6 @@ void bl31_plat_runtime_setup(void) if (rc != 0) { panic(); } - - console_switch_state(CONSOLE_FLAG_RUNTIME); } /* diff --git a/plat/xilinx/versal_net/include/plat_ipi.h b/plat/xilinx/versal_net/include/plat_ipi.h index 9f9947e3..e0fe723b 100644 --- a/plat/xilinx/versal_net/include/plat_ipi.h +++ b/plat/xilinx/versal_net/include/plat_ipi.h @@ -24,7 +24,15 @@ #define IPI_ID_3 5U #define IPI_ID_4 6U #define IPI_ID_5 7U -#define IPI_ID_MAX 8U +#define IPI_ID_PMC_NOBUF 8U +#define IPI_ID_6_NOBUF_95 9U +#define IPI_ID_1_NOBUF 10U +#define IPI_ID_2_NOBUF 11U +#define IPI_ID_3_NOBUF 12U +#define IPI_ID_4_NOBUF 13U +#define IPI_ID_5_NOBUF 14U +#define IPI_ID_6_NOBUF_101 15U +#define IPI_ID_MAX 16U /********************************************************************* * IPI message buffers @@ -68,5 +76,21 @@ void versal_net_ipi_config_table_init(void); #define IPI4_TRIG_BIT (1 << 6) #define IPI5_REG_BASE (0xEB380000U) #define IPI5_TRIG_BIT (1 << 7) +#define PMC_NOBUF_REG_BASE (0xEB390000U) +#define PMC_NOBUF_TRIG_BIT (1 << 8) +#define IPI6_NOBUF_95_REG_BASE (0xEB3A0000U) +#define IPI6_NOBUF_95_TRIG_BIT (1 << 9) +#define IPI1_NOBUF_REG_BASE (0xEB3B0000U) +#define IPI1_NOBUF_TRIG_BIT (1 << 10) +#define IPI2_NOBUF_REG_BASE (0xEB3B1000U) +#define IPI2_NOBUF_TRIG_BIT (1 << 11) +#define IPI3_NOBUF_REG_BASE (0xEB3B2000U) +#define IPI3_NOBUF_TRIG_BIT (1 << 12) +#define IPI4_NOBUF_REG_BASE (0xEB3B3000U) +#define IPI4_NOBUF_TRIG_BIT (1 << 13) +#define IPI5_NOBUF_REG_BASE (0xEB3B4000U) +#define IPI5_NOBUF_TRIG_BIT (1 << 14) +#define IPI6_NOBUF_101_REG_BASE (0xEB3B5000U) +#define IPI6_NOBUF_101_TRIG_BIT (1 << 15) #endif /* PLAT_IPI_H */ diff --git a/plat/xilinx/versal_net/include/plat_macros.S b/plat/xilinx/versal_net/include/plat_macros.S index db7e42b3..57f8336b 100644 --- a/plat/xilinx/versal_net/include/plat_macros.S +++ b/plat/xilinx/versal_net/include/plat_macros.S @@ -109,8 +109,8 @@ exit_print_gic_regs: * Uncomment it when versions are stable */ /* - mov_imm x17, PLAT_GICD_BASE_VALUE - mov_imm x16, PLAT_GICR_BASE_VALUE + mov_imm x17, PLAT_ARM_GICD_BASE + mov_imm x16, PLAT_ARM_GICR_BASE versal_net_print_gic_regs */ .endm diff --git a/plat/xilinx/versal_net/include/plat_private.h b/plat/xilinx/versal_net/include/plat_private.h index 9cd86361..0b82ca73 100644 --- a/plat/xilinx/versal_net/include/plat_private.h +++ b/plat/xilinx/versal_net/include/plat_private.h @@ -18,6 +18,7 @@ typedef struct versal_intr_info_type_el3 { } versal_intr_info_type_el3_t; void versal_net_config_setup(void); +void syscnt_freq_config_setup(void); uint32_t get_uart_clk(void); const mmap_region_t *plat_get_mmap(void); diff --git a/plat/xilinx/versal_net/include/platform_def.h b/plat/xilinx/versal_net/include/platform_def.h index 872b6eeb..8cb7deb1 100644 --- a/plat/xilinx/versal_net/include/platform_def.h +++ b/plat/xilinx/versal_net/include/platform_def.h @@ -10,6 +10,7 @@ #define PLATFORM_DEF_H #include <arch.h> +#include <plat_common.h> #include "versal_net_def.h" /******************************************************************************* @@ -107,8 +108,8 @@ #define CACHE_WRITEBACK_SHIFT U(6) #define CACHE_WRITEBACK_GRANULE (1 << CACHE_WRITEBACK_SHIFT) -#define PLAT_GICD_BASE_VALUE U(0xE2000000) -#define PLAT_GICR_BASE_VALUE U(0xE2060000) +#define PLAT_ARM_GICD_BASE U(0xE2000000) +#define PLAT_ARM_GICR_BASE U(0xE2060000) /* * Define a list of Group 1 Secure and Group 0 interrupts as per GICv3 @@ -118,13 +119,15 @@ #define PLAT_VERSAL_NET_IPI_IRQ 89 #define PLAT_VERSAL_IPI_IRQ PLAT_VERSAL_NET_IPI_IRQ -#define PLAT_VERSAL_NET_G1S_IRQ_PROPS(grp) \ +#define PLAT_ARM_G1S_IRQ_PROPS(grp) \ INTR_PROP_DESC(VERSAL_NET_IRQ_SEC_PHY_TIMER, GIC_HIGHEST_SEC_PRIORITY, grp, \ GIC_INTR_CFG_LEVEL) -#define PLAT_VERSAL_NET_G0_IRQ_PROPS(grp) \ +#define PLAT_ARM_G0_IRQ_PROPS(grp) \ INTR_PROP_DESC(PLAT_VERSAL_IPI_IRQ, GIC_HIGHEST_SEC_PRIORITY, grp, \ GIC_INTR_CFG_EDGE), \ + INTR_PROP_DESC(CPU_PWR_DOWN_REQ_INTR, GIC_HIGHEST_SEC_PRIORITY, grp, \ + GIC_INTR_CFG_EDGE) #define IRQ_MAX 200U diff --git a/plat/xilinx/versal_net/include/versal_net_def.h b/plat/xilinx/versal_net/include/versal_net_def.h index dd20faa4..5caf3768 100644 --- a/plat/xilinx/versal_net/include/versal_net_def.h +++ b/plat/xilinx/versal_net/include/versal_net_def.h @@ -15,13 +15,24 @@ #define MAX_INTR_EL3 2 /* List all consoles */ +#define VERSAL_NET_CONSOLE_ID_none U(0) #define VERSAL_NET_CONSOLE_ID_pl011 U(1) #define VERSAL_NET_CONSOLE_ID_pl011_0 U(1) #define VERSAL_NET_CONSOLE_ID_pl011_1 U(2) #define VERSAL_NET_CONSOLE_ID_dcc U(3) +#define VERSAL_NET_CONSOLE_ID_dtb U(4) #define CONSOLE_IS(con) (VERSAL_NET_CONSOLE_ID_ ## con == VERSAL_NET_CONSOLE) +/* Runtime console */ +#define RT_CONSOLE_ID_pl011 1 +#define RT_CONSOLE_ID_pl011_0 1 +#define RT_CONSOLE_ID_pl011_1 2 +#define RT_CONSOLE_ID_dcc 3 +#define RT_CONSOLE_ID_dtb 4 + +#define RT_CONSOLE_IS(con) (RT_CONSOLE_ID_ ## con == CONSOLE_RUNTIME) + /* List all platforms */ #define VERSAL_NET_SILICON U(0) #define VERSAL_NET_SPP U(1) @@ -111,11 +122,11 @@ #define VERSAL_NET_CRL_APB_TIMESTAMP_REF_CTRL_CLKACT_BIT (1U << 25U) /* IOU SCNTRS */ -#define VERSAL_NET_IOU_SCNTRS U(0xEC920000) -#define VERSAL_NET_IOU_SCNTRS_COUNTER_CONTROL_REG_OFFSET U(0) -#define VERSAL_NET_IOU_SCNTRS_BASE_FREQ_OFFSET U(0x20) +#define IOU_SCNTRS_BASE U(0xEC920000) +#define IOU_SCNTRS_COUNTER_CONTROL_REG_OFFSET U(0) +#define IOU_SCNTRS_BASE_FREQ_OFFSET U(0x20) -#define VERSAL_NET_IOU_SCNTRS_CONTROL_EN U(1) +#define IOU_SCNTRS_CONTROL_EN U(1) #define APU_CLUSTER0 U(0xECC00000) #define APU_RVBAR_L_0 U(0x40) @@ -138,11 +149,35 @@ #define UART_BAUDRATE 115200 -#if CONSOLE_IS(pl011_1) -#define UART_BASE VERSAL_NET_UART1_BASE +#if CONSOLE_IS(pl011) || CONSOLE_IS(dtb) +#define UART_BASE VERSAL_NET_UART0_BASE +# define UART_TYPE CONSOLE_PL011 +#elif CONSOLE_IS(pl011_1) +#define UART_BASE VERSAL_NET_UART1_BASE +# define UART_TYPE CONSOLE_PL011 +#elif CONSOLE_IS(dcc) +# define UART_BASE 0x0 +# define UART_TYPE CONSOLE_DCC +#elif CONSOLE_IS(none) +# define UART_TYPE CONSOLE_NONE +#else +# error "invalid VERSAL_NET_CONSOLE" +#endif + +/* Runtime console */ +#if defined(CONSOLE_RUNTIME) +#if RT_CONSOLE_IS(pl011) || RT_CONSOLE_IS(dtb) +# define RT_UART_BASE VERSAL_NET_UART0_BASE +# define RT_UART_TYPE CONSOLE_PL011 +#elif RT_CONSOLE_IS(pl011_1) +# define RT_UART_BASE VERSAL_NET_UART1_BASE +# define RT_UART_TYPE CONSOLE_PL011 +#elif RT_CONSOLE_IS(dcc) +# define RT_UART_BASE 0x0 +# define RT_UART_TYPE CONSOLE_DCC #else -/* Default console is UART0 */ -#define UART_BASE VERSAL_NET_UART0_BASE +# error "invalid CONSOLE_RUNTIME" +#endif #endif /* Processor core device IDs */ diff --git a/plat/xilinx/versal_net/plat_psci.c b/plat/xilinx/versal_net/plat_psci.c index 6e556cdf..fcb32b97 100644 --- a/plat/xilinx/versal_net/plat_psci.c +++ b/plat/xilinx/versal_net/plat_psci.c @@ -108,8 +108,8 @@ static void zynqmp_pwr_domain_suspend(const psci_power_state_t *target_state) static void zynqmp_pwr_domain_on_finish(const psci_power_state_t *target_state) { - plat_versal_net_gic_pcpu_init(); - plat_versal_net_gic_cpuif_enable(); + plat_arm_gic_pcpu_init(); + plat_arm_gic_cpuif_enable(); } static void zynqmp_pwr_domain_suspend_finish(const psci_power_state_t *target_state) diff --git a/plat/xilinx/versal_net/plat_psci_pm.c b/plat/xilinx/versal_net/plat_psci_pm.c index 87e25bcd..1c32879b 100644 --- a/plat/xilinx/versal_net/plat_psci_pm.c +++ b/plat/xilinx/versal_net/plat_psci_pm.c @@ -1,6 +1,6 @@ /* * Copyright (c) 2022, Xilinx, Inc. All rights reserved. - * Copyright (c) 2022-2023, Advanced Micro Devices, Inc. All rights reserved. + * Copyright (c) 2022-2024, Advanced Micro Devices, Inc. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -14,10 +14,12 @@ #include <plat/common/platform.h> #include <plat_arm.h> +#include <drivers/delay_timer.h> #include <plat_private.h> #include "pm_api_sys.h" #include "pm_client.h" #include <pm_common.h> +#include "pm_ipi.h" #include "pm_svc_main.h" #include "versal_net_def.h" @@ -36,11 +38,11 @@ static int32_t versal_net_pwr_domain_on(u_register_t mpidr) } proc = pm_get_proc(cpu_id); - if (!proc) { + if (proc == NULL) { return PSCI_E_INTERN_FAIL; } - pm_req_wakeup(proc->node_id, (versal_net_sec_entry & 0xFFFFFFFFU) | 0x1U, + (void)pm_req_wakeup(proc->node_id, (versal_net_sec_entry & 0xFFFFFFFFU) | 0x1U, versal_net_sec_entry >> 32, 0, 0); /* Clear power down request */ @@ -57,16 +59,21 @@ static int32_t versal_net_pwr_domain_on(u_register_t mpidr) */ static void versal_net_pwr_domain_off(const psci_power_state_t *target_state) { + uint32_t ret, fw_api_version, version_type[RET_PAYLOAD_ARG_CNT] = {0U}; uint32_t cpu_id = plat_my_core_pos(); const struct pm_proc *proc = pm_get_proc(cpu_id); + if (proc == NULL) { + return; + } + for (size_t i = 0; i <= PLAT_MAX_PWR_LVL; i++) { VERBOSE("%s: target_state->pwr_domain_state[%lu]=%x\n", __func__, i, target_state->pwr_domain_state[i]); } /* Prevent interrupts from spuriously waking up this cpu */ - plat_versal_net_gic_cpuif_disable(); + plat_arm_gic_cpuif_disable(); /* * Send request to PMC to power down the appropriate APU CPU @@ -76,8 +83,17 @@ static void versal_net_pwr_domain_off(const psci_power_state_t *target_state) * invoking CPU_on function, during which resume address will * be set. */ - pm_self_suspend(proc->node_id, MAX_LATENCY, PM_STATE_CPU_IDLE, 0, - SECURE_FLAG); + ret = pm_feature_check((uint32_t)PM_SELF_SUSPEND, &version_type[0], SECURE_FLAG); + if (ret == PM_RET_SUCCESS) { + fw_api_version = version_type[0] & 0xFFFFU; + if (fw_api_version >= 3U) { + (void)pm_self_suspend(proc->node_id, MAX_LATENCY, PM_STATE_CPU_OFF, 0, + SECURE_FLAG); + } else { + (void)pm_self_suspend(proc->node_id, MAX_LATENCY, PM_STATE_CPU_IDLE, 0, + SECURE_FLAG); + } + } } /** @@ -88,11 +104,33 @@ static void versal_net_pwr_domain_off(const psci_power_state_t *target_state) */ static void __dead2 versal_net_system_reset(void) { - /* Send the system reset request to the PMC */ - pm_system_shutdown(XPM_SHUTDOWN_TYPE_RESET, - pm_get_shutdown_scope(), SECURE_FLAG); + uint32_t ret, timeout = 10000U; + + request_cpu_pwrdwn(); - while (1) { + /* + * Send the system reset request to the firmware if power down request + * is not received from firmware. + */ + if (!pwrdwn_req_received) { + (void)pm_system_shutdown(XPM_SHUTDOWN_TYPE_RESET, + pm_get_shutdown_scope(), SECURE_FLAG); + + /* + * Wait for system shutdown request completed and idle callback + * not received. + */ + do { + ret = ipi_mb_enquire_status(primary_proc->ipi->local_ipi_id, + primary_proc->ipi->remote_ipi_id); + udelay(100); + timeout--; + } while ((ret != IPI_MB_STATUS_RECV_PENDING) && (timeout > 0U)); + } + + (void)psci_cpu_off(); + + while (true) { wfi(); } } @@ -109,22 +147,26 @@ static void versal_net_pwr_domain_suspend(const psci_power_state_t *target_state uint32_t cpu_id = plat_my_core_pos(); const struct pm_proc *proc = pm_get_proc(cpu_id); + if (proc == NULL) { + return; + } + for (size_t i = 0; i <= PLAT_MAX_PWR_LVL; i++) { VERBOSE("%s: target_state->pwr_domain_state[%lu]=%x\n", __func__, i, target_state->pwr_domain_state[i]); } - plat_versal_net_gic_cpuif_disable(); + plat_arm_gic_cpuif_disable(); if (target_state->pwr_domain_state[1] > PLAT_MAX_RET_STATE) { - plat_versal_net_gic_save(); + plat_arm_gic_save(); } - state = target_state->pwr_domain_state[1] > PLAT_MAX_RET_STATE ? + state = (target_state->pwr_domain_state[1] > PLAT_MAX_RET_STATE) ? PM_STATE_SUSPEND_TO_RAM : PM_STATE_CPU_IDLE; /* Send request to PMC to suspend this core */ - pm_self_suspend(proc->node_id, MAX_LATENCY, state, versal_net_sec_entry, + (void)pm_self_suspend(proc->node_id, MAX_LATENCY, state, versal_net_sec_entry, SECURE_FLAG); /* TODO: disable coherency */ @@ -135,10 +177,10 @@ static void versal_net_pwr_domain_on_finish(const psci_power_state_t *target_sta (void)target_state; /* Enable the gic cpu interface */ - plat_versal_net_gic_pcpu_init(); + plat_arm_gic_pcpu_init(); /* Program the gic per-cpu distributor or re-distributor interface */ - plat_versal_net_gic_cpuif_enable(); + plat_arm_gic_cpuif_enable(); } /** @@ -152,6 +194,10 @@ static void versal_net_pwr_domain_suspend_finish(const psci_power_state_t *targe uint32_t cpu_id = plat_my_core_pos(); const struct pm_proc *proc = pm_get_proc(cpu_id); + if (proc == NULL) { + return; + } + for (size_t i = 0; i <= PLAT_MAX_PWR_LVL; i++) VERBOSE("%s: target_state->pwr_domain_state[%lu]=%x\n", __func__, i, target_state->pwr_domain_state[i]); @@ -163,10 +209,10 @@ static void versal_net_pwr_domain_suspend_finish(const psci_power_state_t *targe /* APU was turned off, so restore GIC context */ if (target_state->pwr_domain_state[1] > PLAT_MAX_RET_STATE) { - plat_versal_net_gic_resume(); + plat_arm_gic_resume(); } - plat_versal_net_gic_cpuif_enable(); + plat_arm_gic_cpuif_enable(); } /** @@ -177,10 +223,10 @@ static void versal_net_pwr_domain_suspend_finish(const psci_power_state_t *targe static void __dead2 versal_net_system_off(void) { /* Send the power down request to the PMC */ - pm_system_shutdown(XPM_SHUTDOWN_TYPE_SHUTDOWN, + (void)pm_system_shutdown(XPM_SHUTDOWN_TYPE_SHUTDOWN, pm_get_shutdown_scope(), SECURE_FLAG); - while (1) { + while (true) { wfi(); } } @@ -201,7 +247,7 @@ static int32_t versal_net_validate_power_state(unsigned int power_state, int32_t pstate = psci_get_pstate_type(power_state); - assert(req_state); + assert(req_state != NULL); /* Sanity check the requested state */ if (pstate == PSTATE_TYPE_STANDBY) { @@ -211,7 +257,7 @@ static int32_t versal_net_validate_power_state(unsigned int power_state, } /* We expect the 'state id' to be zero */ - if (psci_get_pstate_id(power_state)) { + if (psci_get_pstate_id(power_state) != 0U) { return PSCI_E_INVALID_PARAMS; } diff --git a/plat/xilinx/versal_net/platform.mk b/plat/xilinx/versal_net/platform.mk index f2991890..9534118b 100644 --- a/plat/xilinx/versal_net/platform.mk +++ b/plat/xilinx/versal_net/platform.mk @@ -21,6 +21,8 @@ IPI_CRC_CHECK := 0 GIC_ENABLE_V4_EXTN := 0 GICV3_SUPPORT_GIC600 := 1 TFA_NO_PM := 0 +CPU_PWRDWN_SGI ?= 6 +$(eval $(call add_define_val,CPU_PWR_DOWN_REQ_INTR,ARM_IRQ_SEC_SGI_${CPU_PWRDWN_SGI})) override CTX_INCLUDE_AARCH32_REGS := 0 @@ -32,7 +34,7 @@ ifdef VERSAL_NET_ATF_MEM_BASE $(eval $(call add_define,VERSAL_NET_ATF_MEM_BASE)) ifndef VERSAL_NET_ATF_MEM_SIZE - $(error "VERSAL_NET_ATF_BASE defined without VERSAL_NET_ATF_SIZE") + $(error "VERSAL_NET_ATF_MEM_BASE defined without VERSAL_NET_ATF_MEM_SIZE") endif $(eval $(call add_define,VERSAL_NET_ATF_MEM_SIZE)) @@ -45,7 +47,7 @@ ifdef VERSAL_NET_BL32_MEM_BASE $(eval $(call add_define,VERSAL_NET_BL32_MEM_BASE)) ifndef VERSAL_NET_BL32_MEM_SIZE - $(error "VERSAL_NET_BL32_BASE defined without VERSAL_NET_BL32_SIZE") + $(error "VERSAL_NET_BL32_MEM_BASE defined without VERSAL_NET_BL32_MEM_SIZE") endif $(eval $(call add_define,VERSAL_NET_BL32_MEM_SIZE)) endif @@ -58,7 +60,7 @@ USE_COHERENT_MEM := 0 HW_ASSISTED_COHERENCY := 1 VERSAL_NET_CONSOLE ?= pl011 -ifeq (${VERSAL_NET_CONSOLE}, $(filter ${VERSAL_NET_CONSOLE},pl011 pl011_0 pl011_1 dcc)) +ifeq (${VERSAL_NET_CONSOLE}, $(filter ${VERSAL_NET_CONSOLE},pl011 pl011_0 pl011_1 dcc dtb none)) else $(error Please define VERSAL_NET_CONSOLE) endif @@ -69,6 +71,20 @@ ifdef XILINX_OF_BOARD_DTB_ADDR $(eval $(call add_define,XILINX_OF_BOARD_DTB_ADDR)) endif +# Runtime console in default console in DEBUG build +ifeq ($(DEBUG), 1) +CONSOLE_RUNTIME ?= pl011 +endif + +# Runtime console +ifdef CONSOLE_RUNTIME +ifeq (${CONSOLE_RUNTIME}, $(filter ${CONSOLE_RUNTIME},pl011 pl011_0 pl011_1 dcc dtb)) +$(eval $(call add_define_val,CONSOLE_RUNTIME,RT_CONSOLE_ID_${CONSOLE_RUNTIME})) +else +$(error "Please define CONSOLE_RUNTIME") +endif +endif + # enable assert() for release/debug builds ENABLE_ASSERTIONS := 1 @@ -93,7 +109,9 @@ PLAT_BL_COMMON_SOURCES := \ plat/arm/common/arm_common.c \ plat/common/plat_gicv3.c \ ${PLAT_PATH}/aarch64/versal_net_helpers.S \ - ${PLAT_PATH}/aarch64/versal_net_common.c + ${PLAT_PATH}/aarch64/versal_net_common.c \ + ${PLAT_PATH}/plat_topology.c \ + ${XLAT_TABLES_LIB_SRCS} BL31_SOURCES += drivers/arm/cci/cci.c \ lib/cpus/aarch64/cortex_a78_ae.S \ @@ -112,14 +130,14 @@ endif BL31_SOURCES += plat/xilinx/common/plat_fdt.c \ plat/xilinx/common/plat_startup.c \ plat/xilinx/common/plat_console.c \ + plat/xilinx/common/plat_clkfunc.c \ plat/xilinx/common/ipi.c \ plat/xilinx/common/ipi_mailbox_service/ipi_mailbox_svc.c \ plat/xilinx/common/versal.c \ ${PLAT_PATH}/bl31_versal_net_setup.c \ - ${PLAT_PATH}/plat_topology.c \ common/fdt_fixup.c \ common/fdt_wrappers.c \ + plat/arm/common/arm_gicv3.c \ ${LIBFDT_SRCS} \ ${PLAT_PATH}/sip_svc_setup.c \ - ${PLAT_PATH}/versal_net_gicv3.c \ ${XLAT_TABLES_LIB_SRCS} diff --git a/plat/xilinx/versal_net/sip_svc_setup.c b/plat/xilinx/versal_net/sip_svc_setup.c index 0c27dec5..bf06e2c6 100644 --- a/plat/xilinx/versal_net/sip_svc_setup.c +++ b/plat/xilinx/versal_net/sip_svc_setup.c @@ -1,7 +1,7 @@ /* * Copyright (c) 2018-2019, Arm Limited and Contributors. All rights reserved. * Copyright (c) 2018-2022, Xilinx, Inc. All rights reserved. - * Copyright (c) 2022, Advanced Micro Devices, Inc. All rights reserved. + * Copyright (c) 2022-2024, Advanced Micro Devices, Inc. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -20,13 +20,12 @@ #include "pm_svc_main.h" /* SMC function IDs for SiP Service queries */ -#define VERSAL_NET_SIP_SVC_CALL_COUNT (0x8200ff00U) #define VERSAL_NET_SIP_SVC_UID (0x8200ff01U) #define VERSAL_NET_SIP_SVC_VERSION (0x8200ff03U) /* SiP Service Calls version numbers */ #define SIP_SVC_VERSION_MAJOR (0U) -#define SIP_SVC_VERSION_MINOR (1U) +#define SIP_SVC_VERSION_MINOR (2U) /* These macros are used to identify PM calls from the SMC function ID */ #define SIP_FID_MASK GENMASK(23, 16) @@ -70,7 +69,7 @@ static uintptr_t sip_svc_smc_handler(uint32_t smc_fid, VERBOSE("SMCID: 0x%08x, x1: 0x%016" PRIx64 ", x2: 0x%016" PRIx64 ", x3: 0x%016" PRIx64 ", x4: 0x%016" PRIx64 "\n", smc_fid, x1, x2, x3, x4); - if (smc_fid & SIP_FID_MASK) { + if ((smc_fid & SIP_FID_MASK) != 0U) { WARN("SMC out of SiP assinged range: 0x%x\n", smc_fid); SMC_RET1(handle, SMC_UNK); } @@ -88,10 +87,6 @@ static uintptr_t sip_svc_smc_handler(uint32_t smc_fid, /* Let PM SMC handler deal with PM-related requests */ switch (smc_fid) { - case VERSAL_NET_SIP_SVC_CALL_COUNT: - /* PM functions + default functions */ - SMC_RET1(handle, 2); - case VERSAL_NET_SIP_SVC_UID: SMC_UUID_RET(handle, versal_net_sip_uuid); diff --git a/plat/xilinx/versal_net/tsp/tsp-versal_net.mk b/plat/xilinx/versal_net/tsp/tsp-versal_net.mk index 87638ab4..ab7871c0 100644 --- a/plat/xilinx/versal_net/tsp/tsp-versal_net.mk +++ b/plat/xilinx/versal_net/tsp/tsp-versal_net.mk @@ -8,6 +8,3 @@ PLAT_XILINX_COMMON := plat/xilinx/common/ include ${PLAT_XILINX_COMMON}/tsp/tsp.mk - -BL32_SOURCES += plat/xilinx/versal_net/plat_topology.c \ - ${XLAT_TABLES_LIB_SRCS} diff --git a/plat/xilinx/versal_net/versal_net_ipi.c b/plat/xilinx/versal_net/versal_net_ipi.c index e8d8fb76..7c38921c 100644 --- a/plat/xilinx/versal_net/versal_net_ipi.c +++ b/plat/xilinx/versal_net/versal_net_ipi.c @@ -63,6 +63,62 @@ static const struct ipi_config versal_net_ipi_table[IPI_ID_MAX] = { .ipi_reg_base = IPI5_REG_BASE, .secure_only = 0, }, + + /* PMC_NOBUF IPI */ + [IPI_ID_PMC_NOBUF] = { + .ipi_bit_mask = PMC_NOBUF_TRIG_BIT, + .ipi_reg_base = PMC_NOBUF_REG_BASE, + .secure_only = IPI_SECURE_MASK, + }, + + /* IPI6 IPI */ + [IPI_ID_6_NOBUF_95] = { + .ipi_bit_mask = IPI6_NOBUF_95_TRIG_BIT, + .ipi_reg_base = IPI6_NOBUF_95_REG_BASE, + .secure_only = 0, + }, + + /* IPI1 NO BUF IPI */ + [IPI_ID_1_NOBUF] = { + .ipi_bit_mask = IPI1_NOBUF_TRIG_BIT, + .ipi_reg_base = IPI1_NOBUF_REG_BASE, + .secure_only = 0, + }, + + /* IPI2 NO BUF IPI */ + [IPI_ID_2_NOBUF] = { + .ipi_bit_mask = IPI2_NOBUF_TRIG_BIT, + .ipi_reg_base = IPI2_NOBUF_REG_BASE, + .secure_only = 0, + }, + + /* IPI3 NO BUF IPI */ + [IPI_ID_3_NOBUF] = { + .ipi_bit_mask = IPI3_NOBUF_TRIG_BIT, + .ipi_reg_base = IPI3_NOBUF_REG_BASE, + .secure_only = 0, + }, + + /* IPI4 NO BUF IPI */ + [IPI_ID_4_NOBUF] = { + .ipi_bit_mask = IPI4_NOBUF_TRIG_BIT, + .ipi_reg_base = IPI4_NOBUF_REG_BASE, + .secure_only = 0, + }, + + /* IPI5 NO BUF IPI */ + [IPI_ID_5_NOBUF] = { + .ipi_bit_mask = IPI5_NOBUF_TRIG_BIT, + .ipi_reg_base = IPI5_NOBUF_REG_BASE, + .secure_only = 0, + }, + + /* IPI6 NO BUF IPI */ + [IPI_ID_6_NOBUF_101] = { + .ipi_bit_mask = IPI6_NOBUF_101_TRIG_BIT, + .ipi_reg_base = IPI6_NOBUF_101_REG_BASE, + .secure_only = 0, + }, }; /* versal_net_ipi_config_table_init() - Initialize versal_net IPI configuration diff --git a/plat/xilinx/zynqmp/aarch64/zynqmp_common.c b/plat/xilinx/zynqmp/aarch64/zynqmp_common.c index dba1734c..0e698f73 100644 --- a/plat/xilinx/zynqmp/aarch64/zynqmp_common.c +++ b/plat/xilinx/zynqmp/aarch64/zynqmp_common.c @@ -1,6 +1,6 @@ /* * Copyright (c) 2013-2022, Arm Limited and Contributors. All rights reserved. - * Copyright (c) 2022-2023, Advanced Micro Devices, Inc. All rights reserved. + * Copyright (c) 2022-2024, Advanced Micro Devices, Inc. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -12,8 +12,9 @@ #include <drivers/generic_delay_timer.h> #include <lib/mmio.h> #include <lib/smccc.h> -#include <lib/xlat_tables/xlat_tables.h> +#include <lib/xlat_tables/xlat_tables_v2.h> #include <plat/common/platform.h> +#include <plat_arm.h> #include <services/arm_arch_svc.h> #include <plat_ipi.h> @@ -28,9 +29,9 @@ * configure_mmu_elx() will give the available subset of that, */ const mmap_region_t plat_zynqmp_mmap[] = { - { DEVICE0_BASE, DEVICE0_BASE, DEVICE0_SIZE, MT_DEVICE | MT_RW | MT_SECURE }, - { DEVICE1_BASE, DEVICE1_BASE, DEVICE1_SIZE, MT_DEVICE | MT_RW | MT_SECURE }, - { CRF_APB_BASE, CRF_APB_BASE, CRF_APB_SIZE, MT_DEVICE | MT_RW | MT_SECURE }, + MAP_REGION_FLAT(DEVICE0_BASE, DEVICE0_SIZE, MT_DEVICE | MT_RW | MT_SECURE), + MAP_REGION_FLAT(DEVICE1_BASE, DEVICE1_SIZE, MT_DEVICE | MT_RW | MT_SECURE), + MAP_REGION_FLAT(CRF_APB_BASE, CRF_APB_SIZE, MT_DEVICE | MT_RW | MT_SECURE), {0} }; @@ -244,8 +245,8 @@ static char *zynqmp_get_silicon_idcode_name(void) ver = chipid[1] >> ZYNQMP_EFUSE_IPDISABLE_SHIFT; for (i = 0; i < ARRAY_SIZE(zynqmp_devices); i++) { - if (zynqmp_devices[i].id == id && - zynqmp_devices[i].ver == (ver & ZYNQMP_CSU_VERSION_MASK)) { + if ((zynqmp_devices[i].id == id) && + (zynqmp_devices[i].ver == (ver & ZYNQMP_CSU_VERSION_MASK))) { break; } } @@ -299,8 +300,8 @@ static char *zynqmp_print_silicon_idcode(void) tmp = id; tmp &= ZYNQMP_CSU_IDCODE_XILINX_ID_MASK | ZYNQMP_CSU_IDCODE_FAMILY_MASK; - maskid = ZYNQMP_CSU_IDCODE_XILINX_ID << ZYNQMP_CSU_IDCODE_XILINX_ID_SHIFT | - ZYNQMP_CSU_IDCODE_FAMILY << ZYNQMP_CSU_IDCODE_FAMILY_SHIFT; + maskid = (ZYNQMP_CSU_IDCODE_XILINX_ID << ZYNQMP_CSU_IDCODE_XILINX_ID_SHIFT) | + (ZYNQMP_CSU_IDCODE_FAMILY << ZYNQMP_CSU_IDCODE_FAMILY_SHIFT); if (tmp != maskid) { ERROR("Incorrect IDCODE 0x%x, maskid 0x%x\n", id, maskid); return "UNKN"; @@ -348,7 +349,7 @@ static void zynqmp_print_platform_name(void) { uint32_t ver = zynqmp_get_silicon_ver(); uint32_t rtl = zynqmp_get_rtl_ver(); - char *label = "Unknown"; + const char *label = "Unknown"; switch (ver) { case ZYNQMP_CSU_VERSION_QEMU: diff --git a/plat/xilinx/zynqmp/bl31_zynqmp_setup.c b/plat/xilinx/zynqmp/bl31_zynqmp_setup.c index baf67170..ede3a21f 100644 --- a/plat/xilinx/zynqmp/bl31_zynqmp_setup.c +++ b/plat/xilinx/zynqmp/bl31_zynqmp_setup.c @@ -1,6 +1,6 @@ /* - * Copyright (c) 2013-2021, Arm Limited and Contributors. All rights reserved. - * Copyright (c) 2023, Advanced Micro Devices, Inc. All rights reserved. + * Copyright (c) 2013-2024, Arm Limited and Contributors. All rights reserved. + * Copyright (c) 2023-2024, Advanced Micro Devices, Inc. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -14,6 +14,7 @@ #include <common/fdt_fixup.h> #include <common/fdt_wrappers.h> #include <lib/mmio.h> +#include <lib/xlat_tables/xlat_tables_v2.h> #include <libfdt.h> #include <plat/arm/common/plat_arm.h> #include <plat/common/platform.h> @@ -71,6 +72,10 @@ static inline void bl31_set_default_config(void) void bl31_early_platform_setup2(u_register_t arg0, u_register_t arg1, u_register_t arg2, u_register_t arg3) { + (void)arg0; + (void)arg1; + (void)arg2; + (void)arg3; uint64_t tfa_handoff_addr; setup_console(); @@ -190,8 +195,6 @@ void bl31_plat_runtime_setup(void) #endif custom_runtime_setup(); - - console_switch_state(CONSOLE_FLAG_RUNTIME); } /* @@ -222,5 +225,5 @@ void bl31_plat_arch_setup(void) custom_mmap_add(); setup_page_tables(bl_regions, plat_get_mmap()); - enable_mmu_el3(0); + enable_mmu(0); } diff --git a/plat/xilinx/zynqmp/custom_sip_svc.c b/plat/xilinx/zynqmp/custom_sip_svc.c index b9664afa..c39e4bef 100644 --- a/plat/xilinx/zynqmp/custom_sip_svc.c +++ b/plat/xilinx/zynqmp/custom_sip_svc.c @@ -8,10 +8,18 @@ #include <common/debug.h> #include <smccc_helpers.h> +#include <custom_svc.h> + uint64_t custom_smc_handler(uint32_t smc_fid, uint64_t x1, uint64_t x2, uint64_t x3, uint64_t x4, void *cookie, void *handle, uint64_t flags) { + (void)x1; + (void)x2; + (void)x3; + (void)x4; + (void)cookie; + (void)flags; WARN("Unimplemented SiP Service Call: 0x%x\n", smc_fid); SMC_RET1(handle, SMC_UNK); } diff --git a/plat/xilinx/zynqmp/include/plat_private.h b/plat/xilinx/zynqmp/include/plat_private.h index afa102d8..1b41b7cd 100644 --- a/plat/xilinx/zynqmp/include/plat_private.h +++ b/plat/xilinx/zynqmp/include/plat_private.h @@ -1,6 +1,6 @@ /* * Copyright (c) 2014-2020, Arm Limited and Contributors. All rights reserved. - * Copyright (c) 2023, Advanced Micro Devices, Inc. All rights reserved. + * Copyright (c) 2023-2024, Advanced Micro Devices, Inc. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -13,7 +13,7 @@ #include <bl31/interrupt_mgmt.h> #include <common/bl_common.h> #include <drivers/cadence/cdns_uart.h> -#include <lib/xlat_tables/xlat_tables.h> +#include <lib/xlat_tables/xlat_tables_v2.h> void zynqmp_config_setup(void); diff --git a/plat/xilinx/zynqmp/include/zynqmp_def.h b/plat/xilinx/zynqmp/include/zynqmp_def.h index d715ce2f..68485cf4 100644 --- a/plat/xilinx/zynqmp/include/zynqmp_def.h +++ b/plat/xilinx/zynqmp/include/zynqmp_def.h @@ -10,13 +10,24 @@ #include <plat/arm/common/smccc_def.h> #include <plat/common/common_def.h> +#define ZYNQMP_CONSOLE_ID_none 0 #define ZYNQMP_CONSOLE_ID_cadence 1 #define ZYNQMP_CONSOLE_ID_cadence0 1 #define ZYNQMP_CONSOLE_ID_cadence1 2 #define ZYNQMP_CONSOLE_ID_dcc 3 +#define ZYNQMP_CONSOLE_ID_dtb 4 #define CONSOLE_IS(con) (ZYNQMP_CONSOLE_ID_ ## con == ZYNQMP_CONSOLE) +/* Runtime console */ +#define RT_CONSOLE_ID_cadence 1 +#define RT_CONSOLE_ID_cadence0 1 +#define RT_CONSOLE_ID_cadence1 2 +#define RT_CONSOLE_ID_dcc 3 +#define RT_CONSOLE_ID_dtb 4 + +#define RT_CONSOLE_IS(con) (RT_CONSOLE_ID_ ## con == CONSOLE_RUNTIME) + /* Default counter frequency */ #define ZYNQMP_DEFAULT_COUNTER_FREQ 0U @@ -144,14 +155,38 @@ #define ZYNQMP_UART0_BASE U(0xFF000000) #define ZYNQMP_UART1_BASE U(0xFF010000) -#if CONSOLE_IS(cadence) || CONSOLE_IS(dcc) +/* Boot console */ +#if CONSOLE_IS(cadence) || CONSOLE_IS(dtb) # define UART_BASE ZYNQMP_UART0_BASE +# define UART_TYPE CONSOLE_CDNS #elif CONSOLE_IS(cadence1) # define UART_BASE ZYNQMP_UART1_BASE +# define UART_TYPE CONSOLE_CDNS +#elif CONSOLE_IS(dcc) +# define UART_BASE 0x0 +# define UART_TYPE CONSOLE_DCC +#elif CONSOLE_IS(none) +# define UART_TYPE CONSOLE_NONE #else # error "invalid ZYNQMP_CONSOLE" #endif +/* Runtime console */ +#if defined(CONSOLE_RUNTIME) +#if RT_CONSOLE_IS(cadence) || RT_CONSOLE_IS(dtb) +# define RT_UART_BASE ZYNQMP_UART0_BASE +# define RT_UART_TYPE CONSOLE_CDNS +#elif RT_CONSOLE_IS(cadence1) +# define RT_UART_BASE ZYNQMP_UART1_BASE +# define RT_UART_TYPE CONSOLE_CDNS +#elif RT_CONSOLE_IS(dcc) +# define RT_UART_BASE 0x0 +# define RT_UART_TYPE CONSOLE_DCC +#else +# error "invalid CONSOLE_RUNTIME" +#endif +#endif + /* Must be non zero */ #define UART_BAUDRATE 115200 diff --git a/plat/xilinx/zynqmp/plat_psci.c b/plat/xilinx/zynqmp/plat_psci.c index c6c6c4ba..58db2e4c 100644 --- a/plat/xilinx/zynqmp/plat_psci.c +++ b/plat/xilinx/zynqmp/plat_psci.c @@ -1,6 +1,6 @@ /* * Copyright (c) 2013-2022, Arm Limited and Contributors. All rights reserved. - * Copyright (c) 2022-2023, Advanced Micro Devices, Inc. All rights reserved. + * Copyright (c) 2022-2024, Advanced Micro Devices, Inc. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -42,7 +42,11 @@ static int32_t zynqmp_pwr_domain_on(u_register_t mpidr) if (cpu_id == -1) { return PSCI_E_INTERN_FAIL; } + proc = pm_get_proc(cpu_id); + if (proc == NULL) { + return PSCI_E_INTERN_FAIL; + } /* Check the APU proc status before wakeup */ ret = pm_get_node_status(proc->node_id, buff); @@ -54,7 +58,7 @@ static int32_t zynqmp_pwr_domain_on(u_register_t mpidr) pm_client_wakeup(proc); /* Send request to PMU to wake up selected APU CPU core */ - pm_req_wakeup(proc->node_id, 1, zynqmp_sec_entry, REQ_ACK_BLOCKING); + (void)pm_req_wakeup(proc->node_id, 1, zynqmp_sec_entry, REQ_ACK_BLOCKING); return PSCI_E_SUCCESS; } @@ -64,6 +68,10 @@ static void zynqmp_pwr_domain_off(const psci_power_state_t *target_state) uint32_t cpu_id = plat_my_core_pos(); const struct pm_proc *proc = pm_get_proc(cpu_id); + if (proc == NULL) { + return; + } + for (size_t i = 0; i <= PLAT_MAX_PWR_LVL; i++) { VERBOSE("%s: target_state->pwr_domain_state[%lu]=%x\n", __func__, i, target_state->pwr_domain_state[i]); @@ -80,7 +88,7 @@ static void zynqmp_pwr_domain_off(const psci_power_state_t *target_state) * invoking CPU_on function, during which resume address will * be set. */ - pm_self_suspend(proc->node_id, MAX_LATENCY, PM_STATE_CPU_IDLE, 0); + (void)pm_self_suspend(proc->node_id, MAX_LATENCY, PM_STATE_CPU_IDLE, 0); } static void zynqmp_pwr_domain_suspend(const psci_power_state_t *target_state) @@ -89,15 +97,19 @@ static void zynqmp_pwr_domain_suspend(const psci_power_state_t *target_state) uint32_t cpu_id = plat_my_core_pos(); const struct pm_proc *proc = pm_get_proc(cpu_id); + if (proc == NULL) { + return; + } + for (size_t i = 0; i <= PLAT_MAX_PWR_LVL; i++) VERBOSE("%s: target_state->pwr_domain_state[%lu]=%x\n", __func__, i, target_state->pwr_domain_state[i]); - state = target_state->pwr_domain_state[1] > PLAT_MAX_RET_STATE ? + state = (target_state->pwr_domain_state[1] > PLAT_MAX_RET_STATE) ? PM_STATE_SUSPEND_TO_RAM : PM_STATE_CPU_IDLE; /* Send request to PMU to suspend this core */ - pm_self_suspend(proc->node_id, MAX_LATENCY, state, zynqmp_sec_entry); + (void)pm_self_suspend(proc->node_id, MAX_LATENCY, state, zynqmp_sec_entry); /* APU is to be turned off */ if (target_state->pwr_domain_state[1] > PLAT_MAX_RET_STATE) { @@ -121,6 +133,10 @@ static void zynqmp_pwr_domain_suspend_finish(const psci_power_state_t *target_st uint32_t cpu_id = plat_my_core_pos(); const struct pm_proc *proc = pm_get_proc(cpu_id); + if (proc == NULL) { + return; + } + for (size_t i = 0; i <= PLAT_MAX_PWR_LVL; i++) { VERBOSE("%s: target_state->pwr_domain_state[%lu]=%x\n", __func__, i, target_state->pwr_domain_state[i]); @@ -150,10 +166,10 @@ static void __dead2 zynqmp_system_off(void) plat_arm_interconnect_exit_coherency(); /* Send the power down request to the PMU */ - pm_system_shutdown(PMF_SHUTDOWN_TYPE_SHUTDOWN, + (void)pm_system_shutdown((uint32_t)PMF_SHUTDOWN_TYPE_SHUTDOWN, pm_get_shutdown_scope()); - while (1) { + while (true) { wfi(); } } @@ -164,10 +180,10 @@ static void __dead2 zynqmp_system_reset(void) plat_arm_interconnect_exit_coherency(); /* Send the system reset request to the PMU */ - pm_system_shutdown(PMF_SHUTDOWN_TYPE_RESET, + (void)pm_system_shutdown((uint32_t)PMF_SHUTDOWN_TYPE_RESET, pm_get_shutdown_scope()); - while (1) { + while (true) { wfi(); } } @@ -188,7 +204,7 @@ static int32_t zynqmp_validate_power_state(uint32_t power_state, req_state->pwr_domain_state[MPIDR_AFFLVL0] = PLAT_MAX_OFF_STATE; } /* We expect the 'state id' to be zero */ - if (psci_get_pstate_id(power_state)) { + if (psci_get_pstate_id(power_state) != 0U) { return PSCI_E_INVALID_PARAMS; } diff --git a/plat/xilinx/zynqmp/plat_topology.c b/plat/xilinx/zynqmp/plat_topology.c index 25966508..3755513c 100644 --- a/plat/xilinx/zynqmp/plat_topology.c +++ b/plat/xilinx/zynqmp/plat_topology.c @@ -5,6 +5,8 @@ */ #include <stdint.h> +#include <plat/common/platform.h> + static const uint8_t plat_power_domain_tree_desc[] = {1, 4}; const uint8_t *plat_get_power_domain_tree_desc(void) diff --git a/plat/xilinx/zynqmp/plat_zynqmp.c b/plat/xilinx/zynqmp/plat_zynqmp.c index e3a979ea..65faa2fc 100644 --- a/plat/xilinx/zynqmp/plat_zynqmp.c +++ b/plat/xilinx/zynqmp/plat_zynqmp.c @@ -10,7 +10,7 @@ int32_t plat_core_pos_by_mpidr(u_register_t mpidr) { - if (mpidr & MPIDR_CLUSTER_MASK) { + if ((mpidr & MPIDR_CLUSTER_MASK) != 0U) { return -1; } diff --git a/plat/xilinx/zynqmp/platform.mk b/plat/xilinx/zynqmp/platform.mk index e266615f..5a866582 100644 --- a/plat/xilinx/zynqmp/platform.mk +++ b/plat/xilinx/zynqmp/platform.mk @@ -2,7 +2,7 @@ # Copyright (c) 2013-2021, Arm Limited and Contributors. All rights reserved. # Portions copyright (c) 2021-2022, ProvenRun S.A.S. All rights reserved. # Copyright (c) 2018-2022, Xilinx, Inc. All rights reserved. -# Copyright (c) 2022-2023, Advanced Micro Devices, Inc. All rights reserved. +# Copyright (c) 2022-2024, Advanced Micro Devices, Inc. All rights reserved. # # SPDX-License-Identifier: BSD-3-Clause @@ -16,11 +16,12 @@ ZYNQMP_WDT_RESTART := 0 IPI_CRC_CHECK := 0 override RESET_TO_BL31 := 1 override WARMBOOT_ENABLE_DCACHE_EARLY := 1 +ENABLE_LTO := 1 EL3_EXCEPTION_HANDLING := $(SDEI_SUPPORT) # pncd SPD requires secure SGI to be handled at EL1 -ifeq (${SPD}, $(filter ${SPD},pncd tspd)) +ifeq (${SPD}, $(filter ${SPD},pncd tspd opteed)) ifeq (${ZYNQMP_WDT_RESTART},1) $(error "Error: ZYNQMP_WDT_RESTART and SPD=pncd are incompatible") endif @@ -34,15 +35,11 @@ ENABLE_SVE_FOR_NS := 0 WORKAROUND_CVE_2017_5715 := 0 -ARM_XLAT_TABLES_LIB_V1 := 1 -$(eval $(call assert_boolean,ARM_XLAT_TABLES_LIB_V1)) -$(eval $(call add_define,ARM_XLAT_TABLES_LIB_V1)) - ifdef ZYNQMP_ATF_MEM_BASE $(eval $(call add_define,ZYNQMP_ATF_MEM_BASE)) ifndef ZYNQMP_ATF_MEM_SIZE - $(error "ZYNQMP_ATF_BASE defined without ZYNQMP_ATF_SIZE") + $(error "ZYNQMP_ATF_MEM_BASE defined without ZYNQMP_ATF_MEM_SIZE") endif $(eval $(call add_define,ZYNQMP_ATF_MEM_SIZE)) @@ -59,7 +56,7 @@ ifdef ZYNQMP_BL32_MEM_BASE $(eval $(call add_define,ZYNQMP_BL32_MEM_BASE)) ifndef ZYNQMP_BL32_MEM_SIZE - $(error "ZYNQMP_BL32_BASE defined without ZYNQMP_BL32_SIZE") + $(error "ZYNQMP_BL32_MEM_BASE defined without ZYNQMP_BL32_MEM_SIZE") endif $(eval $(call add_define,ZYNQMP_BL32_MEM_SIZE)) endif @@ -95,10 +92,9 @@ PLAT_INCLUDES := -Iinclude/plat/arm/common/ \ include lib/libfdt/libfdt.mk # Include GICv2 driver files include drivers/arm/gic/v2/gicv2.mk +include lib/xlat_tables_v2/xlat_tables.mk -PLAT_BL_COMMON_SOURCES := lib/xlat_tables/xlat_tables_common.c \ - lib/xlat_tables/aarch64/xlat_tables.c \ - drivers/arm/dcc/dcc_console.c \ +PLAT_BL_COMMON_SOURCES := drivers/arm/dcc/dcc_console.c \ drivers/delay_timer/delay_timer.c \ drivers/delay_timer/generic_delay_timer.c \ ${GICV2_SOURCES} \ @@ -111,15 +107,30 @@ PLAT_BL_COMMON_SOURCES := lib/xlat_tables/xlat_tables_common.c \ plat/xilinx/zynqmp/zynqmp_ipi.c \ plat/common/aarch64/crash_console_helpers.S \ plat/xilinx/zynqmp/aarch64/zynqmp_helpers.S \ - plat/xilinx/zynqmp/aarch64/zynqmp_common.c + plat/xilinx/zynqmp/aarch64/zynqmp_common.c \ + ${XLAT_TABLES_LIB_SRCS} ZYNQMP_CONSOLE ?= cadence -ifeq (${ZYNQMP_CONSOLE}, $(filter ${ZYNQMP_CONSOLE},cadence cadence0 cadence1 dcc)) +ifeq (${ZYNQMP_CONSOLE}, $(filter ${ZYNQMP_CONSOLE},cadence cadence0 cadence1 dcc dtb none)) else $(error "Please define ZYNQMP_CONSOLE") endif $(eval $(call add_define_val,ZYNQMP_CONSOLE,ZYNQMP_CONSOLE_ID_${ZYNQMP_CONSOLE})) +# Runtime console in default console in DEBUG build +ifeq ($(DEBUG), 1) +CONSOLE_RUNTIME ?= cadence +endif + +# Runtime console +ifdef CONSOLE_RUNTIME +ifeq (${CONSOLE_RUNTIME}, $(filter ${CONSOLE_RUNTIME},cadence cadence0 cadence1 dcc dtb)) +$(eval $(call add_define_val,CONSOLE_RUNTIME,RT_CONSOLE_ID_${CONSOLE_RUNTIME})) +else +$(error "Please define CONSOLE_RUNTIME") +endif +endif + # Build PM code as a Library include plat/xilinx/zynqmp/libpm.mk diff --git a/plat/xilinx/zynqmp/pm_service/pm_api_clock.c b/plat/xilinx/zynqmp/pm_service/pm_api_clock.c index 9682e590..91adb07b 100644 --- a/plat/xilinx/zynqmp/pm_service/pm_api_clock.c +++ b/plat/xilinx/zynqmp/pm_service/pm_api_clock.c @@ -1226,7 +1226,7 @@ static struct pm_clock clocks[] = { .control_reg = CRF_APB_ACPU_CTRL, .status_reg = 0, .parents = &((int32_t []) { - CLK_ACPU | PARENT_CLK_NODE2 << CLK_PARENTS_ID_LEN, + (CLK_ACPU | (PARENT_CLK_NODE2 << CLK_PARENTS_ID_LEN)), CLK_NA_PARENT }), .nodes = &acpu_full_nodes, @@ -2117,7 +2117,7 @@ static struct pm_clock clocks[] = { .control_reg = CRF_APB_ACPU_CTRL, .status_reg = 0, .parents = &((int32_t []) { - CLK_ACPU | PARENT_CLK_NODE2 << CLK_PARENTS_ID_LEN, + (CLK_ACPU | (PARENT_CLK_NODE2 << CLK_PARENTS_ID_LEN)), CLK_NA_PARENT }), .nodes = &acpu_half_nodes, @@ -2140,7 +2140,7 @@ static struct pm_clock clocks[] = { .control_reg = CRF_APB_GPU_REF_CTRL, .status_reg = 0, .parents = &((int32_t []) { - CLK_GPU_REF | PARENT_CLK_NODE2 << CLK_PARENTS_ID_LEN, + (CLK_GPU_REF | (PARENT_CLK_NODE2 << CLK_PARENTS_ID_LEN)), CLK_NA_PARENT }), .nodes = &gpu_pp0_nodes, @@ -2151,7 +2151,7 @@ static struct pm_clock clocks[] = { .control_reg = CRF_APB_GPU_REF_CTRL, .status_reg = 0, .parents = &((int32_t []) { - CLK_GPU_REF | PARENT_CLK_NODE2 << CLK_PARENTS_ID_LEN, + (CLK_GPU_REF | (PARENT_CLK_NODE2 << CLK_PARENTS_ID_LEN)), CLK_NA_PARENT }), .nodes = &gpu_pp1_nodes, @@ -2176,7 +2176,7 @@ static struct pm_clock clocks[] = { .control_reg = CRL_APB_CPU_R5_CTRL, .status_reg = 0, .parents = &((int32_t []) { - CLK_CPU_R5 | PARENT_CLK_NODE2 << CLK_PARENTS_ID_LEN, + (CLK_CPU_R5 | (PARENT_CLK_NODE2 << CLK_PARENTS_ID_LEN)), CLK_DUMMY_PARENT, CLK_NA_PARENT }), @@ -2456,14 +2456,14 @@ enum pm_ret_status pm_api_clock_get_num_clocks(uint32_t *nclocks) void pm_api_clock_get_name(uint32_t clock_id, char *name) { if (clock_id == CLK_MAX) { - memcpy(name, END_OF_CLK, sizeof(END_OF_CLK) > CLK_NAME_LEN ? - CLK_NAME_LEN : sizeof(END_OF_CLK)); + (void)memcpy(name, END_OF_CLK, ((sizeof(END_OF_CLK) > CLK_NAME_LEN) ? + CLK_NAME_LEN : sizeof(END_OF_CLK))); } else if ((clock_id > CLK_MAX) || (!pm_clock_valid(clock_id))) { - memset(name, 0, CLK_NAME_LEN); + (void)memset(name, 0, CLK_NAME_LEN); } else if (clock_id < CLK_MAX_OUTPUT_CLK) { - memcpy(name, clocks[clock_id].name, CLK_NAME_LEN); + (void)memcpy(name, clocks[clock_id].name, CLK_NAME_LEN); } else { - memcpy(name, ext_clocks[clock_id - CLK_MAX_OUTPUT_CLK].name, + (void)memcpy(name, ext_clocks[clock_id - CLK_MAX_OUTPUT_CLK].name, CLK_NAME_LEN); } } @@ -2486,7 +2486,7 @@ enum pm_ret_status pm_api_clock_get_topology(uint32_t clock_id, uint32_t index, uint32_t *topology) { - struct pm_clock_node *clock_nodes; + const struct pm_clock_node *clock_nodes; uint8_t num_nodes; uint32_t i; uint16_t typeflags; @@ -2499,7 +2499,7 @@ enum pm_ret_status pm_api_clock_get_topology(uint32_t clock_id, return PM_RET_ERROR_NOTSUPPORTED; } - memset(topology, 0, CLK_TOPOLOGY_PAYLOAD_LEN); + (void)memset(topology, 0, CLK_TOPOLOGY_PAYLOAD_LEN); clock_nodes = *clocks[clock_id].nodes; num_nodes = clocks[clock_id].num_nodes; @@ -2543,7 +2543,7 @@ enum pm_ret_status pm_api_clock_get_fixedfactor_params(uint32_t clock_id, uint32_t *mul, uint32_t *div) { - struct pm_clock_node *clock_nodes; + const struct pm_clock_node *clock_nodes; uint8_t num_nodes; uint32_t type, i; @@ -2598,7 +2598,7 @@ enum pm_ret_status pm_api_clock_get_parents(uint32_t clock_id, uint32_t *parents) { uint32_t i; - int32_t *clk_parents; + const int32_t *clk_parents; if (!pm_clock_valid(clock_id)) { return PM_RET_ERROR_ARGS; @@ -2613,7 +2613,7 @@ enum pm_ret_status pm_api_clock_get_parents(uint32_t clock_id, return PM_RET_ERROR_ARGS; } - memset(parents, 0, CLK_PARENTS_PAYLOAD_LEN); + (void)memset(parents, 0, CLK_PARENTS_PAYLOAD_LEN); /* Skip parent till index */ for (i = 0; i < index; i++) { @@ -2675,7 +2675,7 @@ enum pm_ret_status pm_api_clock_get_max_divisor(enum clock_id clock_id, uint32_t *max_div) { uint32_t i; - struct pm_clock_node *nodes; + const struct pm_clock_node *nodes; if (clock_id >= CLK_MAX_OUTPUT_CLK) { return PM_RET_ERROR_ARGS; @@ -2684,8 +2684,8 @@ enum pm_ret_status pm_api_clock_get_max_divisor(enum clock_id clock_id, nodes = *clocks[clock_id].nodes; for (i = 0; i < clocks[clock_id].num_nodes; i++) { if (nodes[i].type == div_type) { - if (CLK_DIVIDER_POWER_OF_TWO & - nodes[i].typeflags) { + if ((CLK_DIVIDER_POWER_OF_TWO & + nodes[i].typeflags) != 0U) { *max_div = (1U << (BIT(nodes[i].width) - 1U)); } else { *max_div = BIT(nodes[i].width) - 1U; @@ -2789,9 +2789,9 @@ struct pm_pll *pm_clock_get_pll(enum clock_id clock_id) enum pm_ret_status pm_clock_get_pll_node_id(enum clock_id clock_id, enum pm_node_id *node_id) { - struct pm_pll *pll = pm_clock_get_pll(clock_id); + const struct pm_pll *pll = pm_clock_get_pll(clock_id); - if (pll) { + if (pll != NULL) { *node_id = pll->nid; return PM_RET_SUCCESS; } @@ -2812,10 +2812,10 @@ struct pm_pll *pm_clock_get_pll_by_related_clk(enum clock_id clock_id) uint32_t i; for (i = 0; i < ARRAY_SIZE(pm_plls); i++) { - if (pm_plls[i].pre_src == clock_id || - pm_plls[i].post_src == clock_id || - pm_plls[i].div2 == clock_id || - pm_plls[i].bypass == clock_id) { + if ((pm_plls[i].pre_src == clock_id) || + (pm_plls[i].post_src == clock_id) || + (pm_plls[i].div2 == clock_id) || + (pm_plls[i].bypass == clock_id)) { return &pm_plls[i]; } } @@ -2883,7 +2883,7 @@ enum pm_ret_status pm_clock_pll_get_state(struct pm_pll *pll, enum pm_ret_status status; enum pm_pll_mode mode; - if ((pll == NULL) || !state) { + if ((pll == NULL) || (state == NULL)) { return PM_RET_ERROR_ARGS; } @@ -2990,7 +2990,7 @@ enum pm_ret_status pm_clock_set_pll_mode(enum clock_id clock_id, { struct pm_pll *pll = pm_clock_get_pll(clock_id); - if ((pll == NULL) || (mode != PLL_FRAC_MODE && mode != PLL_INT_MODE)) { + if ((pll == NULL) || ((mode != PLL_FRAC_MODE) && (mode != PLL_INT_MODE))) { return PM_RET_ERROR_ARGS; } pll->mode = mode; @@ -3011,9 +3011,9 @@ enum pm_ret_status pm_clock_set_pll_mode(enum clock_id clock_id, enum pm_ret_status pm_clock_get_pll_mode(enum clock_id clock_id, uint32_t *mode) { - struct pm_pll *pll = pm_clock_get_pll(clock_id); + const struct pm_pll *pll = pm_clock_get_pll(clock_id); - if ((pll == NULL) || !mode) { + if ((pll == NULL) || (mode == NULL)) { return PM_RET_ERROR_ARGS; } *mode = pll->mode; @@ -3052,7 +3052,7 @@ enum pm_ret_status pm_clock_id_is_valid(uint32_t clock_id) uint8_t pm_clock_has_div(uint32_t clock_id, enum pm_clock_div_id div_id) { uint32_t i; - struct pm_clock_node *nodes; + const struct pm_clock_node *nodes; if (clock_id >= CLK_MAX_OUTPUT_CLK) { return 0; diff --git a/plat/xilinx/zynqmp/pm_service/pm_api_ioctl.c b/plat/xilinx/zynqmp/pm_service/pm_api_ioctl.c index dd214996..0dbfa57d 100644 --- a/plat/xilinx/zynqmp/pm_service/pm_api_ioctl.c +++ b/plat/xilinx/zynqmp/pm_service/pm_api_ioctl.c @@ -62,7 +62,7 @@ static enum pm_ret_status pm_ioctl_set_rpu_oper_mode(uint32_t mode) { uint32_t val; - if (mmio_read_32(CRL_APB_RST_LPD_TOP) & CRL_APB_RPU_AMBA_RESET) { + if ((mmio_read_32(CRL_APB_RST_LPD_TOP) & CRL_APB_RPU_AMBA_RESET) != 0U) { return PM_RET_ERROR_ACCESS; } @@ -165,8 +165,8 @@ static enum pm_ret_status pm_ioctl_config_tcm_comb(uint32_t value) static enum pm_ret_status pm_ioctl_set_tapdelay_bypass(uint32_t type, uint32_t value) { - if ((value != PM_TAPDELAY_BYPASS_ENABLE && - value != PM_TAPDELAY_BYPASS_DISABLE) || type >= PM_TAPDELAY_MAX) { + if ((((value != PM_TAPDELAY_BYPASS_ENABLE) && + (value != PM_TAPDELAY_BYPASS_DISABLE)) || (type >= PM_TAPDELAY_MAX))) { return PM_RET_ERROR_ARGS; } @@ -481,7 +481,7 @@ static enum pm_ret_status pm_ioctl_afi(uint32_t index, uint32_t value) { uint32_t mask; - uint32_t regarr[] = {0xFD360000U, + const uint32_t regarr[] = {0xFD360000U, 0xFD360014U, 0xFD370000U, 0xFD370014U, @@ -682,7 +682,7 @@ enum pm_ret_status pm_api_ioctl(enum pm_node_id nid, */ enum pm_ret_status tfa_ioctl_bitmask(uint32_t *bit_mask) { - uint8_t supported_ids[] = { + const uint8_t supported_ids[] = { IOCTL_GET_RPU_OPER_MODE, IOCTL_SET_RPU_OPER_MODE, IOCTL_RPU_BOOT_ADDR_CONFIG, diff --git a/plat/xilinx/zynqmp/pm_service/pm_api_pinctrl.c b/plat/xilinx/zynqmp/pm_service/pm_api_pinctrl.c index 2d8c23b4..1477e25d 100644 --- a/plat/xilinx/zynqmp/pm_service/pm_api_pinctrl.c +++ b/plat/xilinx/zynqmp/pm_service/pm_api_pinctrl.c @@ -2012,9 +2012,9 @@ enum pm_ret_status pm_api_pinctrl_get_num_func_groups(uint32_t fid, void pm_api_pinctrl_get_function_name(uint32_t fid, char *name) { if (fid >= MAX_FUNCTION) { - memcpy(name, END_OF_FUNCTION, FUNCTION_NAME_LEN); + (void)memcpy(name, END_OF_FUNCTION, FUNCTION_NAME_LEN); } else { - memcpy(name, pinctrl_functions[fid].name, FUNCTION_NAME_LEN); + (void)memcpy(name, pinctrl_functions[fid].name, FUNCTION_NAME_LEN); } } @@ -2049,7 +2049,7 @@ enum pm_ret_status pm_api_pinctrl_get_function_groups(uint32_t fid, return PM_RET_ERROR_ARGS; } - memset(groups, END_OF_GROUPS, GROUPS_PAYLOAD_LEN); + (void)memset(groups, END_OF_GROUPS, GROUPS_PAYLOAD_LEN); grps = pinctrl_functions[fid].group_base; end_of_grp_offset = grps + pinctrl_functions[fid].group_size; @@ -2088,13 +2088,13 @@ enum pm_ret_status pm_api_pinctrl_get_pin_groups(uint32_t pin, uint16_t *groups) { uint32_t i; - uint16_t *grps; + const uint16_t *grps; if (pin >= MAX_PIN) { return PM_RET_ERROR_ARGS; } - memset(groups, END_OF_GROUPS, GROUPS_PAYLOAD_LEN); + (void)memset(groups, END_OF_GROUPS, GROUPS_PAYLOAD_LEN); grps = *zynqmp_pin_groups[pin].groups; if (grps == NULL) { diff --git a/plat/xilinx/zynqmp/pm_service/pm_client.c b/plat/xilinx/zynqmp/pm_service/pm_client.c index 4afa01d6..a5172575 100644 --- a/plat/xilinx/zynqmp/pm_service/pm_client.c +++ b/plat/xilinx/zynqmp/pm_service/pm_client.c @@ -1,6 +1,6 @@ /* * Copyright (c) 2013-2018, Arm Limited and Contributors. All rights reserved. - * Copyright (c) 2022-2023, Advanced Micro Devices, Inc. All rights reserved. + * Copyright (c) 2022-2024, Advanced Micro Devices, Inc. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -204,7 +204,7 @@ static void pm_client_set_wakeup_sources(void) continue; } - while (reg) { + while (reg != 0U) { enum pm_node_id node; uint32_t idx, ret, irq, lowest_set = reg & (-reg); @@ -218,7 +218,7 @@ static void pm_client_set_wakeup_sources(void) node = irq_to_pm_node(irq); reg &= ~lowest_set; - if (node > NODE_UNKNOWN && node < NODE_MAX) { + if ((node > NODE_UNKNOWN) && (node < NODE_MAX)) { if (pm_wakeup_nodes_set[node] == 0U) { ret = pm_set_wakeup_source(NODE_APU, node, 1U); pm_wakeup_nodes_set[node] = (ret == PM_RET_SUCCESS) ? 1U : 0U; @@ -244,23 +244,6 @@ const struct pm_proc *pm_get_proc(uint32_t cpuid) return NULL; } -/** - * pm_get_proc_by_node() - returns pointer to the proc structure. - * @nid: node id of the processor. - * - * Return: pointer to a proc structure if proc is found, otherwise NULL. - * - */ -const struct pm_proc *pm_get_proc_by_node(enum pm_node_id nid) -{ - for (size_t i = 0; i < ARRAY_SIZE(pm_procs_all); i++) { - if (nid == pm_procs_all[i].node_id) { - return &pm_procs_all[i]; - } - } - return NULL; -} - /** * pm_get_cpuid() - get the local cpu ID for a global node ID. * @nid: node id of the processor. diff --git a/plat/xilinx/zynqmp/pm_service/zynqmp_pm_api_sys.c b/plat/xilinx/zynqmp/pm_service/zynqmp_pm_api_sys.c index 6b42055d..719ab6f8 100644 --- a/plat/xilinx/zynqmp/pm_service/zynqmp_pm_api_sys.c +++ b/plat/xilinx/zynqmp/pm_service/zynqmp_pm_api_sys.c @@ -1,6 +1,6 @@ /* * Copyright (c) 2013-2022, Arm Limited and Contributors. All rights reserved. - * Copyright (c) 2022-2023, Advanced Micro Devices, Inc. All rights reserved. + * Copyright (c) 2022-2024, Advanced Micro Devices, Inc. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -268,10 +268,16 @@ enum pm_ret_status pm_self_suspend(enum pm_node_id nid, uint32_t state, uintptr_t address) { + (void)nid; uint32_t payload[PAYLOAD_ARG_CNT]; uint32_t cpuid = plat_my_core_pos(); const struct pm_proc *proc = pm_get_proc(cpuid); + if (proc == NULL) { + WARN("Failed to get proc %d\n", cpuid); + return PM_RET_ERROR_INTERNAL; + } + /* * Do client specific suspend operations * (e.g. set powerdown request bit) @@ -763,7 +769,7 @@ static enum pm_ret_status fw_api_version(uint32_t id, uint32_t *version, enum pm_ret_status check_api_dependency(uint8_t id) { uint8_t i; - uint32_t version; + uint32_t version_type; int ret; for (i = 0U; i < ARRAY_SIZE(api_dep_table); i++) { @@ -773,13 +779,13 @@ enum pm_ret_status check_api_dependency(uint8_t id) } ret = fw_api_version(api_dep_table[i].api_id, - &version, 1); + &version_type, 1); if (ret != PM_RET_SUCCESS) { return ret; } /* Check if fw version matches TF-A expected version */ - if (version != tfa_expected_ver_id[api_dep_table[i].api_id]) { + if (version_type != tfa_expected_ver_id[api_dep_table[i].api_id]) { return PM_RET_ERROR_NOTSUPPORTED; } } @@ -914,7 +920,7 @@ static enum pm_ret_status feature_check_partial(uint32_t api_id, enum pm_ret_status pm_feature_check(uint32_t api_id, uint32_t *version, uint32_t *bit_mask, uint8_t len) { - uint32_t ret_payload[PAYLOAD_ARG_CNT] = {0U}; + uint32_t ret_payload[RET_PAYLOAD_ARG_CNT] = {0U}; uint32_t status; /* Get API version implemented in TF-A */ @@ -1109,7 +1115,7 @@ static enum pm_ret_status pm_clock_gate(uint32_t clock_id, return status; } - if (enable) { + if (enable != 0U) { api_id = PM_CLOCK_ENABLE; } else { api_id = PM_CLOCK_DISABLE; @@ -1144,7 +1150,7 @@ enum pm_ret_status pm_clock_enable(uint32_t clock_id) /* First try to handle it as a PLL */ pll = pm_clock_get_pll(clock_id); - if (pll) { + if (pll != NULL) { return pm_clock_pll_enable(pll); } @@ -1169,7 +1175,7 @@ enum pm_ret_status pm_clock_disable(uint32_t clock_id) /* First try to handle it as a PLL */ pll = pm_clock_get_pll(clock_id); - if (pll) { + if (pll != NULL) { return pm_clock_pll_disable(pll); } @@ -1197,9 +1203,9 @@ enum pm_ret_status pm_clock_getstate(uint32_t clock_id, /* First try to handle it as a PLL */ pll = pm_clock_get_pll(clock_id); - if (pll) + if (pll != NULL) { return pm_clock_pll_get_state(pll, state); - + } /* Check if clock ID is a valid on-chip clock */ status = pm_clock_id_is_valid(clock_id); if (status != PM_RET_SUCCESS) { @@ -1291,7 +1297,7 @@ enum pm_ret_status pm_clock_getdivider(uint32_t clock_id, return status; } - if (pm_clock_has_div(clock_id, PM_CLOCK_DIV0_ID)) { + if ((pm_clock_has_div(clock_id, PM_CLOCK_DIV0_ID)) != 0U) { /* Send request to the PMU to get div0 */ PM_PACK_PAYLOAD3(payload, PM_CLOCK_GETDIVIDER, clock_id, PM_CLOCK_DIV0_ID); @@ -1302,7 +1308,7 @@ enum pm_ret_status pm_clock_getdivider(uint32_t clock_id, *divider = val; } - if (pm_clock_has_div(clock_id, PM_CLOCK_DIV1_ID)) { + if ((pm_clock_has_div(clock_id, PM_CLOCK_DIV1_ID)) != 0U) { /* Send request to the PMU to get div1 */ PM_PACK_PAYLOAD3(payload, PM_CLOCK_GETDIVIDER, clock_id, PM_CLOCK_DIV1_ID); @@ -1335,7 +1341,7 @@ enum pm_ret_status pm_clock_setparent(uint32_t clock_id, /* First try to handle it as a PLL */ pll = pm_clock_get_pll_by_related_clk(clock_id); - if (pll) { + if (pll != NULL) { return pm_clock_pll_set_parent(pll, clock_id, parent_index); } @@ -1370,7 +1376,7 @@ enum pm_ret_status pm_clock_getparent(uint32_t clock_id, /* First try to handle it as a PLL */ pll = pm_clock_get_pll_by_related_clk(clock_id); - if (pll) { + if (pll != NULL) { return pm_clock_pll_get_parent(pll, clock_id, parent_index); } @@ -1509,6 +1515,7 @@ static enum pm_ret_status pm_pinctrl_get_pin_groups(uint32_t pin_id, void pm_query_data(enum pm_query_ids qid, uint32_t arg1, uint32_t arg2, uint32_t arg3, uint32_t *data) { + (void)arg3; switch (qid) { case PM_QID_CLOCK_GET_NAME: pm_clock_get_name(arg1, (char *)data); @@ -1650,7 +1657,7 @@ enum pm_ret_status pm_pll_set_parameter(enum pm_node_id nid, uint32_t payload[PAYLOAD_ARG_CNT]; /* Check if given node ID is a PLL node */ - if (nid < NODE_APLL || nid > NODE_IOPLL) { + if ((nid < NODE_APLL) || (nid > NODE_IOPLL)) { return PM_RET_ERROR_ARGS; } @@ -1681,7 +1688,7 @@ enum pm_ret_status pm_pll_get_parameter(enum pm_node_id nid, uint32_t payload[PAYLOAD_ARG_CNT]; /* Check if given node ID is a PLL node */ - if (nid < NODE_APLL || nid > NODE_IOPLL) { + if ((nid < NODE_APLL) || (nid > NODE_IOPLL)) { return PM_RET_ERROR_ARGS; } @@ -1714,7 +1721,7 @@ enum pm_ret_status pm_pll_set_mode(enum pm_node_id nid, enum pm_pll_mode mode) uint32_t payload[PAYLOAD_ARG_CNT]; /* Check if given node ID is a PLL node */ - if (nid < NODE_APLL || nid > NODE_IOPLL) { + if ((nid < NODE_APLL) || (nid > NODE_IOPLL)) { return PM_RET_ERROR_ARGS; } @@ -1742,7 +1749,7 @@ enum pm_ret_status pm_pll_get_mode(enum pm_node_id nid, enum pm_pll_mode *mode) uint32_t payload[PAYLOAD_ARG_CNT]; /* Check if given node ID is a PLL node */ - if (nid < NODE_APLL || nid > NODE_IOPLL) { + if ((nid < NODE_APLL) || (nid > NODE_IOPLL)) { return PM_RET_ERROR_ARGS; } diff --git a/plat/xilinx/zynqmp/pm_service/zynqmp_pm_svc_main.c b/plat/xilinx/zynqmp/pm_service/zynqmp_pm_svc_main.c index 5a6a9f8c..738699ea 100644 --- a/plat/xilinx/zynqmp/pm_service/zynqmp_pm_svc_main.c +++ b/plat/xilinx/zynqmp/pm_service/zynqmp_pm_svc_main.c @@ -22,6 +22,7 @@ #include <plat_private.h> #include "pm_client.h" #include "pm_ipi.h" +#include "pm_svc_main.h" #include "zynqmp_pm_api_sys.h" #include "zynqmp_pm_defs.h" @@ -281,11 +282,14 @@ int32_t pm_setup(void) uint64_t pm_smc_handler(uint32_t smc_fid, uint64_t x1, uint64_t x2, uint64_t x3, uint64_t x4, const void *cookie, void *handle, uint64_t flags) { + (void)x4; + (void)cookie; + (void)flags; enum pm_ret_status ret; uint32_t payload[PAYLOAD_ARG_CNT]; uint32_t pm_arg[5]; - uint32_t result[PAYLOAD_ARG_CNT] = {0}; + uint32_t result[RET_PAYLOAD_ARG_CNT] = {0}; uint32_t api_id; /* Handle case where PM wasn't initialized properly */ @@ -371,7 +375,7 @@ uint64_t pm_smc_handler(uint32_t smc_fid, uint64_t x1, uint64_t x2, uint64_t x3, uint32_t value = 0U; ret = pm_fpga_get_status(&value); - SMC_RET1(handle, (uint64_t)ret | ((uint64_t)value) << 32); + SMC_RET1(handle, ((uint64_t)ret | (((uint64_t)value) << 32))); } case PM_SECURE_RSA_AES: @@ -386,15 +390,15 @@ uint64_t pm_smc_handler(uint32_t smc_fid, uint64_t x1, uint64_t x2, uint64_t x3, } SMC_RET2(handle, - (uint64_t)result[0] | ((uint64_t)result[1] << 32), - (uint64_t)result[2] | ((uint64_t)result[3] << 32)); + ((uint64_t)result[0] | ((uint64_t)result[1] << 32)), + ((uint64_t)result[2] | ((uint64_t)result[3] << 32))); case PM_IOCTL: { uint32_t value = 0U; ret = pm_ioctl(pm_arg[0], pm_arg[1], pm_arg[2], pm_arg[3], &value); - SMC_RET1(handle, (uint64_t)ret | ((uint64_t)value) << 32); + SMC_RET1(handle, ((uint64_t)ret | (((uint64_t)value) << 32))); } case PM_QUERY_DATA: @@ -403,8 +407,8 @@ uint64_t pm_smc_handler(uint32_t smc_fid, uint64_t x1, uint64_t x2, uint64_t x3, pm_query_data(pm_arg[0], pm_arg[1], pm_arg[2], pm_arg[3], data); - SMC_RET2(handle, (uint64_t)data[0] | ((uint64_t)data[1] << 32), - (uint64_t)data[2] | ((uint64_t)data[3] << 32)); + SMC_RET2(handle, ((uint64_t)data[0] | ((uint64_t)data[1] << 32)), + ((uint64_t)data[2] | ((uint64_t)data[3] << 32))); } case PM_CLOCK_ENABLE: @@ -420,7 +424,7 @@ uint64_t pm_smc_handler(uint32_t smc_fid, uint64_t x1, uint64_t x2, uint64_t x3, uint32_t value = 0U; ret = pm_clock_getstate(pm_arg[0], &value); - SMC_RET1(handle, (uint64_t)ret | ((uint64_t)value) << 32); + SMC_RET1(handle, ((uint64_t)ret | (((uint64_t)value) << 32))); } case PM_CLOCK_SETDIVIDER: @@ -432,7 +436,7 @@ uint64_t pm_smc_handler(uint32_t smc_fid, uint64_t x1, uint64_t x2, uint64_t x3, uint32_t value = 0U; ret = pm_clock_getdivider(pm_arg[0], &value); - SMC_RET1(handle, (uint64_t)ret | ((uint64_t)value) << 32); + SMC_RET1(handle, ((uint64_t)ret | (((uint64_t)value) << 32))); } case PM_CLOCK_SETPARENT: @@ -444,7 +448,7 @@ uint64_t pm_smc_handler(uint32_t smc_fid, uint64_t x1, uint64_t x2, uint64_t x3, uint32_t value = 0U; ret = pm_clock_getparent(pm_arg[0], &value); - SMC_RET1(handle, (uint64_t)ret | ((uint64_t)value) << 32U); + SMC_RET1(handle, ((uint64_t)ret | (((uint64_t)value) << 32U))); } case PM_GET_TRUSTZONE_VERSION: @@ -469,7 +473,7 @@ uint64_t pm_smc_handler(uint32_t smc_fid, uint64_t x1, uint64_t x2, uint64_t x3, { ret = pm_secure_image(pm_arg[0], pm_arg[1], pm_arg[2], pm_arg[3], &result[0]); - SMC_RET2(handle, (uint64_t)ret | ((uint64_t)result[0] << 32U), + SMC_RET2(handle, ((uint64_t)ret | ((uint64_t)result[0] << 32U)), result[1]); } @@ -479,7 +483,7 @@ uint64_t pm_smc_handler(uint32_t smc_fid, uint64_t x1, uint64_t x2, uint64_t x3, ret = pm_fpga_read(pm_arg[0], pm_arg[1], pm_arg[2], pm_arg[3], &value); - SMC_RET1(handle, (uint64_t)ret | ((uint64_t)value) << 32U); + SMC_RET1(handle, ((uint64_t)ret | (((uint64_t)value) << 32U))); } case PM_SECURE_AES: @@ -487,7 +491,7 @@ uint64_t pm_smc_handler(uint32_t smc_fid, uint64_t x1, uint64_t x2, uint64_t x3, uint32_t value = 0U; ret = pm_aes_engine(pm_arg[0], pm_arg[1], &value); - SMC_RET1(handle, (uint64_t)ret | ((uint64_t)value) << 32U); + SMC_RET1(handle, ((uint64_t)ret | (((uint64_t)value) << 32U))); } case PM_PLL_SET_PARAMETER: @@ -499,7 +503,7 @@ uint64_t pm_smc_handler(uint32_t smc_fid, uint64_t x1, uint64_t x2, uint64_t x3, uint32_t value = 0U; ret = pm_pll_get_parameter(pm_arg[0], pm_arg[1], &value); - SMC_RET1(handle, (uint64_t)ret | ((uint64_t)value << 32U)); + SMC_RET1(handle, ((uint64_t)ret | ((uint64_t)value << 32U))); } case PM_PLL_SET_MODE: @@ -511,7 +515,7 @@ uint64_t pm_smc_handler(uint32_t smc_fid, uint64_t x1, uint64_t x2, uint64_t x3, uint32_t mode = 0U; ret = pm_pll_get_mode(pm_arg[0], &mode); - SMC_RET1(handle, (uint64_t)ret | ((uint64_t)mode << 32U)); + SMC_RET1(handle, ((uint64_t)ret | ((uint64_t)mode << 32U))); } case PM_REGISTER_ACCESS: @@ -520,7 +524,7 @@ uint64_t pm_smc_handler(uint32_t smc_fid, uint64_t x1, uint64_t x2, uint64_t x3, ret = pm_register_access(pm_arg[0], pm_arg[1], pm_arg[2], pm_arg[3], &value); - SMC_RET1(handle, (uint64_t)ret | ((uint64_t)value) << 32U); + SMC_RET1(handle, ((uint64_t)ret | (((uint64_t)value) << 32U))); } case PM_EFUSE_ACCESS: @@ -535,7 +539,7 @@ uint64_t pm_smc_handler(uint32_t smc_fid, uint64_t x1, uint64_t x2, uint64_t x3, } #endif ret = pm_efuse_access(pm_arg[0], pm_arg[1], &value); - SMC_RET1(handle, (uint64_t)ret | ((uint64_t)value) << 32U); + SMC_RET1(handle, (uint64_t)ret | (((uint64_t)value) << 32U)); } case PM_FPGA_GET_VERSION: @@ -546,19 +550,19 @@ uint64_t pm_smc_handler(uint32_t smc_fid, uint64_t x1, uint64_t x2, uint64_t x3, PM_PACK_PAYLOAD5(payload, smc_fid & FUNCID_NUM_MASK, pm_arg[0], pm_arg[1], pm_arg[2], pm_arg[3]); ret = pm_ipi_send_sync(primary_proc, payload, ret_payload, 3U); - SMC_RET2(handle, (uint64_t)ret | (uint64_t)ret_payload[0] << 32U, - (uint64_t)ret_payload[1] | (uint64_t)ret_payload[2] << 32U); + SMC_RET2(handle, ((uint64_t)ret | ((uint64_t)ret_payload[0] << 32U)), + ((uint64_t)ret_payload[1] | ((uint64_t)ret_payload[2] << 32U))); } case PM_FEATURE_CHECK: { - uint32_t version = 0; + uint32_t version_type = 0; uint32_t bit_mask[2] = {0}; - ret = pm_feature_check(pm_arg[0], &version, bit_mask, + ret = pm_feature_check(pm_arg[0], &version_type, bit_mask, ARRAY_SIZE(bit_mask)); - SMC_RET2(handle, (uint64_t)ret | ((uint64_t)version << 32U), - (uint64_t)bit_mask[0] | ((uint64_t)bit_mask[1] << 32U)); + SMC_RET2(handle, ((uint64_t)ret | ((uint64_t)version_type << 32U)), + ((uint64_t)bit_mask[0] | ((uint64_t)bit_mask[1] << 32U))); } default: @@ -566,8 +570,8 @@ uint64_t pm_smc_handler(uint32_t smc_fid, uint64_t x1, uint64_t x2, uint64_t x3, PM_PACK_PAYLOAD6(payload, api_id, pm_arg[0], pm_arg[1], pm_arg[2], pm_arg[3], pm_arg[4]); ret = pm_ipi_send_sync(primary_proc, payload, result, - PAYLOAD_ARG_CNT); - SMC_RET2(handle, (uint64_t)ret | ((uint64_t)result[0] << 32U), - (uint64_t)result[1] | ((uint64_t)result[2] << 32U)); + RET_PAYLOAD_ARG_CNT); + SMC_RET2(handle, ((uint64_t)ret | ((uint64_t)result[0] << 32U)), + ((uint64_t)result[1] | ((uint64_t)result[2] << 32U))); } } diff --git a/plat/xilinx/zynqmp/sip_svc_setup.c b/plat/xilinx/zynqmp/sip_svc_setup.c index 6a8555ee..1baefb3c 100644 --- a/plat/xilinx/zynqmp/sip_svc_setup.c +++ b/plat/xilinx/zynqmp/sip_svc_setup.c @@ -18,7 +18,6 @@ #include "zynqmp_pm_svc_main.h" /* SMC function IDs for SiP Service queries */ -#define ZYNQMP_SIP_SVC_CALL_COUNT U(0x8200ff00) #define ZYNQMP_SIP_SVC_UID U(0x8200ff01) #define ZYNQMP_SIP_SVC_VERSION U(0x8200ff03) @@ -82,7 +81,7 @@ static uintptr_t sip_svc_smc_handler(uint32_t smc_fid, VERBOSE("SMCID: 0x%08x, x1: 0x%016" PRIx64 ", x2: 0x%016" PRIx64 ", x3: 0x%016" PRIx64 ", x4: 0x%016" PRIx64 "\n", smc_fid, x1, x2, x3, x4); - if (smc_fid & SIP_FID_MASK) { + if ((smc_fid & (uint32_t)SIP_FID_MASK) != 0U) { WARN("SMC out of SiP assinged range: 0x%x\n", smc_fid); SMC_RET1(handle, SMC_UNK); } @@ -100,10 +99,6 @@ static uintptr_t sip_svc_smc_handler(uint32_t smc_fid, } switch (smc_fid) { - case ZYNQMP_SIP_SVC_CALL_COUNT: - /* PM functions + default functions */ - SMC_RET1(handle, PM_API_MAX + 2); - case ZYNQMP_SIP_SVC_UID: SMC_UUID_RET(handle, zynqmp_sip_uuid); diff --git a/poetry.lock b/poetry.lock index 08b2b374..9a907044 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry 1.6.1 and should not be changed by hand. +# This file is automatically @generated by Poetry 1.8.2 and should not be changed by hand. [[package]] name = "alabaster" @@ -13,163 +13,201 @@ files = [ [[package]] name = "anytree" -version = "2.8.0" -description = "Powerful and Lightweight Python Tree Data Structure.." +version = "2.12.1" +description = "Powerful and Lightweight Python Tree Data Structure with various plugins" optional = false -python-versions = "*" +python-versions = ">=3.7.2,<4" files = [ - {file = "anytree-2.8.0-py2.py3-none-any.whl", hash = "sha256:14c55ac77492b11532395049a03b773d14c7e30b22aa012e337b1e983de31521"}, - {file = "anytree-2.8.0.tar.gz", hash = "sha256:3f0f93f355a91bc3e6245319bf4c1d50e3416cc7a35cc1133c1ff38306bbccab"}, + {file = "anytree-2.12.1-py3-none-any.whl", hash = "sha256:5ea9e61caf96db1e5b3d0a914378d2cd83c269dfce1fb8242ce96589fa3382f0"}, + {file = "anytree-2.12.1.tar.gz", hash = "sha256:244def434ccf31b668ed282954e5d315b4e066c4940b94aff4a7962d85947830"}, ] [package.dependencies] -six = ">=1.9.0" - -[package.extras] -dev = ["check-manifest"] -test = ["coverage"] +six = "*" [[package]] name = "babel" -version = "2.12.1" +version = "2.16.0" description = "Internationalization utilities" optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "Babel-2.12.1-py3-none-any.whl", hash = "sha256:b4246fb7677d3b98f501a39d43396d3cafdc8eadb045f4a31be01863f655c610"}, - {file = "Babel-2.12.1.tar.gz", hash = "sha256:cc2d99999cd01d44420ae725a21c9e3711b3aadc7976d6147f622d8581963455"}, + {file = "babel-2.16.0-py3-none-any.whl", hash = "sha256:368b5b98b37c06b7daf6696391c3240c938b37767d4584413e8438c5c435fa8b"}, + {file = "babel-2.16.0.tar.gz", hash = "sha256:d1f3554ca26605fe173f3de0c65f750f5a42f924499bf134de6423582298e316"}, ] [package.dependencies] pytz = {version = ">=2015.7", markers = "python_version < \"3.9\""} +[package.extras] +dev = ["freezegun (>=1.0,<2.0)", "pytest (>=6.0)", "pytest-cov"] + [[package]] name = "build" -version = "0.10.0" +version = "1.2.2" description = "A simple, correct Python build frontend" optional = false -python-versions = ">= 3.7" +python-versions = ">=3.8" files = [ - {file = "build-0.10.0-py3-none-any.whl", hash = "sha256:af266720050a66c893a6096a2f410989eeac74ff9a68ba194b3f6473e8e26171"}, - {file = "build-0.10.0.tar.gz", hash = "sha256:d5b71264afdb5951d6704482aac78de887c80691c52b88a9ad195983ca2c9269"}, + {file = "build-1.2.2-py3-none-any.whl", hash = "sha256:277ccc71619d98afdd841a0e96ac9fe1593b823af481d3b0cea748e8894e0613"}, + {file = "build-1.2.2.tar.gz", hash = "sha256:119b2fb462adef986483438377a13b2f42064a2a3a4161f24a0cca698a07ac8c"}, ] [package.dependencies] colorama = {version = "*", markers = "os_name == \"nt\""} -packaging = ">=19.0" +importlib-metadata = {version = ">=4.6", markers = "python_full_version < \"3.10.2\""} +packaging = ">=19.1" pyproject_hooks = "*" tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} [package.extras] -docs = ["furo (>=2021.08.31)", "sphinx (>=4.0,<5.0)", "sphinx-argparse-cli (>=1.5)", "sphinx-autodoc-typehints (>=1.10)"] -test = ["filelock (>=3)", "pytest (>=6.2.4)", "pytest-cov (>=2.12)", "pytest-mock (>=2)", "pytest-rerunfailures (>=9.1)", "pytest-xdist (>=1.34)", "setuptools (>=42.0.0)", "setuptools (>=56.0.0)", "toml (>=0.10.0)", "wheel (>=0.36.0)"] -typing = ["importlib-metadata (>=5.1)", "mypy (==0.991)", "tomli", "typing-extensions (>=3.7.4.3)"] +docs = ["furo (>=2023.08.17)", "sphinx (>=7.0,<8.0)", "sphinx-argparse-cli (>=1.5)", "sphinx-autodoc-typehints (>=1.10)", "sphinx-issues (>=3.0.0)"] +test = ["build[uv,virtualenv]", "filelock (>=3)", "pytest (>=6.2.4)", "pytest-cov (>=2.12)", "pytest-mock (>=2)", "pytest-rerunfailures (>=9.1)", "pytest-xdist (>=1.34)", "setuptools (>=42.0.0)", "setuptools (>=56.0.0)", "setuptools (>=56.0.0)", "setuptools (>=67.8.0)", "wheel (>=0.36.0)"] +typing = ["build[uv]", "importlib-metadata (>=5.1)", "mypy (>=1.9.0,<1.10.0)", "tomli", "typing-extensions (>=3.7.4.3)"] +uv = ["uv (>=0.1.18)"] virtualenv = ["virtualenv (>=20.0.35)"] +[[package]] +name = "cachetools" +version = "5.5.0" +description = "Extensible memoizing collections and decorators" +optional = false +python-versions = ">=3.7" +files = [ + {file = "cachetools-5.5.0-py3-none-any.whl", hash = "sha256:02134e8439cdc2ffb62023ce1debca2944c3f289d66bb17ead3ab3dede74b292"}, + {file = "cachetools-5.5.0.tar.gz", hash = "sha256:2cc24fb4cbe39633fb7badd9db9ca6295d766d9c2995f245725a46715d050f2a"}, +] + [[package]] name = "certifi" -version = "2023.7.22" +version = "2024.8.30" description = "Python package for providing Mozilla's CA Bundle." optional = false python-versions = ">=3.6" files = [ - {file = "certifi-2023.7.22-py3-none-any.whl", hash = "sha256:92d6037539857d8206b8f6ae472e8b77db8058fec5937a1ef3f54304089edbb9"}, - {file = "certifi-2023.7.22.tar.gz", hash = "sha256:539cc1d13202e33ca466e88b2807e29f4c13049d6d87031a3c110744495cb082"}, + {file = "certifi-2024.8.30-py3-none-any.whl", hash = "sha256:922820b53db7a7257ffbda3f597266d435245903d80737e34f8a45ff3e3230d8"}, + {file = "certifi-2024.8.30.tar.gz", hash = "sha256:bec941d2aa8195e248a60b31ff9f0558284cf01a52591ceda73ea9afffd69fd9"}, +] + +[[package]] +name = "chardet" +version = "5.2.0" +description = "Universal encoding detector for Python 3" +optional = false +python-versions = ">=3.7" +files = [ + {file = "chardet-5.2.0-py3-none-any.whl", hash = "sha256:e1cf59446890a00105fe7b7912492ea04b6e6f06d4b742b2c788469e34c82970"}, + {file = "chardet-5.2.0.tar.gz", hash = "sha256:1b3b6ff479a8c414bc3fa2c0852995695c4a026dcd6d0633b2dd092ca39c1cf7"}, ] [[package]] name = "charset-normalizer" -version = "3.1.0" +version = "3.3.2" description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." optional = false python-versions = ">=3.7.0" files = [ - {file = "charset-normalizer-3.1.0.tar.gz", hash = "sha256:34e0a2f9c370eb95597aae63bf85eb5e96826d81e3dcf88b8886012906f509b5"}, - {file = "charset_normalizer-3.1.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:e0ac8959c929593fee38da1c2b64ee9778733cdf03c482c9ff1d508b6b593b2b"}, - {file = "charset_normalizer-3.1.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d7fc3fca01da18fbabe4625d64bb612b533533ed10045a2ac3dd194bfa656b60"}, - {file = "charset_normalizer-3.1.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:04eefcee095f58eaabe6dc3cc2262f3bcd776d2c67005880894f447b3f2cb9c1"}, - {file = "charset_normalizer-3.1.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:20064ead0717cf9a73a6d1e779b23d149b53daf971169289ed2ed43a71e8d3b0"}, - {file = "charset_normalizer-3.1.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1435ae15108b1cb6fffbcea2af3d468683b7afed0169ad718451f8db5d1aff6f"}, - {file = "charset_normalizer-3.1.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c84132a54c750fda57729d1e2599bb598f5fa0344085dbde5003ba429a4798c0"}, - {file = "charset_normalizer-3.1.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:75f2568b4189dda1c567339b48cba4ac7384accb9c2a7ed655cd86b04055c795"}, - {file = "charset_normalizer-3.1.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:11d3bcb7be35e7b1bba2c23beedac81ee893ac9871d0ba79effc7fc01167db6c"}, - {file = "charset_normalizer-3.1.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:891cf9b48776b5c61c700b55a598621fdb7b1e301a550365571e9624f270c203"}, - {file = "charset_normalizer-3.1.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:5f008525e02908b20e04707a4f704cd286d94718f48bb33edddc7d7b584dddc1"}, - {file = "charset_normalizer-3.1.0-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:b06f0d3bf045158d2fb8837c5785fe9ff9b8c93358be64461a1089f5da983137"}, - {file = "charset_normalizer-3.1.0-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:49919f8400b5e49e961f320c735388ee686a62327e773fa5b3ce6721f7e785ce"}, - {file = "charset_normalizer-3.1.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:22908891a380d50738e1f978667536f6c6b526a2064156203d418f4856d6e86a"}, - {file = "charset_normalizer-3.1.0-cp310-cp310-win32.whl", hash = "sha256:12d1a39aa6b8c6f6248bb54550efcc1c38ce0d8096a146638fd4738e42284448"}, - {file = "charset_normalizer-3.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:65ed923f84a6844de5fd29726b888e58c62820e0769b76565480e1fdc3d062f8"}, - {file = "charset_normalizer-3.1.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:9a3267620866c9d17b959a84dd0bd2d45719b817245e49371ead79ed4f710d19"}, - {file = "charset_normalizer-3.1.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6734e606355834f13445b6adc38b53c0fd45f1a56a9ba06c2058f86893ae8017"}, - {file = "charset_normalizer-3.1.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f8303414c7b03f794347ad062c0516cee0e15f7a612abd0ce1e25caf6ceb47df"}, - {file = "charset_normalizer-3.1.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:aaf53a6cebad0eae578f062c7d462155eada9c172bd8c4d250b8c1d8eb7f916a"}, - {file = "charset_normalizer-3.1.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3dc5b6a8ecfdc5748a7e429782598e4f17ef378e3e272eeb1340ea57c9109f41"}, - {file = "charset_normalizer-3.1.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e1b25e3ad6c909f398df8921780d6a3d120d8c09466720226fc621605b6f92b1"}, - {file = "charset_normalizer-3.1.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0ca564606d2caafb0abe6d1b5311c2649e8071eb241b2d64e75a0d0065107e62"}, - {file = "charset_normalizer-3.1.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b82fab78e0b1329e183a65260581de4375f619167478dddab510c6c6fb04d9b6"}, - {file = "charset_normalizer-3.1.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:bd7163182133c0c7701b25e604cf1611c0d87712e56e88e7ee5d72deab3e76b5"}, - {file = "charset_normalizer-3.1.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:11d117e6c63e8f495412d37e7dc2e2fff09c34b2d09dbe2bee3c6229577818be"}, - {file = "charset_normalizer-3.1.0-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:cf6511efa4801b9b38dc5546d7547d5b5c6ef4b081c60b23e4d941d0eba9cbeb"}, - {file = "charset_normalizer-3.1.0-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:abc1185d79f47c0a7aaf7e2412a0eb2c03b724581139193d2d82b3ad8cbb00ac"}, - {file = "charset_normalizer-3.1.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:cb7b2ab0188829593b9de646545175547a70d9a6e2b63bf2cd87a0a391599324"}, - {file = "charset_normalizer-3.1.0-cp311-cp311-win32.whl", hash = "sha256:c36bcbc0d5174a80d6cccf43a0ecaca44e81d25be4b7f90f0ed7bcfbb5a00909"}, - {file = "charset_normalizer-3.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:cca4def576f47a09a943666b8f829606bcb17e2bc2d5911a46c8f8da45f56755"}, - {file = "charset_normalizer-3.1.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:0c95f12b74681e9ae127728f7e5409cbbef9cd914d5896ef238cc779b8152373"}, - {file = "charset_normalizer-3.1.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fca62a8301b605b954ad2e9c3666f9d97f63872aa4efcae5492baca2056b74ab"}, - {file = "charset_normalizer-3.1.0-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ac0aa6cd53ab9a31d397f8303f92c42f534693528fafbdb997c82bae6e477ad9"}, - {file = "charset_normalizer-3.1.0-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c3af8e0f07399d3176b179f2e2634c3ce9c1301379a6b8c9c9aeecd481da494f"}, - {file = "charset_normalizer-3.1.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3a5fc78f9e3f501a1614a98f7c54d3969f3ad9bba8ba3d9b438c3bc5d047dd28"}, - {file = "charset_normalizer-3.1.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:628c985afb2c7d27a4800bfb609e03985aaecb42f955049957814e0491d4006d"}, - {file = "charset_normalizer-3.1.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:74db0052d985cf37fa111828d0dd230776ac99c740e1a758ad99094be4f1803d"}, - {file = "charset_normalizer-3.1.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:1e8fcdd8f672a1c4fc8d0bd3a2b576b152d2a349782d1eb0f6b8e52e9954731d"}, - {file = "charset_normalizer-3.1.0-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:04afa6387e2b282cf78ff3dbce20f0cc071c12dc8f685bd40960cc68644cfea6"}, - {file = "charset_normalizer-3.1.0-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:dd5653e67b149503c68c4018bf07e42eeed6b4e956b24c00ccdf93ac79cdff84"}, - {file = "charset_normalizer-3.1.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:d2686f91611f9e17f4548dbf050e75b079bbc2a82be565832bc8ea9047b61c8c"}, - {file = "charset_normalizer-3.1.0-cp37-cp37m-win32.whl", hash = "sha256:4155b51ae05ed47199dc5b2a4e62abccb274cee6b01da5b895099b61b1982974"}, - {file = "charset_normalizer-3.1.0-cp37-cp37m-win_amd64.whl", hash = "sha256:322102cdf1ab682ecc7d9b1c5eed4ec59657a65e1c146a0da342b78f4112db23"}, - {file = "charset_normalizer-3.1.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:e633940f28c1e913615fd624fcdd72fdba807bf53ea6925d6a588e84e1151531"}, - {file = "charset_normalizer-3.1.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:3a06f32c9634a8705f4ca9946d667609f52cf130d5548881401f1eb2c39b1e2c"}, - {file = "charset_normalizer-3.1.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:7381c66e0561c5757ffe616af869b916c8b4e42b367ab29fedc98481d1e74e14"}, - {file = "charset_normalizer-3.1.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3573d376454d956553c356df45bb824262c397c6e26ce43e8203c4c540ee0acb"}, - {file = "charset_normalizer-3.1.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e89df2958e5159b811af9ff0f92614dabf4ff617c03a4c1c6ff53bf1c399e0e1"}, - {file = "charset_normalizer-3.1.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:78cacd03e79d009d95635e7d6ff12c21eb89b894c354bd2b2ed0b4763373693b"}, - {file = "charset_normalizer-3.1.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:de5695a6f1d8340b12a5d6d4484290ee74d61e467c39ff03b39e30df62cf83a0"}, - {file = "charset_normalizer-3.1.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1c60b9c202d00052183c9be85e5eaf18a4ada0a47d188a83c8f5c5b23252f649"}, - {file = "charset_normalizer-3.1.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:f645caaf0008bacf349875a974220f1f1da349c5dbe7c4ec93048cdc785a3326"}, - {file = "charset_normalizer-3.1.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:ea9f9c6034ea2d93d9147818f17c2a0860d41b71c38b9ce4d55f21b6f9165a11"}, - {file = "charset_normalizer-3.1.0-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:80d1543d58bd3d6c271b66abf454d437a438dff01c3e62fdbcd68f2a11310d4b"}, - {file = "charset_normalizer-3.1.0-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:73dc03a6a7e30b7edc5b01b601e53e7fc924b04e1835e8e407c12c037e81adbd"}, - {file = "charset_normalizer-3.1.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:6f5c2e7bc8a4bf7c426599765b1bd33217ec84023033672c1e9a8b35eaeaaaf8"}, - {file = "charset_normalizer-3.1.0-cp38-cp38-win32.whl", hash = "sha256:12a2b561af122e3d94cdb97fe6fb2bb2b82cef0cdca131646fdb940a1eda04f0"}, - {file = "charset_normalizer-3.1.0-cp38-cp38-win_amd64.whl", hash = "sha256:3160a0fd9754aab7d47f95a6b63ab355388d890163eb03b2d2b87ab0a30cfa59"}, - {file = "charset_normalizer-3.1.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:38e812a197bf8e71a59fe55b757a84c1f946d0ac114acafaafaf21667a7e169e"}, - {file = "charset_normalizer-3.1.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:6baf0baf0d5d265fa7944feb9f7451cc316bfe30e8df1a61b1bb08577c554f31"}, - {file = "charset_normalizer-3.1.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:8f25e17ab3039b05f762b0a55ae0b3632b2e073d9c8fc88e89aca31a6198e88f"}, - {file = "charset_normalizer-3.1.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3747443b6a904001473370d7810aa19c3a180ccd52a7157aacc264a5ac79265e"}, - {file = "charset_normalizer-3.1.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b116502087ce8a6b7a5f1814568ccbd0e9f6cfd99948aa59b0e241dc57cf739f"}, - {file = "charset_normalizer-3.1.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d16fd5252f883eb074ca55cb622bc0bee49b979ae4e8639fff6ca3ff44f9f854"}, - {file = "charset_normalizer-3.1.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:21fa558996782fc226b529fdd2ed7866c2c6ec91cee82735c98a197fae39f706"}, - {file = "charset_normalizer-3.1.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6f6c7a8a57e9405cad7485f4c9d3172ae486cfef1344b5ddd8e5239582d7355e"}, - {file = "charset_normalizer-3.1.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:ac3775e3311661d4adace3697a52ac0bab17edd166087d493b52d4f4f553f9f0"}, - {file = "charset_normalizer-3.1.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:10c93628d7497c81686e8e5e557aafa78f230cd9e77dd0c40032ef90c18f2230"}, - {file = "charset_normalizer-3.1.0-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:6f4f4668e1831850ebcc2fd0b1cd11721947b6dc7c00bf1c6bd3c929ae14f2c7"}, - {file = "charset_normalizer-3.1.0-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:0be65ccf618c1e7ac9b849c315cc2e8a8751d9cfdaa43027d4f6624bd587ab7e"}, - {file = "charset_normalizer-3.1.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:53d0a3fa5f8af98a1e261de6a3943ca631c526635eb5817a87a59d9a57ebf48f"}, - {file = "charset_normalizer-3.1.0-cp39-cp39-win32.whl", hash = "sha256:a04f86f41a8916fe45ac5024ec477f41f886b3c435da2d4e3d2709b22ab02af1"}, - {file = "charset_normalizer-3.1.0-cp39-cp39-win_amd64.whl", hash = "sha256:830d2948a5ec37c386d3170c483063798d7879037492540f10a475e3fd6f244b"}, - {file = "charset_normalizer-3.1.0-py3-none-any.whl", hash = "sha256:3d9098b479e78c85080c98e1e35ff40b4a31d8953102bb0fd7d1b6f8a2111a3d"}, + {file = "charset-normalizer-3.3.2.tar.gz", hash = "sha256:f30c3cb33b24454a82faecaf01b19c18562b1e89558fb6c56de4d9118a032fd5"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:25baf083bf6f6b341f4121c2f3c548875ee6f5339300e08be3f2b2ba1721cdd3"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:06435b539f889b1f6f4ac1758871aae42dc3a8c0e24ac9e60c2384973ad73027"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9063e24fdb1e498ab71cb7419e24622516c4a04476b17a2dab57e8baa30d6e03"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6897af51655e3691ff853668779c7bad41579facacf5fd7253b0133308cf000d"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1d3193f4a680c64b4b6a9115943538edb896edc190f0b222e73761716519268e"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cd70574b12bb8a4d2aaa0094515df2463cb429d8536cfb6c7ce983246983e5a6"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8465322196c8b4d7ab6d1e049e4c5cb460d0394da4a27d23cc242fbf0034b6b5"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a9a8e9031d613fd2009c182b69c7b2c1ef8239a0efb1df3f7c8da66d5dd3d537"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:beb58fe5cdb101e3a055192ac291b7a21e3b7ef4f67fa1d74e331a7f2124341c"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:e06ed3eb3218bc64786f7db41917d4e686cc4856944f53d5bdf83a6884432e12"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:2e81c7b9c8979ce92ed306c249d46894776a909505d8f5a4ba55b14206e3222f"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:572c3763a264ba47b3cf708a44ce965d98555f618ca42c926a9c1616d8f34269"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:fd1abc0d89e30cc4e02e4064dc67fcc51bd941eb395c502aac3ec19fab46b519"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-win32.whl", hash = "sha256:3d47fa203a7bd9c5b6cee4736ee84ca03b8ef23193c0d1ca99b5089f72645c73"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-win_amd64.whl", hash = "sha256:10955842570876604d404661fbccbc9c7e684caf432c09c715ec38fbae45ae09"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:802fe99cca7457642125a8a88a084cef28ff0cf9407060f7b93dca5aa25480db"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:573f6eac48f4769d667c4442081b1794f52919e7edada77495aaed9236d13a96"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:549a3a73da901d5bc3ce8d24e0600d1fa85524c10287f6004fbab87672bf3e1e"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f27273b60488abe721a075bcca6d7f3964f9f6f067c8c4c605743023d7d3944f"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1ceae2f17a9c33cb48e3263960dc5fc8005351ee19db217e9b1bb15d28c02574"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:65f6f63034100ead094b8744b3b97965785388f308a64cf8d7c34f2f2e5be0c4"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:753f10e867343b4511128c6ed8c82f7bec3bd026875576dfd88483c5c73b2fd8"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4a78b2b446bd7c934f5dcedc588903fb2f5eec172f3d29e52a9096a43722adfc"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e537484df0d8f426ce2afb2d0f8e1c3d0b114b83f8850e5f2fbea0e797bd82ae"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:eb6904c354526e758fda7167b33005998fb68c46fbc10e013ca97f21ca5c8887"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:deb6be0ac38ece9ba87dea880e438f25ca3eddfac8b002a2ec3d9183a454e8ae"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:4ab2fe47fae9e0f9dee8c04187ce5d09f48eabe611be8259444906793ab7cbce"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:80402cd6ee291dcb72644d6eac93785fe2c8b9cb30893c1af5b8fdd753b9d40f"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-win32.whl", hash = "sha256:7cd13a2e3ddeed6913a65e66e94b51d80a041145a026c27e6bb76c31a853c6ab"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-win_amd64.whl", hash = "sha256:663946639d296df6a2bb2aa51b60a2454ca1cb29835324c640dafb5ff2131a77"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:0b2b64d2bb6d3fb9112bafa732def486049e63de9618b5843bcdd081d8144cd8"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:ddbb2551d7e0102e7252db79ba445cdab71b26640817ab1e3e3648dad515003b"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:55086ee1064215781fff39a1af09518bc9255b50d6333f2e4c74ca09fac6a8f6"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8f4a014bc36d3c57402e2977dada34f9c12300af536839dc38c0beab8878f38a"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a10af20b82360ab00827f916a6058451b723b4e65030c5a18577c8b2de5b3389"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8d756e44e94489e49571086ef83b2bb8ce311e730092d2c34ca8f7d925cb20aa"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:90d558489962fd4918143277a773316e56c72da56ec7aa3dc3dbbe20fdfed15b"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6ac7ffc7ad6d040517be39eb591cac5ff87416c2537df6ba3cba3bae290c0fed"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:7ed9e526742851e8d5cc9e6cf41427dfc6068d4f5a3bb03659444b4cabf6bc26"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:8bdb58ff7ba23002a4c5808d608e4e6c687175724f54a5dade5fa8c67b604e4d"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:6b3251890fff30ee142c44144871185dbe13b11bab478a88887a639655be1068"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:b4a23f61ce87adf89be746c8a8974fe1c823c891d8f86eb218bb957c924bb143"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:efcb3f6676480691518c177e3b465bcddf57cea040302f9f4e6e191af91174d4"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-win32.whl", hash = "sha256:d965bba47ddeec8cd560687584e88cf699fd28f192ceb452d1d7ee807c5597b7"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-win_amd64.whl", hash = "sha256:96b02a3dc4381e5494fad39be677abcb5e6634bf7b4fa83a6dd3112607547001"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:95f2a5796329323b8f0512e09dbb7a1860c46a39da62ecb2324f116fa8fdc85c"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c002b4ffc0be611f0d9da932eb0f704fe2602a9a949d1f738e4c34c75b0863d5"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a981a536974bbc7a512cf44ed14938cf01030a99e9b3a06dd59578882f06f985"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3287761bc4ee9e33561a7e058c72ac0938c4f57fe49a09eae428fd88aafe7bb6"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:42cb296636fcc8b0644486d15c12376cb9fa75443e00fb25de0b8602e64c1714"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0a55554a2fa0d408816b3b5cedf0045f4b8e1a6065aec45849de2d6f3f8e9786"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:c083af607d2515612056a31f0a8d9e0fcb5876b7bfc0abad3ecd275bc4ebc2d5"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:87d1351268731db79e0f8e745d92493ee2841c974128ef629dc518b937d9194c"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:bd8f7df7d12c2db9fab40bdd87a7c09b1530128315d047a086fa3ae3435cb3a8"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:c180f51afb394e165eafe4ac2936a14bee3eb10debc9d9e4db8958fe36afe711"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:8c622a5fe39a48f78944a87d4fb8a53ee07344641b0562c540d840748571b811"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-win32.whl", hash = "sha256:db364eca23f876da6f9e16c9da0df51aa4f104a972735574842618b8c6d999d4"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-win_amd64.whl", hash = "sha256:86216b5cee4b06df986d214f664305142d9c76df9b6512be2738aa72a2048f99"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:6463effa3186ea09411d50efc7d85360b38d5f09b870c48e4600f63af490e56a"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:6c4caeef8fa63d06bd437cd4bdcf3ffefe6738fb1b25951440d80dc7df8c03ac"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:37e55c8e51c236f95b033f6fb391d7d7970ba5fe7ff453dad675e88cf303377a"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fb69256e180cb6c8a894fee62b3afebae785babc1ee98b81cdf68bbca1987f33"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ae5f4161f18c61806f411a13b0310bea87f987c7d2ecdbdaad0e94eb2e404238"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b2b0a0c0517616b6869869f8c581d4eb2dd83a4d79e0ebcb7d373ef9956aeb0a"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:45485e01ff4d3630ec0d9617310448a8702f70e9c01906b0d0118bdf9d124cf2"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:eb00ed941194665c332bf8e078baf037d6c35d7c4f3102ea2d4f16ca94a26dc8"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:2127566c664442652f024c837091890cb1942c30937add288223dc895793f898"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:a50aebfa173e157099939b17f18600f72f84eed3049e743b68ad15bd69b6bf99"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:4d0d1650369165a14e14e1e47b372cfcb31d6ab44e6e33cb2d4e57265290044d"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:923c0c831b7cfcb071580d3f46c4baf50f174be571576556269530f4bbd79d04"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:06a81e93cd441c56a9b65d8e1d043daeb97a3d0856d177d5c90ba85acb3db087"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-win32.whl", hash = "sha256:6ef1d82a3af9d3eecdba2321dc1b3c238245d890843e040e41e470ffa64c3e25"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-win_amd64.whl", hash = "sha256:eb8821e09e916165e160797a6c17edda0679379a4be5c716c260e836e122f54b"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:c235ebd9baae02f1b77bcea61bce332cb4331dc3617d254df3323aa01ab47bd4"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:5b4c145409bef602a690e7cfad0a15a55c13320ff7a3ad7ca59c13bb8ba4d45d"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:68d1f8a9e9e37c1223b656399be5d6b448dea850bed7d0f87a8311f1ff3dabb0"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:22afcb9f253dac0696b5a4be4a1c0f8762f8239e21b99680099abd9b2b1b2269"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e27ad930a842b4c5eb8ac0016b0a54f5aebbe679340c26101df33424142c143c"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1f79682fbe303db92bc2b1136016a38a42e835d932bab5b3b1bfcfbf0640e519"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b261ccdec7821281dade748d088bb6e9b69e6d15b30652b74cbbac25e280b796"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:122c7fa62b130ed55f8f285bfd56d5f4b4a5b503609d181f9ad85e55c89f4185"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:d0eccceffcb53201b5bfebb52600a5fb483a20b61da9dbc885f8b103cbe7598c"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:9f96df6923e21816da7e0ad3fd47dd8f94b2a5ce594e00677c0013018b813458"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:7f04c839ed0b6b98b1a7501a002144b76c18fb1c1850c8b98d458ac269e26ed2"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:34d1c8da1e78d2e001f363791c98a272bb734000fcef47a491c1e3b0505657a8"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:ff8fa367d09b717b2a17a052544193ad76cd49979c805768879cb63d9ca50561"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-win32.whl", hash = "sha256:aed38f6e4fb3f5d6bf81bfa990a07806be9d83cf7bacef998ab1a9bd660a581f"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-win_amd64.whl", hash = "sha256:b01b88d45a6fcb69667cd6d2f7a9aeb4bf53760d7fc536bf679ec94fe9f3ff3d"}, + {file = "charset_normalizer-3.3.2-py3-none-any.whl", hash = "sha256:3e4d1f6587322d2788836a99c69062fbb091331ec940e02d12d179c1d53e25fc"}, ] [[package]] name = "click" -version = "8.1.3" +version = "8.1.7" description = "Composable command line interface toolkit" optional = false python-versions = ">=3.7" files = [ - {file = "click-8.1.3-py3-none-any.whl", hash = "sha256:bb4d8133cb15a609f44e8213d9b391b0809795062913b383c62be0ee95b1db48"}, - {file = "click-8.1.3.tar.gz", hash = "sha256:7682dc8afb30297001674575ea00d1814d808d6a36af415a82bd481d37ba7b8e"}, + {file = "click-8.1.7-py3-none-any.whl", hash = "sha256:ae74fb96c20a0277a1d615f1e4d73c8414f5a98db8b799a7931d1582f3390c28"}, + {file = "click-8.1.7.tar.gz", hash = "sha256:ca9853ad459e787e2192211578cc907e7594e294c7ccc834310722b41b9ca6de"}, ] [package.dependencies] @@ -186,6 +224,51 @@ files = [ {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, ] +[[package]] +name = "commonmark" +version = "0.9.1" +description = "Python parser for the CommonMark Markdown spec" +optional = false +python-versions = "*" +files = [ + {file = "commonmark-0.9.1-py2.py3-none-any.whl", hash = "sha256:da2f38c92590f83de410ba1a3cbceafbc74fee9def35f9251ba9a971d6d66fd9"}, + {file = "commonmark-0.9.1.tar.gz", hash = "sha256:452f9dc859be7f06631ddcb328b6919c67984aca654e5fefb3914d54691aed60"}, +] + +[package.extras] +test = ["flake8 (==3.7.8)", "hypothesis (==3.55.3)"] + +[[package]] +name = "cot-dt2c" +version = "0.1.0" +description = "CoT-dt2c Tool is a python script to convert CoT DT file into corresponding C file" +optional = false +python-versions = "^3.8" +files = [] +develop = true + +[package.dependencies] +click = "^8.1.7" +igraph = "^0.11.6" +plotly = "^5.23.0" +pydevicetree = "0.0.13" +pyparsing = "^3.1.2" + +[package.source] +type = "directory" +url = "tools/cot_dt2c" + +[[package]] +name = "distlib" +version = "0.3.8" +description = "Distribution utilities" +optional = false +python-versions = "*" +files = [ + {file = "distlib-0.3.8-py2.py3-none-any.whl", hash = "sha256:034db59a0b96f8ca18035f36290806a9a6e6bd9d1ff91e45a7f172eb17e51784"}, + {file = "distlib-0.3.8.tar.gz", hash = "sha256:1530ea13e350031b6312d8580ddb6b27a104275a31106523b8f123787f494f64"}, +] + [[package]] name = "docutils" version = "0.18.1" @@ -197,17 +280,93 @@ files = [ {file = "docutils-0.18.1.tar.gz", hash = "sha256:679987caf361a7539d76e584cbeddc311e3aee937877c87346f31debc63e9d06"}, ] +[[package]] +name = "filelock" +version = "3.16.0" +description = "A platform independent file lock." +optional = false +python-versions = ">=3.8" +files = [ + {file = "filelock-3.16.0-py3-none-any.whl", hash = "sha256:f6ed4c963184f4c84dd5557ce8fece759a3724b37b80c6c4f20a2f63a4dc6609"}, + {file = "filelock-3.16.0.tar.gz", hash = "sha256:81de9eb8453c769b63369f87f11131a7ab04e367f8d97ad39dc230daa07e3bec"}, +] + +[package.extras] +docs = ["furo (>=2024.8.6)", "sphinx (>=8.0.2)", "sphinx-autodoc-typehints (>=2.4)"] +testing = ["covdefaults (>=2.3)", "coverage (>=7.6.1)", "diff-cover (>=9.1.1)", "pytest (>=8.3.2)", "pytest-asyncio (>=0.24)", "pytest-cov (>=5)", "pytest-mock (>=3.14)", "pytest-timeout (>=2.3.1)", "virtualenv (>=20.26.3)"] +typing = ["typing-extensions (>=4.12.2)"] + [[package]] name = "idna" -version = "3.4" +version = "3.8" description = "Internationalized Domain Names in Applications (IDNA)" optional = false -python-versions = ">=3.5" +python-versions = ">=3.6" files = [ - {file = "idna-3.4-py3-none-any.whl", hash = "sha256:90b77e79eaa3eba6de819a0c442c0b4ceefc341a7a2ab77d7562bf49f425c5c2"}, - {file = "idna-3.4.tar.gz", hash = "sha256:814f528e8dead7d329833b91c5faa87d60bf71824cd12a7530b5526063d02cb4"}, + {file = "idna-3.8-py3-none-any.whl", hash = "sha256:050b4e5baadcd44d760cedbd2b8e639f2ff89bbc7a5730fcc662954303377aac"}, + {file = "idna-3.8.tar.gz", hash = "sha256:d838c2c0ed6fced7693d5e8ab8e734d5f8fda53a039c0164afb0b82e771e3603"}, ] +[[package]] +name = "igraph" +version = "0.11.6" +description = "High performance graph data structures and algorithms" +optional = false +python-versions = ">=3.8" +files = [ + {file = "igraph-0.11.6-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:3f8b837181e8e87676be3873ce87cc92cc234efd58a2da2f6b4e050db150fcf4"}, + {file = "igraph-0.11.6-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:245c4b7d7657849eff80416f5df4525c8fc44c74a981ee4d44f0ef2612c3bada"}, + {file = "igraph-0.11.6-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bdb7be3d165073c0136295c0808e9edc57ba096cdb26e94086abb04561f7a292"}, + {file = "igraph-0.11.6-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:58974e20df2986a1ae52a16e51ecb387cc0cbeb41c5c0ddff4d373a1bbf1d9c5"}, + {file = "igraph-0.11.6-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7bef14de5e8ab70724a43808b1ed14aaa6fe1002f87e592289027a3827a8f44a"}, + {file = "igraph-0.11.6-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:86c1e98de2e32d074df8510bf18abfa1f4c5fda4cb28a009985a5d746b0c0125"}, + {file = "igraph-0.11.6-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:ebc5b3d702158abeb2e4d2414374586a2b932e1a07e48352b470600e1733d528"}, + {file = "igraph-0.11.6-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:0efe6d0fb22d3987a800eb3857ed04df9eb4c5dddd0998be05232cb646f1c337"}, + {file = "igraph-0.11.6-cp38-cp38-win32.whl", hash = "sha256:f4e68b27497b1c8ada2fb2bc35ef3fa7b0d72e84306b3d648d3de240fc618c32"}, + {file = "igraph-0.11.6-cp38-cp38-win_amd64.whl", hash = "sha256:5665b33dfbfca5f54ce9b4fea6b97903bd0e99fb1b02acf5e57e600bdfa5a355"}, + {file = "igraph-0.11.6-cp39-abi3-macosx_10_9_x86_64.whl", hash = "sha256:8aabef03d787b519d1075dfc0da4a1109fb113b941334883e3e7947ac30a459e"}, + {file = "igraph-0.11.6-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:1f2cc4a518d99cdf6cae514f85e93e56852bc8c325b3abb96037d1d690b5975f"}, + {file = "igraph-0.11.6-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c1e859238be52ab8ccc614d18f9362942bc88ce543afc12548f81ae99b10801d"}, + {file = "igraph-0.11.6-cp39-abi3-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d61fbe5e85eb4ae9efe08c461f9bdeedb02a2b5739fbc223d324a71f40a28be2"}, + {file = "igraph-0.11.6-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b6620ba39df29fd42151becf82309b54e57148233c9c3ef890eed62e25eed8a5"}, + {file = "igraph-0.11.6-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:59666589bb3d07f310cda2c5106a8adeeb77c2ef27fecf1c6438b6091f4ca69d"}, + {file = "igraph-0.11.6-cp39-abi3-musllinux_1_2_i686.whl", hash = "sha256:8750b6d6caebf199cf7dc41c931f58e330153779707391e30f0a29f02666fb6e"}, + {file = "igraph-0.11.6-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:967d6f2c30fe94317da15e459374d0fb8ca3e56020412f201ecd07dd5b5352f2"}, + {file = "igraph-0.11.6-cp39-abi3-win32.whl", hash = "sha256:9744f95a67319eb6cb487ceabf30f5d7940de34bada51f0ba63adbd23e0f94ad"}, + {file = "igraph-0.11.6-cp39-abi3-win_amd64.whl", hash = "sha256:b80e69eb11faa9c57330a9ffebdde5808966efe1c1f638d4d4827ea04df7aca8"}, + {file = "igraph-0.11.6-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:0329c16092e2ea7930d5f8368666ce7cb704900cc0ea04e4afe9ea1dd46e44af"}, + {file = "igraph-0.11.6-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:21752313f449bd8688e5688e95ea7231cea5e9199c7162535029be0d9af848ac"}, + {file = "igraph-0.11.6-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ea25e136c6c4161f53ff58868b23ff6c845193050ab0e502236d68e5d4174e32"}, + {file = "igraph-0.11.6-pp310-pypy310_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ac84433a03aef15e4b810010b08882b09854a3669450ccf31e392dbe295d2a66"}, + {file = "igraph-0.11.6-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ac697a44e3573169fa2b28c9c37dcf9cf01e0f558b845dd7123860d4c7c8fb89"}, + {file = "igraph-0.11.6-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:bdeae8bf35316eb1fb27bf667dcf5ecf5fcfb0b8f51831bc1b00c39c09c2d73b"}, + {file = "igraph-0.11.6-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:ad7e4aa442935de72554b96733bf6d7f09eac5cee97988a2562bdd3ca173cfa3"}, + {file = "igraph-0.11.6-pp38-pypy38_pp73-macosx_11_0_arm64.whl", hash = "sha256:8d2818780358a686178866d01568b9df1f29678581734ad7a78882bab54df004"}, + {file = "igraph-0.11.6-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2352276a20d979f1dea360af4202bb9f0c9a7d2c77f51815c0e625165e82013d"}, + {file = "igraph-0.11.6-pp38-pypy38_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:687fdab543b507d622fa3043f4227e5b26dc61dcf8ff8c0919fccddcc655f8b8"}, + {file = "igraph-0.11.6-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:57f7f8214cd48c9a4d97f7346a4152ba2d4ac95fb5ee0df4ecf224fce4ba3d14"}, + {file = "igraph-0.11.6-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:2b9cc69ede53f76ffae03b066609aa90184dd68ef15da8c104a97cebb9210838"}, + {file = "igraph-0.11.6-pp39-pypy39_pp73-macosx_10_15_x86_64.whl", hash = "sha256:591e1e447c3f0092daf7613a3eaedab83f9a0b0adbaf7702724c5117ded038a5"}, + {file = "igraph-0.11.6-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:ca558eb331bc687bc33e5cd23717e22676e9412f8cda3a31d30c996a0487610d"}, + {file = "igraph-0.11.6-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bf43c30e08debb087c9e3da69aa5cf1b6732968da34d55a614e3421b9a452146"}, + {file = "igraph-0.11.6-pp39-pypy39_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1d38e8d7db72b187d9d2211d0d06b3271fa9f32b04d49d789e2859b5480db0d0"}, + {file = "igraph-0.11.6-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1a318b059051ff78144a1c3cb880f4d933c812bcdb3d833a49cd7168d0427672"}, + {file = "igraph-0.11.6-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:2c54027add809b3c5b6685b8deca4ea4763fd000b9ea45c7ee46b7c9d61ff15e"}, + {file = "igraph-0.11.6.tar.gz", hash = "sha256:837f233256c3319f2a35a6a80d94eafe47b43791ef4c6f9e9871061341ac8e28"}, +] + +[package.dependencies] +texttable = ">=1.6.2" + +[package.extras] +cairo = ["cairocffi (>=1.2.0)"] +doc = ["Sphinx (>=7.0.0)", "pydoctor (>=23.4.0)", "sphinx-gallery (>=0.14.0)", "sphinx-rtd-theme (>=1.3.0)"] +matplotlib = ["matplotlib (>=3.6.0)"] +plotly = ["plotly (>=5.3.0)"] +plotting = ["cairocffi (>=1.2.0)"] +test = ["Pillow (>=9)", "cairocffi (>=1.2.0)", "matplotlib (>=3.6.0)", "networkx (>=2.5)", "numpy (>=1.19.0)", "pandas (>=1.1.0)", "plotly (>=5.3.0)", "pytest (>=7.0.1)", "pytest-timeout (>=2.1.0)", "scipy (>=1.5.0)"] +test-musl = ["cairocffi (>=1.2.0)", "networkx (>=2.5)", "pytest (>=7.0.1)", "pytest-timeout (>=2.1.0)"] + [[package]] name = "imagesize" version = "1.4.1" @@ -221,32 +380,32 @@ files = [ [[package]] name = "importlib-metadata" -version = "6.6.0" +version = "8.4.0" description = "Read metadata from Python packages" optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "importlib_metadata-6.6.0-py3-none-any.whl", hash = "sha256:43dd286a2cd8995d5eaef7fee2066340423b818ed3fd70adf0bad5f1fac53fed"}, - {file = "importlib_metadata-6.6.0.tar.gz", hash = "sha256:92501cdf9cc66ebd3e612f1b4f0c0765dfa42f0fa38ffb319b6bd84dd675d705"}, + {file = "importlib_metadata-8.4.0-py3-none-any.whl", hash = "sha256:66f342cc6ac9818fc6ff340576acd24d65ba0b3efabb2b4ac08b598965a4a2f1"}, + {file = "importlib_metadata-8.4.0.tar.gz", hash = "sha256:9a547d3bc3608b025f93d403fdd1aae741c24fbb8314df4b155675742ce303c5"}, ] [package.dependencies] zipp = ">=0.5" [package.extras] -docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] +doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] perf = ["ipython"] -testing = ["flake8 (<5)", "flufl.flake8", "importlib-resources (>=1.3)", "packaging", "pyfakefs", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)", "pytest-perf (>=0.9.2)"] +test = ["flufl.flake8", "importlib-resources (>=1.3)", "jaraco.test (>=5.4)", "packaging", "pyfakefs", "pytest (>=6,!=8.1.*)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy", "pytest-perf (>=0.9.2)", "pytest-ruff (>=0.2.1)"] [[package]] name = "jinja2" -version = "3.1.2" +version = "3.1.4" description = "A very fast and expressive template engine." optional = false python-versions = ">=3.7" files = [ - {file = "Jinja2-3.1.2-py3-none-any.whl", hash = "sha256:6088930bfe239f0e6710546ab9c19c9ef35e29792895fed6e6e31a023a182a61"}, - {file = "Jinja2-3.1.2.tar.gz", hash = "sha256:31351a702a408a9e7595a8fc6150fc3f43bb6bf7e319770cbc0db9df9437e852"}, + {file = "jinja2-3.1.4-py3-none-any.whl", hash = "sha256:bc5dd2abb727a5319567b7a813e6a2e7318c39f4f487cfe6c89c6f9c7d25197d"}, + {file = "jinja2-3.1.4.tar.gz", hash = "sha256:4a3aee7acbbe7303aede8e9648d13b8bf88a429282aa6122a993f0ac800cb369"}, ] [package.dependencies] @@ -281,61 +440,71 @@ testing = ["coverage", "pytest", "pytest-cov", "pytest-regressions"] [[package]] name = "markupsafe" -version = "2.1.2" +version = "2.1.5" description = "Safely add untrusted strings to HTML/XML markup." optional = false python-versions = ">=3.7" files = [ - {file = "MarkupSafe-2.1.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:665a36ae6f8f20a4676b53224e33d456a6f5a72657d9c83c2aa00765072f31f7"}, - {file = "MarkupSafe-2.1.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:340bea174e9761308703ae988e982005aedf427de816d1afe98147668cc03036"}, - {file = "MarkupSafe-2.1.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:22152d00bf4a9c7c83960521fc558f55a1adbc0631fbb00a9471e097b19d72e1"}, - {file = "MarkupSafe-2.1.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:28057e985dace2f478e042eaa15606c7efccb700797660629da387eb289b9323"}, - {file = "MarkupSafe-2.1.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ca244fa73f50a800cf8c3ebf7fd93149ec37f5cb9596aa8873ae2c1d23498601"}, - {file = "MarkupSafe-2.1.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:d9d971ec1e79906046aa3ca266de79eac42f1dbf3612a05dc9368125952bd1a1"}, - {file = "MarkupSafe-2.1.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:7e007132af78ea9df29495dbf7b5824cb71648d7133cf7848a2a5dd00d36f9ff"}, - {file = "MarkupSafe-2.1.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:7313ce6a199651c4ed9d7e4cfb4aa56fe923b1adf9af3b420ee14e6d9a73df65"}, - {file = "MarkupSafe-2.1.2-cp310-cp310-win32.whl", hash = "sha256:c4a549890a45f57f1ebf99c067a4ad0cb423a05544accaf2b065246827ed9603"}, - {file = "MarkupSafe-2.1.2-cp310-cp310-win_amd64.whl", hash = "sha256:835fb5e38fd89328e9c81067fd642b3593c33e1e17e2fdbf77f5676abb14a156"}, - {file = "MarkupSafe-2.1.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:2ec4f2d48ae59bbb9d1f9d7efb9236ab81429a764dedca114f5fdabbc3788013"}, - {file = "MarkupSafe-2.1.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:608e7073dfa9e38a85d38474c082d4281f4ce276ac0010224eaba11e929dd53a"}, - {file = "MarkupSafe-2.1.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:65608c35bfb8a76763f37036547f7adfd09270fbdbf96608be2bead319728fcd"}, - {file = "MarkupSafe-2.1.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f2bfb563d0211ce16b63c7cb9395d2c682a23187f54c3d79bfec33e6705473c6"}, - {file = "MarkupSafe-2.1.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:da25303d91526aac3672ee6d49a2f3db2d9502a4a60b55519feb1a4c7714e07d"}, - {file = "MarkupSafe-2.1.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:9cad97ab29dfc3f0249b483412c85c8ef4766d96cdf9dcf5a1e3caa3f3661cf1"}, - {file = "MarkupSafe-2.1.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:085fd3201e7b12809f9e6e9bc1e5c96a368c8523fad5afb02afe3c051ae4afcc"}, - {file = "MarkupSafe-2.1.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:1bea30e9bf331f3fef67e0a3877b2288593c98a21ccb2cf29b74c581a4eb3af0"}, - {file = "MarkupSafe-2.1.2-cp311-cp311-win32.whl", hash = "sha256:7df70907e00c970c60b9ef2938d894a9381f38e6b9db73c5be35e59d92e06625"}, - {file = "MarkupSafe-2.1.2-cp311-cp311-win_amd64.whl", hash = "sha256:e55e40ff0cc8cc5c07996915ad367fa47da6b3fc091fdadca7f5403239c5fec3"}, - {file = "MarkupSafe-2.1.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:a6e40afa7f45939ca356f348c8e23048e02cb109ced1eb8420961b2f40fb373a"}, - {file = "MarkupSafe-2.1.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cf877ab4ed6e302ec1d04952ca358b381a882fbd9d1b07cccbfd61783561f98a"}, - {file = "MarkupSafe-2.1.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:63ba06c9941e46fa389d389644e2d8225e0e3e5ebcc4ff1ea8506dce646f8c8a"}, - {file = "MarkupSafe-2.1.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f1cd098434e83e656abf198f103a8207a8187c0fc110306691a2e94a78d0abb2"}, - {file = "MarkupSafe-2.1.2-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:55f44b440d491028addb3b88f72207d71eeebfb7b5dbf0643f7c023ae1fba619"}, - {file = "MarkupSafe-2.1.2-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:a6f2fcca746e8d5910e18782f976489939d54a91f9411c32051b4aab2bd7c513"}, - {file = "MarkupSafe-2.1.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:0b462104ba25f1ac006fdab8b6a01ebbfbce9ed37fd37fd4acd70c67c973e460"}, - {file = "MarkupSafe-2.1.2-cp37-cp37m-win32.whl", hash = "sha256:7668b52e102d0ed87cb082380a7e2e1e78737ddecdde129acadb0eccc5423859"}, - {file = "MarkupSafe-2.1.2-cp37-cp37m-win_amd64.whl", hash = "sha256:6d6607f98fcf17e534162f0709aaad3ab7a96032723d8ac8750ffe17ae5a0666"}, - {file = "MarkupSafe-2.1.2-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:a806db027852538d2ad7555b203300173dd1b77ba116de92da9afbc3a3be3eed"}, - {file = "MarkupSafe-2.1.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:a4abaec6ca3ad8660690236d11bfe28dfd707778e2442b45addd2f086d6ef094"}, - {file = "MarkupSafe-2.1.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f03a532d7dee1bed20bc4884194a16160a2de9ffc6354b3878ec9682bb623c54"}, - {file = "MarkupSafe-2.1.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4cf06cdc1dda95223e9d2d3c58d3b178aa5dacb35ee7e3bbac10e4e1faacb419"}, - {file = "MarkupSafe-2.1.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:22731d79ed2eb25059ae3df1dfc9cb1546691cc41f4e3130fe6bfbc3ecbbecfa"}, - {file = "MarkupSafe-2.1.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:f8ffb705ffcf5ddd0e80b65ddf7bed7ee4f5a441ea7d3419e861a12eaf41af58"}, - {file = "MarkupSafe-2.1.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:8db032bf0ce9022a8e41a22598eefc802314e81b879ae093f36ce9ddf39ab1ba"}, - {file = "MarkupSafe-2.1.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:2298c859cfc5463f1b64bd55cb3e602528db6fa0f3cfd568d3605c50678f8f03"}, - {file = "MarkupSafe-2.1.2-cp38-cp38-win32.whl", hash = "sha256:50c42830a633fa0cf9e7d27664637532791bfc31c731a87b202d2d8ac40c3ea2"}, - {file = "MarkupSafe-2.1.2-cp38-cp38-win_amd64.whl", hash = "sha256:bb06feb762bade6bf3c8b844462274db0c76acc95c52abe8dbed28ae3d44a147"}, - {file = "MarkupSafe-2.1.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:99625a92da8229df6d44335e6fcc558a5037dd0a760e11d84be2260e6f37002f"}, - {file = "MarkupSafe-2.1.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:8bca7e26c1dd751236cfb0c6c72d4ad61d986e9a41bbf76cb445f69488b2a2bd"}, - {file = "MarkupSafe-2.1.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:40627dcf047dadb22cd25ea7ecfe9cbf3bbbad0482ee5920b582f3809c97654f"}, - {file = "MarkupSafe-2.1.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:40dfd3fefbef579ee058f139733ac336312663c6706d1163b82b3003fb1925c4"}, - {file = "MarkupSafe-2.1.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:090376d812fb6ac5f171e5938e82e7f2d7adc2b629101cec0db8b267815c85e2"}, - {file = "MarkupSafe-2.1.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:2e7821bffe00aa6bd07a23913b7f4e01328c3d5cc0b40b36c0bd81d362faeb65"}, - {file = "MarkupSafe-2.1.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:c0a33bc9f02c2b17c3ea382f91b4db0e6cde90b63b296422a939886a7a80de1c"}, - {file = "MarkupSafe-2.1.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:b8526c6d437855442cdd3d87eede9c425c4445ea011ca38d937db299382e6fa3"}, - {file = "MarkupSafe-2.1.2-cp39-cp39-win32.whl", hash = "sha256:137678c63c977754abe9086a3ec011e8fd985ab90631145dfb9294ad09c102a7"}, - {file = "MarkupSafe-2.1.2-cp39-cp39-win_amd64.whl", hash = "sha256:0576fe974b40a400449768941d5d0858cc624e3249dfd1e0c33674e5c7ca7aed"}, - {file = "MarkupSafe-2.1.2.tar.gz", hash = "sha256:abcabc8c2b26036d62d4c746381a6f7cf60aafcc653198ad678306986b09450d"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:a17a92de5231666cfbe003f0e4b9b3a7ae3afb1ec2845aadc2bacc93ff85febc"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:72b6be590cc35924b02c78ef34b467da4ba07e4e0f0454a2c5907f473fc50ce5"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e61659ba32cf2cf1481e575d0462554625196a1f2fc06a1c777d3f48e8865d46"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2174c595a0d73a3080ca3257b40096db99799265e1c27cc5a610743acd86d62f"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ae2ad8ae6ebee9d2d94b17fb62763125f3f374c25618198f40cbb8b525411900"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:075202fa5b72c86ad32dc7d0b56024ebdbcf2048c0ba09f1cde31bfdd57bcfff"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:598e3276b64aff0e7b3451b72e94fa3c238d452e7ddcd893c3ab324717456bad"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:fce659a462a1be54d2ffcacea5e3ba2d74daa74f30f5f143fe0c58636e355fdd"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-win32.whl", hash = "sha256:d9fad5155d72433c921b782e58892377c44bd6252b5af2f67f16b194987338a4"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-win_amd64.whl", hash = "sha256:bf50cd79a75d181c9181df03572cdce0fbb75cc353bc350712073108cba98de5"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:629ddd2ca402ae6dbedfceeba9c46d5f7b2a61d9749597d4307f943ef198fc1f"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:5b7b716f97b52c5a14bffdf688f971b2d5ef4029127f1ad7a513973cfd818df2"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6ec585f69cec0aa07d945b20805be741395e28ac1627333b1c5b0105962ffced"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b91c037585eba9095565a3556f611e3cbfaa42ca1e865f7b8015fe5c7336d5a5"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7502934a33b54030eaf1194c21c692a534196063db72176b0c4028e140f8f32c"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:0e397ac966fdf721b2c528cf028494e86172b4feba51d65f81ffd65c63798f3f"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:c061bb86a71b42465156a3ee7bd58c8c2ceacdbeb95d05a99893e08b8467359a"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:3a57fdd7ce31c7ff06cdfbf31dafa96cc533c21e443d57f5b1ecc6cdc668ec7f"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-win32.whl", hash = "sha256:397081c1a0bfb5124355710fe79478cdbeb39626492b15d399526ae53422b906"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-win_amd64.whl", hash = "sha256:2b7c57a4dfc4f16f7142221afe5ba4e093e09e728ca65c51f5620c9aaeb9a617"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:8dec4936e9c3100156f8a2dc89c4b88d5c435175ff03413b443469c7c8c5f4d1"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:3c6b973f22eb18a789b1460b4b91bf04ae3f0c4234a0a6aa6b0a92f6f7b951d4"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ac07bad82163452a6884fe8fa0963fb98c2346ba78d779ec06bd7a6262132aee"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f5dfb42c4604dddc8e4305050aa6deb084540643ed5804d7455b5df8fe16f5e5"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ea3d8a3d18833cf4304cd2fc9cbb1efe188ca9b5efef2bdac7adc20594a0e46b"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:d050b3361367a06d752db6ead6e7edeb0009be66bc3bae0ee9d97fb326badc2a"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:bec0a414d016ac1a18862a519e54b2fd0fc8bbfd6890376898a6c0891dd82e9f"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:58c98fee265677f63a4385256a6d7683ab1832f3ddd1e66fe948d5880c21a169"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-win32.whl", hash = "sha256:8590b4ae07a35970728874632fed7bd57b26b0102df2d2b233b6d9d82f6c62ad"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-win_amd64.whl", hash = "sha256:823b65d8706e32ad2df51ed89496147a42a2a6e01c13cfb6ffb8b1e92bc910bb"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:c8b29db45f8fe46ad280a7294f5c3ec36dbac9491f2d1c17345be8e69cc5928f"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ec6a563cff360b50eed26f13adc43e61bc0c04d94b8be985e6fb24b81f6dcfdf"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a549b9c31bec33820e885335b451286e2969a2d9e24879f83fe904a5ce59d70a"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4f11aa001c540f62c6166c7726f71f7573b52c68c31f014c25cc7901deea0b52"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:7b2e5a267c855eea6b4283940daa6e88a285f5f2a67f2220203786dfa59b37e9"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:2d2d793e36e230fd32babe143b04cec8a8b3eb8a3122d2aceb4a371e6b09b8df"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:ce409136744f6521e39fd8e2a24c53fa18ad67aa5bc7c2cf83645cce5b5c4e50"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-win32.whl", hash = "sha256:4096e9de5c6fdf43fb4f04c26fb114f61ef0bf2e5604b6ee3019d51b69e8c371"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-win_amd64.whl", hash = "sha256:4275d846e41ecefa46e2015117a9f491e57a71ddd59bbead77e904dc02b1bed2"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:656f7526c69fac7f600bd1f400991cc282b417d17539a1b228617081106feb4a"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:97cafb1f3cbcd3fd2b6fbfb99ae11cdb14deea0736fc2b0952ee177f2b813a46"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f3fbcb7ef1f16e48246f704ab79d79da8a46891e2da03f8783a5b6fa41a9532"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fa9db3f79de01457b03d4f01b34cf91bc0048eb2c3846ff26f66687c2f6d16ab"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ffee1f21e5ef0d712f9033568f8344d5da8cc2869dbd08d87c84656e6a2d2f68"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:5dedb4db619ba5a2787a94d877bc8ffc0566f92a01c0ef214865e54ecc9ee5e0"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:30b600cf0a7ac9234b2638fbc0fb6158ba5bdcdf46aeb631ead21248b9affbc4"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:8dd717634f5a044f860435c1d8c16a270ddf0ef8588d4887037c5028b859b0c3"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-win32.whl", hash = "sha256:daa4ee5a243f0f20d528d939d06670a298dd39b1ad5f8a72a4275124a7819eff"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-win_amd64.whl", hash = "sha256:619bc166c4f2de5caa5a633b8b7326fbe98e0ccbfacabd87268a2b15ff73a029"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:7a68b554d356a91cce1236aa7682dc01df0edba8d043fd1ce607c49dd3c1edcf"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:db0b55e0f3cc0be60c1f19efdde9a637c32740486004f20d1cff53c3c0ece4d2"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3e53af139f8579a6d5f7b76549125f0d94d7e630761a2111bc431fd820e163b8"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:17b950fccb810b3293638215058e432159d2b71005c74371d784862b7e4683f3"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4c31f53cdae6ecfa91a77820e8b151dba54ab528ba65dfd235c80b086d68a465"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:bff1b4290a66b490a2f4719358c0cdcd9bafb6b8f061e45c7a2460866bf50c2e"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:bc1667f8b83f48511b94671e0e441401371dfd0f0a795c7daa4a3cd1dde55bea"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:5049256f536511ee3f7e1b3f87d1d1209d327e818e6ae1365e8653d7e3abb6a6"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-win32.whl", hash = "sha256:00e046b6dd71aa03a41079792f8473dc494d564611a8f89bbbd7cb93295ebdcf"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-win_amd64.whl", hash = "sha256:fa173ec60341d6bb97a89f5ea19c85c5643c1e7dedebc22f5181eb73573142c5"}, + {file = "MarkupSafe-2.1.5.tar.gz", hash = "sha256:d283d37a890ba4c1ae73ffadf8046435c76e7bc2247bbb63c00bd1a709c6544b"}, ] [[package]] @@ -396,35 +565,35 @@ testing = ["beautifulsoup4", "coverage[toml]", "pytest (>=6,<7)", "pytest-cov", [[package]] name = "packaging" -version = "23.1" +version = "24.1" description = "Core utilities for Python packages" optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "packaging-23.1-py3-none-any.whl", hash = "sha256:994793af429502c4ea2ebf6bf664629d07c1a9fe974af92966e4b8d2df7edc61"}, - {file = "packaging-23.1.tar.gz", hash = "sha256:a392980d2b6cffa644431898be54b0045151319d1e7ec34f0cfed48767dd334f"}, + {file = "packaging-24.1-py3-none-any.whl", hash = "sha256:5b8f2217dbdbd2f7f384c41c628544e6d52f2d0f53c6d0c3ea61aa5d1d7ff124"}, + {file = "packaging-24.1.tar.gz", hash = "sha256:026ed72c8ed3fcce5bf8950572258698927fd1dbda10a5e981cdf0ac37f4f002"}, ] [[package]] name = "pip" -version = "23.1.2" +version = "24.2" description = "The PyPA recommended tool for installing Python packages." optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "pip-23.1.2-py3-none-any.whl", hash = "sha256:3ef6ac33239e4027d9a5598a381b9d30880a1477e50039db2eac6e8a8f6d1b18"}, - {file = "pip-23.1.2.tar.gz", hash = "sha256:0e7c86f486935893c708287b30bd050a36ac827ec7fe5e43fe7cb198dd835fba"}, + {file = "pip-24.2-py3-none-any.whl", hash = "sha256:2cd581cf58ab7fcfca4ce8efa6dcacd0de5bf8d0a3eb9ec927e07405f4d9e2a2"}, + {file = "pip-24.2.tar.gz", hash = "sha256:5b5e490b5e9cb275c879595064adce9ebd31b854e3e803740b72f9ccf34a45b8"}, ] [[package]] name = "pip-tools" -version = "6.13.0" +version = "6.14.0" description = "pip-tools keeps your pinned dependencies fresh." optional = false python-versions = ">=3.7" files = [ - {file = "pip-tools-6.13.0.tar.gz", hash = "sha256:61d46bd2eb8016ed4a924e196e6e5b0a268cd3babd79e593048720db23522bb1"}, - {file = "pip_tools-6.13.0-py3-none-any.whl", hash = "sha256:50943f151d87e752abddec8158622c34ad7f292e193836e90e30d87da60b19d9"}, + {file = "pip-tools-6.14.0.tar.gz", hash = "sha256:06366be0e08d86b416407333e998b4d305d5bd925151b08942ed149380ba3e47"}, + {file = "pip_tools-6.14.0-py3-none-any.whl", hash = "sha256:c5ad042cd27c0b343b10db1db7f77a7d087beafbec59ae6df1bba4d3368dfe8c"}, ] [package.dependencies] @@ -432,28 +601,89 @@ build = "*" click = ">=8" pip = ">=22.2" setuptools = "*" +tomli = {version = "*", markers = "python_version < \"3.11\""} wheel = "*" [package.extras] -coverage = ["pytest-cov"] -testing = ["flit-core (>=2,<4)", "poetry-core (>=1.0.0)", "pytest (>=7.2.0)", "pytest-rerunfailures", "pytest-xdist"] +coverage = ["covdefaults", "pytest-cov"] +testing = ["flit-core (>=2,<4)", "poetry-core (>=1.0.0)", "pytest (>=7.2.0)", "pytest-rerunfailures", "pytest-xdist", "tomli-w"] + +[[package]] +name = "platformdirs" +version = "4.3.2" +description = "A small Python package for determining appropriate platform-specific dirs, e.g. a `user data dir`." +optional = false +python-versions = ">=3.8" +files = [ + {file = "platformdirs-4.3.2-py3-none-any.whl", hash = "sha256:eb1c8582560b34ed4ba105009a4badf7f6f85768b30126f351328507b2beb617"}, + {file = "platformdirs-4.3.2.tar.gz", hash = "sha256:9e5e27a08aa095dd127b9f2e764d74254f482fef22b0970773bfba79d091ab8c"}, +] + +[package.extras] +docs = ["furo (>=2024.8.6)", "proselint (>=0.14)", "sphinx (>=8.0.2)", "sphinx-autodoc-typehints (>=2.4)"] +test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=8.3.2)", "pytest-cov (>=5)", "pytest-mock (>=3.14)"] +type = ["mypy (>=1.11.2)"] + +[[package]] +name = "plotly" +version = "5.24.0" +description = "An open-source, interactive data visualization library for Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "plotly-5.24.0-py3-none-any.whl", hash = "sha256:0e54efe52c8cef899f7daa41be9ed97dfb6be622613a2a8f56a86a0634b2b67e"}, + {file = "plotly-5.24.0.tar.gz", hash = "sha256:eae9f4f54448682442c92c1e97148e3ad0c52f0cf86306e1b76daba24add554a"}, +] + +[package.dependencies] +packaging = "*" +tenacity = ">=6.2.0" + +[[package]] +name = "pluggy" +version = "1.5.0" +description = "plugin and hook calling mechanisms for python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pluggy-1.5.0-py3-none-any.whl", hash = "sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669"}, + {file = "pluggy-1.5.0.tar.gz", hash = "sha256:2cffa88e94fdc978c4c574f15f9e59b7f4201d439195c3715ca9e2486f1d0cf1"}, +] + +[package.extras] +dev = ["pre-commit", "tox"] +testing = ["pytest", "pytest-benchmark"] [[package]] name = "prettytable" -version = "3.7.0" +version = "3.11.0" description = "A simple Python library for easily displaying tabular data in a visually appealing ASCII table format" optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "prettytable-3.7.0-py3-none-any.whl", hash = "sha256:f4aaf2ed6e6062a82fd2e6e5289bbbe705ec2788fe401a3a1f62a1cea55526d2"}, - {file = "prettytable-3.7.0.tar.gz", hash = "sha256:ef8334ee40b7ec721651fc4d37ecc7bb2ef55fde5098d994438f0dfdaa385c0c"}, + {file = "prettytable-3.11.0-py3-none-any.whl", hash = "sha256:aa17083feb6c71da11a68b2c213b04675c4af4ce9c541762632ca3f2cb3546dd"}, + {file = "prettytable-3.11.0.tar.gz", hash = "sha256:7e23ca1e68bbfd06ba8de98bf553bf3493264c96d5e8a615c0471025deeba722"}, ] [package.dependencies] wcwidth = "*" [package.extras] -tests = ["pytest", "pytest-cov", "pytest-lazy-fixture"] +tests = ["pytest", "pytest-cov", "pytest-lazy-fixtures"] + +[[package]] +name = "pydevicetree" +version = "0.0.13" +description = "A library for parsing Devicetree Source v1" +optional = false +python-versions = ">=3.5" +files = [ + {file = "pydevicetree-0.0.13-py3-none-any.whl", hash = "sha256:d61c695cec925b90a8b5740053f4b604e51154a9b36e62a2f12ed9ceaf2f8c38"}, + {file = "pydevicetree-0.0.13.tar.gz", hash = "sha256:5700c05df89bad8fd729c11aa6f764a3323bcb3796f13b32481ae34445cfc1b7"}, +] + +[package.dependencies] +pyparsing = "*" [[package]] name = "pyelftools" @@ -468,101 +698,144 @@ files = [ [[package]] name = "pygments" -version = "2.15.1" +version = "2.18.0" description = "Pygments is a syntax highlighting package written in Python." optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" +files = [ + {file = "pygments-2.18.0-py3-none-any.whl", hash = "sha256:b8e6aca0523f3ab76fee51799c488e38782ac06eafcf95e7ba832985c8e7b13a"}, + {file = "pygments-2.18.0.tar.gz", hash = "sha256:786ff802f32e91311bff3889f6e9a86e81505fe99f2735bb6d60ae0c5004f199"}, +] + +[package.extras] +windows-terminal = ["colorama (>=0.4.6)"] + +[[package]] +name = "pyparsing" +version = "3.1.4" +description = "pyparsing module - Classes and methods to define and execute parsing grammars" +optional = false +python-versions = ">=3.6.8" +files = [ + {file = "pyparsing-3.1.4-py3-none-any.whl", hash = "sha256:a6a7ee4235a3f944aa1fa2249307708f893fe5717dc603503c6c7969c070fb7c"}, + {file = "pyparsing-3.1.4.tar.gz", hash = "sha256:f86ec8d1a83f11977c9a6ea7598e8c27fc5cddfa5b07ea2241edbbde1d7bc032"}, +] + +[package.extras] +diagrams = ["jinja2", "railroad-diagrams"] + +[[package]] +name = "pyproject-api" +version = "1.7.1" +description = "API to interact with the python pyproject.toml based projects" +optional = false +python-versions = ">=3.8" files = [ - {file = "Pygments-2.15.1-py3-none-any.whl", hash = "sha256:db2db3deb4b4179f399a09054b023b6a586b76499d36965813c71aa8ed7b5fd1"}, - {file = "Pygments-2.15.1.tar.gz", hash = "sha256:8ace4d3c1dd481894b2005f560ead0f9f19ee64fe983366be1a21e171d12775c"}, + {file = "pyproject_api-1.7.1-py3-none-any.whl", hash = "sha256:2dc1654062c2b27733d8fd4cdda672b22fe8741ef1dde8e3a998a9547b071eeb"}, + {file = "pyproject_api-1.7.1.tar.gz", hash = "sha256:7ebc6cd10710f89f4cf2a2731710a98abce37ebff19427116ff2174c9236a827"}, ] +[package.dependencies] +packaging = ">=24.1" +tomli = {version = ">=2.0.1", markers = "python_version < \"3.11\""} + [package.extras] -plugins = ["importlib-metadata"] +docs = ["furo (>=2024.5.6)", "sphinx-autodoc-typehints (>=2.2.1)"] +testing = ["covdefaults (>=2.3)", "pytest (>=8.2.2)", "pytest-cov (>=5)", "pytest-mock (>=3.14)", "setuptools (>=70.1)"] [[package]] name = "pyproject-hooks" -version = "1.0.0" +version = "1.1.0" description = "Wrappers to call pyproject.toml-based build backend hooks." optional = false python-versions = ">=3.7" files = [ - {file = "pyproject_hooks-1.0.0-py3-none-any.whl", hash = "sha256:283c11acd6b928d2f6a7c73fa0d01cb2bdc5f07c57a2eeb6e83d5e56b97976f8"}, - {file = "pyproject_hooks-1.0.0.tar.gz", hash = "sha256:f271b298b97f5955d53fb12b72c1fb1948c22c1a6b70b315c54cedaca0264ef5"}, + {file = "pyproject_hooks-1.1.0-py3-none-any.whl", hash = "sha256:7ceeefe9aec63a1064c18d939bdc3adf2d8aa1988a510afec15151578b232aa2"}, + {file = "pyproject_hooks-1.1.0.tar.gz", hash = "sha256:4b37730834edbd6bd37f26ece6b44802fb1c1ee2ece0e54ddff8bfc06db86965"}, ] -[package.dependencies] -tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} - [[package]] name = "pytz" -version = "2023.3" +version = "2024.1" description = "World timezone definitions, modern and historical" optional = false python-versions = "*" files = [ - {file = "pytz-2023.3-py2.py3-none-any.whl", hash = "sha256:a151b3abb88eda1d4e34a9814df37de2a80e301e68ba0fd856fb9b46bfbbbffb"}, - {file = "pytz-2023.3.tar.gz", hash = "sha256:1d8ce29db189191fb55338ee6d0387d82ab59f3d00eac103412d64e0ebd0c588"}, + {file = "pytz-2024.1-py2.py3-none-any.whl", hash = "sha256:328171f4e3623139da4983451950b28e95ac706e13f3f2630a879749e7a8b319"}, + {file = "pytz-2024.1.tar.gz", hash = "sha256:2a29735ea9c18baf14b448846bde5a48030ed267578472d8955cd0e7443a9812"}, ] [[package]] name = "pyyaml" -version = "6.0" +version = "6.0.2" description = "YAML parser and emitter for Python" optional = false -python-versions = ">=3.6" +python-versions = ">=3.8" files = [ - {file = "PyYAML-6.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d4db7c7aef085872ef65a8fd7d6d09a14ae91f691dec3e87ee5ee0539d516f53"}, - {file = "PyYAML-6.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9df7ed3b3d2e0ecfe09e14741b857df43adb5a3ddadc919a2d94fbdf78fea53c"}, - {file = "PyYAML-6.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:77f396e6ef4c73fdc33a9157446466f1cff553d979bd00ecb64385760c6babdc"}, - {file = "PyYAML-6.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a80a78046a72361de73f8f395f1f1e49f956c6be882eed58505a15f3e430962b"}, - {file = "PyYAML-6.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:f84fbc98b019fef2ee9a1cb3ce93e3187a6df0b2538a651bfb890254ba9f90b5"}, - {file = "PyYAML-6.0-cp310-cp310-win32.whl", hash = "sha256:2cd5df3de48857ed0544b34e2d40e9fac445930039f3cfe4bcc592a1f836d513"}, - {file = "PyYAML-6.0-cp310-cp310-win_amd64.whl", hash = "sha256:daf496c58a8c52083df09b80c860005194014c3698698d1a57cbcfa182142a3a"}, - {file = "PyYAML-6.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:d4b0ba9512519522b118090257be113b9468d804b19d63c71dbcf4a48fa32358"}, - {file = "PyYAML-6.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:81957921f441d50af23654aa6c5e5eaf9b06aba7f0a19c18a538dc7ef291c5a1"}, - {file = "PyYAML-6.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:afa17f5bc4d1b10afd4466fd3a44dc0e245382deca5b3c353d8b757f9e3ecb8d"}, - {file = "PyYAML-6.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:dbad0e9d368bb989f4515da330b88a057617d16b6a8245084f1b05400f24609f"}, - {file = "PyYAML-6.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:432557aa2c09802be39460360ddffd48156e30721f5e8d917f01d31694216782"}, - {file = "PyYAML-6.0-cp311-cp311-win32.whl", hash = "sha256:bfaef573a63ba8923503d27530362590ff4f576c626d86a9fed95822a8255fd7"}, - {file = "PyYAML-6.0-cp311-cp311-win_amd64.whl", hash = "sha256:01b45c0191e6d66c470b6cf1b9531a771a83c1c4208272ead47a3ae4f2f603bf"}, - {file = "PyYAML-6.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:897b80890765f037df3403d22bab41627ca8811ae55e9a722fd0392850ec4d86"}, - {file = "PyYAML-6.0-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:50602afada6d6cbfad699b0c7bb50d5ccffa7e46a3d738092afddc1f9758427f"}, - {file = "PyYAML-6.0-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:48c346915c114f5fdb3ead70312bd042a953a8ce5c7106d5bfb1a5254e47da92"}, - {file = "PyYAML-6.0-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:98c4d36e99714e55cfbaaee6dd5badbc9a1ec339ebfc3b1f52e293aee6bb71a4"}, - {file = "PyYAML-6.0-cp36-cp36m-win32.whl", hash = "sha256:0283c35a6a9fbf047493e3a0ce8d79ef5030852c51e9d911a27badfde0605293"}, - {file = "PyYAML-6.0-cp36-cp36m-win_amd64.whl", hash = "sha256:07751360502caac1c067a8132d150cf3d61339af5691fe9e87803040dbc5db57"}, - {file = "PyYAML-6.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:819b3830a1543db06c4d4b865e70ded25be52a2e0631ccd2f6a47a2822f2fd7c"}, - {file = "PyYAML-6.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:473f9edb243cb1935ab5a084eb238d842fb8f404ed2193a915d1784b5a6b5fc0"}, - {file = "PyYAML-6.0-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0ce82d761c532fe4ec3f87fc45688bdd3a4c1dc5e0b4a19814b9009a29baefd4"}, - {file = "PyYAML-6.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:231710d57adfd809ef5d34183b8ed1eeae3f76459c18fb4a0b373ad56bedcdd9"}, - {file = "PyYAML-6.0-cp37-cp37m-win32.whl", hash = "sha256:c5687b8d43cf58545ade1fe3e055f70eac7a5a1a0bf42824308d868289a95737"}, - {file = "PyYAML-6.0-cp37-cp37m-win_amd64.whl", hash = "sha256:d15a181d1ecd0d4270dc32edb46f7cb7733c7c508857278d3d378d14d606db2d"}, - {file = "PyYAML-6.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:0b4624f379dab24d3725ffde76559cff63d9ec94e1736b556dacdfebe5ab6d4b"}, - {file = "PyYAML-6.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:213c60cd50106436cc818accf5baa1aba61c0189ff610f64f4a3e8c6726218ba"}, - {file = "PyYAML-6.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9fa600030013c4de8165339db93d182b9431076eb98eb40ee068700c9c813e34"}, - {file = "PyYAML-6.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:277a0ef2981ca40581a47093e9e2d13b3f1fbbeffae064c1d21bfceba2030287"}, - {file = "PyYAML-6.0-cp38-cp38-win32.whl", hash = "sha256:d4eccecf9adf6fbcc6861a38015c2a64f38b9d94838ac1810a9023a0609e1b78"}, - {file = "PyYAML-6.0-cp38-cp38-win_amd64.whl", hash = "sha256:1e4747bc279b4f613a09eb64bba2ba602d8a6664c6ce6396a4d0cd413a50ce07"}, - {file = "PyYAML-6.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:055d937d65826939cb044fc8c9b08889e8c743fdc6a32b33e2390f66013e449b"}, - {file = "PyYAML-6.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:e61ceaab6f49fb8bdfaa0f92c4b57bcfbea54c09277b1b4f7ac376bfb7a7c174"}, - {file = "PyYAML-6.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d67d839ede4ed1b28a4e8909735fc992a923cdb84e618544973d7dfc71540803"}, - {file = "PyYAML-6.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cba8c411ef271aa037d7357a2bc8f9ee8b58b9965831d9e51baf703280dc73d3"}, - {file = "PyYAML-6.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:40527857252b61eacd1d9af500c3337ba8deb8fc298940291486c465c8b46ec0"}, - {file = "PyYAML-6.0-cp39-cp39-win32.whl", hash = "sha256:b5b9eccad747aabaaffbc6064800670f0c297e52c12754eb1d976c57e4f74dcb"}, - {file = "PyYAML-6.0-cp39-cp39-win_amd64.whl", hash = "sha256:b3d267842bf12586ba6c734f89d1f5b871df0273157918b0ccefa29deb05c21c"}, - {file = "PyYAML-6.0.tar.gz", hash = "sha256:68fb519c14306fec9720a2a5b45bc9f0c8d1b9c72adf45c37baedfcd949c35a2"}, + {file = "PyYAML-6.0.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0a9a2848a5b7feac301353437eb7d5957887edbf81d56e903999a75a3d743086"}, + {file = "PyYAML-6.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:29717114e51c84ddfba879543fb232a6ed60086602313ca38cce623c1d62cfbf"}, + {file = "PyYAML-6.0.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8824b5a04a04a047e72eea5cec3bc266db09e35de6bdfe34c9436ac5ee27d237"}, + {file = "PyYAML-6.0.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7c36280e6fb8385e520936c3cb3b8042851904eba0e58d277dca80a5cfed590b"}, + {file = "PyYAML-6.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ec031d5d2feb36d1d1a24380e4db6d43695f3748343d99434e6f5f9156aaa2ed"}, + {file = "PyYAML-6.0.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:936d68689298c36b53b29f23c6dbb74de12b4ac12ca6cfe0e047bedceea56180"}, + {file = "PyYAML-6.0.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:23502f431948090f597378482b4812b0caae32c22213aecf3b55325e049a6c68"}, + {file = "PyYAML-6.0.2-cp310-cp310-win32.whl", hash = "sha256:2e99c6826ffa974fe6e27cdb5ed0021786b03fc98e5ee3c5bfe1fd5015f42b99"}, + {file = "PyYAML-6.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:a4d3091415f010369ae4ed1fc6b79def9416358877534caf6a0fdd2146c87a3e"}, + {file = "PyYAML-6.0.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:cc1c1159b3d456576af7a3e4d1ba7e6924cb39de8f67111c735f6fc832082774"}, + {file = "PyYAML-6.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1e2120ef853f59c7419231f3bf4e7021f1b936f6ebd222406c3b60212205d2ee"}, + {file = "PyYAML-6.0.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5d225db5a45f21e78dd9358e58a98702a0302f2659a3c6cd320564b75b86f47c"}, + {file = "PyYAML-6.0.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5ac9328ec4831237bec75defaf839f7d4564be1e6b25ac710bd1a96321cc8317"}, + {file = "PyYAML-6.0.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3ad2a3decf9aaba3d29c8f537ac4b243e36bef957511b4766cb0057d32b0be85"}, + {file = "PyYAML-6.0.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:ff3824dc5261f50c9b0dfb3be22b4567a6f938ccce4587b38952d85fd9e9afe4"}, + {file = "PyYAML-6.0.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:797b4f722ffa07cc8d62053e4cff1486fa6dc094105d13fea7b1de7d8bf71c9e"}, + {file = "PyYAML-6.0.2-cp311-cp311-win32.whl", hash = "sha256:11d8f3dd2b9c1207dcaf2ee0bbbfd5991f571186ec9cc78427ba5bd32afae4b5"}, + {file = "PyYAML-6.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:e10ce637b18caea04431ce14fabcf5c64a1c61ec9c56b071a4b7ca131ca52d44"}, + {file = "PyYAML-6.0.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:c70c95198c015b85feafc136515252a261a84561b7b1d51e3384e0655ddf25ab"}, + {file = "PyYAML-6.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ce826d6ef20b1bc864f0a68340c8b3287705cae2f8b4b1d932177dcc76721725"}, + {file = "PyYAML-6.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f71ea527786de97d1a0cc0eacd1defc0985dcf6b3f17bb77dcfc8c34bec4dc5"}, + {file = "PyYAML-6.0.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9b22676e8097e9e22e36d6b7bda33190d0d400f345f23d4065d48f4ca7ae0425"}, + {file = "PyYAML-6.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:80bab7bfc629882493af4aa31a4cfa43a4c57c83813253626916b8c7ada83476"}, + {file = "PyYAML-6.0.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:0833f8694549e586547b576dcfaba4a6b55b9e96098b36cdc7ebefe667dfed48"}, + {file = "PyYAML-6.0.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8b9c7197f7cb2738065c481a0461e50ad02f18c78cd75775628afb4d7137fb3b"}, + {file = "PyYAML-6.0.2-cp312-cp312-win32.whl", hash = "sha256:ef6107725bd54b262d6dedcc2af448a266975032bc85ef0172c5f059da6325b4"}, + {file = "PyYAML-6.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:7e7401d0de89a9a855c839bc697c079a4af81cf878373abd7dc625847d25cbd8"}, + {file = "PyYAML-6.0.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:efdca5630322a10774e8e98e1af481aad470dd62c3170801852d752aa7a783ba"}, + {file = "PyYAML-6.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:50187695423ffe49e2deacb8cd10510bc361faac997de9efef88badc3bb9e2d1"}, + {file = "PyYAML-6.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0ffe8360bab4910ef1b9e87fb812d8bc0a308b0d0eef8c8f44e0254ab3b07133"}, + {file = "PyYAML-6.0.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:17e311b6c678207928d649faa7cb0d7b4c26a0ba73d41e99c4fff6b6c3276484"}, + {file = "PyYAML-6.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:70b189594dbe54f75ab3a1acec5f1e3faa7e8cf2f1e08d9b561cb41b845f69d5"}, + {file = "PyYAML-6.0.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:41e4e3953a79407c794916fa277a82531dd93aad34e29c2a514c2c0c5fe971cc"}, + {file = "PyYAML-6.0.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:68ccc6023a3400877818152ad9a1033e3db8625d899c72eacb5a668902e4d652"}, + {file = "PyYAML-6.0.2-cp313-cp313-win32.whl", hash = "sha256:bc2fa7c6b47d6bc618dd7fb02ef6fdedb1090ec036abab80d4681424b84c1183"}, + {file = "PyYAML-6.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:8388ee1976c416731879ac16da0aff3f63b286ffdd57cdeb95f3f2e085687563"}, + {file = "PyYAML-6.0.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:24471b829b3bf607e04e88d79542a9d48bb037c2267d7927a874e6c205ca7e9a"}, + {file = "PyYAML-6.0.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d7fded462629cfa4b685c5416b949ebad6cec74af5e2d42905d41e257e0869f5"}, + {file = "PyYAML-6.0.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d84a1718ee396f54f3a086ea0a66d8e552b2ab2017ef8b420e92edbc841c352d"}, + {file = "PyYAML-6.0.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9056c1ecd25795207ad294bcf39f2db3d845767be0ea6e6a34d856f006006083"}, + {file = "PyYAML-6.0.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:82d09873e40955485746739bcb8b4586983670466c23382c19cffecbf1fd8706"}, + {file = "PyYAML-6.0.2-cp38-cp38-win32.whl", hash = "sha256:43fa96a3ca0d6b1812e01ced1044a003533c47f6ee8aca31724f78e93ccc089a"}, + {file = "PyYAML-6.0.2-cp38-cp38-win_amd64.whl", hash = "sha256:01179a4a8559ab5de078078f37e5c1a30d76bb88519906844fd7bdea1b7729ff"}, + {file = "PyYAML-6.0.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:688ba32a1cffef67fd2e9398a2efebaea461578b0923624778664cc1c914db5d"}, + {file = "PyYAML-6.0.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a8786accb172bd8afb8be14490a16625cbc387036876ab6ba70912730faf8e1f"}, + {file = "PyYAML-6.0.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d8e03406cac8513435335dbab54c0d385e4a49e4945d2909a581c83647ca0290"}, + {file = "PyYAML-6.0.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f753120cb8181e736c57ef7636e83f31b9c0d1722c516f7e86cf15b7aa57ff12"}, + {file = "PyYAML-6.0.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3b1fdb9dc17f5a7677423d508ab4f243a726dea51fa5e70992e59a7411c89d19"}, + {file = "PyYAML-6.0.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:0b69e4ce7a131fe56b7e4d770c67429700908fc0752af059838b1cfb41960e4e"}, + {file = "PyYAML-6.0.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:a9f8c2e67970f13b16084e04f134610fd1d374bf477b17ec1599185cf611d725"}, + {file = "PyYAML-6.0.2-cp39-cp39-win32.whl", hash = "sha256:6395c297d42274772abc367baaa79683958044e5d3835486c16da75d2a694631"}, + {file = "PyYAML-6.0.2-cp39-cp39-win_amd64.whl", hash = "sha256:39693e1f8320ae4f43943590b49779ffb98acb81f788220ea932a6b6c51004d8"}, + {file = "pyyaml-6.0.2.tar.gz", hash = "sha256:d584d9ec91ad65861cc08d42e834324ef890a082e591037abe114850ff7bbc3e"}, ] [[package]] name = "requests" -version = "2.31.0" +version = "2.32.3" description = "Python HTTP for Humans." optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "requests-2.31.0-py3-none-any.whl", hash = "sha256:58cd2187c01e70e6e26505bca751777aa9f2ee0b7f4300988b709f44e013003f"}, - {file = "requests-2.31.0.tar.gz", hash = "sha256:942c5a758f98d790eaed1a29cb6eefc7ffb0d1cf7af05c3d2791656dbd6ad1e1"}, + {file = "requests-2.32.3-py3-none-any.whl", hash = "sha256:70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6"}, + {file = "requests-2.32.3.tar.gz", hash = "sha256:55365417734eb18255590a9ff9eb97e9e1da868d4ccd6402399eaf68af20a760"}, ] [package.dependencies] @@ -575,21 +848,55 @@ urllib3 = ">=1.21.1,<3" socks = ["PySocks (>=1.5.6,!=1.5.7)"] use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"] +[[package]] +name = "rich" +version = "10.16.2" +description = "Render rich text, tables, progress bars, syntax highlighting, markdown and more to the terminal" +optional = false +python-versions = ">=3.6.2,<4.0.0" +files = [ + {file = "rich-10.16.2-py3-none-any.whl", hash = "sha256:c59d73bd804c90f747c8d7b1d023b88f2a9ac2454224a4aeaf959b21eeb42d03"}, + {file = "rich-10.16.2.tar.gz", hash = "sha256:720974689960e06c2efdb54327f8bf0cdbdf4eae4ad73b6c94213cad405c371b"}, +] + +[package.dependencies] +colorama = ">=0.4.0,<0.5.0" +commonmark = ">=0.9.0,<0.10.0" +pygments = ">=2.6.0,<3.0.0" + +[package.extras] +jupyter = ["ipywidgets (>=7.5.1,<8.0.0)"] + [[package]] name = "setuptools" -version = "67.7.2" +version = "74.1.2" description = "Easily download, build, install, upgrade, and uninstall Python packages" optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "setuptools-67.7.2-py3-none-any.whl", hash = "sha256:23aaf86b85ca52ceb801d32703f12d77517b2556af839621c641fca11287952b"}, - {file = "setuptools-67.7.2.tar.gz", hash = "sha256:f104fa03692a2602fa0fec6c6a9e63b6c8a968de13e17c026957dd1f53d80990"}, + {file = "setuptools-74.1.2-py3-none-any.whl", hash = "sha256:5f4c08aa4d3ebcb57a50c33b1b07e94315d7fc7230f7115e47fc99776c8ce308"}, + {file = "setuptools-74.1.2.tar.gz", hash = "sha256:95b40ed940a1c67eb70fc099094bd6e99c6ee7c23aa2306f4d2697ba7916f9c6"}, ] [package.extras] -docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-hoverxref (<2)", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (==0.8.3)", "sphinx-reredirects", "sphinxcontrib-towncrier"] -testing = ["build[virtualenv]", "filelock (>=3.4.0)", "flake8 (<5)", "flake8-2020", "ini2toml[lite] (>=0.9)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "pip (>=19.1)", "pip-run (>=8.8)", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)", "pytest-perf", "pytest-timeout", "pytest-xdist", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"] -testing-integration = ["build[virtualenv]", "filelock (>=3.4.0)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "pytest", "pytest-enabler", "pytest-xdist", "tomli", "virtualenv (>=13.0.0)", "wheel"] +check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1)", "ruff (>=0.5.2)"] +core = ["importlib-metadata (>=6)", "importlib-resources (>=5.10.2)", "jaraco.text (>=3.7)", "more-itertools (>=8.8)", "packaging (>=24)", "platformdirs (>=2.6.2)", "tomli (>=2.0.1)", "wheel (>=0.43.0)"] +cover = ["pytest-cov"] +doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "pyproject-hooks (!=1.1)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (>=1,<2)", "sphinx-reredirects", "sphinxcontrib-towncrier", "towncrier (<24.7)"] +enabler = ["pytest-enabler (>=2.2)"] +test = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "ini2toml[lite] (>=0.14)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "jaraco.test", "packaging (>=23.2)", "pip (>=19.1)", "pyproject-hooks (!=1.1)", "pytest (>=6,!=8.1.*)", "pytest-home (>=0.5)", "pytest-perf", "pytest-subprocess", "pytest-timeout", "pytest-xdist (>=3)", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel (>=0.44.0)"] +type = ["importlib-metadata (>=7.0.2)", "jaraco.develop (>=7.21)", "mypy (==1.11.*)", "pytest-mypy"] + +[[package]] +name = "shellingham" +version = "1.5.4" +description = "Tool to Detect Surrounding Shell" +optional = false +python-versions = ">=3.7" +files = [ + {file = "shellingham-1.5.4-py2.py3-none-any.whl", hash = "sha256:7ecfff8f2fd72616f7481040475a65b2bf8af90a56c89140852d1120324e8686"}, + {file = "shellingham-1.5.4.tar.gz", hash = "sha256:8dbca0739d487e5bd35ab3ca4b36e11c4078f3a234bfce294b0a0291363404de"}, +] [[package]] name = "six" @@ -650,19 +957,19 @@ test = ["cython", "html5lib", "pytest (>=4.6)", "typed_ast"] [[package]] name = "sphinx-rtd-theme" -version = "1.2.0" +version = "1.3.0" description = "Read the Docs theme for Sphinx" optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,>=2.7" files = [ - {file = "sphinx_rtd_theme-1.2.0-py2.py3-none-any.whl", hash = "sha256:f823f7e71890abe0ac6aaa6013361ea2696fc8d3e1fa798f463e82bdb77eeff2"}, - {file = "sphinx_rtd_theme-1.2.0.tar.gz", hash = "sha256:a0d8bd1a2ed52e0b338cbe19c4b2eef3c5e7a048769753dac6a9f059c7b641b8"}, + {file = "sphinx_rtd_theme-1.3.0-py2.py3-none-any.whl", hash = "sha256:46ddef89cc2416a81ecfbeaceab1881948c014b1b6e4450b815311a89fb977b0"}, + {file = "sphinx_rtd_theme-1.3.0.tar.gz", hash = "sha256:590b030c7abb9cf038ec053b95e5380b5c70d61591eb0b552063fbe7c41f0931"}, ] [package.dependencies] docutils = "<0.19" -sphinx = ">=1.6,<7" -sphinxcontrib-jquery = {version = ">=2.0.0,<3.0.0 || >3.0.0", markers = "python_version > \"3\""} +sphinx = ">=1.6,<8" +sphinxcontrib-jquery = ">=4,<5" [package.extras] dev = ["bump2version", "sphinxcontrib-httpdomain", "transifex-client", "wheel"] @@ -803,6 +1110,53 @@ Sphinx = ">=1.6.3" [package.extras] cairosvg = ["cairosvg (>=1.0)"] +[[package]] +name = "tenacity" +version = "9.0.0" +description = "Retry code until it succeeds" +optional = false +python-versions = ">=3.8" +files = [ + {file = "tenacity-9.0.0-py3-none-any.whl", hash = "sha256:93de0c98785b27fcf659856aa9f54bfbd399e29969b0621bc7f762bd441b4539"}, + {file = "tenacity-9.0.0.tar.gz", hash = "sha256:807f37ca97d62aa361264d497b0e31e92b8027044942bfa756160d908320d73b"}, +] + +[package.extras] +doc = ["reno", "sphinx"] +test = ["pytest", "tornado (>=4.5)", "typeguard"] + +[[package]] +name = "texttable" +version = "1.7.0" +description = "module to create simple ASCII tables" +optional = false +python-versions = "*" +files = [ + {file = "texttable-1.7.0-py2.py3-none-any.whl", hash = "sha256:72227d592c82b3d7f672731ae73e4d1f88cd8e2ef5b075a7a7f01a23a3743917"}, + {file = "texttable-1.7.0.tar.gz", hash = "sha256:2d2068fb55115807d3ac77a4ca68fa48803e84ebb0ee2340f858107a36522638"}, +] + +[[package]] +name = "tlc" +version = "0.9.0" +description = "Transfer List Compiler (TLC) is a Python-based CLI for efficiently handling transfer lists." +optional = false +python-versions = "^3.8" +files = [] +develop = true + +[package.dependencies] +click = "^8.1.7" +jinja2 = "^3.1.4" +pyyaml = "^6.0.1" +rich = "^10.14.0" +tox = "^4.18.0" +typer = {version = "^0.4.0", extras = ["all"]} + +[package.source] +type = "directory" +url = "tools/tlc" + [[package]] name = "tomli" version = "2.0.1" @@ -814,75 +1168,148 @@ files = [ {file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"}, ] +[[package]] +name = "tox" +version = "4.18.1" +description = "tox is a generic virtualenv management and test command line tool" +optional = false +python-versions = ">=3.8" +files = [ + {file = "tox-4.18.1-py3-none-any.whl", hash = "sha256:35d472032ee1f73fe20c3e0e73d7073a4e85075c86ff02c576f9fc7c6a15a578"}, + {file = "tox-4.18.1.tar.gz", hash = "sha256:3c0c96bc3a568a5c7e66387a4cfcf8c875b52e09f4d47c9f7a277ec82f1a0b11"}, +] + +[package.dependencies] +cachetools = ">=5.5" +chardet = ">=5.2" +colorama = ">=0.4.6" +filelock = ">=3.15.4" +packaging = ">=24.1" +platformdirs = ">=4.2.2" +pluggy = ">=1.5" +pyproject-api = ">=1.7.1" +tomli = {version = ">=2.0.1", markers = "python_version < \"3.11\""} +virtualenv = ">=20.26.3" + +[package.extras] +docs = ["furo (>=2024.8.6)", "sphinx (>=8.0.2)", "sphinx-argparse-cli (>=1.17)", "sphinx-autodoc-typehints (>=2.4)", "sphinx-copybutton (>=0.5.2)", "sphinx-inline-tabs (>=2023.4.21)", "sphinxcontrib-towncrier (>=0.2.1a0)", "towncrier (>=24.8)"] +testing = ["build[virtualenv] (>=1.2.2)", "covdefaults (>=2.3)", "detect-test-pollution (>=1.2)", "devpi-process (>=1)", "diff-cover (>=9.1.1)", "distlib (>=0.3.8)", "flaky (>=3.8.1)", "hatch-vcs (>=0.4)", "hatchling (>=1.25)", "psutil (>=6)", "pytest (>=8.3.2)", "pytest-cov (>=5)", "pytest-mock (>=3.14)", "pytest-xdist (>=3.6.1)", "re-assert (>=1.1)", "setuptools (>=74.1.2)", "time-machine (>=2.15)", "wheel (>=0.44)"] + +[[package]] +name = "typer" +version = "0.4.2" +description = "Typer, build great CLIs. Easy to code. Based on Python type hints." +optional = false +python-versions = ">=3.6" +files = [ + {file = "typer-0.4.2-py3-none-any.whl", hash = "sha256:023bae00d1baf358a6cc7cea45851639360bb716de687b42b0a4641cd99173f1"}, + {file = "typer-0.4.2.tar.gz", hash = "sha256:b8261c6c0152dd73478b5ba96ba677e5d6948c715c310f7c91079f311f62ec03"}, +] + +[package.dependencies] +click = ">=7.1.1,<9.0.0" +colorama = {version = ">=0.4.3,<0.5.0", optional = true, markers = "extra == \"all\""} +shellingham = {version = ">=1.3.0,<2.0.0", optional = true, markers = "extra == \"all\""} + +[package.extras] +all = ["colorama (>=0.4.3,<0.5.0)", "shellingham (>=1.3.0,<2.0.0)"] +dev = ["autoflake (>=1.3.1,<2.0.0)", "flake8 (>=3.8.3,<4.0.0)", "pre-commit (>=2.17.0,<3.0.0)"] +doc = ["mdx-include (>=1.4.1,<2.0.0)", "mkdocs (>=1.1.2,<2.0.0)", "mkdocs-material (>=8.1.4,<9.0.0)"] +test = ["black (>=22.3.0,<23.0.0)", "coverage (>=5.2,<6.0)", "isort (>=5.0.6,<6.0.0)", "mypy (==0.910)", "pytest (>=4.4.0,<5.4.0)", "pytest-cov (>=2.10.0,<3.0.0)", "pytest-sugar (>=0.9.4,<0.10.0)", "pytest-xdist (>=1.32.0,<2.0.0)", "shellingham (>=1.3.0,<2.0.0)"] + [[package]] name = "typing-extensions" -version = "4.5.0" -description = "Backported and Experimental Type Hints for Python 3.7+" +version = "4.12.2" +description = "Backported and Experimental Type Hints for Python 3.8+" optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "typing_extensions-4.5.0-py3-none-any.whl", hash = "sha256:fb33085c39dd998ac16d1431ebc293a8b3eedd00fd4a32de0ff79002c19511b4"}, - {file = "typing_extensions-4.5.0.tar.gz", hash = "sha256:5cb5f4a79139d699607b3ef622a1dedafa84e115ab0024e0d9c044a9479ca7cb"}, + {file = "typing_extensions-4.12.2-py3-none-any.whl", hash = "sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d"}, + {file = "typing_extensions-4.12.2.tar.gz", hash = "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8"}, ] [[package]] name = "urllib3" -version = "2.0.2" +version = "2.2.2" description = "HTTP library with thread-safe connection pooling, file post, and more." optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "urllib3-2.0.2-py3-none-any.whl", hash = "sha256:d055c2f9d38dc53c808f6fdc8eab7360b6fdbbde02340ed25cfbcd817c62469e"}, - {file = "urllib3-2.0.2.tar.gz", hash = "sha256:61717a1095d7e155cdb737ac7bb2f4324a858a1e2e6466f6d03ff630ca68d3cc"}, + {file = "urllib3-2.2.2-py3-none-any.whl", hash = "sha256:a448b2f64d686155468037e1ace9f2d2199776e17f0a46610480d311f73e3472"}, + {file = "urllib3-2.2.2.tar.gz", hash = "sha256:dd505485549a7a552833da5e6063639d0d177c04f23bc3864e41e5dc5f612168"}, ] [package.extras] brotli = ["brotli (>=1.0.9)", "brotlicffi (>=0.8.0)"] -secure = ["certifi", "cryptography (>=1.9)", "idna (>=2.0.0)", "pyopenssl (>=17.1.0)", "urllib3-secure-extra"] +h2 = ["h2 (>=4,<5)"] socks = ["pysocks (>=1.5.6,!=1.5.7,<2.0)"] zstd = ["zstandard (>=0.18.0)"] +[[package]] +name = "virtualenv" +version = "20.26.4" +description = "Virtual Python Environment builder" +optional = false +python-versions = ">=3.7" +files = [ + {file = "virtualenv-20.26.4-py3-none-any.whl", hash = "sha256:48f2695d9809277003f30776d155615ffc11328e6a0a8c1f0ec80188d7874a55"}, + {file = "virtualenv-20.26.4.tar.gz", hash = "sha256:c17f4e0f3e6036e9f26700446f85c76ab11df65ff6d8a9cbfad9f71aabfcf23c"}, +] + +[package.dependencies] +distlib = ">=0.3.7,<1" +filelock = ">=3.12.2,<4" +platformdirs = ">=3.9.1,<5" + +[package.extras] +docs = ["furo (>=2023.7.26)", "proselint (>=0.13)", "sphinx (>=7.1.2,!=7.3)", "sphinx-argparse (>=0.4)", "sphinxcontrib-towncrier (>=0.2.1a0)", "towncrier (>=23.6)"] +test = ["covdefaults (>=2.3)", "coverage (>=7.2.7)", "coverage-enable-subprocess (>=1)", "flaky (>=3.7)", "packaging (>=23.1)", "pytest (>=7.4)", "pytest-env (>=0.8.2)", "pytest-freezer (>=0.4.8)", "pytest-mock (>=3.11.1)", "pytest-randomly (>=3.12)", "pytest-timeout (>=2.1)", "setuptools (>=68)", "time-machine (>=2.10)"] + [[package]] name = "wcwidth" -version = "0.2.6" +version = "0.2.13" description = "Measures the displayed width of unicode strings in a terminal" optional = false python-versions = "*" files = [ - {file = "wcwidth-0.2.6-py2.py3-none-any.whl", hash = "sha256:795b138f6875577cd91bba52baf9e445cd5118fd32723b460e30a0af30ea230e"}, - {file = "wcwidth-0.2.6.tar.gz", hash = "sha256:a5220780a404dbe3353789870978e472cfe477761f06ee55077256e509b156d0"}, + {file = "wcwidth-0.2.13-py2.py3-none-any.whl", hash = "sha256:3da69048e4540d84af32131829ff948f1e022c1c6bdb8d6102117aac784f6859"}, + {file = "wcwidth-0.2.13.tar.gz", hash = "sha256:72ea0c06399eb286d978fdedb6923a9eb47e1c486ce63e9b4e64fc18303972b5"}, ] [[package]] name = "wheel" -version = "0.40.0" +version = "0.44.0" description = "A built-package format for Python" optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "wheel-0.40.0-py3-none-any.whl", hash = "sha256:d236b20e7cb522daf2390fa84c55eea81c5c30190f90f29ae2ca1ad8355bf247"}, - {file = "wheel-0.40.0.tar.gz", hash = "sha256:cd1196f3faee2b31968d626e1731c94f99cbdb67cf5a46e4f5656cbee7738873"}, + {file = "wheel-0.44.0-py3-none-any.whl", hash = "sha256:2376a90c98cc337d18623527a97c31797bd02bad0033d41547043a1cbfbe448f"}, + {file = "wheel-0.44.0.tar.gz", hash = "sha256:a29c3f2817e95ab89aa4660681ad547c0e9547f20e75b0562fe7723c9a2a9d49"}, ] [package.extras] -test = ["pytest (>=6.0.0)"] +test = ["pytest (>=6.0.0)", "setuptools (>=65)"] [[package]] name = "zipp" -version = "3.15.0" +version = "3.20.1" description = "Backport of pathlib-compatible object wrapper for zip files" optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "zipp-3.15.0-py3-none-any.whl", hash = "sha256:48904fc76a60e542af151aded95726c1a5c34ed43ab4134b597665c86d7ad556"}, - {file = "zipp-3.15.0.tar.gz", hash = "sha256:112929ad649da941c23de50f356a2b5570c954b65150642bccdd66bf194d224b"}, + {file = "zipp-3.20.1-py3-none-any.whl", hash = "sha256:9960cd8967c8f85a56f920d5d507274e74f9ff813a0ab8889a5b5be2daf44064"}, + {file = "zipp-3.20.1.tar.gz", hash = "sha256:c22b14cc4763c5a5b04134207736c107db42e9d3ef2d9779d465f5f1bcba572b"}, ] [package.extras] -docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] -testing = ["big-O", "flake8 (<5)", "jaraco.functools", "jaraco.itertools", "more-itertools", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)"] +check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1)"] +cover = ["pytest-cov"] +doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] +enabler = ["pytest-enabler (>=2.2)"] +test = ["big-O", "importlib-resources", "jaraco.functools", "jaraco.itertools", "jaraco.test", "more-itertools", "pytest (>=6,!=8.1.*)", "pytest-ignore-flaky"] +type = ["pytest-mypy"] [metadata] lock-version = "2.0" python-versions = "^3.8" -content-hash = "62d9ce9ca1c9f4669c7b40724acfc93968cde31c0460d1d7515d289739dc9464" +content-hash = "6a6d2fe9390a4d7d1ecf808d5f303f2dc1eeb44736827b706a858046f3eea1db" diff --git a/pyproject.toml b/pyproject.toml index 0fe23838..62878b41 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "trusted-firmware-a" -version = "2.10.0" +version = "2.12.0" description = "Trusted Firmware-A (TF-A) Python dependencies." authors = ["Arm Ltd."] license = "BSD-3-Clause" @@ -14,8 +14,13 @@ memory = "memory.memmap:main" [tool.poetry.dependencies] python = "^3.8" +cot-dt2c = {path = "tools/cot_dt2c", develop = true} +tlc = {path = "tools/tlc", develop = true} -[tool.poetry.group.doc.dependencies] +[tool.poetry.group.docs] +optional = true + +[tool.poetry.group.docs.dependencies] sphinx = "^5.3.0" myst-parser = "^0.18.1" sphinxcontrib-plantuml = "^0.24.1" diff --git a/readme.rst b/readme.rst index 148d477a..171b32bf 100644 --- a/readme.rst +++ b/readme.rst @@ -39,7 +39,7 @@ that is available through `trustedfirmware.org`_. .. _Secure Monitor: http://www.arm.com/products/processors/technologies/trustzone/tee-smc.php .. _Power State Coordination Interface (PSCI): PSCI_ .. _PSCI: http://infocenter.arm.com/help/topic/com.arm.doc.den0022d/Power_State_Coordination_Interface_PDD_v1_1_DEN0022D.pdf -.. _Trusted Board Boot Requirements CLIENT (TBBR-CLIENT): https://developer.arm.com/docs/den0006/latest/trusted-board-boot-requirements-client-tbbr-client-armv8-a +.. _Trusted Board Boot Requirements CLIENT (TBBR-CLIENT): https://developer.arm.com/docs/den0006/latest .. _SMC Calling Convention: http://infocenter.arm.com/help/topic/com.arm.doc.den0028b/ARM_DEN0028B_SMC_Calling_Convention.pdf .. _System Control and Management Interface (SCMI): SCMI_ .. _SCMI: http://infocenter.arm.com/help/topic/com.arm.doc.den0056a/DEN0056A_System_Control_and_Management_Interface.pdf diff --git a/services/arm_arch_svc/arm_arch_svc_setup.c b/services/arm_arch_svc/arm_arch_svc_setup.c index 57d211ed..54561646 100644 --- a/services/arm_arch_svc/arm_arch_svc_setup.c +++ b/services/arm_arch_svc/arm_arch_svc_setup.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018-2023, Arm Limited and Contributors. All rights reserved. + * Copyright (c) 2018-2024, Arm Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -54,7 +54,7 @@ static int32_t smccc_arch_features(u_register_t arg1) * If architectural SSBS is available on this PE, no firmware * mitigation via SMCCC_ARCH_WORKAROUND_2 is required. */ - if (ssbs != SSBS_UNAVAILABLE) + if (ssbs != SSBS_NOT_IMPLEMENTED) return 1; /* diff --git a/services/el3/ven_el3_svc.c b/services/el3/ven_el3_svc.c new file mode 100644 index 00000000..32a3dc27 --- /dev/null +++ b/services/el3/ven_el3_svc.c @@ -0,0 +1,97 @@ +/* + * Copyright (c) 2024, Arm Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <stdint.h> + +#include <common/debug.h> +#include <common/runtime_svc.h> +#include <lib/debugfs.h> +#include <lib/pmf/pmf.h> +#include <services/ven_el3_svc.h> +#include <tools_share/uuid.h> + +/* vendor-specific EL3 UUID */ +DEFINE_SVC_UUID2(ven_el3_svc_uid, + 0xb6011dca, 0x57c4, 0x407e, 0x83, 0xf0, + 0xa7, 0xed, 0xda, 0xf0, 0xdf, 0x6c); + +static int ven_el3_svc_setup(void) +{ +#if USE_DEBUGFS + if (debugfs_smc_setup() != 0) { + return 1; + } +#endif /* USE_DEBUGFS */ + +#if ENABLE_PMF + if (pmf_setup() != 0) { + return 1; + } +#endif /* ENABLE_PMF */ + + return 0; +} + +/* + * This function handles Arm defined vendor-specific EL3 Service Calls. + */ +static uintptr_t ven_el3_svc_handler(unsigned int smc_fid, + u_register_t x1, + u_register_t x2, + u_register_t x3, + u_register_t x4, + void *cookie, + void *handle, + u_register_t flags) +{ +#if USE_DEBUGFS + /* + * Dispatch debugfs calls to debugfs SMC handler and return its + * return value. + */ + if (is_debugfs_fid(smc_fid)) { + return debugfs_smc_handler(smc_fid, x1, x2, x3, x4, cookie, + handle, flags); + } +#endif /* USE_DEBUGFS */ + +#if ENABLE_PMF + + /* + * Dispatch PMF calls to PMF SMC handler and return its return + * value + */ + if (is_pmf_fid(smc_fid)) { + return pmf_smc_handler(smc_fid, x1, x2, x3, x4, cookie, + handle, flags); + } + +#endif /* ENABLE_PMF */ + + switch (smc_fid) { + case VEN_EL3_SVC_UID: + /* Return UID to the caller */ + SMC_UUID_RET(handle, ven_el3_svc_uid); + break; + case VEN_EL3_SVC_VERSION: + SMC_RET2(handle, VEN_EL3_SVC_VERSION_MAJOR, VEN_EL3_SVC_VERSION_MINOR); + break; + default: + WARN("Unimplemented vendor-specific EL3 Service call: 0x%x\n", smc_fid); + SMC_RET1(handle, SMC_UNK); + break; + } +} + +/* Define a runtime service descriptor for fast SMC calls */ +DECLARE_RT_SVC( + ven_el3_svc, + OEN_VEN_EL3_START, + OEN_VEN_EL3_END, + SMC_TYPE_FAST, + ven_el3_svc_setup, + ven_el3_svc_handler +); diff --git a/services/oem/chromeos/widevine_smc_handlers.c b/services/oem/chromeos/widevine_smc_handlers.c new file mode 100644 index 00000000..83c6ccc9 --- /dev/null +++ b/services/oem/chromeos/widevine_smc_handlers.c @@ -0,0 +1,98 @@ +/* + * Copyright (c) 2024, The ChromiumOS Authors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <assert.h> +#include <stdint.h> +#include <string.h> + +#include <common/debug.h> +#include <common/runtime_svc.h> +#include <lib/psci/psci.h> +#include <lib/xlat_tables/xlat_tables_v2.h> +#include <services/oem/chromeos/widevine_smc_handlers.h> +#include <tools_share/uuid.h> + +#define CROS_OEM_TPM_AUTH_PK_MAX_LEN 128 +#define CROS_OEM_HUK_LEN 32 +#define CROS_OEM_ROT_LEN 32 + +static uint8_t cros_oem_tpm_auth_pk_buffer[CROS_OEM_TPM_AUTH_PK_MAX_LEN]; +static uint8_t cros_oem_huk_buffer[CROS_OEM_HUK_LEN]; +static uint8_t cros_oem_rot_len_buffer[CROS_OEM_ROT_LEN]; + +struct cros_oem_data cros_oem_tpm_auth_pk = { + .buffer = cros_oem_tpm_auth_pk_buffer, + .max_length = sizeof(cros_oem_tpm_auth_pk_buffer), +}; + +struct cros_oem_data cros_oem_huk = { + .buffer = cros_oem_huk_buffer, + .max_length = sizeof(cros_oem_huk_buffer), +}; + +struct cros_oem_data cros_oem_rot = { + .buffer = cros_oem_rot_len_buffer, + .max_length = sizeof(cros_oem_rot_len_buffer), +}; + +static uintptr_t cros_write_data(struct cros_oem_data *data, + u_register_t length, u_register_t address, + void *handle) +{ + uintptr_t aligned_address; + uintptr_t aligned_size; + int32_t rc; + + if (data->length) { + SMC_RET1(handle, PSCI_E_ALREADY_ON); + } + + if (length > data->max_length) { + SMC_RET1(handle, PSCI_E_INVALID_PARAMS); + } + + aligned_address = page_align(address, DOWN); + aligned_size = page_align(length + (address - aligned_address), UP); + + /* + * We do not validate the passed in address because we are trusting the + * non-secure world at this point still. + */ + rc = mmap_add_dynamic_region(aligned_address, aligned_address, + aligned_size, MT_MEMORY | MT_RO | MT_NS); + if (rc != 0) { + SMC_RET1(handle, PSCI_E_INVALID_ADDRESS); + } + + memcpy(data->buffer, (void *)address, length); + data->length = length; + + mmap_remove_dynamic_region(aligned_address, aligned_size); + SMC_RET1(handle, SMC_OK); +} + +/* Handler for servicing specific SMC calls. */ +static uintptr_t cros_oem_svc_smc_handler(uint32_t smc_fid, u_register_t x1, + u_register_t x2, u_register_t x3, + u_register_t x4, void *cookie, + void *handle, u_register_t flags) +{ + switch (smc_fid) { + case CROS_OEM_SMC_DRM_SET_TPM_AUTH_PUB_FUNC_ID: + return cros_write_data(&cros_oem_tpm_auth_pk, x1, x2, handle); + case CROS_OEM_SMC_DRM_SET_HARDWARE_UNIQUE_KEY_FUNC_ID: + return cros_write_data(&cros_oem_huk, x1, x2, handle); + case CROS_OEM_SMC_DRM_SET_ROOT_OF_TRUST_FUNC_ID: + return cros_write_data(&cros_oem_rot, x1, x2, handle); + default: + WARN("Unimplemented OEM Call: 0x%x\n", smc_fid); + SMC_RET1(handle, SMC_UNK); + } +} + +/* Register OEM Service Calls as runtime service */ +DECLARE_RT_SVC(cros_oem_svc_smc_handler, OEN_OEM_START, OEN_OEM_END, + SMC_TYPE_FAST, NULL, cros_oem_svc_smc_handler); diff --git a/services/spd/opteed/opteed.mk b/services/spd/opteed/opteed.mk index f394744e..289b3e77 100644 --- a/services/spd/opteed/opteed.mk +++ b/services/spd/opteed/opteed.mk @@ -33,3 +33,11 @@ $(eval $(call add_define,PLAT_XLAT_TABLES_DYNAMIC)) $(eval $(call add_define,OPTEE_ALLOW_SMC_LOAD)) include lib/libfdt/libfdt.mk endif + +CROS_WIDEVINE_SMC := 0 +ifeq ($(CROS_WIDEVINE_SMC),1) +ifeq ($(OPTEE_ALLOW_SMC_LOAD),0) +$(error When CROS_WIDEVINE_SMC=1, OPTEE_ALLOW_SMC_LOAD must also be 1) +endif +$(eval $(call add_define,CROS_WIDEVINE_SMC)) +endif diff --git a/services/spd/opteed/opteed_common.c b/services/spd/opteed/opteed_common.c index 9aa19c5b..8a769fb0 100644 --- a/services/spd/opteed/opteed_common.c +++ b/services/spd/opteed/opteed_common.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013-2017, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2013-2023, Arm Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -20,9 +20,9 @@ * initialize OPTEE context and entry point info for OPTEE. ******************************************************************************/ void opteed_init_optee_ep_state(struct entry_point_info *optee_entry_point, - uint32_t rw, uint64_t pc, - uint64_t pageable_part, uint64_t mem_limit, - uint64_t dt_addr, optee_context_t *optee_ctx) + uint32_t rw, uint64_t pc, uint64_t arg0, + uint64_t arg1, uint64_t arg2, uint64_t arg3, + optee_context_t *optee_ctx) { uint32_t ep_attr; @@ -54,9 +54,10 @@ void opteed_init_optee_ep_state(struct entry_point_info *optee_entry_point, DAIF_IRQ_BIT | DAIF_ABT_BIT); zeromem(&optee_entry_point->args, sizeof(optee_entry_point->args)); - optee_entry_point->args.arg0 = pageable_part; - optee_entry_point->args.arg1 = mem_limit; - optee_entry_point->args.arg2 = dt_addr; + optee_entry_point->args.arg0 = arg0; + optee_entry_point->args.arg1 = arg1; + optee_entry_point->args.arg2 = arg2; + optee_entry_point->args.arg3 = arg3; } /******************************************************************************* diff --git a/services/spd/opteed/opteed_main.c b/services/spd/opteed/opteed_main.c index 4d055db1..9e838484 100644 --- a/services/spd/opteed/opteed_main.c +++ b/services/spd/opteed/opteed_main.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013-2023, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2013-2023, Arm Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -27,16 +27,22 @@ #include <lib/coreboot.h> #include <lib/el3_runtime/context_mgmt.h> #include <lib/optee_utils.h> +#include <lib/transfer_list.h> #include <lib/xlat_tables/xlat_tables_v2.h> #if OPTEE_ALLOW_SMC_LOAD #include <libfdt.h> #endif /* OPTEE_ALLOW_SMC_LOAD */ #include <plat/common/platform.h> +#include <services/oem/chromeos/widevine_smc_handlers.h> #include <tools_share/uuid.h> #include "opteed_private.h" #include "teesmc_opteed.h" +#if OPTEE_ALLOW_SMC_LOAD +static struct transfer_list_header *bl31_tl; +#endif + /******************************************************************************* * Address of the entrypoint vector table in OPTEE. It is * initialised once on the primary core after a cold boot. @@ -56,7 +62,7 @@ DEFINE_SVC_UUID2(optee_image_load_uuid, 0xb1eafba3, 0x5d31, 0x4612, 0xb9, 0x06, 0xc4, 0xc7, 0xa4, 0xbe, 0x3c, 0xc0); -#define OPTEED_FDT_SIZE 256 +#define OPTEED_FDT_SIZE 1024 static uint8_t fdt_buf[OPTEED_FDT_SIZE] __aligned(CACHE_WRITEBACK_GRANULE); #else @@ -81,6 +87,13 @@ static uint64_t opteed_sel1_interrupt_handler(uint32_t id, uint32_t linear_id; optee_context_t *optee_ctx; +#if OPTEE_ALLOW_SMC_LOAD + if (optee_vector_table == NULL) { + /* OPTEE is not loaded yet, ignore this interrupt */ + SMC_RET0(handle); + } +#endif + /* Check the security state when the exception was generated */ assert(get_interrupt_src_ss(flags) == NON_SECURE); @@ -109,6 +122,24 @@ static uint64_t opteed_sel1_interrupt_handler(uint32_t id, SMC_RET1(&optee_ctx->cpu_ctx, read_elr_el3()); } +/* + * Registers an interrupt handler for S-EL1 interrupts when generated during + * code executing in the non-secure state. Panics if it fails to do so. + */ +static void register_opteed_interrupt_handler(void) +{ + u_register_t flags; + uint64_t rc; + + flags = 0; + set_interrupt_rm_flag(flags, NON_SECURE); + rc = register_interrupt_type_handler(INTR_TYPE_S_EL1, + opteed_sel1_interrupt_handler, + flags); + if (rc) + panic(); +} + /******************************************************************************* * OPTEE Dispatcher setup. The OPTEED finds out the OPTEE entrypoint and type * (aarch32/aarch64) if not already known and initialises the context for entry @@ -119,13 +150,22 @@ static int32_t opteed_setup(void) #if OPTEE_ALLOW_SMC_LOAD opteed_allow_load = true; INFO("Delaying OP-TEE setup until we receive an SMC call to load it\n"); + /* + * We must register the interrupt handler now so that the interrupt + * priorities are not changed after starting the linux kernel. + */ + register_opteed_interrupt_handler(); return 0; #else entry_point_info_t *optee_ep_info; uint32_t linear_id; - uint64_t opteed_pageable_part; - uint64_t opteed_mem_limit; - uint64_t dt_addr; + uint64_t arg0; + uint64_t arg1; + uint64_t arg2; + uint64_t arg3; + struct transfer_list_header *tl = NULL; + struct transfer_list_entry *te = NULL; + void *dt = NULL; linear_id = plat_my_core_pos(); @@ -150,17 +190,39 @@ static int32_t opteed_setup(void) if (!optee_ep_info->pc) return 1; - opteed_rw = optee_ep_info->args.arg0; - opteed_pageable_part = optee_ep_info->args.arg1; - opteed_mem_limit = optee_ep_info->args.arg2; - dt_addr = optee_ep_info->args.arg3; - - opteed_init_optee_ep_state(optee_ep_info, - opteed_rw, - optee_ep_info->pc, - opteed_pageable_part, - opteed_mem_limit, - dt_addr, + if (TRANSFER_LIST && + optee_ep_info->args.arg1 == (TRANSFER_LIST_SIGNATURE | + REGISTER_CONVENTION_VERSION_MASK)) { + tl = (void *)optee_ep_info->args.arg3; + if (transfer_list_check_header(tl) == TL_OPS_NON) { + return 1; + } + + opteed_rw = GET_RW(optee_ep_info->spsr); + te = transfer_list_find(tl, TL_TAG_FDT); + dt = transfer_list_entry_data(te); + + if (opteed_rw == OPTEE_AARCH64) { + arg0 = (uint64_t)dt; + arg2 = 0; + } else { + arg2 = (uint64_t)dt; + arg0 = 0; + } + + arg1 = optee_ep_info->args.arg1; + arg3 = optee_ep_info->args.arg3; + } else { + /* Default handoff arguments */ + opteed_rw = optee_ep_info->args.arg0; + arg0 = optee_ep_info->args.arg1; /* opteed_pageable_part */ + arg1 = optee_ep_info->args.arg2; /* opteed_mem_limit */ + arg2 = optee_ep_info->args.arg3; /* dt_addr */ + arg3 = 0; + } + + opteed_init_optee_ep_state(optee_ep_info, opteed_rw, optee_ep_info->pc, + arg0, arg1, arg2, arg3, &opteed_sp_context[linear_id]); /* @@ -268,6 +330,62 @@ static int add_coreboot_node(void *fdt) } #endif /* COREBOOT */ +#if CROS_WIDEVINE_SMC +/* + * Adds a options/widevine node with the widevine table information to a device + * tree. Returns zero on success or if there is no widevine table information; + * failure code otherwise. + */ +static int add_options_widevine_node(void *fdt) +{ + int ret; + + ret = fdt_begin_node(fdt, "options"); + if (ret) + return ret; + + ret = fdt_begin_node(fdt, "op-tee"); + if (ret) + return ret; + + ret = fdt_begin_node(fdt, "widevine"); + if (ret) + return ret; + + if (cros_oem_tpm_auth_pk.length) { + ret = fdt_property(fdt, "tcg,tpm-auth-public-key", + cros_oem_tpm_auth_pk.buffer, + cros_oem_tpm_auth_pk.length); + if (ret) + return ret; + } + + if (cros_oem_huk.length) { + ret = fdt_property(fdt, "op-tee,hardware-unique-key", + cros_oem_huk.buffer, cros_oem_huk.length); + if (ret) + return ret; + } + + if (cros_oem_rot.length) { + ret = fdt_property(fdt, "google,widevine-root-of-trust-ecc-p256", + cros_oem_rot.buffer, cros_oem_rot.length); + if (ret) + return ret; + } + + ret = fdt_end_node(fdt); + if (ret) + return ret; + + ret = fdt_end_node(fdt); + if (ret) + return ret; + + return fdt_end_node(fdt); +} +#endif /* CROS_WIDEVINE_SMC */ + /* * Creates a device tree for passing into OP-TEE. Currently is populated with * the coreboot table address. @@ -295,6 +413,12 @@ static int create_opteed_dt(void) return ret; #endif /* COREBOOT */ +#if CROS_WIDEVINE_SMC + ret = add_options_widevine_node(fdt_buf); + if (ret) + return ret; +#endif /* CROS_WIDEVINE_SMC */ + ret = fdt_end_node(fdt_buf); if (ret) return ret; @@ -302,6 +426,26 @@ static int create_opteed_dt(void) return fdt_finish(fdt_buf); } +static int32_t create_smc_tl(const void *fdt, uint32_t fdt_sz) +{ +#if TRANSFER_LIST + bl31_tl = transfer_list_init((void *)(uintptr_t)FW_HANDOFF_BASE, + FW_HANDOFF_SIZE); + if (!bl31_tl) { + ERROR("Failed to initialize Transfer List at 0x%lx\n", + (unsigned long)FW_HANDOFF_BASE); + return -1; + } + + if (!transfer_list_add(bl31_tl, TL_TAG_FDT, fdt_sz, fdt)) { + return -1; + } + return 0; +#else + return -1; +#endif +} + /******************************************************************************* * This function is responsible for handling the SMC that loads the OP-TEE * binary image via a non-secure SMC call. It takes the size and physical @@ -326,6 +470,10 @@ static int32_t opteed_handle_smc_load(uint64_t data_size, uint32_t data_pa) entry_point_info_t optee_ep_info; uint32_t linear_id = plat_my_core_pos(); uint64_t dt_addr = 0; + uint64_t arg0 = 0; + uint64_t arg1 = 0; + uint64_t arg2 = 0; + uint64_t arg3 = 0; mapped_data_pa = page_align(data_pa, DOWN); mapped_data_va = mapped_data_pa; @@ -394,12 +542,37 @@ static int32_t opteed_handle_smc_load(uint64_t data_size, uint32_t data_pa) dt_addr = (uint64_t)fdt_buf; flush_dcache_range(dt_addr, OPTEED_FDT_SIZE); + if (TRANSFER_LIST && + !create_smc_tl((void *)dt_addr, OPTEED_FDT_SIZE)) { + struct transfer_list_entry *te = NULL; + void *dt = NULL; + + te = transfer_list_find(bl31_tl, TL_TAG_FDT); + dt = transfer_list_entry_data(te); + + if (opteed_rw == OPTEE_AARCH64) { + arg0 = (uint64_t)dt; + arg1 = TRANSFER_LIST_HANDOFF_X1_VALUE(REGISTER_CONVENTION_VERSION); + arg2 = 0; + } else { + arg0 = 0; + arg1 = TRANSFER_LIST_HANDOFF_R1_VALUE(REGISTER_CONVENTION_VERSION); + arg2 = (uint64_t)dt; + } + + arg3 = (uint64_t)bl31_tl; + } else { + /* Default handoff arguments */ + arg2 = dt_addr; + } + opteed_init_optee_ep_state(&optee_ep_info, opteed_rw, image_pa, - 0, - 0, - dt_addr, + arg0, + arg1, + arg2, + arg3, &opteed_sp_context[linear_id]); if (opteed_init_with_entry_point(&optee_ep_info) == 0) { rc = -EFAULT; @@ -433,7 +606,6 @@ static uintptr_t opteed_smc_handler(uint32_t smc_fid, cpu_context_t *ns_cpu_context; uint32_t linear_id = plat_my_core_pos(); optee_context_t *optee_ctx = &opteed_sp_context[linear_id]; - uint64_t rc; /* * Determine which security state this SMC originated from @@ -567,18 +739,9 @@ static uintptr_t opteed_smc_handler(uint32_t smc_fid, */ psci_register_spd_pm_hook(&opteed_pm); - /* - * Register an interrupt handler for S-EL1 interrupts - * when generated during code executing in the - * non-secure state. - */ - flags = 0; - set_interrupt_rm_flag(flags, NON_SECURE); - rc = register_interrupt_type_handler(INTR_TYPE_S_EL1, - opteed_sel1_interrupt_handler, - flags); - if (rc) - panic(); +#if !OPTEE_ALLOW_SMC_LOAD + register_opteed_interrupt_handler(); +#endif } /* diff --git a/services/spd/opteed/opteed_pm.c b/services/spd/opteed/opteed_pm.c index fa724a11..c949823e 100644 --- a/services/spd/opteed/opteed_pm.c +++ b/services/spd/opteed/opteed_pm.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013-2023, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2013-2023, Arm Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -113,7 +113,7 @@ void opteed_cpu_on_finish_handler(u_register_t unused) opteed_init_optee_ep_state(&optee_on_entrypoint, opteed_rw, (uint64_t)&optee_vector_table->cpu_on_entry, - 0, 0, 0, optee_ctx); + 0, 0, 0, 0, optee_ctx); /* Initialise this cpu's secure context */ cm_init_my_context(&optee_on_entrypoint); diff --git a/services/spd/opteed/opteed_private.h b/services/spd/opteed/opteed_private.h index c8fbc221..c484516d 100644 --- a/services/spd/opteed/opteed_private.h +++ b/services/spd/opteed/opteed_private.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013-2023, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2013-2023, Arm Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -148,11 +148,8 @@ void __dead2 opteed_exit_sp(uint64_t c_rt_ctx, uint64_t ret); uint64_t opteed_synchronous_sp_entry(optee_context_t *optee_ctx); void __dead2 opteed_synchronous_sp_exit(optee_context_t *optee_ctx, uint64_t ret); void opteed_init_optee_ep_state(struct entry_point_info *optee_entry_point, - uint32_t rw, - uint64_t pc, - uint64_t pageable_part, - uint64_t mem_limit, - uint64_t dt_addr, + uint32_t rw, uint64_t pc, uint64_t arg0, + uint64_t arg1, uint64_t arg2, uint64_t arg3, optee_context_t *optee_ctx); void opteed_cpu_on_finish_handler(u_register_t unused); diff --git a/services/spd/pncd/pncd_common.c b/services/spd/pncd/pncd_common.c index 6fdb6293..8e89491c 100644 --- a/services/spd/pncd/pncd_common.c +++ b/services/spd/pncd/pncd_common.c @@ -67,8 +67,9 @@ uint64_t pncd_synchronous_sp_entry(pnc_context_t *pnc_ctx) /* Apply the Secure EL1 system register context and switch to it */ assert(cm_get_context(SECURE) == &pnc_ctx->cpu_ctx); cm_el1_sysregs_context_restore(SECURE); + #if CTX_INCLUDE_FPREGS - fpregs_context_restore(get_fpregs_ctx(cm_get_context(SECURE))); + simd_ctx_restore(SECURE); #endif cm_set_next_eret_context(SECURE); @@ -90,8 +91,9 @@ void pncd_synchronous_sp_exit(pnc_context_t *pnc_ctx, uint64_t ret) /* Save the Secure EL1 system register context */ assert(cm_get_context(SECURE) == &pnc_ctx->cpu_ctx); cm_el1_sysregs_context_save(SECURE); + #if CTX_INCLUDE_FPREGS - fpregs_context_save(get_fpregs_ctx(cm_get_context(SECURE))); + simd_ctx_save(SECURE, false); #endif assert(pnc_ctx->c_rt_ctx != 0); diff --git a/services/spd/pncd/pncd_main.c b/services/spd/pncd/pncd_main.c index 99c4aa1b..cc1c1f28 100644 --- a/services/spd/pncd/pncd_main.c +++ b/services/spd/pncd/pncd_main.c @@ -55,8 +55,9 @@ static void context_save(unsigned long security_state) assert(sec_state_is_valid(security_state)); cm_el1_sysregs_context_save((uint32_t) security_state); + #if CTX_INCLUDE_FPREGS - fpregs_context_save(get_fpregs_ctx(cm_get_context(security_state))); + simd_ctx_save((uint32_t)security_state, false); #endif } @@ -72,8 +73,9 @@ static void *context_restore(unsigned long security_state) /* Restore state */ cm_el1_sysregs_context_restore((uint32_t) security_state); + #if CTX_INCLUDE_FPREGS - fpregs_context_restore(get_fpregs_ctx(cm_get_context(security_state))); + simd_ctx_restore((uint32_t)security_state); #endif cm_set_next_eret_context((uint32_t) security_state); diff --git a/services/spd/tlkd/tlkd.mk b/services/spd/tlkd/tlkd.mk index 56de0a64..fc8840d6 100644 --- a/services/spd/tlkd/tlkd.mk +++ b/services/spd/tlkd/tlkd.mk @@ -1,5 +1,5 @@ # -# Copyright (c) 2015, ARM Limited and Contributors. All rights reserved. +# Copyright (c) 2024, ARM Limited and Contributors. All rights reserved. # # SPDX-License-Identifier: BSD-3-Clause # @@ -8,7 +8,9 @@ ifeq (${ERROR_DEPRECATED},0) SPD_INCLUDES := -Iinclude/bl32/payloads endif +ifeq (${ENABLE_FEAT_D128}, 0) SPD_SOURCES := services/spd/tlkd/tlkd_common.c \ services/spd/tlkd/tlkd_helpers.S \ services/spd/tlkd/tlkd_main.c \ services/spd/tlkd/tlkd_pm.c +endif \ No newline at end of file diff --git a/services/spd/trusty/trusty.c b/services/spd/trusty/trusty.c index 7daebcdd..aae2d9a0 100644 --- a/services/spd/trusty/trusty.c +++ b/services/spd/trusty/trusty.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016-2019, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2016-2024, Arm Limited and Contributors. All rights reserved. * Copyright (c) 2020, NVIDIA Corporation. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause @@ -118,8 +118,10 @@ static struct smc_args trusty_context_switch(uint32_t security_state, uint64_t r * when it's needed the PSCI caller has preserved FP context before * going here. */ - if (r0 != SMC_FC_CPU_SUSPEND && r0 != SMC_FC_CPU_RESUME) - fpregs_context_save(get_fpregs_ctx(cm_get_context(security_state))); + if (r0 != SMC_FC_CPU_SUSPEND && r0 != SMC_FC_CPU_RESUME) { + simd_ctx_save(security_state, false); + } + cm_el1_sysregs_context_save(security_state); ctx->saved_security_state = security_state; @@ -128,8 +130,9 @@ static struct smc_args trusty_context_switch(uint32_t security_state, uint64_t r assert(ctx->saved_security_state == ((security_state == 0U) ? 1U : 0U)); cm_el1_sysregs_context_restore(security_state); - if (r0 != SMC_FC_CPU_SUSPEND && r0 != SMC_FC_CPU_RESUME) - fpregs_context_restore(get_fpregs_ctx(cm_get_context(security_state))); + if (r0 != SMC_FC_CPU_SUSPEND && r0 != SMC_FC_CPU_RESUME) { + simd_ctx_restore(security_state); + } cm_set_next_eret_context(security_state); @@ -160,9 +163,9 @@ static uint64_t trusty_fiq_handler(uint32_t id, (void)memcpy(&ctx->fiq_gpregs, get_gpregs_ctx(handle), sizeof(ctx->fiq_gpregs)); ctx->fiq_pc = SMC_GET_EL3(handle, CTX_ELR_EL3); ctx->fiq_cpsr = SMC_GET_EL3(handle, CTX_SPSR_EL3); - ctx->fiq_sp_el1 = read_ctx_reg(get_el1_sysregs_ctx(handle), CTX_SP_EL1); + ctx->fiq_sp_el1 = read_el1_ctx_common(get_el1_sysregs_ctx(handle), sp_el1); - write_ctx_reg(get_el1_sysregs_ctx(handle), CTX_SP_EL1, ctx->fiq_handler_sp); + write_el1_ctx_common(get_el1_sysregs_ctx(handle), sp_el1, ctx->fiq_handler_sp); cm_set_elr_spsr_el3(NON_SECURE, ctx->fiq_handler_pc, (uint32_t)ctx->fiq_handler_cpsr); SMC_RET0(handle); @@ -221,7 +224,7 @@ static uint64_t trusty_fiq_exit(void *handle, uint64_t x1, uint64_t x2, uint64_t */ (void)memcpy(get_gpregs_ctx(handle), &ctx->fiq_gpregs, sizeof(ctx->fiq_gpregs)); ctx->fiq_handler_active = 0; - write_ctx_reg(get_el1_sysregs_ctx(handle), CTX_SP_EL1, ctx->fiq_sp_el1); + write_el1_ctx_common(get_el1_sysregs_ctx(handle), sp_el1, ctx->fiq_sp_el1); cm_set_elr_spsr_el3(NON_SECURE, ctx->fiq_pc, (uint32_t)ctx->fiq_cpsr); SMC_RET0(handle); @@ -320,7 +323,7 @@ static int32_t trusty_init(void) ep_info = bl31_plat_get_next_image_ep_info(SECURE); assert(ep_info != NULL); - fpregs_context_save(get_fpregs_ctx(cm_get_context(NON_SECURE))); + simd_ctx_save(NON_SECURE, false); cm_el1_sysregs_context_save(NON_SECURE); cm_set_context(&ctx->cpu_ctx, SECURE); @@ -337,7 +340,7 @@ static int32_t trusty_init(void) } cm_el1_sysregs_context_restore(SECURE); - fpregs_context_restore(get_fpregs_ctx(cm_get_context(SECURE))); + simd_ctx_restore(SECURE); cm_set_next_eret_context(SECURE); ctx->saved_security_state = ~0U; /* initial saved state is invalid */ @@ -346,7 +349,7 @@ static int32_t trusty_init(void) (void)trusty_context_switch_helper(&ctx->saved_sp, &zero_args); cm_el1_sysregs_context_restore(NON_SECURE); - fpregs_context_restore(get_fpregs_ctx(cm_get_context(NON_SECURE))); + simd_ctx_restore(NON_SECURE); cm_set_next_eret_context(NON_SECURE); return 1; diff --git a/services/spd/tspd/tspd_main.c b/services/spd/tspd/tspd_main.c index 6cb4992a..8ff71cc8 100644 --- a/services/spd/tspd/tspd_main.c +++ b/services/spd/tspd/tspd_main.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013-2022, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2013-2024, ARM Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -575,6 +575,11 @@ static uintptr_t tspd_smc_handler(uint32_t smc_fid, * of the DIT PSTATE bit. */ case TSP_YIELD_FID(TSP_CHECK_DIT): + /* + * Request from non-secure client to modify the EL1 + * context registers. + */ + case TSP_YIELD_FID(TSP_MODIFY_EL1_CTX): if (ns) { /* * This is a fresh request from the non-secure client. diff --git a/services/std_svc/drtm/drtm_main.c b/services/std_svc/drtm/drtm_main.c index 3acf6838..8d27e96d 100644 --- a/services/std_svc/drtm/drtm_main.c +++ b/services/std_svc/drtm/drtm_main.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022 Arm Limited. All rights reserved. + * Copyright (c) 2022-2024 Arm Limited. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause * @@ -211,7 +211,7 @@ static enum drtm_retc drtm_dl_check_cores(void) running_on_single_core = psci_is_last_on_cpu_safe(); if (!running_on_single_core) { ERROR("DRTM: invalid launch due to non-boot PE not being turned off\n"); - return DENIED; + return SECONDARY_PE_NOT_OFF; } return SUCCESS; @@ -463,7 +463,7 @@ static enum drtm_retc drtm_dl_check_args(uint64_t x1, * is required to avoid / defend against racing with cache evictions */ va_mapping_size = ALIGNED_UP((dlme_end - dlme_start), DRTM_PAGE_SIZE); - rc = mmap_add_dynamic_region_alloc_va(dlme_img_start, &va_mapping, va_mapping_size, + rc = mmap_add_dynamic_region_alloc_va(dlme_start, &va_mapping, va_mapping_size, MT_MEMORY | MT_NS | MT_RO | MT_SHAREABILITY_ISH); if (rc != 0) { @@ -512,10 +512,10 @@ static void drtm_dl_reset_dlme_el_state(enum drtm_dlme_el dlme_el) sctlr &= ~(/* Disable DLME's EL MMU, since the existing page-tables are untrusted. */ SCTLR_M_BIT | SCTLR_EE_BIT /* Little-endian data accesses. */ + | SCTLR_C_BIT /* disable data caching */ + | SCTLR_I_BIT /* disable instruction caching */ ); - sctlr |= SCTLR_C_BIT | SCTLR_I_BIT; /* Allow instruction and data caching. */ - switch (dlme_el) { case DLME_AT_EL1: write_sctlr_el1(sctlr); @@ -655,10 +655,14 @@ static uint64_t drtm_dynamic_launch(uint64_t x1, void *handle) drtm_dl_reset_dlme_el_state(dlme_el); drtm_dl_reset_dlme_context(dlme_el); + /* + * Setting the Generic Timer frequency is required before launching + * DLME and is already done for running CPU during PSCI setup. + */ drtm_dl_prepare_eret_to_dlme(&args, dlme_el); /* - * As per DRTM beta0 spec table #28 invalidate the instruction cache + * As per DRTM 1.0 spec table #30 invalidate the instruction cache * before jumping to the DLME. This is required to defend against * potentially-malicious cache contents. */ @@ -808,12 +812,12 @@ uint64_t drtm_smc_handler(uint32_t smc_fid, case ARM_DRTM_SVC_GET_ERROR: INFO("DRTM service handler: get error\n"); - drtm_get_error(handle); + return drtm_get_error(handle); break; /* not reached */ case ARM_DRTM_SVC_SET_ERROR: INFO("DRTM service handler: set error\n"); - drtm_set_error(x1, handle); + return drtm_set_error(x1, handle); break; /* not reached */ case ARM_DRTM_SVC_SET_TCB_HASH: diff --git a/services/std_svc/drtm/drtm_main.h b/services/std_svc/drtm/drtm_main.h index 60051632..a7d053f1 100644 --- a/services/std_svc/drtm/drtm_main.h +++ b/services/std_svc/drtm/drtm_main.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022 Arm Limited. All rights reserved. + * Copyright (c) 2022-2024 Arm Limited. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause * @@ -55,6 +55,12 @@ enum drtm_retc { NOT_FOUND = -4, INTERNAL_ERROR = -5, MEM_PROTECT_INVALID = -6, + COPROCESSOR_ERROR = -7, + OUT_OF_RESOURCE = -8, + INVALID_DATA = -9, + SECONDARY_PE_NOT_OFF = -10, + ALREADY_CLOSED = -11, + TPM_ERROR = -12 }; typedef struct { @@ -89,6 +95,7 @@ struct __packed dlme_data_header_v1 { uint64_t dlme_addr_map_size; uint64_t dlme_tpm_log_size; uint64_t dlme_tcb_hashes_table_size; + uint64_t dlme_acpi_tables_region_size; uint64_t dlme_impdef_region_size; } __aligned(__alignof(uint16_t /* First member's type, `uint16_t version'. */)); diff --git a/services/std_svc/drtm/drtm_remediation.c b/services/std_svc/drtm/drtm_remediation.c index 696b4ea6..81d27ec2 100644 --- a/services/std_svc/drtm/drtm_remediation.c +++ b/services/std_svc/drtm/drtm_remediation.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022 Arm Limited. All rights reserved. + * Copyright (c) 2022-2024 Arm Limited. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause * @@ -21,7 +21,7 @@ uint64_t drtm_set_error(uint64_t x1, void *ctx) rc = plat_set_drtm_error(x1); if (rc != 0) { - SMC_RET1(ctx, INTERNAL_ERROR); + SMC_RET1(ctx, NOT_FOUND); } SMC_RET1(ctx, SUCCESS); @@ -35,7 +35,7 @@ uint64_t drtm_get_error(void *ctx) rc = plat_get_drtm_error(&error_code); if (rc != 0) { - SMC_RET1(ctx, INTERNAL_ERROR); + SMC_RET1(ctx, NOT_FOUND); } SMC_RET2(ctx, SUCCESS, error_code); diff --git a/services/std_svc/errata_abi/cpu_errata_info.h b/services/std_svc/errata_abi/cpu_errata_info.h index e24a6217..d6884319 100644 --- a/services/std_svc/errata_abi/cpu_errata_info.h +++ b/services/std_svc/errata_abi/cpu_errata_info.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023, Arm Limited and Contributors. All rights reserved. + * Copyright (c) 2023-2024, Arm Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -8,46 +8,29 @@ #define ERRATA_CPUSPEC_H #include <stdint.h> +#include <arch.h> #include <arch_helpers.h> #if __aarch64__ -#include <cortex_a35.h> -#include <cortex_a510.h> -#include <cortex_a53.h> -#include <cortex_a57.h> -#include <cortex_a55.h> #include <cortex_a710.h> -#include <cortex_a72.h> -#include <cortex_a73.h> -#include <cortex_a75.h> -#include <cortex_a76.h> -#include <cortex_a77.h> #include <cortex_a78.h> #include <cortex_a78_ae.h> #include <cortex_a78c.h> -#include <cortex_a715.h> -#include <cortex_x1.h> #include <cortex_x2.h> #include <cortex_x3.h> -#include <neoverse_n1.h> +#include <cortex_x4.h> #include <neoverse_n2.h> #include <neoverse_v1.h> #include <neoverse_v2.h> -#else -#include <cortex_a15.h> -#include <cortex_a17.h> -#include <cortex_a57.h> -#include <cortex_a9.h> #endif -#define MAX_ERRATA_ENTRIES 32 +/* Max number of platform based errata with no workaround in EL3 */ +#define MAX_PLAT_CPU_ERRATA_ENTRIES 2 -#define ERRATA_LIST_END (MAX_ERRATA_ENTRIES - 1) +#define ERRATA_LIST_END (MAX_PLAT_CPU_ERRATA_ENTRIES - 1) /* Default values for unused memory in the array */ -#define UNDEF_ERRATA {UINT_MAX, UCHAR_MAX, UCHAR_MAX, false, false} - -#define EXTRACT_PARTNUM(x) ((x >> MIDR_PN_SHIFT) & MIDR_PN_MASK) +#define UNDEF_ERRATA {UINT_MAX, UCHAR_MAX, UCHAR_MAX} #define RXPX_RANGE(x, y, z) (((x >= y) && (x <= z)) ? true : false) @@ -58,15 +41,11 @@ struct em_cpu{ unsigned int em_errata_id; unsigned char em_rxpx_lo; /* lowest revision of errata applicable for the cpu */ unsigned char em_rxpx_hi; /* highest revision of errata applicable for the cpu */ - bool errata_enabled; /* indicate if errata enabled */ - /* flag to indicate if errata query is based out of non-arm interconnect */ - bool non_arm_interconnect; }; struct em_cpu_list{ - /* field to hold cpu specific part number defined in midr reg */ - unsigned long cpu_partnumber; - struct em_cpu cpu_errata_list[MAX_ERRATA_ENTRIES]; + unsigned long cpu_midr; /* cpu specific part number is bit[15:4] of midr value */ + struct em_cpu cpu_errata_list[MAX_PLAT_CPU_ERRATA_ENTRIES]; }; int32_t verify_errata_implemented(uint32_t errata_id, uint32_t forward_flag); diff --git a/services/std_svc/errata_abi/errata_abi_main.c b/services/std_svc/errata_abi/errata_abi_main.c index 0b263e5f..0d0ecc3d 100644 --- a/services/std_svc/errata_abi/errata_abi_main.c +++ b/services/std_svc/errata_abi/errata_abi_main.c @@ -1,11 +1,13 @@ /* - * Copyright (c) 2023, Arm Limited and Contributors. All rights reserved. + * Copyright (c) 2023-2024, Arm Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ #include <assert.h> #include "cpu_errata_info.h" +#include <lib/cpus/cpu_ops.h> +#include <lib/cpus/errata.h> #include <lib/smccc.h> #include <lib/utils_def.h> #include <services/errata_abi_svc.h> @@ -17,516 +19,192 @@ */ struct em_cpu_list *cpu_ptr; -extern uint8_t cpu_get_rev_var(void); - /* Structure array that holds CPU specific errata information */ struct em_cpu_list cpu_list[] = { -#if CORTEX_A9_H_INC -{ - .cpu_partnumber = CORTEX_A9_MIDR, - .cpu_errata_list = { - [0] = {794073, 0x00, 0xFF, ERRATA_A9_794073}, - [1 ... ERRATA_LIST_END] = UNDEF_ERRATA, - } -}, -#endif /* CORTEX_A9_H_INC */ - -#if CORTEX_A15_H_INC -{ - .cpu_partnumber = CORTEX_A15_MIDR, - .cpu_errata_list = { - [0] = {816470, 0x30, 0xFF, ERRATA_A15_816470}, - [1] = {827671, 0x30, 0xFF, ERRATA_A15_827671}, - [2 ... ERRATA_LIST_END] = UNDEF_ERRATA, - } -}, -#endif /* CORTEX_A15_H_INC */ - -#if CORTEX_A17_H_INC -{ - .cpu_partnumber = CORTEX_A17_MIDR, - .cpu_errata_list = { - [0] = {852421, 0x00, 0x12, ERRATA_A17_852421}, - [1] = {852423, 0x00, 0x12, ERRATA_A17_852423}, - [2 ... ERRATA_LIST_END] = UNDEF_ERRATA, - } -}, -#endif /* CORTEX_A17_H_INC */ - -#if CORTEX_A35_H_INC -{ - .cpu_partnumber = CORTEX_A35_MIDR, - .cpu_errata_list = { - [0] = {855472, 0x00, 0x00, ERRATA_A35_855472}, - [1 ... ERRATA_LIST_END] = UNDEF_ERRATA, - } -}, -#endif /* CORTEX_A35_H_INC */ - -#if CORTEX_A53_H_INC -{ - .cpu_partnumber = CORTEX_A53_MIDR, - .cpu_errata_list = { - [0] = {819472, 0x00, 0x01, ERRATA_A53_819472}, - [1] = {824069, 0x00, 0x02, ERRATA_A53_824069}, - [2] = {826319, 0x00, 0x02, ERRATA_A53_826319}, - [3] = {827319, 0x00, 0x02, ERRATA_A53_827319}, - [4] = {835769, 0x00, 0x04, ERRATA_A53_835769}, - [5] = {836870, 0x00, 0x03, ERRATA_A53_836870}, - [6] = {843419, 0x00, 0x04, ERRATA_A53_843419}, - [7] = {855873, 0x03, 0xFF, ERRATA_A53_855873}, - [8] = {1530924, 0x00, 0xFF, ERRATA_A53_1530924}, - [9 ... ERRATA_LIST_END] = UNDEF_ERRATA, - } -}, -#endif /* CORTEX_A53_H_INC */ - -#if CORTEX_A55_H_INC -{ - .cpu_partnumber = CORTEX_A55_MIDR, - .cpu_errata_list = { - [0] = {768277, 0x00, 0x00, ERRATA_A55_768277}, - [1] = {778703, 0x00, 0x00, ERRATA_A55_778703}, - [2] = {798797, 0x00, 0x00, ERRATA_A55_798797}, - [3] = {846532, 0x00, 0x01, ERRATA_A55_846532}, - [4] = {903758, 0x00, 0x01, ERRATA_A55_903758}, - [5] = {1221012, 0x00, 0x10, ERRATA_A55_1221012}, - [6] = {1530923, 0x00, 0xFF, ERRATA_A55_1530923}, - [7 ... ERRATA_LIST_END] = UNDEF_ERRATA, - } -}, -#endif /* CORTEX_A55_H_INC */ - -#if CORTEX_A57_H_INC -{ - .cpu_partnumber = CORTEX_A57_MIDR, - .cpu_errata_list = { - [0] = {806969, 0x00, 0x00, ERRATA_A57_806969}, - [1] = {813419, 0x00, 0x00, ERRATA_A57_813419}, - [2] = {813420, 0x00, 0x00, ERRATA_A57_813420}, - [3] = {814670, 0x00, 0x00, ERRATA_A57_814670}, - [4] = {817169, 0x00, 0x01, ERRATA_A57_817169}, - [5] = {826974, 0x00, 0x11, ERRATA_A57_826974}, - [6] = {826977, 0x00, 0x11, ERRATA_A57_826977}, - [7] = {828024, 0x00, 0x11, ERRATA_A57_828024}, - [8] = {829520, 0x00, 0x12, ERRATA_A57_829520}, - [9] = {833471, 0x00, 0x12, ERRATA_A57_833471}, - [10] = {859972, 0x00, 0x13, ERRATA_A57_859972}, - [11] = {1319537, 0x00, 0xFF, ERRATA_A57_1319537}, - [12 ... ERRATA_LIST_END] = UNDEF_ERRATA, - } -}, -#endif /* CORTEX_A57_H_INC */ - -#if CORTEX_A72_H_INC -{ - .cpu_partnumber = CORTEX_A72_MIDR, - .cpu_errata_list = { - [0] = {859971, 0x00, 0x03, ERRATA_A72_859971}, - [1] = {1319367, 0x00, 0xFF, ERRATA_A72_1319367}, - [2 ... ERRATA_LIST_END] = UNDEF_ERRATA, - } -}, -#endif /* CORTEX_A72_H_INC */ - -#if CORTEX_A73_H_INC -{ - .cpu_partnumber = CORTEX_A73_MIDR, - .cpu_errata_list = { - [0] = {852427, 0x00, 0x00, ERRATA_A73_852427}, - [1] = {855423, 0x00, 0x01, ERRATA_A73_855423}, - [2 ... ERRATA_LIST_END] = UNDEF_ERRATA, - } -}, -#endif /* CORTEX_A73_H_INC */ - -#if CORTEX_A75_H_INC -{ - .cpu_partnumber = CORTEX_A75_MIDR, - .cpu_errata_list = { - [0] = {764081, 0x00, 0x00, ERRATA_A75_764081}, - [1] = {790748, 0x00, 0x00, ERRATA_A75_790748}, - [2 ... ERRATA_LIST_END] = UNDEF_ERRATA, - } -}, -#endif /* CORTEX_A75_H_INC */ - -#if CORTEX_A76_H_INC -{ - .cpu_partnumber = CORTEX_A76_MIDR, - .cpu_errata_list = { - [0] = {1073348, 0x00, 0x10, ERRATA_A76_1073348}, - [1] = {1130799, 0x00, 0x20, ERRATA_A76_1130799}, - [2] = {1165522, 0x00, 0xFF, ERRATA_A76_1165522}, - [3] = {1220197, 0x00, 0x20, ERRATA_A76_1220197}, - [4] = {1257314, 0x00, 0x30, ERRATA_A76_1257314}, - [5] = {1262606, 0x00, 0x30, ERRATA_A76_1262606}, - [6] = {1262888, 0x00, 0x30, ERRATA_A76_1262888}, - [7] = {1275112, 0x00, 0x30, ERRATA_A76_1275112}, - [8] = {1286807, 0x00, 0x30, ERRATA_A76_1286807}, - [9] = {1791580, 0x00, 0x40, ERRATA_A76_1791580}, - [10] = {1868343, 0x00, 0x40, ERRATA_A76_1868343}, - [11] = {1946160, 0x30, 0x41, ERRATA_A76_1946160}, - [12] = {2743102, 0x00, 0x41, ERRATA_A76_2743102}, - [13 ... ERRATA_LIST_END] = UNDEF_ERRATA, - } -}, -#endif /* CORTEX_A76_H_INC */ - -#if CORTEX_A77_H_INC -{ - .cpu_partnumber = CORTEX_A77_MIDR, - .cpu_errata_list = { - [0] = {1508412, 0x00, 0x10, ERRATA_A77_1508412}, - [1] = {1791578, 0x00, 0x11, ERRATA_A77_1791578}, - [2] = {1800714, 0x00, 0x11, ERRATA_A77_1800714}, - [3] = {1925769, 0x00, 0x11, ERRATA_A77_1925769}, - [4] = {1946167, 0x00, 0x11, ERRATA_A77_1946167}, - [5] = {2356587, 0x00, 0x11, ERRATA_A77_2356587}, - [6] = {2743100, 0x00, 0x11, ERRATA_A77_2743100}, - [7 ... ERRATA_LIST_END] = UNDEF_ERRATA, - } -}, -#endif /* CORTEX_A77_H_INC */ - #if CORTEX_A78_H_INC { - .cpu_partnumber = CORTEX_A78_MIDR, + .cpu_midr = CORTEX_A78_MIDR, .cpu_errata_list = { - [0] = {1688305, 0x00, 0x10, ERRATA_A78_1688305}, - [1] = {1821534, 0x00, 0x10, ERRATA_A78_1821534}, - [2] = {1941498, 0x00, 0x11, ERRATA_A78_1941498}, - [3] = {1951500, 0x10, 0x11, ERRATA_A78_1951500}, - [4] = {1952683, 0x00, 0x00, ERRATA_A78_1952683}, - [5] = {2132060, 0x00, 0x12, ERRATA_A78_2132060}, - [6] = {2242635, 0x10, 0x12, ERRATA_A78_2242635}, - [7] = {2376745, 0x00, 0x12, ERRATA_A78_2376745}, - [8] = {2395406, 0x00, 0x12, ERRATA_A78_2395406}, - [9] = {2712571, 0x00, 0x12, ERRATA_A78_2712571, \ - ERRATA_NON_ARM_INTERCONNECT}, - [10] = {2742426, 0x00, 0x12, ERRATA_A78_2742426}, - [11] = {2772019, 0x00, 0x12, ERRATA_A78_2772019}, - [12] = {2779479, 0x00, 0x12, ERRATA_A78_2779479}, - [13 ... ERRATA_LIST_END] = UNDEF_ERRATA, + [0] = {2712571, 0x00, 0x12}, + [1 ... ERRATA_LIST_END] = UNDEF_ERRATA, } }, #endif /* CORTEX_A78_H_INC */ #if CORTEX_A78_AE_H_INC { - .cpu_partnumber = CORTEX_A78_AE_MIDR, + .cpu_midr = CORTEX_A78_AE_MIDR, .cpu_errata_list = { - [0] = {1941500, 0x00, 0x01, ERRATA_A78_AE_1941500}, - [1] = {1951502, 0x00, 0x01, ERRATA_A78_AE_1951502}, - [2] = {2376748, 0x00, 0x02, ERRATA_A78_AE_2376748}, - [3] = {2395408, 0x00, 0x01, ERRATA_A78_AE_2395408}, - [4] = {2712574, 0x00, 0x02, ERRATA_A78_AE_2712574, \ - ERRATA_NON_ARM_INTERCONNECT}, - [5 ... ERRATA_LIST_END] = UNDEF_ERRATA, + [0] = {2712574, 0x00, 0x02}, + [1 ... ERRATA_LIST_END] = UNDEF_ERRATA, } }, #endif /* CORTEX_A78_AE_H_INC */ #if CORTEX_A78C_H_INC { - .cpu_partnumber = CORTEX_A78C_MIDR, + .cpu_midr = CORTEX_A78C_MIDR, .cpu_errata_list = { - [0] = {1827430, 0x00, 0x00, ERRATA_A78C_1827430}, - [1] = {1827440, 0x00, 0x00, ERRATA_A78C_1827440}, - [2] = {2132064, 0x01, 0x02, ERRATA_A78C_2132064}, - [3] = {2242638, 0x01, 0x02, ERRATA_A78C_2242638}, - [4] = {2376749, 0x01, 0x02, ERRATA_A78C_2376749}, - [5] = {2395411, 0x01, 0x02, ERRATA_A78C_2395411}, - [6] = {2712575, 0x01, 0x02, ERRATA_A78C_2712575, \ - ERRATA_NON_ARM_INTERCONNECT}, - [7] = {2772121, 0x00, 0x02, ERRATA_A78C_2772121}, - [8] = {2779484, 0x01, 0x02, ERRATA_A78C_2779484}, - [9 ... ERRATA_LIST_END] = UNDEF_ERRATA, + [0] = {2712575, 0x01, 0x02}, + [1 ... ERRATA_LIST_END] = UNDEF_ERRATA, } }, #endif /* CORTEX_A78C_H_INC */ -#if CORTEX_X1_H_INC -{ - .cpu_partnumber = CORTEX_X1_MIDR, - .cpu_errata_list = { - [0] = {1688305, 0x00, 0x10, ERRATA_X1_1688305}, - [1] = {1821534, 0x00, 0x10, ERRATA_X1_1821534}, - [2] = {1827429, 0x00, 0x10, ERRATA_X1_1827429}, - [3 ... ERRATA_LIST_END] = UNDEF_ERRATA, - } -}, -#endif /* CORTEX_X1_H_INC */ - -#if NEOVERSE_N1_H_INC -{ - .cpu_partnumber = NEOVERSE_N1_MIDR, - .cpu_errata_list = { - [0] = {1043202, 0x00, 0x10, ERRATA_N1_1043202}, - [1] = {1073348, 0x00, 0x10, ERRATA_N1_1073348}, - [2] = {1130799, 0x00, 0x20, ERRATA_N1_1130799}, - [3] = {1165347, 0x00, 0x20, ERRATA_N1_1165347}, - [4] = {1207823, 0x00, 0x20, ERRATA_N1_1207823}, - [5] = {1220197, 0x00, 0x20, ERRATA_N1_1220197}, - [6] = {1257314, 0x00, 0x30, ERRATA_N1_1257314}, - [7] = {1262606, 0x00, 0x30, ERRATA_N1_1262606}, - [8] = {1262888, 0x00, 0x30, ERRATA_N1_1262888}, - [9] = {1275112, 0x00, 0x30, ERRATA_N1_1275112}, - [10] = {1315703, 0x00, 0x30, ERRATA_N1_1315703}, - [11] = {1542419, 0x30, 0x40, ERRATA_N1_1542419}, - [12] = {1868343, 0x00, 0x40, ERRATA_N1_1868343}, - [13] = {1946160, 0x30, 0x41, ERRATA_N1_1946160}, - [14] = {2743102, 0x00, 0x41, ERRATA_N1_2743102}, - [15 ... ERRATA_LIST_END] = UNDEF_ERRATA, - } -}, -#endif /* NEOVERSE_N1_H_INC */ - #if NEOVERSE_V1_H_INC { - .cpu_partnumber = NEOVERSE_V1_MIDR, + .cpu_midr = NEOVERSE_V1_MIDR, .cpu_errata_list = { - [0] = {1618635, 0x00, 0x00, ERRATA_V1_1618635}, - [1] = {1774420, 0x00, 0x10, ERRATA_V1_1774420}, - [2] = {1791573, 0x00, 0x10, ERRATA_V1_1791573}, - [3] = {1852267, 0x00, 0x10, ERRATA_V1_1852267}, - [4] = {1925756, 0x00, 0x11, ERRATA_V1_1925756}, - [5] = {1940577, 0x10, 0x11, ERRATA_V1_1940577}, - [6] = {1966096, 0x10, 0x11, ERRATA_V1_1966096}, - [7] = {2108267, 0x00, 0x12, ERRATA_V1_2108267}, - [8] = {2139242, 0x00, 0x11, ERRATA_V1_2139242}, - [9] = {2216392, 0x10, 0x11, ERRATA_V1_2216392}, - [10] = {2294912, 0x00, 0x12, ERRATA_V1_2294912}, - [11] = {2372203, 0x00, 0x11, ERRATA_V1_2372203}, - [12] = {2701953, 0x00, 0x11, ERRATA_V1_2701953, \ - ERRATA_NON_ARM_INTERCONNECT}, - [13] = {2743093, 0x00, 0x12, ERRATA_V1_2743093}, - [14] = {2743233, 0x00, 0x12, ERRATA_V1_2743233}, - [15] = {2779461, 0x00, 0x12, ERRATA_V1_2779461}, - [16 ... ERRATA_LIST_END] = UNDEF_ERRATA, + [0] = {2701953, 0x00, 0x11}, + [1 ... ERRATA_LIST_END] = UNDEF_ERRATA, } }, #endif /* NEOVERSE_V1_H_INC */ #if CORTEX_A710_H_INC { - .cpu_partnumber = CORTEX_A710_MIDR, + .cpu_midr = CORTEX_A710_MIDR, .cpu_errata_list = { - [0] = {1987031, 0x00, 0x20, ERRATA_A710_1987031}, - [1] = {2008768, 0x00, 0x20, ERRATA_A710_2008768}, - [2] = {2017096, 0x00, 0x20, ERRATA_A710_2017096}, - [3] = {2055002, 0x10, 0x20, ERRATA_A710_2055002}, - [4] = {2058056, 0x00, 0x21, ERRATA_A710_2058056}, - [5] = {2081180, 0x00, 0x20, ERRATA_A710_2081180}, - [6] = {2083908, 0x20, 0x20, ERRATA_A710_2083908}, - [7] = {2136059, 0x00, 0x20, ERRATA_A710_2136059}, - [8] = {2147715, 0x20, 0x20, ERRATA_A710_2147715}, - [9] = {2216384, 0x00, 0x20, ERRATA_A710_2216384}, - [10] = {2267065, 0x00, 0x20, ERRATA_A710_2267065}, - [11] = {2282622, 0x00, 0x21, ERRATA_A710_2282622}, - [12] = {2291219, 0x00, 0x20, ERRATA_A710_2291219}, - [13] = {2371105, 0x00, 0x20, ERRATA_A710_2371105}, - [14] = {2701952, 0x00, 0x21, ERRATA_A710_2701952, \ - ERRATA_NON_ARM_INTERCONNECT}, - [15] = {2742423, 0x00, 0x21, ERRATA_A710_2742423}, - [16] = {2768515, 0x00, 0x21, ERRATA_A710_2768515}, - [17 ... ERRATA_LIST_END] = UNDEF_ERRATA, + [0] = {2701952, 0x00, 0x21}, + [1 ... ERRATA_LIST_END] = UNDEF_ERRATA, } }, #endif /* CORTEX_A710_H_INC */ #if NEOVERSE_N2_H_INC { - .cpu_partnumber = NEOVERSE_N2_MIDR, + .cpu_midr = NEOVERSE_N2_MIDR, .cpu_errata_list = { - [0] = {2002655, 0x00, 0x00, ERRATA_N2_2002655}, - [1] = {2009478, 0x00, 0x00, ERRATA_N2_2009478}, - [2] = {2025414, 0x00, 0x00, ERRATA_N2_2025414}, - [3] = {2067956, 0x00, 0x00, ERRATA_N2_2067956}, - [4] = {2138953, 0x00, 0x03, ERRATA_N2_2138953}, - [5] = {2138956, 0x00, 0x00, ERRATA_N2_2138956}, - [6] = {2138958, 0x00, 0x00, ERRATA_N2_2138958}, - [7] = {2189731, 0x00, 0x00, ERRATA_N2_2189731}, - [8] = {2242400, 0x00, 0x00, ERRATA_N2_2242400}, - [9] = {2242415, 0x00, 0x00, ERRATA_N2_2242415}, - [10] = {2280757, 0x00, 0x00, ERRATA_N2_2280757}, - [11] = {2326639, 0x00, 0x00, ERRATA_N2_2326639}, - [12] = {2340933, 0x00, 0x00, ERRATA_N2_2340933}, - [13] = {2346952, 0x00, 0x02, ERRATA_N2_2346952}, - [14] = {2376738, 0x00, 0x00, ERRATA_N2_2376738}, - [15] = {2388450, 0x00, 0x00, ERRATA_N2_2388450}, - [16] = {2728475, 0x00, 0x02, ERRATA_N2_2728475, \ - ERRATA_NON_ARM_INTERCONNECT}, - [17] = {2743014, 0x00, 0x02, ERRATA_N2_2743014}, - [18] = {2743089, 0x00, 0x02, ERRATA_N2_2743089}, - [19] = {2779511, 0x00, 0x02, ERRATA_N2_2779511}, - [20 ... ERRATA_LIST_END] = UNDEF_ERRATA, + [0] = {2728475, 0x00, 0x02}, + [1 ... ERRATA_LIST_END] = UNDEF_ERRATA, } }, #endif /* NEOVERSE_N2_H_INC */ #if CORTEX_X2_H_INC { - .cpu_partnumber = CORTEX_X2_MIDR, + .cpu_midr = CORTEX_X2_MIDR, .cpu_errata_list = { - [0] = {2002765, 0x00, 0x20, ERRATA_X2_2002765}, - [1] = {2017096, 0x00, 0x20, ERRATA_X2_2017096}, - [2] = {2058056, 0x00, 0x21, ERRATA_X2_2058056}, - [3] = {2081180, 0x00, 0x20, ERRATA_X2_2081180}, - [4] = {2083908, 0x20, 0x20, ERRATA_X2_2083908}, - [5] = {2147715, 0x20, 0x20, ERRATA_X2_2147715}, - [6] = {2216384, 0x00, 0x20, ERRATA_X2_2216384}, - [7] = {2282622, 0x00, 0x21, ERRATA_X2_2282622}, - [8] = {2371105, 0x00, 0x20, ERRATA_X2_2371105}, - [9] = {2701952, 0x00, 0x21, ERRATA_X2_2701952, \ - ERRATA_NON_ARM_INTERCONNECT}, - [10] = {2742423, 0x00, 0x21, ERRATA_X2_2742423}, - [11] = {2768515, 0x00, 0x21, ERRATA_X2_2768515}, - [12 ... ERRATA_LIST_END] = UNDEF_ERRATA, + [0] = {2701952, 0x00, 0x21}, + [1 ... ERRATA_LIST_END] = UNDEF_ERRATA, } }, #endif /* CORTEX_X2_H_INC */ -#if CORTEX_A510_H_INC -{ - .cpu_partnumber = CORTEX_A510_MIDR, - .cpu_errata_list = { - [0] = {1922240, 0x00, 0x00, ERRATA_A510_1922240}, - [1] = {2041909, 0x02, 0x02, ERRATA_A510_2041909}, - [2] = {2042739, 0x00, 0x02, ERRATA_A510_2042739}, - [3] = {2080326, 0x02, 0x02, ERRATA_A510_2080326}, - [4] = {2172148, 0x00, 0x10, ERRATA_A510_2172148}, - [5] = {2218950, 0x00, 0x10, ERRATA_A510_2218950}, - [6] = {2250311, 0x00, 0x10, ERRATA_A510_2250311}, - [7] = {2288014, 0x00, 0x10, ERRATA_A510_2288014}, - [8] = {2347730, 0x00, 0x11, ERRATA_A510_2347730}, - [9] = {2371937, 0x00, 0x11, ERRATA_A510_2371937}, - [10] = {2666669, 0x00, 0x11, ERRATA_A510_2666669}, - [11] = {2684597, 0x00, 0x12, ERRATA_A510_2684597}, - [12 ... ERRATA_LIST_END] = UNDEF_ERRATA, - } -}, -#endif /* CORTEX_A510_H_INC */ - #if NEOVERSE_V2_H_INC { - .cpu_partnumber = NEOVERSE_V2_MIDR, + .cpu_midr = NEOVERSE_V2_MIDR, .cpu_errata_list = { - [0] = {2331132, 0x00, 0x02, ERRATA_V2_2331132}, - [1] = {2719103, 0x00, 0x01, ERRATA_V2_2719103, \ - ERRATA_NON_ARM_INTERCONNECT}, - [2] = {2719105, 0x00, 0x01, ERRATA_V2_2719105}, - [3] = {2743011, 0x00, 0x01, ERRATA_V2_2743011}, - [4] = {2779510, 0x00, 0x01, ERRATA_V2_2779510}, - [5] = {2801372, 0x00, 0x01, ERRATA_V2_2801372}, - [6 ... ERRATA_LIST_END] = UNDEF_ERRATA, + [0] = {2719103, 0x00, 0x01}, + [1 ... ERRATA_LIST_END] = UNDEF_ERRATA, } }, #endif /* NEOVERSE_V2_H_INC */ -#if CORTEX_A715_H_INC +#if CORTEX_X3_H_INC { - .cpu_partnumber = CORTEX_A715_MIDR, + .cpu_midr = CORTEX_X3_MIDR, .cpu_errata_list = { - [0] = {2701951, 0x00, 0x11, ERRATA_A715_2701951, \ - ERRATA_NON_ARM_INTERCONNECT}, + [0] = {2701951, 0x00, 0x11}, [1 ... ERRATA_LIST_END] = UNDEF_ERRATA, } }, -#endif /* CORTEX_A715_H_INC */ +#endif /* CORTEX_X3_H_INC */ -#if CORTEX_X3_H_INC +#if CORTEX_X4_H_INC { - .cpu_partnumber = CORTEX_X3_MIDR, + .cpu_midr = CORTEX_X4_MIDR, .cpu_errata_list = { - [0] = {2070301, 0x00, 0x12, ERRATA_X3_2070301}, - [1] = {2313909, 0x00, 0x10, ERRATA_X3_2313909}, - [2] = {2615812, 0x00, 0x11, ERRATA_X3_2615812}, - [3] = {2742421, 0x00, 0x11, ERRATA_X3_2742421}, - [4 ... ERRATA_LIST_END] = UNDEF_ERRATA, + [0] = {2701112, 0x00, 0x00}, + [1 ... ERRATA_LIST_END] = UNDEF_ERRATA, } }, -#endif /* CORTEX_X3_H_INC */ -}; - -/* - * Function to do binary search and check for the specific errata ID - * in the array of structures specific to the cpu identified. - */ -int32_t binary_search(struct em_cpu_list *ptr, uint32_t erratum_id, uint8_t rxpx_val) -{ - int low_index = 0U, mid_index = 0U; +#endif /* CORTEX_X4_H_INC */ - int high_index = MAX_ERRATA_ENTRIES - 1; +}; - assert(ptr != NULL); +#if ERRATA_NON_ARM_INTERCONNECT - /* - * Pointer to the errata list of the cpu that matches - * extracted partnumber in the cpu list - */ - struct em_cpu *erratum_ptr = NULL; +/* Check if the errata is enabled for non-arm interconnect */ +static int32_t non_arm_interconnect_errata(uint32_t errata_id, long rev_var) +{ + int32_t ret_val = EM_UNKNOWN_ERRATUM; - while (low_index <= high_index) { - mid_index = (low_index + high_index) / 2; + /* Determine the number of cpu listed in the cpu list */ + uint8_t size_cpulist = ARRAY_SIZE(cpu_list); - erratum_ptr = &ptr->cpu_errata_list[mid_index]; - assert(erratum_ptr != NULL); + /* Read the midr reg to extract cpu, revision and variant info */ + uint32_t midr_val = read_midr(); - if (erratum_id < erratum_ptr->em_errata_id) { - high_index = mid_index - 1; - } else if (erratum_id > erratum_ptr->em_errata_id) { - low_index = mid_index + 1; - } else if (erratum_id == erratum_ptr->em_errata_id) { - if (RXPX_RANGE(rxpx_val, erratum_ptr->em_rxpx_lo, \ - erratum_ptr->em_rxpx_hi)) { - if ((erratum_ptr->errata_enabled) && \ - (!(erratum_ptr->non_arm_interconnect))) { - return EM_HIGHER_EL_MITIGATION; + for (uint8_t i = 0U; i < size_cpulist; i++) { + cpu_ptr = &cpu_list[i]; + /* + * If the cpu partnumber in the cpu list, matches the midr + * part number, check to see if the errata ID matches + */ + if (EXTRACT_PARTNUM(midr_val) == EXTRACT_PARTNUM(cpu_ptr->cpu_midr)) { + + struct em_cpu *ptr = NULL; + + for (int j = 0; j < MAX_PLAT_CPU_ERRATA_ENTRIES; j++) { + ptr = &cpu_ptr->cpu_errata_list[j]; + assert(ptr != NULL); + if (errata_id == ptr->em_errata_id) { + if (RXPX_RANGE(rev_var, ptr->em_rxpx_lo, ptr->em_rxpx_hi)) { + ret_val = EM_AFFECTED; + break; + } + ret_val = EM_NOT_AFFECTED; + break; } - return EM_AFFECTED; } - return EM_NOT_AFFECTED; + break; } } - /* no matching errata ID */ - return EM_UNKNOWN_ERRATUM; + return ret_val; } +#endif /* Function to check if the errata exists for the specific CPU and rxpx */ int32_t verify_errata_implemented(uint32_t errata_id, uint32_t forward_flag) { - /* - * Read MIDR value and extract the revision, variant and partnumber - */ - static uint32_t midr_val, cpu_partnum; - static uint8_t cpu_rxpx_val; - int32_t ret_val = EM_UNKNOWN_ERRATUM; + int32_t ret_val; + struct cpu_ops *cpu_ops; + struct erratum_entry *entry, *end; + long rev_var; + + ret_val = EM_UNKNOWN_ERRATUM; + rev_var = cpu_get_rev_var(); + +#if ERRATA_NON_ARM_INTERCONNECT + ret_val = non_arm_interconnect_errata(errata_id, rev_var); + if (ret_val != EM_UNKNOWN_ERRATUM) { + return ret_val; + } +#endif - /* Determine the number of cpu listed in the cpu list */ - uint8_t size_cpulist = ARRAY_SIZE(cpu_list); + cpu_ops = get_cpu_ops_ptr(); + assert(cpu_ops != NULL); - /* Read the midr reg to extract cpu, revision and variant info */ - midr_val = read_midr(); + entry = cpu_ops->errata_list_start; + assert(entry != NULL); - /* Extract revision and variant from the MIDR register */ - cpu_rxpx_val = cpu_get_rev_var(); + end = cpu_ops->errata_list_end; + assert(end != NULL); - /* Extract the cpu partnumber and check if the cpu is in the cpu list */ - cpu_partnum = EXTRACT_PARTNUM(midr_val); + end--; /* point to the last erratum entry of the queried cpu */ - for (uint8_t i = 0; i < size_cpulist; i++) { - cpu_ptr = &cpu_list[i]; - uint16_t partnum_extracted = EXTRACT_PARTNUM(cpu_ptr->cpu_partnumber); - - if (partnum_extracted == cpu_partnum) { - /* - * If the midr value is in the cpu list, binary search - * for the errata ID and specific revision in the list. - */ - ret_val = binary_search(cpu_ptr, errata_id, cpu_rxpx_val); - break; + while ((entry <= end) && (ret_val == EM_UNKNOWN_ERRATUM)) { + if (entry->id == errata_id) { + if (entry->check_func(rev_var)) { + if (entry->chosen) + return EM_HIGHER_EL_MITIGATION; + else + return EM_AFFECTED; + } + return EM_NOT_AFFECTED; } + entry += 1; } return ret_val; } diff --git a/services/std_svc/rmmd/rmmd.mk b/services/std_svc/rmmd/rmmd.mk index bcf54e1b..eae5031f 100644 --- a/services/std_svc/rmmd/rmmd.mk +++ b/services/std_svc/rmmd/rmmd.mk @@ -1,5 +1,5 @@ # -# Copyright (c) 2021-2022, ARM Limited and Contributors. All rights reserved. +# Copyright (c) 2021-2024, ARM Limited and Contributors. All rights reserved. # # SPDX-License-Identifier: BSD-3-Clause # @@ -8,7 +8,10 @@ ifneq (${ARCH},aarch64) $(error "Error: RMMD is only supported on aarch64.") endif -include services/std_svc/rmmd/trp/trp.mk +# Include TRP makefile only if RMM is not defined. +ifeq ($(RMM),) + include services/std_svc/rmmd/trp/trp.mk +endif RMMD_SOURCES += $(addprefix services/std_svc/rmmd/, \ ${ARCH}/rmmd_helpers.S \ diff --git a/services/std_svc/rmmd/rmmd_attest.c b/services/std_svc/rmmd/rmmd_attest.c index 25adf502..7d4ea701 100644 --- a/services/std_svc/rmmd/rmmd_attest.c +++ b/services/std_svc/rmmd/rmmd_attest.c @@ -1,8 +1,10 @@ /* - * Copyright (c) 2022, Arm Limited. All rights reserved. + * Copyright (c) 2022-2024, Arm Limited. All rights reserved. + * Copyright (c) 2024, NVIDIA Corporation. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ +#include <errno.h> #include <stdint.h> #include <string.h> @@ -11,7 +13,8 @@ #include <lib/xlat_tables/xlat_tables_v2.h> #include <plat/common/platform.h> #include "rmmd_private.h" -#include <services/rmmd_svc.h> +#include <services/rmm_el3_token_sign.h> +#include <smccc_helpers.h> static spinlock_t lock; @@ -85,7 +88,8 @@ static int validate_buffer_params(uint64_t buf_pa, uint64_t buf_len) } int rmmd_attest_get_platform_token(uint64_t buf_pa, uint64_t *buf_size, - uint64_t c_size) + uint64_t c_size, + uint64_t *remaining_len) { int err; uint8_t temp_buf[SHA512_DIGEST_SIZE]; @@ -110,9 +114,19 @@ int rmmd_attest_get_platform_token(uint64_t buf_pa, uint64_t *buf_size, /* Get the platform token. */ err = plat_rmmd_get_cca_attest_token((uintptr_t)buf_pa, - buf_size, (uintptr_t)temp_buf, c_size); + buf_size, (uintptr_t)temp_buf, c_size, remaining_len); - if (err != 0) { + switch (err) { + case 0: + err = E_RMM_OK; + break; + case -EAGAIN: + err = E_RMM_AGAIN; + break; + case -EINVAL: + err = E_RMM_INVAL; + break; + default: ERROR("Failed to get platform token: %d.\n", err); err = E_RMM_UNK; } @@ -144,10 +158,110 @@ int rmmd_attest_get_signing_key(uint64_t buf_pa, uint64_t *buf_size, (unsigned int)ecc_curve); if (err != 0) { ERROR("Failed to get attestation key: %d.\n", err); - err = E_RMM_UNK; + err = E_RMM_UNK; } spin_unlock(&lock); return err; } + +static int rmmd_el3_token_sign_push_req(uint64_t buf_pa, uint64_t buf_size) +{ + int err; + + err = validate_buffer_params(buf_pa, buf_size); + if (err != 0) { + return err; + } + + if (buf_size < sizeof(struct el3_token_sign_request)) { + return E_RMM_INVAL; + } + + spin_lock(&lock); + + /* Call platform port to handle attestation toekn signing request. */ + err = plat_rmmd_el3_token_sign_push_req((struct el3_token_sign_request *)buf_pa); + + spin_unlock(&lock); + + return err; +} + +static int rmmd_el3_token_sign_pull_resp(uint64_t buf_pa, uint64_t buf_size) +{ + int err; + + err = validate_buffer_params(buf_pa, buf_size); + if (err != 0) { + return err; + } + + + if (buf_size < sizeof(struct el3_token_sign_response)) { + return E_RMM_INVAL; + } + + spin_lock(&lock); + + /* Pull attestation signing response from HES. */ + err = plat_rmmd_el3_token_sign_pull_resp( + (struct el3_token_sign_response *)buf_pa); + + spin_unlock(&lock); + + return err; +} + +static int rmmd_attest_get_attest_pub_key(uint64_t buf_pa, uint64_t *buf_size, + uint64_t ecc_curve) +{ + int err; + + err = validate_buffer_params(buf_pa, *buf_size); + if (err != 0) { + return err; + } + + if (ecc_curve != ATTEST_KEY_CURVE_ECC_SECP384R1) { + ERROR("Invalid ECC curve specified\n"); + return E_RMM_INVAL; + } + + spin_lock(&lock); + + /* Get the Realm attestation public key from platform port. */ + err = plat_rmmd_el3_token_sign_get_rak_pub( + (uintptr_t)buf_pa, buf_size, (unsigned int)ecc_curve); + + spin_unlock(&lock); + if (err != 0) { + ERROR("Failed to get attestation public key from HES: %d.\n", + err); + err = E_RMM_UNK; + } + + + return err; +} + +uint64_t rmmd_el3_token_sign(void *handle, uint64_t opcode, uint64_t x2, + uint64_t x3, uint64_t x4) +{ + int ret; + + switch (opcode) { + case RMM_EL3_TOKEN_SIGN_PUSH_REQ_OP: + ret = rmmd_el3_token_sign_push_req(x2, x3); + SMC_RET1(handle, ret); + case RMM_EL3_TOKEN_SIGN_PULL_RESP_OP: + ret = rmmd_el3_token_sign_pull_resp(x2, x3); + SMC_RET1(handle, ret); + case RMM_EL3_TOKEN_SIGN_GET_RAK_PUB_OP: + ret = rmmd_attest_get_attest_pub_key(x2, &x3, x4); + SMC_RET2(handle, ret, x3); + default: + SMC_RET1(handle, SMC_UNK); + } +} diff --git a/services/std_svc/rmmd/rmmd_main.c b/services/std_svc/rmmd/rmmd_main.c index 8b78b135..d063ea34 100644 --- a/services/std_svc/rmmd/rmmd_main.c +++ b/services/std_svc/rmmd/rmmd_main.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021-2023, Arm Limited and Contributors. All rights reserved. + * Copyright (c) 2021-2024, Arm Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -70,7 +70,6 @@ uint64_t rmmd_rmm_sync_entry(rmmd_rmm_context_t *rmm_ctx) cm_set_context(&(rmm_ctx->cpu_ctx), REALM); /* Restore the realm context assigned above */ - cm_el1_sysregs_context_restore(REALM); cm_el2_sysregs_context_restore(REALM); cm_set_next_eret_context(REALM); @@ -78,12 +77,10 @@ uint64_t rmmd_rmm_sync_entry(rmmd_rmm_context_t *rmm_ctx) rc = rmmd_rmm_enter(&rmm_ctx->c_rt_ctx); /* - * Save realm context. EL1 and EL2 Non-secure - * contexts will be restored before exiting to - * Non-secure world, therefore there is no need - * to clear EL1 and EL2 context registers. + * Save realm context. EL2 Non-secure context will be restored + * before exiting Non-secure world, therefore there is no need + * to clear EL2 context registers. */ - cm_el1_sysregs_context_save(REALM); cm_el2_sysregs_context_save(REALM); return rc; @@ -112,8 +109,8 @@ __dead2 void rmmd_rmm_sync_exit(uint64_t rc) static void rmm_el2_context_init(el2_sysregs_t *regs) { - regs->ctx_regs[CTX_SPSR_EL2 >> 3] = REALM_SPSR_EL2; - regs->ctx_regs[CTX_SCTLR_EL2 >> 3] = SCTLR_EL2_RES1; + write_el2_ctx_common(regs, spsr_el2, REALM_SPSR_EL2); + write_el2_ctx_common(regs, sctlr_el2, SCTLR_EL2_RES1); } /******************************************************************************* @@ -134,6 +131,8 @@ static void manage_extensions_realm(cpu_context_t *ctx) static void manage_extensions_realm_per_world(void) { + cm_el3_arch_init_per_world(&per_world_context[CPU_CONTEXT_REALM]); + if (is_feat_sve_supported()) { /* * Enable SVE and FPU in realm context when it is enabled for NS. @@ -203,19 +202,23 @@ int rmmd_setup(void) int rc; /* Make sure RME is supported. */ - assert(get_armv9_2_feat_rme_support() != 0U); + if (is_feat_rme_present() == 0U) { + /* Mark the RMM boot as failed for all the CPUs */ + rmm_boot_failed = true; + return -ENOTSUP; + } rmm_ep_info = bl31_plat_get_next_image_ep_info(REALM); - if (rmm_ep_info == NULL) { + if ((rmm_ep_info == NULL) || (rmm_ep_info->pc == 0)) { WARN("No RMM image provided by BL2 boot loader, Booting " "device without RMM initialization. SMCs destined for " "RMM will return SMC_UNK\n"); + + /* Mark the boot as failed for all the CPUs */ + rmm_boot_failed = true; return -ENOENT; } - /* Under no circumstances will this parameter be 0 */ - assert(rmm_ep_info->pc == RMM_BASE); - /* Initialise an entrypoint to set up the CPU context */ ep_attr = EP_REALM; if ((read_sctlr_el3() & SCTLR_EE_BIT) != 0U) { @@ -233,11 +236,15 @@ int rmmd_setup(void) assert((shared_buf_size == SZ_4K) && ((void *)shared_buf_base != NULL)); - /* Load the boot manifest at the beginning of the shared area */ + /* Zero out and load the boot manifest at the beginning of the share area */ manifest = (struct rmm_manifest *)shared_buf_base; + (void)memset((void *)manifest, 0, sizeof(struct rmm_manifest)); + rc = plat_rmmd_load_manifest(manifest); if (rc != 0) { ERROR("Error loading RMM Boot Manifest (%i)\n", rc); + /* Mark the boot as failed for all the CPUs */ + rmm_boot_failed = true; return rc; } flush_dcache_range((uintptr_t)shared_buf_base, shared_buf_size); @@ -277,11 +284,9 @@ static uint64_t rmmd_smc_forward(uint32_t src_sec_state, cpu_context_t *ctx = cm_get_context(dst_sec_state); /* Save incoming security state */ - cm_el1_sysregs_context_save(src_sec_state); cm_el2_sysregs_context_save(src_sec_state); /* Restore outgoing security state */ - cm_el1_sysregs_context_restore(dst_sec_state); cm_el2_sysregs_context_restore(dst_sec_state); cm_set_next_eret_context(dst_sec_state); @@ -436,6 +441,21 @@ static int gpt_to_gts_error(int error, uint32_t smc_fid, uint64_t address) return ret; } +static int rmm_el3_ifc_get_feat_register(uint64_t feat_reg_idx, + uint64_t *feat_reg) +{ + if (feat_reg_idx != RMM_EL3_FEAT_REG_0_IDX) { + ERROR("RMMD: Failed to get feature register %ld\n", feat_reg_idx); + return E_RMM_INVAL; + } + + *feat_reg = 0UL; +#if RMMD_ENABLE_EL3_TOKEN_SIGN + *feat_reg |= RMM_EL3_FEAT_REG_0_EL3_TOKEN_SIGN_MASK; +#endif + return E_RMM_OK; +} + /******************************************************************************* * This function handles RMM-EL3 interface SMCs ******************************************************************************/ @@ -443,6 +463,7 @@ uint64_t rmmd_rmm_el3_handler(uint32_t smc_fid, uint64_t x1, uint64_t x2, uint64_t x3, uint64_t x4, void *cookie, void *handle, uint64_t flags) { + uint64_t remaining_len = 0UL; uint32_t src_sec_state; int ret; @@ -468,12 +489,18 @@ uint64_t rmmd_rmm_el3_handler(uint32_t smc_fid, uint64_t x1, uint64_t x2, ret = gpt_undelegate_pas(x1, PAGE_SIZE_4KB, SMC_FROM_REALM); SMC_RET1(handle, gpt_to_gts_error(ret, smc_fid, x1)); case RMM_ATTEST_GET_PLAT_TOKEN: - ret = rmmd_attest_get_platform_token(x1, &x2, x3); - SMC_RET2(handle, ret, x2); + ret = rmmd_attest_get_platform_token(x1, &x2, x3, &remaining_len); + SMC_RET3(handle, ret, x2, remaining_len); case RMM_ATTEST_GET_REALM_KEY: ret = rmmd_attest_get_signing_key(x1, &x2, x3); SMC_RET2(handle, ret, x2); - + case RMM_EL3_FEATURES: + ret = rmm_el3_ifc_get_feat_register(x1, &x2); + SMC_RET2(handle, ret, x2); +#if RMMD_ENABLE_EL3_TOKEN_SIGN + case RMM_EL3_TOKEN_SIGN: + return rmmd_el3_token_sign(handle, x1, x2, x3, x4); +#endif case RMM_BOOT_COMPLETE: VERBOSE("RMMD: running rmmd_rmm_sync_exit\n"); rmmd_rmm_sync_exit(x1); diff --git a/services/std_svc/rmmd/rmmd_private.h b/services/std_svc/rmmd/rmmd_private.h index 4954a435..0ce104d0 100644 --- a/services/std_svc/rmmd/rmmd_private.h +++ b/services/std_svc/rmmd/rmmd_private.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021-2022, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2021-2024, Arm Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -47,9 +47,12 @@ __dead2 void rmmd_rmm_sync_exit(uint64_t rc); /* Functions implementing attestation utilities for RMM */ int rmmd_attest_get_platform_token(uint64_t buf_pa, uint64_t *buf_size, - uint64_t c_size); + uint64_t c_size, + uint64_t *remaining_len); int rmmd_attest_get_signing_key(uint64_t buf_pa, uint64_t *buf_size, uint64_t ecc_curve); +uint64_t rmmd_el3_token_sign(void *handle, uint64_t x1, uint64_t x2, + uint64_t x3, uint64_t x4); /* Assembly helpers */ uint64_t rmmd_rmm_enter(uint64_t *c_rt_ctx); diff --git a/services/std_svc/rmmd/trp/trp.mk b/services/std_svc/rmmd/trp/trp.mk index b7bd3176..bb963431 100644 --- a/services/std_svc/rmmd/trp/trp.mk +++ b/services/std_svc/rmmd/trp/trp.mk @@ -1,5 +1,5 @@ # -# Copyright (c) 2021-2023 Arm Limited and Contributors. All rights reserved. +# Copyright (c) 2021-2024, Arm Limited and Contributors. All rights reserved. # # SPDX-License-Identifier: BSD-3-Clause # @@ -10,9 +10,9 @@ RMM_SOURCES += services/std_svc/rmmd/trp/trp_entry.S \ RMM_DEFAULT_LINKER_SCRIPT_SOURCE := services/std_svc/rmmd/trp/linker.ld.S -ifneq ($(findstring gcc,$(notdir $(LD))),) +ifeq ($($(ARCH)-ld-id),gnu-gcc) RMM_LDFLAGS += -Wl,--sort-section=alignment -else ifneq ($(findstring ld,$(notdir $(LD))),) +else ifneq ($(filter llvm-lld gnu-ld,$($(ARCH)-ld-id)),) RMM_LDFLAGS += --sort-section=alignment endif diff --git a/services/std_svc/rmmd/trp/trp_main.c b/services/std_svc/rmmd/trp/trp_main.c index 33f2fb08..b75483cb 100644 --- a/services/std_svc/rmmd/trp/trp_main.c +++ b/services/std_svc/rmmd/trp/trp_main.c @@ -1,9 +1,10 @@ /* - * Copyright (c) 2021-2022, Arm Limited and Contributors. All rights reserved. + * Copyright (c) 2021-2024, Arm Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ +#include <common/build_message.h> #include <common/debug.h> #include <plat/common/platform.h> #include <services/rmm_core_manifest.h> @@ -86,7 +87,7 @@ int trp_validate_warmboot_args(uint64_t x0, uint64_t x1, /* Main function for TRP */ void trp_main(void) { - NOTICE("TRP: %s\n", version_string); + NOTICE("TRP: %s\n", build_version_string); NOTICE("TRP: %s\n", build_message); NOTICE("TRP: Supported RMM-EL3 Interface ABI: v.%u.%u\n", TRP_RMM_EL3_ABI_VERS_MAJOR, TRP_RMM_EL3_ABI_VERS_MINOR); diff --git a/services/std_svc/sdei/sdei_intr_mgmt.c b/services/std_svc/sdei/sdei_intr_mgmt.c index 3bdf4a2b..c58adba5 100644 --- a/services/std_svc/sdei/sdei_intr_mgmt.c +++ b/services/std_svc/sdei/sdei_intr_mgmt.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2021, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2017-2024, Arm Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -13,6 +13,7 @@ #include <arch_features.h> #include <bl31/ehf.h> #include <bl31/interrupt_mgmt.h> +#include <bl31/sync_handle.h> #include <common/bl_common.h> #include <common/debug.h> #include <common/runtime_svc.h> @@ -237,9 +238,7 @@ static cpu_context_t *restore_and_resume_ns_context(void) /* * Prepare for ERET: * - Set the ELR to the registered handler address - * - Set the SPSR register as described in the SDEI documentation and - * the AArch64.TakeException() pseudocode function in - * ARM DDI 0487F.c page J1-7635 + * - Set the SPSR register by calling the common create_spsr() function */ static void sdei_set_elr_spsr(sdei_entry_t *se, sdei_dispatch_context_t *disp_ctx) @@ -250,57 +249,7 @@ static void sdei_set_elr_spsr(sdei_entry_t *se, sdei_dispatch_context_t *disp_ct u_register_t interrupted_pstate = disp_ctx->spsr_el3; - /* Check the SPAN bit in the client el SCTLR */ - u_register_t client_el_sctlr; - - if (client_el == MODE_EL2) { - client_el_sctlr = read_sctlr_el2(); - } else { - client_el_sctlr = read_sctlr_el1(); - } - - /* - * Check whether to force the PAN bit or use the value in the - * interrupted EL according to the check described in - * TakeException. Since the client can only be Non-Secure - * EL2 or El1 some of the conditions in ElIsInHost() we know - * will always be True. - * When the client_el is EL2 we know that there will be a SPAN - * bit in SCTLR_EL2 as we have already checked for the condition - * HCR_EL2.E2H = 1 and HCR_EL2.TGE = 1 - */ - u_register_t hcr_el2 = read_hcr(); - bool el_is_in_host = (read_feat_vhe_id_field() != 0U) && - (hcr_el2 & HCR_TGE_BIT) && - (hcr_el2 & HCR_E2H_BIT); - - if (is_feat_pan_supported() && - ((client_el == MODE_EL1) || - (client_el == MODE_EL2 && el_is_in_host)) && - ((client_el_sctlr & SCTLR_SPAN_BIT) == 0U)) { - sdei_spsr |= SPSR_PAN_BIT; - } else { - sdei_spsr |= (interrupted_pstate & SPSR_PAN_BIT); - } - - /* If SSBS is implemented, take the value from the client el SCTLR */ - u_register_t ssbs_enabled = (read_id_aa64pfr1_el1() - >> ID_AA64PFR1_EL1_SSBS_SHIFT) - & ID_AA64PFR1_EL1_SSBS_MASK; - if (ssbs_enabled != SSBS_UNAVAILABLE) { - u_register_t ssbs_bit = ((client_el_sctlr & SCTLR_DSSBS_BIT) - >> SCTLR_DSSBS_SHIFT) - << SPSR_SSBS_SHIFT_AARCH64; - sdei_spsr |= ssbs_bit; - } - - /* If MTE is implemented in the client el set the TCO bit */ - if (get_armv8_5_mte_support() >= MTE_IMPLEMENTED_ELX) { - sdei_spsr |= SPSR_TCO_BIT_AARCH64; - } - - /* Take the DIT field from the pstate of the interrupted el */ - sdei_spsr |= (interrupted_pstate & SPSR_DIT_BIT); + sdei_spsr = create_spsr(interrupted_pstate, client_el); cm_set_elr_spsr_el3(NON_SECURE, (uintptr_t) se->ep, sdei_spsr); } diff --git a/services/std_svc/sdei/sdei_main.c b/services/std_svc/sdei/sdei_main.c index 59a1673d..01cc1315 100644 --- a/services/std_svc/sdei/sdei_main.c +++ b/services/std_svc/sdei/sdei_main.c @@ -744,7 +744,9 @@ static int sdei_interrupt_bind(unsigned int intr_num) return SDEI_ENOMEM; /* The returned mapping must be dynamic */ - assert(is_map_dynamic(map)); + if (!is_map_dynamic(map)) { + return SDEI_ENOMEM; + } /* * We cannot assert for bound maps here, as we might be racing diff --git a/services/std_svc/spm/el3_spmc/spmc.h b/services/std_svc/spm/el3_spmc/spmc.h index 48644ac7..e093a82b 100644 --- a/services/std_svc/spm/el3_spmc/spmc.h +++ b/services/std_svc/spm/el3_spmc/spmc.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2022-2023, ARM Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -168,6 +168,12 @@ struct secure_partition_desc { /* Mailbox tracking. */ struct mailbox mailbox; + /* Lock to protect the runtime state of a S-EL0 SP execution context. */ + spinlock_t rt_state_lock; + + /* Pointer to translation table context of a S-EL0 SP. */ + xlat_ctx_t *xlat_ctx_handle; + /* Secondary entrypoint. Only valid for a S-EL1 SP. */ uintptr_t secondary_ep; @@ -224,6 +230,10 @@ void spmc_el1_sp_setup(struct secure_partition_desc *sp, entry_point_info_t *ep_info); void spmc_sp_common_ep_commit(struct secure_partition_desc *sp, entry_point_info_t *ep_info); +void spmc_el0_sp_spsr_setup(entry_point_info_t *ep_info); +void spmc_el0_sp_setup(struct secure_partition_desc *sp, + int32_t boot_info_reg, + void *sp_manifest); /* * Helper function to perform a synchronous entry into a SP. diff --git a/services/std_svc/spm/el3_spmc/spmc_main.c b/services/std_svc/spm/el3_spmc/spmc_main.c index ada6f455..c6ec30c3 100644 --- a/services/std_svc/spm/el3_spmc/spmc_main.c +++ b/services/std_svc/spm/el3_spmc/spmc_main.c @@ -1,11 +1,12 @@ /* - * Copyright (c) 2022, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2022-2024, ARM Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ #include <assert.h> #include <errno.h> +#include <stdio.h> #include <arch_helpers.h> #include <bl31/bl31.h> @@ -30,6 +31,17 @@ #include <platform_def.h> +/* FFA_MEM_PERM_* helpers */ +#define FFA_MEM_PERM_MASK U(7) +#define FFA_MEM_PERM_DATA_MASK U(3) +#define FFA_MEM_PERM_DATA_SHIFT U(0) +#define FFA_MEM_PERM_DATA_NA U(0) +#define FFA_MEM_PERM_DATA_RW U(1) +#define FFA_MEM_PERM_DATA_RES U(2) +#define FFA_MEM_PERM_DATA_RO U(3) +#define FFA_MEM_PERM_INST_EXEC (U(0) << 2) +#define FFA_MEM_PERM_INST_NON_EXEC (U(1) << 2) + /* Declare the maximum number of SPs and El3 LPs. */ #define MAX_SP_LP_PARTITIONS SECURE_PARTITION_COUNT + MAX_EL3_LP_DESCS_COUNT @@ -222,7 +234,7 @@ static uint64_t spmc_smc_return(uint32_t smc_fid, /* If we originated in the normal world then switch contexts. */ else if (!secure_origin && ffa_is_secure_world_id(dst_id)) { return spmd_smc_switch_state(smc_fid, secure_origin, x1, x2, - x3, x4, handle); + x3, x4, handle, flags); } else { /* Unknown State. */ panic(); @@ -390,6 +402,11 @@ static uint64_t direct_req_smc_handler(uint32_t smc_fid, FFA_ERROR_INVALID_PARAMETER); } + /* Protect the runtime state of a UP S-EL0 SP with a lock. */ + if (sp->runtime_el == S_EL0) { + spin_lock(&sp->rt_state_lock); + } + /* * Check that the target execution context is in a waiting state before * forwarding the direct request to it. @@ -398,6 +415,11 @@ static uint64_t direct_req_smc_handler(uint32_t smc_fid, if (sp->ec[idx].rt_state != RT_STATE_WAITING) { VERBOSE("SP context on core%u is not waiting (%u).\n", idx, sp->ec[idx].rt_model); + + if (sp->runtime_el == S_EL0) { + spin_unlock(&sp->rt_state_lock); + } + return spmc_ffa_error_return(handle, FFA_ERROR_BUSY); } @@ -408,6 +430,11 @@ static uint64_t direct_req_smc_handler(uint32_t smc_fid, sp->ec[idx].rt_state = RT_STATE_RUNNING; sp->ec[idx].rt_model = RT_MODEL_DIR_REQ; sp->ec[idx].dir_req_origin_id = src_id; + + if (sp->runtime_el == S_EL0) { + spin_unlock(&sp->rt_state_lock); + } + return spmc_smc_return(smc_fid, secure_origin, x1, x2, x3, x4, handle, cookie, flags, dst_id); } @@ -462,6 +489,10 @@ static uint64_t direct_resp_smc_handler(uint32_t smc_fid, FFA_ERROR_INVALID_PARAMETER); } + if (sp->runtime_el == S_EL0) { + spin_lock(&sp->rt_state_lock); + } + /* Sanity check state is being tracked correctly in the SPMC. */ idx = get_ec_index(sp); assert(sp->ec[idx].rt_state == RT_STATE_RUNNING); @@ -470,12 +501,18 @@ static uint64_t direct_resp_smc_handler(uint32_t smc_fid, if (sp->ec[idx].rt_model != RT_MODEL_DIR_REQ) { VERBOSE("SP context on core%u not handling direct req (%u).\n", idx, sp->ec[idx].rt_model); + if (sp->runtime_el == S_EL0) { + spin_unlock(&sp->rt_state_lock); + } return spmc_ffa_error_return(handle, FFA_ERROR_DENIED); } if (sp->ec[idx].dir_req_origin_id != dst_id) { WARN("Invalid direct resp partition ID 0x%x != 0x%x on core%u.\n", dst_id, sp->ec[idx].dir_req_origin_id, idx); + if (sp->runtime_el == S_EL0) { + spin_unlock(&sp->rt_state_lock); + } return spmc_ffa_error_return(handle, FFA_ERROR_DENIED); } @@ -485,6 +522,10 @@ static uint64_t direct_resp_smc_handler(uint32_t smc_fid, /* Clear the ongoing direct request ID. */ sp->ec[idx].dir_req_origin_id = INV_SP_ID; + if (sp->runtime_el == S_EL0) { + spin_unlock(&sp->rt_state_lock); + } + /* * If the receiver is not the SPMC then forward the response to the * Normal world. @@ -536,9 +577,15 @@ static uint64_t msg_wait_handler(uint32_t smc_fid, * Get the execution context of the SP that invoked FFA_MSG_WAIT. */ idx = get_ec_index(sp); + if (sp->runtime_el == S_EL0) { + spin_lock(&sp->rt_state_lock); + } /* Ensure SP execution context was in the right runtime model. */ if (sp->ec[idx].rt_model == RT_MODEL_DIR_REQ) { + if (sp->runtime_el == S_EL0) { + spin_unlock(&sp->rt_state_lock); + } return spmc_ffa_error_return(handle, FFA_ERROR_DENIED); } @@ -550,6 +597,9 @@ static uint64_t msg_wait_handler(uint32_t smc_fid, * state is updated after the exit. */ if (sp->ec[idx].rt_model == RT_MODEL_INIT) { + if (sp->runtime_el == S_EL0) { + spin_unlock(&sp->rt_state_lock); + } spmc_sp_synchronous_exit(&sp->ec[idx], x4); /* Should not get here */ panic(); @@ -567,9 +617,19 @@ static uint64_t msg_wait_handler(uint32_t smc_fid, cm_el1_sysregs_context_save(secure_state_in); cm_el1_sysregs_context_restore(secure_state_out); cm_set_next_eret_context(secure_state_out); + + if (sp->runtime_el == S_EL0) { + spin_unlock(&sp->rt_state_lock); + } + SMC_RET0(cm_get_context(secure_state_out)); } + /* Protect the runtime state of a S-EL0 SP with a lock. */ + if (sp->runtime_el == S_EL0) { + spin_unlock(&sp->rt_state_lock); + } + /* Forward the response to the Normal world. */ return spmc_smc_return(smc_fid, secure_origin, x1, x2, x3, x4, handle, cookie, flags, FFA_NWD_ID); @@ -1231,6 +1291,8 @@ static uint64_t ffa_features_handler(uint32_t smc_fid, case FFA_MSG_SEND_DIRECT_RESP_SMC64: case FFA_MEM_RELINQUISH: case FFA_MSG_WAIT: + case FFA_CONSOLE_LOG_SMC32: + case FFA_CONSOLE_LOG_SMC64: if (!secure_origin) { return spmc_ffa_error_return(handle, @@ -1343,14 +1405,21 @@ static uint64_t ffa_run_handler(uint32_t smc_fid, } idx = get_ec_index(sp); + if (idx != vcpu_id) { ERROR("Cannot run vcpu %d != %d.\n", idx, vcpu_id); return spmc_ffa_error_return(handle, FFA_ERROR_INVALID_PARAMETER); } + if (sp->runtime_el == S_EL0) { + spin_lock(&sp->rt_state_lock); + } rt_state = &((sp->ec[idx]).rt_state); rt_model = &((sp->ec[idx]).rt_model); if (*rt_state == RT_STATE_RUNNING) { + if (sp->runtime_el == S_EL0) { + spin_unlock(&sp->rt_state_lock); + } ERROR("Partition (0x%x) is already running.\n", target_id); return spmc_ffa_error_return(handle, FFA_ERROR_BUSY); } @@ -1377,6 +1446,10 @@ static uint64_t ffa_run_handler(uint32_t smc_fid, */ *rt_state = RT_STATE_RUNNING; + if (sp->runtime_el == S_EL0) { + spin_unlock(&sp->rt_state_lock); + } + return spmc_smc_return(smc_fid, secure_origin, x1, 0, 0, 0, handle, cookie, flags, target_id); } @@ -1406,6 +1479,58 @@ static uint64_t rx_release_handler(uint32_t smc_fid, SMC_RET1(handle, FFA_SUCCESS_SMC32); } +static uint64_t spmc_ffa_console_log(uint32_t smc_fid, + bool secure_origin, + uint64_t x1, + uint64_t x2, + uint64_t x3, + uint64_t x4, + void *cookie, + void *handle, + uint64_t flags) +{ + /* Maximum number of characters is 48: 6 registers of 8 bytes each. */ + char chars[48] = {0}; + size_t chars_max; + size_t chars_count = x1; + + /* Does not support request from Nwd. */ + if (!secure_origin) { + return spmc_ffa_error_return(handle, FFA_ERROR_NOT_SUPPORTED); + } + + assert(smc_fid == FFA_CONSOLE_LOG_SMC32 || smc_fid == FFA_CONSOLE_LOG_SMC64); + if (smc_fid == FFA_CONSOLE_LOG_SMC32) { + uint32_t *registers = (uint32_t *)chars; + registers[0] = (uint32_t)x2; + registers[1] = (uint32_t)x3; + registers[2] = (uint32_t)x4; + registers[3] = (uint32_t)SMC_GET_GP(handle, CTX_GPREG_X5); + registers[4] = (uint32_t)SMC_GET_GP(handle, CTX_GPREG_X6); + registers[5] = (uint32_t)SMC_GET_GP(handle, CTX_GPREG_X7); + chars_max = 6 * sizeof(uint32_t); + } else { + uint64_t *registers = (uint64_t *)chars; + registers[0] = x2; + registers[1] = x3; + registers[2] = x4; + registers[3] = SMC_GET_GP(handle, CTX_GPREG_X5); + registers[4] = SMC_GET_GP(handle, CTX_GPREG_X6); + registers[5] = SMC_GET_GP(handle, CTX_GPREG_X7); + chars_max = 6 * sizeof(uint64_t); + } + + if ((chars_count == 0) || (chars_count > chars_max)) { + return spmc_ffa_error_return(handle, FFA_ERROR_INVALID_PARAMETER); + } + + for (size_t i = 0; (i < chars_count) && (chars[i] != '\0'); i++) { + putchar(chars[i]); + } + + SMC_RET1(handle, FFA_SUCCESS_SMC32); +} + /* * Perform initial validation on the provided secondary entry point. * For now ensure it does not lie within the BL31 Image or the SP's @@ -1504,6 +1629,223 @@ static uint64_t ffa_sec_ep_register_handler(uint32_t smc_fid, SMC_RET1(handle, FFA_SUCCESS_SMC32); } +/******************************************************************************* + * Permissions are encoded using a different format in the FFA_MEM_PERM_* ABIs + * than in the Trusted Firmware, where the mmap_attr_t enum type is used. This + * function converts a permission value from the FF-A format to the mmap_attr_t + * format by setting MT_RW/MT_RO, MT_USER/MT_PRIVILEGED and + * MT_EXECUTE/MT_EXECUTE_NEVER. The other fields are left as 0 because they are + * ignored by the function xlat_change_mem_attributes_ctx(). + ******************************************************************************/ +static unsigned int ffa_perm_to_mmap_perm(unsigned int perms) +{ + unsigned int tf_attr = 0U; + unsigned int access; + + /* Deal with data access permissions first. */ + access = (perms & FFA_MEM_PERM_DATA_MASK) >> FFA_MEM_PERM_DATA_SHIFT; + + switch (access) { + case FFA_MEM_PERM_DATA_RW: + /* Return 0 if the execute is set with RW. */ + if ((perms & FFA_MEM_PERM_INST_NON_EXEC) != 0) { + tf_attr |= MT_RW | MT_USER | MT_EXECUTE_NEVER; + } + break; + + case FFA_MEM_PERM_DATA_RO: + tf_attr |= MT_RO | MT_USER; + /* Deal with the instruction access permissions next. */ + if ((perms & FFA_MEM_PERM_INST_NON_EXEC) == 0) { + tf_attr |= MT_EXECUTE; + } else { + tf_attr |= MT_EXECUTE_NEVER; + } + break; + + case FFA_MEM_PERM_DATA_NA: + default: + return tf_attr; + } + + return tf_attr; +} + +/******************************************************************************* + * Handler to set the permissions of a set of contiguous pages of a S-EL0 SP + ******************************************************************************/ +static uint64_t ffa_mem_perm_set_handler(uint32_t smc_fid, + bool secure_origin, + uint64_t x1, + uint64_t x2, + uint64_t x3, + uint64_t x4, + void *cookie, + void *handle, + uint64_t flags) +{ + struct secure_partition_desc *sp; + unsigned int idx; + uintptr_t base_va = (uintptr_t) x1; + size_t size = (size_t)(x2 * PAGE_SIZE); + uint32_t tf_attr; + int ret; + + /* This request cannot originate from the Normal world. */ + if (!secure_origin) { + return spmc_ffa_error_return(handle, FFA_ERROR_NOT_SUPPORTED); + } + + if (size == 0) { + return spmc_ffa_error_return(handle, + FFA_ERROR_INVALID_PARAMETER); + } + + /* Get the context of the current SP. */ + sp = spmc_get_current_sp_ctx(); + if (sp == NULL) { + return spmc_ffa_error_return(handle, + FFA_ERROR_INVALID_PARAMETER); + } + + /* A S-EL1 SP has no business invoking this ABI. */ + if (sp->runtime_el == S_EL1) { + return spmc_ffa_error_return(handle, FFA_ERROR_DENIED); + } + + if ((x3 & ~((uint64_t)FFA_MEM_PERM_MASK)) != 0) { + return spmc_ffa_error_return(handle, + FFA_ERROR_INVALID_PARAMETER); + } + + /* Get the execution context of the calling SP. */ + idx = get_ec_index(sp); + + /* + * Ensure that the S-EL0 SP is initialising itself. We do not need to + * synchronise this operation through a spinlock since a S-EL0 SP is UP + * and can only be initialising on this cpu. + */ + if (sp->ec[idx].rt_model != RT_MODEL_INIT) { + return spmc_ffa_error_return(handle, FFA_ERROR_DENIED); + } + + VERBOSE("Setting memory permissions:\n"); + VERBOSE(" Start address : 0x%lx\n", base_va); + VERBOSE(" Number of pages: %lu (%zu bytes)\n", x2, size); + VERBOSE(" Attributes : 0x%x\n", (uint32_t)x3); + + /* Convert inbound permissions to TF-A permission attributes */ + tf_attr = ffa_perm_to_mmap_perm((unsigned int)x3); + if (tf_attr == 0U) { + return spmc_ffa_error_return(handle, + FFA_ERROR_INVALID_PARAMETER); + } + + /* Request the change in permissions */ + ret = xlat_change_mem_attributes_ctx(sp->xlat_ctx_handle, + base_va, size, tf_attr); + if (ret != 0) { + return spmc_ffa_error_return(handle, + FFA_ERROR_INVALID_PARAMETER); + } + + SMC_RET1(handle, FFA_SUCCESS_SMC32); +} + +/******************************************************************************* + * Permissions are encoded using a different format in the FFA_MEM_PERM_* ABIs + * than in the Trusted Firmware, where the mmap_attr_t enum type is used. This + * function converts a permission value from the mmap_attr_t format to the FF-A + * format. + ******************************************************************************/ +static unsigned int mmap_perm_to_ffa_perm(unsigned int attr) +{ + unsigned int perms = 0U; + unsigned int data_access; + + if ((attr & MT_USER) == 0) { + /* No access from EL0. */ + data_access = FFA_MEM_PERM_DATA_NA; + } else { + if ((attr & MT_RW) != 0) { + data_access = FFA_MEM_PERM_DATA_RW; + } else { + data_access = FFA_MEM_PERM_DATA_RO; + } + } + + perms |= (data_access & FFA_MEM_PERM_DATA_MASK) + << FFA_MEM_PERM_DATA_SHIFT; + + if ((attr & MT_EXECUTE_NEVER) != 0U) { + perms |= FFA_MEM_PERM_INST_NON_EXEC; + } + + return perms; +} + +/******************************************************************************* + * Handler to get the permissions of a set of contiguous pages of a S-EL0 SP + ******************************************************************************/ +static uint64_t ffa_mem_perm_get_handler(uint32_t smc_fid, + bool secure_origin, + uint64_t x1, + uint64_t x2, + uint64_t x3, + uint64_t x4, + void *cookie, + void *handle, + uint64_t flags) +{ + struct secure_partition_desc *sp; + unsigned int idx; + uintptr_t base_va = (uintptr_t)x1; + uint32_t tf_attr = 0; + int ret; + + /* This request cannot originate from the Normal world. */ + if (!secure_origin) { + return spmc_ffa_error_return(handle, FFA_ERROR_NOT_SUPPORTED); + } + + /* Get the context of the current SP. */ + sp = spmc_get_current_sp_ctx(); + if (sp == NULL) { + return spmc_ffa_error_return(handle, + FFA_ERROR_INVALID_PARAMETER); + } + + /* A S-EL1 SP has no business invoking this ABI. */ + if (sp->runtime_el == S_EL1) { + return spmc_ffa_error_return(handle, FFA_ERROR_DENIED); + } + + /* Get the execution context of the calling SP. */ + idx = get_ec_index(sp); + + /* + * Ensure that the S-EL0 SP is initialising itself. We do not need to + * synchronise this operation through a spinlock since a S-EL0 SP is UP + * and can only be initialising on this cpu. + */ + if (sp->ec[idx].rt_model != RT_MODEL_INIT) { + return spmc_ffa_error_return(handle, FFA_ERROR_DENIED); + } + + /* Request the permissions */ + ret = xlat_get_mem_attributes_ctx(sp->xlat_ctx_handle, base_va, &tf_attr); + if (ret != 0) { + return spmc_ffa_error_return(handle, + FFA_ERROR_INVALID_PARAMETER); + } + + /* Convert TF-A permission to FF-A permissions attributes. */ + x2 = mmap_perm_to_ffa_perm(tf_attr); + + SMC_RET3(handle, FFA_SUCCESS_SMC32, 0, x2); +} + /******************************************************************************* * This function will parse the Secure Partition Manifest. From manifest, it * will fetch details for preparing Secure partition image context and secure @@ -1588,7 +1930,7 @@ static int sp_manifest_parse(void *sp_manifest, int offset, * since this is currently a hardcoded value for S-EL1 partitions * we don't need to save it here, just validate. */ - if (config_32 != PLATFORM_CORE_COUNT) { + if ((sp->runtime_el == S_EL1) && (config_32 != PLATFORM_CORE_COUNT)) { ERROR("SP Execution Context Count (%u) must be %u.\n", config_32, PLATFORM_CORE_COUNT); return -EINVAL; @@ -1615,6 +1957,11 @@ static int sp_manifest_parse(void *sp_manifest, int offset, if (ret != 0) { WARN("Missing Power Management Messages entry.\n"); } else { + if ((sp->runtime_el == S_EL0) && (config_32 != 0)) { + ERROR("Power messages not supported for S-EL0 SP\n"); + return -EINVAL; + } + /* * Ensure only the currently supported power messages have * been requested. @@ -1704,7 +2051,8 @@ static int find_and_prepare_sp_context(void) * the manifest as boot information later. */ next_image_ep_info->args.arg1 = fdt_totalsize(sp_manifest); - INFO("Manifest size = %lu bytes.\n", next_image_ep_info->args.arg1); + INFO("Manifest adr = %lx , size = %lu bytes\n", manifest_base, + next_image_ep_info->args.arg1); /* * Select an SP descriptor for initialising the partition's execution @@ -1712,6 +2060,11 @@ static int find_and_prepare_sp_context(void) */ sp = spmc_get_current_sp_ctx(); +#if SPMC_AT_EL3_SEL0_SP + /* Assign translation tables context. */ + sp_desc->xlat_ctx_handle = spm_get_sp_xlat_context(); + +#endif /* SPMC_AT_EL3_SEL0_SP */ /* Initialize entry point information for the SP */ SET_PARAM_HEAD(next_image_ep_info, PARAM_EP, VERSION_1, SECURE | EP_ST_ENABLE); @@ -1725,7 +2078,7 @@ static int find_and_prepare_sp_context(void) } /* Check that the runtime EL in the manifest was correct. */ - if (sp->runtime_el != S_EL1) { + if (sp->runtime_el != S_EL0 && sp->runtime_el != S_EL1) { ERROR("Unexpected runtime EL: %d\n", sp->runtime_el); return -EINVAL; } @@ -1734,11 +2087,29 @@ static int find_and_prepare_sp_context(void) spmc_sp_common_setup(sp, next_image_ep_info, boot_info_reg); /* Perform any initialisation specific to S-EL1 SPs. */ - spmc_el1_sp_setup(sp, next_image_ep_info); + if (sp->runtime_el == S_EL1) { + spmc_el1_sp_setup(sp, next_image_ep_info); + } + +#if SPMC_AT_EL3_SEL0_SP + /* Setup spsr in endpoint info for common context management routine. */ + if (sp->runtime_el == S_EL0) { + spmc_el0_sp_spsr_setup(next_image_ep_info); + } +#endif /* SPMC_AT_EL3_SEL0_SP */ /* Initialize the SP context with the required ep info. */ spmc_sp_common_ep_commit(sp, next_image_ep_info); +#if SPMC_AT_EL3_SEL0_SP + /* + * Perform any initialisation specific to S-EL0 not set by common + * context management routine. + */ + if (sp->runtime_el == S_EL0) { + spmc_el0_sp_setup(sp, boot_info_reg, sp_manifest); + } +#endif /* SPMC_AT_EL3_SEL0_SP */ return 0; } @@ -2049,7 +2420,19 @@ uint64_t spmc_smc_handler(uint32_t smc_fid, case FFA_MEM_RECLAIM: return spmc_ffa_mem_reclaim(smc_fid, secure_origin, x1, x2, x3, - x4, cookie, handle, flags); + x4, cookie, handle, flags); + case FFA_CONSOLE_LOG_SMC32: + case FFA_CONSOLE_LOG_SMC64: + return spmc_ffa_console_log(smc_fid, secure_origin, x1, x2, x3, + x4, cookie, handle, flags); + + case FFA_MEM_PERM_GET: + return ffa_mem_perm_get_handler(smc_fid, secure_origin, x1, x2, + x3, x4, cookie, handle, flags); + + case FFA_MEM_PERM_SET: + return ffa_mem_perm_set_handler(smc_fid, secure_origin, x1, x2, + x3, x4, cookie, handle, flags); default: WARN("Unsupported FF-A call 0x%08x.\n", smc_fid); @@ -2104,9 +2487,11 @@ static uint64_t spmc_sp_interrupt_handler(uint32_t id, /* * Forward the interrupt to the S-EL1 SP. The interrupt ID is not * populated as the SP can determine this by itself. + * The flags field is forced to 0 mainly to pass the SVE hint bit + * cleared for consumption by the lower EL. */ return spmd_smc_switch_state(FFA_INTERRUPT, false, FFA_PARAM_MBZ, FFA_PARAM_MBZ, FFA_PARAM_MBZ, FFA_PARAM_MBZ, - handle); + handle, 0ULL); } diff --git a/services/std_svc/spm/el3_spmc/spmc_pm.c b/services/std_svc/spm/el3_spmc/spmc_pm.c index c7e864f3..517d6d5e 100644 --- a/services/std_svc/spm/el3_spmc/spmc_pm.c +++ b/services/std_svc/spm/el3_spmc/spmc_pm.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2022-2023, ARM Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -36,7 +36,7 @@ static void spmc_build_pm_message(gp_regs_t *gpregs, } /******************************************************************************* - * This CPU has been turned on. Enter the SP to initialise S-EL1. + * This CPU has been turned on. Enter the SP to initialise S-EL0 or S-EL1. ******************************************************************************/ static void spmc_cpu_on_finish_handler(u_register_t unused) { @@ -49,6 +49,19 @@ static void spmc_cpu_on_finish_handler(u_register_t unused) /* Sanity check for a NULL pointer dereference. */ assert(sp != NULL); + /* Obtain a reference to the SP execution context */ + ec = &sp->ec[get_ec_index(sp)]; + + /* + * In case of a S-EL0 SP, only initialise the context data structure for + * the secure world on this cpu and return. + */ + if (sp->runtime_el == S_EL0) { + /* Assign the context of the SP to this CPU */ + cm_set_context(&(ec->cpu_ctx), SECURE); + return; + } + /* Initialize entry point information for the SP. */ SET_PARAM_HEAD(&sec_ec_ep_info, PARAM_EP, VERSION_1, SECURE | EP_ST_ENABLE); diff --git a/services/std_svc/spm/el3_spmc/spmc_setup.c b/services/std_svc/spm/el3_spmc/spmc_setup.c index 6de25f64..f7357f1f 100644 --- a/services/std_svc/spm/el3_spmc/spmc_setup.c +++ b/services/std_svc/spm/el3_spmc/spmc_setup.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2022-2024, Arm Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -20,6 +20,7 @@ #include <plat/common/platform.h> #include <services/ffa_svc.h> #include "spm_common.h" +#include "spm_shim_private.h" #include "spmc.h" #include <tools_share/firmware_image_package.h> @@ -30,6 +31,26 @@ */ static uint8_t ffa_boot_info_mem[PAGE_SIZE] __aligned(PAGE_SIZE); +/* + * We need to choose one execution context from all those available for a S-EL0 + * SP. This execution context will be used subsequently irrespective of which + * physical CPU the SP runs on. + */ +#define SEL0_SP_EC_INDEX 0 +#define SP_MEM_READ 0x1 +#define SP_MEM_WRITE 0x2 +#define SP_MEM_EXECUTE 0x4 +#define SP_MEM_NON_SECURE 0x8 +#define SP_MEM_READ_ONLY SP_MEM_READ +#define SP_MEM_READ_WRITE (SP_MEM_READ | SP_MEM_WRITE) + +/* Type of the memory region in SP's manifest. */ +enum sp_memory_region_type { + SP_MEM_REGION_DEVICE, + SP_MEM_REGION_MEMORY, + SP_MEM_REGION_NOT_SPECIFIED +}; + /* * This function creates a initialization descriptor in the memory reserved * for passing boot information to an SP. It then copies the partition manifest @@ -143,14 +164,310 @@ static void spmc_create_boot_info(entry_point_info_t *ep_info, } /* - * We are assuming that the index of the execution - * context used is the linear index of the current physical cpu. + * S-EL1 partitions can be assigned with multiple execution contexts, each + * pinned to the physical CPU. Each execution context index corresponds to the + * respective liner core position. + * S-EL0 partitions execute in a single execution context (index 0). */ unsigned int get_ec_index(struct secure_partition_desc *sp) { - return plat_my_core_pos(); + return (sp->runtime_el == S_EL0) ? + SEL0_SP_EC_INDEX : plat_my_core_pos(); +} + +#if SPMC_AT_EL3_SEL0_SP +/* Setup spsr in entry point info for common context management code to use. */ +void spmc_el0_sp_spsr_setup(entry_point_info_t *ep_info) +{ + /* Setup Secure Partition SPSR for S-EL0 SP. */ + ep_info->spsr = SPSR_64(MODE_EL0, MODE_SP_EL0, DISABLE_ALL_EXCEPTIONS); +} + +static void read_optional_string(void *manifest, int32_t offset, + char *property, char *out, size_t len) +{ + const fdt32_t *prop; + int lenp; + + prop = fdt_getprop(manifest, offset, property, &lenp); + if (prop == NULL) { + out[0] = '\0'; + } else { + memcpy(out, prop, MIN(lenp, (int)len)); + } +} + +/******************************************************************************* + * This function will parse the Secure Partition Manifest for fetching secure + * partition specific memory/device region details. It will find base address, + * size, memory attributes for each region and then add the respective region + * into secure parition's translation context. + ******************************************************************************/ +static void populate_sp_regions(struct secure_partition_desc *sp, + void *sp_manifest, int node, + enum sp_memory_region_type type) +{ + uintptr_t base_address; + uint32_t mem_attr, mem_region, size; + struct mmap_region sp_mem_regions = {0}; + int32_t offset, ret; + char *compatibility[SP_MEM_REGION_NOT_SPECIFIED] = { + "arm,ffa-manifest-device-regions", + "arm,ffa-manifest-memory-regions" + }; + char description[10]; + char *property; + char *region[SP_MEM_REGION_NOT_SPECIFIED] = { + "device regions", + "memory regions" + }; + + if (type >= SP_MEM_REGION_NOT_SPECIFIED) { + WARN("Invalid region type\n"); + return; + } + + INFO("Mapping SP's %s\n", region[type]); + + if (fdt_node_check_compatible(sp_manifest, node, + compatibility[type]) != 0) { + WARN("Incompatible region node in manifest\n"); + return; + } + + for (offset = fdt_first_subnode(sp_manifest, node), mem_region = 0; + offset >= 0; + offset = fdt_next_subnode(sp_manifest, offset), mem_region++) { + read_optional_string(sp_manifest, offset, "description", + description, sizeof(description)); + + INFO("Mapping: region: %d, %s\n", mem_region, description); + + property = "base-address"; + ret = fdt_read_uint64(sp_manifest, offset, property, + &base_address); + if (ret < 0) { + WARN("Missing:%s for %s.\n", property, description); + continue; + } + + property = "pages-count"; + ret = fdt_read_uint32(sp_manifest, offset, property, &size); + if (ret < 0) { + WARN("Missing: %s for %s.\n", property, description); + continue; + } + size *= PAGE_SIZE; + + property = "attributes"; + ret = fdt_read_uint32(sp_manifest, offset, property, &mem_attr); + if (ret < 0) { + WARN("Missing: %s for %s.\n", property, description); + continue; + } + + sp_mem_regions.attr = MT_USER; + if (type == SP_MEM_REGION_DEVICE) { + sp_mem_regions.attr |= MT_EXECUTE_NEVER; + } else { + sp_mem_regions.attr |= MT_MEMORY; + if ((mem_attr & SP_MEM_EXECUTE) == SP_MEM_EXECUTE) { + sp_mem_regions.attr &= ~MT_EXECUTE_NEVER; + } else { + sp_mem_regions.attr |= MT_EXECUTE_NEVER; + } + } + + if ((mem_attr & SP_MEM_READ_WRITE) == SP_MEM_READ_WRITE) { + sp_mem_regions.attr |= MT_RW; + } + + if ((mem_attr & SP_MEM_NON_SECURE) == SP_MEM_NON_SECURE) { + sp_mem_regions.attr |= MT_NS; + } else { + sp_mem_regions.attr |= MT_SECURE; + } + + sp_mem_regions.base_pa = base_address; + sp_mem_regions.base_va = base_address; + sp_mem_regions.size = size; + + INFO("Adding PA: 0x%llx VA: 0x%lx Size: 0x%lx attr:0x%x\n", + sp_mem_regions.base_pa, + sp_mem_regions.base_va, + sp_mem_regions.size, + sp_mem_regions.attr); + + if (type == SP_MEM_REGION_DEVICE) { + sp_mem_regions.granularity = XLAT_BLOCK_SIZE(1); + } else { + sp_mem_regions.granularity = XLAT_BLOCK_SIZE(3); + } + mmap_add_region_ctx(sp->xlat_ctx_handle, &sp_mem_regions); + } +} + +static void spmc_el0_sp_setup_mmu(struct secure_partition_desc *sp, + cpu_context_t *ctx) +{ + xlat_ctx_t *xlat_ctx; + uint64_t mmu_cfg_params[MMU_CFG_PARAM_MAX]; + + xlat_ctx = sp->xlat_ctx_handle; + init_xlat_tables_ctx(sp->xlat_ctx_handle); + setup_mmu_cfg((uint64_t *)&mmu_cfg_params, 0, xlat_ctx->base_table, + xlat_ctx->pa_max_address, xlat_ctx->va_max_address, + EL1_EL0_REGIME); + + write_el1_ctx_common(get_el1_sysregs_ctx(ctx), mair_el1, + mmu_cfg_params[MMU_CFG_MAIR]); + + write_ctx_tcr_el1_reg_errata(ctx, mmu_cfg_params[MMU_CFG_TCR]); + + write_el1_ctx_common(get_el1_sysregs_ctx(ctx), ttbr0_el1, + mmu_cfg_params[MMU_CFG_TTBR0]); +} + +static void spmc_el0_sp_setup_sctlr_el1(cpu_context_t *ctx) +{ + u_register_t sctlr_el1_val; + + /* Setup SCTLR_EL1 */ + sctlr_el1_val = read_ctx_sctlr_el1_reg_errata(ctx); + + sctlr_el1_val |= + /*SCTLR_EL1_RES1 |*/ + /* Don't trap DC CVAU, DC CIVAC, DC CVAC, DC CVAP, or IC IVAU */ + SCTLR_UCI_BIT | + /* RW regions at xlat regime EL1&0 are forced to be XN. */ + SCTLR_WXN_BIT | + /* Don't trap to EL1 execution of WFI or WFE at EL0. */ + SCTLR_NTWI_BIT | SCTLR_NTWE_BIT | + /* Don't trap to EL1 accesses to CTR_EL0 from EL0. */ + SCTLR_UCT_BIT | + /* Don't trap to EL1 execution of DZ ZVA at EL0. */ + SCTLR_DZE_BIT | + /* Enable SP Alignment check for EL0 */ + SCTLR_SA0_BIT | + /* Don't change PSTATE.PAN on taking an exception to EL1 */ + SCTLR_SPAN_BIT | + /* Allow cacheable data and instr. accesses to normal memory. */ + SCTLR_C_BIT | SCTLR_I_BIT | + /* Enable MMU. */ + SCTLR_M_BIT; + + sctlr_el1_val &= ~( + /* Explicit data accesses at EL0 are little-endian. */ + SCTLR_E0E_BIT | + /* + * Alignment fault checking disabled when at EL1 and EL0 as + * the UEFI spec permits unaligned accesses. + */ + SCTLR_A_BIT | + /* Accesses to DAIF from EL0 are trapped to EL1. */ + SCTLR_UMA_BIT + ); + + /* Store the initialised SCTLR_EL1 value in the cpu_context */ + write_ctx_sctlr_el1_reg_errata(ctx, sctlr_el1_val); } +static void spmc_el0_sp_setup_system_registers(struct secure_partition_desc *sp, + cpu_context_t *ctx) +{ + + spmc_el0_sp_setup_mmu(sp, ctx); + + spmc_el0_sp_setup_sctlr_el1(ctx); + + /* Setup other system registers. */ + + /* Shim Exception Vector Base Address */ + write_el1_ctx_common(get_el1_sysregs_ctx(ctx), vbar_el1, + SPM_SHIM_EXCEPTIONS_PTR); +#if NS_TIMER_SWITCH + write_el1_ctx_arch_timer(get_el1_sysregs_ctx(ctx), cntkctl_el1, + EL0PTEN_BIT | EL0VTEN_BIT | EL0PCTEN_BIT | EL0VCTEN_BIT); +#endif + + /* + * FPEN: Allow the Secure Partition to access FP/SIMD registers. + * Note that SPM will not do any saving/restoring of these registers on + * behalf of the SP. This falls under the SP's responsibility. + * TTA: Enable access to trace registers. + * ZEN (v8.2): Trap SVE instructions and access to SVE registers. + */ + write_el1_ctx_common(get_el1_sysregs_ctx(ctx), cpacr_el1, + CPACR_EL1_FPEN(CPACR_EL1_FP_TRAP_NONE)); +} + +/* Setup context of an EL0 Secure Partition. */ +void spmc_el0_sp_setup(struct secure_partition_desc *sp, + int32_t boot_info_reg, + void *sp_manifest) +{ + mmap_region_t sel1_exception_vectors = + MAP_REGION_FLAT(SPM_SHIM_EXCEPTIONS_START, + SPM_SHIM_EXCEPTIONS_SIZE, + MT_CODE | MT_SECURE | MT_PRIVILEGED); + cpu_context_t *ctx; + int node; + int offset = 0; + + ctx = &sp->ec[SEL0_SP_EC_INDEX].cpu_ctx; + + sp->xlat_ctx_handle->xlat_regime = EL1_EL0_REGIME; + + /* This region contains the exception vectors used at S-EL1. */ + mmap_add_region_ctx(sp->xlat_ctx_handle, + &sel1_exception_vectors); + + /* + * If the SP manifest specified the register to pass the address of the + * boot information, then map the memory region to pass boot + * information. + */ + if (boot_info_reg >= 0) { + mmap_region_t ffa_boot_info_region = MAP_REGION_FLAT( + (uintptr_t) ffa_boot_info_mem, + PAGE_SIZE, + MT_RO_DATA | MT_SECURE | MT_USER); + mmap_add_region_ctx(sp->xlat_ctx_handle, &ffa_boot_info_region); + } + + /* + * Parse the manifest for any device regions that the SP wants to be + * mapped in its translation regime. + */ + node = fdt_subnode_offset_namelen(sp_manifest, offset, + "device-regions", + sizeof("device-regions") - 1); + if (node < 0) { + WARN("Not found device-region configuration for SP.\n"); + } else { + populate_sp_regions(sp, sp_manifest, node, + SP_MEM_REGION_DEVICE); + } + + /* + * Parse the manifest for any memory regions that the SP wants to be + * mapped in its translation regime. + */ + node = fdt_subnode_offset_namelen(sp_manifest, offset, + "memory-regions", + sizeof("memory-regions") - 1); + if (node < 0) { + WARN("Not found memory-region configuration for SP.\n"); + } else { + populate_sp_regions(sp, sp_manifest, node, + SP_MEM_REGION_MEMORY); + } + + spmc_el0_sp_setup_system_registers(sp, ctx); + +} +#endif /* SPMC_AT_EL3_SEL0_SP */ + /* S-EL1 partition specific initialisation. */ void spmc_el1_sp_setup(struct secure_partition_desc *sp, entry_point_info_t *ep_info) @@ -211,12 +528,6 @@ void spmc_sp_common_setup(struct secure_partition_desc *sp, sp->sp_id = sp_id; } - /* - * We currently only support S-EL1 partitions so ensure this is the - * case. - */ - assert(sp->runtime_el == S_EL1); - /* Check if the SP wants to use the FF-A boot protocol. */ if (boot_info_reg >= 0) { /* diff --git a/services/std_svc/spm/spm_mm/spm_mm_main.c b/services/std_svc/spm/spm_mm/spm_mm_main.c index 1ff7bb77..34e2c00f 100644 --- a/services/std_svc/spm/spm_mm/spm_mm_main.c +++ b/services/std_svc/spm/spm_mm/spm_mm_main.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2022, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2017-2024, Arm Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -13,6 +13,7 @@ #include <common/debug.h> #include <common/runtime_svc.h> #include <lib/el3_runtime/context_mgmt.h> +#include <lib/el3_runtime/simd_ctx.h> #include <lib/smccc.h> #include <lib/spinlock.h> #include <lib/utils.h> @@ -190,13 +191,13 @@ uint64_t spm_mm_sp_call(uint32_t smc_fid, uint64_t x1, uint64_t x2, uint64_t x3) uint64_t rc; sp_context_t *sp_ptr = &sp_ctx; -#if CTX_INCLUDE_FPREGS +#if CTX_INCLUDE_FPREGS || CTX_INCLUDE_SVE_REGS /* - * SP runs to completion, no need to restore FP registers of secure context. - * Save FP registers only for non secure context. + * SP runs to completion, no need to restore FP/SVE registers of secure context. + * Save FP/SVE registers only for non secure context. */ - fpregs_context_save(get_fpregs_ctx(cm_get_context(NON_SECURE))); -#endif + simd_ctx_save(NON_SECURE, false); +#endif /* CTX_INCLUDE_FPREGS || CTX_INCLUDE_SVE_REGS */ /* Wait until the Secure Partition is idle and set it to busy. */ sp_state_wait_switch(sp_ptr, SP_STATE_IDLE, SP_STATE_BUSY); @@ -216,13 +217,13 @@ uint64_t spm_mm_sp_call(uint32_t smc_fid, uint64_t x1, uint64_t x2, uint64_t x3) assert(sp_ptr->state == SP_STATE_BUSY); sp_state_set(sp_ptr, SP_STATE_IDLE); -#if CTX_INCLUDE_FPREGS +#if CTX_INCLUDE_FPREGS || CTX_INCLUDE_SVE_REGS /* - * SP runs to completion, no need to save FP registers of secure context. - * Restore only non secure world FP registers. + * SP runs to completion, no need to save FP/SVE registers of secure context. + * Restore only non secure world FP/SVE registers. */ - fpregs_context_restore(get_fpregs_ctx(cm_get_context(NON_SECURE))); -#endif + simd_ctx_restore(NON_SECURE); +#endif /* CTX_INCLUDE_FPREGS || CTX_INCLUDE_SVE_REGS */ return rc; } diff --git a/services/std_svc/spm/spm_mm/spm_mm_setup.c b/services/std_svc/spm/spm_mm/spm_mm_setup.c index 4e65c9cb..de054597 100644 --- a/services/std_svc/spm/spm_mm/spm_mm_setup.c +++ b/services/std_svc/spm/spm_mm/spm_mm_setup.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2023, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2017-2024, Arm Limited and Contributors. All rights reserved. * Copyright (c) 2021, NVIDIA Corporation. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause @@ -27,7 +27,7 @@ void spm_sp_setup(sp_context_t *sp_ctx) { cpu_context_t *ctx = &(sp_ctx->cpu_ctx); - + u_register_t sctlr_el1_val; /* Pointer to the MP information from the platform port. */ const spm_mm_boot_info_t *sp_boot_info = plat_get_secure_partition_boot_info(NULL); @@ -122,19 +122,17 @@ void spm_sp_setup(sp_context_t *sp_ctx) xlat_ctx->pa_max_address, xlat_ctx->va_max_address, EL1_EL0_REGIME); - write_ctx_reg(get_el1_sysregs_ctx(ctx), CTX_MAIR_EL1, + write_el1_ctx_common(get_el1_sysregs_ctx(ctx), mair_el1, mmu_cfg_params[MMU_CFG_MAIR]); + write_ctx_tcr_el1_reg_errata(ctx, mmu_cfg_params[MMU_CFG_TCR]); - write_ctx_reg(get_el1_sysregs_ctx(ctx), CTX_TCR_EL1, - mmu_cfg_params[MMU_CFG_TCR]); - - write_ctx_reg(get_el1_sysregs_ctx(ctx), CTX_TTBR0_EL1, + write_el1_ctx_common(get_el1_sysregs_ctx(ctx), ttbr0_el1, mmu_cfg_params[MMU_CFG_TTBR0]); /* Setup SCTLR_EL1 */ - u_register_t sctlr_el1 = read_ctx_reg(get_el1_sysregs_ctx(ctx), CTX_SCTLR_EL1); + sctlr_el1_val = read_ctx_sctlr_el1_reg_errata(ctx); - sctlr_el1 |= + sctlr_el1_val |= /*SCTLR_EL1_RES1 |*/ /* Don't trap DC CVAU, DC CIVAC, DC CVAC, DC CVAP, or IC IVAU */ SCTLR_UCI_BIT | @@ -156,7 +154,7 @@ void spm_sp_setup(sp_context_t *sp_ctx) SCTLR_M_BIT ; - sctlr_el1 &= ~( + sctlr_el1_val &= ~( /* Explicit data accesses at EL0 are little-endian. */ SCTLR_E0E_BIT | /* @@ -168,7 +166,8 @@ void spm_sp_setup(sp_context_t *sp_ctx) SCTLR_UMA_BIT ); - write_ctx_reg(get_el1_sysregs_ctx(ctx), CTX_SCTLR_EL1, sctlr_el1); + /* Store the initialised SCTLR_EL1 value in the cpu_context */ + write_ctx_sctlr_el1_reg_errata(ctx, sctlr_el1_val); /* * Setup other system registers @@ -176,10 +175,10 @@ void spm_sp_setup(sp_context_t *sp_ctx) */ /* Shim Exception Vector Base Address */ - write_ctx_reg(get_el1_sysregs_ctx(ctx), CTX_VBAR_EL1, + write_el1_ctx_common(get_el1_sysregs_ctx(ctx), vbar_el1, SPM_SHIM_EXCEPTIONS_PTR); - write_ctx_reg(get_el1_sysregs_ctx(ctx), CTX_CNTKCTL_EL1, + write_el1_ctx_arch_timer(get_el1_sysregs_ctx(ctx), cntkctl_el1, EL0PTEN_BIT | EL0VTEN_BIT | EL0PCTEN_BIT | EL0VCTEN_BIT); /* @@ -189,7 +188,7 @@ void spm_sp_setup(sp_context_t *sp_ctx) * TTA: Enable access to trace registers. * ZEN (v8.2): Trap SVE instructions and access to SVE registers. */ - write_ctx_reg(get_el1_sysregs_ctx(ctx), CTX_CPACR_EL1, + write_el1_ctx_common(get_el1_sysregs_ctx(ctx), cpacr_el1, CPACR_EL1_FPEN(CPACR_EL1_FP_TRAP_NONE)); /* diff --git a/services/std_svc/spmd/spmd_logical_sp.c b/services/std_svc/spmd/spmd_logical_sp.c index d992187d..64d506e7 100644 --- a/services/std_svc/spmd/spmd_logical_sp.c +++ b/services/std_svc/spmd/spmd_logical_sp.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023, Arm Limited and Contributors. All rights reserved. + * Copyright (c) 2023-2024, Arm Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -528,9 +528,10 @@ bool spmd_el3_invoke_partition_info_get( } /* Save the non-secure context before entering SPMC */ - cm_el1_sysregs_context_save(NON_SECURE); #if SPMD_SPM_AT_SEL2 cm_el2_sysregs_context_save(NON_SECURE); +#else + cm_el1_sysregs_context_save(NON_SECURE); #endif spmd_build_ffa_info_get_regs(ctx, target_uuid, start_index, tag); @@ -548,9 +549,10 @@ bool spmd_el3_invoke_partition_info_get( assert(is_ffa_error(retval) || is_ffa_success(retval)); - cm_el1_sysregs_context_restore(NON_SECURE); #if SPMD_SPM_AT_SEL2 cm_el2_sysregs_context_restore(NON_SECURE); +#else + cm_el1_sysregs_context_restore(NON_SECURE); #endif cm_set_next_eret_context(NON_SECURE); return true; @@ -667,9 +669,10 @@ bool spmd_el3_ffa_msg_direct_req(uint64_t x1, } /* Save the non-secure context before entering SPMC */ - cm_el1_sysregs_context_save(NON_SECURE); #if SPMD_SPM_AT_SEL2 cm_el2_sysregs_context_save(NON_SECURE); +#else + cm_el1_sysregs_context_save(NON_SECURE); #endif /* @@ -707,9 +710,10 @@ bool spmd_el3_ffa_msg_direct_req(uint64_t x1, ffa_endpoint_destination(x1))); } - cm_el1_sysregs_context_restore(NON_SECURE); #if SPMD_SPM_AT_SEL2 cm_el2_sysregs_context_restore(NON_SECURE); +#else + cm_el1_sysregs_context_restore(NON_SECURE); #endif cm_set_next_eret_context(NON_SECURE); diff --git a/services/std_svc/spmd/spmd_main.c b/services/std_svc/spmd/spmd_main.c index 066571e9..3953b245 100644 --- a/services/std_svc/spmd/spmd_main.c +++ b/services/std_svc/spmd/spmd_main.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020-2023, Arm Limited and Contributors. All rights reserved. + * Copyright (c) 2020-2024, Arm Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -51,27 +51,12 @@ static spmc_manifest_attribute_t spmc_attrs; ******************************************************************************/ static entry_point_info_t *spmc_ep_info; -/******************************************************************************* - * SPM Core context on CPU based on mpidr. - ******************************************************************************/ -spmd_spm_core_context_t *spmd_get_context_by_mpidr(uint64_t mpidr) -{ - int core_idx = plat_core_pos_by_mpidr(mpidr); - - if (core_idx < 0) { - ERROR("Invalid mpidr: %" PRIx64 ", returned ID: %d\n", mpidr, core_idx); - panic(); - } - - return &spm_core_context[core_idx]; -} - /******************************************************************************* * SPM Core context on current CPU get helper. ******************************************************************************/ spmd_spm_core_context_t *spmd_get_context(void) { - return spmd_get_context_by_mpidr(read_mpidr()); + return &spm_core_context[plat_my_core_pos()]; } /******************************************************************************* @@ -217,7 +202,6 @@ static uint64_t spmd_secure_interrupt_handler(uint32_t id, { spmd_spm_core_context_t *ctx = spmd_get_context(); gp_regs_t *gpregs = get_gpregs_ctx(&ctx->cpu_ctx); - unsigned int linear_id = plat_my_core_pos(); int64_t rc; /* Sanity check the security state when the exception was generated */ @@ -227,9 +211,18 @@ static uint64_t spmd_secure_interrupt_handler(uint32_t id, assert(handle == cm_get_context(NON_SECURE)); /* Save the non-secure context before entering SPMC */ - cm_el1_sysregs_context_save(NON_SECURE); #if SPMD_SPM_AT_SEL2 cm_el2_sysregs_context_save(NON_SECURE); +#else + cm_el1_sysregs_context_save(NON_SECURE); + +#if CTX_INCLUDE_FPREGS || CTX_INCLUDE_SVE_REGS + /* + * The hint bit denoting absence of SVE live state is effectively false + * in this scenario where execution was trapped to EL3 due to FIQ. + */ + simd_ctx_save(NON_SECURE, false); +#endif #endif /* Convey the event to the SPMC through the FFA_INTERRUPT interface. */ @@ -245,16 +238,28 @@ static uint64_t spmd_secure_interrupt_handler(uint32_t id, /* Mark current core as handling a secure interrupt. */ ctx->secure_interrupt_ongoing = true; +#if CTX_INCLUDE_FPREGS || CTX_INCLUDE_SVE_REGS + simd_ctx_restore(SECURE); +#endif rc = spmd_spm_core_sync_entry(ctx); + +#if CTX_INCLUDE_FPREGS || CTX_INCLUDE_SVE_REGS + simd_ctx_save(SECURE, false); +#endif if (rc != 0ULL) { - ERROR("%s failed (%" PRId64 ") on CPU%u\n", __func__, rc, linear_id); + ERROR("%s failed (%" PRId64 ") on CPU%u\n", __func__, rc, plat_my_core_pos()); } ctx->secure_interrupt_ongoing = false; - cm_el1_sysregs_context_restore(NON_SECURE); #if SPMD_SPM_AT_SEL2 cm_el2_sysregs_context_restore(NON_SECURE); +#else + cm_el1_sysregs_context_restore(NON_SECURE); + +#if CTX_INCLUDE_FPREGS || CTX_INCLUDE_SVE_REGS + simd_ctx_restore(NON_SECURE); +#endif #endif cm_set_next_eret_context(NON_SECURE); @@ -593,14 +598,17 @@ static int spmd_spmc_init(void *pm_addr) */ #if (EL3_EXCEPTION_HANDLING == 0) /* - * Register an interrupt handler routing Group0 interrupts to SPMD - * while the NWd is running. + * If EL3 interrupts are supported by the platform, register an + * interrupt handler routing Group0 interrupts to SPMD while the NWd is + * running. */ - rc = register_interrupt_type_handler(INTR_TYPE_EL3, - spmd_group0_interrupt_handler_nwd, - flags); - if (rc != 0) { - panic(); + if (plat_ic_has_interrupt_type(INTR_TYPE_EL3)) { + rc = register_interrupt_type_handler(INTR_TYPE_EL3, + spmd_group0_interrupt_handler_nwd, + flags); + if (rc != 0) { + panic(); + } } #endif @@ -667,32 +675,46 @@ uint64_t spmd_smc_switch_state(uint32_t smc_fid, uint64_t x2, uint64_t x3, uint64_t x4, - void *handle) + void *handle, + uint64_t flags) { unsigned int secure_state_in = (secure_origin) ? SECURE : NON_SECURE; unsigned int secure_state_out = (!secure_origin) ? SECURE : NON_SECURE; + void *ctx_out; - /* Save incoming security state */ #if SPMD_SPM_AT_SEL2 - if (secure_state_in == NON_SECURE) { - cm_el1_sysregs_context_save(secure_state_in); + if ((secure_state_out == SECURE) && (is_sve_hint_set(flags) == true)) { + /* + * Set the SVE hint bit in x0 and pass to the lower secure EL, + * if it was set by the caller. + */ + smc_fid |= (FUNCID_SVE_HINT_MASK << FUNCID_SVE_HINT_SHIFT); } +#endif + + /* Save incoming security state */ +#if SPMD_SPM_AT_SEL2 cm_el2_sysregs_context_save(secure_state_in); #else cm_el1_sysregs_context_save(secure_state_in); +#if CTX_INCLUDE_FPREGS || CTX_INCLUDE_SVE_REGS + /* Forward the hint bit denoting the absence of SVE live state. */ + simd_ctx_save(secure_state_in, (!secure_origin && (is_sve_hint_set(flags) == true))); +#endif #endif /* Restore outgoing security state */ #if SPMD_SPM_AT_SEL2 - if (secure_state_out == NON_SECURE) { - cm_el1_sysregs_context_restore(secure_state_out); - } cm_el2_sysregs_context_restore(secure_state_out); #else cm_el1_sysregs_context_restore(secure_state_out); +#if CTX_INCLUDE_FPREGS || CTX_INCLUDE_SVE_REGS + simd_ctx_restore(secure_state_out); +#endif #endif cm_set_next_eret_context(secure_state_out); + ctx_out = cm_get_context(secure_state_out); #if SPMD_SPM_AT_SEL2 /* * If SPMC is at SEL2, save additional registers x8-x17, which may @@ -705,7 +727,7 @@ uint64_t spmd_smc_switch_state(uint32_t smc_fid, * preserved, so the SPMD passes through these registers and expects the * SPMC to save and restore (potentially also modify) them. */ - SMC_RET18(cm_get_context(secure_state_out), smc_fid, x1, x2, x3, x4, + SMC_RET18(ctx_out, smc_fid, x1, x2, x3, x4, SMC_GET_GP(handle, CTX_GPREG_X5), SMC_GET_GP(handle, CTX_GPREG_X6), SMC_GET_GP(handle, CTX_GPREG_X7), @@ -722,7 +744,7 @@ uint64_t spmd_smc_switch_state(uint32_t smc_fid, ); #else - SMC_RET8(cm_get_context(secure_state_out), smc_fid, x1, x2, x3, x4, + SMC_RET8(ctx_out, smc_fid, x1, x2, x3, x4, SMC_GET_GP(handle, CTX_GPREG_X5), SMC_GET_GP(handle, CTX_GPREG_X6), SMC_GET_GP(handle, CTX_GPREG_X7)); @@ -746,8 +768,9 @@ static uint64_t spmd_smc_forward(uint32_t smc_fid, return spmc_smc_handler(smc_fid, secure_origin, x1, x2, x3, x4, cookie, handle, flags); } + return spmd_smc_switch_state(smc_fid, secure_origin, x1, x2, x3, x4, - handle); + handle, flags); } @@ -786,19 +809,6 @@ static bool spmd_is_spmc_message(unsigned int ep) && (ffa_endpoint_source(ep) == spmc_attrs.spmc_id)); } -/****************************************************************************** - * spmd_handle_spmc_message - *****************************************************************************/ -static int spmd_handle_spmc_message(unsigned long long msg, - unsigned long long parm1, unsigned long long parm2, - unsigned long long parm3, unsigned long long parm4) -{ - VERBOSE("%s %llx %llx %llx %llx %llx\n", __func__, - msg, parm1, parm2, parm3, parm4); - - return -EINVAL; -} - /******************************************************************************* * This function forwards FF-A SMCs to either the main SPMD handler or the * SPMC at EL3, depending on the origin security state, if enabled. @@ -841,10 +851,9 @@ uint64_t spmd_smc_handler(uint32_t smc_fid, void *handle, uint64_t flags) { - unsigned int linear_id = plat_my_core_pos(); spmd_spm_core_context_t *ctx = spmd_get_context(); bool secure_origin; - int32_t ret; + int ret; uint32_t input_version; /* Determine which security state this SMC originated from */ @@ -852,7 +861,7 @@ uint64_t spmd_smc_handler(uint32_t smc_fid, VERBOSE("SPM(%u): 0x%x 0x%" PRIx64 " 0x%" PRIx64 " 0x%" PRIx64 " 0x%" PRIx64 " 0x%" PRIx64 " 0x%" PRIx64 " 0x%" PRIx64 "\n", - linear_id, smc_fid, x1, x2, x3, x4, + plat_my_core_pos(), smc_fid, x1, x2, x3, x4, SMC_GET_GP(handle, CTX_GPREG_X5), SMC_GET_GP(handle, CTX_GPREG_X6), SMC_GET_GP(handle, CTX_GPREG_X7)); @@ -936,9 +945,10 @@ uint64_t spmd_smc_handler(uint32_t smc_fid, break; } /* Save non-secure system registers context */ - cm_el1_sysregs_context_save(NON_SECURE); #if SPMD_SPM_AT_SEL2 cm_el2_sysregs_context_save(NON_SECURE); +#else + cm_el1_sysregs_context_save(NON_SECURE); #endif /* @@ -1100,6 +1110,7 @@ uint64_t spmd_smc_handler(uint32_t smc_fid, case FFA_MSG_SEND_DIRECT_REQ_SMC32: case FFA_MSG_SEND_DIRECT_REQ_SMC64: + case FFA_MSG_SEND_DIRECT_REQ2_SMC64: /* * Regardless of secure_origin, SPMD logical partitions cannot * handle direct messages. They can only initiate direct @@ -1133,16 +1144,8 @@ uint64_t spmd_smc_handler(uint32_t smc_fid, } } if (secure_origin && spmd_is_spmc_message(x1)) { - ret = spmd_handle_spmc_message(x3, x4, - SMC_GET_GP(handle, CTX_GPREG_X5), - SMC_GET_GP(handle, CTX_GPREG_X6), - SMC_GET_GP(handle, CTX_GPREG_X7)); - - SMC_RET8(handle, FFA_SUCCESS_SMC32, - FFA_TARGET_INFO_MBZ, ret, - FFA_PARAM_MBZ, FFA_PARAM_MBZ, - FFA_PARAM_MBZ, FFA_PARAM_MBZ, - FFA_PARAM_MBZ); + return spmd_ffa_error_return(handle, + FFA_ERROR_DENIED); } else { /* Forward direct message to the other world */ return spmd_smc_forward(smc_fid, secure_origin, @@ -1153,6 +1156,7 @@ uint64_t spmd_smc_handler(uint32_t smc_fid, case FFA_MSG_SEND_DIRECT_RESP_SMC32: case FFA_MSG_SEND_DIRECT_RESP_SMC64: + case FFA_MSG_SEND_DIRECT_RESP2_SMC64: if (secure_origin && (spmd_is_spmc_message(x1) || is_spmd_logical_sp_dir_req_in_progress(ctx))) { spmd_spm_core_sync_exit(0ULL); @@ -1163,7 +1167,6 @@ uint64_t spmd_smc_handler(uint32_t smc_fid, handle, flags); } break; /* Not reached */ - case FFA_RX_RELEASE: case FFA_RXTX_MAP_SMC32: case FFA_RXTX_MAP_SMC64: @@ -1279,6 +1282,12 @@ uint64_t spmd_smc_handler(uint32_t smc_fid, handle, flags); break; /* Not reached */ #endif + case FFA_CONSOLE_LOG_SMC32: + case FFA_CONSOLE_LOG_SMC64: + /* This interface must not be forwarded to other worlds. */ + return spmd_ffa_error_return(handle, FFA_ERROR_NOT_SUPPORTED); + break; /* not reached */ + case FFA_EL3_INTR_HANDLE: if (secure_origin) { return spmd_handle_group0_intr_swd(handle); diff --git a/services/std_svc/std_svc_setup.c b/services/std_svc/std_svc_setup.c index e782d09d..5cfe5f9d 100644 --- a/services/std_svc/std_svc_setup.c +++ b/services/std_svc/std_svc_setup.c @@ -66,7 +66,7 @@ static int32_t std_svc_setup(void) #if ENABLE_RME if (rmmd_setup() != 0) { - ret = 1; + WARN("RMMD setup failed. Continuing boot.\n"); } #endif diff --git a/tools/amlogic/Makefile b/tools/amlogic/Makefile index 1a1d1f81..7bfee7d7 100644 --- a/tools/amlogic/Makefile +++ b/tools/amlogic/Makefile @@ -4,13 +4,15 @@ # SPDX-License-Identifier: BSD-3-Clause # https://spdx.org/licenses # + MAKE_HELPERS_DIRECTORY := ../../make_helpers/ include ${MAKE_HELPERS_DIRECTORY}build_macros.mk include ${MAKE_HELPERS_DIRECTORY}build_env.mk +include ${MAKE_HELPERS_DIRECTORY}common.mk +include ${MAKE_HELPERS_DIRECTORY}toolchain.mk PROJECT := doimage${BIN_EXT} OBJECTS := doimage.o -V := 0 HOSTCCFLAGS := -Wall -Werror -pedantic -std=c99 -D_GNU_SOURCE @@ -20,28 +22,20 @@ else HOSTCCFLAGS += -O2 endif -ifeq (${V},0) - Q := @ -else - Q := -endif - -HOSTCC := gcc - .PHONY: all clean distclean all: ${PROJECT} ${PROJECT}: ${OBJECTS} Makefile - @echo " HOSTLD $@" - ${Q}${HOSTCC} ${OBJECTS} -o $@ - @${ECHO_BLANK_LINE} - @echo "Built $@ successfully" - @${ECHO_BLANK_LINE} + $(s)echo " HOSTLD $@" + $(q)$(host-cc) ${OBJECTS} -o $@ + $(s)echo + $(s)echo "Built $@ successfully" + $(s)echo %.o: %.c Makefile - @echo " HOSTCC $<" - ${Q}${HOSTCC} -c ${HOSTCCFLAGS} $< -o $@ + $(s)echo " HOSTCC $<" + $(q)$(host-cc) -c ${HOSTCCFLAGS} $< -o $@ clean: $(call SHELL_DELETE_ALL, ${PROJECT} ${OBJECTS}) diff --git a/tools/cert_create/Makefile b/tools/cert_create/Makefile index b911d19d..ce12a660 100644 --- a/tools/cert_create/Makefile +++ b/tools/cert_create/Makefile @@ -1,11 +1,10 @@ # -# Copyright (c) 2015-2022, Arm Limited and Contributors. All rights reserved. +# Copyright (c) 2015-2024, Arm Limited and Contributors. All rights reserved. # # SPDX-License-Identifier: BSD-3-Clause # PLAT := none -V ?= 0 DEBUG := 0 CRTTOOL ?= cert_create${BIN_EXT} BINARY := $(notdir ${CRTTOOL}) @@ -14,7 +13,10 @@ COT := tbbr MAKE_HELPERS_DIRECTORY := ../../make_helpers/ include ${MAKE_HELPERS_DIRECTORY}build_macros.mk include ${MAKE_HELPERS_DIRECTORY}build_env.mk +include ${MAKE_HELPERS_DIRECTORY}common.mk include ${MAKE_HELPERS_DIRECTORY}defaults.mk +include ${MAKE_HELPERS_DIRECTORY}toolchain.mk +include ${MAKE_HELPERS_DIRECTORY}utilities.mk ifneq (${PLAT},none) TF_PLATFORM_ROOT := ../../plat/ @@ -57,13 +59,7 @@ else HOSTCCFLAGS += -O2 -DLOG_LEVEL=20 endif -ifeq (${V},0) - Q := @ -else - Q := -endif - -HOSTCCFLAGS += ${DEFINES} +HOSTCCFLAGS += ${DEFINES} -DPLAT_MSG=$(call escape-shell,"$(PLAT_MSG)") # USING_OPENSSL3 flag will be added to the HOSTCCFLAGS variable with the proper # computed value. HOSTCCFLAGS += -DUSING_OPENSSL3=$(USING_OPENSSL3) @@ -81,31 +77,25 @@ INC_DIR += -I ./include -I ${PLAT_INCLUDE} -I ${OPENSSL_DIR}/include LIB_DIR := -L ${OPENSSL_DIR}/lib -L ${OPENSSL_DIR} LIB := -lssl -lcrypto -HOSTCC ?= gcc - .PHONY: all clean realclean --openssl all: --openssl ${BINARY} ${BINARY}: ${OBJECTS} Makefile - @echo " HOSTLD $@" - @echo 'const char build_msg[] = "Built : "__TIME__", "__DATE__; \ - const char platform_msg[] = "${PLAT_MSG}";' | \ - ${HOSTCC} -c ${HOSTCCFLAGS} -xc - -o src/build_msg.o - ${Q}${HOSTCC} src/build_msg.o ${OBJECTS} ${LIB_DIR} ${LIB} -o $@ + $(s)echo " HOSTLD $@" + $(q)$(host-cc) ${OBJECTS} ${LIB_DIR} ${LIB} -o $@ %.o: %.c - @echo " HOSTCC $<" - ${Q}${HOSTCC} -c ${HOSTCCFLAGS} ${INC_DIR} $< -o $@ + $(s)echo " HOSTCC $<" + $(q)$(host-cc) -c ${HOSTCCFLAGS} ${INC_DIR} $< -o $@ --openssl: ifeq ($(DEBUG),1) - @echo "Selected OpenSSL version: ${OPENSSL_CURRENT_VER}" + $(s)echo "Selected OpenSSL version: ${OPENSSL_CURRENT_VER}" endif clean: - $(call SHELL_DELETE_ALL, src/build_msg.o ${OBJECTS}) + $(call SHELL_DELETE_ALL,${OBJECTS}) realclean: clean $(call SHELL_DELETE,${BINARY}) - diff --git a/tools/cert_create/include/key.h b/tools/cert_create/include/key.h index e0ecdaed..f7adfab2 100644 --- a/tools/cert_create/include/key.h +++ b/tools/cert_create/include/key.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015-2023, Arm Limited and Contributors. All rights reserved. + * Copyright (c) 2015-2024, Arm Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -65,35 +65,35 @@ typedef struct key_s { const char *desc; /* Key description (debug purposes) */ char *fn; /* Filename to load/store the key */ EVP_PKEY *key; /* Key container */ -} key_t; +} cert_key_t; /* Exported API */ int key_init(void); -key_t *key_get_by_opt(const char *opt); +cert_key_t *key_get_by_opt(const char *opt); #if !USING_OPENSSL3 -int key_new(key_t *key); +int key_new(cert_key_t *key); #endif -int key_create(key_t *key, int type, int key_bits); -unsigned int key_load(key_t *key); -int key_store(key_t *key); +int key_create(cert_key_t *key, int type, int key_bits); +unsigned int key_load(cert_key_t *key); +int key_store(cert_key_t *key); void key_cleanup(void); /* Macro to register the keys used in the CoT */ #define REGISTER_KEYS(_keys) \ - key_t *def_keys = &_keys[0]; \ + cert_key_t *def_keys = &_keys[0]; \ const unsigned int num_def_keys = sizeof(_keys)/sizeof(_keys[0]) /* Macro to register the platform defined keys used in the CoT */ #define PLAT_REGISTER_KEYS(_pdef_keys) \ - key_t *pdef_keys = &_pdef_keys[0]; \ + cert_key_t *pdef_keys = &_pdef_keys[0]; \ const unsigned int num_pdef_keys = sizeof(_pdef_keys)/sizeof(_pdef_keys[0]) /* Exported variables */ -extern key_t *def_keys; +extern cert_key_t *def_keys; extern const unsigned int num_def_keys; -extern key_t *pdef_keys; +extern cert_key_t *pdef_keys; extern const unsigned int num_pdef_keys; -extern key_t *keys; +extern cert_key_t *keys; extern unsigned int num_keys; #endif /* KEY_H */ diff --git a/tools/cert_create/src/cca/cot.c b/tools/cert_create/src/cca/cot.c index 372d9087..658b81c2 100644 --- a/tools/cert_create/src/cca/cot.c +++ b/tools/cert_create/src/cca/cot.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022-2023, Arm Limited. All rights reserved. + * Copyright (c) 2022-2024, Arm Limited. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -410,7 +410,7 @@ static ext_t cot_ext[] = { REGISTER_EXTENSIONS(cot_ext); /* Keys used to establish the chain of trust. */ -static key_t cot_keys[] = { +static cert_key_t cot_keys[] = { [ROT_KEY] = { .id = ROT_KEY, .opt = "rot-key", diff --git a/tools/cert_create/src/cert.c b/tools/cert_create/src/cert.c index 2513213a..4a36ee8f 100644 --- a/tools/cert_create/src/cert.c +++ b/tools/cert_create/src/cert.c @@ -22,7 +22,6 @@ #include "sha.h" #define SERIAL_RAND_BITS 64 -#define RSA_SALT_LEN 32 cert_t *certs; unsigned int num_certs; @@ -152,7 +151,7 @@ int cert_new( goto END; } - if (!EVP_PKEY_CTX_set_rsa_pss_saltlen(pKeyCtx, RSA_SALT_LEN)) { + if (!EVP_PKEY_CTX_set_rsa_pss_saltlen(pKeyCtx, EVP_MD_size(get_digest(md_alg)))) { ERR_print_errors_fp(stdout); goto END; } diff --git a/tools/cert_create/src/dualroot/cot.c b/tools/cert_create/src/dualroot/cot.c index 81a7d75d..d2c15bf9 100644 --- a/tools/cert_create/src/dualroot/cot.c +++ b/tools/cert_create/src/dualroot/cot.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, Arm Limited. All rights reserved. + * Copyright (c) 2020-2024, Arm Limited. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -536,7 +536,7 @@ REGISTER_EXTENSIONS(cot_ext); /* Keys used to establish the chain of trust. */ -static key_t cot_keys[] = { +static cert_key_t cot_keys[] = { [ROT_KEY] = { .id = ROT_KEY, .opt = "rot-key", diff --git a/tools/cert_create/src/key.c b/tools/cert_create/src/key.c index 04214aac..190c0963 100644 --- a/tools/cert_create/src/key.c +++ b/tools/cert_create/src/key.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015-2023, Arm Limited and Contributors. All rights reserved. + * Copyright (c) 2015-2024, Arm Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -26,14 +26,14 @@ #define MAX_FILENAME_LEN 1024 -key_t *keys; +cert_key_t *keys; unsigned int num_keys; #if !USING_OPENSSL3 /* * Create a new key container */ -int key_new(key_t *key) +int key_new(cert_key_t *key) { /* Create key pair container */ key->key = EVP_PKEY_new(); @@ -45,7 +45,7 @@ int key_new(key_t *key) } #endif -static int key_create_rsa(key_t *key, int key_bits) +static int key_create_rsa(cert_key_t *key, int key_bits) { #if USING_OPENSSL3 EVP_PKEY *rsa = EVP_RSA_gen(key_bits); @@ -99,7 +99,7 @@ err2: #ifndef OPENSSL_NO_EC #if USING_OPENSSL3 -static int key_create_ecdsa(key_t *key, int key_bits, const char *curve) +static int key_create_ecdsa(cert_key_t *key, int key_bits, const char *curve) { EVP_PKEY *ec = EVP_EC_gen(curve); if (ec == NULL) { @@ -111,7 +111,7 @@ static int key_create_ecdsa(key_t *key, int key_bits, const char *curve) return 1; } -static int key_create_ecdsa_nist(key_t *key, int key_bits) +static int key_create_ecdsa_nist(cert_key_t *key, int key_bits) { if (key_bits == 384) { return key_create_ecdsa(key, key_bits, "secp384r1"); @@ -121,17 +121,17 @@ static int key_create_ecdsa_nist(key_t *key, int key_bits) } } -static int key_create_ecdsa_brainpool_r(key_t *key, int key_bits) +static int key_create_ecdsa_brainpool_r(cert_key_t *key, int key_bits) { return key_create_ecdsa(key, key_bits, "brainpoolP256r1"); } -static int key_create_ecdsa_brainpool_t(key_t *key, int key_bits) +static int key_create_ecdsa_brainpool_t(cert_key_t *key, int key_bits) { return key_create_ecdsa(key, key_bits, "brainpoolP256t1"); } #else -static int key_create_ecdsa(key_t *key, int key_bits, const int curve_id) +static int key_create_ecdsa(cert_key_t *key, int key_bits, const int curve_id) { EC_KEY *ec; @@ -158,7 +158,7 @@ err: return 0; } -static int key_create_ecdsa_nist(key_t *key, int key_bits) +static int key_create_ecdsa_nist(cert_key_t *key, int key_bits) { if (key_bits == 384) { return key_create_ecdsa(key, key_bits, NID_secp384r1); @@ -168,29 +168,33 @@ static int key_create_ecdsa_nist(key_t *key, int key_bits) } } -static int key_create_ecdsa_brainpool_r(key_t *key, int key_bits) +#if OPENSSL_VERSION_NUMBER >= 0x10100000L +static int key_create_ecdsa_brainpool_r(cert_key_t *key, int key_bits) { return key_create_ecdsa(key, key_bits, NID_brainpoolP256r1); } -static int key_create_ecdsa_brainpool_t(key_t *key, int key_bits) +static int key_create_ecdsa_brainpool_t(cert_key_t *key, int key_bits) { return key_create_ecdsa(key, key_bits, NID_brainpoolP256t1); } +#endif #endif /* USING_OPENSSL3 */ #endif /* OPENSSL_NO_EC */ -typedef int (*key_create_fn_t)(key_t *key, int key_bits); +typedef int (*key_create_fn_t)(cert_key_t *key, int key_bits); static const key_create_fn_t key_create_fn[KEY_ALG_MAX_NUM] = { [KEY_ALG_RSA] = key_create_rsa, #ifndef OPENSSL_NO_EC [KEY_ALG_ECDSA_NIST] = key_create_ecdsa_nist, +#if OPENSSL_VERSION_NUMBER >= 0x10100000L [KEY_ALG_ECDSA_BRAINPOOL_R] = key_create_ecdsa_brainpool_r, [KEY_ALG_ECDSA_BRAINPOOL_T] = key_create_ecdsa_brainpool_t, +#endif #endif /* OPENSSL_NO_EC */ }; -int key_create(key_t *key, int type, int key_bits) +int key_create(cert_key_t *key, int type, int key_bits) { if (type >= KEY_ALG_MAX_NUM) { printf("Invalid key type\n"); @@ -239,7 +243,7 @@ err: } -unsigned int key_load(key_t *key) +unsigned int key_load(cert_key_t *key) { if (key->fn == NULL) { VERBOSE("Key not specified\n"); @@ -269,7 +273,7 @@ unsigned int key_load(key_t *key) return KEY_ERR_NONE; } -int key_store(key_t *key) +int key_store(cert_key_t *key) { FILE *fp; @@ -297,7 +301,7 @@ int key_store(key_t *key) int key_init(void) { cmd_opt_t cmd_opt; - key_t *key; + cert_key_t *key; unsigned int i; keys = malloc((num_def_keys * sizeof(def_keys[0])) @@ -337,9 +341,9 @@ int key_init(void) return 0; } -key_t *key_get_by_opt(const char *opt) +cert_key_t *key_get_by_opt(const char *opt) { - key_t *key; + cert_key_t *key; unsigned int i; /* Sequential search. This is not a performance concern since the number diff --git a/tools/cert_create/src/main.c b/tools/cert_create/src/main.c index f10a768b..aa21206c 100644 --- a/tools/cert_create/src/main.c +++ b/tools/cert_create/src/main.c @@ -1,9 +1,11 @@ /* - * Copyright (c) 2015-2022, Arm Limited and Contributors. All rights reserved. + * Copyright (c) 2015-2024, Arm Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ +#define _POSIX_C_SOURCE 200809L + #include <assert.h> #include <ctype.h> #include <getopt.h> @@ -66,27 +68,17 @@ static int new_keys; static int save_keys; static int print_cert; -/* Info messages created in the Makefile */ -extern const char build_msg[]; -extern const char platform_msg[]; - - -static char *strdup(const char *str) -{ - int n = strlen(str) + 1; - char *dup = malloc(n); - if (dup) { - strcpy(dup, str); - } - return dup; -} +static const char build_msg[] = "Built : " __TIME__ ", " __DATE__; +static const char platform_msg[] = PLAT_MSG; static const char *key_algs_str[] = { [KEY_ALG_RSA] = "rsa", #ifndef OPENSSL_NO_EC [KEY_ALG_ECDSA_NIST] = "ecdsa", +#if OPENSSL_VERSION_NUMBER >= 0x10100000L [KEY_ALG_ECDSA_BRAINPOOL_R] = "ecdsa-brainpool-regular", [KEY_ALG_ECDSA_BRAINPOOL_T] = "ecdsa-brainpool-twisted", +#endif #endif /* OPENSSL_NO_EC */ }; @@ -178,7 +170,7 @@ static void check_cmd_params(void) { cert_t *cert; ext_t *ext; - key_t *key; + cert_key_t *key; int i, j; bool valid_size; @@ -269,8 +261,12 @@ static const cmd_opt_t common_cmd_opt[] = { }, { { "key-alg", required_argument, NULL, 'a' }, - "Key algorithm: 'rsa' (default)- RSAPSS scheme as per PKCS#1 v2.1, " \ + "Key algorithm: 'rsa' (default)- RSAPSS scheme as per PKCS#1 v2.1, " +#if OPENSSL_VERSION_NUMBER >= 0x10100000L "'ecdsa', 'ecdsa-brainpool-regular', 'ecdsa-brainpool-twisted'" +#else + "'ecdsa'" +#endif }, { { "key-size", required_argument, NULL, 'b' }, @@ -299,7 +295,7 @@ int main(int argc, char *argv[]) STACK_OF(X509_EXTENSION) * sk; X509_EXTENSION *cert_ext = NULL; ext_t *ext; - key_t *key; + cert_key_t *key; cert_t *cert; FILE *file; int i, j, ext_nid, nvctr; diff --git a/tools/cert_create/src/tbbr/tbb_key.c b/tools/cert_create/src/tbbr/tbb_key.c index 5b84b6e9..3d99067f 100644 --- a/tools/cert_create/src/tbbr/tbb_key.c +++ b/tools/cert_create/src/tbbr/tbb_key.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2015-2024, Arm Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -11,7 +11,7 @@ * * The order of the keys must follow the enumeration specified in tbb_key.h */ -static key_t tbb_keys[] = { +static cert_key_t tbb_keys[] = { [ROT_KEY] = { .id = ROT_KEY, .opt = "rot-key", diff --git a/tools/conventional-changelog-tf-a/package.json b/tools/conventional-changelog-tf-a/package.json index d0efab8f..9975ea32 100644 --- a/tools/conventional-changelog-tf-a/package.json +++ b/tools/conventional-changelog-tf-a/package.json @@ -1,6 +1,6 @@ { "name": "conventional-changelog-tf-a", - "version": "2.10.0", + "version": "2.12.0", "license": "BSD-3-Clause", "private": true, "main": "index.js", diff --git a/tools/cot_dt2c/.gitignore b/tools/cot_dt2c/.gitignore new file mode 100644 index 00000000..ad4a1f17 --- /dev/null +++ b/tools/cot_dt2c/.gitignore @@ -0,0 +1,176 @@ +# Created by https://www.toptal.com/developers/gitignore/api/python +# Edit at https://www.toptal.com/developers/gitignore?templates=python + +### Python ### +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.py,cover +.hypothesis/ +.pytest_cache/ +cover/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 +db.sqlite3-journal + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +.pybuilder/ +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +# For a library or package, you might want to ignore these files since the code is +# intended to run in multiple environments; otherwise, check them in: +# .python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. +#Pipfile.lock + +# poetry +# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. +# This is especially recommended for binary packages to ensure reproducibility, and is more +# commonly ignored for libraries. +# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control +#poetry.lock + +# pdm +# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. +#pdm.lock +# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it +# in version control. +# https://pdm.fming.dev/#use-with-ide +.pdm.toml + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm +__pypackages__/ + +# Celery stuff +celerybeat-schedule +celerybeat.pid + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ + +# pytype static type analyzer +.pytype/ + +# Cython debug symbols +cython_debug/ + +# PyCharm +# JetBrains specific template is maintained in a separate JetBrains.gitignore that can +# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore +# and can be added to the global gitignore or merged into this file. For a more nuclear +# option (not recommended) you can uncomment the following to ignore the entire idea folder. +#.idea/ + +### Python Patch ### +# Poetry local configuration file - https://python-poetry.org/docs/configuration/#local-configuration +poetry.toml + +# ruff +.ruff_cache/ + +# LSP config files +pyrightconfig.json + +# End of https://www.toptal.com/developers/gitignore/api/python diff --git a/tools/cot_dt2c/cot_dt2c/__init__.py b/tools/cot_dt2c/cot_dt2c/__init__.py new file mode 100644 index 00000000..621c55a1 --- /dev/null +++ b/tools/cot_dt2c/cot_dt2c/__init__.py @@ -0,0 +1,25 @@ +#!/usr/bin/env python3 +# type: ignore[attr-defined] + +# +# Copyright (c) 2024, Arm Limited. All rights reserved. +# +# SPDX-License-Identifier: BSD-3-Clause +# + +import sys + +if sys.version_info >= (3, 8): + from importlib import metadata as importlib_metadata +else: + import importlib_metadata + + +def get_version() -> str: + try: + return importlib_metadata.version(__name__) + except importlib_metadata.PackageNotFoundError: # pragma: no cover + return "unknown" + + +version: str = get_version() diff --git a/tools/cot_dt2c/cot_dt2c/__main__.py b/tools/cot_dt2c/cot_dt2c/__main__.py new file mode 100644 index 00000000..5aa4a92f --- /dev/null +++ b/tools/cot_dt2c/cot_dt2c/__main__.py @@ -0,0 +1,10 @@ +#!/usr/bin/env python3 +# type: ignore[attr-defined] +# +# Copyright (c) 2024, Arm Limited. All rights reserved. +# +# SPDX-License-Identifier: BSD-3-Clause +# +from cot_dt2c.cli import cli +if __name__ == "__main__": + cli() diff --git a/tools/cot_dt2c/cot_dt2c/cli.py b/tools/cot_dt2c/cot_dt2c/cli.py new file mode 100644 index 00000000..d338430c --- /dev/null +++ b/tools/cot_dt2c/cot_dt2c/cli.py @@ -0,0 +1,39 @@ +# +# Copyright (c) 2024, Arm Limited and Contributors. All rights reserved. +# +# SPDX-License-Identifier: BSD-3-Clause +# + +from pathlib import Path +from cot_dt2c.cot_dt2c import generateMain +from cot_dt2c.cot_dt2c import validateMain +from cot_dt2c.cot_dt2c import visualizeMain +from cot_dt2c.dt_validator import dtValidatorMain + +import click + +@click.group() +@click.version_option() +def cli(): + pass + +@cli.command() +@click.argument("inputfile", type=click.Path(dir_okay=True)) +@click.argument("outputfile", type=click.Path(dir_okay=True)) +def convert_to_c(inputfile, outputfile): + generateMain(inputfile, outputfile) + +@cli.command() +@click.argument("inputfile", type=click.Path(dir_okay=True)) +def validate_cot(inputfile): + validateMain(inputfile) + +@cli.command() +@click.argument("inputfile", type=click.Path(dir_okay=True)) +def visualize_cot(inputfile): + visualizeMain(inputfile) + +@cli.command() +@click.argument("inputfiledir", type=click.Path(dir_okay=True)) +def validate_dt(inputfiledir): + dtValidatorMain(inputfiledir) diff --git a/tools/cot_dt2c/cot_dt2c/cot_dt2c.py b/tools/cot_dt2c/cot_dt2c/cot_dt2c.py new file mode 100644 index 00000000..4056aac9 --- /dev/null +++ b/tools/cot_dt2c/cot_dt2c/cot_dt2c.py @@ -0,0 +1,30 @@ +# +# Copyright (c) 2024, Arm Limited and Contributors. All rights reserved. +# +# SPDX-License-Identifier: BSD-3-Clause +# + +import sys +from cot_dt2c.cot_parser import COT + +def generateMain(input, output=None): + cot = COT(input, output) + cot.generate_c_file() + +def validateMain(input): + cot = COT(input) + if not cot.validate_nodes(): + print("not a valid CoT DT file") + +def visualizeMain(input): + cot = COT(input) + cot.tree_visualization() + +if __name__=="__main__": + if (len(sys.argv) < 2): + print("usage: python3 " + sys.argv[0] + " [dtsi file path] [optional output c file path]") + exit() + if len(sys.argv) == 3: + generateMain(sys.argv[1], sys.argv[2]) + if len(sys.argv) == 2: + validateMain(sys.argv[1]) diff --git a/tools/cot_dt2c/cot_dt2c/cot_parser.py b/tools/cot_dt2c/cot_dt2c/cot_parser.py new file mode 100644 index 00000000..39e51db3 --- /dev/null +++ b/tools/cot_dt2c/cot_dt2c/cot_parser.py @@ -0,0 +1,673 @@ +# +# Copyright (c) 2024, Arm Limited and Contributors. All rights reserved. +# +# SPDX-License-Identifier: BSD-3-Clause +# + +import sys +import re +from pydevicetree.ast import CellArray, LabelReference +from pydevicetree import Devicetree, Property, Node +from pathlib import Path +from typing import List, Optional + +class COT: + def __init__(self, inputfile: str, outputfile=None): + try: + self.tree = Devicetree.parseFile(inputfile) + except: + print("not a valid CoT DT file") + exit(1) + + self.output = outputfile + self.input = inputfile + self.has_root = False + + # edge cases + certs = self.get_all_certificates() + for c in certs: + if self.if_root(c): + if not c.get_fields("signing-key"): + c.properties.append(Property("signing-key", CellArray([LabelReference("subject_pk")]))) + + def print_cert_info(self, node:Node): + img_id = node.get_field("image-id").values[0].replace('"', "") + sign_key = self.get_sign_key(node) + nv = self.get_nv_ctr(node) + + info = "<b>name:</b> {}<br><b>image-id:</b> {}<br>{}{}{}"\ + .format(node.name, img_id, "<b>root-certificate</b><br>" if self.if_root(node) else "", \ + "<b>signing-key:</b> " + self.extract_label(sign_key) + "<br>" if sign_key else "", \ + "<b>nv counter:</b> " + self.extract_label(nv) + "<br>" if nv else "") + return info + + def print_data_info(self, node:Node): + oid = node.get_field("oid") + info = "<b>name:</b> {}<br><b>oid:</b> {}<br>" \ + .format(node.name, oid) + + return info + + def print_img_info(self, node:Node): + hash = self.extract_label(node.get_fields("hash")) + img_id = node.get_field("image-id").values[0].replace('"', "") + info = "<b>name:</b> {}<br><b>image-id:</b> {}<br><b>hash:</b> {}"\ + .format(node.name, img_id, hash) + + return info + + def tree_width(self, parent_set, root): + ans = 1 + stack = [root] + + while stack: + tmp_stack = [] + while stack: + cur_node = stack.pop() + child = parent_set[cur_node] + for c in child: + tmp_stack.append(c) + + stack = tmp_stack.copy() + ans = max(ans, len(tmp_stack)) + + return ans + + def resolve_lay(self, parent_set, lay, name_idx, root, bounds, break_name): + child = parent_set[root] + + if len(child) == 0: + return + + width = [] + total_width = 0 + for c in child: + w = self.tree_width(parent_set, c) + width.append(w) + total_width += w + + allow_width = bounds[1] - bounds[0] + interval = allow_width / total_width + start = bounds[0] + for i, c in enumerate(child): + end = start + interval * width[i] + new_bounds = [start, end] + lay[name_idx[c]][0] = start + (end - start) / 2 + if end - start < 0.28: + break_name.add(c) + start = end + self.resolve_lay(parent_set, lay, name_idx, c, new_bounds, break_name) + + def tree_visualization(self): + import igraph + from igraph import Graph, EdgeSeq + import collections + + cert = self.get_certificates() + pk = self.get_rot_keys() + nv = self.get_nv_counters() + image = self.get_images() + + certs = cert.children + if pk: + pks = pk.children + else: + pks = [] + nvs = nv.children + images = image.children + + root_name = "CoT" + + G = Graph() + detail = [] + lay = [] + name_idx = {} + parent_set = collections.defaultdict(list) + + G.add_vertex(root_name) + detail.append("CoT Root") + name_idx[root_name] = len(lay) + lay.append([0,0]) + + G.add_vertex(cert.name) + G.add_edge(root_name, cert.name) + detail.append("All Certificates") + name_idx[cert.name] = len(lay) + lay.append([0, 1]) + parent_set[root_name].append(cert.name) + + if pk: + G.add_vertex(pk.name) + detail.append("All Public Trusted Key") + G.add_edge(root_name, pk.name) + name_idx[pk.name] = len(lay) + lay.append([-2.0, 1]) + parent_set[root_name].append(pk.name) + + G.add_vertex(nv.name) + detail.append("All NV Counters") + G.add_edge(root_name, nv.name) + name_idx[nv.name] = len(lay) + lay.append([2.0, 1]) + parent_set[root_name].append(nv.name) + + if pks: + for i, p in enumerate(pks): + G.add_vertex(p.name) + detail.append(self.print_data_info(p)) + G.add_edge(pk.name, p.name) + name_idx[p.name] = len(lay) + parent_set[pk.name].append(p.name) + lay.append([0, lay[name_idx[pk.name]][1] + 1]) + + for c in certs: + G.add_vertex(c.name) + detail.append(self.print_cert_info(c)) + name_idx[c.name] = len(lay) + if self.if_root(c): + G.add_edge(cert.name, c.name) + parent_set[cert.name].append(c.name) + lay.append([0, 2]) + else: + parent = self.extract_label(c.get_fields("parent")) + G.add_edge(parent, c.name) + parent_set[parent].append(c.name) + lay.append([0, lay[name_idx[parent]][1] + 1]) + + for idx, i in enumerate(images): + G.add_vertex(i.name) + detail.append(self.print_img_info(i)) + parent = self.extract_label(i.get_fields("parent")) + G.add_edge(parent, i.name) + parent_set[parent].append(i.name) + name_idx[i.name] = len(lay) + lay.append([0, lay[name_idx[parent]][1] + 1]) + + for i, n in enumerate(nvs): + G.add_vertex(n.name) + detail.append(self.print_data_info(n)) + G.add_edge(nv.name, n.name) + name_idx[n.name] = len(lay) + parent_set[nv.name].append(n.name) + lay.append([0, lay[name_idx[nv.name]][1] + 1]) + + break_name = set() + self.resolve_lay(parent_set, lay, name_idx, root_name, [-3, 3], break_name) + #lay = G.layout('rt') + + numVertex = len(G.get_vertex_dataframe()) + vertices = G.get_vertex_dataframe() + v_label = [] + + for i in vertices['name']: + if i in break_name and len(i) > 10: + middle = len(i) // 2 + v_label.append(i[:middle] + "<br>" + i[middle:]) + else: + v_label.append(i) + + position = {k: lay[k] for k in range(numVertex)} + Y = [lay[k][1] for k in range(numVertex)] + M = max(Y) + + es = EdgeSeq(G) # sequence of edges + E = [e.tuple for e in G.es] # list of edges + + L = len(position) + Xn = [position[k][0] for k in range(L)] + Yn = [2*M-position[k][1] for k in range(L)] + Xe = [] + Ye = [] + for edge in E: + Xe += [position[edge[0]][0], position[edge[1]][0], None] + Ye += [2*M-position[edge[0]][1], 2*M-position[edge[1]][1], None] + + labels = v_label + + import plotly.graph_objects as go + fig = go.Figure() + fig.add_trace(go.Scatter(x = Xe, + y = Ye, + mode = 'lines', + line = dict(color='rgb(210,210,210)', width=2), + hoverinfo = 'none' + )) + fig.add_trace(go.Scatter(x = Xn, + y = Yn, + mode = 'markers', + name = 'detail', + marker = dict(symbol = 'circle-dot', + size = 50, + color = 'rgba(135, 206, 250, 0.8)', #'#DB4551', + line = dict(color='MediumPurple', width=3) + ), + text=detail, + hoverinfo='text', + hovertemplate = + '<b>Detail</b><br>' + '%{text}', + opacity=0.8 + )) + + def make_annotations(pos, text, font_size=10, font_color='rgb(0,0,0)'): + L = len(pos) + if len(text) != L: + raise ValueError('The lists pos and text must have the same len') + annotations = [] + for k in range(L): + annotations.append( + dict( + text = labels[k], + x = pos[k][0], y = 2*M-position[k][1], + xref = 'x1', yref = 'y1', + font = dict(color = font_color, size = font_size), + showarrow = False) + ) + return annotations + + axis = dict(showline=False, # hide axis line, grid, ticklabels and title + zeroline=False, + showgrid=False, + showticklabels=False, + ) + + fig.update_layout(title= 'CoT Device Tree', + annotations=make_annotations(position, v_label), + font_size=12, + showlegend=False, + xaxis=axis, + yaxis=axis, + margin=dict(l=40, r=40, b=85, t=100), + hovermode='closest', + plot_bgcolor='rgb(248,248,248)' + ) + + fig.show() + + return + + def if_root(self, node:Node) -> bool: + for p in node.properties: + if p.name == "root-certificate": + return True + return False + + def get_sign_key(self, node:Node): + for p in node.properties: + if p.name == "signing-key": + return p.values + + return None + + def get_nv_ctr(self, node:Node): + for nv in node.properties: + if nv.name == "antirollback-counter": + return nv.values + + return None + + def extract_label(self, label) -> str: + if not label: + return label + return label[0].label.name + + def get_auth_data(self, node:Node): + return node.children + + def format_auth_data_val(self, node:Node, cert:Node): + type_desc = node.name + ptr = type_desc + "_buf" + len = "HASH_DER_LEN" + if re.search("_pk$", type_desc): + len = "PK_DER_LEN" + + # edge case + if not self.if_root(cert) and "key_cert" in cert.name: + if "content_pk" in ptr: + ptr = "content_pk_buf" + + return type_desc, ptr, len + + def get_node(self, nodes: List[Node], name: str) -> Node: + for i in nodes: + if i.name == name: + return i + + def get_certificates(self) -> Node: + children = self.tree.children + for i in children: + if i.name == "cot": + return self.get_node(i.children, "manifests") + + def get_images(self)-> Node: + children = self.tree.children + for i in children: + if i.name == "cot": + return self.get_node(i.children, "images") + + def get_nv_counters(self) -> Node: + children = self.tree.children + return self.get_node(children, "non_volatile_counters") + + def get_rot_keys(self) -> Node: + children = self.tree.children + return self.get_node(children, "rot_keys") + + def get_all_certificates(self) -> Node: + cert = self.get_certificates() + return cert.children + + def get_all_images(self) -> Node: + image = self.get_images() + return image.children + + def get_all_nv_counters(self) -> Node: + nv = self.get_nv_counters() + return nv.children + + def get_all_pks(self) -> Node: + pk = self.get_rot_keys() + if not pk: + return [] + return pk.children + + def validate_cert(self, node:Node) -> bool: + valid = True + if not node.has_field("image-id"): + print("{} missing mandatory attribute image-id".format(node.name)) + valid = False + + if not node.has_field("root-certificate"): + if not node.has_field("parent"): + print("{} missing mandatory attribute parent".format(node.name)) + valid = False + else: + # check if refer to non existing parent + certs = self.get_all_certificates() + found = False + for c in certs: + if c.name == self.extract_label(node.get_fields("parent")): + found = True + + if not found: + print("{} refer to non existing parent".format(node.name)) + valid = False + + else: + self.has_root = True + + child = node.children + if child: + for c in child: + if not c.has_field("oid"): + print("{} missing mandatory attribute oid".format(c.name)) + valid = False + + return valid + + def validate_img(self, node:Node) -> bool: + valid = True + if not node.has_field("image-id"): + print("{} missing mandatory attribute image-id".format(node.name)) + valid = False + + if not node.has_field("parent"): + print("{} missing mandatory attribute parent".format(node.name)) + valid = False + + if not node.has_field("hash"): + print("{} missing mandatory attribute hash".format(node.name)) + valid = False + + # check if refer to non existing parent + certs = self.get_all_certificates() + found = False + for c in certs: + if c.name == self.extract_label(node.get_fields("parent")): + found = True + + if not found: + print("{} refer to non existing parent".format(node.name)) + valid = False + + return valid + + def validate_nodes(self) -> bool: + valid = True + + certs = self.get_all_certificates() + images = self.get_all_images() + + for n in certs: + node_valid = self.validate_cert(n) + valid = valid and node_valid + + for i in images: + node_valid = self.validate_img(i) + valid = valid and node_valid + + if not self.has_root: + print("missing root certificate") + + return valid + + def include_to_c(self, f): + f.write("#include <stddef.h>\n") + f.write("#include <mbedtls/version.h>\n") + f.write("#include <common/tbbr/cot_def.h>\n") + f.write("#include <drivers/auth/auth_mod.h>\n") + f.write("#include <platform_def.h>\n\n") + return + + def generate_header(self, output): + self.include_to_c(output) + + def all_cert_to_c(self, f): + certs = self.get_all_certificates() + for c in certs: + self.cert_to_c(c, f) + + f.write("\n") + + def cert_to_c(self, node: Node, f): + node_image_id: int = node.get_field("image-id") + + f.write(f"static const auth_img_desc_t {node.name} = {{\n") + f.write(f"\t.img_id = {node_image_id},\n") + f.write("\t.img_type = IMG_CERT,\n") + + if not self.if_root(node): + node_parent: Node = node.get_field("parent") + + f.write(f"\t.parent = &{node_parent.label.name},\n") + else: + f.write("\t.parent = NULL,\n") + + sign = self.get_sign_key(node) + nv_ctr = self.get_nv_ctr(node) + + if sign or nv_ctr: + f.write("\t.img_auth_methods = (const auth_method_desc_t[AUTH_METHOD_NUM]) {\n") + + if sign: + f.write("\t\t[0] = {\n") + f.write("\t\t\t.type = AUTH_METHOD_SIG,\n") + f.write("\t\t\t.param.sig = {\n") + + f.write("\t\t\t\t.pk = &{},\n".format(self.extract_label(sign))) + f.write("\t\t\t\t.sig = &sig,\n") + f.write("\t\t\t\t.alg = &sig_alg,\n") + f.write("\t\t\t\t.data = &raw_data\n") + f.write("\t\t\t}\n") + f.write("\t\t}}{}\n".format("," if nv_ctr else "")) + + if nv_ctr: + f.write("\t\t[1] = {\n") + f.write("\t\t\t.type = AUTH_METHOD_NV_CTR,\n") + f.write("\t\t\t.param.nv_ctr = {\n") + + f.write("\t\t\t\t.cert_nv_ctr = &{},\n".format(self.extract_label(nv_ctr))) + f.write("\t\t\t\t.plat_nv_ctr = &{}\n".format(self.extract_label(nv_ctr))) + + f.write("\t\t\t}\n") + f.write("\t\t}\n") + + f.write("\t},\n") + + auth_data = self.get_auth_data(node) + if auth_data: + f.write("\t.authenticated_data = (const auth_param_desc_t[COT_MAX_VERIFIED_PARAMS]) {\n") + + for i, d in enumerate(auth_data): + type_desc, ptr, data_len = self.format_auth_data_val(d, node) + + f.write("\t\t[{}] = {{\n".format(i)) + f.write("\t\t\t.type_desc = &{},\n".format(type_desc)) + f.write("\t\t\t.data = {\n") + + f.write("\t\t\t\t.ptr = (void *){},\n".format(ptr)) + + f.write("\t\t\t\t.len = (unsigned int){}\n".format(data_len)) + f.write("\t\t\t}\n") + + f.write("\t\t}}{}\n".format("," if i != len(auth_data) - 1 else "")) + + f.write("\t}\n") + + f.write("};\n\n") + + return + + + def img_to_c(self, node:Node, f): + node_image_id: int = node.get_field("image-id") + node_parent: Node = node.get_field("parent") + node_hash: Node = node.get_field("hash") + + f.write(f"static const auth_img_desc_t {node.name} = {{\n") + f.write(f"\t.img_id = {node_image_id},\n") + f.write("\t.img_type = IMG_RAW,\n") + f.write(f"\t.parent = &{node_parent.label.name},\n") + f.write("\t.img_auth_methods = (const auth_method_desc_t[AUTH_METHOD_NUM]) {\n") + + f.write("\t\t[0] = {\n") + f.write("\t\t\t.type = AUTH_METHOD_HASH,\n") + f.write("\t\t\t.param.hash = {\n") + f.write("\t\t\t\t.data = &raw_data,\n") + f.write(f"\t\t\t\t.hash = &{node_hash.label.name}\n") + f.write("\t\t\t}\n") + + f.write("\t\t}\n") + f.write("\t}\n") + f.write("};\n\n") + + return + + def all_img_to_c(self, f): + images = self.get_all_images() + for i in images: + self.img_to_c(i, f) + + f.write("\n") + + def nv_to_c(self, f): + nv_ctr = self.get_all_nv_counters() + + for nv in nv_ctr: + nv_oid: str = nv.get_field("oid") + + f.write(f"static auth_param_type_desc_t {nv.name} = "\ + f"AUTH_PARAM_TYPE_DESC(AUTH_PARAM_NV_CTR, \"{nv_oid}\");\n") + + f.write("\n") + + return + + def pk_to_c(self, f): + pks = self.get_all_pks() + + for p in pks: + pk_oid: str = p.get_field("oid") + + f.write(f"static auth_param_type_desc_t {p.name} = "\ + f"AUTH_PARAM_TYPE_DESC(AUTH_PARAM_PUB_KEY, \"{pk_oid}\");\n") + + f.write("\n") + return + + def buf_to_c(self, f): + certs = self.get_all_certificates() + + buffers = set() + + for c in certs: + auth_data = self.get_auth_data(c) + + for a in auth_data: + type_desc, ptr, data_len = self.format_auth_data_val(a, c) + + if not ptr in buffers: + f.write(f"static unsigned char {ptr}[{data_len}];\n") + buffers.add(ptr) + + f.write("\n") + + def param_to_c(self, f): + f.write("static auth_param_type_desc_t subject_pk = AUTH_PARAM_TYPE_DESC(AUTH_PARAM_PUB_KEY, 0);\n") + f.write("static auth_param_type_desc_t sig = AUTH_PARAM_TYPE_DESC(AUTH_PARAM_SIG, 0);\n") + f.write("static auth_param_type_desc_t sig_alg = AUTH_PARAM_TYPE_DESC(AUTH_PARAM_SIG_ALG, 0);\n") + f.write("static auth_param_type_desc_t raw_data = AUTH_PARAM_TYPE_DESC(AUTH_PARAM_RAW_DATA, 0);\n") + f.write("\n") + + certs = self.get_all_certificates() + for c in certs: + hash = c.children + for h in hash: + name = h.name + oid = h.get_field("oid") + + if re.search("_pk$", name): + ty = "AUTH_PARAM_PUB_KEY" + elif re.search("_hash$", name): + ty = "AUTH_PARAM_HASH" + + f.write(f"static auth_param_type_desc_t {name} = "\ + f"AUTH_PARAM_TYPE_DESC({ty}, \"{oid}\");\n") + + f.write("\n") + + def cot_to_c(self, f): + certs = self.get_all_certificates() + images = self.get_all_images() + + f.write("static const auth_img_desc_t * const cot_desc[] = {\n") + + for i, c in enumerate(certs): + c_image_id: int = c.get_field("image-id") + + f.write(f"\t[{c_image_id}] = &{c.name},\n") + + for i, c in enumerate(images): + c_image_id: int = c.get_field("image-id") + + f.write(f"\t[{c_image_id}] = &{c.name},\n") + + f.write("};\n\n") + f.write("REGISTER_COT(cot_desc);\n") + return + + def generate_c_file(self): + filename = Path(self.output) + filename.parent.mkdir(exist_ok=True, parents=True) + + with open(self.output, 'w+') as output: + self.generate_header(output) + self.buf_to_c(output) + self.param_to_c(output) + self.nv_to_c(output) + self.pk_to_c(output) + self.all_cert_to_c(output) + self.all_img_to_c(output) + self.cot_to_c(output) + + return diff --git a/tools/cot_dt2c/cot_dt2c/dt_validator.py b/tools/cot_dt2c/cot_dt2c/dt_validator.py new file mode 100644 index 00000000..ade037ca --- /dev/null +++ b/tools/cot_dt2c/cot_dt2c/dt_validator.py @@ -0,0 +1,130 @@ +# +# Copyright (c) 2024, Arm Limited and Contributors. All rights reserved. +# +# SPDX-License-Identifier: BSD-3-Clause +# + +import sys +from os import path, walk, mkdir +import subprocess +from pydevicetree import Devicetree + +class bcolors: + HEADER = '\033[95m' + OKBLUE = '\033[94m' + OKCYAN = '\033[96m' + OKGREEN = '\033[92m' + WARNING = '\033[93m' + FAIL = '\033[91m' + ENDC = '\033[0m' + BOLD = '\033[1m' + UNDERLINE = '\033[4m' + +class DTTree: + def __init__(self, input): + self.input = input + self.test_dir = "./tmp" + self.logging_file = self.test_dir + "/result.log" + + def dtValidate(self): + subprocess.run(["rm", "-rf", self.test_dir]) + + if not path.exists(self.test_dir): + mkdir(self.test_dir) + + if path.isfile(self.input): + self.dtValidateFile(self.input, printInfo=True) + return + + if path.isdir(self.input): + self.dtValidateFiles() + return + + def dtValidateFile(self, input, printInfo=False): + valid, tree = self.dtParseFile(input, printInfo) + + if not valid: + return False + + if input.rfind("/") != -1: + filename = self.test_dir + input[input.rfind("/"):] + else: + filename = self.test_dir + "/" + input + + f = open(filename, "w+") + if "/dts-v1/;" not in str(tree): + f.write("/dts-v1/;\n\n") + f.write(str(tree)) + f.close() + + if str(tree) == "": + return valid + + return valid + + def dtParseFile(self, input, printInfo=False): + with open(input, 'r') as f: + contents = f.read() + + pos = contents.find("/ {") + if pos != -1: + contents = contents[pos:] + + try: + tree = Devicetree.parseStr(contents) + if printInfo: + print(bcolors.OKGREEN + "{} parse tree successfully".format(input) + bcolors.ENDC) + except Exception as e: + if printInfo: + print(bcolors.FAIL + "{} parse tree failed:\t{}".format(input, str(e)) + bcolors.ENDC) + else: + f = open(self.logging_file, "a") + f.write("=====================================================================================\n") + f.write("{} result:\n".format(input)) + f.write("{} INVALID:\t{}\n".format(input, str(e))) + f.close() + return False, None + + return True, tree + + def dtValidateFiles(self): + f = [] + for (dirpath, dirnames, filenames) in walk(self.input): + f.extend(filenames) + + allFile = len(f) + dtsiFile = 0 + validFile = 0 + invalidFile = 0 + + for i in f: + if (".dtsi" in i or ".dts" in i) and "cot" not in i and "fw-config" not in i: + dtsiFile += 1 + valid = True + + if self.input[-1] == "/": + valid = self.dtValidateFile(self.input + i) + else: + valid = self.dtValidateFile(self.input + "/" + i) + + if valid: + validFile += 1 + else: + invalidFile += 1 + + print("=====================================================") + print("Total File: " + str(allFile)) + print("Total DT File: " + str(dtsiFile)) + print("Total Valid File: " + str(validFile)) + print("Total Invalid File: " + str(invalidFile)) + +def dtValidatorMain(input): + dt = DTTree(input) + dt.dtValidate() + +if __name__=="__main__": + if (len(sys.argv) < 2): + print("usage: python3 " + sys.argv[0] + " [dtsi file path] or [dtsi folder path]") + exit() + if len(sys.argv) == 2: + dtValidatorMain(sys.argv[1]) diff --git a/tools/cot_dt2c/poetry.lock b/tools/cot_dt2c/poetry.lock new file mode 100644 index 00000000..df58d54c --- /dev/null +++ b/tools/cot_dt2c/poetry.lock @@ -0,0 +1,334 @@ +# This file is automatically @generated by Poetry 1.8.2 and should not be changed by hand. + +[[package]] +name = "atomicwrites" +version = "1.4.1" +description = "Atomic file writes." +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +files = [ + {file = "atomicwrites-1.4.1.tar.gz", hash = "sha256:81b2c9071a49367a7f770170e5eec8cb66567cfbbc8c73d20ce5ca4a8d71cf11"}, +] + +[[package]] +name = "attrs" +version = "24.2.0" +description = "Classes Without Boilerplate" +optional = false +python-versions = ">=3.7" +files = [ + {file = "attrs-24.2.0-py3-none-any.whl", hash = "sha256:81921eb96de3191c8258c199618104dd27ac608d9366f5e35d011eae1867ede2"}, + {file = "attrs-24.2.0.tar.gz", hash = "sha256:5cfb1b9148b5b086569baec03f20d7b6bf3bcacc9a42bebf87ffaaca362f6346"}, +] + +[package.extras] +benchmark = ["cloudpickle", "hypothesis", "mypy (>=1.11.1)", "pympler", "pytest (>=4.3.0)", "pytest-codspeed", "pytest-mypy-plugins", "pytest-xdist[psutil]"] +cov = ["cloudpickle", "coverage[toml] (>=5.3)", "hypothesis", "mypy (>=1.11.1)", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-xdist[psutil]"] +dev = ["cloudpickle", "hypothesis", "mypy (>=1.11.1)", "pre-commit", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-xdist[psutil]"] +docs = ["cogapp", "furo", "myst-parser", "sphinx", "sphinx-notfound-page", "sphinxcontrib-towncrier", "towncrier (<24.7)"] +tests = ["cloudpickle", "hypothesis", "mypy (>=1.11.1)", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-xdist[psutil]"] +tests-mypy = ["mypy (>=1.11.1)", "pytest-mypy-plugins"] + +[[package]] +name = "click" +version = "8.1.7" +description = "Composable command line interface toolkit" +optional = false +python-versions = ">=3.7" +files = [ + {file = "click-8.1.7-py3-none-any.whl", hash = "sha256:ae74fb96c20a0277a1d615f1e4d73c8414f5a98db8b799a7931d1582f3390c28"}, + {file = "click-8.1.7.tar.gz", hash = "sha256:ca9853ad459e787e2192211578cc907e7594e294c7ccc834310722b41b9ca6de"}, +] + +[package.dependencies] +colorama = {version = "*", markers = "platform_system == \"Windows\""} + +[[package]] +name = "colorama" +version = "0.4.6" +description = "Cross-platform colored terminal text." +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" +files = [ + {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, + {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, +] + +[[package]] +name = "igraph" +version = "0.11.6" +description = "High performance graph data structures and algorithms" +optional = false +python-versions = ">=3.8" +files = [ + {file = "igraph-0.11.6-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:3f8b837181e8e87676be3873ce87cc92cc234efd58a2da2f6b4e050db150fcf4"}, + {file = "igraph-0.11.6-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:245c4b7d7657849eff80416f5df4525c8fc44c74a981ee4d44f0ef2612c3bada"}, + {file = "igraph-0.11.6-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bdb7be3d165073c0136295c0808e9edc57ba096cdb26e94086abb04561f7a292"}, + {file = "igraph-0.11.6-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:58974e20df2986a1ae52a16e51ecb387cc0cbeb41c5c0ddff4d373a1bbf1d9c5"}, + {file = "igraph-0.11.6-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7bef14de5e8ab70724a43808b1ed14aaa6fe1002f87e592289027a3827a8f44a"}, + {file = "igraph-0.11.6-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:86c1e98de2e32d074df8510bf18abfa1f4c5fda4cb28a009985a5d746b0c0125"}, + {file = "igraph-0.11.6-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:ebc5b3d702158abeb2e4d2414374586a2b932e1a07e48352b470600e1733d528"}, + {file = "igraph-0.11.6-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:0efe6d0fb22d3987a800eb3857ed04df9eb4c5dddd0998be05232cb646f1c337"}, + {file = "igraph-0.11.6-cp38-cp38-win32.whl", hash = "sha256:f4e68b27497b1c8ada2fb2bc35ef3fa7b0d72e84306b3d648d3de240fc618c32"}, + {file = "igraph-0.11.6-cp38-cp38-win_amd64.whl", hash = "sha256:5665b33dfbfca5f54ce9b4fea6b97903bd0e99fb1b02acf5e57e600bdfa5a355"}, + {file = "igraph-0.11.6-cp39-abi3-macosx_10_9_x86_64.whl", hash = "sha256:8aabef03d787b519d1075dfc0da4a1109fb113b941334883e3e7947ac30a459e"}, + {file = "igraph-0.11.6-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:1f2cc4a518d99cdf6cae514f85e93e56852bc8c325b3abb96037d1d690b5975f"}, + {file = "igraph-0.11.6-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c1e859238be52ab8ccc614d18f9362942bc88ce543afc12548f81ae99b10801d"}, + {file = "igraph-0.11.6-cp39-abi3-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d61fbe5e85eb4ae9efe08c461f9bdeedb02a2b5739fbc223d324a71f40a28be2"}, + {file = "igraph-0.11.6-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b6620ba39df29fd42151becf82309b54e57148233c9c3ef890eed62e25eed8a5"}, + {file = "igraph-0.11.6-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:59666589bb3d07f310cda2c5106a8adeeb77c2ef27fecf1c6438b6091f4ca69d"}, + {file = "igraph-0.11.6-cp39-abi3-musllinux_1_2_i686.whl", hash = "sha256:8750b6d6caebf199cf7dc41c931f58e330153779707391e30f0a29f02666fb6e"}, + {file = "igraph-0.11.6-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:967d6f2c30fe94317da15e459374d0fb8ca3e56020412f201ecd07dd5b5352f2"}, + {file = "igraph-0.11.6-cp39-abi3-win32.whl", hash = "sha256:9744f95a67319eb6cb487ceabf30f5d7940de34bada51f0ba63adbd23e0f94ad"}, + {file = "igraph-0.11.6-cp39-abi3-win_amd64.whl", hash = "sha256:b80e69eb11faa9c57330a9ffebdde5808966efe1c1f638d4d4827ea04df7aca8"}, + {file = "igraph-0.11.6-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:0329c16092e2ea7930d5f8368666ce7cb704900cc0ea04e4afe9ea1dd46e44af"}, + {file = "igraph-0.11.6-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:21752313f449bd8688e5688e95ea7231cea5e9199c7162535029be0d9af848ac"}, + {file = "igraph-0.11.6-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ea25e136c6c4161f53ff58868b23ff6c845193050ab0e502236d68e5d4174e32"}, + {file = "igraph-0.11.6-pp310-pypy310_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ac84433a03aef15e4b810010b08882b09854a3669450ccf31e392dbe295d2a66"}, + {file = "igraph-0.11.6-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ac697a44e3573169fa2b28c9c37dcf9cf01e0f558b845dd7123860d4c7c8fb89"}, + {file = "igraph-0.11.6-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:bdeae8bf35316eb1fb27bf667dcf5ecf5fcfb0b8f51831bc1b00c39c09c2d73b"}, + {file = "igraph-0.11.6-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:ad7e4aa442935de72554b96733bf6d7f09eac5cee97988a2562bdd3ca173cfa3"}, + {file = "igraph-0.11.6-pp38-pypy38_pp73-macosx_11_0_arm64.whl", hash = "sha256:8d2818780358a686178866d01568b9df1f29678581734ad7a78882bab54df004"}, + {file = "igraph-0.11.6-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2352276a20d979f1dea360af4202bb9f0c9a7d2c77f51815c0e625165e82013d"}, + {file = "igraph-0.11.6-pp38-pypy38_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:687fdab543b507d622fa3043f4227e5b26dc61dcf8ff8c0919fccddcc655f8b8"}, + {file = "igraph-0.11.6-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:57f7f8214cd48c9a4d97f7346a4152ba2d4ac95fb5ee0df4ecf224fce4ba3d14"}, + {file = "igraph-0.11.6-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:2b9cc69ede53f76ffae03b066609aa90184dd68ef15da8c104a97cebb9210838"}, + {file = "igraph-0.11.6-pp39-pypy39_pp73-macosx_10_15_x86_64.whl", hash = "sha256:591e1e447c3f0092daf7613a3eaedab83f9a0b0adbaf7702724c5117ded038a5"}, + {file = "igraph-0.11.6-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:ca558eb331bc687bc33e5cd23717e22676e9412f8cda3a31d30c996a0487610d"}, + {file = "igraph-0.11.6-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bf43c30e08debb087c9e3da69aa5cf1b6732968da34d55a614e3421b9a452146"}, + {file = "igraph-0.11.6-pp39-pypy39_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1d38e8d7db72b187d9d2211d0d06b3271fa9f32b04d49d789e2859b5480db0d0"}, + {file = "igraph-0.11.6-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1a318b059051ff78144a1c3cb880f4d933c812bcdb3d833a49cd7168d0427672"}, + {file = "igraph-0.11.6-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:2c54027add809b3c5b6685b8deca4ea4763fd000b9ea45c7ee46b7c9d61ff15e"}, + {file = "igraph-0.11.6.tar.gz", hash = "sha256:837f233256c3319f2a35a6a80d94eafe47b43791ef4c6f9e9871061341ac8e28"}, +] + +[package.dependencies] +texttable = ">=1.6.2" + +[package.extras] +cairo = ["cairocffi (>=1.2.0)"] +doc = ["Sphinx (>=7.0.0)", "pydoctor (>=23.4.0)", "sphinx-gallery (>=0.14.0)", "sphinx-rtd-theme (>=1.3.0)"] +matplotlib = ["matplotlib (>=3.6.0)"] +plotly = ["plotly (>=5.3.0)"] +plotting = ["cairocffi (>=1.2.0)"] +test = ["Pillow (>=9)", "cairocffi (>=1.2.0)", "matplotlib (>=3.6.0)", "networkx (>=2.5)", "numpy (>=1.19.0)", "pandas (>=1.1.0)", "plotly (>=5.3.0)", "pytest (>=7.0.1)", "pytest-timeout (>=2.1.0)", "scipy (>=1.5.0)"] +test-musl = ["cairocffi (>=1.2.0)", "networkx (>=2.5)", "pytest (>=7.0.1)", "pytest-timeout (>=2.1.0)"] + +[[package]] +name = "iniconfig" +version = "2.0.0" +description = "brain-dead simple config-ini parsing" +optional = false +python-versions = ">=3.7" +files = [ + {file = "iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374"}, + {file = "iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3"}, +] + +[[package]] +name = "mypy" +version = "0.910" +description = "Optional static typing for Python" +optional = false +python-versions = ">=3.5" +files = [ + {file = "mypy-0.910-cp35-cp35m-macosx_10_9_x86_64.whl", hash = "sha256:a155d80ea6cee511a3694b108c4494a39f42de11ee4e61e72bc424c490e46457"}, + {file = "mypy-0.910-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:b94e4b785e304a04ea0828759172a15add27088520dc7e49ceade7834275bedb"}, + {file = "mypy-0.910-cp35-cp35m-manylinux2010_x86_64.whl", hash = "sha256:088cd9c7904b4ad80bec811053272986611b84221835e079be5bcad029e79dd9"}, + {file = "mypy-0.910-cp35-cp35m-win_amd64.whl", hash = "sha256:adaeee09bfde366d2c13fe6093a7df5df83c9a2ba98638c7d76b010694db760e"}, + {file = "mypy-0.910-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:ecd2c3fe726758037234c93df7e98deb257fd15c24c9180dacf1ef829da5f921"}, + {file = "mypy-0.910-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:d9dd839eb0dc1bbe866a288ba3c1afc33a202015d2ad83b31e875b5905a079b6"}, + {file = "mypy-0.910-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:3e382b29f8e0ccf19a2df2b29a167591245df90c0b5a2542249873b5c1d78212"}, + {file = "mypy-0.910-cp36-cp36m-win_amd64.whl", hash = "sha256:53fd2eb27a8ee2892614370896956af2ff61254c275aaee4c230ae771cadd885"}, + {file = "mypy-0.910-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:b6fb13123aeef4a3abbcfd7e71773ff3ff1526a7d3dc538f3929a49b42be03f0"}, + {file = "mypy-0.910-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:e4dab234478e3bd3ce83bac4193b2ecd9cf94e720ddd95ce69840273bf44f6de"}, + {file = "mypy-0.910-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:7df1ead20c81371ccd6091fa3e2878559b5c4d4caadaf1a484cf88d93ca06703"}, + {file = "mypy-0.910-cp37-cp37m-win_amd64.whl", hash = "sha256:0aadfb2d3935988ec3815952e44058a3100499f5be5b28c34ac9d79f002a4a9a"}, + {file = "mypy-0.910-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:ec4e0cd079db280b6bdabdc807047ff3e199f334050db5cbb91ba3e959a67504"}, + {file = "mypy-0.910-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:119bed3832d961f3a880787bf621634ba042cb8dc850a7429f643508eeac97b9"}, + {file = "mypy-0.910-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:866c41f28cee548475f146aa4d39a51cf3b6a84246969f3759cb3e9c742fc072"}, + {file = "mypy-0.910-cp38-cp38-win_amd64.whl", hash = "sha256:ceb6e0a6e27fb364fb3853389607cf7eb3a126ad335790fa1e14ed02fba50811"}, + {file = "mypy-0.910-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:1a85e280d4d217150ce8cb1a6dddffd14e753a4e0c3cf90baabb32cefa41b59e"}, + {file = "mypy-0.910-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:42c266ced41b65ed40a282c575705325fa7991af370036d3f134518336636f5b"}, + {file = "mypy-0.910-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:3c4b8ca36877fc75339253721f69603a9c7fdb5d4d5a95a1a1b899d8b86a4de2"}, + {file = "mypy-0.910-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:c0df2d30ed496a08de5daed2a9ea807d07c21ae0ab23acf541ab88c24b26ab97"}, + {file = "mypy-0.910-cp39-cp39-win_amd64.whl", hash = "sha256:c6c2602dffb74867498f86e6129fd52a2770c48b7cd3ece77ada4fa38f94eba8"}, + {file = "mypy-0.910-py3-none-any.whl", hash = "sha256:ef565033fa5a958e62796867b1df10c40263ea9ded87164d67572834e57a174d"}, + {file = "mypy-0.910.tar.gz", hash = "sha256:704098302473cb31a218f1775a873b376b30b4c18229421e9e9dc8916fd16150"}, +] + +[package.dependencies] +mypy-extensions = ">=0.4.3,<0.5.0" +toml = "*" +typing-extensions = ">=3.7.4" + +[package.extras] +dmypy = ["psutil (>=4.0)"] +python2 = ["typed-ast (>=1.4.0,<1.5.0)"] + +[[package]] +name = "mypy-extensions" +version = "0.4.4" +description = "Experimental type system extensions for programs checked with the mypy typechecker." +optional = false +python-versions = ">=2.7" +files = [ + {file = "mypy_extensions-0.4.4.tar.gz", hash = "sha256:c8b707883a96efe9b4bb3aaf0dcc07e7e217d7d8368eec4db4049ee9e142f4fd"}, +] + +[[package]] +name = "packaging" +version = "24.1" +description = "Core utilities for Python packages" +optional = false +python-versions = ">=3.8" +files = [ + {file = "packaging-24.1-py3-none-any.whl", hash = "sha256:5b8f2217dbdbd2f7f384c41c628544e6d52f2d0f53c6d0c3ea61aa5d1d7ff124"}, + {file = "packaging-24.1.tar.gz", hash = "sha256:026ed72c8ed3fcce5bf8950572258698927fd1dbda10a5e981cdf0ac37f4f002"}, +] + +[[package]] +name = "plotly" +version = "5.23.0" +description = "An open-source, interactive data visualization library for Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "plotly-5.23.0-py3-none-any.whl", hash = "sha256:76cbe78f75eddc10c56f5a4ee3e7ccaade7c0a57465546f02098c0caed6c2d1a"}, + {file = "plotly-5.23.0.tar.gz", hash = "sha256:89e57d003a116303a34de6700862391367dd564222ab71f8531df70279fc0193"}, +] + +[package.dependencies] +packaging = "*" +tenacity = ">=6.2.0" + +[[package]] +name = "pluggy" +version = "1.5.0" +description = "plugin and hook calling mechanisms for python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pluggy-1.5.0-py3-none-any.whl", hash = "sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669"}, + {file = "pluggy-1.5.0.tar.gz", hash = "sha256:2cffa88e94fdc978c4c574f15f9e59b7f4201d439195c3715ca9e2486f1d0cf1"}, +] + +[package.extras] +dev = ["pre-commit", "tox"] +testing = ["pytest", "pytest-benchmark"] + +[[package]] +name = "py" +version = "1.11.0" +description = "library with cross-python path, ini-parsing, io, code, log facilities" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" +files = [ + {file = "py-1.11.0-py2.py3-none-any.whl", hash = "sha256:607c53218732647dff4acdfcd50cb62615cedf612e72d1724fb1a0cc6405b378"}, + {file = "py-1.11.0.tar.gz", hash = "sha256:51c75c4126074b472f746a24399ad32f6053d1b34b68d2fa41e558e6f4a98719"}, +] + +[[package]] +name = "pydevicetree" +version = "0.0.13" +description = "A library for parsing Devicetree Source v1" +optional = false +python-versions = ">=3.5" +files = [ + {file = "pydevicetree-0.0.13-py3-none-any.whl", hash = "sha256:d61c695cec925b90a8b5740053f4b604e51154a9b36e62a2f12ed9ceaf2f8c38"}, + {file = "pydevicetree-0.0.13.tar.gz", hash = "sha256:5700c05df89bad8fd729c11aa6f764a3323bcb3796f13b32481ae34445cfc1b7"}, +] + +[package.dependencies] +pyparsing = "*" + +[[package]] +name = "pyparsing" +version = "3.1.2" +description = "pyparsing module - Classes and methods to define and execute parsing grammars" +optional = false +python-versions = ">=3.6.8" +files = [ + {file = "pyparsing-3.1.2-py3-none-any.whl", hash = "sha256:f9db75911801ed778fe61bb643079ff86601aca99fcae6345aa67292038fb742"}, + {file = "pyparsing-3.1.2.tar.gz", hash = "sha256:a1bac0ce561155ecc3ed78ca94d3c9378656ad4c94c1270de543f621420f94ad"}, +] + +[package.extras] +diagrams = ["jinja2", "railroad-diagrams"] + +[[package]] +name = "pytest" +version = "6.2.5" +description = "pytest: simple powerful testing with Python" +optional = false +python-versions = ">=3.6" +files = [ + {file = "pytest-6.2.5-py3-none-any.whl", hash = "sha256:7310f8d27bc79ced999e760ca304d69f6ba6c6649c0b60fb0e04a4a77cacc134"}, + {file = "pytest-6.2.5.tar.gz", hash = "sha256:131b36680866a76e6781d13f101efb86cf674ebb9762eb70d3082b6f29889e89"}, +] + +[package.dependencies] +atomicwrites = {version = ">=1.0", markers = "sys_platform == \"win32\""} +attrs = ">=19.2.0" +colorama = {version = "*", markers = "sys_platform == \"win32\""} +iniconfig = "*" +packaging = "*" +pluggy = ">=0.12,<2.0" +py = ">=1.8.2" +toml = "*" + +[package.extras] +testing = ["argcomplete", "hypothesis (>=3.56)", "mock", "nose", "requests", "xmlschema"] + +[[package]] +name = "tenacity" +version = "9.0.0" +description = "Retry code until it succeeds" +optional = false +python-versions = ">=3.8" +files = [ + {file = "tenacity-9.0.0-py3-none-any.whl", hash = "sha256:93de0c98785b27fcf659856aa9f54bfbd399e29969b0621bc7f762bd441b4539"}, + {file = "tenacity-9.0.0.tar.gz", hash = "sha256:807f37ca97d62aa361264d497b0e31e92b8027044942bfa756160d908320d73b"}, +] + +[package.extras] +doc = ["reno", "sphinx"] +test = ["pytest", "tornado (>=4.5)", "typeguard"] + +[[package]] +name = "texttable" +version = "1.7.0" +description = "module to create simple ASCII tables" +optional = false +python-versions = "*" +files = [ + {file = "texttable-1.7.0-py2.py3-none-any.whl", hash = "sha256:72227d592c82b3d7f672731ae73e4d1f88cd8e2ef5b075a7a7f01a23a3743917"}, + {file = "texttable-1.7.0.tar.gz", hash = "sha256:2d2068fb55115807d3ac77a4ca68fa48803e84ebb0ee2340f858107a36522638"}, +] + +[[package]] +name = "toml" +version = "0.10.2" +description = "Python Library for Tom's Obvious, Minimal Language" +optional = false +python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" +files = [ + {file = "toml-0.10.2-py2.py3-none-any.whl", hash = "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b"}, + {file = "toml-0.10.2.tar.gz", hash = "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f"}, +] + +[[package]] +name = "typing-extensions" +version = "4.12.2" +description = "Backported and Experimental Type Hints for Python 3.8+" +optional = false +python-versions = ">=3.8" +files = [ + {file = "typing_extensions-4.12.2-py3-none-any.whl", hash = "sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d"}, + {file = "typing_extensions-4.12.2.tar.gz", hash = "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8"}, +] + +[metadata] +lock-version = "2.0" +python-versions = "^3.8" +content-hash = "afa5cb49be96467a848bab753a630c6f5ec42d6750d67d29920c3e3971774e36" diff --git a/tools/cot_dt2c/pyproject.toml b/tools/cot_dt2c/pyproject.toml new file mode 100644 index 00000000..73251d7e --- /dev/null +++ b/tools/cot_dt2c/pyproject.toml @@ -0,0 +1,63 @@ +# Poetry pyproject.toml: https://python-poetry.org/docs/pyproject/ +[build-system] +requires = ["poetry_core>=1.0.0"] +build-backend = "poetry.core.masonry.api" + +[tool.poetry] +name = "cot_dt2c" +version = "0.1.0" +description = "CoT-dt2c Tool is a python script to convert CoT DT file into corresponding C file" +authors = ["Arm Ltd <tf-a@lists.trustedfirmware.org>"] +license = "BSD-3" +repository = "https://git.trustedfirmware.org/TF-A/trusted-firmware-a.git/" +homepage = "https://trustedfirmware-a.readthedocs.io/en/latest/index.html" + +# Pypi classifiers: https://pypi.org/classifiers/ +classifiers = [ + "Development Status :: 3 - Alpha", + "Intended Audience :: Developers", + "Operating System :: OS Independent", + "Topic :: Software Development :: Libraries :: Python Modules", + "License :: OSI Approved :: BSD License", + "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3.8", + "Programming Language :: Python :: 3.9", +] + + +[tool.poetry.dependencies] +python = "^3.8" +click = "^8.1.7" +plotly = "^5.23.0" +pydevicetree = "0.0.13" +igraph = "^0.11.6" +pyparsing = "^3.1.2" + +[tool.poetry.group.dev] +optional = true + +[tool.poetry.group.dev.dependencies] +mypy = "^0.910" +pytest = "^6.2.5" + +[tool.mypy] +# https://mypy.readthedocs.io/en/latest/config_file.html#using-a-pyproject-toml-file +python_version = "3.8" +pretty = true +show_traceback = true +color_output = true + +[[tool.mypy.overrides]] +module = ["igraph", "pydevicetree", "pydevicetree.ast", "plotly", "plotly.graph_objects"] +ignore_missing_imports = true + +[tool.coverage.run] +source = ["tests"] + +[tool.coverage.paths] +source = "cot_dt2c" + +[tool.poetry.scripts] +# Entry points for the package https://python-poetry.org/docs/pyproject/#scripts +# "cot-dt2c" = "cot_dt2c.__main__:cli" +"cot-dt2c" = "cot_dt2c.__main__:cli" diff --git a/tools/cot_dt2c/tests/test.dtsi b/tools/cot_dt2c/tests/test.dtsi new file mode 100644 index 00000000..ee744e66 --- /dev/null +++ b/tools/cot_dt2c/tests/test.dtsi @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2024, Arm Limited. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + * + * This file provide a valid CoT DT file + * + */ + +#include <example/example.h> +#include <example/example/example.h> + +cot { + manifests { + compatible = "arm, cert-descs"; + +#if defined(test) + example_cert: example_cert { + root-certificate; + image-id =<EXAMPLE_ID>; + antirollback-counter = <&example_ctr>; + + example_hash: example_hash + { + oid = EXAMPLE_HASH_ID; + }; + }; +#endif + }; + + images { + compatible = "arm, img-descs"; + + example { + image-id = <EXAMPLE_ID>; + parent = <&example_cert>; + hash = <&example_hash>; + }; + }; +}; + +non_volatile_counters: non_volatile_counters { + compatible = "arm, non-volatile-counter"; + + #address-cells = <1>; + #size-cells = <0>; + + example_ctr: example_ctr { + id = <TRUSTED_NV_CTR_ID>; + oid = CCA_FW_NVCOUNTER_OID; + }; +}; + +rot_keys { + example_pk: example_pk { + oid = EXAMPLE_PK_OID; + }; +}; diff --git a/tools/cot_dt2c/tests/test2.dtsi b/tools/cot_dt2c/tests/test2.dtsi new file mode 100644 index 00000000..c4dbf833 --- /dev/null +++ b/tools/cot_dt2c/tests/test2.dtsi @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2024, Arm Limited. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + * + * This file provide a valid CoT DT file + * + */ + +#if test +#include <example/example.h> +#include <example/example/example.h> +#endif + +cot +{ + manifests + { + compatible = "arm, cert-descs"; +#if defined (test) + example_cert: example_cert + { + root-certificate; + image-id =<EXAMPLE_ID>; + antirollback-counter = <&example_ctr>; + + example_hash: example_hash + { + oid = EXAMPLE_HASH_ID; + }; + + }; +#endif + }; + + images + { + compatible = "arm, img-descs"; + + example + { + image-id = <EXAMPLE_ID>; + parent = <&example_cert>; + hash = <&example_hash>; + }; + }; +}; + +non_volatile_counters: non_volatile_counters +{ + compatible = "arm, non-volatile-counter"; + + #address-cells = <1>; + #size-cells = <0>; + + example_ctr: example_ctr + { + id = <TRUSTED_NV_CTR_ID>; + oid = CCA_FW_NVCOUNTER_OID; + }; +}; + +rot_keys +{ + example_pk: example_pk + { + oid = EXAMPLE_PK_OID; + }; +}; diff --git a/tools/cot_dt2c/tests/test_invalid_bracket.dtsi b/tools/cot_dt2c/tests/test_invalid_bracket.dtsi new file mode 100644 index 00000000..9752ecfa --- /dev/null +++ b/tools/cot_dt2c/tests/test_invalid_bracket.dtsi @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2024, Arm Limited. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + * + * This file provide a malformed CoT DT file that there is + * unmatching bracket + * + */ + +cot { + manifests { + compatible = "arm, cert-descs"; + + example_cert: example_cert { + root-certificate; + image-id =<2>; + antirollback-counter = <&example_ctr>; + + example_hash: example_hash + { + oid = "1.3.6.1.4.1.4128.2100.101"; + }; + + }; + }; + + images { + compatible = "arm, img-descs"; + + example { + image-id = <2>; + parent = <&example_cert>; + hash = <&example_hash>; + }; +}; + +non_volatile_counters: non_volatile_counters { + compatible = "arm, non-volatile-counter"; + + #address-cells = <1>; + #size-cells = <0>; + + example_ctr: example_ctr { + id = <0>; + oid = "1.3.6.1.4.1.4128.2100.3"; + }; +}; + +rot_keys { + example_pk: example_pk { + oid = "1.3.6.1.4.1.4128.2100.101"; + }; +}; diff --git a/tools/cot_dt2c/tests/test_invalid_missing_attribute.dtsi b/tools/cot_dt2c/tests/test_invalid_missing_attribute.dtsi new file mode 100644 index 00000000..e35ab737 --- /dev/null +++ b/tools/cot_dt2c/tests/test_invalid_missing_attribute.dtsi @@ -0,0 +1,108 @@ +/* + * Copyright (c) 2024, Arm Limited. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + * + * This file provide a malformed CoT DT file that there + * are image/certificate that missing mandantory attributes + * + */ + +cot { + manifests { + compatible = "arm, cert-descs"; + + cca_content_cert: cca_content_cert { + root-certificate; + antirollback-counter = <&cca_nv_ctr>; + + hw_config_hash: hw_config_hash { + }; + + soc_fw_config_hash: soc_fw_config_hash { + oid = "1.3.6.1.4.1.4128.2100.604"; + }; + }; + + plat_key_cert: plat_key_cert { + root-certificate; + image-id = <38>; + signing-key = <&prot_pk>; + antirollback-counter = <&non_trusted_nv_ctr>; + + plat_pk: plat_pk { + oid = "1.3.6.1.4.1.4128.2100.1105"; + }; + }; + + non_trusted_fw_content_cert: non_trusted_fw_content_cert { + image-id = <15>; + signing-key = <&plat_pk>; + antirollback-counter = <&non_trusted_nv_ctr>; + + nt_world_bl_hash: nt_world_bl_hash { + oid = "1.3.6.1.4.1.4128.2100.1201"; + }; + nt_fw_config_hash: nt_fw_config_hash { + oid = "1.3.6.1.4.1.4128.2100.1202"; + }; + }; + + images { + compatible = "arm, img-descs"; + + hw_config { + image-id = <23>; + hash = <&hw_config_hash>; + }; + + soc_fw_config { + image-id = <25>; + parent = <&cca_content_cert>; + hash = <&soc_fw_config_hash>; + }; + + bl33_image { + image-id = <5>; + parent = <&non_trusted_fw_content_cert>; + hash = <&nt_world_bl_hash>; + }; + + nt_fw_config { + image-id = <27>; + parent = <&non_trusted_fw_content_cert>; + hash = <&nt_fw_config_hash>; + }; + }; +}; + +non_volatile_counters: non_volatile_counters { + compatible = "arm, non-volatile-counter"; + + #address-cells = <1>; + #size-cells = <0>; + + cca_nv_ctr: cca_nv_ctr { + id = <0>; + oid = "1.3.6.1.4.1.4128.2100.3"; + }; + + trusted_nv_ctr: trusted_nv_ctr { + id = <0>; + oid = "1.3.6.1.4.1.4128.2100.1"; + }; + + non_trusted_nv_ctr: non_trusted_nv_ctr { + id = <1>; + oid = "1.3.6.1.4.1.4128.2100.2"; + }; +}; + +rot_keys { + swd_rot_pk: swd_rot_pk { + oid = "1.3.6.1.4.1.4128.2100.1103"; + }; + prot_pk: prot_pk { + oid = "1.3.6.1.4.1.4128.2100.1102"; + }; +}; diff --git a/tools/cot_dt2c/tests/test_invalid_missing_ctr.dtsi b/tools/cot_dt2c/tests/test_invalid_missing_ctr.dtsi new file mode 100644 index 00000000..c572b1af --- /dev/null +++ b/tools/cot_dt2c/tests/test_invalid_missing_ctr.dtsi @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2024, Arm Limited. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + * + * This file provide a malformed CoT DT file that there + * are image/certificate that missing definition of + * nv counters + * + */ + +cot { + manifests { + compatible = "arm, cert-descs"; + + example_cert: example_cert { + root-certificate; + image-id =<2>; + signing-key = <&swd_rot_pk>; + antirollback-counter = <&example_ctr>; + + example_hash: example_hash + { + oid = "1.3.6.1.4.1.4128.2100.104"; + }; + + }; + }; + + images { + compatible = "arm, img-descs"; + + example { + image-id = <2>; + parent = <&example_cert>; + hash = <&example_hash>; + }; + }; +}; + +non_volatile_counters: non_volatile_counters { + compatible = "arm, non-volatile-counter"; + + #address-cells = <1>; + #size-cells = <0>; + +}; + +rot_keys { + example_pk: example_pk { + oid = "1.3.6.1.4.1.4128.2100.104"; + }; +}; diff --git a/tools/cot_dt2c/tests/test_invalid_missing_root.dtsi b/tools/cot_dt2c/tests/test_invalid_missing_root.dtsi new file mode 100644 index 00000000..465a4c6b --- /dev/null +++ b/tools/cot_dt2c/tests/test_invalid_missing_root.dtsi @@ -0,0 +1,242 @@ +/* + * Copyright (c) 2024, Arm Limited. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + * + * This file provide a malformed CoT DT file that there + * are image/certificate that missing root certificate + * + */ + +#include <tools_share/cca_oid.h> +#include <common/tbbr/tbbr_img_def.h> +#include <common/nv_cntr_ids.h> + +cot { + manifests { + compatible = "arm, cert-descs"; + + core_swd_key_cert: core_swd_key_cert { + image-id = <CORE_SWD_KEY_CERT_ID>; + signing-key = <&swd_rot_pk>; + antirollback-counter = <&trusted_nv_ctr>; + + core_swd_pk: core_swd_pk { + oid = CORE_SWD_PK_OID; + }; + }; + + trusted_os_fw_content_cert: trusted_os_fw_content_cert { + image-id = <TRUSTED_OS_FW_CONTENT_CERT_ID>; + parent = <&core_swd_key_cert>; + signing-key = <&core_swd_pk>; + antirollback-counter = <&trusted_nv_ctr>; + + tos_fw_hash: tos_fw_hash { + oid = TRUSTED_OS_FW_HASH_OID; + }; + tos_fw_config_hash: tos_fw_config_hash { + oid = TRUSTED_OS_FW_CONFIG_HASH_OID; + }; + }; + + plat_key_cert: plat_key_cert { + image-id = <PLAT_KEY_CERT_ID>; + signing-key = <&prot_pk>; + antirollback-counter = <&non_trusted_nv_ctr>; + + plat_pk: plat_pk { + oid = PLAT_PK_OID; + }; + }; + + non_trusted_fw_content_cert: non_trusted_fw_content_cert { + image-id = <NON_TRUSTED_FW_CONTENT_CERT_ID>; + parent = <&plat_key_cert>; + signing-key = <&plat_pk>; + antirollback-counter = <&non_trusted_nv_ctr>; + + nt_world_bl_hash: nt_world_bl_hash { + oid = NON_TRUSTED_WORLD_BOOTLOADER_HASH_OID; + }; + nt_fw_config_hash: nt_fw_config_hash { + oid = NON_TRUSTED_FW_CONFIG_HASH_OID; + }; + }; + +#if defined(SPD_spmd) + sip_sp_content_cert: sip_sp_content_cert { + image-id = <SIP_SP_CONTENT_CERT_ID>; + parent = <&core_swd_key_cert>; + signing-key = <&core_swd_pk>; + antirollback-counter = <&trusted_nv_ctr>; + + sp_pkg1_hash: sp_pkg1_hash { + oid = SP_PKG1_HASH_OID; + }; + sp_pkg2_hash: sp_pkg2_hash { + oid = SP_PKG2_HASH_OID; + }; + sp_pkg3_hash: sp_pkg3_hash { + oid = SP_PKG3_HASH_OID; + }; + sp_pkg4_hash: sp_pkg4_hash { + oid = SP_PKG4_HASH_OID; + }; + }; + + plat_sp_content_cert: plat_sp_content_cert { + image-id = <PLAT_SP_CONTENT_CERT_ID>; + parent = <&plat_key_cert>; + signing-key = <&plat_pk>; + antirollback-counter = <&non_trusted_nv_ctr>; + + sp_pkg5_hash: sp_pkg5_hash { + oid = SP_PKG5_HASH_OID; + }; + sp_pkg6_hash: sp_pkg6_hash { + oid = SP_PKG6_HASH_OID; + }; + sp_pkg7_hash: sp_pkg7_hash { + oid = SP_PKG7_HASH_OID; + }; + sp_pkg8_hash: sp_pkg8_hash { + oid = SP_PKG8_HASH_OID; + }; + }; +#endif + }; + + images { + compatible = "arm, img-descs"; + + hw_config { + image-id = <HW_CONFIG_ID>; + parent = <&cca_content_cert>; + hash = <&hw_config_hash>; + }; + + bl31_image { + image-id = <BL31_IMAGE_ID>; + parent = <&cca_content_cert>; + hash = <&soc_fw_hash>; + }; + + soc_fw_config { + image-id = <SOC_FW_CONFIG_ID>; + parent = <&cca_content_cert>; + hash = <&soc_fw_config_hash>; + }; + + rmm_image { + image-id = <RMM_IMAGE_ID>; + parent = <&cca_content_cert>; + hash = <&rmm_hash>; + }; + + bl32_image { + image-id = <BL32_IMAGE_ID>; + parent = <&trusted_os_fw_content_cert>; + hash = <&tos_fw_hash>; + }; + + tos_fw_config { + image-id = <TOS_FW_CONFIG_ID>; + parent = <&trusted_os_fw_content_cert>; + hash = <&tos_fw_config_hash>; + }; + + bl33_image { + image-id = <BL33_IMAGE_ID>; + parent = <&non_trusted_fw_content_cert>; + hash = <&nt_world_bl_hash>; + }; + + nt_fw_config { + image-id = <NT_FW_CONFIG_ID>; + parent = <&non_trusted_fw_content_cert>; + hash = <&nt_fw_config_hash>; + }; + +#if defined(SPD_spmd) + sp_pkg1 { + image-id = <SP_PKG1_ID>; + parent = <&sip_sp_content_cert>; + hash = <&sp_pkg1_hash>; + }; + + sp_pkg2 { + image-id = <SP_PKG2_ID>; + parent = <&sip_sp_content_cert>; + hash = <&sp_pkg2_hash>; + }; + + sp_pkg3 { + image-id = <SP_PKG3_ID>; + parent = <&sip_sp_content_cert>; + hash = <&sp_pkg3_hash>; + }; + + sp_pkg4 { + image-id = <SP_PKG4_ID>; + parent = <&sip_sp_content_cert>; + hash = <&sp_pkg4_hash>; + }; + + sp_pkg5 { + image-id = <SP_PKG5_ID>; + parent = <&plat_sp_content_cert>; + hash = <&sp_pkg5_hash>; + }; + + sp_pkg6 { + image-id = <SP_PKG6_ID>; + parent = <&plat_sp_content_cert>; + hash = <&sp_pkg6_hash>; + }; + + sp_pkg7 { + image-id = <SP_PKG7_ID>; + parent = <&plat_sp_content_cert>; + hash = <&sp_pkg7_hash>; + }; + + sp_pkg8 { + image-id = <SP_PKG8_ID>; + parent = <&plat_sp_content_cert>; + hash = <&sp_pkg8_hash>; + }; +#endif + }; +}; + +non_volatile_counters: non_volatile_counters { + compatible = "arm, non-volatile-counter"; + + #address-cells = <1>; + #size-cells = <0>; + + cca_nv_ctr: cca_nv_ctr { + id = <TRUSTED_NV_CTR_ID>; + oid = CCA_FW_NVCOUNTER_OID; + }; + + trusted_nv_ctr: trusted_nv_ctr { + id = <TRUSTED_NV_CTR_ID>; + oid = TRUSTED_FW_NVCOUNTER_OID; + }; + + non_trusted_nv_ctr: non_trusted_nv_ctr { + id = <NON_TRUSTED_NV_CTR_ID>; + oid = NON_TRUSTED_FW_NVCOUNTER_OID; + }; +}; + +rot_keys { + swd_rot_pk: swd_rot_pk { + oid = SWD_ROT_PK_OID; + }; + prot_pk: prot_pk { + oid = PROT_PK_OID; + }; +}; diff --git a/tools/cot_dt2c/tests/test_invalid_undefined_parent.dtsi b/tools/cot_dt2c/tests/test_invalid_undefined_parent.dtsi new file mode 100644 index 00000000..b6056caf --- /dev/null +++ b/tools/cot_dt2c/tests/test_invalid_undefined_parent.dtsi @@ -0,0 +1,113 @@ +/* + * Copyright (c) 2024, Arm Limited. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + * + * This file provide a malformed CoT DT file that there + * are image/certificate that points to invalid parent + * + */ + +cot { + manifests { + compatible = "arm, cert-descs"; + + cca_content_cert: cca_content_cert { + root-certificate; + image-id =<36>; + antirollback-counter = <&cca_nv_ctr>; + + hw_config_hash: hw_config_hash { + oid = "1.3.6.1.4.1.4128.2100.203"; + }; + soc_fw_config_hash: soc_fw_config_hash { + oid = "1.3.6.1.4.1.4128.2100.604"; + }; + }; + + plat_key_cert: plat_key_cert { + root-certificate; + image-id = <38>; + signing-key = <&prot_pk>; + antirollback-counter = <&non_trusted_nv_ctr>; + + plat_pk: plat_pk { + oid = "1.3.6.1.4.1.4128.2100.1105"; + }; + }; + + non_trusted_fw_content_cert: non_trusted_fw_content_cert { + image-id = <NON_TRUSTED_FW_CONTENT_CERT_ID>; + parent = <&wrong_parent>; + signing-key = <&plat_pk>; + antirollback-counter = <&non_trusted_nv_ctr>; + + nt_world_bl_hash: nt_world_bl_hash { + oid = "1.3.6.1.4.1.4128.2100.1201"; + }; + nt_fw_config_hash: nt_fw_config_hash { + oid = "1.3.6.1.4.1.4128.2100.1202"; + }; + }; + + }; + + images { + compatible = "arm, img-descs"; + + hw_config { + image-id = <23>; + parent = <&cca_content_cert>; + hash = <&hw_config_hash>; + }; + + soc_fw_config { + image-id = <25>; + parent = <&cca_content_cert>; + hash = <&soc_fw_config_hash>; + }; + + bl33_image { + image-id = <5>; + parent = <&non_trusted_fw_content_cert>; + hash = <&nt_world_bl_hash>; + }; + + nt_fw_config { + image-id = <27>; + hash = <&nt_fw_config_hash>; + }; + }; +}; + +non_volatile_counters: non_volatile_counters { + compatible = "arm, non-volatile-counter"; + + #address-cells = <1>; + #size-cells = <0>; + + cca_nv_ctr: cca_nv_ctr { + id = <0>; + oid = "1.3.6.1.4.1.4128.2100.3"; + }; + + trusted_nv_ctr: trusted_nv_ctr { + id = <0>; + oid = "1.3.6.1.4.1.4128.2100.1"; + }; + + non_trusted_nv_ctr: non_trusted_nv_ctr { + id = <1>; + oid = "1.3.6.1.4.1.4128.2100.2"; + }; +}; + +rot_keys { + swd_rot_pk: swd_rot_pk { + oid = "1.3.6.1.4.1.4128.2100.1103"; + }; + + prot_pk: prot_pk { + oid = "1.3.6.1.4.1.4128.2100.1102"; + }; +}; diff --git a/tools/cot_dt2c/tests/test_util.py b/tools/cot_dt2c/tests/test_util.py new file mode 100644 index 00000000..b8e44d47 --- /dev/null +++ b/tools/cot_dt2c/tests/test_util.py @@ -0,0 +1,33 @@ +# +# Copyright (c) 2024, Arm Limited and Contributors. All rights reserved. +# +# SPDX-License-Identifier: BSD-3-Clause +# + +import os +import sys + +from cot_dt2c.cli import * +from click.testing import CliRunner + +def get_script_path(): + return os.path.dirname(os.path.realpath(sys.argv[0])) + +def test_convert(): + runner = CliRunner() + test_file = get_script_path() + "/test.dtsi" + test_output = get_script_path() + "/test.c" + + result = runner.invoke(convert_to_c, [test_file, test_output]) + try: + assert result.output == "" + except: + print("test convert fail") + + try: + os.remove(test_output) + except OSError: + pass + +if __name__=="__main__": + test_convert() diff --git a/tools/encrypt_fw/Makefile b/tools/encrypt_fw/Makefile index 924e5feb..50b0fa2f 100644 --- a/tools/encrypt_fw/Makefile +++ b/tools/encrypt_fw/Makefile @@ -1,21 +1,22 @@ # +# Copyright (c) 2024, Arm Limited. All rights reserved. # Copyright (c) 2019-2022, Linaro Limited. All rights reserved. # # SPDX-License-Identifier: BSD-3-Clause # -V ?= 0 BUILD_INFO ?= 1 DEBUG := 0 ENCTOOL ?= encrypt_fw${BIN_EXT} BINARY := $(notdir ${ENCTOOL}) OPENSSL_DIR := /usr - MAKE_HELPERS_DIRECTORY := ../../make_helpers/ include ${MAKE_HELPERS_DIRECTORY}build_macros.mk include ${MAKE_HELPERS_DIRECTORY}build_env.mk +include ${MAKE_HELPERS_DIRECTORY}common.mk include ${MAKE_HELPERS_DIRECTORY}defaults.mk +include ${MAKE_HELPERS_DIRECTORY}toolchain.mk OBJECTS := src/encrypt.o \ src/cmd_opt.o \ @@ -36,11 +37,6 @@ else HOSTCCFLAGS += -O2 -DLOG_LEVEL=10 endif endif -ifeq (${V},0) - Q := @ -else - Q := -endif HOSTCCFLAGS += ${DEFINES} # USING_OPENSSL3 flag will be added to the HOSTCCFLAGS variable with the proper @@ -61,29 +57,25 @@ INC_DIR := -I ./include -I ../../include/tools_share -I ${OPENSSL_DIR}/include LIB_DIR := -L ${OPENSSL_DIR}/lib -L ${OPENSSL_DIR} LIB := -lssl -lcrypto -HOSTCC ?= gcc - .PHONY: all clean realclean --openssl all: --openssl ${BINARY} ${BINARY}: ${OBJECTS} Makefile - @echo " HOSTLD $@" - @echo 'const char build_msg[] = "Built : "__TIME__", "__DATE__;' | \ - ${HOSTCC} -c ${HOSTCCFLAGS} -xc - -o src/build_msg.o - ${Q}${HOSTCC} src/build_msg.o ${OBJECTS} ${LIB_DIR} ${LIB} -o $@ + $(s)echo " HOSTLD $@" + $(q)$(host-cc) ${OBJECTS} ${LIB_DIR} ${LIB} -o $@ %.o: %.c - @echo " HOSTCC $<" - ${Q}${HOSTCC} -c ${HOSTCCFLAGS} ${INC_DIR} $< -o $@ + $(s)echo " HOSTCC $<" + $(q)$(host-cc) -c ${HOSTCCFLAGS} ${INC_DIR} $< -o $@ --openssl: ifeq ($(DEBUG),1) - @echo "Selected OpenSSL version: ${OPENSSL_CURRENT_VER}" + $(s)echo "Selected OpenSSL version: ${OPENSSL_CURRENT_VER}" endif clean: - $(call SHELL_DELETE_ALL, src/build_msg.o ${OBJECTS}) + $(call SHELL_DELETE_ALL,${OBJECTS}) realclean: clean $(call SHELL_DELETE,${BINARY}) diff --git a/tools/encrypt_fw/src/main.c b/tools/encrypt_fw/src/main.c index 39b7af76..6e43e73a 100644 --- a/tools/encrypt_fw/src/main.c +++ b/tools/encrypt_fw/src/main.c @@ -1,4 +1,5 @@ /* + * Copyright (c) 2024, Arm Limited and Contributors. All rights reserved. * Copyright (c) 2019, Linaro Limited. All rights reserved. * Author: Sumit Garg <sumit.garg@linaro.org> * @@ -25,8 +26,7 @@ /* Global options */ -/* Info messages created in the Makefile */ -extern const char build_msg[]; +static const char build_msg[] = "Built : " __TIME__ ", " __DATE__; static char *key_algs_str[] = { [KEY_ALG_GCM] = "gcm", diff --git a/tools/fiptool/Makefile b/tools/fiptool/Makefile index fda7c779..54dee87e 100644 --- a/tools/fiptool/Makefile +++ b/tools/fiptool/Makefile @@ -1,5 +1,5 @@ # -# Copyright (c) 2014-2023, Arm Limited and Contributors. All rights reserved. +# Copyright (c) 2014-2024, Arm Limited and Contributors. All rights reserved. # # SPDX-License-Identifier: BSD-3-Clause # @@ -7,12 +7,13 @@ MAKE_HELPERS_DIRECTORY := ../../make_helpers/ include ${MAKE_HELPERS_DIRECTORY}build_macros.mk include ${MAKE_HELPERS_DIRECTORY}build_env.mk +include ${MAKE_HELPERS_DIRECTORY}common.mk include ${MAKE_HELPERS_DIRECTORY}defaults.mk +include ${MAKE_HELPERS_DIRECTORY}toolchain.mk FIPTOOL ?= fiptool${BIN_EXT} PROJECT := $(notdir ${FIPTOOL}) OBJECTS := fiptool.o tbbr_config.o -V ?= 0 STATIC ?= 0 override CPPFLAGS += -D_GNU_SOURCE -D_XOPEN_SOURCE=700 @@ -52,14 +53,6 @@ endif # STATIC HOSTCCFLAGS += ${DEFINES} -ifeq (${V},0) - Q := @ -else - Q := -endif - -HOSTCC ?= gcc - ifneq (${PLAT},) TF_PLATFORM_ROOT := ../../plat/ include ${MAKE_HELPERS_DIRECTORY}plat_helpers.mk @@ -80,22 +73,22 @@ DEPS := $(patsubst %.o,%.d,$(OBJECTS)) all: --openssl ${PROJECT} ${PROJECT}: ${OBJECTS} Makefile - @echo " HOSTLD $@" - ${Q}${HOSTCC} ${OBJECTS} -o $@ $(LDOPTS) - @${ECHO_BLANK_LINE} - @echo "Built $@ successfully" - @${ECHO_BLANK_LINE} + $(s)echo " HOSTLD $@" + $(q)$(host-cc) ${OBJECTS} -o $@ $(LDOPTS) + $(s)echo + $(s)echo "Built $@ successfully" + $(s)echo %.o: %.c Makefile - @echo " HOSTCC $<" - ${Q}${HOSTCC} -c ${CPPFLAGS} ${HOSTCCFLAGS} ${INCLUDE_PATHS} -MD -MP $< -o $@ + $(s)echo " HOSTCC $<" + $(q)$(host-cc) -c ${CPPFLAGS} ${HOSTCCFLAGS} ${INCLUDE_PATHS} -MD -MP $< -o $@ -include $(DEPS) --openssl: ifeq ($(STATIC),0) ifeq ($(DEBUG),1) - @echo "Selected OpenSSL version: ${OPENSSL_CURRENT_VER}" + $(s)echo "Selected OpenSSL version: ${OPENSSL_CURRENT_VER}" endif endif # STATIC diff --git a/tools/fiptool/fiptool.c b/tools/fiptool/fiptool.c index 6c566ef0..27119a1a 100644 --- a/tools/fiptool/fiptool.c +++ b/tools/fiptool/fiptool.c @@ -1,12 +1,13 @@ /* - * Copyright (c) 2016-2023, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2016-2024, Arm Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ -#ifndef _MSC_VER +#ifdef __linux__ #include <sys/mount.h> #endif + #include <sys/types.h> #include <sys/stat.h> diff --git a/plat/arm/board/juno/fip/plat_def_uuid_config.c b/tools/fiptool/plat_fiptool/arm/board/juno/plat_def_uuid_config.c similarity index 100% rename from plat/arm/board/juno/fip/plat_def_uuid_config.c rename to tools/fiptool/plat_fiptool/arm/board/juno/plat_def_uuid_config.c diff --git a/tools/fiptool/plat_fiptool/arm/board/juno/plat_fiptool.mk b/tools/fiptool/plat_fiptool/arm/board/juno/plat_fiptool.mk index fef2116e..5549b0d3 100644 --- a/tools/fiptool/plat_fiptool/arm/board/juno/plat_fiptool.mk +++ b/tools/fiptool/plat_fiptool/arm/board/juno/plat_fiptool.mk @@ -11,6 +11,6 @@ HOSTCCFLAGS += -DPLAT_DEF_FIP_UUID ifeq (${ETHOSN_NPU_TZMP1},1) HOSTCCFLAGS += -DETHOSN_NPU_TZMP1 endif -INCLUDE_PATHS += -I./ -I${PLAT_DIR}fip -I../../include/ -OBJECTS += ${PLAT_DIR}fip/plat_def_uuid_config.o +INCLUDE_PATHS += -I./ -I../../plat/arm/board/juno/fip -I../../include +OBJECTS += plat_fiptool/arm/board/juno/plat_def_uuid_config.o endif diff --git a/tools/fiptool/plat_fiptool/arm/board/tc/plat_def_uuid_config.c b/tools/fiptool/plat_fiptool/arm/board/tc/plat_def_uuid_config.c index 903310b2..792593f3 100644 --- a/tools/fiptool/plat_fiptool/arm/board/tc/plat_def_uuid_config.c +++ b/tools/fiptool/plat_fiptool/arm/board/tc/plat_def_uuid_config.c @@ -13,44 +13,44 @@ toc_entry_t plat_def_toc_entries[] = { { - .name = "RSS Firmware BL1_2 image", - .uuid = UUID_RSS_FIRMWARE_BL1_2, - .cmdline_name = "rss-bl1_2" + .name = "RSE Firmware BL1_2 image", + .uuid = UUID_RSE_FIRMWARE_BL1_2, + .cmdline_name = "rse-bl1_2" }, { - .name = "RSS Firmware BL2 image", - .uuid = UUID_RSS_FIRMWARE_BL2, - .cmdline_name = "rss-bl2" + .name = "RSE Firmware BL2 image", + .uuid = UUID_RSE_FIRMWARE_BL2, + .cmdline_name = "rse-bl2" }, { - .name = "RSS Firmware SCP BL1 image", - .uuid = UUID_RSS_FIRMWARE_SCP_BL1, - .cmdline_name = "rss-scp-bl1" + .name = "RSE Firmware SCP BL1 image", + .uuid = UUID_RSE_FIRMWARE_SCP_BL1, + .cmdline_name = "rse-scp-bl1" }, { - .name = "RSS Firmware AP BL1 image", - .uuid = UUID_RSS_FIRMWARE_AP_BL1, - .cmdline_name = "rss-ap-bl1" + .name = "RSE Firmware AP BL1 image", + .uuid = UUID_RSE_FIRMWARE_AP_BL1, + .cmdline_name = "rse-ap-bl1" }, { - .name = "RSS Firmware non-secure image", - .uuid = UUID_RSS_FIRMWARE_NS, - .cmdline_name = "rss-ns" + .name = "RSE Firmware non-secure image", + .uuid = UUID_RSE_FIRMWARE_NS, + .cmdline_name = "rse-ns" }, { - .name = "RSS Firmware secure image", - .uuid = UUID_RSS_FIRMWARE_S, - .cmdline_name = "rss-s" + .name = "RSE Firmware secure image", + .uuid = UUID_RSE_FIRMWARE_S, + .cmdline_name = "rse-s" }, { - .name = "RSS Firmware non-secure SIC tables", - .uuid = UUID_RSS_SIC_TABLES_NS, - .cmdline_name = "rss-sic-tables-ns" + .name = "RSE Firmware non-secure SIC tables", + .uuid = UUID_RSE_SIC_TABLES_NS, + .cmdline_name = "rse-sic-tables-ns" }, { - .name = "RSS Firmware secure SIC tables", - .uuid = UUID_RSS_SIC_TABLES_S, - .cmdline_name = "rss-sic-tables-s" + .name = "RSE Firmware secure SIC tables", + .uuid = UUID_RSE_SIC_TABLES_S, + .cmdline_name = "rse-sic-tables-s" }, { diff --git a/tools/fiptool/plat_fiptool/st/stm32mp1/plat_def_uuid_config.c b/tools/fiptool/plat_fiptool/st/plat_def_uuid_config.c similarity index 71% rename from tools/fiptool/plat_fiptool/st/stm32mp1/plat_def_uuid_config.c rename to tools/fiptool/plat_fiptool/st/plat_def_uuid_config.c index 4df41446..8d3329fc 100644 --- a/tools/fiptool/plat_fiptool/st/stm32mp1/plat_def_uuid_config.c +++ b/tools/fiptool/plat_fiptool/st/plat_def_uuid_config.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022-2023, STMicroelectronics - All Rights Reserved + * Copyright (c) 2022-2024, STMicroelectronics - All Rights Reserved * * SPDX-License-Identifier: BSD-3-Clause */ @@ -11,6 +11,11 @@ #include "tbbr_config.h" toc_entry_t plat_def_toc_entries[] = { + { + .name = "DDR_FW", + .uuid = UUID_DDR_FW, + .cmdline_name = "ddr-fw" + }, { .name = "STM32MP CONFIG CERT", .uuid = UUID_STM32MP_CONFIG_CERT, diff --git a/tools/fiptool/plat_fiptool/st/stm32mp1/plat_fiptool.mk b/tools/fiptool/plat_fiptool/st/plat_fiptool.mk similarity index 60% rename from tools/fiptool/plat_fiptool/st/stm32mp1/plat_fiptool.mk rename to tools/fiptool/plat_fiptool/st/plat_fiptool.mk index 1ba47c1f..494715cc 100644 --- a/tools/fiptool/plat_fiptool/st/stm32mp1/plat_fiptool.mk +++ b/tools/fiptool/plat_fiptool/st/plat_fiptool.mk @@ -1,5 +1,5 @@ # -# Copyright (c) 2021-2022, STMicroelectronics - All Rights Reserved +# Copyright (c) 2021-2024, STMicroelectronics - All Rights Reserved # # SPDX-License-Identifier: BSD-3-Clause # @@ -9,15 +9,15 @@ # in the plat_def_toc_entries[]. PLAT_DEF_UUID_FILE_NAME := plat_def_uuid_config -INCLUDE_PATHS += -I${PLAT_DIR}/include -I./ +INCLUDE_PATHS += -I../../plat/st/common/include -I./ PLAT_DEF_UUID := yes ifeq (${PLAT_DEF_UUID},yes) HOSTCCFLAGS += -DPLAT_DEF_FIP_UUID -${PLAT_DEF_UUID_FILE_NAME}.o: plat_fiptool/st/stm32mp1/${PLAT_DEF_UUID_FILE_NAME}.c - ${HOSTCC} -c ${CPPFLAGS} ${HOSTCCFLAGS} ${INCLUDE_PATHS} $< -o $@ +${PLAT_DEF_UUID_FILE_NAME}.o: plat_fiptool/st/${PLAT_DEF_UUID_FILE_NAME}.c + $(host-cc) -c ${CPPFLAGS} ${HOSTCCFLAGS} ${INCLUDE_PATHS} $< -o $@ PLAT_OBJECTS += ${PLAT_DEF_UUID_FILE_NAME}.o endif diff --git a/tools/marvell/doimage/Makefile b/tools/marvell/doimage/Makefile index 9f0d89d3..a4f7a1d8 100644 --- a/tools/marvell/doimage/Makefile +++ b/tools/marvell/doimage/Makefile @@ -4,6 +4,9 @@ # SPDX-License-Identifier: BSD-3-Clause # https://spdx.org/licenses +include ../../../make_helpers/common.mk +include ../../../make_helpers/toolchain.mk + PROJECT = doimage OBJECTS = doimage.o @@ -25,7 +28,6 @@ HOSTCCFLAGS += ${DOIMAGE_CC_FLAGS} # could get pulled in from firmware tree. INCLUDE_PATHS = -I. -HOSTCC ?= gcc RM := rm -rf .PHONY: all clean @@ -33,16 +35,16 @@ RM := rm -rf all: ${PROJECT} ${PROJECT}: ${OBJECTS} Makefile - @echo " HOSTLD $@" - ${Q}${HOSTCC} ${OBJECTS} ${DOIMAGE_LD_FLAGS} -o $@ - @echo - @echo "Built $@ successfully" - @echo + $(s)echo " HOSTLD $@" + $(q)$(host-cc) ${OBJECTS} ${DOIMAGE_LD_FLAGS} -o $@ + $(s)echo + $(s)echo "Built $@ successfully" + $(s)echo %.o: %.c Makefile - @echo " HOSTCC $<" - ${Q}${HOSTCC} -c ${HOSTCCFLAGS} ${INCLUDE_PATHS} $< -o $@ + $(s)echo " HOSTCC $<" + $(q)$(host-cc) -c ${HOSTCCFLAGS} ${INCLUDE_PATHS} $< -o $@ clean: - ${Q}${RM} ${PROJECT} - ${Q}${RM} ${OBJECTS} + $(q)${RM} ${PROJECT} + $(q)${RM} ${OBJECTS} diff --git a/tools/marvell/doimage/doimage.c b/tools/marvell/doimage/doimage.c index 513f33f3..1f0985c4 100644 --- a/tools/marvell/doimage/doimage.c +++ b/tools/marvell/doimage/doimage.c @@ -18,6 +18,7 @@ #include <libconfig.h> /* for parsing config file */ /* mbedTLS stuff */ +#include <mbedtls/version.h> #if defined(MBEDTLS_BIGNUM_C) && defined(MBEDTLS_ENTROPY_C) && \ defined(MBEDTLS_SHA256_C) && \ defined(MBEDTLS_PK_PARSE_C) && defined(MBEDTLS_FS_IO) && \ @@ -28,7 +29,6 @@ #include <mbedtls/md.h> #include <mbedtls/pk.h> #include <mbedtls/sha256.h> -#include <mbedtls/version.h> #include <mbedtls/x509.h> #else #error "Bad mbedTLS configuration!" diff --git a/tools/memory/memory/mapparser.py b/tools/memory/memory/mapparser.py index b1a4b4c5..ce4cc311 100644 --- a/tools/memory/memory/mapparser.py +++ b/tools/memory/memory/mapparser.py @@ -1,5 +1,5 @@ # -# Copyright (c) 2023, Arm Limited. All rights reserved. +# Copyright (c) 2023-2024, Arm Limited. All rights reserved. # # SPDX-License-Identifier: BSD-3-Clause # @@ -57,7 +57,7 @@ class TfaMapParser: if "start" and "length" and "end" in memory_layout[region]: memory_layout[region]["limit"] = ( - memory_layout[region]["end"] + memory_layout[region]["start"] + memory_layout[region]["length"] ) memory_layout[region]["free"] = ( diff --git a/tools/memory/memory/memmap.py b/tools/memory/memory/memmap.py index 99149b54..34f5069c 100755 --- a/tools/memory/memory/memmap.py +++ b/tools/memory/memory/memmap.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 # -# Copyright (c) 2023, Arm Limited. All rights reserved. +# Copyright (c) 2023-2024, Arm Limited. All rights reserved. # # SPDX-License-Identifier: BSD-3-Clause # @@ -99,7 +99,7 @@ def main( if symbols: expr = ( - r"(.*)(TEXT|BSS|RODATA|STACKS|_OPS|PMF|XLAT|GOT|FCONF" + r"(.*)(TEXT|BSS|RO|RODATA|STACKS|_OPS|PMF|XLAT|GOT|FCONF|RELA" r"|R.M)(.*)(START|UNALIGNED|END)__$" ) printer.print_symbol_table( diff --git a/tools/nxp/cert_create_helper/src/pdef_tbb_key.c b/tools/nxp/cert_create_helper/src/pdef_tbb_key.c index cf2ebda3..cd48866c 100644 --- a/tools/nxp/cert_create_helper/src/pdef_tbb_key.c +++ b/tools/nxp/cert_create_helper/src/pdef_tbb_key.c @@ -6,7 +6,7 @@ #include <pdef_tbb_key.h> -static key_t pdef_tbb_keys[] = { +static cert_key_t pdef_tbb_keys[] = { [DDR_FW_CONTENT_KEY - DDR_FW_CONTENT_KEY] = { .id = DDR_FW_CONTENT_KEY, .opt = "ddr-fw-key", diff --git a/tools/nxp/create_pbl/Makefile b/tools/nxp/create_pbl/Makefile index f971a746..22aa921d 100644 --- a/tools/nxp/create_pbl/Makefile +++ b/tools/nxp/create_pbl/Makefile @@ -7,12 +7,13 @@ MAKE_HELPERS_DIRECTORY := ../../../make_helpers/ include ${MAKE_HELPERS_DIRECTORY}build_macros.mk include ${MAKE_HELPERS_DIRECTORY}build_env.mk +include ${MAKE_HELPERS_DIRECTORY}common.mk +include ${MAKE_HELPERS_DIRECTORY}toolchain.mk PROJECT_1 := create_pbl${BIN_EXT} OBJECTS_1 := create_pbl.o PROJECT_2 := byte_swap${BIN_EXT} OBJECTS_2 := byte_swap.o -V ?= 0 override CPPFLAGS += -D_GNU_SOURCE -D_XOPEN_SOURCE=700 CFLAGS := -Wall -Werror -pedantic -std=c99 @@ -23,38 +24,29 @@ else endif LDLIBS := -ifeq (${V},0) - Q := @ -else - Q := -endif - INCLUDE_PATHS := -HOSTCC ?= gcc -CC = gcc - .PHONY: all clean distclean all: create_pbl byte_swap ${PROJECT_1}: ${OBJECTS_1} Makefile - @echo " LD $@" - ${Q}${HOSTCC} ${OBJECTS_1} -o $@ ${LDLIBS} - @${ECHO_BLANK_LINE} - @echo "Built $@ successfully" - @${ECHO_BLANK_LINE} + $(s)echo " LD $@" + $(q)$(host-cc) ${OBJECTS_1} -o $@ ${LDLIBS} + $(s)echo + $(s)echo "Built $@ successfully" + $(s)echo ${PROJECT_2}: ${OBJECTS_2} Makefile - @echo " LD $@" - ${Q}${HOSTCC} ${OBJECTS_2} -o $@ ${LDLIBS} - @${ECHO_BLANK_LINE} - @echo "Built $@ successfully" - @${ECHO_BLANK_LINE} + $(s)echo " LD $@" + $(q)$(host-cc) ${OBJECTS_2} -o $@ ${LDLIBS} + $(s)echo + $(s)echo "Built $@ successfully" + $(s)echo %.o: %.c %.h Makefile - @echo " CC $<" - ${Q}${HOSTCC} -c ${CPPFLAGS} ${CFLAGS} ${INCLUDE_PATHS} $< -o $@ + $(s)echo " CC $<" + $(q)$(host-cc) -c ${CPPFLAGS} ${CFLAGS} ${INCLUDE_PATHS} $< -o $@ clean: $(call SHELL_DELETE_ALL, ${PROJECT_1} ${OBJECTS_1}) diff --git a/tools/nxp/create_pbl/pbl_ch2.mk b/tools/nxp/create_pbl/pbl_ch2.mk index e6f1d8b9..bf05a12d 100644 --- a/tools/nxp/create_pbl/pbl_ch2.mk +++ b/tools/nxp/create_pbl/pbl_ch2.mk @@ -15,12 +15,12 @@ pbl: ${BUILD_PLAT}/bl2.bin ifeq ($(SECURE_BOOT),yes) pbl: ${BUILD_PLAT}/bl2.bin ifeq ($(RCW),"") - ${Q}echo "Platform ${PLAT} requires rcw file. Please set RCW to point to the right RCW file for boot mode ${BOOT_MODE}" + $(s)echo "Platform ${PLAT} requires rcw file. Please set RCW to point to the right RCW file for boot mode ${BOOT_MODE}" else # Generate header for bl2.bin - $(Q)$(CST_DIR)/create_hdr_isbc --in ${BUILD_PLAT}/bl2.bin --out ${BUILD_PLAT}/hdr_bl2 ${BL2_INPUT_FILE} + $(q)$(CST_DIR)/create_hdr_isbc --in ${BUILD_PLAT}/bl2.bin --out ${BUILD_PLAT}/hdr_bl2 ${BL2_INPUT_FILE} # Compile create_pbl tool - ${Q}${MAKE} CPPFLAGS="-DVERSION='\"${VERSION_STRING}\"'" --no-print-directory -C ${CREATE_PBL_TOOL_PATH};\ + $(q)${MAKE} CPPFLAGS="-DVERSION='\"${VERSION_STRING}\"'" --no-print-directory -C ${CREATE_PBL_TOOL_PATH};\ # Add bl2.bin to RCW ${CREATE_PBL} -r ${RCW} -i ${BUILD_PLAT}/bl2.bin -b ${BOOT_MODE} -c ${SOC_NUM} -d ${BL2_BASE} -e ${BL2_BASE}\ -o ${BUILD_PLAT}/bl2_${BOOT_MODE}.pbl ;\ @@ -31,7 +31,7 @@ else # Swapping of RCW is required for QSPi Chassis 2 devices ifeq (${BOOT_MODE}, qspi) ifeq ($(SWAP),1) - ${Q}echo "Byteswapping RCW for QSPI" + $(s)echo "Byteswapping RCW for QSPI" ${BYTE_SWAP} ${BUILD_PLAT}/bl2_${BOOT_MODE}_sec.pbl; endif # SWAP endif # BOOT_MODE @@ -39,22 +39,19 @@ endif # BOOT_MODE endif else # NON SECURE_BOOT ifeq ($(RCW),"") - ${Q}echo "Platform ${PLAT} requires rcw file. Please set RCW to point to the right RCW file for boot mode ${BOOT_MODE}" + $(s)echo "Platform ${PLAT} requires rcw file. Please set RCW to point to the right RCW file for boot mode ${BOOT_MODE}" else # -a option appends the image for Chassis 3 devices in case of non secure boot - ${Q}${MAKE} CPPFLAGS="-DVERSION='\"${VERSION_STRING}\"'" --no-print-directory -C ${CREATE_PBL_TOOL_PATH}; + $(q)${MAKE} CPPFLAGS="-DVERSION='\"${VERSION_STRING}\"'" --no-print-directory -C ${CREATE_PBL_TOOL_PATH}; ${CREATE_PBL} -r ${RCW} -i ${BUILD_PLAT}/bl2.bin -b ${BOOT_MODE} -c ${SOC_NUM} -d ${BL2_BASE} -e ${BL2_BASE} \ -o ${BUILD_PLAT}/bl2_${BOOT_MODE}.pbl ; # Swapping of RCW is required for QSPi Chassis 2 devices ifeq (${BOOT_MODE}, qspi) ifeq ($(SWAP),1) - ${Q}echo "Byteswapping RCW for QSPI" + $(s)echo "Byteswapping RCW for QSPI" ${BYTE_SWAP} ${BUILD_PLAT}/bl2_${BOOT_MODE}.pbl; endif # SWAP endif # BOOT_MODE cd ${CREATE_PBL_TOOL_PATH}; ${MAKE} clean ; cd -; endif endif # SECURE_BOOT - - - diff --git a/tools/nxp/create_pbl/pbl_ch3.mk b/tools/nxp/create_pbl/pbl_ch3.mk index 92834741..15129e40 100644 --- a/tools/nxp/create_pbl/pbl_ch3.mk +++ b/tools/nxp/create_pbl/pbl_ch3.mk @@ -21,13 +21,13 @@ pbl: ${BUILD_PLAT}/bl2.bin ifeq ($(SECURE_BOOT),yes) pbl: ${BUILD_PLAT}/bl2.bin ifeq ($(RCW),"") - ${Q}echo "Platform ${PLAT} requires rcw file. Please set RCW to point to the right RCW file for boot mode ${BOOT_MODE}" + $(s)echo "Platform ${PLAT} requires rcw file. Please set RCW to point to the right RCW file for boot mode ${BOOT_MODE}" else # Generate header for bl2.bin - $(Q)$(CST_DIR)/create_hdr_isbc --in ${BUILD_PLAT}/bl2.bin --out ${BUILD_PLAT}/hdr_bl2 ${BL2_INPUT_FILE} + $(q)$(CST_DIR)/create_hdr_isbc --in ${BUILD_PLAT}/bl2.bin --out ${BUILD_PLAT}/hdr_bl2 ${BL2_INPUT_FILE} # Compile create_pbl tool - ${Q}${MAKE} CPPFLAGS="-DVERSION='\"${VERSION_STRING}\"'" --no-print-directory -C ${CREATE_PBL_TOOL_PATH};\ + $(q)${MAKE} CPPFLAGS="-DVERSION='\"${VERSION_STRING}\"'" --no-print-directory -C ${CREATE_PBL_TOOL_PATH};\ # Add Block Copy command for bl2.bin to RCW ${CREATE_PBL} -r ${RCW} -i ${BUILD_PLAT}/bl2.bin -b ${BOOT_MODE} -c ${SOC_NUM} -d ${BL2_BASE} -e ${BL2_BASE}\ @@ -39,14 +39,14 @@ else -o ${BUILD_PLAT}/rcw_sec.pbl # Sign and add "Load Security Header command to PBI commands - $(Q)$(CST_DIR)/create_hdr_pbi --out ${BUILD_PLAT}/bl2_${BOOT_MODE}_sec.pbl --in ${BUILD_PLAT}/rcw_sec.pbl ${PBI_INPUT_FILE} + $(q)$(CST_DIR)/create_hdr_pbi --out ${BUILD_PLAT}/bl2_${BOOT_MODE}_sec.pbl --in ${BUILD_PLAT}/rcw_sec.pbl ${PBI_INPUT_FILE} # Append the bl2_hdr to the RCW image - @echo "${bl2_hdr_loc}" + $(s)echo "${bl2_hdr_loc}" dd if=${BUILD_PLAT}/hdr_bl2 of=${BUILD_PLAT}/bl2_${BOOT_MODE}_sec.pbl bs=1K seek=${bl2_hdr_loc} # Append the bl2.bin to the RCW image - @echo "${bl2_loc}" + $(s)echo "${bl2_loc}" dd if=${BUILD_PLAT}/bl2.bin of=${BUILD_PLAT}/bl2_${BOOT_MODE}_sec.pbl bs=1K seek=${bl2_loc} rm ${BUILD_PLAT}/bl2_${BOOT_MODE}.pbl @@ -54,16 +54,16 @@ else endif else #SECURE_BOOT ifeq ($(RCW),"") - ${Q}echo "Platform ${PLAT} requires rcw file. Please set RCW to point to the right RCW file for boot mode ${BOOT_MODE}" + $(s)echo "Platform ${PLAT} requires rcw file. Please set RCW to point to the right RCW file for boot mode ${BOOT_MODE}" else - ${Q}${MAKE} CPPFLAGS="-DVERSION='\"${VERSION_STRING}\"'" --no-print-directory -C ${CREATE_PBL_TOOL_PATH}; + $(q)${MAKE} CPPFLAGS="-DVERSION='\"${VERSION_STRING}\"'" --no-print-directory -C ${CREATE_PBL_TOOL_PATH}; # Add Block Copy command and populate boot loc ptrfor bl2.bin to RCW ${CREATE_PBL} -r ${RCW} -i ${BUILD_PLAT}/bl2.bin -b ${BOOT_MODE} -c ${SOC_NUM} -d ${BL2_BASE} -e ${BL2_BASE} \ -o ${BUILD_PLAT}/bl2_${BOOT_MODE}.pbl -f ${BL2_SRC_OFFSET}; # Append the bl2.bin to the RCW image - @echo "bl2_loc is ${bl2_loc} KB" + $(s)echo "bl2_loc is ${bl2_loc} KB" dd if=${BUILD_PLAT}/bl2.bin of=${BUILD_PLAT}/bl2_${BOOT_MODE}.pbl bs=1K seek=${bl2_loc} cd ${CREATE_PBL_TOOL_PATH}; ${MAKE} clean ; cd -; diff --git a/tools/renesas/rcar_layout_create/makefile b/tools/renesas/rcar_layout_create/makefile index d5857549..7a64b190 100644 --- a/tools/renesas/rcar_layout_create/makefile +++ b/tools/renesas/rcar_layout_create/makefile @@ -1,9 +1,16 @@ # +# Copyright (c) 2024, Arm Limited and Contributors. All rights reserved. # Copyright (c) 2015-2018, Renesas Electronics Corporation. All rights reserved. # # SPDX-License-Identifier: BSD-3-Clause # +toolchains := aarch64 + +include ../../../make_helpers/build-rules.mk +include ../../../make_helpers/common.mk +include ../../../make_helpers/toolchain.mk + ################################################### # makefile ################################################### @@ -67,16 +74,9 @@ $(eval $(call add_define,RCAR_VMA_ADJUST_ADDR)) ################################################### #c compiler -CC = $(CROSS_COMPILE)gcc CFLAGS += ${DEFINES} CFLAGS += -I../../include/lib/stdlib -#Linker -LD = $(CROSS_COMPILE)ld - -#objcopy -objcopy = $(CROSS_COMPILE)objcopy - #clean CL = rm -f @@ -87,34 +87,38 @@ CL = rm -f # command .PHONY: all -all: $(OUTPUT_FILE_SA0) $(OUTPUT_FILE_SA6) + +all: $(FILE_NAME_SA0).srec $(FILE_NAME_SA0).bin +all: $(FILE_NAME_SA6).srec $(FILE_NAME_SA6).bin + ################################################### # Linker ################################################### -$(OUTPUT_FILE_SA0) : $(MEMORY_DEF_SA0) $(OBJ_FILE_SA0) - $(LD) $(OBJ_FILE_SA0) \ - -T $(MEMORY_DEF_SA0) \ - -o $(OUTPUT_FILE_SA0) \ - -Map $(FILE_NAME_SA0).map \ - $(objcopy) -O srec --adjust-vma=$(RCAR_VMA_ADJUST_ADDR) --srec-forceS3 $(OUTPUT_FILE_SA0) $(FILE_NAME_SA0).srec - $(objcopy) -O binary --adjust-vma=$(RCAR_VMA_ADJUST_ADDR) --srec-forceS3 $(OUTPUT_FILE_SA0) $(FILE_NAME_SA0).bin +$(FILE_NAME_SA0).srec: $(OUTPUT_FILE_SA0) | $$(@D)/ + $(aarch64-oc) -O srec --adjust-vma=$(RCAR_VMA_ADJUST_ADDR) --srec-forceS3 $(OUTPUT_FILE_SA0) $(FILE_NAME_SA0).srec + +$(FILE_NAME_SA0).bin: $(OUTPUT_FILE_SA0) | $$(@D)/ + $(aarch64-oc) -O binary --adjust-vma=$(RCAR_VMA_ADJUST_ADDR) --srec-forceS3 $(OUTPUT_FILE_SA0) $(FILE_NAME_SA0).bin + +$(OUTPUT_FILE_SA0): $(MEMORY_DEF_SA0) $(OBJ_FILE_SA0) | $$(@D)/ + $(aarch64-ld) $(OBJ_FILE_SA0) -nostdlib -T $(MEMORY_DEF_SA0) -o $(OUTPUT_FILE_SA0) -Wl,-Map $(FILE_NAME_SA0).map + +$(FILE_NAME_SA6).srec: $(OUTPUT_FILE_SA6) | $$(@D)/ + $(aarch64-oc) -O srec --adjust-vma=$(RCAR_VMA_ADJUST_ADDR) --srec-forceS3 $(OUTPUT_FILE_SA6) $(FILE_NAME_SA6).srec -$(OUTPUT_FILE_SA6) : $(MEMORY_DEF_SA6) $(OBJ_FILE_SA6) - $(LD) $(OBJ_FILE_SA6) \ - -T $(MEMORY_DEF_SA6) \ - -o $(OUTPUT_FILE_SA6) \ - -Map $(FILE_NAME_SA6).map \ +$(FILE_NAME_SA6).bin: $(OUTPUT_FILE_SA6) | $$(@D)/ + $(aarch64-oc) -O binary --adjust-vma=$(RCAR_VMA_ADJUST_ADDR) --srec-forceS3 $(OUTPUT_FILE_SA6) $(FILE_NAME_SA6).bin - $(objcopy) -O srec --adjust-vma=$(RCAR_VMA_ADJUST_ADDR) --srec-forceS3 $(OUTPUT_FILE_SA6) $(FILE_NAME_SA6).srec - $(objcopy) -O binary --adjust-vma=$(RCAR_VMA_ADJUST_ADDR) --srec-forceS3 $(OUTPUT_FILE_SA6) $(FILE_NAME_SA6).bin +$(OUTPUT_FILE_SA6): $(MEMORY_DEF_SA6) $(OBJ_FILE_SA6) | $$(@D)/ + $(aarch64-ld) $(OBJ_FILE_SA6) -nostdlib -T $(MEMORY_DEF_SA6) -o $(OUTPUT_FILE_SA6) -Wl,-Map $(FILE_NAME_SA6).map ################################################### # Compile ################################################### -%.o:../%.c - $(CC) -c -I $< -o $@ +%.o: %.c | $$(@D)/ + $(aarch64-cc) $(CPPFLAGS) $(CFLAGS) -c -o $@ $< .PHONY: clean clean: diff --git a/tools/renesas/rcar_layout_create/sa6.c b/tools/renesas/rcar_layout_create/sa6.c index 8fafdade..58881f97 100644 --- a/tools/renesas/rcar_layout_create/sa6.c +++ b/tools/renesas/rcar_layout_create/sa6.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015-2021, Renesas Electronics Corporation. All rights reserved. + * Copyright (c) 2015-2023, Renesas Electronics Corporation. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -91,7 +91,7 @@ #define RCAR_BL31DST_ADDRESS (0x44000000U) #define RCAR_BL31DST_ADDRESSH (0x00000000U) /* Destination size for BL31 */ -#define RCAR_BL31DST_SIZE (0x00004000U) +#define RCAR_BL31DST_SIZE (0x0000F800U) /* Destination address for BL32 */ #define RCAR_BL32DST_ADDRESS (0x44100000U) #define RCAR_BL32DST_ADDRESSH (0x00000000U) diff --git a/tools/renesas/rzg_layout_create/makefile b/tools/renesas/rzg_layout_create/makefile index 2d438b92..936420db 100644 --- a/tools/renesas/rzg_layout_create/makefile +++ b/tools/renesas/rzg_layout_create/makefile @@ -1,9 +1,16 @@ # +# Copyright (c) 2024, Arm Limited and Contributors. All rights reserved. # Copyright (c) 2020, Renesas Electronics Corporation. All rights reserved. # # SPDX-License-Identifier: BSD-3-Clause # +toolchains := aarch64 + +include ../../../make_helpers/build-rules.mk +include ../../../make_helpers/common.mk +include ../../../make_helpers/toolchain.mk + ################################################### # makefile ################################################### @@ -62,18 +69,11 @@ $(eval $(call add_define,RCAR_VMA_ADJUST_ADDR)) ################################################### #c compiler -CC = $(CROSS_COMPILE)gcc CFLAGS += ${DEFINES} CFLAGS += -nostdinc \ -I../../../include/lib/libc \ -I../../../include/lib/libc/aarch64 -#Linker -LD = $(CROSS_COMPILE)ld - -#objcopy -objcopy = $(CROSS_COMPILE)objcopy - #clean CL = rm -f @@ -84,34 +84,38 @@ CL = rm -f # command .PHONY: all -all: $(OUTPUT_FILE_SA0) $(OUTPUT_FILE_SA6) + +all: $(FILE_NAME_SA0).srec $(FILE_NAME_SA0).bin +all: $(FILE_NAME_SA6).srec $(FILE_NAME_SA6).bin + ################################################### # Linker ################################################### -$(OUTPUT_FILE_SA0) : $(MEMORY_DEF_SA0) $(OBJ_FILE_SA0) - $(LD) $(OBJ_FILE_SA0) \ - -T $(MEMORY_DEF_SA0) \ - -o $(OUTPUT_FILE_SA0) \ - -Map $(FILE_NAME_SA0).map \ - $(objcopy) -O srec --adjust-vma=$(RCAR_VMA_ADJUST_ADDR) --srec-forceS3 $(OUTPUT_FILE_SA0) $(FILE_NAME_SA0).srec - $(objcopy) -O binary --adjust-vma=$(RCAR_VMA_ADJUST_ADDR) --srec-forceS3 $(OUTPUT_FILE_SA0) $(FILE_NAME_SA0).bin +$(FILE_NAME_SA0).srec: $(OUTPUT_FILE_SA0) | $$(@D)/ + $(aarch64-oc) -O srec --adjust-vma=$(RCAR_VMA_ADJUST_ADDR) --srec-forceS3 $(OUTPUT_FILE_SA0) $(FILE_NAME_SA0).srec + +$(FILE_NAME_SA0).bin: $(OUTPUT_FILE_SA0) | $$(@D)/ + $(aarch64-oc) -O binary --adjust-vma=$(RCAR_VMA_ADJUST_ADDR) --srec-forceS3 $(OUTPUT_FILE_SA0) $(FILE_NAME_SA0).bin + +$(OUTPUT_FILE_SA0): $(MEMORY_DEF_SA0) $(OBJ_FILE_SA0) | $$(@D)/ + $(aarch64-ld) $(OBJ_FILE_SA0) -nostdlib -T $(MEMORY_DEF_SA0) -o $(OUTPUT_FILE_SA0) -Wl,-Map $(FILE_NAME_SA0).map + +$(FILE_NAME_SA6).srec: $(OUTPUT_FILE_SA6) | $$(@D)/ + $(aarch64-oc) -O srec --adjust-vma=$(RCAR_VMA_ADJUST_ADDR) --srec-forceS3 $(OUTPUT_FILE_SA6) $(FILE_NAME_SA6).srec -$(OUTPUT_FILE_SA6) : $(MEMORY_DEF_SA6) $(OBJ_FILE_SA6) - $(LD) $(OBJ_FILE_SA6) \ - -T $(MEMORY_DEF_SA6) \ - -o $(OUTPUT_FILE_SA6) \ - -Map $(FILE_NAME_SA6).map \ +$(FILE_NAME_SA6).bin: $(OUTPUT_FILE_SA6) | $$(@D)/ + $(aarch64-oc) -O binary --adjust-vma=$(RCAR_VMA_ADJUST_ADDR) --srec-forceS3 $(OUTPUT_FILE_SA6) $(FILE_NAME_SA6).bin - $(objcopy) -O srec --adjust-vma=$(RCAR_VMA_ADJUST_ADDR) --srec-forceS3 $(OUTPUT_FILE_SA6) $(FILE_NAME_SA6).srec - $(objcopy) -O binary --adjust-vma=$(RCAR_VMA_ADJUST_ADDR) --srec-forceS3 $(OUTPUT_FILE_SA6) $(FILE_NAME_SA6).bin +$(OUTPUT_FILE_SA6): $(MEMORY_DEF_SA6) $(OBJ_FILE_SA6) | $$(@D)/ + $(aarch64-ld) $(OBJ_FILE_SA6) -nostdlib -T $(MEMORY_DEF_SA6) -o $(OUTPUT_FILE_SA6) -Wl,-Map $(FILE_NAME_SA6).map ################################################### # Compile ################################################### -%.o:../%.c - $(CC) -c -I $< -o $@ +%.o: %.c | $$(@D)/ + $(aarch64-cc) $(CPPFLAGS) $(CFLAGS) -c -o $@ $< .PHONY: clean clean: diff --git a/tools/sptool/Makefile b/tools/sptool/Makefile index 1fa85fb2..0da5c09c 100644 --- a/tools/sptool/Makefile +++ b/tools/sptool/Makefile @@ -1,5 +1,5 @@ # -# Copyright (c) 2018-2020, Arm Limited. All rights reserved. +# Copyright (c) 2018-2024, Arm Limited. All rights reserved. # # SPDX-License-Identifier: BSD-3-Clause # @@ -7,11 +7,12 @@ MAKE_HELPERS_DIRECTORY := ../../make_helpers/ include ${MAKE_HELPERS_DIRECTORY}build_macros.mk include ${MAKE_HELPERS_DIRECTORY}build_env.mk +include ${MAKE_HELPERS_DIRECTORY}common.mk +include ${MAKE_HELPERS_DIRECTORY}toolchain.mk SPTOOL ?= sptool${BIN_EXT} PROJECT := $(notdir ${SPTOOL}) OBJECTS := sptool.o -V ?= 0 override CPPFLAGS += -D_GNU_SOURCE -D_XOPEN_SOURCE=700 HOSTCCFLAGS := -Wall -Werror -pedantic -std=c99 @@ -21,30 +22,22 @@ else HOSTCCFLAGS += -O2 endif -ifeq (${V},0) - Q := @ -else - Q := -endif - INCLUDE_PATHS := -I../../include/tools_share -HOSTCC ?= gcc - .PHONY: all clean distclean all: ${PROJECT} ${PROJECT}: ${OBJECTS} Makefile - @echo " HOSTLD $@" - ${Q}${HOSTCC} ${OBJECTS} -o $@ ${LDLIBS} - @${ECHO_BLANK_LINE} - @echo "Built $@ successfully" - @${ECHO_BLANK_LINE} + $(s)echo " HOSTLD $@" + $(q)$(host-cc) ${OBJECTS} -o $@ ${LDLIBS} + $(s)echo + $(s)echo "Built $@ successfully" + $(s)echo %.o: %.c Makefile - @echo " HOSTCC $<" - ${Q}${HOSTCC} -c ${CPPFLAGS} ${HOSTCCFLAGS} ${INCLUDE_PATHS} $< -o $@ + $(s)echo " HOSTCC $<" + $(q)$(host-cc) -c ${CPPFLAGS} ${HOSTCCFLAGS} ${INCLUDE_PATHS} $< -o $@ clean: $(call SHELL_DELETE_ALL, ${PROJECT} ${OBJECTS}) diff --git a/tools/sptool/sp_mk_generator.py b/tools/sptool/sp_mk_generator.py index c69e0a73..1edb77d0 100644 --- a/tools/sptool/sp_mk_generator.py +++ b/tools/sptool/sp_mk_generator.py @@ -1,5 +1,5 @@ #!/usr/bin/python3 -# Copyright (c) 2020-2023, Arm Limited. All rights reserved. +# Copyright (c) 2020-2024, Arm Limited. All rights reserved. # # SPDX-License-Identifier: BSD-3-Clause @@ -136,7 +136,10 @@ def get_load_address(sp_layout, sp, args :dict): ''' Helper to fetch load-address from pm file listed in sp_layout.json''' with open(get_sp_manifest_full_path(sp_layout[sp], args), "r") as pm_f: load_address_lines = [l for l in pm_f if 'load-address' in l] - assert(len(load_address_lines) == 1) + + if len(load_address_lines) != 1: + return None + load_address_parsed = re.search("(0x[0-9a-f]+)", load_address_lines[0]) return load_address_parsed.group(0) @@ -240,7 +243,8 @@ def gen_fconf_fragment(sp_layout, sp, args: dict): else: load_address = get_load_address(sp_layout, sp, args) - f.write( + if load_address is not None: + f.write( f'''\ {sp} {{ uuid = "{uuid}"; @@ -249,6 +253,9 @@ f'''\ }}; ''') + else: + print("Warning: No load-address was found in the SP manifest.") + return args def init_sp_actions(sys): diff --git a/tools/stm32image/Makefile b/tools/stm32image/Makefile index 9c9b7b5f..453daaeb 100644 --- a/tools/stm32image/Makefile +++ b/tools/stm32image/Makefile @@ -1,5 +1,5 @@ # -# Copyright (c) 2017-2018, ARM Limited and Contributors. All rights reserved. +# Copyright (c) 2017-2024, Arm Limited and Contributors. All rights reserved. # # SPDX-License-Identifier: BSD-3-Clause # @@ -7,10 +7,11 @@ MAKE_HELPERS_DIRECTORY := ../../make_helpers/ include ${MAKE_HELPERS_DIRECTORY}build_macros.mk include ${MAKE_HELPERS_DIRECTORY}build_env.mk +include ${MAKE_HELPERS_DIRECTORY}common.mk +include ${MAKE_HELPERS_DIRECTORY}toolchain.mk PROJECT := stm32image${BIN_EXT} OBJECTS := stm32image.o -V := 0 HOSTCCFLAGS := -Wall -Werror -pedantic -std=c99 -D_GNU_SOURCE @@ -20,28 +21,20 @@ else HOSTCCFLAGS += -O2 endif -ifeq (${V},0) - Q := @ -else - Q := -endif - -HOSTCC := gcc - .PHONY: all clean distclean all: ${PROJECT} ${PROJECT}: ${OBJECTS} Makefile - @echo " HOSTLD $@" - ${Q}${HOSTCC} ${OBJECTS} -o $@ - @${ECHO_BLANK_LINE} - @echo "Built $@ successfully" - @${ECHO_BLANK_LINE} + $(s)echo " HOSTLD $@" + $(q)$(host-cc) ${OBJECTS} -o $@ + $(s)echo + $(s)echo "Built $@ successfully" + $(s)echo %.o: %.c Makefile - @echo " HOSTCC $<" - ${Q}${HOSTCC} -c ${HOSTCCFLAGS} $< -o $@ + $(s)echo " HOSTCC $<" + $(q)$(host-cc) -c ${HOSTCCFLAGS} $< -o $@ clean: $(call SHELL_DELETE_ALL, ${PROJECT} ${OBJECTS}) diff --git a/tools/tlc/.gitignore b/tools/tlc/.gitignore new file mode 100644 index 00000000..ad4a1f17 --- /dev/null +++ b/tools/tlc/.gitignore @@ -0,0 +1,176 @@ +# Created by https://www.toptal.com/developers/gitignore/api/python +# Edit at https://www.toptal.com/developers/gitignore?templates=python + +### Python ### +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.py,cover +.hypothesis/ +.pytest_cache/ +cover/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 +db.sqlite3-journal + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +.pybuilder/ +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +# For a library or package, you might want to ignore these files since the code is +# intended to run in multiple environments; otherwise, check them in: +# .python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. +#Pipfile.lock + +# poetry +# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. +# This is especially recommended for binary packages to ensure reproducibility, and is more +# commonly ignored for libraries. +# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control +#poetry.lock + +# pdm +# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. +#pdm.lock +# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it +# in version control. +# https://pdm.fming.dev/#use-with-ide +.pdm.toml + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm +__pypackages__/ + +# Celery stuff +celerybeat-schedule +celerybeat.pid + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ + +# pytype static type analyzer +.pytype/ + +# Cython debug symbols +cython_debug/ + +# PyCharm +# JetBrains specific template is maintained in a separate JetBrains.gitignore that can +# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore +# and can be added to the global gitignore or merged into this file. For a more nuclear +# option (not recommended) you can uncomment the following to ignore the entire idea folder. +#.idea/ + +### Python Patch ### +# Poetry local configuration file - https://python-poetry.org/docs/configuration/#local-configuration +poetry.toml + +# ruff +.ruff_cache/ + +# LSP config files +pyrightconfig.json + +# End of https://www.toptal.com/developers/gitignore/api/python diff --git a/tools/tlc/assets/images/coverage.svg b/tools/tlc/assets/images/coverage.svg new file mode 100644 index 00000000..b6c4e361 --- /dev/null +++ b/tools/tlc/assets/images/coverage.svg @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="UTF-8"?> +<svg xmlns="http://www.w3.org/2000/svg" width="99" height="20"> + <linearGradient id="b" x2="0" y2="100%"> + <stop offset="0" stop-color="#bbb" stop-opacity=".1"/> + <stop offset="1" stop-opacity=".1"/> + </linearGradient> + <mask id="a"> + <rect width="99" height="20" rx="3" fill="#fff"/> + </mask> + <g mask="url(#a)"> + <path fill="#555" d="M0 0h63v20H0z"/> + <path fill="#4c1" d="M63 0h36v20H63z"/> + <path fill="url(#b)" d="M0 0h99v20H0z"/> + </g> + <g fill="#fff" text-anchor="middle" font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="11"> + <text x="31.5" y="15" fill="#010101" fill-opacity=".3">coverage</text> + <text x="31.5" y="14">coverage</text> + <text x="80" y="15" fill="#010101" fill-opacity=".3">95%</text> + <text x="80" y="14">95%</text> + </g> +</svg> diff --git a/tools/tlc/poetry.lock b/tools/tlc/poetry.lock new file mode 100644 index 00000000..decec598 --- /dev/null +++ b/tools/tlc/poetry.lock @@ -0,0 +1,1434 @@ +# This file is automatically @generated by Poetry 1.8.2 and should not be changed by hand. + +[[package]] +name = "astroid" +version = "2.15.8" +description = "An abstract syntax tree for Python with inference support." +optional = false +python-versions = ">=3.7.2" +files = [ + {file = "astroid-2.15.8-py3-none-any.whl", hash = "sha256:1aa149fc5c6589e3d0ece885b4491acd80af4f087baafa3fb5203b113e68cd3c"}, + {file = "astroid-2.15.8.tar.gz", hash = "sha256:6c107453dffee9055899705de3c9ead36e74119cee151e5a9aaf7f0b0e020a6a"}, +] + +[package.dependencies] +lazy-object-proxy = ">=1.4.0" +typing-extensions = {version = ">=4.0.0", markers = "python_version < \"3.11\""} +wrapt = [ + {version = ">=1.11,<2", markers = "python_version < \"3.11\""}, + {version = ">=1.14,<2", markers = "python_version >= \"3.11\""}, +] + +[[package]] +name = "bandit" +version = "1.7.9" +description = "Security oriented static analyser for python code." +optional = false +python-versions = ">=3.8" +files = [ + {file = "bandit-1.7.9-py3-none-any.whl", hash = "sha256:52077cb339000f337fb25f7e045995c4ad01511e716e5daac37014b9752de8ec"}, + {file = "bandit-1.7.9.tar.gz", hash = "sha256:7c395a436743018f7be0a4cbb0a4ea9b902b6d87264ddecf8cfdc73b4f78ff61"}, +] + +[package.dependencies] +colorama = {version = ">=0.3.9", markers = "platform_system == \"Windows\""} +PyYAML = ">=5.3.1" +rich = "*" +stevedore = ">=1.20.0" + +[package.extras] +baseline = ["GitPython (>=3.1.30)"] +sarif = ["jschema-to-python (>=1.2.3)", "sarif-om (>=1.0.4)"] +test = ["beautifulsoup4 (>=4.8.0)", "coverage (>=4.5.4)", "fixtures (>=3.0.0)", "flake8 (>=4.0.0)", "pylint (==1.9.4)", "stestr (>=2.5.0)", "testscenarios (>=0.5.0)", "testtools (>=2.3.0)"] +toml = ["tomli (>=1.1.0)"] +yaml = ["PyYAML"] + +[[package]] +name = "black" +version = "24.8.0" +description = "The uncompromising code formatter." +optional = false +python-versions = ">=3.8" +files = [ + {file = "black-24.8.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:09cdeb74d494ec023ded657f7092ba518e8cf78fa8386155e4a03fdcc44679e6"}, + {file = "black-24.8.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:81c6742da39f33b08e791da38410f32e27d632260e599df7245cccee2064afeb"}, + {file = "black-24.8.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:707a1ca89221bc8a1a64fb5e15ef39cd755633daa672a9db7498d1c19de66a42"}, + {file = "black-24.8.0-cp310-cp310-win_amd64.whl", hash = "sha256:d6417535d99c37cee4091a2f24eb2b6d5ec42b144d50f1f2e436d9fe1916fe1a"}, + {file = "black-24.8.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:fb6e2c0b86bbd43dee042e48059c9ad7830abd5c94b0bc518c0eeec57c3eddc1"}, + {file = "black-24.8.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:837fd281f1908d0076844bc2b801ad2d369c78c45cf800cad7b61686051041af"}, + {file = "black-24.8.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:62e8730977f0b77998029da7971fa896ceefa2c4c4933fcd593fa599ecbf97a4"}, + {file = "black-24.8.0-cp311-cp311-win_amd64.whl", hash = "sha256:72901b4913cbac8972ad911dc4098d5753704d1f3c56e44ae8dce99eecb0e3af"}, + {file = "black-24.8.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:7c046c1d1eeb7aea9335da62472481d3bbf3fd986e093cffd35f4385c94ae368"}, + {file = "black-24.8.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:649f6d84ccbae73ab767e206772cc2d7a393a001070a4c814a546afd0d423aed"}, + {file = "black-24.8.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2b59b250fdba5f9a9cd9d0ece6e6d993d91ce877d121d161e4698af3eb9c1018"}, + {file = "black-24.8.0-cp312-cp312-win_amd64.whl", hash = "sha256:6e55d30d44bed36593c3163b9bc63bf58b3b30e4611e4d88a0c3c239930ed5b2"}, + {file = "black-24.8.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:505289f17ceda596658ae81b61ebbe2d9b25aa78067035184ed0a9d855d18afd"}, + {file = "black-24.8.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:b19c9ad992c7883ad84c9b22aaa73562a16b819c1d8db7a1a1a49fb7ec13c7d2"}, + {file = "black-24.8.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1f13f7f386f86f8121d76599114bb8c17b69d962137fc70efe56137727c7047e"}, + {file = "black-24.8.0-cp38-cp38-win_amd64.whl", hash = "sha256:f490dbd59680d809ca31efdae20e634f3fae27fba3ce0ba3208333b713bc3920"}, + {file = "black-24.8.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:eab4dd44ce80dea27dc69db40dab62d4ca96112f87996bca68cd75639aeb2e4c"}, + {file = "black-24.8.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:3c4285573d4897a7610054af5a890bde7c65cb466040c5f0c8b732812d7f0e5e"}, + {file = "black-24.8.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9e84e33b37be070ba135176c123ae52a51f82306def9f7d063ee302ecab2cf47"}, + {file = "black-24.8.0-cp39-cp39-win_amd64.whl", hash = "sha256:73bbf84ed136e45d451a260c6b73ed674652f90a2b3211d6a35e78054563a9bb"}, + {file = "black-24.8.0-py3-none-any.whl", hash = "sha256:972085c618ee94f402da1af548a4f218c754ea7e5dc70acb168bfaca4c2542ed"}, + {file = "black-24.8.0.tar.gz", hash = "sha256:2500945420b6784c38b9ee885af039f5e7471ef284ab03fa35ecdde4688cd83f"}, +] + +[package.dependencies] +click = ">=8.0.0" +mypy-extensions = ">=0.4.3" +packaging = ">=22.0" +pathspec = ">=0.9.0" +platformdirs = ">=2" +tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} +typing-extensions = {version = ">=4.0.1", markers = "python_version < \"3.11\""} + +[package.extras] +colorama = ["colorama (>=0.4.3)"] +d = ["aiohttp (>=3.7.4)", "aiohttp (>=3.7.4,!=3.9.0)"] +jupyter = ["ipython (>=7.8.0)", "tokenize-rt (>=3.2.0)"] +uvloop = ["uvloop (>=0.15.2)"] + +[[package]] +name = "cachetools" +version = "5.5.0" +description = "Extensible memoizing collections and decorators" +optional = false +python-versions = ">=3.7" +files = [ + {file = "cachetools-5.5.0-py3-none-any.whl", hash = "sha256:02134e8439cdc2ffb62023ce1debca2944c3f289d66bb17ead3ab3dede74b292"}, + {file = "cachetools-5.5.0.tar.gz", hash = "sha256:2cc24fb4cbe39633fb7badd9db9ca6295d766d9c2995f245725a46715d050f2a"}, +] + +[[package]] +name = "certifi" +version = "2024.8.30" +description = "Python package for providing Mozilla's CA Bundle." +optional = false +python-versions = ">=3.6" +files = [ + {file = "certifi-2024.8.30-py3-none-any.whl", hash = "sha256:922820b53db7a7257ffbda3f597266d435245903d80737e34f8a45ff3e3230d8"}, + {file = "certifi-2024.8.30.tar.gz", hash = "sha256:bec941d2aa8195e248a60b31ff9f0558284cf01a52591ceda73ea9afffd69fd9"}, +] + +[[package]] +name = "cfgv" +version = "3.4.0" +description = "Validate configuration and produce human readable error messages." +optional = false +python-versions = ">=3.8" +files = [ + {file = "cfgv-3.4.0-py2.py3-none-any.whl", hash = "sha256:b7265b1f29fd3316bfcd2b330d63d024f2bfd8bcb8b0272f8e19a504856c48f9"}, + {file = "cfgv-3.4.0.tar.gz", hash = "sha256:e52591d4c5f5dead8e0f673fb16db7949d2cfb3f7da4582893288f0ded8fe560"}, +] + +[[package]] +name = "chardet" +version = "5.2.0" +description = "Universal encoding detector for Python 3" +optional = false +python-versions = ">=3.7" +files = [ + {file = "chardet-5.2.0-py3-none-any.whl", hash = "sha256:e1cf59446890a00105fe7b7912492ea04b6e6f06d4b742b2c788469e34c82970"}, + {file = "chardet-5.2.0.tar.gz", hash = "sha256:1b3b6ff479a8c414bc3fa2c0852995695c4a026dcd6d0633b2dd092ca39c1cf7"}, +] + +[[package]] +name = "charset-normalizer" +version = "3.3.2" +description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." +optional = false +python-versions = ">=3.7.0" +files = [ + {file = "charset-normalizer-3.3.2.tar.gz", hash = "sha256:f30c3cb33b24454a82faecaf01b19c18562b1e89558fb6c56de4d9118a032fd5"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:25baf083bf6f6b341f4121c2f3c548875ee6f5339300e08be3f2b2ba1721cdd3"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:06435b539f889b1f6f4ac1758871aae42dc3a8c0e24ac9e60c2384973ad73027"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9063e24fdb1e498ab71cb7419e24622516c4a04476b17a2dab57e8baa30d6e03"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6897af51655e3691ff853668779c7bad41579facacf5fd7253b0133308cf000d"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1d3193f4a680c64b4b6a9115943538edb896edc190f0b222e73761716519268e"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cd70574b12bb8a4d2aaa0094515df2463cb429d8536cfb6c7ce983246983e5a6"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8465322196c8b4d7ab6d1e049e4c5cb460d0394da4a27d23cc242fbf0034b6b5"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a9a8e9031d613fd2009c182b69c7b2c1ef8239a0efb1df3f7c8da66d5dd3d537"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:beb58fe5cdb101e3a055192ac291b7a21e3b7ef4f67fa1d74e331a7f2124341c"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:e06ed3eb3218bc64786f7db41917d4e686cc4856944f53d5bdf83a6884432e12"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:2e81c7b9c8979ce92ed306c249d46894776a909505d8f5a4ba55b14206e3222f"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:572c3763a264ba47b3cf708a44ce965d98555f618ca42c926a9c1616d8f34269"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:fd1abc0d89e30cc4e02e4064dc67fcc51bd941eb395c502aac3ec19fab46b519"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-win32.whl", hash = "sha256:3d47fa203a7bd9c5b6cee4736ee84ca03b8ef23193c0d1ca99b5089f72645c73"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-win_amd64.whl", hash = "sha256:10955842570876604d404661fbccbc9c7e684caf432c09c715ec38fbae45ae09"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:802fe99cca7457642125a8a88a084cef28ff0cf9407060f7b93dca5aa25480db"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:573f6eac48f4769d667c4442081b1794f52919e7edada77495aaed9236d13a96"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:549a3a73da901d5bc3ce8d24e0600d1fa85524c10287f6004fbab87672bf3e1e"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f27273b60488abe721a075bcca6d7f3964f9f6f067c8c4c605743023d7d3944f"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1ceae2f17a9c33cb48e3263960dc5fc8005351ee19db217e9b1bb15d28c02574"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:65f6f63034100ead094b8744b3b97965785388f308a64cf8d7c34f2f2e5be0c4"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:753f10e867343b4511128c6ed8c82f7bec3bd026875576dfd88483c5c73b2fd8"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4a78b2b446bd7c934f5dcedc588903fb2f5eec172f3d29e52a9096a43722adfc"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e537484df0d8f426ce2afb2d0f8e1c3d0b114b83f8850e5f2fbea0e797bd82ae"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:eb6904c354526e758fda7167b33005998fb68c46fbc10e013ca97f21ca5c8887"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:deb6be0ac38ece9ba87dea880e438f25ca3eddfac8b002a2ec3d9183a454e8ae"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:4ab2fe47fae9e0f9dee8c04187ce5d09f48eabe611be8259444906793ab7cbce"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:80402cd6ee291dcb72644d6eac93785fe2c8b9cb30893c1af5b8fdd753b9d40f"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-win32.whl", hash = "sha256:7cd13a2e3ddeed6913a65e66e94b51d80a041145a026c27e6bb76c31a853c6ab"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-win_amd64.whl", hash = "sha256:663946639d296df6a2bb2aa51b60a2454ca1cb29835324c640dafb5ff2131a77"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:0b2b64d2bb6d3fb9112bafa732def486049e63de9618b5843bcdd081d8144cd8"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:ddbb2551d7e0102e7252db79ba445cdab71b26640817ab1e3e3648dad515003b"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:55086ee1064215781fff39a1af09518bc9255b50d6333f2e4c74ca09fac6a8f6"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8f4a014bc36d3c57402e2977dada34f9c12300af536839dc38c0beab8878f38a"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a10af20b82360ab00827f916a6058451b723b4e65030c5a18577c8b2de5b3389"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8d756e44e94489e49571086ef83b2bb8ce311e730092d2c34ca8f7d925cb20aa"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:90d558489962fd4918143277a773316e56c72da56ec7aa3dc3dbbe20fdfed15b"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6ac7ffc7ad6d040517be39eb591cac5ff87416c2537df6ba3cba3bae290c0fed"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:7ed9e526742851e8d5cc9e6cf41427dfc6068d4f5a3bb03659444b4cabf6bc26"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:8bdb58ff7ba23002a4c5808d608e4e6c687175724f54a5dade5fa8c67b604e4d"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:6b3251890fff30ee142c44144871185dbe13b11bab478a88887a639655be1068"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:b4a23f61ce87adf89be746c8a8974fe1c823c891d8f86eb218bb957c924bb143"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:efcb3f6676480691518c177e3b465bcddf57cea040302f9f4e6e191af91174d4"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-win32.whl", hash = "sha256:d965bba47ddeec8cd560687584e88cf699fd28f192ceb452d1d7ee807c5597b7"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-win_amd64.whl", hash = "sha256:96b02a3dc4381e5494fad39be677abcb5e6634bf7b4fa83a6dd3112607547001"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:95f2a5796329323b8f0512e09dbb7a1860c46a39da62ecb2324f116fa8fdc85c"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c002b4ffc0be611f0d9da932eb0f704fe2602a9a949d1f738e4c34c75b0863d5"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a981a536974bbc7a512cf44ed14938cf01030a99e9b3a06dd59578882f06f985"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3287761bc4ee9e33561a7e058c72ac0938c4f57fe49a09eae428fd88aafe7bb6"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:42cb296636fcc8b0644486d15c12376cb9fa75443e00fb25de0b8602e64c1714"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0a55554a2fa0d408816b3b5cedf0045f4b8e1a6065aec45849de2d6f3f8e9786"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:c083af607d2515612056a31f0a8d9e0fcb5876b7bfc0abad3ecd275bc4ebc2d5"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:87d1351268731db79e0f8e745d92493ee2841c974128ef629dc518b937d9194c"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:bd8f7df7d12c2db9fab40bdd87a7c09b1530128315d047a086fa3ae3435cb3a8"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:c180f51afb394e165eafe4ac2936a14bee3eb10debc9d9e4db8958fe36afe711"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:8c622a5fe39a48f78944a87d4fb8a53ee07344641b0562c540d840748571b811"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-win32.whl", hash = "sha256:db364eca23f876da6f9e16c9da0df51aa4f104a972735574842618b8c6d999d4"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-win_amd64.whl", hash = "sha256:86216b5cee4b06df986d214f664305142d9c76df9b6512be2738aa72a2048f99"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:6463effa3186ea09411d50efc7d85360b38d5f09b870c48e4600f63af490e56a"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:6c4caeef8fa63d06bd437cd4bdcf3ffefe6738fb1b25951440d80dc7df8c03ac"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:37e55c8e51c236f95b033f6fb391d7d7970ba5fe7ff453dad675e88cf303377a"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fb69256e180cb6c8a894fee62b3afebae785babc1ee98b81cdf68bbca1987f33"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ae5f4161f18c61806f411a13b0310bea87f987c7d2ecdbdaad0e94eb2e404238"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b2b0a0c0517616b6869869f8c581d4eb2dd83a4d79e0ebcb7d373ef9956aeb0a"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:45485e01ff4d3630ec0d9617310448a8702f70e9c01906b0d0118bdf9d124cf2"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:eb00ed941194665c332bf8e078baf037d6c35d7c4f3102ea2d4f16ca94a26dc8"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:2127566c664442652f024c837091890cb1942c30937add288223dc895793f898"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:a50aebfa173e157099939b17f18600f72f84eed3049e743b68ad15bd69b6bf99"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:4d0d1650369165a14e14e1e47b372cfcb31d6ab44e6e33cb2d4e57265290044d"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:923c0c831b7cfcb071580d3f46c4baf50f174be571576556269530f4bbd79d04"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:06a81e93cd441c56a9b65d8e1d043daeb97a3d0856d177d5c90ba85acb3db087"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-win32.whl", hash = "sha256:6ef1d82a3af9d3eecdba2321dc1b3c238245d890843e040e41e470ffa64c3e25"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-win_amd64.whl", hash = "sha256:eb8821e09e916165e160797a6c17edda0679379a4be5c716c260e836e122f54b"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:c235ebd9baae02f1b77bcea61bce332cb4331dc3617d254df3323aa01ab47bd4"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:5b4c145409bef602a690e7cfad0a15a55c13320ff7a3ad7ca59c13bb8ba4d45d"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:68d1f8a9e9e37c1223b656399be5d6b448dea850bed7d0f87a8311f1ff3dabb0"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:22afcb9f253dac0696b5a4be4a1c0f8762f8239e21b99680099abd9b2b1b2269"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e27ad930a842b4c5eb8ac0016b0a54f5aebbe679340c26101df33424142c143c"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1f79682fbe303db92bc2b1136016a38a42e835d932bab5b3b1bfcfbf0640e519"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b261ccdec7821281dade748d088bb6e9b69e6d15b30652b74cbbac25e280b796"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:122c7fa62b130ed55f8f285bfd56d5f4b4a5b503609d181f9ad85e55c89f4185"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:d0eccceffcb53201b5bfebb52600a5fb483a20b61da9dbc885f8b103cbe7598c"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:9f96df6923e21816da7e0ad3fd47dd8f94b2a5ce594e00677c0013018b813458"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:7f04c839ed0b6b98b1a7501a002144b76c18fb1c1850c8b98d458ac269e26ed2"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:34d1c8da1e78d2e001f363791c98a272bb734000fcef47a491c1e3b0505657a8"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:ff8fa367d09b717b2a17a052544193ad76cd49979c805768879cb63d9ca50561"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-win32.whl", hash = "sha256:aed38f6e4fb3f5d6bf81bfa990a07806be9d83cf7bacef998ab1a9bd660a581f"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-win_amd64.whl", hash = "sha256:b01b88d45a6fcb69667cd6d2f7a9aeb4bf53760d7fc536bf679ec94fe9f3ff3d"}, + {file = "charset_normalizer-3.3.2-py3-none-any.whl", hash = "sha256:3e4d1f6587322d2788836a99c69062fbb091331ec940e02d12d179c1d53e25fc"}, +] + +[[package]] +name = "click" +version = "8.1.7" +description = "Composable command line interface toolkit" +optional = false +python-versions = ">=3.7" +files = [ + {file = "click-8.1.7-py3-none-any.whl", hash = "sha256:ae74fb96c20a0277a1d615f1e4d73c8414f5a98db8b799a7931d1582f3390c28"}, + {file = "click-8.1.7.tar.gz", hash = "sha256:ca9853ad459e787e2192211578cc907e7594e294c7ccc834310722b41b9ca6de"}, +] + +[package.dependencies] +colorama = {version = "*", markers = "platform_system == \"Windows\""} + +[[package]] +name = "colorama" +version = "0.4.6" +description = "Cross-platform colored terminal text." +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" +files = [ + {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, + {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, +] + +[[package]] +name = "commonmark" +version = "0.9.1" +description = "Python parser for the CommonMark Markdown spec" +optional = false +python-versions = "*" +files = [ + {file = "commonmark-0.9.1-py2.py3-none-any.whl", hash = "sha256:da2f38c92590f83de410ba1a3cbceafbc74fee9def35f9251ba9a971d6d66fd9"}, + {file = "commonmark-0.9.1.tar.gz", hash = "sha256:452f9dc859be7f06631ddcb328b6919c67984aca654e5fefb3914d54691aed60"}, +] + +[package.extras] +test = ["flake8 (==3.7.8)", "hypothesis (==3.55.3)"] + +[[package]] +name = "coverage" +version = "6.5.0" +description = "Code coverage measurement for Python" +optional = false +python-versions = ">=3.7" +files = [ + {file = "coverage-6.5.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ef8674b0ee8cc11e2d574e3e2998aea5df5ab242e012286824ea3c6970580e53"}, + {file = "coverage-6.5.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:784f53ebc9f3fd0e2a3f6a78b2be1bd1f5575d7863e10c6e12504f240fd06660"}, + {file = "coverage-6.5.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b4a5be1748d538a710f87542f22c2cad22f80545a847ad91ce45e77417293eb4"}, + {file = "coverage-6.5.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:83516205e254a0cb77d2d7bb3632ee019d93d9f4005de31dca0a8c3667d5bc04"}, + {file = "coverage-6.5.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:af4fffaffc4067232253715065e30c5a7ec6faac36f8fc8d6f64263b15f74db0"}, + {file = "coverage-6.5.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:97117225cdd992a9c2a5515db1f66b59db634f59d0679ca1fa3fe8da32749cae"}, + {file = "coverage-6.5.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:a1170fa54185845505fbfa672f1c1ab175446c887cce8212c44149581cf2d466"}, + {file = "coverage-6.5.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:11b990d520ea75e7ee8dcab5bc908072aaada194a794db9f6d7d5cfd19661e5a"}, + {file = "coverage-6.5.0-cp310-cp310-win32.whl", hash = "sha256:5dbec3b9095749390c09ab7c89d314727f18800060d8d24e87f01fb9cfb40b32"}, + {file = "coverage-6.5.0-cp310-cp310-win_amd64.whl", hash = "sha256:59f53f1dc5b656cafb1badd0feb428c1e7bc19b867479ff72f7a9dd9b479f10e"}, + {file = "coverage-6.5.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4a5375e28c5191ac38cca59b38edd33ef4cc914732c916f2929029b4bfb50795"}, + {file = "coverage-6.5.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c4ed2820d919351f4167e52425e096af41bfabacb1857186c1ea32ff9983ed75"}, + {file = "coverage-6.5.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:33a7da4376d5977fbf0a8ed91c4dffaaa8dbf0ddbf4c8eea500a2486d8bc4d7b"}, + {file = "coverage-6.5.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a8fb6cf131ac4070c9c5a3e21de0f7dc5a0fbe8bc77c9456ced896c12fcdad91"}, + {file = "coverage-6.5.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:a6b7d95969b8845250586f269e81e5dfdd8ff828ddeb8567a4a2eaa7313460c4"}, + {file = "coverage-6.5.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:1ef221513e6f68b69ee9e159506d583d31aa3567e0ae84eaad9d6ec1107dddaa"}, + {file = "coverage-6.5.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:cca4435eebea7962a52bdb216dec27215d0df64cf27fc1dd538415f5d2b9da6b"}, + {file = "coverage-6.5.0-cp311-cp311-win32.whl", hash = "sha256:98e8a10b7a314f454d9eff4216a9a94d143a7ee65018dd12442e898ee2310578"}, + {file = "coverage-6.5.0-cp311-cp311-win_amd64.whl", hash = "sha256:bc8ef5e043a2af066fa8cbfc6e708d58017024dc4345a1f9757b329a249f041b"}, + {file = "coverage-6.5.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:4433b90fae13f86fafff0b326453dd42fc9a639a0d9e4eec4d366436d1a41b6d"}, + {file = "coverage-6.5.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f4f05d88d9a80ad3cac6244d36dd89a3c00abc16371769f1340101d3cb899fc3"}, + {file = "coverage-6.5.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:94e2565443291bd778421856bc975d351738963071e9b8839ca1fc08b42d4bef"}, + {file = "coverage-6.5.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:027018943386e7b942fa832372ebc120155fd970837489896099f5cfa2890f79"}, + {file = "coverage-6.5.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:255758a1e3b61db372ec2736c8e2a1fdfaf563977eedbdf131de003ca5779b7d"}, + {file = "coverage-6.5.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:851cf4ff24062c6aec510a454b2584f6e998cada52d4cb58c5e233d07172e50c"}, + {file = "coverage-6.5.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:12adf310e4aafddc58afdb04d686795f33f4d7a6fa67a7a9d4ce7d6ae24d949f"}, + {file = "coverage-6.5.0-cp37-cp37m-win32.whl", hash = "sha256:b5604380f3415ba69de87a289a2b56687faa4fe04dbee0754bfcae433489316b"}, + {file = "coverage-6.5.0-cp37-cp37m-win_amd64.whl", hash = "sha256:4a8dbc1f0fbb2ae3de73eb0bdbb914180c7abfbf258e90b311dcd4f585d44bd2"}, + {file = "coverage-6.5.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:d900bb429fdfd7f511f868cedd03a6bbb142f3f9118c09b99ef8dc9bf9643c3c"}, + {file = "coverage-6.5.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:2198ea6fc548de52adc826f62cb18554caedfb1d26548c1b7c88d8f7faa8f6ba"}, + {file = "coverage-6.5.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6c4459b3de97b75e3bd6b7d4b7f0db13f17f504f3d13e2a7c623786289dd670e"}, + {file = "coverage-6.5.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:20c8ac5386253717e5ccc827caad43ed66fea0efe255727b1053a8154d952398"}, + {file = "coverage-6.5.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6b07130585d54fe8dff3d97b93b0e20290de974dc8177c320aeaf23459219c0b"}, + {file = "coverage-6.5.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:dbdb91cd8c048c2b09eb17713b0c12a54fbd587d79adcebad543bc0cd9a3410b"}, + {file = "coverage-6.5.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:de3001a203182842a4630e7b8d1a2c7c07ec1b45d3084a83d5d227a3806f530f"}, + {file = "coverage-6.5.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:e07f4a4a9b41583d6eabec04f8b68076ab3cd44c20bd29332c6572dda36f372e"}, + {file = "coverage-6.5.0-cp38-cp38-win32.whl", hash = "sha256:6d4817234349a80dbf03640cec6109cd90cba068330703fa65ddf56b60223a6d"}, + {file = "coverage-6.5.0-cp38-cp38-win_amd64.whl", hash = "sha256:7ccf362abd726b0410bf8911c31fbf97f09f8f1061f8c1cf03dfc4b6372848f6"}, + {file = "coverage-6.5.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:633713d70ad6bfc49b34ead4060531658dc6dfc9b3eb7d8a716d5873377ab745"}, + {file = "coverage-6.5.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:95203854f974e07af96358c0b261f1048d8e1083f2de9b1c565e1be4a3a48cfc"}, + {file = "coverage-6.5.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b9023e237f4c02ff739581ef35969c3739445fb059b060ca51771e69101efffe"}, + {file = "coverage-6.5.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:265de0fa6778d07de30bcf4d9dc471c3dc4314a23a3c6603d356a3c9abc2dfcf"}, + {file = "coverage-6.5.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8f830ed581b45b82451a40faabb89c84e1a998124ee4212d440e9c6cf70083e5"}, + {file = "coverage-6.5.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:7b6be138d61e458e18d8e6ddcddd36dd96215edfe5f1168de0b1b32635839b62"}, + {file = "coverage-6.5.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:42eafe6778551cf006a7c43153af1211c3aaab658d4d66fa5fcc021613d02518"}, + {file = "coverage-6.5.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:723e8130d4ecc8f56e9a611e73b31219595baa3bb252d539206f7bbbab6ffc1f"}, + {file = "coverage-6.5.0-cp39-cp39-win32.whl", hash = "sha256:d9ecf0829c6a62b9b573c7bb6d4dcd6ba8b6f80be9ba4fc7ed50bf4ac9aecd72"}, + {file = "coverage-6.5.0-cp39-cp39-win_amd64.whl", hash = "sha256:fc2af30ed0d5ae0b1abdb4ebdce598eafd5b35397d4d75deb341a614d333d987"}, + {file = "coverage-6.5.0-pp36.pp37.pp38-none-any.whl", hash = "sha256:1431986dac3923c5945271f169f59c45b8802a114c8f548d611f2015133df77a"}, + {file = "coverage-6.5.0.tar.gz", hash = "sha256:f642e90754ee3e06b0e7e51bce3379590e76b7f76b708e1a71ff043f87025c84"}, +] + +[package.dependencies] +tomli = {version = "*", optional = true, markers = "python_full_version <= \"3.11.0a6\" and extra == \"toml\""} + +[package.extras] +toml = ["tomli"] + +[[package]] +name = "coverage-badge" +version = "1.1.2" +description = "Generate coverage badges for Coverage.py." +optional = false +python-versions = "*" +files = [ + {file = "coverage_badge-1.1.2-py2.py3-none-any.whl", hash = "sha256:d8413ce51c91043a1692b943616b450868cbeeb0ea6a0c54a32f8318c9c96ff7"}, + {file = "coverage_badge-1.1.2.tar.gz", hash = "sha256:fe7ed58a3b72dad85a553b64a99e963dea3847dcd0b8ddd2b38a00333618642c"}, +] + +[package.dependencies] +coverage = "*" +setuptools = "*" + +[[package]] +name = "darglint" +version = "1.8.1" +description = "A utility for ensuring Google-style docstrings stay up to date with the source code." +optional = false +python-versions = ">=3.6,<4.0" +files = [ + {file = "darglint-1.8.1-py3-none-any.whl", hash = "sha256:5ae11c259c17b0701618a20c3da343a3eb98b3bc4b5a83d31cdd94f5ebdced8d"}, + {file = "darglint-1.8.1.tar.gz", hash = "sha256:080d5106df149b199822e7ee7deb9c012b49891538f14a11be681044f0bb20da"}, +] + +[[package]] +name = "dill" +version = "0.3.8" +description = "serialize all of Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "dill-0.3.8-py3-none-any.whl", hash = "sha256:c36ca9ffb54365bdd2f8eb3eff7d2a21237f8452b57ace88b1ac615b7e815bd7"}, + {file = "dill-0.3.8.tar.gz", hash = "sha256:3ebe3c479ad625c4553aca177444d89b486b1d84982eeacded644afc0cf797ca"}, +] + +[package.extras] +graph = ["objgraph (>=1.7.2)"] +profile = ["gprof2dot (>=2022.7.29)"] + +[[package]] +name = "distlib" +version = "0.3.8" +description = "Distribution utilities" +optional = false +python-versions = "*" +files = [ + {file = "distlib-0.3.8-py2.py3-none-any.whl", hash = "sha256:034db59a0b96f8ca18035f36290806a9a6e6bd9d1ff91e45a7f172eb17e51784"}, + {file = "distlib-0.3.8.tar.gz", hash = "sha256:1530ea13e350031b6312d8580ddb6b27a104275a31106523b8f123787f494f64"}, +] + +[[package]] +name = "dparse" +version = "0.6.3" +description = "A parser for Python dependency files" +optional = false +python-versions = ">=3.6" +files = [ + {file = "dparse-0.6.3-py3-none-any.whl", hash = "sha256:0d8fe18714056ca632d98b24fbfc4e9791d4e47065285ab486182288813a5318"}, + {file = "dparse-0.6.3.tar.gz", hash = "sha256:27bb8b4bcaefec3997697ba3f6e06b2447200ba273c0b085c3d012a04571b528"}, +] + +[package.dependencies] +packaging = "*" +tomli = {version = "*", markers = "python_version < \"3.11\""} + +[package.extras] +conda = ["pyyaml"] +pipenv = ["pipenv (<=2022.12.19)"] + +[[package]] +name = "exceptiongroup" +version = "1.2.2" +description = "Backport of PEP 654 (exception groups)" +optional = false +python-versions = ">=3.7" +files = [ + {file = "exceptiongroup-1.2.2-py3-none-any.whl", hash = "sha256:3111b9d131c238bec2f8f516e123e14ba243563fb135d3fe885990585aa7795b"}, + {file = "exceptiongroup-1.2.2.tar.gz", hash = "sha256:47c2edf7c6738fafb49fd34290706d1a1a2f4d1c6df275526b62cbb4aa5393cc"}, +] + +[package.extras] +test = ["pytest (>=6)"] + +[[package]] +name = "filelock" +version = "3.16.1" +description = "A platform independent file lock." +optional = false +python-versions = ">=3.8" +files = [ + {file = "filelock-3.16.1-py3-none-any.whl", hash = "sha256:2082e5703d51fbf98ea75855d9d5527e33d8ff23099bec374a134febee6946b0"}, + {file = "filelock-3.16.1.tar.gz", hash = "sha256:c249fbfcd5db47e5e2d6d62198e565475ee65e4831e2561c8e313fa7eb961435"}, +] + +[package.extras] +docs = ["furo (>=2024.8.6)", "sphinx (>=8.0.2)", "sphinx-autodoc-typehints (>=2.4.1)"] +testing = ["covdefaults (>=2.3)", "coverage (>=7.6.1)", "diff-cover (>=9.2)", "pytest (>=8.3.3)", "pytest-asyncio (>=0.24)", "pytest-cov (>=5)", "pytest-mock (>=3.14)", "pytest-timeout (>=2.3.1)", "virtualenv (>=20.26.4)"] +typing = ["typing-extensions (>=4.12.2)"] + +[[package]] +name = "identify" +version = "2.6.1" +description = "File identification library for Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "identify-2.6.1-py2.py3-none-any.whl", hash = "sha256:53863bcac7caf8d2ed85bd20312ea5dcfc22226800f6d6881f232d861db5a8f0"}, + {file = "identify-2.6.1.tar.gz", hash = "sha256:91478c5fb7c3aac5ff7bf9b4344f803843dc586832d5f110d672b19aa1984c98"}, +] + +[package.extras] +license = ["ukkonen"] + +[[package]] +name = "idna" +version = "3.10" +description = "Internationalized Domain Names in Applications (IDNA)" +optional = false +python-versions = ">=3.6" +files = [ + {file = "idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3"}, + {file = "idna-3.10.tar.gz", hash = "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9"}, +] + +[package.extras] +all = ["flake8 (>=7.1.1)", "mypy (>=1.11.2)", "pytest (>=8.3.2)", "ruff (>=0.6.2)"] + +[[package]] +name = "iniconfig" +version = "2.0.0" +description = "brain-dead simple config-ini parsing" +optional = false +python-versions = ">=3.7" +files = [ + {file = "iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374"}, + {file = "iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3"}, +] + +[[package]] +name = "isort" +version = "5.13.2" +description = "A Python utility / library to sort Python imports." +optional = false +python-versions = ">=3.8.0" +files = [ + {file = "isort-5.13.2-py3-none-any.whl", hash = "sha256:8ca5e72a8d85860d5a3fa69b8745237f2939afe12dbf656afbcb47fe72d947a6"}, + {file = "isort-5.13.2.tar.gz", hash = "sha256:48fdfcb9face5d58a4f6dde2e72a1fb8dcaf8ab26f95ab49fab84c2ddefb0109"}, +] + +[package.dependencies] +colorama = {version = ">=0.4.6", optional = true, markers = "extra == \"colors\""} + +[package.extras] +colors = ["colorama (>=0.4.6)"] + +[[package]] +name = "jinja2" +version = "3.1.4" +description = "A very fast and expressive template engine." +optional = false +python-versions = ">=3.7" +files = [ + {file = "jinja2-3.1.4-py3-none-any.whl", hash = "sha256:bc5dd2abb727a5319567b7a813e6a2e7318c39f4f487cfe6c89c6f9c7d25197d"}, + {file = "jinja2-3.1.4.tar.gz", hash = "sha256:4a3aee7acbbe7303aede8e9648d13b8bf88a429282aa6122a993f0ac800cb369"}, +] + +[package.dependencies] +MarkupSafe = ">=2.0" + +[package.extras] +i18n = ["Babel (>=2.7)"] + +[[package]] +name = "lazy-object-proxy" +version = "1.10.0" +description = "A fast and thorough lazy object proxy." +optional = false +python-versions = ">=3.8" +files = [ + {file = "lazy-object-proxy-1.10.0.tar.gz", hash = "sha256:78247b6d45f43a52ef35c25b5581459e85117225408a4128a3daf8bf9648ac69"}, + {file = "lazy_object_proxy-1.10.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:855e068b0358ab916454464a884779c7ffa312b8925c6f7401e952dcf3b89977"}, + {file = "lazy_object_proxy-1.10.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7ab7004cf2e59f7c2e4345604a3e6ea0d92ac44e1c2375527d56492014e690c3"}, + {file = "lazy_object_proxy-1.10.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dc0d2fc424e54c70c4bc06787e4072c4f3b1aa2f897dfdc34ce1013cf3ceef05"}, + {file = "lazy_object_proxy-1.10.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:e2adb09778797da09d2b5ebdbceebf7dd32e2c96f79da9052b2e87b6ea495895"}, + {file = "lazy_object_proxy-1.10.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:b1f711e2c6dcd4edd372cf5dec5c5a30d23bba06ee012093267b3376c079ec83"}, + {file = "lazy_object_proxy-1.10.0-cp310-cp310-win32.whl", hash = "sha256:76a095cfe6045c7d0ca77db9934e8f7b71b14645f0094ffcd842349ada5c5fb9"}, + {file = "lazy_object_proxy-1.10.0-cp310-cp310-win_amd64.whl", hash = "sha256:b4f87d4ed9064b2628da63830986c3d2dca7501e6018347798313fcf028e2fd4"}, + {file = "lazy_object_proxy-1.10.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:fec03caabbc6b59ea4a638bee5fce7117be8e99a4103d9d5ad77f15d6f81020c"}, + {file = "lazy_object_proxy-1.10.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:02c83f957782cbbe8136bee26416686a6ae998c7b6191711a04da776dc9e47d4"}, + {file = "lazy_object_proxy-1.10.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:009e6bb1f1935a62889ddc8541514b6a9e1fcf302667dcb049a0be5c8f613e56"}, + {file = "lazy_object_proxy-1.10.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:75fc59fc450050b1b3c203c35020bc41bd2695ed692a392924c6ce180c6f1dc9"}, + {file = "lazy_object_proxy-1.10.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:782e2c9b2aab1708ffb07d4bf377d12901d7a1d99e5e410d648d892f8967ab1f"}, + {file = "lazy_object_proxy-1.10.0-cp311-cp311-win32.whl", hash = "sha256:edb45bb8278574710e68a6b021599a10ce730d156e5b254941754a9cc0b17d03"}, + {file = "lazy_object_proxy-1.10.0-cp311-cp311-win_amd64.whl", hash = "sha256:e271058822765ad5e3bca7f05f2ace0de58a3f4e62045a8c90a0dfd2f8ad8cc6"}, + {file = "lazy_object_proxy-1.10.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:e98c8af98d5707dcdecc9ab0863c0ea6e88545d42ca7c3feffb6b4d1e370c7ba"}, + {file = "lazy_object_proxy-1.10.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:952c81d415b9b80ea261d2372d2a4a2332a3890c2b83e0535f263ddfe43f0d43"}, + {file = "lazy_object_proxy-1.10.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:80b39d3a151309efc8cc48675918891b865bdf742a8616a337cb0090791a0de9"}, + {file = "lazy_object_proxy-1.10.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:e221060b701e2aa2ea991542900dd13907a5c90fa80e199dbf5a03359019e7a3"}, + {file = "lazy_object_proxy-1.10.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:92f09ff65ecff3108e56526f9e2481b8116c0b9e1425325e13245abfd79bdb1b"}, + {file = "lazy_object_proxy-1.10.0-cp312-cp312-win32.whl", hash = "sha256:3ad54b9ddbe20ae9f7c1b29e52f123120772b06dbb18ec6be9101369d63a4074"}, + {file = "lazy_object_proxy-1.10.0-cp312-cp312-win_amd64.whl", hash = "sha256:127a789c75151db6af398b8972178afe6bda7d6f68730c057fbbc2e96b08d282"}, + {file = "lazy_object_proxy-1.10.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:9e4ed0518a14dd26092614412936920ad081a424bdcb54cc13349a8e2c6d106a"}, + {file = "lazy_object_proxy-1.10.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5ad9e6ed739285919aa9661a5bbed0aaf410aa60231373c5579c6b4801bd883c"}, + {file = "lazy_object_proxy-1.10.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2fc0a92c02fa1ca1e84fc60fa258458e5bf89d90a1ddaeb8ed9cc3147f417255"}, + {file = "lazy_object_proxy-1.10.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:0aefc7591920bbd360d57ea03c995cebc204b424524a5bd78406f6e1b8b2a5d8"}, + {file = "lazy_object_proxy-1.10.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:5faf03a7d8942bb4476e3b62fd0f4cf94eaf4618e304a19865abf89a35c0bbee"}, + {file = "lazy_object_proxy-1.10.0-cp38-cp38-win32.whl", hash = "sha256:e333e2324307a7b5d86adfa835bb500ee70bfcd1447384a822e96495796b0ca4"}, + {file = "lazy_object_proxy-1.10.0-cp38-cp38-win_amd64.whl", hash = "sha256:cb73507defd385b7705c599a94474b1d5222a508e502553ef94114a143ec6696"}, + {file = "lazy_object_proxy-1.10.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:366c32fe5355ef5fc8a232c5436f4cc66e9d3e8967c01fb2e6302fd6627e3d94"}, + {file = "lazy_object_proxy-1.10.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2297f08f08a2bb0d32a4265e98a006643cd7233fb7983032bd61ac7a02956b3b"}, + {file = "lazy_object_proxy-1.10.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:18dd842b49456aaa9a7cf535b04ca4571a302ff72ed8740d06b5adcd41fe0757"}, + {file = "lazy_object_proxy-1.10.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:217138197c170a2a74ca0e05bddcd5f1796c735c37d0eee33e43259b192aa424"}, + {file = "lazy_object_proxy-1.10.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:9a3a87cf1e133e5b1994144c12ca4aa3d9698517fe1e2ca82977781b16955658"}, + {file = "lazy_object_proxy-1.10.0-cp39-cp39-win32.whl", hash = "sha256:30b339b2a743c5288405aa79a69e706a06e02958eab31859f7f3c04980853b70"}, + {file = "lazy_object_proxy-1.10.0-cp39-cp39-win_amd64.whl", hash = "sha256:a899b10e17743683b293a729d3a11f2f399e8a90c73b089e29f5d0fe3509f0dd"}, + {file = "lazy_object_proxy-1.10.0-pp310.pp311.pp312.pp38.pp39-none-any.whl", hash = "sha256:80fa48bd89c8f2f456fc0765c11c23bf5af827febacd2f523ca5bc1893fcc09d"}, +] + +[[package]] +name = "markupsafe" +version = "2.1.5" +description = "Safely add untrusted strings to HTML/XML markup." +optional = false +python-versions = ">=3.7" +files = [ + {file = "MarkupSafe-2.1.5-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:a17a92de5231666cfbe003f0e4b9b3a7ae3afb1ec2845aadc2bacc93ff85febc"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:72b6be590cc35924b02c78ef34b467da4ba07e4e0f0454a2c5907f473fc50ce5"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e61659ba32cf2cf1481e575d0462554625196a1f2fc06a1c777d3f48e8865d46"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2174c595a0d73a3080ca3257b40096db99799265e1c27cc5a610743acd86d62f"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ae2ad8ae6ebee9d2d94b17fb62763125f3f374c25618198f40cbb8b525411900"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:075202fa5b72c86ad32dc7d0b56024ebdbcf2048c0ba09f1cde31bfdd57bcfff"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:598e3276b64aff0e7b3451b72e94fa3c238d452e7ddcd893c3ab324717456bad"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:fce659a462a1be54d2ffcacea5e3ba2d74daa74f30f5f143fe0c58636e355fdd"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-win32.whl", hash = "sha256:d9fad5155d72433c921b782e58892377c44bd6252b5af2f67f16b194987338a4"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-win_amd64.whl", hash = "sha256:bf50cd79a75d181c9181df03572cdce0fbb75cc353bc350712073108cba98de5"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:629ddd2ca402ae6dbedfceeba9c46d5f7b2a61d9749597d4307f943ef198fc1f"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:5b7b716f97b52c5a14bffdf688f971b2d5ef4029127f1ad7a513973cfd818df2"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6ec585f69cec0aa07d945b20805be741395e28ac1627333b1c5b0105962ffced"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b91c037585eba9095565a3556f611e3cbfaa42ca1e865f7b8015fe5c7336d5a5"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7502934a33b54030eaf1194c21c692a534196063db72176b0c4028e140f8f32c"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:0e397ac966fdf721b2c528cf028494e86172b4feba51d65f81ffd65c63798f3f"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:c061bb86a71b42465156a3ee7bd58c8c2ceacdbeb95d05a99893e08b8467359a"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:3a57fdd7ce31c7ff06cdfbf31dafa96cc533c21e443d57f5b1ecc6cdc668ec7f"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-win32.whl", hash = "sha256:397081c1a0bfb5124355710fe79478cdbeb39626492b15d399526ae53422b906"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-win_amd64.whl", hash = "sha256:2b7c57a4dfc4f16f7142221afe5ba4e093e09e728ca65c51f5620c9aaeb9a617"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:8dec4936e9c3100156f8a2dc89c4b88d5c435175ff03413b443469c7c8c5f4d1"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:3c6b973f22eb18a789b1460b4b91bf04ae3f0c4234a0a6aa6b0a92f6f7b951d4"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ac07bad82163452a6884fe8fa0963fb98c2346ba78d779ec06bd7a6262132aee"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f5dfb42c4604dddc8e4305050aa6deb084540643ed5804d7455b5df8fe16f5e5"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ea3d8a3d18833cf4304cd2fc9cbb1efe188ca9b5efef2bdac7adc20594a0e46b"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:d050b3361367a06d752db6ead6e7edeb0009be66bc3bae0ee9d97fb326badc2a"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:bec0a414d016ac1a18862a519e54b2fd0fc8bbfd6890376898a6c0891dd82e9f"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:58c98fee265677f63a4385256a6d7683ab1832f3ddd1e66fe948d5880c21a169"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-win32.whl", hash = "sha256:8590b4ae07a35970728874632fed7bd57b26b0102df2d2b233b6d9d82f6c62ad"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-win_amd64.whl", hash = "sha256:823b65d8706e32ad2df51ed89496147a42a2a6e01c13cfb6ffb8b1e92bc910bb"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:c8b29db45f8fe46ad280a7294f5c3ec36dbac9491f2d1c17345be8e69cc5928f"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ec6a563cff360b50eed26f13adc43e61bc0c04d94b8be985e6fb24b81f6dcfdf"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a549b9c31bec33820e885335b451286e2969a2d9e24879f83fe904a5ce59d70a"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4f11aa001c540f62c6166c7726f71f7573b52c68c31f014c25cc7901deea0b52"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:7b2e5a267c855eea6b4283940daa6e88a285f5f2a67f2220203786dfa59b37e9"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:2d2d793e36e230fd32babe143b04cec8a8b3eb8a3122d2aceb4a371e6b09b8df"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:ce409136744f6521e39fd8e2a24c53fa18ad67aa5bc7c2cf83645cce5b5c4e50"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-win32.whl", hash = "sha256:4096e9de5c6fdf43fb4f04c26fb114f61ef0bf2e5604b6ee3019d51b69e8c371"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-win_amd64.whl", hash = "sha256:4275d846e41ecefa46e2015117a9f491e57a71ddd59bbead77e904dc02b1bed2"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:656f7526c69fac7f600bd1f400991cc282b417d17539a1b228617081106feb4a"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:97cafb1f3cbcd3fd2b6fbfb99ae11cdb14deea0736fc2b0952ee177f2b813a46"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f3fbcb7ef1f16e48246f704ab79d79da8a46891e2da03f8783a5b6fa41a9532"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fa9db3f79de01457b03d4f01b34cf91bc0048eb2c3846ff26f66687c2f6d16ab"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ffee1f21e5ef0d712f9033568f8344d5da8cc2869dbd08d87c84656e6a2d2f68"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:5dedb4db619ba5a2787a94d877bc8ffc0566f92a01c0ef214865e54ecc9ee5e0"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:30b600cf0a7ac9234b2638fbc0fb6158ba5bdcdf46aeb631ead21248b9affbc4"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:8dd717634f5a044f860435c1d8c16a270ddf0ef8588d4887037c5028b859b0c3"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-win32.whl", hash = "sha256:daa4ee5a243f0f20d528d939d06670a298dd39b1ad5f8a72a4275124a7819eff"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-win_amd64.whl", hash = "sha256:619bc166c4f2de5caa5a633b8b7326fbe98e0ccbfacabd87268a2b15ff73a029"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:7a68b554d356a91cce1236aa7682dc01df0edba8d043fd1ce607c49dd3c1edcf"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:db0b55e0f3cc0be60c1f19efdde9a637c32740486004f20d1cff53c3c0ece4d2"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3e53af139f8579a6d5f7b76549125f0d94d7e630761a2111bc431fd820e163b8"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:17b950fccb810b3293638215058e432159d2b71005c74371d784862b7e4683f3"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4c31f53cdae6ecfa91a77820e8b151dba54ab528ba65dfd235c80b086d68a465"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:bff1b4290a66b490a2f4719358c0cdcd9bafb6b8f061e45c7a2460866bf50c2e"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:bc1667f8b83f48511b94671e0e441401371dfd0f0a795c7daa4a3cd1dde55bea"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:5049256f536511ee3f7e1b3f87d1d1209d327e818e6ae1365e8653d7e3abb6a6"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-win32.whl", hash = "sha256:00e046b6dd71aa03a41079792f8473dc494d564611a8f89bbbd7cb93295ebdcf"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-win_amd64.whl", hash = "sha256:fa173ec60341d6bb97a89f5ea19c85c5643c1e7dedebc22f5181eb73573142c5"}, + {file = "MarkupSafe-2.1.5.tar.gz", hash = "sha256:d283d37a890ba4c1ae73ffadf8046435c76e7bc2247bbb63c00bd1a709c6544b"}, +] + +[[package]] +name = "mccabe" +version = "0.7.0" +description = "McCabe checker, plugin for flake8" +optional = false +python-versions = ">=3.6" +files = [ + {file = "mccabe-0.7.0-py2.py3-none-any.whl", hash = "sha256:6c2d30ab6be0e4a46919781807b4f0d834ebdd6c6e3dca0bda5a15f863427b6e"}, + {file = "mccabe-0.7.0.tar.gz", hash = "sha256:348e0240c33b60bbdf4e523192ef919f28cb2c3d7d5c7794f74009290f236325"}, +] + +[[package]] +name = "mypy" +version = "0.910" +description = "Optional static typing for Python" +optional = false +python-versions = ">=3.5" +files = [ + {file = "mypy-0.910-cp35-cp35m-macosx_10_9_x86_64.whl", hash = "sha256:a155d80ea6cee511a3694b108c4494a39f42de11ee4e61e72bc424c490e46457"}, + {file = "mypy-0.910-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:b94e4b785e304a04ea0828759172a15add27088520dc7e49ceade7834275bedb"}, + {file = "mypy-0.910-cp35-cp35m-manylinux2010_x86_64.whl", hash = "sha256:088cd9c7904b4ad80bec811053272986611b84221835e079be5bcad029e79dd9"}, + {file = "mypy-0.910-cp35-cp35m-win_amd64.whl", hash = "sha256:adaeee09bfde366d2c13fe6093a7df5df83c9a2ba98638c7d76b010694db760e"}, + {file = "mypy-0.910-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:ecd2c3fe726758037234c93df7e98deb257fd15c24c9180dacf1ef829da5f921"}, + {file = "mypy-0.910-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:d9dd839eb0dc1bbe866a288ba3c1afc33a202015d2ad83b31e875b5905a079b6"}, + {file = "mypy-0.910-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:3e382b29f8e0ccf19a2df2b29a167591245df90c0b5a2542249873b5c1d78212"}, + {file = "mypy-0.910-cp36-cp36m-win_amd64.whl", hash = "sha256:53fd2eb27a8ee2892614370896956af2ff61254c275aaee4c230ae771cadd885"}, + {file = "mypy-0.910-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:b6fb13123aeef4a3abbcfd7e71773ff3ff1526a7d3dc538f3929a49b42be03f0"}, + {file = "mypy-0.910-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:e4dab234478e3bd3ce83bac4193b2ecd9cf94e720ddd95ce69840273bf44f6de"}, + {file = "mypy-0.910-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:7df1ead20c81371ccd6091fa3e2878559b5c4d4caadaf1a484cf88d93ca06703"}, + {file = "mypy-0.910-cp37-cp37m-win_amd64.whl", hash = "sha256:0aadfb2d3935988ec3815952e44058a3100499f5be5b28c34ac9d79f002a4a9a"}, + {file = "mypy-0.910-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:ec4e0cd079db280b6bdabdc807047ff3e199f334050db5cbb91ba3e959a67504"}, + {file = "mypy-0.910-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:119bed3832d961f3a880787bf621634ba042cb8dc850a7429f643508eeac97b9"}, + {file = "mypy-0.910-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:866c41f28cee548475f146aa4d39a51cf3b6a84246969f3759cb3e9c742fc072"}, + {file = "mypy-0.910-cp38-cp38-win_amd64.whl", hash = "sha256:ceb6e0a6e27fb364fb3853389607cf7eb3a126ad335790fa1e14ed02fba50811"}, + {file = "mypy-0.910-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:1a85e280d4d217150ce8cb1a6dddffd14e753a4e0c3cf90baabb32cefa41b59e"}, + {file = "mypy-0.910-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:42c266ced41b65ed40a282c575705325fa7991af370036d3f134518336636f5b"}, + {file = "mypy-0.910-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:3c4b8ca36877fc75339253721f69603a9c7fdb5d4d5a95a1a1b899d8b86a4de2"}, + {file = "mypy-0.910-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:c0df2d30ed496a08de5daed2a9ea807d07c21ae0ab23acf541ab88c24b26ab97"}, + {file = "mypy-0.910-cp39-cp39-win_amd64.whl", hash = "sha256:c6c2602dffb74867498f86e6129fd52a2770c48b7cd3ece77ada4fa38f94eba8"}, + {file = "mypy-0.910-py3-none-any.whl", hash = "sha256:ef565033fa5a958e62796867b1df10c40263ea9ded87164d67572834e57a174d"}, + {file = "mypy-0.910.tar.gz", hash = "sha256:704098302473cb31a218f1775a873b376b30b4c18229421e9e9dc8916fd16150"}, +] + +[package.dependencies] +mypy-extensions = ">=0.4.3,<0.5.0" +toml = "*" +typing-extensions = ">=3.7.4" + +[package.extras] +dmypy = ["psutil (>=4.0)"] +python2 = ["typed-ast (>=1.4.0,<1.5.0)"] + +[[package]] +name = "mypy-extensions" +version = "0.4.4" +description = "Experimental type system extensions for programs checked with the mypy typechecker." +optional = false +python-versions = ">=2.7" +files = [ + {file = "mypy_extensions-0.4.4.tar.gz", hash = "sha256:c8b707883a96efe9b4bb3aaf0dcc07e7e217d7d8368eec4db4049ee9e142f4fd"}, +] + +[[package]] +name = "nodeenv" +version = "1.9.1" +description = "Node.js virtual environment builder" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" +files = [ + {file = "nodeenv-1.9.1-py2.py3-none-any.whl", hash = "sha256:ba11c9782d29c27c70ffbdda2d7415098754709be8a7056d79a737cd901155c9"}, + {file = "nodeenv-1.9.1.tar.gz", hash = "sha256:6ec12890a2dab7946721edbfbcd91f3319c6ccc9aec47be7c7e6b7011ee6645f"}, +] + +[[package]] +name = "packaging" +version = "24.1" +description = "Core utilities for Python packages" +optional = false +python-versions = ">=3.8" +files = [ + {file = "packaging-24.1-py3-none-any.whl", hash = "sha256:5b8f2217dbdbd2f7f384c41c628544e6d52f2d0f53c6d0c3ea61aa5d1d7ff124"}, + {file = "packaging-24.1.tar.gz", hash = "sha256:026ed72c8ed3fcce5bf8950572258698927fd1dbda10a5e981cdf0ac37f4f002"}, +] + +[[package]] +name = "pathspec" +version = "0.12.1" +description = "Utility library for gitignore style pattern matching of file paths." +optional = false +python-versions = ">=3.8" +files = [ + {file = "pathspec-0.12.1-py3-none-any.whl", hash = "sha256:a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08"}, + {file = "pathspec-0.12.1.tar.gz", hash = "sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712"}, +] + +[[package]] +name = "pbr" +version = "6.1.0" +description = "Python Build Reasonableness" +optional = false +python-versions = ">=2.6" +files = [ + {file = "pbr-6.1.0-py2.py3-none-any.whl", hash = "sha256:a776ae228892d8013649c0aeccbb3d5f99ee15e005a4cbb7e61d55a067b28a2a"}, + {file = "pbr-6.1.0.tar.gz", hash = "sha256:788183e382e3d1d7707db08978239965e8b9e4e5ed42669bf4758186734d5f24"}, +] + +[[package]] +name = "platformdirs" +version = "4.3.6" +description = "A small Python package for determining appropriate platform-specific dirs, e.g. a `user data dir`." +optional = false +python-versions = ">=3.8" +files = [ + {file = "platformdirs-4.3.6-py3-none-any.whl", hash = "sha256:73e575e1408ab8103900836b97580d5307456908a03e92031bab39e4554cc3fb"}, + {file = "platformdirs-4.3.6.tar.gz", hash = "sha256:357fb2acbc885b0419afd3ce3ed34564c13c9b95c89360cd9563f73aa5e2b907"}, +] + +[package.extras] +docs = ["furo (>=2024.8.6)", "proselint (>=0.14)", "sphinx (>=8.0.2)", "sphinx-autodoc-typehints (>=2.4)"] +test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=8.3.2)", "pytest-cov (>=5)", "pytest-mock (>=3.14)"] +type = ["mypy (>=1.11.2)"] + +[[package]] +name = "pluggy" +version = "1.5.0" +description = "plugin and hook calling mechanisms for python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pluggy-1.5.0-py3-none-any.whl", hash = "sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669"}, + {file = "pluggy-1.5.0.tar.gz", hash = "sha256:2cffa88e94fdc978c4c574f15f9e59b7f4201d439195c3715ca9e2486f1d0cf1"}, +] + +[package.extras] +dev = ["pre-commit", "tox"] +testing = ["pytest", "pytest-benchmark"] + +[[package]] +name = "pre-commit" +version = "2.21.0" +description = "A framework for managing and maintaining multi-language pre-commit hooks." +optional = false +python-versions = ">=3.7" +files = [ + {file = "pre_commit-2.21.0-py2.py3-none-any.whl", hash = "sha256:e2f91727039fc39a92f58a588a25b87f936de6567eed4f0e673e0507edc75bad"}, + {file = "pre_commit-2.21.0.tar.gz", hash = "sha256:31ef31af7e474a8d8995027fefdfcf509b5c913ff31f2015b4ec4beb26a6f658"}, +] + +[package.dependencies] +cfgv = ">=2.0.0" +identify = ">=1.0.0" +nodeenv = ">=0.11.1" +pyyaml = ">=5.1" +virtualenv = ">=20.10.0" + +[[package]] +name = "pydocstyle" +version = "6.3.0" +description = "Python docstring style checker" +optional = false +python-versions = ">=3.6" +files = [ + {file = "pydocstyle-6.3.0-py3-none-any.whl", hash = "sha256:118762d452a49d6b05e194ef344a55822987a462831ade91ec5c06fd2169d019"}, + {file = "pydocstyle-6.3.0.tar.gz", hash = "sha256:7ce43f0c0ac87b07494eb9c0b462c0b73e6ff276807f204d6b53edc72b7e44e1"}, +] + +[package.dependencies] +snowballstemmer = ">=2.2.0" + +[package.extras] +toml = ["tomli (>=1.2.3)"] + +[[package]] +name = "pygments" +version = "2.18.0" +description = "Pygments is a syntax highlighting package written in Python." +optional = false +python-versions = ">=3.8" +files = [ + {file = "pygments-2.18.0-py3-none-any.whl", hash = "sha256:b8e6aca0523f3ab76fee51799c488e38782ac06eafcf95e7ba832985c8e7b13a"}, + {file = "pygments-2.18.0.tar.gz", hash = "sha256:786ff802f32e91311bff3889f6e9a86e81505fe99f2735bb6d60ae0c5004f199"}, +] + +[package.extras] +windows-terminal = ["colorama (>=0.4.6)"] + +[[package]] +name = "pylint" +version = "2.17.7" +description = "python code static checker" +optional = false +python-versions = ">=3.7.2" +files = [ + {file = "pylint-2.17.7-py3-none-any.whl", hash = "sha256:27a8d4c7ddc8c2f8c18aa0050148f89ffc09838142193fdbe98f172781a3ff87"}, + {file = "pylint-2.17.7.tar.gz", hash = "sha256:f4fcac7ae74cfe36bc8451e931d8438e4a476c20314b1101c458ad0f05191fad"}, +] + +[package.dependencies] +astroid = ">=2.15.8,<=2.17.0-dev0" +colorama = {version = ">=0.4.5", markers = "sys_platform == \"win32\""} +dill = [ + {version = ">=0.2", markers = "python_version < \"3.11\""}, + {version = ">=0.3.6", markers = "python_version >= \"3.11\""}, +] +isort = ">=4.2.5,<6" +mccabe = ">=0.6,<0.8" +platformdirs = ">=2.2.0" +tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} +tomlkit = ">=0.10.1" +typing-extensions = {version = ">=3.10.0", markers = "python_version < \"3.10\""} + +[package.extras] +spelling = ["pyenchant (>=3.2,<4.0)"] +testutils = ["gitpython (>3)"] + +[[package]] +name = "pyproject-api" +version = "1.8.0" +description = "API to interact with the python pyproject.toml based projects" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pyproject_api-1.8.0-py3-none-any.whl", hash = "sha256:3d7d347a047afe796fd5d1885b1e391ba29be7169bd2f102fcd378f04273d228"}, + {file = "pyproject_api-1.8.0.tar.gz", hash = "sha256:77b8049f2feb5d33eefcc21b57f1e279636277a8ac8ad6b5871037b243778496"}, +] + +[package.dependencies] +packaging = ">=24.1" +tomli = {version = ">=2.0.1", markers = "python_version < \"3.11\""} + +[package.extras] +docs = ["furo (>=2024.8.6)", "sphinx-autodoc-typehints (>=2.4.1)"] +testing = ["covdefaults (>=2.3)", "pytest (>=8.3.3)", "pytest-cov (>=5)", "pytest-mock (>=3.14)", "setuptools (>=75.1)"] + +[[package]] +name = "pytest" +version = "8.3.3" +description = "pytest: simple powerful testing with Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pytest-8.3.3-py3-none-any.whl", hash = "sha256:a6853c7375b2663155079443d2e45de913a911a11d669df02a50814944db57b2"}, + {file = "pytest-8.3.3.tar.gz", hash = "sha256:70b98107bd648308a7952b06e6ca9a50bc660be218d53c257cc1fc94fda10181"}, +] + +[package.dependencies] +colorama = {version = "*", markers = "sys_platform == \"win32\""} +exceptiongroup = {version = ">=1.0.0rc8", markers = "python_version < \"3.11\""} +iniconfig = "*" +packaging = "*" +pluggy = ">=1.5,<2" +tomli = {version = ">=1", markers = "python_version < \"3.11\""} + +[package.extras] +dev = ["argcomplete", "attrs (>=19.2)", "hypothesis (>=3.56)", "mock", "pygments (>=2.7.2)", "requests", "setuptools", "xmlschema"] + +[[package]] +name = "pytest-cov" +version = "5.0.0" +description = "Pytest plugin for measuring coverage." +optional = false +python-versions = ">=3.8" +files = [ + {file = "pytest-cov-5.0.0.tar.gz", hash = "sha256:5837b58e9f6ebd335b0f8060eecce69b662415b16dc503883a02f45dfeb14857"}, + {file = "pytest_cov-5.0.0-py3-none-any.whl", hash = "sha256:4f0764a1219df53214206bf1feea4633c3b558a2925c8b59f144f682861ce652"}, +] + +[package.dependencies] +coverage = {version = ">=5.2.1", extras = ["toml"]} +pytest = ">=4.6" + +[package.extras] +testing = ["fields", "hunter", "process-tests", "pytest-xdist", "virtualenv"] + +[[package]] +name = "pytest-html" +version = "4.1.1" +description = "pytest plugin for generating HTML reports" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pytest_html-4.1.1-py3-none-any.whl", hash = "sha256:c8152cea03bd4e9bee6d525573b67bbc6622967b72b9628dda0ea3e2a0b5dd71"}, + {file = "pytest_html-4.1.1.tar.gz", hash = "sha256:70a01e8ae5800f4a074b56a4cb1025c8f4f9b038bba5fe31e3c98eb996686f07"}, +] + +[package.dependencies] +jinja2 = ">=3.0.0" +pytest = ">=7.0.0" +pytest-metadata = ">=2.0.0" + +[package.extras] +docs = ["pip-tools (>=6.13.0)"] +test = ["assertpy (>=1.1)", "beautifulsoup4 (>=4.11.1)", "black (>=22.1.0)", "flake8 (>=4.0.1)", "pre-commit (>=2.17.0)", "pytest-mock (>=3.7.0)", "pytest-rerunfailures (>=11.1.2)", "pytest-xdist (>=2.4.0)", "selenium (>=4.3.0)", "tox (>=3.24.5)"] + +[[package]] +name = "pytest-metadata" +version = "3.1.1" +description = "pytest plugin for test session metadata" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pytest_metadata-3.1.1-py3-none-any.whl", hash = "sha256:c8e0844db684ee1c798cfa38908d20d67d0463ecb6137c72e91f418558dd5f4b"}, + {file = "pytest_metadata-3.1.1.tar.gz", hash = "sha256:d2a29b0355fbc03f168aa96d41ff88b1a3b44a3b02acbe491801c98a048017c8"}, +] + +[package.dependencies] +pytest = ">=7.0.0" + +[package.extras] +test = ["black (>=22.1.0)", "flake8 (>=4.0.1)", "pre-commit (>=2.17.0)", "tox (>=3.24.5)"] + +[[package]] +name = "pyupgrade" +version = "2.38.4" +description = "A tool to automatically upgrade syntax for newer versions." +optional = false +python-versions = ">=3.7" +files = [ + {file = "pyupgrade-2.38.4-py2.py3-none-any.whl", hash = "sha256:944ff993c396ddc2b9012eb3de4cda138eb4c149b22c6c560d4c8bfd0e180982"}, + {file = "pyupgrade-2.38.4.tar.gz", hash = "sha256:1eb43a49f416752929741ba4d706bf3f33593d3cac9bdc217fc1ef55c047c1f4"}, +] + +[package.dependencies] +tokenize-rt = "<5" + +[[package]] +name = "pyyaml" +version = "6.0.2" +description = "YAML parser and emitter for Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "PyYAML-6.0.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0a9a2848a5b7feac301353437eb7d5957887edbf81d56e903999a75a3d743086"}, + {file = "PyYAML-6.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:29717114e51c84ddfba879543fb232a6ed60086602313ca38cce623c1d62cfbf"}, + {file = "PyYAML-6.0.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8824b5a04a04a047e72eea5cec3bc266db09e35de6bdfe34c9436ac5ee27d237"}, + {file = "PyYAML-6.0.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7c36280e6fb8385e520936c3cb3b8042851904eba0e58d277dca80a5cfed590b"}, + {file = "PyYAML-6.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ec031d5d2feb36d1d1a24380e4db6d43695f3748343d99434e6f5f9156aaa2ed"}, + {file = "PyYAML-6.0.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:936d68689298c36b53b29f23c6dbb74de12b4ac12ca6cfe0e047bedceea56180"}, + {file = "PyYAML-6.0.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:23502f431948090f597378482b4812b0caae32c22213aecf3b55325e049a6c68"}, + {file = "PyYAML-6.0.2-cp310-cp310-win32.whl", hash = "sha256:2e99c6826ffa974fe6e27cdb5ed0021786b03fc98e5ee3c5bfe1fd5015f42b99"}, + {file = "PyYAML-6.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:a4d3091415f010369ae4ed1fc6b79def9416358877534caf6a0fdd2146c87a3e"}, + {file = "PyYAML-6.0.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:cc1c1159b3d456576af7a3e4d1ba7e6924cb39de8f67111c735f6fc832082774"}, + {file = "PyYAML-6.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1e2120ef853f59c7419231f3bf4e7021f1b936f6ebd222406c3b60212205d2ee"}, + {file = "PyYAML-6.0.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5d225db5a45f21e78dd9358e58a98702a0302f2659a3c6cd320564b75b86f47c"}, + {file = "PyYAML-6.0.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5ac9328ec4831237bec75defaf839f7d4564be1e6b25ac710bd1a96321cc8317"}, + {file = "PyYAML-6.0.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3ad2a3decf9aaba3d29c8f537ac4b243e36bef957511b4766cb0057d32b0be85"}, + {file = "PyYAML-6.0.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:ff3824dc5261f50c9b0dfb3be22b4567a6f938ccce4587b38952d85fd9e9afe4"}, + {file = "PyYAML-6.0.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:797b4f722ffa07cc8d62053e4cff1486fa6dc094105d13fea7b1de7d8bf71c9e"}, + {file = "PyYAML-6.0.2-cp311-cp311-win32.whl", hash = "sha256:11d8f3dd2b9c1207dcaf2ee0bbbfd5991f571186ec9cc78427ba5bd32afae4b5"}, + {file = "PyYAML-6.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:e10ce637b18caea04431ce14fabcf5c64a1c61ec9c56b071a4b7ca131ca52d44"}, + {file = "PyYAML-6.0.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:c70c95198c015b85feafc136515252a261a84561b7b1d51e3384e0655ddf25ab"}, + {file = "PyYAML-6.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ce826d6ef20b1bc864f0a68340c8b3287705cae2f8b4b1d932177dcc76721725"}, + {file = "PyYAML-6.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f71ea527786de97d1a0cc0eacd1defc0985dcf6b3f17bb77dcfc8c34bec4dc5"}, + {file = "PyYAML-6.0.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9b22676e8097e9e22e36d6b7bda33190d0d400f345f23d4065d48f4ca7ae0425"}, + {file = "PyYAML-6.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:80bab7bfc629882493af4aa31a4cfa43a4c57c83813253626916b8c7ada83476"}, + {file = "PyYAML-6.0.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:0833f8694549e586547b576dcfaba4a6b55b9e96098b36cdc7ebefe667dfed48"}, + {file = "PyYAML-6.0.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8b9c7197f7cb2738065c481a0461e50ad02f18c78cd75775628afb4d7137fb3b"}, + {file = "PyYAML-6.0.2-cp312-cp312-win32.whl", hash = "sha256:ef6107725bd54b262d6dedcc2af448a266975032bc85ef0172c5f059da6325b4"}, + {file = "PyYAML-6.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:7e7401d0de89a9a855c839bc697c079a4af81cf878373abd7dc625847d25cbd8"}, + {file = "PyYAML-6.0.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:efdca5630322a10774e8e98e1af481aad470dd62c3170801852d752aa7a783ba"}, + {file = "PyYAML-6.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:50187695423ffe49e2deacb8cd10510bc361faac997de9efef88badc3bb9e2d1"}, + {file = "PyYAML-6.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0ffe8360bab4910ef1b9e87fb812d8bc0a308b0d0eef8c8f44e0254ab3b07133"}, + {file = "PyYAML-6.0.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:17e311b6c678207928d649faa7cb0d7b4c26a0ba73d41e99c4fff6b6c3276484"}, + {file = "PyYAML-6.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:70b189594dbe54f75ab3a1acec5f1e3faa7e8cf2f1e08d9b561cb41b845f69d5"}, + {file = "PyYAML-6.0.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:41e4e3953a79407c794916fa277a82531dd93aad34e29c2a514c2c0c5fe971cc"}, + {file = "PyYAML-6.0.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:68ccc6023a3400877818152ad9a1033e3db8625d899c72eacb5a668902e4d652"}, + {file = "PyYAML-6.0.2-cp313-cp313-win32.whl", hash = "sha256:bc2fa7c6b47d6bc618dd7fb02ef6fdedb1090ec036abab80d4681424b84c1183"}, + {file = "PyYAML-6.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:8388ee1976c416731879ac16da0aff3f63b286ffdd57cdeb95f3f2e085687563"}, + {file = "PyYAML-6.0.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:24471b829b3bf607e04e88d79542a9d48bb037c2267d7927a874e6c205ca7e9a"}, + {file = "PyYAML-6.0.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d7fded462629cfa4b685c5416b949ebad6cec74af5e2d42905d41e257e0869f5"}, + {file = "PyYAML-6.0.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d84a1718ee396f54f3a086ea0a66d8e552b2ab2017ef8b420e92edbc841c352d"}, + {file = "PyYAML-6.0.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9056c1ecd25795207ad294bcf39f2db3d845767be0ea6e6a34d856f006006083"}, + {file = "PyYAML-6.0.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:82d09873e40955485746739bcb8b4586983670466c23382c19cffecbf1fd8706"}, + {file = "PyYAML-6.0.2-cp38-cp38-win32.whl", hash = "sha256:43fa96a3ca0d6b1812e01ced1044a003533c47f6ee8aca31724f78e93ccc089a"}, + {file = "PyYAML-6.0.2-cp38-cp38-win_amd64.whl", hash = "sha256:01179a4a8559ab5de078078f37e5c1a30d76bb88519906844fd7bdea1b7729ff"}, + {file = "PyYAML-6.0.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:688ba32a1cffef67fd2e9398a2efebaea461578b0923624778664cc1c914db5d"}, + {file = "PyYAML-6.0.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a8786accb172bd8afb8be14490a16625cbc387036876ab6ba70912730faf8e1f"}, + {file = "PyYAML-6.0.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d8e03406cac8513435335dbab54c0d385e4a49e4945d2909a581c83647ca0290"}, + {file = "PyYAML-6.0.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f753120cb8181e736c57ef7636e83f31b9c0d1722c516f7e86cf15b7aa57ff12"}, + {file = "PyYAML-6.0.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3b1fdb9dc17f5a7677423d508ab4f243a726dea51fa5e70992e59a7411c89d19"}, + {file = "PyYAML-6.0.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:0b69e4ce7a131fe56b7e4d770c67429700908fc0752af059838b1cfb41960e4e"}, + {file = "PyYAML-6.0.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:a9f8c2e67970f13b16084e04f134610fd1d374bf477b17ec1599185cf611d725"}, + {file = "PyYAML-6.0.2-cp39-cp39-win32.whl", hash = "sha256:6395c297d42274772abc367baaa79683958044e5d3835486c16da75d2a694631"}, + {file = "PyYAML-6.0.2-cp39-cp39-win_amd64.whl", hash = "sha256:39693e1f8320ae4f43943590b49779ffb98acb81f788220ea932a6b6c51004d8"}, + {file = "pyyaml-6.0.2.tar.gz", hash = "sha256:d584d9ec91ad65861cc08d42e834324ef890a082e591037abe114850ff7bbc3e"}, +] + +[[package]] +name = "requests" +version = "2.32.3" +description = "Python HTTP for Humans." +optional = false +python-versions = ">=3.8" +files = [ + {file = "requests-2.32.3-py3-none-any.whl", hash = "sha256:70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6"}, + {file = "requests-2.32.3.tar.gz", hash = "sha256:55365417734eb18255590a9ff9eb97e9e1da868d4ccd6402399eaf68af20a760"}, +] + +[package.dependencies] +certifi = ">=2017.4.17" +charset-normalizer = ">=2,<4" +idna = ">=2.5,<4" +urllib3 = ">=1.21.1,<3" + +[package.extras] +socks = ["PySocks (>=1.5.6,!=1.5.7)"] +use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"] + +[[package]] +name = "rich" +version = "10.16.2" +description = "Render rich text, tables, progress bars, syntax highlighting, markdown and more to the terminal" +optional = false +python-versions = ">=3.6.2,<4.0.0" +files = [ + {file = "rich-10.16.2-py3-none-any.whl", hash = "sha256:c59d73bd804c90f747c8d7b1d023b88f2a9ac2454224a4aeaf959b21eeb42d03"}, + {file = "rich-10.16.2.tar.gz", hash = "sha256:720974689960e06c2efdb54327f8bf0cdbdf4eae4ad73b6c94213cad405c371b"}, +] + +[package.dependencies] +colorama = ">=0.4.0,<0.5.0" +commonmark = ">=0.9.0,<0.10.0" +pygments = ">=2.6.0,<3.0.0" + +[package.extras] +jupyter = ["ipywidgets (>=7.5.1,<8.0.0)"] + +[[package]] +name = "ruamel-yaml" +version = "0.18.6" +description = "ruamel.yaml is a YAML parser/emitter that supports roundtrip preservation of comments, seq/map flow style, and map key order" +optional = false +python-versions = ">=3.7" +files = [ + {file = "ruamel.yaml-0.18.6-py3-none-any.whl", hash = "sha256:57b53ba33def16c4f3d807c0ccbc00f8a6081827e81ba2491691b76882d0c636"}, + {file = "ruamel.yaml-0.18.6.tar.gz", hash = "sha256:8b27e6a217e786c6fbe5634d8f3f11bc63e0f80f6a5890f28863d9c45aac311b"}, +] + +[package.dependencies] +"ruamel.yaml.clib" = {version = ">=0.2.7", markers = "platform_python_implementation == \"CPython\" and python_version < \"3.13\""} + +[package.extras] +docs = ["mercurial (>5.7)", "ryd"] +jinja2 = ["ruamel.yaml.jinja2 (>=0.2)"] + +[[package]] +name = "ruamel-yaml-clib" +version = "0.2.8" +description = "C version of reader, parser and emitter for ruamel.yaml derived from libyaml" +optional = false +python-versions = ">=3.6" +files = [ + {file = "ruamel.yaml.clib-0.2.8-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:b42169467c42b692c19cf539c38d4602069d8c1505e97b86387fcf7afb766e1d"}, + {file = "ruamel.yaml.clib-0.2.8-cp310-cp310-macosx_13_0_arm64.whl", hash = "sha256:07238db9cbdf8fc1e9de2489a4f68474e70dffcb32232db7c08fa61ca0c7c462"}, + {file = "ruamel.yaml.clib-0.2.8-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:fff3573c2db359f091e1589c3d7c5fc2f86f5bdb6f24252c2d8e539d4e45f412"}, + {file = "ruamel.yaml.clib-0.2.8-cp310-cp310-manylinux_2_24_aarch64.whl", hash = "sha256:aa2267c6a303eb483de8d02db2871afb5c5fc15618d894300b88958f729ad74f"}, + {file = "ruamel.yaml.clib-0.2.8-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:840f0c7f194986a63d2c2465ca63af8ccbbc90ab1c6001b1978f05119b5e7334"}, + {file = "ruamel.yaml.clib-0.2.8-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:024cfe1fc7c7f4e1aff4a81e718109e13409767e4f871443cbff3dba3578203d"}, + {file = "ruamel.yaml.clib-0.2.8-cp310-cp310-win32.whl", hash = "sha256:c69212f63169ec1cfc9bb44723bf2917cbbd8f6191a00ef3410f5a7fe300722d"}, + {file = "ruamel.yaml.clib-0.2.8-cp310-cp310-win_amd64.whl", hash = "sha256:cabddb8d8ead485e255fe80429f833172b4cadf99274db39abc080e068cbcc31"}, + {file = "ruamel.yaml.clib-0.2.8-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:bef08cd86169d9eafb3ccb0a39edb11d8e25f3dae2b28f5c52fd997521133069"}, + {file = "ruamel.yaml.clib-0.2.8-cp311-cp311-macosx_13_0_arm64.whl", hash = "sha256:b16420e621d26fdfa949a8b4b47ade8810c56002f5389970db4ddda51dbff248"}, + {file = "ruamel.yaml.clib-0.2.8-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:25c515e350e5b739842fc3228d662413ef28f295791af5e5110b543cf0b57d9b"}, + {file = "ruamel.yaml.clib-0.2.8-cp311-cp311-manylinux_2_24_aarch64.whl", hash = "sha256:1707814f0d9791df063f8c19bb51b0d1278b8e9a2353abbb676c2f685dee6afe"}, + {file = "ruamel.yaml.clib-0.2.8-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:46d378daaac94f454b3a0e3d8d78cafd78a026b1d71443f4966c696b48a6d899"}, + {file = "ruamel.yaml.clib-0.2.8-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:09b055c05697b38ecacb7ac50bdab2240bfca1a0c4872b0fd309bb07dc9aa3a9"}, + {file = "ruamel.yaml.clib-0.2.8-cp311-cp311-win32.whl", hash = "sha256:53a300ed9cea38cf5a2a9b069058137c2ca1ce658a874b79baceb8f892f915a7"}, + {file = "ruamel.yaml.clib-0.2.8-cp311-cp311-win_amd64.whl", hash = "sha256:c2a72e9109ea74e511e29032f3b670835f8a59bbdc9ce692c5b4ed91ccf1eedb"}, + {file = "ruamel.yaml.clib-0.2.8-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:ebc06178e8821efc9692ea7544aa5644217358490145629914d8020042c24aa1"}, + {file = "ruamel.yaml.clib-0.2.8-cp312-cp312-macosx_13_0_arm64.whl", hash = "sha256:edaef1c1200c4b4cb914583150dcaa3bc30e592e907c01117c08b13a07255ec2"}, + {file = "ruamel.yaml.clib-0.2.8-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d176b57452ab5b7028ac47e7b3cf644bcfdc8cacfecf7e71759f7f51a59e5c92"}, + {file = "ruamel.yaml.clib-0.2.8-cp312-cp312-manylinux_2_24_aarch64.whl", hash = "sha256:1dc67314e7e1086c9fdf2680b7b6c2be1c0d8e3a8279f2e993ca2a7545fecf62"}, + {file = "ruamel.yaml.clib-0.2.8-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:3213ece08ea033eb159ac52ae052a4899b56ecc124bb80020d9bbceeb50258e9"}, + {file = "ruamel.yaml.clib-0.2.8-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:aab7fd643f71d7946f2ee58cc88c9b7bfc97debd71dcc93e03e2d174628e7e2d"}, + {file = "ruamel.yaml.clib-0.2.8-cp312-cp312-win32.whl", hash = "sha256:5c365d91c88390c8d0a8545df0b5857172824b1c604e867161e6b3d59a827eaa"}, + {file = "ruamel.yaml.clib-0.2.8-cp312-cp312-win_amd64.whl", hash = "sha256:1758ce7d8e1a29d23de54a16ae867abd370f01b5a69e1a3ba75223eaa3ca1a1b"}, + {file = "ruamel.yaml.clib-0.2.8-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:a5aa27bad2bb83670b71683aae140a1f52b0857a2deff56ad3f6c13a017a26ed"}, + {file = "ruamel.yaml.clib-0.2.8-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:c58ecd827313af6864893e7af0a3bb85fd529f862b6adbefe14643947cfe2942"}, + {file = "ruamel.yaml.clib-0.2.8-cp37-cp37m-macosx_12_0_arm64.whl", hash = "sha256:f481f16baec5290e45aebdc2a5168ebc6d35189ae6fea7a58787613a25f6e875"}, + {file = "ruamel.yaml.clib-0.2.8-cp37-cp37m-manylinux_2_24_aarch64.whl", hash = "sha256:77159f5d5b5c14f7c34073862a6b7d34944075d9f93e681638f6d753606c6ce6"}, + {file = "ruamel.yaml.clib-0.2.8-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:7f67a1ee819dc4562d444bbafb135832b0b909f81cc90f7aa00260968c9ca1b3"}, + {file = "ruamel.yaml.clib-0.2.8-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:4ecbf9c3e19f9562c7fdd462e8d18dd902a47ca046a2e64dba80699f0b6c09b7"}, + {file = "ruamel.yaml.clib-0.2.8-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:87ea5ff66d8064301a154b3933ae406b0863402a799b16e4a1d24d9fbbcbe0d3"}, + {file = "ruamel.yaml.clib-0.2.8-cp37-cp37m-win32.whl", hash = "sha256:75e1ed13e1f9de23c5607fe6bd1aeaae21e523b32d83bb33918245361e9cc51b"}, + {file = "ruamel.yaml.clib-0.2.8-cp37-cp37m-win_amd64.whl", hash = "sha256:3f215c5daf6a9d7bbed4a0a4f760f3113b10e82ff4c5c44bec20a68c8014f675"}, + {file = "ruamel.yaml.clib-0.2.8-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1b617618914cb00bf5c34d4357c37aa15183fa229b24767259657746c9077615"}, + {file = "ruamel.yaml.clib-0.2.8-cp38-cp38-macosx_12_0_arm64.whl", hash = "sha256:a6a9ffd280b71ad062eae53ac1659ad86a17f59a0fdc7699fd9be40525153337"}, + {file = "ruamel.yaml.clib-0.2.8-cp38-cp38-manylinux_2_24_aarch64.whl", hash = "sha256:305889baa4043a09e5b76f8e2a51d4ffba44259f6b4c72dec8ca56207d9c6fe1"}, + {file = "ruamel.yaml.clib-0.2.8-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:700e4ebb569e59e16a976857c8798aee258dceac7c7d6b50cab63e080058df91"}, + {file = "ruamel.yaml.clib-0.2.8-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:e2b4c44b60eadec492926a7270abb100ef9f72798e18743939bdbf037aab8c28"}, + {file = "ruamel.yaml.clib-0.2.8-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:e79e5db08739731b0ce4850bed599235d601701d5694c36570a99a0c5ca41a9d"}, + {file = "ruamel.yaml.clib-0.2.8-cp38-cp38-win32.whl", hash = "sha256:955eae71ac26c1ab35924203fda6220f84dce57d6d7884f189743e2abe3a9fbe"}, + {file = "ruamel.yaml.clib-0.2.8-cp38-cp38-win_amd64.whl", hash = "sha256:56f4252222c067b4ce51ae12cbac231bce32aee1d33fbfc9d17e5b8d6966c312"}, + {file = "ruamel.yaml.clib-0.2.8-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:03d1162b6d1df1caa3a4bd27aa51ce17c9afc2046c31b0ad60a0a96ec22f8001"}, + {file = "ruamel.yaml.clib-0.2.8-cp39-cp39-macosx_12_0_arm64.whl", hash = "sha256:bba64af9fa9cebe325a62fa398760f5c7206b215201b0ec825005f1b18b9bccf"}, + {file = "ruamel.yaml.clib-0.2.8-cp39-cp39-manylinux_2_24_aarch64.whl", hash = "sha256:a1a45e0bb052edf6a1d3a93baef85319733a888363938e1fc9924cb00c8df24c"}, + {file = "ruamel.yaml.clib-0.2.8-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:da09ad1c359a728e112d60116f626cc9f29730ff3e0e7db72b9a2dbc2e4beed5"}, + {file = "ruamel.yaml.clib-0.2.8-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:184565012b60405d93838167f425713180b949e9d8dd0bbc7b49f074407c5a8b"}, + {file = "ruamel.yaml.clib-0.2.8-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:a75879bacf2c987c003368cf14bed0ffe99e8e85acfa6c0bfffc21a090f16880"}, + {file = "ruamel.yaml.clib-0.2.8-cp39-cp39-win32.whl", hash = "sha256:84b554931e932c46f94ab306913ad7e11bba988104c5cff26d90d03f68258cd5"}, + {file = "ruamel.yaml.clib-0.2.8-cp39-cp39-win_amd64.whl", hash = "sha256:25ac8c08322002b06fa1d49d1646181f0b2c72f5cbc15a85e80b4c30a544bb15"}, + {file = "ruamel.yaml.clib-0.2.8.tar.gz", hash = "sha256:beb2e0404003de9a4cab9753a8805a8fe9320ee6673136ed7f04255fe60bb512"}, +] + +[[package]] +name = "safety" +version = "2.3.4" +description = "Checks installed dependencies for known vulnerabilities and licenses." +optional = false +python-versions = "*" +files = [ + {file = "safety-2.3.4-py3-none-any.whl", hash = "sha256:6224dcd9b20986a2b2c5e7acfdfba6bca42bb11b2783b24ed04f32317e5167ea"}, + {file = "safety-2.3.4.tar.gz", hash = "sha256:b9e74e794e82f54d11f4091c5d820c4d2d81de9f953bf0b4f33ac8bc402ae72c"}, +] + +[package.dependencies] +Click = ">=8.0.2" +dparse = ">=0.6.2" +packaging = ">=21.0" +requests = "*" +"ruamel.yaml" = ">=0.17.21" +setuptools = ">=19.3" + +[package.extras] +github = ["jinja2 (>=3.1.0)", "pygithub (>=1.43.3)"] +gitlab = ["python-gitlab (>=1.3.0)"] + +[[package]] +name = "setuptools" +version = "75.1.0" +description = "Easily download, build, install, upgrade, and uninstall Python packages" +optional = false +python-versions = ">=3.8" +files = [ + {file = "setuptools-75.1.0-py3-none-any.whl", hash = "sha256:35ab7fd3bcd95e6b7fd704e4a1539513edad446c097797f2985e0e4b960772f2"}, + {file = "setuptools-75.1.0.tar.gz", hash = "sha256:d59a21b17a275fb872a9c3dae73963160ae079f1049ed956880cd7c09b120538"}, +] + +[package.extras] +check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1)", "ruff (>=0.5.2)"] +core = ["importlib-metadata (>=6)", "importlib-resources (>=5.10.2)", "jaraco.collections", "jaraco.functools", "jaraco.text (>=3.7)", "more-itertools", "more-itertools (>=8.8)", "packaging", "packaging (>=24)", "platformdirs (>=2.6.2)", "tomli (>=2.0.1)", "wheel (>=0.43.0)"] +cover = ["pytest-cov"] +doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "pyproject-hooks (!=1.1)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (>=1,<2)", "sphinx-reredirects", "sphinxcontrib-towncrier", "towncrier (<24.7)"] +enabler = ["pytest-enabler (>=2.2)"] +test = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "ini2toml[lite] (>=0.14)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "jaraco.test", "packaging (>=23.2)", "pip (>=19.1)", "pyproject-hooks (!=1.1)", "pytest (>=6,!=8.1.*)", "pytest-home (>=0.5)", "pytest-perf", "pytest-subprocess", "pytest-timeout", "pytest-xdist (>=3)", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel (>=0.44.0)"] +type = ["importlib-metadata (>=7.0.2)", "jaraco.develop (>=7.21)", "mypy (==1.11.*)", "pytest-mypy"] + +[[package]] +name = "shellingham" +version = "1.5.4" +description = "Tool to Detect Surrounding Shell" +optional = false +python-versions = ">=3.7" +files = [ + {file = "shellingham-1.5.4-py2.py3-none-any.whl", hash = "sha256:7ecfff8f2fd72616f7481040475a65b2bf8af90a56c89140852d1120324e8686"}, + {file = "shellingham-1.5.4.tar.gz", hash = "sha256:8dbca0739d487e5bd35ab3ca4b36e11c4078f3a234bfce294b0a0291363404de"}, +] + +[[package]] +name = "snowballstemmer" +version = "2.2.0" +description = "This package provides 29 stemmers for 28 languages generated from Snowball algorithms." +optional = false +python-versions = "*" +files = [ + {file = "snowballstemmer-2.2.0-py2.py3-none-any.whl", hash = "sha256:c8e1716e83cc398ae16824e5572ae04e0d9fc2c6b985fb0f900f5f0c96ecba1a"}, + {file = "snowballstemmer-2.2.0.tar.gz", hash = "sha256:09b16deb8547d3412ad7b590689584cd0fe25ec8db3be37788be3810cbf19cb1"}, +] + +[[package]] +name = "stevedore" +version = "5.3.0" +description = "Manage dynamic plugins for Python applications" +optional = false +python-versions = ">=3.8" +files = [ + {file = "stevedore-5.3.0-py3-none-any.whl", hash = "sha256:1efd34ca08f474dad08d9b19e934a22c68bb6fe416926479ba29e5013bcc8f78"}, + {file = "stevedore-5.3.0.tar.gz", hash = "sha256:9a64265f4060312828151c204efbe9b7a9852a0d9228756344dbc7e4023e375a"}, +] + +[package.dependencies] +pbr = ">=2.0.0" + +[[package]] +name = "tokenize-rt" +version = "4.2.1" +description = "A wrapper around the stdlib `tokenize` which roundtrips." +optional = false +python-versions = ">=3.6.1" +files = [ + {file = "tokenize_rt-4.2.1-py2.py3-none-any.whl", hash = "sha256:08a27fa032a81cf45e8858d0ac706004fcd523e8463415ddf1442be38e204ea8"}, + {file = "tokenize_rt-4.2.1.tar.gz", hash = "sha256:0d4f69026fed520f8a1e0103aa36c406ef4661417f20ca643f913e33531b3b94"}, +] + +[[package]] +name = "toml" +version = "0.10.2" +description = "Python Library for Tom's Obvious, Minimal Language" +optional = false +python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" +files = [ + {file = "toml-0.10.2-py2.py3-none-any.whl", hash = "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b"}, + {file = "toml-0.10.2.tar.gz", hash = "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f"}, +] + +[[package]] +name = "tomli" +version = "2.0.1" +description = "A lil' TOML parser" +optional = false +python-versions = ">=3.7" +files = [ + {file = "tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc"}, + {file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"}, +] + +[[package]] +name = "tomlkit" +version = "0.13.2" +description = "Style preserving TOML library" +optional = false +python-versions = ">=3.8" +files = [ + {file = "tomlkit-0.13.2-py3-none-any.whl", hash = "sha256:7a974427f6e119197f670fbbbeae7bef749a6c14e793db934baefc1b5f03efde"}, + {file = "tomlkit-0.13.2.tar.gz", hash = "sha256:fff5fe59a87295b278abd31bec92c15d9bc4a06885ab12bcea52c71119392e79"}, +] + +[[package]] +name = "tox" +version = "4.20.0" +description = "tox is a generic virtualenv management and test command line tool" +optional = false +python-versions = ">=3.8" +files = [ + {file = "tox-4.20.0-py3-none-any.whl", hash = "sha256:21a8005e3d3fe5658a8e36b8ca3ed13a4230429063c5cc2a2fdac6ee5aa0de34"}, + {file = "tox-4.20.0.tar.gz", hash = "sha256:5b78a49b6eaaeab3ae4186415e7c97d524f762ae967c63562687c3e5f0ec23d5"}, +] + +[package.dependencies] +cachetools = ">=5.5" +chardet = ">=5.2" +colorama = ">=0.4.6" +filelock = ">=3.15.4" +packaging = ">=24.1" +platformdirs = ">=4.2.2" +pluggy = ">=1.5" +pyproject-api = ">=1.7.1" +tomli = {version = ">=2.0.1", markers = "python_version < \"3.11\""} +virtualenv = ">=20.26.3" + +[package.extras] +docs = ["furo (>=2024.8.6)", "sphinx (>=8.0.2)", "sphinx-argparse-cli (>=1.17)", "sphinx-autodoc-typehints (>=2.4)", "sphinx-copybutton (>=0.5.2)", "sphinx-inline-tabs (>=2023.4.21)", "sphinxcontrib-towncrier (>=0.2.1a0)", "towncrier (>=24.8)"] +testing = ["build[virtualenv] (>=1.2.2)", "covdefaults (>=2.3)", "detect-test-pollution (>=1.2)", "devpi-process (>=1)", "diff-cover (>=9.1.1)", "distlib (>=0.3.8)", "flaky (>=3.8.1)", "hatch-vcs (>=0.4)", "hatchling (>=1.25)", "psutil (>=6)", "pytest (>=8.3.2)", "pytest-cov (>=5)", "pytest-mock (>=3.14)", "pytest-xdist (>=3.6.1)", "re-assert (>=1.1)", "setuptools (>=74.1.2)", "time-machine (>=2.15)", "wheel (>=0.44)"] + +[[package]] +name = "typer" +version = "0.4.2" +description = "Typer, build great CLIs. Easy to code. Based on Python type hints." +optional = false +python-versions = ">=3.6" +files = [ + {file = "typer-0.4.2-py3-none-any.whl", hash = "sha256:023bae00d1baf358a6cc7cea45851639360bb716de687b42b0a4641cd99173f1"}, + {file = "typer-0.4.2.tar.gz", hash = "sha256:b8261c6c0152dd73478b5ba96ba677e5d6948c715c310f7c91079f311f62ec03"}, +] + +[package.dependencies] +click = ">=7.1.1,<9.0.0" +colorama = {version = ">=0.4.3,<0.5.0", optional = true, markers = "extra == \"all\""} +shellingham = {version = ">=1.3.0,<2.0.0", optional = true, markers = "extra == \"all\""} + +[package.extras] +all = ["colorama (>=0.4.3,<0.5.0)", "shellingham (>=1.3.0,<2.0.0)"] +dev = ["autoflake (>=1.3.1,<2.0.0)", "flake8 (>=3.8.3,<4.0.0)", "pre-commit (>=2.17.0,<3.0.0)"] +doc = ["mdx-include (>=1.4.1,<2.0.0)", "mkdocs (>=1.1.2,<2.0.0)", "mkdocs-material (>=8.1.4,<9.0.0)"] +test = ["black (>=22.3.0,<23.0.0)", "coverage (>=5.2,<6.0)", "isort (>=5.0.6,<6.0.0)", "mypy (==0.910)", "pytest (>=4.4.0,<5.4.0)", "pytest-cov (>=2.10.0,<3.0.0)", "pytest-sugar (>=0.9.4,<0.10.0)", "pytest-xdist (>=1.32.0,<2.0.0)", "shellingham (>=1.3.0,<2.0.0)"] + +[[package]] +name = "typing-extensions" +version = "4.12.2" +description = "Backported and Experimental Type Hints for Python 3.8+" +optional = false +python-versions = ">=3.8" +files = [ + {file = "typing_extensions-4.12.2-py3-none-any.whl", hash = "sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d"}, + {file = "typing_extensions-4.12.2.tar.gz", hash = "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8"}, +] + +[[package]] +name = "urllib3" +version = "2.2.3" +description = "HTTP library with thread-safe connection pooling, file post, and more." +optional = false +python-versions = ">=3.8" +files = [ + {file = "urllib3-2.2.3-py3-none-any.whl", hash = "sha256:ca899ca043dcb1bafa3e262d73aa25c465bfb49e0bd9dd5d59f1d0acba2f8fac"}, + {file = "urllib3-2.2.3.tar.gz", hash = "sha256:e7d814a81dad81e6caf2ec9fdedb284ecc9c73076b62654547cc64ccdcae26e9"}, +] + +[package.extras] +brotli = ["brotli (>=1.0.9)", "brotlicffi (>=0.8.0)"] +h2 = ["h2 (>=4,<5)"] +socks = ["pysocks (>=1.5.6,!=1.5.7,<2.0)"] +zstd = ["zstandard (>=0.18.0)"] + +[[package]] +name = "virtualenv" +version = "20.26.5" +description = "Virtual Python Environment builder" +optional = false +python-versions = ">=3.7" +files = [ + {file = "virtualenv-20.26.5-py3-none-any.whl", hash = "sha256:4f3ac17b81fba3ce3bd6f4ead2749a72da5929c01774948e243db9ba41df4ff6"}, + {file = "virtualenv-20.26.5.tar.gz", hash = "sha256:ce489cac131aa58f4b25e321d6d186171f78e6cb13fafbf32a840cee67733ff4"}, +] + +[package.dependencies] +distlib = ">=0.3.7,<1" +filelock = ">=3.12.2,<4" +platformdirs = ">=3.9.1,<5" + +[package.extras] +docs = ["furo (>=2023.7.26)", "proselint (>=0.13)", "sphinx (>=7.1.2,!=7.3)", "sphinx-argparse (>=0.4)", "sphinxcontrib-towncrier (>=0.2.1a0)", "towncrier (>=23.6)"] +test = ["covdefaults (>=2.3)", "coverage (>=7.2.7)", "coverage-enable-subprocess (>=1)", "flaky (>=3.7)", "packaging (>=23.1)", "pytest (>=7.4)", "pytest-env (>=0.8.2)", "pytest-freezer (>=0.4.8)", "pytest-mock (>=3.11.1)", "pytest-randomly (>=3.12)", "pytest-timeout (>=2.1)", "setuptools (>=68)", "time-machine (>=2.10)"] + +[[package]] +name = "wrapt" +version = "1.16.0" +description = "Module for decorators, wrappers and monkey patching." +optional = false +python-versions = ">=3.6" +files = [ + {file = "wrapt-1.16.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ffa565331890b90056c01db69c0fe634a776f8019c143a5ae265f9c6bc4bd6d4"}, + {file = "wrapt-1.16.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e4fdb9275308292e880dcbeb12546df7f3e0f96c6b41197e0cf37d2826359020"}, + {file = "wrapt-1.16.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bb2dee3874a500de01c93d5c71415fcaef1d858370d405824783e7a8ef5db440"}, + {file = "wrapt-1.16.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2a88e6010048489cda82b1326889ec075a8c856c2e6a256072b28eaee3ccf487"}, + {file = "wrapt-1.16.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ac83a914ebaf589b69f7d0a1277602ff494e21f4c2f743313414378f8f50a4cf"}, + {file = "wrapt-1.16.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:73aa7d98215d39b8455f103de64391cb79dfcad601701a3aa0dddacf74911d72"}, + {file = "wrapt-1.16.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:807cc8543a477ab7422f1120a217054f958a66ef7314f76dd9e77d3f02cdccd0"}, + {file = "wrapt-1.16.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:bf5703fdeb350e36885f2875d853ce13172ae281c56e509f4e6eca049bdfb136"}, + {file = "wrapt-1.16.0-cp310-cp310-win32.whl", hash = "sha256:f6b2d0c6703c988d334f297aa5df18c45e97b0af3679bb75059e0e0bd8b1069d"}, + {file = "wrapt-1.16.0-cp310-cp310-win_amd64.whl", hash = "sha256:decbfa2f618fa8ed81c95ee18a387ff973143c656ef800c9f24fb7e9c16054e2"}, + {file = "wrapt-1.16.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:1a5db485fe2de4403f13fafdc231b0dbae5eca4359232d2efc79025527375b09"}, + {file = "wrapt-1.16.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:75ea7d0ee2a15733684badb16de6794894ed9c55aa5e9903260922f0482e687d"}, + {file = "wrapt-1.16.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a452f9ca3e3267cd4d0fcf2edd0d035b1934ac2bd7e0e57ac91ad6b95c0c6389"}, + {file = "wrapt-1.16.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:43aa59eadec7890d9958748db829df269f0368521ba6dc68cc172d5d03ed8060"}, + {file = "wrapt-1.16.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:72554a23c78a8e7aa02abbd699d129eead8b147a23c56e08d08dfc29cfdddca1"}, + {file = "wrapt-1.16.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:d2efee35b4b0a347e0d99d28e884dfd82797852d62fcd7ebdeee26f3ceb72cf3"}, + {file = "wrapt-1.16.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:6dcfcffe73710be01d90cae08c3e548d90932d37b39ef83969ae135d36ef3956"}, + {file = "wrapt-1.16.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:eb6e651000a19c96f452c85132811d25e9264d836951022d6e81df2fff38337d"}, + {file = "wrapt-1.16.0-cp311-cp311-win32.whl", hash = "sha256:66027d667efe95cc4fa945af59f92c5a02c6f5bb6012bff9e60542c74c75c362"}, + {file = "wrapt-1.16.0-cp311-cp311-win_amd64.whl", hash = "sha256:aefbc4cb0a54f91af643660a0a150ce2c090d3652cf4052a5397fb2de549cd89"}, + {file = "wrapt-1.16.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:5eb404d89131ec9b4f748fa5cfb5346802e5ee8836f57d516576e61f304f3b7b"}, + {file = "wrapt-1.16.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:9090c9e676d5236a6948330e83cb89969f433b1943a558968f659ead07cb3b36"}, + {file = "wrapt-1.16.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:94265b00870aa407bd0cbcfd536f17ecde43b94fb8d228560a1e9d3041462d73"}, + {file = "wrapt-1.16.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f2058f813d4f2b5e3a9eb2eb3faf8f1d99b81c3e51aeda4b168406443e8ba809"}, + {file = "wrapt-1.16.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:98b5e1f498a8ca1858a1cdbffb023bfd954da4e3fa2c0cb5853d40014557248b"}, + {file = "wrapt-1.16.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:14d7dc606219cdd7405133c713f2c218d4252f2a469003f8c46bb92d5d095d81"}, + {file = "wrapt-1.16.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:49aac49dc4782cb04f58986e81ea0b4768e4ff197b57324dcbd7699c5dfb40b9"}, + {file = "wrapt-1.16.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:418abb18146475c310d7a6dc71143d6f7adec5b004ac9ce08dc7a34e2babdc5c"}, + {file = "wrapt-1.16.0-cp312-cp312-win32.whl", hash = "sha256:685f568fa5e627e93f3b52fda002c7ed2fa1800b50ce51f6ed1d572d8ab3e7fc"}, + {file = "wrapt-1.16.0-cp312-cp312-win_amd64.whl", hash = "sha256:dcdba5c86e368442528f7060039eda390cc4091bfd1dca41e8046af7c910dda8"}, + {file = "wrapt-1.16.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:d462f28826f4657968ae51d2181a074dfe03c200d6131690b7d65d55b0f360f8"}, + {file = "wrapt-1.16.0-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a33a747400b94b6d6b8a165e4480264a64a78c8a4c734b62136062e9a248dd39"}, + {file = "wrapt-1.16.0-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b3646eefa23daeba62643a58aac816945cadc0afaf21800a1421eeba5f6cfb9c"}, + {file = "wrapt-1.16.0-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3ebf019be5c09d400cf7b024aa52b1f3aeebeff51550d007e92c3c1c4afc2a40"}, + {file = "wrapt-1.16.0-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:0d2691979e93d06a95a26257adb7bfd0c93818e89b1406f5a28f36e0d8c1e1fc"}, + {file = "wrapt-1.16.0-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:1acd723ee2a8826f3d53910255643e33673e1d11db84ce5880675954183ec47e"}, + {file = "wrapt-1.16.0-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:bc57efac2da352a51cc4658878a68d2b1b67dbe9d33c36cb826ca449d80a8465"}, + {file = "wrapt-1.16.0-cp36-cp36m-win32.whl", hash = "sha256:da4813f751142436b075ed7aa012a8778aa43a99f7b36afe9b742d3ed8bdc95e"}, + {file = "wrapt-1.16.0-cp36-cp36m-win_amd64.whl", hash = "sha256:6f6eac2360f2d543cc875a0e5efd413b6cbd483cb3ad7ebf888884a6e0d2e966"}, + {file = "wrapt-1.16.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:a0ea261ce52b5952bf669684a251a66df239ec6d441ccb59ec7afa882265d593"}, + {file = "wrapt-1.16.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7bd2d7ff69a2cac767fbf7a2b206add2e9a210e57947dd7ce03e25d03d2de292"}, + {file = "wrapt-1.16.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9159485323798c8dc530a224bd3ffcf76659319ccc7bbd52e01e73bd0241a0c5"}, + {file = "wrapt-1.16.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a86373cf37cd7764f2201b76496aba58a52e76dedfaa698ef9e9688bfd9e41cf"}, + {file = "wrapt-1.16.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:73870c364c11f03ed072dda68ff7aea6d2a3a5c3fe250d917a429c7432e15228"}, + {file = "wrapt-1.16.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:b935ae30c6e7400022b50f8d359c03ed233d45b725cfdd299462f41ee5ffba6f"}, + {file = "wrapt-1.16.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:db98ad84a55eb09b3c32a96c576476777e87c520a34e2519d3e59c44710c002c"}, + {file = "wrapt-1.16.0-cp37-cp37m-win32.whl", hash = "sha256:9153ed35fc5e4fa3b2fe97bddaa7cbec0ed22412b85bcdaf54aeba92ea37428c"}, + {file = "wrapt-1.16.0-cp37-cp37m-win_amd64.whl", hash = "sha256:66dfbaa7cfa3eb707bbfcd46dab2bc6207b005cbc9caa2199bcbc81d95071a00"}, + {file = "wrapt-1.16.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1dd50a2696ff89f57bd8847647a1c363b687d3d796dc30d4dd4a9d1689a706f0"}, + {file = "wrapt-1.16.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:44a2754372e32ab315734c6c73b24351d06e77ffff6ae27d2ecf14cf3d229202"}, + {file = "wrapt-1.16.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8e9723528b9f787dc59168369e42ae1c3b0d3fadb2f1a71de14531d321ee05b0"}, + {file = "wrapt-1.16.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:dbed418ba5c3dce92619656802cc5355cb679e58d0d89b50f116e4a9d5a9603e"}, + {file = "wrapt-1.16.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:941988b89b4fd6b41c3f0bfb20e92bd23746579736b7343283297c4c8cbae68f"}, + {file = "wrapt-1.16.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:6a42cd0cfa8ffc1915aef79cb4284f6383d8a3e9dcca70c445dcfdd639d51267"}, + {file = "wrapt-1.16.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:1ca9b6085e4f866bd584fb135a041bfc32cab916e69f714a7d1d397f8c4891ca"}, + {file = "wrapt-1.16.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:d5e49454f19ef621089e204f862388d29e6e8d8b162efce05208913dde5b9ad6"}, + {file = "wrapt-1.16.0-cp38-cp38-win32.whl", hash = "sha256:c31f72b1b6624c9d863fc095da460802f43a7c6868c5dda140f51da24fd47d7b"}, + {file = "wrapt-1.16.0-cp38-cp38-win_amd64.whl", hash = "sha256:490b0ee15c1a55be9c1bd8609b8cecd60e325f0575fc98f50058eae366e01f41"}, + {file = "wrapt-1.16.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:9b201ae332c3637a42f02d1045e1d0cccfdc41f1f2f801dafbaa7e9b4797bfc2"}, + {file = "wrapt-1.16.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:2076fad65c6736184e77d7d4729b63a6d1ae0b70da4868adeec40989858eb3fb"}, + {file = "wrapt-1.16.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c5cd603b575ebceca7da5a3a251e69561bec509e0b46e4993e1cac402b7247b8"}, + {file = "wrapt-1.16.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b47cfad9e9bbbed2339081f4e346c93ecd7ab504299403320bf85f7f85c7d46c"}, + {file = "wrapt-1.16.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f8212564d49c50eb4565e502814f694e240c55551a5f1bc841d4fcaabb0a9b8a"}, + {file = "wrapt-1.16.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:5f15814a33e42b04e3de432e573aa557f9f0f56458745c2074952f564c50e664"}, + {file = "wrapt-1.16.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:db2e408d983b0e61e238cf579c09ef7020560441906ca990fe8412153e3b291f"}, + {file = "wrapt-1.16.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:edfad1d29c73f9b863ebe7082ae9321374ccb10879eeabc84ba3b69f2579d537"}, + {file = "wrapt-1.16.0-cp39-cp39-win32.whl", hash = "sha256:ed867c42c268f876097248e05b6117a65bcd1e63b779e916fe2e33cd6fd0d3c3"}, + {file = "wrapt-1.16.0-cp39-cp39-win_amd64.whl", hash = "sha256:eb1b046be06b0fce7249f1d025cd359b4b80fc1c3e24ad9eca33e0dcdb2e4a35"}, + {file = "wrapt-1.16.0-py3-none-any.whl", hash = "sha256:6906c4100a8fcbf2fa735f6059214bb13b97f75b1a61777fcf6432121ef12ef1"}, + {file = "wrapt-1.16.0.tar.gz", hash = "sha256:5f370f952971e7d17c7d1ead40e49f32345a7f7a5373571ef44d800d06b1899d"}, +] + +[metadata] +lock-version = "2.0" +python-versions = "^3.8" +content-hash = "aac9123f3fa544b8c3e9b085f41f5a1c6c4ed2d59ce3236dcda6ea2aef5a694c" diff --git a/tools/tlc/pyproject.toml b/tools/tlc/pyproject.toml new file mode 100644 index 00000000..b6062384 --- /dev/null +++ b/tools/tlc/pyproject.toml @@ -0,0 +1,151 @@ +# Poetry pyproject.toml: https://python-poetry.org/docs/pyproject/ +[build-system] +requires = ["poetry_core>=1.0.0"] +build-backend = "poetry.core.masonry.api" + +[tool.poetry] +name = "tlc" +version = "0.9.0" +description = "Transfer List Compiler (TLC) is a Python-based CLI for efficiently handling transfer lists." +authors = ["Arm Ltd <tf-a@lists.trustedfirmware.org>"] +license = "BSD-3" +repository = "https://git.trustedfirmware.org/TF-A/trusted-firmware-a.git/" +homepage = "https://trustedfirmware-a.readthedocs.io/en/latest/index.html" + +# Keywords description https://python-poetry.org/docs/pyproject/#keywords +keywords = [] #! Update me + +# Pypi classifiers: https://pypi.org/classifiers/ +classifiers = [ + "Development Status :: 3 - Alpha", + "Intended Audience :: Developers", + "Operating System :: OS Independent", + "Topic :: Software Development :: Libraries :: Python Modules", + "License :: OSI Approved :: BSD License", + "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3.8", + "Programming Language :: Python :: 3.9", +] + +[tool.poetry.scripts] +# Entry points for the package https://python-poetry.org/docs/pyproject/#scripts +"tlc" = "tlc.__main__:cli" + +[tool.poetry.dependencies] +python = "^3.8" + +typer = {extras = ["all"], version = "^0.4.0"} +rich = "^10.14.0" +click = "^8.1.7" +pyyaml = "^6.0.1" +tox = "^4.18.0" +jinja2 = "^3.1.4" + +[tool.poetry.group.dev] +optional = true + +[tool.poetry.group.dev.dependencies] +bandit = "^1.7.1" +tox = "^4.18.0" +darglint = "^1.8.1" +black = "^24.4.2" +isort = {extras = ["colors"], version = "^5.10.1"} +mypy = "^0.910" +mypy-extensions = "^0.4.3" +pre-commit = "^2.15.0" +pydocstyle = "^6.1.1" +pylint = "^2.11.1" +pytest = "^8.0.0" +pyupgrade = "^2.29.1" +safety = "^2.2.0" +coverage = "^6.1.2" +coverage-badge = "^1.1.0" +pytest-html = "^4.1.1" +pytest-cov = "5.0.0" + +[tool.black] +# https://github.com/psf/black +target-version = ["py38"] +line-length = 88 +color = true + +exclude = ''' +/( + \.git + | \.hg + | \.mypy_cache + | \.tox + | \.venv + | _build + | buck-out + | build + | dist + | env + | venv +)/ +''' + +[tool.isort] +# https://github.com/timothycrosley/isort/ +py_version = 38 +line_length = 88 + +known_typing = ["typing", "types", "typing_extensions", "mypy", "mypy_extensions"] +sections = ["FUTURE", "TYPING", "STDLIB", "THIRDPARTY", "FIRSTPARTY", "LOCALFOLDER"] +include_trailing_comma = true +profile = "black" +multi_line_output = 3 +indent = 4 +color_output = true + +[tool.mypy] +# https://mypy.readthedocs.io/en/latest/config_file.html#using-a-pyproject-toml-file +python_version = 3.8 +pretty = true +show_traceback = true +color_output = true + +allow_redefinition = false +check_untyped_defs = true +disallow_any_generics = true +disallow_incomplete_defs = true +ignore_missing_imports = true +implicit_reexport = false +no_implicit_optional = true +show_column_numbers = true +show_error_codes = true +show_error_context = true +strict_equality = true +strict_optional = true +warn_no_return = true +warn_redundant_casts = true +warn_return_any = true +warn_unreachable = true +warn_unused_configs = true +warn_unused_ignores = true + + +[tool.pytest.ini_options] +# https://docs.pytest.org/en/6.2.x/customize.html#pyproject-toml +# Directories that are not visited by pytest collector: +norecursedirs =["hooks", "*.egg", ".eggs", "dist", "build", "docs", ".tox", ".git", "__pycache__"] +doctest_optionflags = ["NUMBER", "NORMALIZE_WHITESPACE", "IGNORE_EXCEPTION_DETAIL"] + +# Extra options: +addopts = [ + "--strict-markers", + "--tb=short", + "--doctest-modules", + "--doctest-continue-on-failure", +] + +[tool.coverage.run] +source = ["tests"] +branch = true + +[tool.coverage.paths] +source = ["tlc"] + +[tool.coverage.report] +fail_under = 50 +show_missing = true diff --git a/tools/tlc/setup.cfg b/tools/tlc/setup.cfg new file mode 100644 index 00000000..3c46a08c --- /dev/null +++ b/tools/tlc/setup.cfg @@ -0,0 +1,4 @@ +[darglint] +# https://github.com/terrencepreilly/darglint +strictness = long +docstring_style = google diff --git a/tools/tlc/tests/conftest.py b/tools/tlc/tests/conftest.py new file mode 100644 index 00000000..b8f88b50 --- /dev/null +++ b/tools/tlc/tests/conftest.py @@ -0,0 +1,72 @@ +#!/usr/bin/env python3 +# type: ignore[attr-defined] + +# +# Copyright (c) 2024, Arm Limited. All rights reserved. +# +# SPDX-License-Identifier: BSD-3-Clause +# + +""" Common configurations and fixtures for test environment.""" + +import pytest +import yaml +from click.testing import CliRunner + +from tlc.cli import cli + + +@pytest.fixture +def tmptlstr(tmpdir): + return tmpdir.join("tl.bin").strpath + + +@pytest.fixture +def tmpyamlconfig(tmpdir): + return tmpdir.join("config.yaml").strpath + + +@pytest.fixture +def tmpfdt(tmpdir): + fdt = tmpdir.join("fdt.dtb") + fdt.write_binary(b"\x00" * 100) + return fdt + + +@pytest.fixture(params=[1, 2, 3, 4, 5, 0x100, 0x101, 0x102, 0x104]) +def non_empty_tag_id(request): + return request.param + + +@pytest.fixture +def tmpyamlconfig_blob_file(tmpdir, tmpfdt, non_empty_tag_id): + config_path = tmpdir.join("config.yaml") + + config = { + "has_checksum": True, + "max_size": 0x1000, + "entries": [ + { + "tag_id": non_empty_tag_id, + "blob_file_path": tmpfdt.strpath, + }, + ], + } + + with open(config_path, "w") as f: + yaml.safe_dump(config, f) + + return config_path + + +@pytest.fixture +def tlcrunner(tmptlstr): + runner = CliRunner() + with runner.isolated_filesystem(): + runner.invoke(cli, ["create", tmptlstr]) + return runner + + +@pytest.fixture +def tlc_entries(tmpfdt): + return [(0, "/dev/null"), (1, tmpfdt.strpath), (0x102, tmpfdt.strpath)] diff --git a/tools/tlc/tests/test_cli.py b/tools/tlc/tests/test_cli.py new file mode 100644 index 00000000..a5ef30ee --- /dev/null +++ b/tools/tlc/tests/test_cli.py @@ -0,0 +1,476 @@ +#!/usr/bin/env python3 +# type: ignore[attr-defined] + +# +# Copyright (c) 2024, Arm Limited. All rights reserved. +# +# SPDX-License-Identifier: BSD-3-Clause +# + +"""Contains unit tests for the CLI functionality.""" + +from math import ceil, log2 +from pathlib import Path +from re import findall, search +from unittest import mock + +import pytest +import yaml +from click.testing import CliRunner + +from tlc.cli import cli +from tlc.te import TransferEntry +from tlc.tl import TransferList + + +def test_create_empty_tl(tmpdir): + runner = CliRunner() + test_file = tmpdir.join("tl.bin") + + result = runner.invoke(cli, ["create", test_file.strpath]) + assert result.exit_code == 0 + assert TransferList.fromfile(test_file) is not None + + +def test_create_with_fdt(tmpdir): + runner = CliRunner() + fdt = tmpdir.join("fdt.dtb") + fdt.write_binary(b"\x00" * 100) + + result = runner.invoke( + cli, + [ + "create", + "--fdt", + fdt.strpath, + "--size", + "1000", + tmpdir.join("tl.bin").strpath, + ], + ) + assert result.exit_code == 0 + + +def test_add_single_entry(tlcrunner, tmptlstr): + tlcrunner.invoke(cli, ["add", "--entry", "0", "/dev/null", tmptlstr]) + + tl = TransferList.fromfile(tmptlstr) + assert tl is not None + assert len(tl.entries) == 1 + assert tl.entries[0].id == 0 + + +def test_add_multiple_entries(tlcrunner, tlc_entries, tmptlstr): + for id, path in tlc_entries: + tlcrunner.invoke(cli, ["add", "--entry", id, path, tmptlstr]) + + tl = TransferList.fromfile(tmptlstr) + assert tl is not None + assert len(tl.entries) == len(tlc_entries) + + +def test_info(tlcrunner, tmptlstr, tmpfdt): + tlcrunner.invoke(cli, ["add", "--entry", "0", "/dev/null", tmptlstr]) + tlcrunner.invoke(cli, ["add", "--fdt", tmpfdt.strpath, tmptlstr]) + + result = tlcrunner.invoke(cli, ["info", tmptlstr]) + assert result.exit_code == 0 + assert "signature" in result.stdout + assert "id" in result.stdout + + result = tlcrunner.invoke(cli, ["info", "--header", tmptlstr]) + assert result.exit_code == 0 + assert "signature" in result.stdout + assert "id" not in result.stdout + + result = tlcrunner.invoke(cli, ["info", "--entries", tmptlstr]) + assert result.exit_code == 0 + assert "signature" not in result.stdout + assert "id" in result.stdout + + +def test_raises_max_size_error(tmptlstr, tmpfdt): + tmpfdt.write_binary(bytes(6000)) + + runner = CliRunner() + result = runner.invoke(cli, ["create", "--fdt", tmpfdt, tmptlstr]) + + assert result.exception + assert isinstance(result.exception, MemoryError) + assert "TL max size exceeded, consider increasing with the option -s" in str( + result.exception + ) + assert "TL size has exceeded the maximum allocation" in str( + result.exception.__cause__ + ) + + +def test_info_get_fdt_offset(tmptlstr, tmpfdt): + runner = CliRunner() + with runner.isolated_filesystem(): + runner.invoke(cli, ["create", "--size", "1000", tmptlstr]) + runner.invoke(cli, ["add", "--entry", "1", tmpfdt.strpath, tmptlstr]) + result = runner.invoke(cli, ["info", "--fdt-offset", tmptlstr]) + + assert result.exit_code == 0 + assert result.output.strip("\n").isdigit() + + +def test_remove_tag(tlcrunner, tmptlstr): + tlcrunner.invoke(cli, ["add", "--entry", "0", "/dev/null", tmptlstr]) + result = tlcrunner.invoke(cli, ["info", tmptlstr]) + + assert result.exit_code == 0 + assert "signature" in result.stdout + + tlcrunner.invoke(cli, ["remove", "--tags", "0", tmptlstr]) + tl = TransferList.fromfile(tmptlstr) + + assert result.exit_code == 0 + assert len(tl.entries) == 0 + + +def test_unpack_tl(tlcrunner, tmptlstr, tmpfdt, tmpdir): + with tlcrunner.isolated_filesystem(temp_dir=tmpdir): + tlcrunner.invoke(cli, ["add", "--entry", 1, tmpfdt.strpath, tmptlstr]) + tlcrunner.invoke(cli, ["unpack", tmptlstr]) + assert Path("te_0_1.bin").exists() + + +def test_unpack_multiple_tes(tlcrunner, tlc_entries, tmptlstr, tmpdir): + with tlcrunner.isolated_filesystem(temp_dir=tmpdir): + for id, path in tlc_entries: + tlcrunner.invoke(cli, ["add", "--entry", id, path, tmptlstr]) + + assert all( + filter( + lambda te: (Path(tmpdir.strpath) / f"te_{te[0]}.bin").exists(), tlc_entries + ) + ) + + +def test_unpack_into_dir(tlcrunner, tmpdir, tmptlstr, tmpfdt): + tlcrunner.invoke(cli, ["add", "--entry", 1, tmpfdt.strpath, tmptlstr]) + tlcrunner.invoke(cli, ["unpack", "-C", tmpdir.strpath, tmptlstr]) + + assert (Path(tmpdir.strpath) / "te_0_1.bin").exists() + + +def test_unpack_into_dir_with_conflicting_tags(tlcrunner, tmpdir, tmptlstr, tmpfdt): + tlcrunner.invoke(cli, ["add", "--entry", 1, tmpfdt.strpath, tmptlstr]) + tlcrunner.invoke(cli, ["add", "--entry", 1, tmpfdt.strpath, tmptlstr]) + tlcrunner.invoke(cli, ["unpack", "-C", tmpdir.strpath, tmptlstr]) + + assert (Path(tmpdir.strpath) / "te_0_1.bin").exists() + assert (Path(tmpdir.strpath) / "te_1_1.bin").exists() + + +def test_validate_invalid_signature(tmptlstr, tlcrunner, monkeypatch): + tl = TransferList() + tl.signature = 0xDEADBEEF + + mock_open = lambda tmptlstr, mode: mock.mock_open(read_data=tl.header_to_bytes())() + monkeypatch.setattr("builtins.open", mock_open) + + result = tlcrunner.invoke(cli, ["validate", tmptlstr]) + assert result.exit_code != 0 + + +def test_validate_misaligned_entries(tmptlstr, tlcrunner, monkeypatch): + """Base address of a TE must be 8-byte aligned.""" + mock_open = lambda tmptlstr, mode: mock.mock_open( + read_data=TransferList().header_to_bytes() + + bytes(5) + + TransferEntry(0, 0, bytes(0)).header_to_bytes + )() + monkeypatch.setattr("builtins.open", mock_open) + + result = tlcrunner.invoke(cli, ["validate", tmptlstr]) + + assert result.exit_code == 1 + + +@pytest.mark.parametrize( + "version", [0, TransferList.version, TransferList.version + 1, 1 << 8] +) +def test_validate_unsupported_version(version, tmptlstr, tlcrunner, monkeypatch): + tl = TransferList() + tl.version = version + + mock_open = lambda tmptlstr, mode: mock.mock_open(read_data=tl.header_to_bytes())() + monkeypatch.setattr("builtins.open", mock_open) + + result = tlcrunner.invoke(cli, ["validate", tmptlstr]) + + if version >= TransferList.version and version <= 0xFF: + assert result.exit_code == 0 + else: + assert result.exit_code == 1 + + +def test_create_entry_from_yaml_and_blob_file( + tlcrunner, tmpyamlconfig_blob_file, tmptlstr, non_empty_tag_id +): + tlcrunner.invoke( + cli, + [ + "create", + "--from-yaml", + tmpyamlconfig_blob_file.strpath, + tmptlstr, + ], + ) + + tl = TransferList.fromfile(tmptlstr) + assert tl is not None + assert len(tl.entries) == 1 + assert tl.entries[0].id == non_empty_tag_id + + +@pytest.mark.parametrize( + "entry", + [ + {"tag_id": 0}, + { + "tag_id": 0x104, + "addr": 0x0400100000000010, + "size": 0x0003300000000000, + }, + { + "tag_id": 0x100, + "pp_addr": 100, + }, + { + "tag_id": "optee_pageable_part", + "pp_addr": 100, + }, + ], +) +def test_create_from_yaml_check_sum_bytes(tlcrunner, tmpyamlconfig, tmptlstr, entry): + """Test creating a TL from a yaml file, but only check that the sum of the + data in the yaml file matches the sum of the data in the TL. This means + you don't have to type the exact sequence of expected bytes. All the data + in the yaml file must be integers (except for the tag IDs, which can be + strings). + """ + # create yaml config file + config = { + "has_checksum": True, + "max_size": 0x1000, + "entries": [entry], + } + with open(tmpyamlconfig, "w") as f: + yaml.safe_dump(config, f) + + # invoke TLC + tlcrunner.invoke( + cli, + [ + "create", + "--from-yaml", + tmpyamlconfig, + tmptlstr, + ], + ) + + # open created TL, and check + tl = TransferList.fromfile(tmptlstr) + assert tl is not None + assert len(tl.entries) == 1 + + # Check that the sum of all the data in the transfer entry in the yaml file + # is the same as the sum of all the data in the transfer list. Don't count + # the tag id or the TE headers. + + # every item in the entry dict must be an integer + yaml_total = 0 + for key, data in iter_nested_dict(entry): + if key != "tag_id": + num_bytes = ceil(log2(data + 1) / 8) + yaml_total += sum(data.to_bytes(num_bytes, "little")) + + tl_total = sum(tl.entries[0].data) + + assert tl_total == yaml_total + + +@pytest.mark.parametrize( + "entry,expected", + [ + ( + { + "tag_id": 0x102, + "ep_info": { + "h": { + "type": 0x01, + "version": 0x02, + "attr": 8, + }, + "pc": 67239936, + "spsr": 965, + "args": [67112976, 67112960, 0, 0, 0, 0, 0, 0], + }, + }, + ( + "0x00580201 0x00000008 0x04020000 0x00000000 " + "0x000003C5 0x00000000 0x04001010 0x00000000 " + "0x04001000 0x00000000 0x00000000 0x00000000 " + "0x00000000 0x00000000 0x00000000 0x00000000 " + "0x00000000 0x00000000 0x00000000 0x00000000 " + "0x00000000 0x00000000" + ), + ), + ( + { + "tag_id": 0x102, + "ep_info": { + "h": { + "type": 0x01, + "version": 0x02, + "attr": "EP_NON_SECURE | EP_ST_ENABLE", + }, + "pc": 67239936, + "spsr": 965, + "args": [67112976, 67112960, 0, 0, 0, 0, 0, 0], + }, + }, + ( + "0x00580201 0x00000005 0x04020000 0x00000000 " + "0x000003C5 0x00000000 0x04001010 0x00000000 " + "0x04001000 0x00000000 0x00000000 0x00000000 " + "0x00000000 0x00000000 0x00000000 0x00000000 " + "0x00000000 0x00000000 0x00000000 0x00000000 " + "0x00000000 0x00000000" + ), + ), + ], +) +def test_create_from_yaml_check_exact_data( + tlcrunner, tmpyamlconfig, tmptlstr, entry, expected +): + """Test creating a TL from a yaml file, checking the exact sequence of + bytes. This is useful for checking that the alignment is correct. You can + get the expected sequence of bytes by copying it from the ArmDS debugger. + """ + # create yaml config file + config = { + "has_checksum": True, + "max_size": 0x1000, + "entries": [entry], + } + with open(tmpyamlconfig, "w") as f: + yaml.safe_dump(config, f) + + # invoke TLC + tlcrunner.invoke( + cli, + [ + "create", + "--from-yaml", + tmpyamlconfig, + tmptlstr, + ], + ) + + # open TL and check + tl = TransferList.fromfile(tmptlstr) + assert tl is not None + assert len(tl.entries) == 1 + + # check expected and actual data + actual = tl.entries[0].data + actual = bytes_to_hex(actual) + + assert actual == expected + + +@pytest.mark.parametrize("option", ["-O", "--output"]) +def test_gen_tl_header_with_output_name(tlcrunner, tmptlstr, option, filename="test.h"): + with tlcrunner.isolated_filesystem(): + result = tlcrunner.invoke( + cli, + [ + "gen-header", + option, + filename, + tmptlstr, + ], + ) + + assert result.exit_code == 0 + assert Path(filename).exists() + + +def test_gen_tl_with_fdt_header(tmptlstr, tmpfdt): + tlcrunner = CliRunner() + + with tlcrunner.isolated_filesystem(): + tlcrunner.invoke(cli, ["create", "--size", 1000, "--fdt", tmpfdt, tmptlstr]) + + result = tlcrunner.invoke( + cli, + [ + "gen-header", + tmptlstr, + ], + ) + + assert result.exit_code == 0 + assert Path("header.h").exists() + + with open("header.h", "r") as f: + dtb_match = search(r"DTB_OFFSET\s+(\d+)", "".join(f.readlines())) + assert dtb_match and dtb_match[1].isnumeric() + + +def test_gen_empty_tl_c_header(tlcrunner, tmptlstr): + with tlcrunner.isolated_filesystem(): + result = tlcrunner.invoke( + cli, + [ + "gen-header", + tmptlstr, + ], + ) + + assert result.exit_code == 0 + assert Path("header.h").exists() + + with open("header.h", "r") as f: + lines = "".join(f.readlines()) + + assert TransferList.hdr_size == int( + findall(r"SIZE\s+(0x[0-9a-fA-F]+|\d+)", lines)[0], 16 + ) + assert TransferList.version == int( + findall(r"VERSION.+(0x[0-9a-fA-F]+|\d+)", lines)[0] + ) + + +def bytes_to_hex(data: bytes) -> str: + """Convert bytes to a hex string in the same format as the debugger in + ArmDS + + You can copy data from the debugger in Arm Development Studio and put it + into a unit test. You can then run this function on the output from tlc, + and compare it to the data you copied. + + The format is groups of 4 bytes with 0x prefixes separated by spaces. + Little endian is used. + """ + words_hex = [] + for i in range(0, len(data), 4): + word = data[i : i + 4] + word_int = int.from_bytes(word, "little") + word_hex = "0x" + f"{word_int:0>8x}".upper() + words_hex.append(word_hex) + + return " ".join(words_hex) + + +def iter_nested_dict(dictionary: dict): + for key, value in dictionary.items(): + if isinstance(value, dict): + yield from iter_nested_dict(value) + else: + yield key, value diff --git a/tools/tlc/tests/test_transfer_list.py b/tools/tlc/tests/test_transfer_list.py new file mode 100644 index 00000000..e8c430e5 --- /dev/null +++ b/tools/tlc/tests/test_transfer_list.py @@ -0,0 +1,234 @@ +#!/usr/bin/env python3 + +# +# Copyright (c) 2024, Arm Limited. All rights reserved. +# +# SPDX-License-Identifier: BSD-3-Clause +# + +"""Contains unit tests for the types TransferEntry and TransferList.""" + +import math + +import pytest + +from tlc.te import TransferEntry +from tlc.tl import TransferList + +large_data = 0xDEADBEEF.to_bytes(4, "big") +small_data = 0x1234.to_bytes(3, "big") +test_entries = [ + (0, b""), + (1, small_data), + (1, large_data), + (0xFFFFFF, small_data), + (0xFFFFFF, large_data), +] + + +@pytest.mark.parametrize( + "size,csum", + [ + (-1, None), + (0x18, 0x9E), + (0x1000, 0xA6), + (0x2000, 0x96), + (0x4000, 0x76), + ], +) +def test_make_transfer_list(size, csum): + if size < 8: + with pytest.raises(AssertionError): + tl = TransferList(size) + else: + tl = TransferList(size) + + assert tl.signature == 0x4A0FB10B + assert not tl.entries + assert tl.sum_of_bytes() == 0 + assert tl.checksum == csum + + +@pytest.mark.parametrize(("tag_id", "data"), test_entries) +def test_add_transfer_entry(tag_id, data): + tl = TransferList(0x1000) + te = TransferEntry(tag_id, len(data), data) + + tl.add_transfer_entry(tag_id, data) + + assert te in tl.entries + assert tl.size == TransferList.hdr_size + te.size + + +@pytest.mark.parametrize( + ("tag_id", "data"), + [ + (-1, None), # tag out of range + (1, None), # no data provided + (1, bytes(8000)), # very large data > total size + (0x100000, b"0dd0edfe"), # tag out of range + ], +) +def test_add_out_of_range_transfer_entry(tag_id, data): + tl = TransferList() + + with pytest.raises(Exception): + tl.add_transfer_entry(tag_id, data) + + +@pytest.mark.parametrize(("tag_id", "data"), test_entries) +def test_calculate_te_sum_of_bytes(tag_id, data): + te = TransferEntry(tag_id, len(data), data) + csum = ( + sum(data) + + sum(len(data).to_bytes(4, "big")) + + te.hdr_size + + sum(tag_id.to_bytes(4, "big")) + ) % 256 + assert te.sum_of_bytes == csum + + +@pytest.mark.parametrize(("tag_id", "data"), test_entries) +def test_calculate_tl_checksum(tag_id, data): + tl = TransferList(0x1000) + + tl.add_transfer_entry(tag_id, data) + assert tl.sum_of_bytes() == 0 + + +def test_empty_transfer_list_blob(tmpdir): + """Check that we can correctly create a transfer list header.""" + test_file = tmpdir.join("test_tl_blob.bin") + tl = TransferList() + tl.write_to_file(test_file) + + with open(test_file, "rb") as f: + assert f.read(tl.hdr_size) == tl.header_to_bytes() + + +@pytest.mark.parametrize(("tag_id", "data"), test_entries) +def test_single_te_transfer_list(tag_id, data, tmpdir): + """Check that we can create a complete TL with a single TE.""" + test_file = tmpdir.join("test_tl_blob.bin") + tl = TransferList(0x1000) + + tl.add_transfer_entry(tag_id, data) + tl.write_to_file(test_file) + + te = tl.entries[0] + + with open(test_file, "rb") as f: + assert f.read(tl.hdr_size) == tl.header_to_bytes() + assert int.from_bytes(f.read(3), "little") == te.id + assert int.from_bytes(f.read(1), "little") == te.hdr_size + assert int.from_bytes(f.read(4), "little") == te.data_size + assert f.read(te.data_size) == te.data + + +def test_multiple_te_transfer_list(tmpdir): + """Check that we can create a TL with multiple TE's.""" + test_file = tmpdir.join("test_tl_blob.bin") + tl = TransferList(0x1000) + + for tag_id, data in test_entries: + tl.add_transfer_entry(tag_id, data) + + tl.write_to_file(test_file) + + with open(test_file, "rb") as f: + assert f.read(tl.hdr_size) == tl.header_to_bytes() + # Ensure that TE's have the correct alignment + for tag_id, data in test_entries: + f.seek(int(math.ceil(f.tell() / 2**tl.alignment) * 2**tl.alignment)) + print(f.tell()) + assert int.from_bytes(f.read(3), "little") == tag_id + assert int.from_bytes(f.read(1), "little") == TransferEntry.hdr_size + # Make sure the data in the TE matches the data in the original case + data_size = int.from_bytes(f.read(4), "little") + assert f.read(data_size) == data + + +def test_read_empty_transfer_list_from_file(tmpdir): + test_file = tmpdir.join("test_tl_blob.bin") + original_tl = TransferList(0x1000) + original_tl.write_to_file(test_file) + + # Read the contents of the file we just wrote + tl = TransferList.fromfile(test_file) + assert tl.header_to_bytes() == original_tl.header_to_bytes() + assert tl.sum_of_bytes() == 0 + + +def test_read_single_transfer_list_from_file(tmpdir): + test_file = tmpdir.join("test_tl_blob.bin") + original_tl = TransferList(0x1000) + + original_tl.add_transfer_entry(test_entries[0][0], test_entries[0][1]) + original_tl.write_to_file(test_file) + + # Read the contents of the file we just wrote + tl = TransferList.fromfile(test_file) + assert tl.entries + + te = tl.entries[0] + assert te.id == test_entries[0][0] + assert te.data == test_entries[0][1] + assert tl.sum_of_bytes() == 0 + + +def test_read_multiple_transfer_list_from_file(tmpdir): + test_file = tmpdir.join("test_tl_blob.bin") + original_tl = TransferList(0x1000) + + for tag_id, data in test_entries: + original_tl.add_transfer_entry(tag_id, data) + + original_tl.write_to_file(test_file) + + # Read the contents of the file we just wrote + tl = TransferList.fromfile(test_file) + + # The TE we derive from the file might have a an associated offset, compare + # the TE's based on the header in bytes, which doesn't account for this. + for te0, te1 in zip(tl.entries, original_tl.entries): + assert te0.header_to_bytes() == te1.header_to_bytes() + + assert tl.sum_of_bytes() == 0 + + +@pytest.mark.parametrize("tag", [tag for tag, _ in test_entries]) +def test_remove_tag_from_file(tag): + tl = TransferList(0x1000) + + for tag_id, data in test_entries: + tl.add_transfer_entry(tag_id, data) + + removed_entries = list(filter(lambda te: te.id == tag, tl.entries)) + original_size = tl.size + tl.remove_tag(tag) + + assert not any(tag == te.id for te in tl.entries) + assert tl.size == original_size - sum(map(lambda te: te.size, removed_entries)) + + +def test_get_fdt_offset(tmpdir): + tl = TransferList(0x1000) + tl.add_transfer_entry(1, 0xEDFE0DD0.to_bytes(4, "big")) + f = tmpdir.join("blob.bin") + + tl.write_to_file(f) + + blob_tl = TransferList.fromfile(f) + + assert blob_tl.hdr_size + TransferEntry.hdr_size == blob_tl.get_entry_data_offset(1) + + +def test_get_missing_fdt_offset(tmpdir): + tl = TransferList(0x1000) + f = tmpdir.join("blob.bin") + + tl.write_to_file(f) + blob_tl = TransferList.fromfile(f) + + with pytest.raises(ValueError): + blob_tl.get_entry_data_offset(1) diff --git a/tools/tlc/tlc/__init__.py b/tools/tlc/tlc/__init__.py new file mode 100644 index 00000000..82f5f5bf --- /dev/null +++ b/tools/tlc/tlc/__init__.py @@ -0,0 +1,27 @@ +#!/usr/bin/env python3 +# type: ignore[attr-defined] + +# +# Copyright (c) 2024, Arm Limited. All rights reserved. +# +# SPDX-License-Identifier: BSD-3-Clause +# + +"""Transfer List Compiler (TLC) is a Python-based CLI for efficiently handling transfer lists.""" + +import sys + +if sys.version_info >= (3, 8): + from importlib import metadata as importlib_metadata +else: + import importlib_metadata + + +def get_version() -> str: + try: + return importlib_metadata.version(__name__) + except importlib_metadata.PackageNotFoundError: # pragma: no cover + return "unknown" + + +version: str = get_version() diff --git a/tools/tlc/tlc/__main__.py b/tools/tlc/tlc/__main__.py new file mode 100644 index 00000000..03ffa0e7 --- /dev/null +++ b/tools/tlc/tlc/__main__.py @@ -0,0 +1,13 @@ +#!/usr/bin/env python3 +# type: ignore[attr-defined] + +# +# Copyright (c) 2024, Arm Limited. All rights reserved. +# +# SPDX-License-Identifier: BSD-3-Clause +# + +from tlc.cli import cli + +if __name__ == "__main__": + cli() diff --git a/tools/tlc/tlc/cli.py b/tools/tlc/tlc/cli.py new file mode 100644 index 00000000..3d609380 --- /dev/null +++ b/tools/tlc/tlc/cli.py @@ -0,0 +1,201 @@ +#!/usr/bin/env python3 +# type: ignore[attr-defined] + +# +# Copyright (c) 2024, Arm Limited. All rights reserved. +# +# SPDX-License-Identifier: BSD-3-Clause +# + +"""Module defining the Transfer List Compiler (TLC) command line interface.""" + +from pathlib import Path + +import click +import jinja2 +import yaml + +from tlc.tl import * + + +@click.group() +@click.version_option() +def cli(): + pass + + +@cli.command() +@click.argument("filename", type=click.Path(dir_okay=False)) +@click.option( + "-s", "--size", default=0x1000, type=int, help="Maximum size of the Transfer List" +) +@click.option( + "--fdt", + type=click.Path(exists=True), + help="Path to flattened device tree (FDT).", +) +@click.option( + "--entry", + type=(int, click.Path(exists=True)), + multiple=True, + help="A tag ID and the corresponding path to a binary blob in the form <id> <path-to-blob>.", +) +@click.option( + "--flags", + default=TRANSFER_LIST_ENABLE_CHECKSUM, + show_default=True, + help="Settings for the TL's properties.", +) +@click.option( + "--from-yaml", + type=click.Path(exists=True), + help="Create the transfer list from a YAML config file.", +) +def create(filename, size, fdt, entry, flags, from_yaml): + """Create a new Transfer List.""" + try: + if from_yaml: + with open(from_yaml, "r") as f: + config = yaml.safe_load(f) + + tl = TransferList.from_dict(config) + else: + tl = TransferList(size) + + entry = (*entry, (1, fdt)) if fdt else entry + + for id, path in entry: + tl.add_transfer_entry_from_file(id, path) + except MemoryError as mem_excp: + raise MemoryError( + "TL max size exceeded, consider increasing with the option -s" + ) from mem_excp + + tl.write_to_file(filename) + + +@cli.command() +@click.argument("filename", type=click.Path(exists=True, dir_okay=False)) +@click.option( + "--fdt-offset", + is_flag=True, + help="Returns the offset of FDT in the TL if it is present.", +) +@click.option( + "--header", + is_flag=True, + help="Print the Transfer List header.", +) +@click.option( + "--entries", + is_flag=True, + help="Print the Transfer List entries.", +) +def info(filename, fdt_offset, header, entries): + """Print the contents of an existing Transfer List. + + This command allows you to extract the data stored in a binary blob + representing a transfer list (TL). The transfer list must comply with the + version of the firmware handoff specification supported by this tool. + """ + tl = TransferList.fromfile(filename) + + if fdt_offset: + return print(tl.get_entry_data_offset(1)) + + if header and entries or not (header or entries): + print(tl, sep="") + if tl.entries: + print("----", tl.get_transfer_entries_str(), sep="\n") + elif entries: + print(tl.get_transfer_entries_str()) + elif header: + print(tl) + + +@cli.command() +@click.argument("filename", type=click.Path(exists=True, dir_okay=False)) +@click.option( + "--tags", + type=int, + multiple=True, + help="Tags to be removed from TL.", +) +def remove(filename, tags): + """Remove Transfer Entries with given tags. + + Remove Transfer Entries with given tags from a Transfer List.""" + tl = TransferList.fromfile(filename) + + for tag in tags: + tl.remove_tag(tag) + tl.write_to_file(filename) + + +@cli.command() +@click.argument("filename", type=click.Path(exists=True, dir_okay=False)) +@click.option( + "--entry", + type=(int, click.Path(exists=True)), + multiple=True, + help="A tag ID and the corresponding path to a binary blob in the form <id> <path-to-blob>.", +) +def add(filename, entry): + """Update an existing Transfer List with given images.""" + tl = TransferList.fromfile(filename) + + for id, path in entry: + tl.add_transfer_entry_from_file(id, path) + + tl.write_to_file(filename) + + +@cli.command() +@click.argument("filename", type=click.Path(exists=True, dir_okay=False)) +@click.option( + "-C", type=click.Path(exists=True), help="Output directory for extracted images." +) +def unpack(filename, c): + """Unpack images from a Transfer List.""" + tl = TransferList.fromfile(filename) + pwd = Path(".") if not c else Path(c) + + for i, te in enumerate(tl.entries): + with open(pwd / f"te_{i}_{te.id}.bin", "wb") as f: + f.write(te.data) + + +@cli.command() +@click.argument("filename", type=click.Path(exists=True, dir_okay=False)) +@click.option( + "--output", + "-O", + type=click.Path(exists=False), + help="Output filename for the header", + default=Path("header.h"), +) +def gen_header(filename, output): + """Generate a header with common definitions.""" + tl = TransferList.fromfile(filename) + tmp_keys = tl.__dict__ + tmp_keys["header_guard"] = Path(output).name.replace(".", "_").upper() + + dtb_te = tl.get_entry(1) + + if dtb_te: + tmp_keys["dtb_offset"] = dtb_te.offset + dtb_te.hdr_size + + env = jinja2.Environment( + loader=jinja2.PackageLoader("tlc", "templates"), + ) + template = env.get_template("header.h.j2") + with open(output, "w") as f: + f.write(template.render(tmp_keys)) + + +@cli.command() +@click.argument("filename", type=click.Path(exists=True, dir_okay=False)) +def validate(filename): + """Validate the contents of an existing Transfer List.""" + TransferList.fromfile(filename) + print("Valid TL!") diff --git a/tools/tlc/tlc/te.py b/tools/tlc/tlc/te.py new file mode 100644 index 00000000..cf7aa67c --- /dev/null +++ b/tools/tlc/tlc/te.py @@ -0,0 +1,56 @@ +#!/usr/bin/env python3 + +# +# Copyright (c) 2024, Arm Limited. All rights reserved. +# +# SPDX-License-Identifier: BSD-3-Clause +# + +"""Module containing definitions pertaining to the 'Transfer Entry' (TE) type.""" + +from typing import ClassVar + +import struct +from dataclasses import dataclass + + +@dataclass +class TransferEntry: + """Class representing a Transfer Entry.""" + + id: int + data_size: int + data: bytes + hdr_size: int = 8 + offset: int = 0 + # Header encoding, with little-endian byte order. + encoding: ClassVar[str] = "<BI" + + def __post_init__(self): + if self.id < 0 or self.id > 0xFFFFFF: + raise ValueError( + f"Out of bounds tag ID: {self.id:x}.\n" + f"Valid range is from 0 to 0xFFFFFF. Please ensure the tag ID is within this range." + ) + + def __str__(self) -> str: + return "\n".join( + [ + f"{k:<10} {hex(v)}" + for k, v in vars(self).items() + if not isinstance(v, bytes) + ] + ) + + @property + def size(self) -> int: + return self.hdr_size + len(self.data) + + @property + def sum_of_bytes(self) -> int: + return (sum(self.header_to_bytes()) + sum(self.data)) % 256 + + def header_to_bytes(self) -> bytes: + return self.id.to_bytes(3, "little") + struct.pack( + self.encoding, self.hdr_size, self.data_size + ) diff --git a/tools/tlc/tlc/templates/header.h.j2 b/tools/tlc/tlc/templates/header.h.j2 new file mode 100644 index 00000000..87707cec --- /dev/null +++ b/tools/tlc/tlc/templates/header.h.j2 @@ -0,0 +1,16 @@ +/* + * Auto-generated by TLC, this file includes declarations and macros + * derived from a Transfer List input. + */ + +#ifndef {{ header_guard }} +#define {{ header_guard }} + +{% if dtb_offset -%} +#define TRANSFER_LIST_DTB_OFFSET {{ "0x%x" % dtb_offset }} +{%- endif %} +#define TRANSFER_LIST_CONVENTION_VERSION {{ version }} +#define TRANSFER_LIST_HEADER_SIZE {{ "0x%x" % hdr_size }} +#define TRANSFER_LIST_SIZE {{ "0x%x" % size }} + +#endif /* {{ header_guard }} */ diff --git a/tools/tlc/tlc/tl.py b/tools/tlc/tlc/tl.py new file mode 100644 index 00000000..98d2205b --- /dev/null +++ b/tools/tlc/tlc/tl.py @@ -0,0 +1,363 @@ +#!/usr/bin/env python3 + +# +# Copyright (c) 2024, Arm Limited. All rights reserved. +# +# SPDX-License-Identifier: BSD-3-Clause +# + +"""Module containing definitions pertaining to the 'Transfer List' (TL) type.""" + +from typing import Any, Dict, List, Optional + +import math +import struct +from dataclasses import dataclass +from functools import reduce +from pathlib import Path + +from tlc.te import TransferEntry + +TRANSFER_LIST_ENABLE_CHECKSUM = 0b1 + +# Description of each TE type. For each TE, there is a tag ID, a format (to be +# used in struct.pack to encode the TE), and a list of field names that can +# appear in the yaml file for that TE. Some fields are missing, if that TE has +# to be processed differently, or if it can only be added with a blob file. +transfer_entry_formats: Dict[int, Any] = { + 0: { + "tag_name": "empty", + "format": "4x", + "fields": [], + }, + 1: { + "tag_name": "fdt", + }, + 2: { + "tag_name": "hob_block", + }, + 3: { + "tag_name": "hob_list", + }, + 4: { + "tag_name": "acpi_table_aggregate", + }, + 5: { + "tag_name": "tpm_event_log_table", + "fields": ["event_log", "flags"], + }, + 6: { + "tag_name": "tpm_crb_base_address_table", + "format": "QI", + "fields": ["crb_base_address", "crb_size"], + }, + 0x100: { + "tag_name": "optee_pageable_part", + "format": "Q", + "fields": ["pp_addr"], + }, + 0x101: { + "tag_name": "dt_spmc_manifest", + }, + 0x102: { + "tag_name": "exec_ep_info", + "format": "2BHIQI4x8Q", + "fields": ["ep_info"], + }, + 0x104: { + "tag_name": "sram_layout", + "format": "2Q", + "fields": ["addr", "size"], + }, +} +tag_name_to_tag_id = { + te["tag_name"]: tag_id for tag_id, te in transfer_entry_formats.items() +} + + +class TransferList: + """Class representing a Transfer List based on version 1.0 of the Firmware Handoff specification.""" + + # Header encoding, with little-endian byte order. + encoding = "<I4B4I" + hdr_size = 0x18 + signature = 0x4A0FB10B + version = 1 + + def __init__( + self, max_size: int = hdr_size, flags: int = TRANSFER_LIST_ENABLE_CHECKSUM + ) -> None: + assert max_size >= self.hdr_size + self.checksum: int = 0 + self.alignment: int = 3 + self.size = self.hdr_size + self.total_size = max_size + self.flags = flags + self.entries: List[TransferEntry] = [] + self.update_checksum() + + def __str__(self) -> str: + return "\n".join( + [ + f"{k:<10} {hex(v)}" + for k, v in vars(self).items() + if not isinstance(v, list) + ] + ) + + def get_transfer_entries_str(self): + return "\n----\n".join([str(te) for _, te in enumerate(self.entries)]) + + @classmethod + def fromfile(cls, filepath: Path) -> "TransferList": + tl = cls() + + with open(filepath, "rb") as f: + ( + tl.signature, + tl.checksum, + tl.version, + tl.hdr_size, + tl.alignment, + used_size, + tl.total_size, + tl.flags, + _, + ) = struct.unpack( + cls.encoding, + f.read(tl.hdr_size), + ) + + if tl.signature != TransferList.signature: + raise ValueError(f"Invalid TL signature 0x{tl.signature:x}!") + elif tl.version == 0 or tl.version > 0xFF: + raise ValueError(f"Invalid TL version 0x{tl.version:x}!") + else: + while tl.size < used_size: + # We add an extra padding byte into the header so we can extract + # the 3-byte wide ID as a 4-byte uint, shift out this padding + # once we have the id. + te_base = f.tell() + (id, hdr_size, data_size) = struct.unpack( + TransferEntry.encoding[0] + "I" + TransferEntry.encoding[1:], + b"\x00" + f.read(TransferEntry.hdr_size), + ) + + id >>= 8 + + te = tl.add_transfer_entry(id, f.read(data_size)) + te.offset = te_base + f.seek(align(te_base + hdr_size + data_size, 2**tl.alignment)) + + return tl + + @classmethod + def from_dict(cls, config: Dict[str, Any]) -> "TransferList": + """Create a TL from data in a dictionary + + The dictionary should have the same format as the yaml config files. + See the readme for more detail. + + :param config: Dictionary containing the data described above. + """ + # get settings from config and set defaults + max_size = config.get("max_size", 0x1000) + has_checksum = config.get("has_checksum", True) + + flags = TRANSFER_LIST_ENABLE_CHECKSUM if has_checksum else 0 + + tl = cls(max_size, flags) + + for entry in config["entries"]: + tl.add_transfer_entry_from_dict(entry) + + return tl + + def header_to_bytes(self) -> bytes: + return struct.pack( + self.encoding, + self.signature, + self.checksum, + self.version, + self.hdr_size, + self.alignment, + self.size, + self.total_size, + self.flags, + 0, + ) + + def update_checksum(self) -> None: + """Calculates the checksum based on the sum of bytes.""" + self.checksum = 256 - ((self.sum_of_bytes() - self.checksum) % 256) + + def sum_of_bytes(self) -> int: + """Sum of all bytes between the base address and the end of that last TE (modulo 0xff).""" + return ( + sum(self.header_to_bytes()) + sum(te.sum_of_bytes for te in self.entries) + ) % 256 + + def get_entry(self, tag_id: int) -> Optional[TransferEntry]: + for te in self.entries: + if te.id == tag_id: + return te + + return None + + def get_entry_data_offset(self, tag_id: int) -> int: + """Returns offset of data of a TE from the base of the TL.""" + te = self.get_entry(tag_id) + + if not te: + raise ValueError(f"Tag {tag_id} not found in TL!") + + return te.offset + te.hdr_size + + def add_transfer_entry(self, tag_id: int, data: bytes) -> TransferEntry: + """Appends a TransferEntry into the internal list of TE's.""" + if not (self.total_size >= self.size + TransferEntry.hdr_size + len(data)): + raise MemoryError( + f"TL size has exceeded the maximum allocation {self.total_size}." + ) + else: + te = TransferEntry(tag_id, len(data), data) + self.entries.append(te) + self.size += te.size + self.update_checksum() + return te + + def add_transfer_entry_from_struct_format( + self, tag_id: int, struct_format: str, *args: Any + ) -> TransferEntry: + struct_format = "<" + struct_format + data = struct.pack(struct_format, *args) + return self.add_transfer_entry(tag_id, data) + + def add_entry_point_info_transfer_entry( + self, entry: Dict[str, Any] + ) -> TransferEntry: + """Add entry_point_info transfer entry + + :param entry: Dictionary of the transfer entry, in the same format as + the YAML file. + """ + ep_info = entry["ep_info"] + header = ep_info["h"] + + # size of the entry_point_info struct + entry_point_size = 88 + + attr = header["attr"] + if type(attr) is str: + # convert string of flags names to an integer + + # bit number | 0 | 1 | + # ------------|-----------------------|----------------------| + # 0 | secure | non-secure | + # 1 | little endian | big-endian | + # 2 | disable secure timer | enable secure timer | + # 3 | executable | non-executable | + # 4 | first exe | not first exe | + # + # Bit 5 and bit 0 are used to determine the security state. + + flag_names = { + "EP_SECURE": 0x0, + "EP_NON_SECURE": 0x1, + "EP_REALM": 0x21, + "EP_EE_LITTLE": 0x0, + "EP_EE_BIG": 0x2, + "EP_ST_DISABLE": 0x0, + "EP_ST_ENABLE": 0x4, + "EP_NON_EXECUTABLE": 0x0, + "EP_EXECUTABLE": 0x8, + "EP_FIRST_EXE": 0x10, + } + + # create list of integer flags, then bitwise-or them together + flags = [flag_names[f.strip()] for f in attr.split("|")] + attr = reduce(lambda x, y: x | y, flags) + + return self.add_transfer_entry_from_struct_format( + 0x102, + transfer_entry_formats[0x102]["format"], + header["type"], + header["version"], + entry_point_size, + attr, + ep_info["pc"], + ep_info["spsr"], + *ep_info["args"], + ) + + def add_transfer_entry_from_dict( + self, + entry: Dict[str, Any], + ) -> TransferEntry: + """Add a transfer entry from data in a dictionary + + The dictionary should have the same format as the entries in the yaml + config files. See the readme for more detail. + + :param entry: Dictionary containing the data described above. + """ + # Tag_id is either a tag name or a tag id. Use it to get the TE format. + tag_id = entry["tag_id"] + if tag_id in tag_name_to_tag_id: + tag_id = tag_name_to_tag_id[tag_id] + te_format = transfer_entry_formats[tag_id] + tag_name = te_format["tag_name"] + + if "blob_file_path" in entry: + return self.add_transfer_entry_from_file(tag_id, entry["blob_file_path"]) + elif tag_name == "tpm_event_log_table": + with open(entry["event_log"], "rb") as f: + event_log_data = f.read() + + flags_bytes = entry["flags"].to_bytes(4, "little") + data = flags_bytes + event_log_data + + return self.add_transfer_entry(tag_id, data) + elif tag_name == "exec_ep_info": + return self.add_entry_point_info_transfer_entry(entry) + elif "format" in te_format and "fields" in te_format: + fields = [entry[field] for field in te_format["fields"]] + return self.add_transfer_entry_from_struct_format( + tag_id, te_format["format"], *fields + ) + else: + raise ValueError(f"Invalid transfer entry {entry}.") + + def add_transfer_entry_from_file(self, tag_id: int, path: Path) -> TransferEntry: + with open(path, "rb") as f: + return self.add_transfer_entry(tag_id, f.read()) + + def write_to_file(self, file: Path) -> None: + """Write the contents of the TL to a file.""" + with open(file, "wb") as f: + f.write(self.header_to_bytes()) + for te in self.entries: + assert f.tell() + te.hdr_size + te.data_size < self.total_size + te_base = f.tell() + f.write(te.header_to_bytes()) + f.write(te.data) + # Ensure the next TE has the correct alignment + f.write( + bytes( + ( + align( + te_base + te.hdr_size + te.data_size, 2**self.alignment + ) + - f.tell() + ) + ) + ) + + def remove_tag(self, tag: int) -> None: + self.entries = list(filter(lambda te: te.id != tag, self.entries)) + self.size = self.hdr_size + sum(map(lambda te: te.size, self.entries)) + self.update_checksum() + + +def align(n, alignment): + return int(math.ceil(n / alignment) * alignment) diff --git a/tools/tlc/tox.ini b/tools/tlc/tox.ini new file mode 100644 index 00000000..4fd141f1 --- /dev/null +++ b/tools/tlc/tox.ini @@ -0,0 +1,26 @@ +[tox] +envlist = py38, py39, py310, py311, py312, lint + +[testenv] +allowlist_externals = poetry +commands = + poetry install -v --with dev + poetry run pytest + +[testenv:format] +description = Run linters and type checks +skip_install = true +allowlist_externals = poetry +commands = + poetry run black . + poetry run isort . + +[testenv:lint] +description = Run linters and type checks +skip_install = true +allowlist_externals = poetry +commands = + poetry run black --check . + poetry run isort --check-only . + poetry run mypy . + poetry run darglint tlc tests -- GitLab From c32f2d4753e527e9ffedddf27ce6036e4b5c84c0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dylan=20A=C3=AFssi?= <dylan.aissi@collabora.com> Date: Mon, 10 Mar 2025 17:48:02 +0100 Subject: [PATCH 2/3] Release arm-trusted-firmware version 2.12.0+dfsg-2+apertis1 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Dylan Aïssi <dylan.aissi@collabora.com> --- debian/changelog | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/debian/changelog b/debian/changelog index 255734c0..bb8ca1e8 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,12 @@ +arm-trusted-firmware (2.12.0+dfsg-2+apertis1) apertis; urgency=medium + + * Sync from debian/trixie. + * Remaining Apertis specific changes: + - Add build profile for QEMU. + - Build for the ls1028ardb platform. + + -- Dylan Aïssi <dylan.aissi@collabora.com> Mon, 10 Mar 2025 17:44:24 +0100 + arm-trusted-firmware (2.12.0+dfsg-2) unstable; urgency=medium [ Martyn Welch ] -- GitLab From 2635af89375f81a8d9e09eb43762590fd55aa2f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dylan=20A=C3=AFssi?= <dylan.aissi@collabora.com> Date: Mon, 10 Mar 2025 16:49:15 +0000 Subject: [PATCH 3/3] Refresh the automatically detected licensing information MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Dylan Aïssi <dylan.aissi@collabora.com> --- debian/apertis/copyright | 2910 +++++++++++++++++++++++--------------- 1 file changed, 1766 insertions(+), 1144 deletions(-) diff --git a/debian/apertis/copyright b/debian/apertis/copyright index 0329b806..52f028b2 100644 --- a/debian/apertis/copyright +++ b/debian/apertis/copyright @@ -1,90 +1,149 @@ Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ Files: * -Copyright: no-info-found +Copyright: 2013-2024, Arm Limited and Contributors. License: BSD-3-Clause -Files: docs/* docs/components/* docs/design/* docs/design_documents/* docs/plat/nxp/* docs/plat/st/* fdts/* include/dt-bindings/pinctrl/* include/lib/libfdt/* lib/libfdt/* +Files: docs/about/* docs/components/* docs/design/* docs/design_documents/* docs/getting_started/* docs/perf/* docs/plat/arm/* docs/plat/st/* docs/process/* docs/threat_model/* docs/tools/* fdts/* include/lib/libfdt/* lib/libfdt/* Copyright: 1989 Regents of the University of California. - 1998 Softweyr LLC. All rights reserved. + 1998 Softweyr LLC. 2001 David E. O'Brien 2005 MontaVista Software, Inc. 2005 Nokia Corporation 2011 The FreeBSD Foundation 2012-2021 Roberto E. Vargas Caballero - 2013-2023 ARM Limited and Contributors. All rights reserved. - 2013-2023 Arm Limited and Contributors. All rights reserved. - 2014-2023 Arm Limited. All rights reserved. - 2014-2016 Freescale Semiconductor, Inc. - 2014 Linaro Limited. All rights reserved. - 2014-2023 STMicroelectronics - All Rights Reserved + 2013-2024 ARM Limited and Contributors. + 2013-2024 Arm Limited and Contributors. + 2014 Linaro Limited. 2014 STMicroelectronics International N.V. + 2014-2016 Freescale Semiconductor, Inc. + 2014-2023 Arm Limited. + 2014-2023 STMicroelectronics 2015-2021 Broadcom - 2015-2023 NVIDIA Corporation. All rights reserved. - 2015-2023 Renesas Electronics Corporation. - 2015-2023 Renesas Electronics Corporation. All rights - 2015-2023 Renesas Electronics Corporation. All rights reserved. - 2016-2020 Marvell International Ltd. - 2016-2022 Linaro Limited. All rights reserved. + 2015-2022 Xilinx Inc. + 2015-2023 NVIDIA Corporation. + 2015-2024 Arm Limited. + 2015-2024 Renesas Electronics Corporation + 2015-2024 STMicroelectronics 2016-2021 Marvell International Ltd. + 2016-2022 Linaro Limited. 2016-2023 NXP - 2017-2020 Arm Limited and Contributors. All rights reserved. - 2017-2023 NVIDIA CORPORATION. All rights reserved. + 2016-2024 ARM Limited and Contributors. + 2016-2024 STMicroelectronics + 2017-2020 Arm Limited and Contributors. 2017-2022 NXP Semiconductors + 2017-2022 Xilinx, Inc. + 2017-2023 NVIDIA CORPORATION. 2017-2023 Nuvoton Ltd. 2017-2023 Nuvoton Technology Corp. - 2017-2022 Xilinx, Inc. All rights reserved. 2018 Andre Przywara <osp@andrep.de> - 2018-2020 Arm Limited and Contributors. 2018 Icenowy Zheng <icenowy@aosc.io> 2018-2019 Linaro - 2018-2022 Texas Instruments Incorporated - https://www.ti.com/ - 2018-2021 The Linux Foundation. All rights reserved. - 2019-2023 STMicroelectronics - All Rights Reserved - 2019-2020 Broadcom. + 2018-2020 Arm Limited and Contributors. + 2018-2021 The Linux Foundation. + 2018-2022 Xilinx Inc. + 2018-2024 Arm Limited and Contributors. + 2018-2024 Arm Limited. + 2018-2024 Marvell International Ltd. + 2018-2024 STMicroelectronics + 2018-2024 Texas Instruments Incorporated - https://www.ti.com/ 2019 Carlo Caione <ccaione@baylibre.com> - 2019-2023 Intel Corporation. All rights reserved. - 2019-2022 Linaro Limited - 2019-2020 Linaro Limited and Contributors. - 2019-2023 Linaro Limited and Contributors. All rights reserved. - 2019-2023 MediaTek Inc. All rights reserved. - 2019-2022 NXP. All rights reserved. 2019 Remi Pommarel <repk@triplefau.lt> 2019 Repk repk@triplefau.lt - 2019-2022 Socionext Inc. All rights reserved. 2019 Ying-Chun Liu (PaulLiu) <paul.liu@linaro.org> - 2020-2022 ARM Limited and Contributors. All rights reserved. - 2020-2023 Google LLC. All rights reserved. + 2019-2020 Broadcom. + 2019-2020 Linaro Limited and Contributors. + 2019-2021 2024 NXP + 2019-2022 Linaro Limited + 2019-2022 Linaro Limited. + 2019-2022 NXP. + 2019-2022 Socionext Inc. + 2019-2023 Intel Corporation. + 2019-2023 Linaro Limited and Contributors. + 2019-2023 MediaTek Inc. + 2019-2023 STMicroelectronics + 2019-2024 Arm Limited and Contributors. + 2019-2024 NXP + 2019-2024 STMicroelectronics 2020 Marek Behun, CZ.NIC - 2020 Marvell Technology Group Ltd. All rights reserved. - 2020 MediaTek Inc. All rights reserved. + 2020 Marvell Technology Group Ltd. + 2020 MediaTek Inc. 2020 NXP. 2020 Nuvia Inc 2020-2021 Sartura Ltd. 2020-2021 Siemens AG - 2021 Xilinx Inc. + 2020-2023 Google LLC + 2020-2024 Arm Limited and Contributors. + 2020-2024 Arm Limited. + 2020-2024 MediaTek Inc. + 2020-2024 NXP 2021 Arm 2021 Arm Limited 2021 Globalscale technologies, Inc. 2021 Marek Behun <marek.behun@nic.cz> - 2021-2022 ProvenRun S.A.S. All rights reserved. 2021 Semihalf. 2021 Sipeed + 2021 Xilinx Inc. + 2021-2022 ProvenRun S.A.S. + 2021-2022 Xilinx Inc. + 2021-2023 Renesas Electronics Corporation. 2021-2023 Stephan Gerhold <stephan@gerhold.net> - 2022-2023 Advanced Micro Devices, Inc. All rights reserved. - 2022-2023 Arm Ltd. All rights reserved. - 2022 Fujitsu Limited and Contributors. All rights reserved. + 2021-2024 ARM Limited and Contributors. + 2021-2024 ARM Limited. + 2021-2024 Arm Limited and Contributors. + 2021-2024 Arm Limited. + 2021-2024 MediaTek Inc. + 2021-2024 NXP + 2021-2024 STMicroelectronics + 2022 Advanced Micro Devices Inc. + 2022 Arm Limited and Contributors. + 2022 Fujitsu Limited and Contributors. 2022 Leica Geosystems AG 2022 Linaro - 2022-2023 Linaro. - 2022 Mediatek Inc. All rights reserved. - 2022 Qualcomm Innovation Center, Inc. All rights reserved. + 2022 Mediatek Inc. + 2022 Qualcomm Innovation Center, Inc. 2022 The Hafnium Authors. - 2023 Advanced Micro Devices. All rights reserved. - 2023 Arm Limited. All rights reserved + 2022 Xilinx Inc. + 2022-2023 Advanced Micro Devices, Inc. + 2022-2023 Arm Ltd. + 2022-2023 Linaro. + 2022-2024 ARM Limited and Contributors. + 2022-2024 Advanced Micro Devices Inc. + 2022-2024 Arm Limited and Contributors. + 2022-2024 Arm Limited. + 2022-2024 Arm Ltd. + 2022-2024 MediaTek Inc. + 2022-2024 STMicroelectronics + 2023 ARM Limited and Contributors. + 2023 Advanced Micro Devices. + 2023 Arm Limited. 2023 Aspeed Technology Inc. - 2023 Pengutronix. All rights reserved. + 2023 MediaTek Inc. + 2023 Pengutronix. + 2023-2024 Advanced Micro Devices Inc. + 2023-2024 Arm Limited and Contributors. + 2023-2024 Arm Limited. + 2023-2024 Arm Ltd. + 2023-2024 Linaro Limited and Contributors. + 2023-2024 NXP + 2023-2024 STMicroelectronics + 2024 ARM Limited and Contributors. + 2024 ARM Limited. + 2024 Altera Corporation. + 2024 Arm Limited and Contributors. + 2024 Arm Limited. + 2024 Arm Ltd. + 2024 Intel Corporation. + 2024 Linaro Limited and Contributors. + 2024 Mario Bălănică <mariobalanica02@gmail.com> + 2024 Mediatek Inc. + 2024 NVIDIA Corporation. + 2024 NXP + 2024 Pengutronix Inc. + 2024 Rockchip Inc. + 2024 STMicroelectronics + 2024 The ChromiumOS Authors. License: BSD-3-Clause Files: lib/zlib/* @@ -94,264 +153,280 @@ Copyright: License: Zlib Files: .checkpatch.conf -Copyright: 2013-2023, ARM Limited and Contributors. +Copyright: 2013-2024, ARM Limited and Contributors. License: BSD-3-clause Files: .commitlintrc.js - .readthedocs.yaml - .versionrc.js + .cz-adapter.cjs + .versionrc.cjs changelog.yaml -Copyright: 2015-2023, Arm Limited. +Copyright: 2015-2024, Arm Limited. License: BSD-3-clause Files: .editorconfig Makefile -Copyright: 2013-2023, Arm Limited and Contributors. +Copyright: 2013-2024, Arm Limited and Contributors. License: BSD-3-clause -Files: .husky/* +Files: .husky/commit-msg.gerrit Copyright: 2009, The Android Open Source Project License: Apache-2.0 +Files: .readthedocs.yaml +Copyright: 2023, 2024, Arm Limited. +License: BSD-3-clause + Files: bl1/* -Copyright: 2013-2023, ARM Limited and Contributors. +Copyright: 2013-2024, Arm Limited and Contributors. +License: BSD-3-clause + +Files: bl1/aarch32/* +Copyright: 2013-2024, ARM Limited and Contributors. +License: BSD-3-clause + +Files: bl1/aarch64/bl1_arch_setup.c + bl1/aarch64/bl1_exceptions.S +Copyright: 2013-2024, ARM Limited and Contributors. License: BSD-3-clause -Files: bl1/aarch64/bl1_context_mgmt.c - bl1/aarch64/bl1_entrypoint.S -Copyright: 2013-2023, Arm Limited and Contributors. +Files: bl1/bl1_fwu.c +Copyright: 2013-2024, ARM Limited and Contributors. License: BSD-3-clause -Files: bl1/bl1.ld.S - bl1/bl1.mk - bl1/bl1_main.c - bl1/bl1_private.h -Copyright: 2013-2023, Arm Limited and Contributors. +Files: bl1/tbbr/* +Copyright: 2013-2024, ARM Limited and Contributors. License: BSD-3-clause Files: bl2/* -Copyright: 2013-2023, ARM Limited and Contributors. +Copyright: 2013-2024, ARM Limited and Contributors. License: BSD-3-clause Files: bl2/aarch32/bl2_el3_entrypoint.S -Copyright: 2013-2023, Arm Limited and Contributors. +Copyright: 2013-2024, Arm Limited and Contributors. License: BSD-3-clause Files: bl2/aarch32/bl2_run_next_image.S -Copyright: 2015-2023, Arm Limited. +Copyright: 2015-2024, Arm Limited. License: BSD-3-clause Files: bl2/aarch64/bl2_el3_entrypoint.S -Copyright: 2013-2023, Arm Limited and Contributors. +Copyright: 2013-2024, Arm Limited and Contributors. License: BSD-3-clause Files: bl2/aarch64/bl2_rme_entrypoint.S bl2/aarch64/bl2_run_next_image.S -Copyright: 2015-2023, Arm Limited. +Copyright: 2015-2024, Arm Limited. License: BSD-3-clause Files: bl2/bl2.ld.S bl2/bl2.mk bl2/bl2_el3.ld.S bl2/bl2_main.c -Copyright: 2013-2023, Arm Limited and Contributors. +Copyright: 2013-2024, Arm Limited and Contributors. License: BSD-3-clause Files: bl2u/* -Copyright: 2013-2023, ARM Limited and Contributors. +Copyright: 2013-2024, Arm Limited and Contributors. +License: BSD-3-clause + +Files: bl2u/aarch32/* +Copyright: 2013-2024, ARM Limited and Contributors. License: BSD-3-clause -Files: bl2u/bl2u.ld.S - bl2u/bl2u.mk -Copyright: 2013-2023, Arm Limited and Contributors. +Files: bl2u/aarch64/* +Copyright: 2013-2024, ARM Limited and Contributors. License: BSD-3-clause Files: bl31/* -Copyright: 2013-2023, Arm Limited and Contributors. +Copyright: 2013-2024, Arm Limited and Contributors. License: BSD-3-clause Files: bl31/aarch64/bl31_entrypoint.S -Copyright: 2013-2023, ARM Limited and Contributors. +Copyright: 2013-2024, ARM Limited and Contributors. License: BSD-3-clause Files: bl31/aarch64/ea_delegate.S Copyright: 2015-2023, NVIDIA Corporation. - 2013-2023, ARM Limited and Contributors. + 2013-2024, ARM Limited and Contributors. License: BSD-3-clause Files: bl31/bl31_traps.c -Copyright: 2023, NVIDIA Corporation. - 2019, 2022, ARM Limited. +Copyright: 2021-2024, NVIDIA Corporation. + 2019-2024, Arm Limited. License: BSD-3-clause Files: bl32/* -Copyright: 2013-2023, Arm Limited and Contributors. +Copyright: 2013-2024, Arm Limited and Contributors. License: BSD-3-clause -Files: bl32/sp_min/sp_min_private.h - bl32/sp_min/wa_cve_2017_5715_bpiall.S +Files: bl32/sp_min/wa_cve_2017_5715_bpiall.S bl32/sp_min/wa_cve_2017_5715_icache_inv.S -Copyright: 2013-2023, ARM Limited and Contributors. +Copyright: 2013-2024, ARM Limited and Contributors. License: BSD-3-clause -Files: bl32/tsp/* -Copyright: 2013-2023, ARM Limited and Contributors. +Files: bl32/tsp/aarch64/* +Copyright: 2013-2024, ARM Limited and Contributors. License: BSD-3-clause Files: bl32/tsp/ffa_helpers.c bl32/tsp/ffa_helpers.h -Copyright: 2015-2023, Arm Limited. +Copyright: 2015-2024, Arm Limited. License: BSD-3-clause -Files: bl32/tsp/tsp.ld.S - bl32/tsp/tsp.mk -Copyright: 2013-2023, Arm Limited and Contributors. +Files: bl32/tsp/tsp_interrupt.c + bl32/tsp/tsp_private.h + bl32/tsp/tsp_timer.c +Copyright: 2013-2024, ARM Limited and Contributors. License: BSD-3-clause Files: common/* -Copyright: 2013-2023, ARM Limited and Contributors. +Copyright: 2013-2024, ARM Limited and Contributors. +License: BSD-3-clause + +Files: common/aarch64/* +Copyright: 2013-2024, Arm Limited and Contributors. License: BSD-3-clause -Files: common/aarch64/debug.S -Copyright: 2013-2023, Arm Limited and Contributors. +Files: common/aarch64/early_exceptions.S +Copyright: 2013-2024, ARM Limited and Contributors. License: BSD-3-clause Files: common/bl_common.c + common/fdt_wrappers.c common/feat_detect.c common/tf_log.c common/uuid.c -Copyright: 2013-2023, Arm Limited and Contributors. +Copyright: 2013-2024, Arm Limited and Contributors. License: BSD-3-clause Files: common/fdt_wrappers.mk common/tf_crc32.c -Copyright: 2015-2023, Arm Limited. +Copyright: 2015-2024, Arm Limited. License: BSD-3-clause Files: debian/* -Copyright: - 1989 Regents of the University of California. - 1998 Softweyr LLC. All rights reserved. - 2001 David E. O'Brien - 2005 MontaVista Software, Inc. - 2005 Nokia Corporation - 2011 The FreeBSD Foundation - 2012-2021 Roberto E. Vargas Caballero - 2013-2023 ARM Limited and Contributors. All rights reserved. - 2013-2023 Arm Limited and Contributors. All rights reserved. - 2014-2023 Arm Limited. All rights reserved. - 2014-2016 Freescale Semiconductor, Inc. - 2014 Linaro Limited. All rights reserved. - 2014-2023 STMicroelectronics - All Rights Reserved - 2014 STMicroelectronics International N.V. - 2015-2021 Broadcom - 2015-2023 NVIDIA Corporation. All rights reserved. - 2015-2023 Renesas Electronics Corporation. - 2015-2023 Renesas Electronics Corporation. All rights - 2015-2023 Renesas Electronics Corporation. All rights reserved. - 2016-2020 Marvell International Ltd. - 2016-2022 Linaro Limited. All rights reserved. - 2016-2021 Marvell International Ltd. - 2016-2023 NXP - 2017-2020 Arm Limited and Contributors. All rights reserved. - 2017-2023 NVIDIA CORPORATION. All rights reserved. - 2017-2022 NXP Semiconductors - 2017-2023 Nuvoton Ltd. - 2017-2023 Nuvoton Technology Corp. - 2017-2022 Xilinx, Inc. All rights reserved. - 2018 Andre Przywara <osp@andrep.de> - 2018-2020 Arm Limited and Contributors. - 2018 Icenowy Zheng <icenowy@aosc.io> - 2018-2019 Linaro - 2018-2022 Texas Instruments Incorporated - https://www.ti.com/ - 2018-2021 The Linux Foundation. All rights reserved. - 2019-2023 STMicroelectronics - All Rights Reserved - 2019-2020 Broadcom. - 2019 Carlo Caione <ccaione@baylibre.com> - 2019-2023 Intel Corporation. All rights reserved. - 2019-2022 Linaro Limited - 2019-2020 Linaro Limited and Contributors. - 2019-2023 Linaro Limited and Contributors. All rights reserved. - 2019-2023 MediaTek Inc. All rights reserved. - 2019-2022 NXP. All rights reserved. - 2019 Remi Pommarel <repk@triplefau.lt> - 2019 Repk repk@triplefau.lt - 2019-2022 Socionext Inc. All rights reserved. - 2019 Ying-Chun Liu (PaulLiu) <paul.liu@linaro.org> - 2020-2022 ARM Limited and Contributors. All rights reserved. - 2020-2023 Google LLC. All rights reserved. - 2020 Marek Behun, CZ.NIC - 2020 Marvell Technology Group Ltd. All rights reserved. - 2020 MediaTek Inc. All rights reserved. - 2020 NXP. - 2020 Nuvia Inc - 2020-2021 Sartura Ltd. - 2020-2021 Siemens AG - 2021 Xilinx Inc. - 2021 Arm - 2021 Arm Limited - 2021 Globalscale technologies, Inc. - 2021 Marek Behun <marek.behun@nic.cz> - 2021-2022 ProvenRun S.A.S. All rights reserved. - 2021 Semihalf. - 2021 Sipeed - 2021-2023 Stephan Gerhold <stephan@gerhold.net> - 2022-2023 Advanced Micro Devices, Inc. All rights reserved. - 2022-2023 Arm Ltd. All rights reserved. - 2022 Fujitsu Limited and Contributors. All rights reserved. - 2022 Leica Geosystems AG - 2022 Linaro - 2022-2023 Linaro. - 2022 Mediatek Inc. All rights reserved. - 2022 Qualcomm Innovation Center, Inc. All rights reserved. - 2022 The Hafnium Authors. - 2023 Advanced Micro Devices. All rights reserved. - 2023 Arm Limited. All rights reserved - 2023 Aspeed Technology Inc. - 2023 Pengutronix. All rights reserved. +Copyright: 2024, The ChromiumOS Authors. + 2024, Rockchip Inc. + 2024, Pengutronix Inc. + 2024, Mario Bălănică <mariobalanica02@gmail.com> + 2024, Linaro Limited and Contributors. + 2024, Arm Limited and Contributors. + 2024, Altera Corporation. + 2024, ARM Limited and Contributors. + 2023, Pengutronix. + 2023, Aspeed Technology Inc. + 2023, Advanced Micro Devices. + 2022-2024, Arm Ltd. + 2022-2024, Advanced Micro Devices Inc. + 2022, The Hafnium Authors. + 2022, Qualcomm Innovation Center, Inc. + 2022, Leica Geosystems AG + 2022, Fujitsu Limited and Contributors. + 2022, 2024, Mediatek Inc. + 2022, 2023, Linaro. + 2022, 2023, Advanced Micro Devices, Inc. + 2021-2024, ARM Limited. + 2021-2023, Stephan Gerhold <stephan@gerhold.net> + 2021-2023, Renesas Electronics Corporation. + 2021, Sipeed + 2021, Semihalf. + 2021, Marek Behun <marek.behun@nic.cz> + 2021, Globalscale technologies, Inc. + 2021, Arm Limited + 2021, Arm + 2021, 2022, ProvenRun S.A.S. + 2020-2023, Google LLC + 2020, Nuvia Inc + 2020, Marvell Technology Group Ltd. + 2020, Marek Behun, CZ.NIC + 2020, 2021, Siemens AG + 2020, 2021, Sartura Ltd. + 2019-2024, MediaTek Inc. + 2019-2024, Intel Corporation. + 2019-2022, Socionext Inc. + 2019-2022, NXP. + 2019-2022, Linaro Limited + 2019, Ying-Chun Liu (PaulLiu) <paul.liu@linaro.org> + 2019, Repk repk@triplefau.lt + 2019, Remi Pommarel <repk@triplefau.lt> + 2019, Carlo Caione <ccaione@baylibre.com> + 2019, 2020, Broadcom. + 2018-2024, Texas Instruments Incorporated - https://www.ti.com + 2018-2021, The Linux Foundation. + 2018, Icenowy Zheng <icenowy@aosc.io> + 2018, Andre Przywara <osp@andrep.de> + 2018, 2019, 2022, Linaro + 2017-2023, Nuvoton Technology Corp. + 2017-2023, Nuvoton Ltd. + 2017-2023, NVIDIA CORPORATION. + 2017-2022, Xilinx, Inc. + 2017-2022, NXP Semiconductors + 2016-2024, NXP + 2016-2024, Marvell International Ltd. + 2015-2024, Renesas Electronics Corporation + 2015-2024, NVIDIA Corporation. + 2015-2022, Xilinx Inc. + 2015-2021, Broadcom + 2014-2024, STMicroelectronics + 2014-2024, Arm Limited. + 2014-2016, Freescale Semiconductor, Inc. + 2014, STMicroelectronics International N.V. + 2014, 2016-2022, Linaro Limited. + 2012-2021, Roberto E. Vargas Caballero + 2011, The FreeBSD Foundation + 2005, Nokia Corporation + 2005, MontaVista Software, Inc. + 2001, David E. O'Brien + 1998, Softweyr LLC. + 1989, Regents of the University of California. License: BSD-3-Clause +Files: docs/* +Copyright: no-info-found +License: Apache-2.0 or BSD-2-clause or BSD-3-clause or Expat or NCSA or Zlib + Files: docs/Makefile -Copyright: 2015-2023, ARM Limited. + docs/conf.py +Copyright: 2015-2024, Arm Limited. License: BSD-3-clause Files: docs/_static/* -Copyright: 2015-2023, Arm Limited. -License: BSD-3-clause - -Files: docs/conf.py -Copyright: 2015-2023, Arm Limited. +Copyright: 2015-2024, Arm Limited. License: BSD-3-clause Files: docs/design/trusted-board-boot-build.rst Copyright: 2019-2022, Arm Limited. License: Apache-2.0 -Files: docs/license.rst -Copyright: no-info-found -License: BSD-2-clause and/or BSD-3-clause and/or Expat and/or NCSA and/or Zlib - Files: docs/process/contributing.rst -Copyright: 2015-2023, Arm Limited. -License: BSD-3-clause - -Files: docs/resources/* -Copyright: 2013-2023, Arm Limited and Contributors. +Copyright: 2015-2024, Arm Limited. License: BSD-3-clause -Files: docs/resources/diagrams/plantuml/* -Copyright: 2015-2023, Arm Limited. +Files: docs/resources/diagrams/Makefile +Copyright: 2013-2024, Arm Limited and Contributors. License: BSD-3-clause Files: docs/resources/diagrams/plantuml/bl2-loading-sp.puml docs/resources/diagrams/plantuml/fip-secure-partitions.puml docs/resources/diagrams/plantuml/sdei_explicit_dispatch.puml docs/resources/diagrams/plantuml/sdei_general.puml -Copyright: 2013-2023, ARM Limited and Contributors. +Copyright: 2013-2024, ARM Limited and Contributors. +License: BSD-3-clause + +Files: docs/resources/diagrams/plantuml/el3_spm_dfd.puml + docs/resources/diagrams/plantuml/spm_dfd.puml + docs/resources/diagrams/plantuml/tfa_arm_cca_dfd.puml + docs/resources/diagrams/plantuml/tfa_dfd.puml + docs/resources/diagrams/plantuml/tfa_rse_dfd.puml +Copyright: 2015-2024, Arm Limited. License: BSD-3-clause Files: drivers/* -Copyright: 2013-2023, ARM Limited and Contributors. +Copyright: 2015-2024, Renesas Electronics Corporation. +License: BSD-3-clause + +Files: drivers/allwinner/* +Copyright: 2013-2024, ARM Limited and Contributors. License: BSD-3-clause Files: drivers/amlogic/* @@ -359,107 +434,133 @@ Copyright: 2019, Remi Pommarel <repk@triplefau.lt> License: BSD-3-clause Files: drivers/amlogic/console/* -Copyright: 2013-2023, ARM Limited and Contributors. +Copyright: 2013-2024, ARM Limited and Contributors. License: BSD-3-clause -Files: drivers/arm/css/scp/css_pm_scmi.c -Copyright: 2013-2023, Arm Limited and Contributors. +Files: drivers/arm/* +Copyright: 2013-2024, ARM Limited and Contributors. +License: BSD-3-clause + +Files: drivers/arm/css/dsu/* +Copyright: 2015-2024, Arm Limited. +License: BSD-3-clause + +Files: drivers/arm/css/scmi/scmi_common.c +Copyright: 2013-2024, Arm Limited and Contributors. +License: BSD-3-clause + +Files: drivers/arm/css/scp/* +Copyright: 2013-2024, Arm Limited and Contributors. +License: BSD-3-clause + +Files: drivers/arm/css/scp/css_bom_bootloader.c + drivers/arm/css/scp/css_pm_scpi.c +Copyright: 2013-2024, ARM Limited and Contributors. +License: BSD-3-clause + +Files: drivers/arm/css/sds/sds.c +Copyright: 2013-2024, Arm Limited and Contributors. License: BSD-3-clause Files: drivers/arm/dcc/* -Copyright: 2015-2021, Xilinx Inc. +Copyright: 2022-2024, Advanced Micro Devices, Inc. + 2015-2022, Xilinx Inc. License: BSD-3-clause Files: drivers/arm/ethosn/* -Copyright: 2015-2023, Arm Limited. +Copyright: 2015-2024, Arm Limited. License: BSD-3-clause Files: drivers/arm/fvp/* -Copyright: 2013-2023, Arm Limited and Contributors. +Copyright: 2013-2024, Arm Limited and Contributors. License: BSD-3-clause Files: drivers/arm/gic/v2/gicv2.mk -Copyright: 2015-2023, Arm Limited. +Copyright: 2015-2024, Arm Limited. License: BSD-3-clause Files: drivers/arm/gic/v2/gicv2_main.c Copyright: 2021, 2022, ProvenRun S.A.S. - 2015-2023, Arm Limited and Contributors. + 2015-2024, Arm Limited and Contributors. License: BSD-3-clause Files: drivers/arm/gic/v3/* Copyright: 2020-2023, NVIDIA Corporation. - 2013-2023, Arm Limited and Contributors. -License: BSD-3-clause - -Files: drivers/arm/gic/v3/arm_gicv3_common.c - drivers/arm/gic/v3/gicdv3_helpers.c -Copyright: 2013-2023, ARM Limited and Contributors. + 2013-2024, Arm Limited and Contributors. License: BSD-3-clause Files: drivers/arm/gic/v3/gic600_multichip.c -Copyright: 2021-2023, NVIDIA Corporation. - 2019-2023, Arm Limited. +Copyright: 2021-2024, NVIDIA Corporation. + 2019-2024, Arm Limited. License: BSD-3-clause Files: drivers/arm/gic/v3/gic600_multichip_private.h -Copyright: 2015-2023, ARM Limited. +Copyright: 2015-2024, ARM Limited. License: BSD-3-clause Files: drivers/arm/gic/v3/gic600ae_fmu.c drivers/arm/gic/v3/gic600ae_fmu_helpers.c -Copyright: 2017-2022, NVIDIA Corporation. +Copyright: 2017-2022, 2024, NVIDIA Corporation. License: BSD-3-clause -Files: drivers/arm/gic/v3/gicrv3_helpers.c -Copyright: 2013-2023, Arm Limited and Contributors. +Files: drivers/arm/gic/v3/gicdv3_helpers.c +Copyright: 2013-2024, ARM Limited and Contributors. License: BSD-3-clause Files: drivers/arm/gic/v3/gicv3_private.h Copyright: 2015-2023, NVIDIA Corporation. - 2013-2023, ARM Limited and Contributors. + 2013-2024, ARM Limited and Contributors. License: BSD-3-clause Files: drivers/arm/mhu/* -Copyright: 2015-2023, Arm Limited. +Copyright: 2013-2024, Arm Limited and Contributors. +License: BSD-3-clause + +Files: drivers/arm/mhu/mhu_v2_x.c + drivers/arm/mhu/mhu_v2_x.h + drivers/arm/mhu/mhu_wrapper_v2_x.c +Copyright: 2015-2024, Arm Limited. License: BSD-3-clause -Files: drivers/arm/rss/* -Copyright: 2015-2023, Arm Limited. +Files: drivers/arm/rse/* +Copyright: 2015-2024, Arm Limited. License: BSD-3-clause Files: drivers/arm/sbsa/* -Copyright: 2015-2023, Arm Limited. +Copyright: 2015-2024, Arm Limited. License: BSD-3-clause Files: drivers/arm/scu/* -Copyright: 2015-2023, Arm Limited. +Copyright: 2015-2024, Arm Limited. License: BSD-3-clause Files: drivers/auth/* -Copyright: 2013-2023, Arm Limited and Contributors. +Copyright: 2013-2024, Arm Limited and Contributors. License: BSD-3-clause Files: drivers/auth/cca/* -Copyright: 2015-2023, Arm Limited. +Copyright: 2015-2024, Arm Limited. License: BSD-3-clause Files: drivers/auth/dualroot/* -Copyright: 2015-2023, Arm Limited. +Copyright: 2015-2024, Arm Limited. License: BSD-3-clause Files: drivers/auth/img_parser_mod.c -Copyright: 2013-2023, ARM Limited and Contributors. +Copyright: 2013-2024, ARM Limited and Contributors. License: BSD-3-clause Files: drivers/auth/mbedtls/mbedtls_common.mk - drivers/auth/mbedtls/mbedtls_psa_crypto.c -Copyright: 2015-2023, Arm Limited. +Copyright: 2015-2024, Arm Limited. +License: BSD-3-clause + +Files: drivers/auth/mbedtls/mbedtls_psa_crypto.c +Copyright: 2023, 2024, Arm Limited. License: BSD-3-clause Files: drivers/auth/mbedtls/mbedtls_x509.mk drivers/auth/mbedtls/mbedtls_x509_parser.c -Copyright: 2013-2023, ARM Limited and Contributors. +Copyright: 2013-2024, ARM Limited and Contributors. License: BSD-3-clause Files: drivers/brcm/* @@ -467,40 +568,77 @@ Copyright: 2015-2021, Broadcom License: BSD-3-clause Files: drivers/cadence/* -Copyright: 2019-2023, Intel Corporation. +Copyright: 2019-2024, Intel Corporation. +License: BSD-3-clause + +Files: drivers/cadence/emmc/* +Copyright: 2024, Altera Corporation. + 2019-2023, Intel Corporation. License: BSD-3-clause Files: drivers/cadence/uart/* -Copyright: 2013-2023, ARM Limited and Contributors. +Copyright: 2013-2024, ARM Limited and Contributors. +License: BSD-3-clause + +Files: drivers/cfi/* +Copyright: 2013-2024, ARM Limited and Contributors. License: BSD-3-clause Files: drivers/clk/* -Copyright: 2014-2023, STMicroelectronics +Copyright: 2014-2024, STMicroelectronics +License: BSD-3-clause + +Files: drivers/console/* +Copyright: 2013-2024, ARM Limited and Contributors. +License: BSD-3-clause + +Files: drivers/coreboot/* +Copyright: 2013-2024, ARM Limited and Contributors. +License: BSD-3-clause + +Files: drivers/delay_timer/* +Copyright: 2013-2024, Arm Limited and Contributors. License: BSD-3-clause Files: drivers/delay_timer/generic_delay_timer.c -Copyright: 2015-2023, NVIDIA Corporation. - 2013-2023, ARM Limited and Contributors. +Copyright: 2020-2023, NVIDIA Corporation. + 2013-2024, Arm Limited and Contributors. License: BSD-3-clause Files: drivers/fwu/* -Copyright: 2015-2023, Arm Limited. +Copyright: 2015-2024, Arm Limited. +License: BSD-3-clause + +Files: drivers/gpio/* +Copyright: 2013-2024, ARM Limited and Contributors. +License: BSD-3-clause + +Files: drivers/imx/* +Copyright: 2013-2024, ARM Limited and Contributors. License: BSD-3-clause Files: drivers/imx/uart/* Copyright: Linaro 2018, Limited and Contributors. License: BSD-3-clause +Files: drivers/intel/* +Copyright: 2013-2024, ARM Limited and Contributors. +License: BSD-3-clause + +Files: drivers/io/* +Copyright: 2013-2024, ARM Limited and Contributors. +License: BSD-3-clause + Files: drivers/io/io_encrypted.c Copyright: 2019, 2020, Linaro Limited. License: BSD-3-clause Files: drivers/marvell/* -Copyright: 2016-2021, Marvell International Ltd. +Copyright: 2016-2024, Marvell International Ltd. License: BSD-3-clause Files: drivers/measured_boot/* -Copyright: 2015-2023, Arm Limited. +Copyright: 2015-2024, Arm Limited. License: BSD-3-clause Files: drivers/mentor/* @@ -508,12 +646,16 @@ Copyright: 2018, Marvell International Ltd. 2018, Icenowy Zheng <icenowy@aosc.io> License: BSD-3-clause +Files: drivers/mmc/* +Copyright: 2013-2024, ARM Limited and Contributors. +License: BSD-3-clause + Files: drivers/mtd/* -Copyright: 2014-2023, STMicroelectronics +Copyright: 2014-2024, STMicroelectronics License: BSD-3-clause Files: drivers/nxp/* -Copyright: 2016-2023, NXP +Copyright: 2016-2024, NXP License: BSD-3-clause Files: drivers/nxp/auth/csf_hdr_parser/cot.c @@ -534,19 +676,19 @@ Copyright: 2020, NXP License: BSD-3-clause Files: drivers/nxp/console/16550_console.S -Copyright: 2013-2023, ARM Limited and Contributors. +Copyright: 2013-2024, ARM Limited and Contributors. License: BSD-3-clause -Files: drivers/partition/partition.c -Copyright: 2013-2023, Arm Limited and Contributors. +Files: drivers/partition/* +Copyright: 2013-2024, Arm Limited and Contributors. License: BSD-3-clause -Files: drivers/rambus/* -Copyright: 2020, Marvell Technology Group Ltd. +Files: drivers/partition/gpt.c +Copyright: 2013-2024, ARM Limited and Contributors. License: BSD-3-clause -Files: drivers/renesas/* -Copyright: 2015-2023, Renesas Electronics Corporation. +Files: drivers/rambus/* +Copyright: 2020, Marvell Technology Group Ltd. License: BSD-3-clause Files: drivers/renesas/common/auth/* @@ -559,11 +701,11 @@ License: BSD-3-clause Files: drivers/renesas/common/ddr/ddr_a/boot_init_dram_regdef.h drivers/renesas/common/ddr/ddr_a/ddr_init_v3m.c -Copyright: 2015-2019, Renesas Electronics Corporation +Copyright: 2015-2024, Renesas Electronics Corporation License: BSD-3-clause Files: drivers/renesas/common/ddr_regs.h -Copyright: 2015-2019, Renesas Electronics Corporation +Copyright: 2015-2024, Renesas Electronics Corporation License: BSD-3-clause Files: drivers/renesas/common/emmc/emmc_interrupt.c @@ -575,76 +717,101 @@ Copyright: 2015-2023, Renesas Electronics Corporation. All rights License: BSD-3-clause Files: drivers/renesas/rcar/pfc/V3M/* -Copyright: 2015-2019, Renesas Electronics Corporation +Copyright: 2015-2024, Renesas Electronics Corporation License: BSD-3-clause Files: drivers/renesas/rcar/qos/D3/qos_init_d3.h -Copyright: 2015-2019, Renesas Electronics Corporation +Copyright: 2015-2024, Renesas Electronics Corporation License: BSD-3-clause Files: drivers/renesas/rcar/qos/V3M/* -Copyright: 2015-2019, Renesas Electronics Corporation +Copyright: 2015-2024, Renesas Electronics Corporation License: BSD-3-clause Files: drivers/rpi3/* -Copyright: 2019, Ying-Chun Liu (PaulLiu) <paul.liu@linaro.org> +Copyright: 2013-2024, Arm Limited and Contributors. +License: BSD-3-clause + +Files: drivers/rpi3/gpio/* +Copyright: 2024, Arm Limited. + 2019, Ying-Chun Liu (PaulLiu) <paul.liu@linaro.org> 2019, Linaro Limited License: BSD-3-clause Files: drivers/rpi3/mailbox/* -Copyright: 2013-2023, ARM Limited and Contributors. +Copyright: 2013-2024, ARM Limited and Contributors. License: BSD-3-clause -Files: drivers/rpi3/rng/* -Copyright: 2013-2023, ARM Limited and Contributors. +Files: drivers/rpi3/sdhost/* +Copyright: 2019, Ying-Chun Liu (PaulLiu) <paul.liu@linaro.org> + 2019, Linaro Limited License: BSD-3-clause Files: drivers/scmi-msg/* Copyright: 2019-2022, Linaro Limited - 2015-2022, Arm Limited and Contributors. + 2015-2024, Arm Limited and Contributors. License: BSD-3-clause Files: drivers/scmi-msg/power_domain.h -Copyright: 2016-2023, NXP + drivers/scmi-msg/sensor.c +Copyright: 2016-2024, NXP +License: BSD-3-clause + +Files: drivers/scmi-msg/sensor.h +Copyright: 2023, 2024, NXP License: BSD-3-clause Files: drivers/st/* -Copyright: 2014-2023, STMicroelectronics +Copyright: 2014-2024, STMicroelectronics License: BSD-3-clause Files: drivers/st/clk/* -Copyright: 2017-2023, STMicroelectronics +Copyright: 2017-2024, STMicroelectronics License: GPL-2 Files: drivers/st/clk/stm32mp_clkfunc.c -Copyright: 2014-2023, STMicroelectronics +Copyright: 2014-2024, STMicroelectronics License: BSD-3-clause Files: drivers/st/ddr/stm32mp1_ddr.c drivers/st/ddr/stm32mp1_ram.c -Copyright: 2017-2023, STMicroelectronics + drivers/st/ddr/stm32mp2_ram.c +Copyright: 2017-2024, STMicroelectronics License: GPL-2 Files: drivers/st/fmc/* -Copyright: 2017-2023, STMicroelectronics +Copyright: 2017-2024, STMicroelectronics License: GPL-2 Files: drivers/st/spi/* -Copyright: 2017-2023, STMicroelectronics +Copyright: 2017-2024, STMicroelectronics License: GPL-2 Files: drivers/st/uart/aarch32/* -Copyright: 2013-2023, Arm Limited and Contributors. +Copyright: 2013-2024, Arm Limited and Contributors. +License: BSD-3-clause + +Files: drivers/synopsys/* +Copyright: 2013-2024, ARM Limited and Contributors. +License: BSD-3-clause + +Files: drivers/ti/* +Copyright: 2013-2024, ARM Limited and Contributors. +License: BSD-3-clause + +Files: drivers/ufs/* +Copyright: 2013-2024, ARM Limited and Contributors. License: BSD-3-clause Files: drivers/usb/* -Copyright: 2014-2023, STMicroelectronics +Copyright: 2014-2024, STMicroelectronics License: BSD-3-clause Files: fdts/a5ds.dts fdts/corstone700.dtsi fdts/corstone700_fpga.dts fdts/corstone700_fvp.dts + fdts/dualroot_cot_descriptors.dtsi fdts/fvp-base-gicv3-psci-dynamiq-2t.dts fdts/fvp-ve-Cortex-A5x1.dts fdts/fvp-ve-Cortex-A7x1.dts @@ -654,13 +821,21 @@ Files: fdts/a5ds.dts fdts/morello-fvp.dts fdts/morello-soc.dts fdts/morello.dtsi - fdts/tc.dts -Copyright: 2015-2023, Arm Limited. + fdts/rd1ae.dts + fdts/tbbr_cot_descriptors.dtsi + fdts/tc-base.dtsi + fdts/tc2.dts + fdts/tc3-4-base.dtsi + fdts/tc3.dts + fdts/tc4.dts +Copyright: 2015-2024, Arm Limited. License: BSD-3-clause -Files: fdts/cot_descriptors.dtsi - fdts/stm32mp1-cot-descriptors.dtsi -Copyright: 2015-2023, ARM Limited. +Files: fdts/cca_cot_descriptors.dtsi + fdts/tc-common.dtsi + fdts/tc-fpga.dtsi + fdts/tc-fvp.dtsi +Copyright: 2023, 2024, Arm Limited. License: BSD-3-clause Files: fdts/fvp-base-gicv2-psci.dts @@ -673,7 +848,7 @@ Files: fdts/fvp-base-gicv2-psci.dts fdts/fvp-foundation-gicv2-psci.dts fdts/fvp-foundation-gicv3-psci.dts fdts/fvp-foundation-motherboard.dtsi -Copyright: 2013-2023, ARM Limited and Contributors. +Copyright: 2013-2024, ARM Limited and Contributors. License: BSD-3-clause Files: fdts/fvp-base-psci-common.dtsi @@ -682,7 +857,11 @@ License: GPL-2 Files: fdts/fvp-defs-dynamiq.dtsi fdts/fvp-defs.dtsi -Copyright: 2013-2023, Arm Limited and Contributors. +Copyright: 2013-2024, Arm Limited and Contributors. +License: BSD-3-clause + +Files: fdts/stm32mp1-cot-descriptors.dtsi +Copyright: 2015-2024, ARM Limited. License: BSD-3-clause Files: fdts/stm32mp13-ddr.dtsi @@ -690,7 +869,10 @@ Files: fdts/stm32mp13-ddr.dtsi fdts/stm32mp15-ddr.dtsi fdts/stm32mp15-ddr3-1x4Gb-1066-binG.dtsi fdts/stm32mp15-ddr3-2x4Gb-1066-binG.dtsi -Copyright: 2017-2023, STMicroelectronics + fdts/stm32mp25-ddr.dtsi + fdts/stm32mp25-ddr4-2x16Gbits-2x16bits-1200MHz.dtsi + fdts/stm32mp25-ddr4-2x8Gbits-2x16bits-1200MHz.dtsi +Copyright: 2017-2024, STMicroelectronics License: GPL-2 Files: fdts/stm32mp15-ddr3-dhsom-2x4Gb-1066-binG.dtsi @@ -699,7 +881,9 @@ License: GPL-2 Files: fdts/stm32mp157a-dhcor-avenger96.dts fdts/stm32mp15xx-dhcor-avenger96.dtsi -Copyright: Linaro Ltd 2019, - / 2022, DH electronics GmbH / 2020, Marek Vasut <marex@denx.de> +Copyright: 2022, DH electronics GmbH + 2020, Marek Vasut <marex@denx.de> + 2019, Linaro Ltd License: GPL-2 Files: fdts/stm32mp157c-dhcom-pdk2-fw-config.dts @@ -713,44 +897,67 @@ Copyright: 2022, DH electronics GmbH License: GPL-2 Files: fdts/stm32mp15xx-dhcom-som.dtsi -Copyright: 2023, STMicroelectronics +Copyright: 2023, 2024, STMicroelectronics 2022, DH electronics GmbH 2019, 2020, Marek Vasut <marex@denx.de> License: GPL-2 Files: fdts/stm32mp15xx-dhcor-som.dtsi -Copyright: Linaro Ltd 2019, - / 2023, STMicroelectronics - / 2022, DH electronics GmbH / 2020, Marek Vasut <marex@denx.de> +Copyright: 2023, 2024, STMicroelectronics + 2022, DH electronics GmbH + 2020, Marek Vasut <marex@denx.de> + 2019, Linaro Ltd License: GPL-2 Files: include/* -Copyright: 2013-2023, ARM Limited and Contributors. +Copyright: 2013-2024, Arm Limited and Contributors. +License: BSD-3-clause + +Files: include/arch/* +Copyright: 2013-2024, ARM Limited and Contributors. License: BSD-3-clause Files: include/arch/aarch32/arch.h -Copyright: 2013-2023, Arm Limited and Contributors. +Copyright: 2013-2024, Arm Limited and Contributors. License: BSD-3-clause Files: include/arch/aarch32/arch_features.h -Copyright: 2015-2023, Arm Limited. +Copyright: 2015-2024, Arm Limited. License: BSD-3-clause Files: include/arch/aarch32/arch_helpers.h Copyright: 2021, 2022, ProvenRun S.A.S. - 2016-2022, ARM Limited and Contributors. + 2016-2024, ARM Limited and Contributors. License: BSD-3-clause Files: include/arch/aarch64/arch.h Copyright: 2020-2023, NVIDIA Corporation. - 2013-2023, Arm Limited and Contributors. + 2013-2024, Arm Limited and Contributors. License: BSD-3-clause Files: include/arch/aarch64/arch_features.h -Copyright: 2015-2023, Arm Limited. +Copyright: 2015-2024, Arm Limited. License: BSD-3-clause Files: include/arch/aarch64/arch_helpers.h include/arch/aarch64/el3_common_macros.S -Copyright: 2013-2023, Arm Limited and Contributors. +Copyright: 2013-2024, Arm Limited and Contributors. +License: BSD-3-clause + +Files: include/bl1/tbbr/* +Copyright: 2013-2024, ARM Limited and Contributors. +License: BSD-3-clause + +Files: include/bl2/* +Copyright: 2013-2024, ARM Limited and Contributors. +License: BSD-3-clause + +Files: include/bl2u/* +Copyright: 2013-2024, ARM Limited and Contributors. +License: BSD-3-clause + +Files: include/bl31/* +Copyright: 2013-2024, ARM Limited and Contributors. License: BSD-3-clause Files: include/bl31/sync_handle.h @@ -760,28 +967,59 @@ License: BSD-3-clause Files: include/bl32/payloads/* Copyright: 2015-2023, NVIDIA Corporation. - 2013-2023, ARM Limited and Contributors. + 2013-2024, ARM Limited and Contributors. License: BSD-3-clause Files: include/bl32/pnc/* Copyright: 2021, 2022, ProvenRun S.A.S. License: BSD-3-clause -Files: include/common/debug.h +Files: include/bl32/sp_min/* +Copyright: 2013-2024, ARM Limited and Contributors. +License: BSD-3-clause + +Files: include/bl32/tsp/platform_tsp.h +Copyright: 2013-2024, ARM Limited and Contributors. +License: BSD-3-clause + +Files: include/common/* +Copyright: 2013-2024, ARM Limited and Contributors. +License: BSD-3-clause + +Files: include/common/bl_common.h + include/common/debug.h include/common/ep_info.h include/common/fdt_wrappers.h include/common/feat_detect.h + include/common/par.h include/common/uuid.h -Copyright: 2013-2023, Arm Limited and Contributors. +Copyright: 2013-2024, Arm Limited and Contributors. License: BSD-3-clause -Files: include/common/nv_cntr_ids.h +Files: include/common/build_message.h + include/common/nv_cntr_ids.h include/common/tf_crc32.h -Copyright: 2015-2023, Arm Limited. +Copyright: 2015-2024, Arm Limited. License: BSD-3-clause -Files: include/common/tbbr/cot_def.h -Copyright: 2013-2023, Arm Limited and Contributors. +Files: include/common/sha_common_macros.h +Copyright: 2017-2022, 2024, NVIDIA Corporation. +License: BSD-3-clause + +Files: include/common/tbbr/* +Copyright: 2013-2024, Arm Limited and Contributors. +License: BSD-3-clause + +Files: include/common/tbbr/tbbr_img_def.h +Copyright: 2013-2024, ARM Limited and Contributors. +License: BSD-3-clause + +Files: include/drivers/* +Copyright: 2016-2024, NXP +License: BSD-3-clause + +Files: include/drivers/allwinner/* +Copyright: 2013-2024, ARM Limited and Contributors. License: BSD-3-clause Files: include/drivers/amlogic/* @@ -789,11 +1027,26 @@ Copyright: 2019, Remi Pommarel <repk@triplefau.lt> License: BSD-3-clause Files: include/drivers/amlogic/meson_console.h -Copyright: 2013-2023, ARM Limited and Contributors. +Copyright: 2013-2024, ARM Limited and Contributors. +License: BSD-3-clause + +Files: include/drivers/arm/* +Copyright: 2013-2024, ARM Limited and Contributors. +License: BSD-3-clause + +Files: include/drivers/arm/css/* +Copyright: 2013-2024, Arm Limited and Contributors. +License: BSD-3-clause + +Files: include/drivers/arm/css/css_mhu.h + include/drivers/arm/css/css_scp.h + include/drivers/arm/css/css_scpi.h +Copyright: 2013-2024, ARM Limited and Contributors. License: BSD-3-clause Files: include/drivers/arm/dcc.h -Copyright: 2015-2021, Xilinx Inc. +Copyright: 2022-2024, Advanced Micro Devices, Inc. + 2015-2022, Xilinx Inc. License: BSD-3-clause Files: include/drivers/arm/ethosn.h @@ -801,10 +1054,10 @@ Files: include/drivers/arm/ethosn.h include/drivers/arm/ethosn_fip.h include/drivers/arm/ethosn_oid.h include/drivers/arm/mhu.h - include/drivers/arm/rss_comms.h + include/drivers/arm/rse_comms.h include/drivers/arm/sbsa.h include/drivers/arm/scu.h -Copyright: 2015-2023, Arm Limited. +Copyright: 2015-2024, Arm Limited. License: BSD-3-clause Files: include/drivers/arm/gic600_multichip.h @@ -813,32 +1066,37 @@ Copyright: 2023, NVIDIA Corporation. License: BSD-3-clause Files: include/drivers/arm/gic600ae_fmu.h -Copyright: 2017-2022, NVIDIA Corporation. +Copyright: 2017-2022, 2024, NVIDIA Corporation. License: BSD-3-clause Files: include/drivers/arm/gicv2.h Copyright: 2021, 2022, ProvenRun S.A.S. - 2015-2023, Arm Limited and Contributors. + 2015-2024, Arm Limited and Contributors. License: BSD-3-clause Files: include/drivers/arm/gicv3.h -Copyright: 2013-2023, Arm Limited and Contributors. +Copyright: 2013-2024, Arm Limited and Contributors. +License: BSD-3-clause + +Files: include/drivers/auth/* +Copyright: 2013-2024, ARM Limited and Contributors. License: BSD-3-clause Files: include/drivers/auth/crypto_mod.h -Copyright: 2013-2023, Arm Limited and Contributors. + include/drivers/auth/tbbr_cot_common.h +Copyright: 2013-2024, Arm Limited and Contributors. License: BSD-3-clause Files: include/drivers/auth/mbedtls/* -Copyright: 2015-2023, Arm Limited. +Copyright: 2022-2024, Arm Ltd. License: BSD-3-clause Files: include/drivers/auth/mbedtls/mbedtls_common.h -Copyright: 2013-2023, ARM Limited and Contributors. +Copyright: 2013-2024, ARM Limited and Contributors. License: BSD-3-clause -Files: include/drivers/auth/mbedtls/psa_mbedtls_config.h -Copyright: 2022, 2023, Arm Ltd. +Files: include/drivers/auth/mbedtls/mbedtls_config-3.h +Copyright: 2023, 2024, Arm Limited. License: BSD-3-clause Files: include/drivers/brcm/* @@ -846,16 +1104,21 @@ Copyright: 2015-2021, Broadcom License: BSD-3-clause Files: include/drivers/cadence/* -Copyright: 2019-2023, Intel Corporation. +Copyright: 2019-2024, Intel Corporation. License: BSD-3-clause Files: include/drivers/cadence/cdns_sdmmc.h -Copyright: 2019-2023, ARM Limited and Contributors. +Copyright: 2024, Altera Corporation. + 2019-2024, ARM Limited and Contributors. 2019-2023, Intel Corporation. License: BSD-3-clause Files: include/drivers/cadence/cdns_uart.h -Copyright: 2013-2023, ARM Limited and Contributors. +Copyright: 2013-2024, ARM Limited and Contributors. +License: BSD-3-clause + +Files: include/drivers/cfi/* +Copyright: 2013-2024, ARM Limited and Contributors. License: BSD-3-clause Files: include/drivers/clk.h @@ -865,16 +1128,35 @@ Files: include/drivers/clk.h include/drivers/spi_nand.h include/drivers/spi_nor.h include/drivers/usb_device.h -Copyright: 2014-2023, STMicroelectronics +Copyright: 2014-2024, STMicroelectronics +License: BSD-3-clause + +Files: include/drivers/console.h + include/drivers/console_assertions.h + include/drivers/dw_ufs.h + include/drivers/generic_delay_timer.h + include/drivers/gpio.h + include/drivers/mmc.h + include/drivers/ufs.h +Copyright: 2013-2024, ARM Limited and Contributors. +License: BSD-3-clause + +Files: include/drivers/coreboot/* +Copyright: 2013-2024, ARM Limited and Contributors. License: BSD-3-clause Files: include/drivers/delay_timer.h -Copyright: 2019, Linaro Limited - 2015-2019, ARM Limited and Contributors. + include/drivers/scmi-msg.h +Copyright: 2019-2022, Linaro Limited + 2015-2024, Arm Limited and Contributors. License: BSD-3-clause Files: include/drivers/fwu/* -Copyright: 2015-2023, Arm Limited. +Copyright: 2015-2024, Arm Limited. +License: BSD-3-clause + +Files: include/drivers/io/* +Copyright: 2013-2024, ARM Limited and Contributors. License: BSD-3-clause Files: include/drivers/io/io_encrypted.h @@ -882,11 +1164,11 @@ Copyright: 2019, 2020, Linaro Limited. License: BSD-3-clause Files: include/drivers/marvell/* -Copyright: 2016-2021, Marvell International Ltd. +Copyright: 2016-2024, Marvell International Ltd. License: BSD-3-clause Files: include/drivers/measured_boot/* -Copyright: 2015-2023, Arm Limited. +Copyright: 2015-2024, Arm Limited. License: BSD-3-clause Files: include/drivers/mentor/* @@ -899,21 +1181,17 @@ Copyright: 2017-2023, Nuvoton Ltd. License: BSD-3-clause Files: include/drivers/nuvoton/npcm845x/npcm845x_lpuart.h -Copyright: 2017-2023, Nuvoton Ltd. +Copyright: 2022, 2023, Nuvoton Ltd. 2015-2023, ARM Limited and Contributors. License: BSD-3-clause -Files: include/drivers/nxp/* -Copyright: 2016-2023, NXP -License: BSD-3-clause - Files: include/drivers/nxp/sd/* Copyright: 2017-2021, NXP 2014-2016, Freescale Semiconductor, Inc. License: BSD-3-clause Files: include/drivers/partition/* -Copyright: 2013-2023, Arm Limited and Contributors. +Copyright: 2013-2024, Arm Limited and Contributors. License: BSD-3-clause Files: include/drivers/partition/efi.h @@ -922,7 +1200,7 @@ Copyright: 2022, STMicroelectronics License: BSD-3-clause Files: include/drivers/partition/mbr.h -Copyright: 2013-2023, ARM Limited and Contributors. +Copyright: 2013-2024, ARM Limited and Contributors. License: BSD-3-clause Files: include/drivers/rambus/* @@ -930,70 +1208,80 @@ Copyright: 2020, Marvell Technology Group Ltd. License: BSD-3-clause Files: include/drivers/renesas/* -Copyright: 2015-2023, Renesas Electronics Corporation. +Copyright: 2015-2024, Renesas Electronics Corporation. License: BSD-3-clause Files: include/drivers/rpi3/* -Copyright: 2019, Ying-Chun Liu (PaulLiu) <paul.liu@linaro.org> - 2019, Linaro Limited -License: BSD-3-clause - -Files: include/drivers/rpi3/mailbox/* -Copyright: 2013-2023, Arm Limited and Contributors. +Copyright: 2013-2024, Arm Limited and Contributors. License: BSD-3-clause -Files: include/drivers/rpi3/rng/* -Copyright: 2013-2023, Arm Limited and Contributors. +Files: include/drivers/rpi3/gpio/* +Copyright: 2019, Ying-Chun Liu (PaulLiu) <paul.liu@linaro.org> + 2019, Linaro Limited License: BSD-3-clause -Files: include/drivers/scmi-msg.h -Copyright: 2019-2022, Linaro Limited - 2015-2022, Arm Limited and Contributors. +Files: include/drivers/rpi3/sdhost/* +Copyright: 2019, Ying-Chun Liu (PaulLiu) <paul.liu@linaro.org> + 2019, Linaro Limited License: BSD-3-clause Files: include/drivers/scmi.h -Copyright: 2013-2023, Arm Limited and Contributors. +Copyright: 2013-2024, Arm Limited and Contributors. License: BSD-3-clause Files: include/drivers/st/* -Copyright: 2014-2023, STMicroelectronics +Copyright: 2014-2024, STMicroelectronics License: BSD-3-clause Files: include/drivers/st/stm32_console.h -Copyright: 2013-2023, ARM Limited and Contributors. +Copyright: 2013-2024, ARM Limited and Contributors. License: BSD-3-clause Files: include/drivers/st/stm32_fmc2_nand.h include/drivers/st/stm32_qspi.h include/drivers/st/stm32mp1_ddr.h include/drivers/st/stm32mp1_ddr_regs.h + include/drivers/st/stm32mp2_ddr.h + include/drivers/st/stm32mp2_ddr_regs.h include/drivers/st/stm32mp_ddr.h include/drivers/st/stm32mp_ddrctrl_regs.h -Copyright: 2017-2023, STMicroelectronics +Copyright: 2017-2024, STMicroelectronics License: GPL-2 +Files: include/drivers/synopsys/* +Copyright: 2013-2024, ARM Limited and Contributors. +License: BSD-3-clause + +Files: include/drivers/ti/* +Copyright: 2013-2024, ARM Limited and Contributors. +License: BSD-3-clause + Files: include/dt-bindings/* -Copyright: 2017-2023, STMicroelectronics +Copyright: 2017-2024, STMicroelectronics License: GPL-2 Files: include/dt-bindings/interrupt-controller/* Copyright: 2019-2021, Arm Limited and Contributors. License: Expat +Files: include/export/* +Copyright: 2013-2024, ARM Limited and Contributors. +License: BSD-3-clause + Files: include/export/common/ep_info_exp.h -Copyright: 2013-2023, Arm Limited and Contributors. +Copyright: 2013-2024, Arm Limited and Contributors. License: BSD-3-clause Files: include/export/lib/* -Copyright: 2013-2023, Arm Limited and Contributors. +Copyright: 2013-2024, Arm Limited and Contributors. License: BSD-3-clause -Files: include/lib/* -Copyright: 2013-2023, Arm Limited and Contributors. +Files: include/lib/cpus/* +Copyright: 2015-2024, Arm Limited. License: BSD-3-clause -Files: include/lib/cpus/aarch64/* -Copyright: 2015-2023, Arm Limited. +Files: include/lib/cpus/aarch32/* +Copyright: 2013-2024, Arm Limited and Contributors. License: BSD-3-clause Files: include/lib/cpus/aarch64/a64fx.h @@ -1016,60 +1304,73 @@ Files: include/lib/cpus/aarch64/aem_generic.h include/lib/cpus/aarch64/neoverse_e1.h include/lib/cpus/aarch64/neoverse_n1.h include/lib/cpus/aarch64/qemu_max.h -Copyright: 2013-2023, Arm Limited and Contributors. +Copyright: 2013-2024, Arm Limited and Contributors. License: BSD-3-clause Files: include/lib/cpus/aarch64/cortex_a57.h Copyright: 2020-2023, NVIDIA Corporation. - 2013-2023, Arm Limited and Contributors. + 2013-2024, Arm Limited and Contributors. License: BSD-3-clause Files: include/lib/cpus/aarch64/cortex_a78_ae.h -Copyright: 2021-2023, NVIDIA Corporation. - 2019-2023, Arm Limited. +Copyright: 2021-2024, NVIDIA Corporation. + 2019-2024, Arm Limited. License: BSD-3-clause Files: include/lib/cpus/aarch64/cortex_x1.h Copyright: 2020, 2022, 2023, Google LLC. License: BSD-3-clause +Files: include/lib/cpus/cpu_ops.h +Copyright: 2023, 2024, Arm Limited and Contributors. +License: BSD-3-clause + +Files: include/lib/cpus/errata.h + include/lib/cpus/wa_cve_2017_5715.h + include/lib/cpus/wa_cve_2018_3639.h + include/lib/cpus/wa_cve_2022_23960.h +Copyright: 2013-2024, Arm Limited and Contributors. +License: BSD-3-clause + Files: include/lib/debugfs.h -Copyright: 2015-2023, Arm Limited. +Copyright: 2015-2024, Arm Limited. +License: BSD-3-clause + +Files: include/lib/dice/* +Copyright: 2020, Google LLC +License: Apache-2.0 + +Files: include/lib/el3_runtime/simd_ctx.h +Copyright: 2024, Arm Limited and Contributors. + 2022, Google LLC. License: BSD-3-clause Files: include/lib/extensions/brbe.h + include/lib/extensions/fgt2.h include/lib/extensions/pmuv3.h include/lib/extensions/sys_reg_trace.h + include/lib/extensions/tcr2.h include/lib/extensions/trbe.h include/lib/extensions/trf.h -Copyright: 2015-2023, Arm Limited. +Copyright: 2015-2024, Arm Limited. License: BSD-3-clause Files: include/lib/extensions/ras.h include/lib/extensions/ras_arch.h Copyright: 2020-2023, NVIDIA Corporation. - 2013-2023, Arm Limited and Contributors. + 2013-2024, Arm Limited and Contributors. License: BSD-3-clause Files: include/lib/fconf/* -Copyright: 2015-2023, Arm Limited. +Copyright: 2015-2024, Arm Limited. License: BSD-3-clause Files: include/lib/gpt_rme/* -Copyright: 2015-2023, Arm Limited. -License: BSD-3-clause - -Files: include/lib/libc/* -Copyright: 2018, 2019, Arm Limited and Contributors. - 2012-2021, Roberto E. Vargas Caballero -License: BSD-3-clause - -Files: include/lib/libc/aarch32/* -Copyright: 2013-2023, Arm Limited and Contributors. +Copyright: 2015-2024, Arm Limited. License: BSD-3-clause Files: include/lib/libc/aarch32/endian_.h -Copyright: 2001, David E. OBrien +Copyright: 2001, David E. O'Brien License: BSD-3-clause Files: include/lib/libc/aarch32/float.h @@ -1082,12 +1383,8 @@ Copyright: 2020, Broadcom 2020, Arm Limited and Contributors. License: BSD-3-clause -Files: include/lib/libc/aarch64/* -Copyright: 2013-2023, Arm Limited and Contributors. -License: BSD-3-clause - Files: include/lib/libc/aarch64/endian_.h -Copyright: 2001, David E. OBrien +Copyright: 2001, David E. O'Brien License: BSD-3-clause Files: include/lib/libc/aarch64/float.h @@ -1104,16 +1401,9 @@ Files: include/lib/libc/arm_acle.h Copyright: 2021, Arm Limited License: BSD-3-clause -Files: include/lib/libc/assert.h - include/lib/libc/cdefs.h - include/lib/libc/setjmp.h - include/lib/libc/stdbool.h -Copyright: 2013-2023, Arm Limited and Contributors. -License: BSD-3-clause - Files: include/lib/libc/endian.h Copyright: 2002, Thomas Moestl <tmm@FreeBSD.org> -License: BSD-2-Clause-FreeBSD and/or BSD-2-clause +License: BSD-2-Clause-FreeBSD or BSD-2-clause Files: include/lib/libc/errno.h Copyright: UNIX System Laboratories, Inc. @@ -1125,6 +1415,17 @@ Copyright: 2020, Broadcom 2020, Arm Limited and Contributors. License: BSD-3-clause +Files: include/lib/libc/limits.h + include/lib/libc/stdarg.h + include/lib/libc/stddef.h + include/lib/libc/stdint.h + include/lib/libc/stdio.h + include/lib/libc/stdlib.h + include/lib/libc/time.h +Copyright: 2018, 2019, Arm Limited and Contributors. + 2012-2021, Roberto E. Vargas Caballero +License: BSD-3-clause + Files: include/lib/libc/string.h Copyright: 2023, Intel Corporation. 2018-2020, Arm Limited and Contributors. @@ -1136,29 +1437,41 @@ Copyright: 1988-1991, 1993, The Regents of the University of California. License: BSD-3-clause Files: include/lib/mpmm/* -Copyright: 2015-2023, Arm Limited. +Copyright: 2015-2024, Arm Limited. License: BSD-3-clause Files: include/lib/pmf/aarch32/* -Copyright: 2015-2023, Arm Limited. +Copyright: 2015-2024, Arm Limited. License: BSD-3-clause Files: include/lib/psa/* -Copyright: 2015-2023, Arm Limited. +Copyright: 2015-2024, Arm Limited. +License: BSD-3-clause + +Files: include/lib/psa/cca_attestation.h +Copyright: 2013-2024, Arm Limited and Contributors. License: BSD-3-clause Files: include/lib/psci/psci.h Copyright: 2020-2023, NVIDIA Corporation. - 2013-2023, Arm Limited and Contributors. + 2013-2024, Arm Limited and Contributors. License: BSD-3-clause Files: include/lib/transfer_list.h -Copyright: 2019-2023, Linaro Limited and Contributors. +Copyright: 2019-2024, Linaro Limited and Contributors. License: BSD-3-clause Files: include/lib/utils_def.h Copyright: 2020-2023, NVIDIA Corporation. - 2013-2023, Arm Limited and Contributors. + 2013-2024, Arm Limited and Contributors. +License: BSD-3-clause + +Files: include/plat/* +Copyright: 2016-2024, Marvell International Ltd. +License: BSD-3-clause + +Files: include/plat/arm/* +Copyright: 2013-2024, ARM Limited and Contributors. License: BSD-3-clause Files: include/plat/arm/common/arm_def.h @@ -1167,45 +1480,41 @@ Files: include/plat/arm/common/arm_def.h include/plat/arm/common/arm_sip_svc.h include/plat/arm/common/fconf_arm_sp_getter.h include/plat/arm/common/plat_arm.h -Copyright: 2013-2023, Arm Limited and Contributors. +Copyright: 2013-2024, Arm Limited and Contributors. License: BSD-3-clause Files: include/plat/arm/common/arm_fconf_getter.h include/plat/arm/common/arm_fconf_io_storage.h -Copyright: 2015-2023, ARM Limited. +Copyright: 2015-2024, ARM Limited. License: BSD-3-clause Files: include/plat/arm/common/fconf_ethosn_getter.h include/plat/arm/common/fconf_nv_cntr_getter.h -Copyright: 2015-2023, Arm Limited. +Copyright: 2015-2024, Arm Limited. License: BSD-3-clause Files: include/plat/arm/css/* -Copyright: 2013-2023, Arm Limited and Contributors. +Copyright: 2013-2024, Arm Limited and Contributors. License: BSD-3-clause Files: include/plat/arm/css/common/aarch64/* -Copyright: 2013-2023, ARM Limited and Contributors. +Copyright: 2013-2024, ARM Limited and Contributors. License: BSD-3-clause Files: include/plat/brcm/* Copyright: 2015-2021, Broadcom License: BSD-3-clause -Files: include/plat/common/plat_drtm.h -Copyright: 2015-2023, Arm Limited. -License: BSD-3-clause - -Files: include/plat/common/plat_trng.h -Copyright: 2015-2023, ARM Limited. +Files: include/plat/common/* +Copyright: 2013-2024, Arm Limited and Contributors. License: BSD-3-clause -Files: include/plat/common/platform.h -Copyright: 2013-2023, Arm Limited and Contributors. +Files: include/plat/common/plat_drtm.h +Copyright: 2015-2024, Arm Limited. License: BSD-3-clause -Files: include/plat/marvell/* -Copyright: 2016-2021, Marvell International Ltd. +Files: include/plat/common/plat_trng.h +Copyright: 2015-2024, ARM Limited. License: BSD-3-clause Files: include/plat/nuvoton/* @@ -1218,33 +1527,46 @@ Copyright: 2017-2023, Nuvoton Ltd. 2015-2023, ARM Limited and Contributors. License: BSD-3-clause +Files: include/services/* +Copyright: 2013-2024, ARM Limited and Contributors. +License: BSD-3-clause + Files: include/services/drtm_svc.h include/services/errata_abi_svc.h include/services/ffa_svc.h include/services/rmm_core_manifest.h include/services/spm_core_manifest.h -Copyright: 2015-2023, Arm Limited. + include/services/ven_el3_svc.h +Copyright: 2015-2024, Arm Limited. +License: BSD-3-clause + +Files: include/services/oem/* +Copyright: 2024, The ChromiumOS Authors. +License: BSD-3-clause + +Files: include/services/rmm_el3_token_sign.h +Copyright: 2017-2022, 2024, NVIDIA Corporation. License: BSD-3-clause Files: include/services/rmmd_svc.h include/services/sdei.h -Copyright: 2013-2023, Arm Limited and Contributors. +Copyright: 2013-2024, Arm Limited and Contributors. License: BSD-3-clause Files: include/services/trng_svc.h -Copyright: 2015-2023, ARM Limited. +Copyright: 2015-2024, ARM Limited. License: BSD-3-clause Files: include/services/trp/* -Copyright: 2013-2023, Arm Limited and Contributors. +Copyright: 2013-2024, Arm Limited and Contributors. License: BSD-3-clause Files: include/services/trp/trp_helpers.h -Copyright: 2015-2023, Arm Limited. +Copyright: 2015-2024, Arm Limited. License: BSD-3-clause Files: include/tools_share/* -Copyright: 2015-2023, Arm Limited. +Copyright: 2015-2024, Arm Limited. License: BSD-3-clause Files: include/tools_share/firmware_encrypted.h @@ -1252,11 +1574,11 @@ Copyright: 2019, 2020, Linaro Limited. License: BSD-3-clause Files: include/tools_share/firmware_image_package.h -Copyright: 2013-2023, ARM Limited and Contributors. +Copyright: 2013-2024, ARM Limited and Contributors. License: BSD-3-clause Files: include/tools_share/tbbr_oid.h -Copyright: 2013-2023, Arm Limited and Contributors. +Copyright: 2013-2024, Arm Limited and Contributors. License: BSD-3-clause Files: include/tools_share/uuid.h @@ -1264,23 +1586,27 @@ Copyright: 2002, Marcel Moolenaar License: BSD-2-clause Files: lib/* -Copyright: 2013-2023, Arm Limited and Contributors. +Copyright: 2013-2024, Arm Limited and Contributors. License: BSD-3-clause Files: lib/compiler-rt/* Copyright: 2009-2016, the contributors listed in CREDITS.TXT -License: Expat and/or NCSA +License: Expat or NCSA Files: lib/compiler-rt/builtins/* Copyright: no-info-found License: Apache-2.0 Files: lib/compiler-rt/compiler-rt.mk -Copyright: 2013-2023, Arm Limited and Contributors. +Copyright: 2013-2024, Arm Limited and Contributors. +License: BSD-3-clause + +Files: lib/cpus/* +Copyright: 2015-2024, Arm Limited. License: BSD-3-clause -Files: lib/cpus/aarch64/* -Copyright: 2015-2023, Arm Limited. +Files: lib/cpus/aarch32/* +Copyright: 2013-2024, Arm Limited and Contributors. License: BSD-3-clause Files: lib/cpus/aarch64/a64fx.S @@ -1307,18 +1633,18 @@ Files: lib/cpus/aarch64/aem_generic.S lib/cpus/aarch64/wa_cve_2017_5715_bpiall.S lib/cpus/aarch64/wa_cve_2017_5715_mmu.S lib/cpus/aarch64/wa_cve_2022_23960_bhb.S -Copyright: 2013-2023, Arm Limited and Contributors. +Copyright: 2013-2024, Arm Limited and Contributors. License: BSD-3-clause Files: lib/cpus/aarch64/cortex_a57.S lib/cpus/aarch64/denver.S Copyright: 2020-2023, NVIDIA Corporation. - 2013-2023, Arm Limited and Contributors. + 2013-2024, Arm Limited and Contributors. License: BSD-3-clause Files: lib/cpus/aarch64/cortex_a78_ae.S -Copyright: 2021-2023, NVIDIA Corporation. - 2019-2023, Arm Limited. +Copyright: 2021-2024, NVIDIA Corporation. + 2019-2024, Arm Limited. License: BSD-3-clause Files: lib/cpus/aarch64/cortex_x1.S @@ -1326,80 +1652,94 @@ Copyright: 2020, 2022, 2023, Google LLC. License: BSD-3-clause Files: lib/cpus/aarch64/cpuamu.c -Copyright: 2013-2023, ARM Limited and Contributors. +Copyright: 2013-2024, ARM Limited and Contributors. License: BSD-3-clause Files: lib/cpus/cpu-ops.mk Copyright: 2020-2023, NVIDIA Corporation. - 2013-2023, Arm Limited and Contributors. + 2013-2024, Arm Limited and Contributors. +License: BSD-3-clause + +Files: lib/cpus/errata_common.c + lib/cpus/errata_report.c +Copyright: 2013-2024, Arm Limited and Contributors. License: BSD-3-clause Files: lib/debugfs/* -Copyright: 2015-2023, Arm Limited. +Copyright: 2015-2024, Arm Limited. License: BSD-3-clause Files: lib/debugfs/debugfs.mk lib/debugfs/debugfs_smc.c -Copyright: 2013-2023, Arm Limited and Contributors. +Copyright: 2013-2024, Arm Limited and Contributors. License: BSD-3-clause Files: lib/el3_runtime/aarch64/context_mgmt.c Copyright: 2020-2023, NVIDIA Corporation. - 2013-2023, Arm Limited and Contributors. + 2013-2024, Arm Limited and Contributors. License: BSD-3-clause -Files: lib/extensions/amu/amu.mk -Copyright: 2015-2023, Arm Limited. +Files: lib/el3_runtime/simd_ctx.c +Copyright: 2024, Arm Limited and Contributors. + 2022, Google LLC. +License: BSD-3-clause + +Files: lib/extensions/* +Copyright: 2015-2024, Arm Limited. License: BSD-3-clause -Files: lib/extensions/brbe/* -Copyright: 2015-2023, Arm Limited. +Files: lib/extensions/amu/* +Copyright: 2013-2024, Arm Limited and Contributors. License: BSD-3-clause -Files: lib/extensions/pauth/* -Copyright: 2015-2023, Arm Limited. +Files: lib/extensions/amu/amu.mk +Copyright: 2015-2024, Arm Limited. +License: BSD-3-clause + +Files: lib/extensions/mpam/* +Copyright: 2013-2024, Arm Limited and Contributors. License: BSD-3-clause -Files: lib/extensions/pmuv3/* -Copyright: 2015-2023, Arm Limited. +Files: lib/extensions/ras/* +Copyright: 2013-2024, Arm Limited and Contributors. License: BSD-3-clause Files: lib/extensions/ras/ras_common.c Copyright: 2020-2023, NVIDIA Corporation. - 2013-2023, Arm Limited and Contributors. + 2013-2024, Arm Limited and Contributors. License: BSD-3-clause -Files: lib/extensions/sys_reg_trace/* -Copyright: 2015-2023, Arm Limited. +Files: lib/extensions/sme/* +Copyright: 2013-2024, Arm Limited and Contributors. License: BSD-3-clause -Files: lib/extensions/trbe/* -Copyright: 2015-2023, Arm Limited. +Files: lib/extensions/spe/* +Copyright: 2013-2024, Arm Limited and Contributors. License: BSD-3-clause -Files: lib/extensions/trf/* -Copyright: 2015-2023, Arm Limited. +Files: lib/extensions/sve/* +Copyright: 2013-2024, Arm Limited and Contributors. License: BSD-3-clause Files: lib/fconf/* -Copyright: 2015-2023, Arm Limited. +Copyright: 2015-2024, Arm Limited. License: BSD-3-clause Files: lib/gpt_rme/* -Copyright: 2015-2023, Arm Limited. +Copyright: 2015-2024, Arm Limited. License: BSD-3-clause Files: lib/libc/aarch32/* -Copyright: 2015-2023, Arm Limited. +Copyright: 2015-2024, Arm Limited. License: BSD-3-clause Files: lib/libc/aarch64/memset.S -Copyright: 2015-2023, Arm Limited. +Copyright: 2015-2024, Arm Limited. License: BSD-3-clause Files: lib/libc/libc_asm.mk lib/libc/memrchr.c -Copyright: 2015-2023, Arm Limited. +Copyright: 2015-2024, Arm Limited. License: BSD-3-clause Files: lib/libc/memcpy_s.c @@ -1421,12 +1761,12 @@ License: ISC Files: lib/libc/strnlen.c Copyright: 2009, David Schultz <das@FreeBSD.org> -License: BSD-2-Clause-FreeBSD and/or BSD-2-clause +License: BSD-2-Clause-FreeBSD or BSD-2-clause Files: lib/libc/strtok.c Copyright: 1998, Softweyr LLC. 1988, 1993, The Regents of the University of California. -License: BSD-3-clause and/or BSD-3-clause +License: BSD-3-clause Files: lib/libc/strtol.c lib/libc/strtoll.c @@ -1437,65 +1777,69 @@ Copyright: 2011, The FreeBSD Foundation License: BSD-3-clause Files: lib/libfdt/libfdt.mk -Copyright: 2013-2023, Arm Limited and Contributors. +Copyright: 2013-2024, Arm Limited and Contributors. License: BSD-3-clause Files: lib/locks/bakery/bakery_lock_normal.c Copyright: 2020-2023, NVIDIA Corporation. - 2013-2023, Arm Limited and Contributors. + 2013-2024, Arm Limited and Contributors. License: BSD-3-clause Files: lib/mpmm/* -Copyright: 2015-2023, Arm Limited. +Copyright: 2015-2024, Arm Limited. License: BSD-3-clause Files: lib/psa/* -Copyright: 2015-2023, Arm Limited. +Copyright: 2015-2024, Arm Limited. +License: BSD-3-clause + +Files: lib/psa/cca_attestation.c +Copyright: 2013-2024, Arm Limited and Contributors. License: BSD-3-clause Files: lib/psci/psci_off.c Copyright: 2015-2023, NVIDIA Corporation. - 2013-2023, ARM Limited and Contributors. + 2013-2024, ARM Limited and Contributors. License: BSD-3-clause Files: lib/psci/psci_suspend.c lib/psci/psci_system_off.c -Copyright: 2013-2023, ARM Limited and Contributors. +Copyright: 2013-2024, ARM Limited and Contributors. License: BSD-3-clause -Files: lib/romlib/romlib_generator.py -Copyright: 2015-2023, Arm Limited. +Files: lib/romlib/* +Copyright: 2015-2024, Arm Limited. License: BSD-3-clause -Files: lib/romlib/templates/* -Copyright: 2015-2023, Arm Limited. +Files: lib/romlib/Makefile + lib/romlib/gen_combined_bl1_romlib.sh + lib/romlib/init.s + lib/romlib/jmptbl.i + lib/romlib/romlib.ld.S +Copyright: 2013-2024, Arm Limited and Contributors. License: BSD-3-clause Files: lib/transfer_list/transfer_list.c -Copyright: 2019-2023, Linaro Limited and Contributors. +Copyright: 2019-2024, Linaro Limited and Contributors. License: BSD-3-clause Files: lib/xlat_mpu/ro_xlat_mpu.mk -Copyright: 2015-2023, Arm Limited. +Copyright: 2015-2024, Arm Limited. License: BSD-3-clause -Files: lib/xlat_tables/aarch32/* +Files: lib/xlat_tables/aarch32/nonlpae_tables.c Copyright: 2016, 2017, Linaro Limited. 2014-2020, Arm Limited. 2014, STMicroelectronics International N.V. License: BSD-3-clause -Files: lib/xlat_tables/aarch32/xlat_tables.c -Copyright: 2013-2023, Arm Limited and Contributors. -License: BSD-3-clause - Files: lib/xlat_tables_v2/ro_xlat_tables.mk -Copyright: 2015-2023, Arm Limited. +Copyright: 2015-2024, Arm Limited. License: BSD-3-clause Files: lib/zlib/tf_gunzip.c lib/zlib/zlib.mk -Copyright: 2013-2023, Arm Limited and Contributors. +Copyright: 2013-2024, Arm Limited and Contributors. License: BSD-3-clause Files: lib/zlib/zlib.h @@ -1504,25 +1848,34 @@ License: Zlib Files: licenses/* Copyright: no-info-found +License: Apache-2.0 or Expat + +Files: licenses/LICENSE-APACHE-2.0.txt +Copyright: no-info-found +License: Apache-2.0 + +Files: licenses/LICENSE.MIT +Copyright: no-info-found License: Expat Files: make_helpers/* -Copyright: 2013-2023, ARM Limited and Contributors. +Copyright: 2013-2024, Arm Limited and Contributors. License: BSD-3-clause Files: make_helpers/arch_features.mk make_helpers/defaults.mk - make_helpers/march.mk -Copyright: 2015-2023, Arm Limited. +Copyright: 2015-2024, Arm Limited. License: BSD-3-clause -Files: make_helpers/build_macros.mk - make_helpers/windows.mk -Copyright: 2013-2023, Arm Limited and Contributors. +Files: make_helpers/armv7-a-cpus.mk + make_helpers/cygwin.mk + make_helpers/msys.mk + make_helpers/plat_helpers.mk +Copyright: 2013-2024, ARM Limited and Contributors. License: BSD-3-clause -Files: make_helpers/tbbr/* -Copyright: 2013-2023, Arm Limited and Contributors. +Files: make_helpers/march.mk +Copyright: 2023, 2024, Arm Limited. License: BSD-3-clause Files: package-lock.json @@ -1530,7 +1883,7 @@ Copyright: no-info-found License: BSD-3-clause Files: plat/* -Copyright: 2013-2023, ARM Limited and Contributors. +Copyright: 2013-2024, ARM Limited and Contributors. License: BSD-3-clause Files: plat/allwinner/common/arisc_off.S @@ -1545,8 +1898,12 @@ Files: plat/allwinner/common/include/sunxi_cpucfg_ncat2.h Copyright: 2021, Sipeed License: BSD-3-clause +Files: plat/allwinner/common/sunxi_bl31_setup.c +Copyright: 2013-2024, Arm Limited and Contributors. +License: BSD-3-clause + Files: plat/allwinner/common/sunxi_prepare_dtb.c -Copyright: 2015-2023, ARM Limited. +Copyright: 2015-2024, ARM Limited. License: BSD-3-clause Files: plat/allwinner/sun50i_a64/sunxi_power.c @@ -1560,7 +1917,8 @@ Copyright: 2018, Icenowy Zheng <icenowy@aosc.io> License: BSD-3-clause Files: plat/allwinner/sun50i_h616/platform.mk -Copyright: 2015-2023, ARM Limited. + plat/allwinner/sun50i_h616/sunxi_h616_dtb.c +Copyright: 2015-2024, ARM Limited. License: BSD-3-clause Files: plat/allwinner/sun50i_h616/sunxi_power.c @@ -1573,317 +1931,375 @@ Copyright: 2021, Sipeed License: BSD-3-clause Files: plat/allwinner/sun50i_r329/include/sunxi_mmap.h -Copyright: 2013-2023, ARM Limited and Contributors. +Copyright: 2013-2024, ARM Limited and Contributors. License: BSD-3-clause Files: plat/allwinner/sun50i_r329/sunxi_idle_states.c -Copyright: 2013-2023, ARM Limited and Contributors. +Copyright: 2013-2024, ARM Limited and Contributors. +License: BSD-3-clause + +Files: plat/amd/* +Copyright: 2022-2024, Advanced Micro Devices, Inc. + 2017-2022, Xilinx, Inc. + 2013-2024, Arm Limited and Contributors. License: BSD-3-clause Files: plat/amlogic/axg/include/* -Copyright: 2013-2023, Arm Limited and Contributors. +Copyright: 2013-2024, Arm Limited and Contributors. +License: BSD-3-clause + +Files: plat/amlogic/axg/platform.mk +Copyright: 2013-2024, Arm Limited and Contributors. License: BSD-3-clause Files: plat/amlogic/common/aml_console.c Copyright: 2019, Carlo Caione <ccaione@baylibre.com> License: BSD-3-clause -Files: plat/arm/board/a5ds/* -Copyright: 2015-2023, Arm Limited. +Files: plat/amlogic/g12a/platform.mk +Copyright: 2013-2024, Arm Limited and Contributors. License: BSD-3-clause -Files: plat/arm/board/a5ds/sp_min/* -Copyright: 2015-2023, ARM Limited. +Files: plat/amlogic/gxbb/platform.mk +Copyright: 2013-2024, Arm Limited and Contributors. License: BSD-3-clause -Files: plat/arm/board/arm_fpga/build_axf.ld.S - plat/arm/board/arm_fpga/platform.mk -Copyright: 2015-2023, Arm Limited. +Files: plat/amlogic/gxl/platform.mk +Copyright: 2013-2024, Arm Limited and Contributors. License: BSD-3-clause -Files: plat/arm/board/arm_fpga/kernel_trampoline.S - plat/arm/board/arm_fpga/rom_trampoline.S -Copyright: 2015-2023, ARM Limited. +Files: plat/arm/* +Copyright: 2013-2024, Arm Limited and Contributors. License: BSD-3-clause -Files: plat/arm/board/common/* -Copyright: 2015-2023, Arm Limited. +Files: plat/arm/board/a5ds/* +Copyright: 2015-2024, Arm Limited. +License: BSD-3-clause + +Files: plat/arm/board/a5ds/sp_min/* +Copyright: 2015-2024, ARM Limited. +License: BSD-3-clause + +Files: plat/arm/board/arm_fpga/* +Copyright: 2013-2024, ARM Limited and Contributors. +License: BSD-3-clause + +Files: plat/arm/board/arm_fpga/build_axf.ld.S + plat/arm/board/arm_fpga/platform.mk +Copyright: 2015-2024, Arm Limited. +License: BSD-3-clause + +Files: plat/arm/board/arm_fpga/kernel_trampoline.S + plat/arm/board/arm_fpga/rom_trampoline.S +Copyright: 2015-2024, ARM Limited. +License: BSD-3-clause + +Files: plat/arm/board/automotive_rd/* +Copyright: 2015-2024, Arm Limited. +License: BSD-3-clause + +Files: plat/arm/board/automotive_rd/platform/rd1ae/rd1ae_tbb.c +Copyright: 2015-2024, ARM Limited. License: BSD-3-clause Files: plat/arm/board/common/aarch32/* -Copyright: 2013-2023, ARM Limited and Contributors. +Copyright: 2013-2024, ARM Limited and Contributors. License: BSD-3-clause Files: plat/arm/board/common/aarch64/* -Copyright: 2013-2023, ARM Limited and Contributors. +Copyright: 2013-2024, ARM Limited and Contributors. License: BSD-3-clause -Files: plat/arm/board/common/board_arm_trusted_boot.c - plat/arm/board/common/board_common.mk -Copyright: 2013-2023, Arm Limited and Contributors. +Files: plat/arm/board/common/protpk/arm_dev_protpk.S +Copyright: 2015-2024, Arm Limited. License: BSD-3-clause Files: plat/arm/board/common/rotpk/arm_dev_rotpk.S -Copyright: 2015-2023, ARM Limited. +Copyright: 2015-2024, ARM Limited. +License: BSD-3-clause + +Files: plat/arm/board/common/rotpk/arm_full_dev_ecdsa_p256_rotpk.S + plat/arm/board/common/rotpk/arm_full_dev_ecdsa_p384_rotpk.S + plat/arm/board/common/rotpk/arm_full_dev_rsa_rotpk.S +Copyright: 2015-2024, Arm Limited. License: BSD-3-clause -Files: plat/arm/board/corstone1000/* -Copyright: 2013-2023, Arm Limited and Contributors. +Files: plat/arm/board/common/swd_rotpk/arm_dev_swd_rotpk.S +Copyright: 2015-2024, Arm Limited. License: BSD-3-clause Files: plat/arm/board/corstone1000/common/corstone1000_bl2_mem_params_desc.c plat/arm/board/corstone1000/common/corstone1000_stack_protector.c plat/arm/board/corstone1000/common/corstone1000_topology.c plat/arm/board/corstone1000/common/corstone1000_trusted_boot.c -Copyright: 2013-2023, ARM Limited and Contributors. +Copyright: 2013-2024, ARM Limited and Contributors. License: BSD-3-clause Files: plat/arm/board/corstone1000/common/fdts/* -Copyright: 2015-2023, Arm Limited. +Copyright: 2015-2024, Arm Limited. License: BSD-3-clause Files: plat/arm/board/corstone1000/include/* -Copyright: 2013-2023, ARM Limited and Contributors. -License: BSD-3-clause - -Files: plat/arm/board/corstone700/* -Copyright: 2013-2023, Arm Limited and Contributors. +Copyright: 2013-2024, ARM Limited and Contributors. License: BSD-3-clause Files: plat/arm/board/corstone700/common/corstone700_stack_protector.c plat/arm/board/corstone700/common/corstone700_topology.c -Copyright: 2013-2023, ARM Limited and Contributors. +Copyright: 2013-2024, ARM Limited and Contributors. License: BSD-3-clause Files: plat/arm/board/corstone700/common/drivers/* -Copyright: 2015-2023, Arm Limited. +Copyright: 2015-2024, Arm Limited. License: BSD-3-clause -Files: plat/arm/board/fvp/aarch64/fvp_ea.c -Copyright: 2015-2023, Arm Limited. +Files: plat/arm/board/fvp/* +Copyright: 2015-2024, Arm Limited. License: BSD-3-clause -Files: plat/arm/board/fvp/fconf/* -Copyright: 2015-2023, Arm Limited. +Files: plat/arm/board/fvp/aarch32/* +Copyright: 2013-2024, ARM Limited and Contributors. License: BSD-3-clause -Files: plat/arm/board/fvp/fdts/* -Copyright: 2015-2023, Arm Limited. +Files: plat/arm/board/fvp/aarch64/* +Copyright: 2013-2024, ARM Limited and Contributors. +License: BSD-3-clause + +Files: plat/arm/board/fvp/aarch64/fvp_ea.c +Copyright: 2015-2024, Arm Limited. +License: BSD-3-clause + +Files: plat/arm/board/fvp/aarch64/fvp_lsp_ras_sp.c +Copyright: 2013-2024, Arm Limited and Contributors. License: BSD-3-clause Files: plat/arm/board/fvp/fdts/event_log.dtsi - plat/arm/board/fvp/fdts/fvp_tb_fw_config.dts -Copyright: 2015-2023, ARM Limited. +Copyright: 2015-2024, ARM Limited. License: BSD-3-clause Files: plat/arm/board/fvp/fdts/fvp_nt_fw_config.dts plat/arm/board/fvp/fdts/fvp_soc_fw_config.dts plat/arm/board/fvp/fdts/fvp_tsp_fw_config.dts -Copyright: 2013-2023, ARM Limited and Contributors. +Copyright: 2013-2024, ARM Limited and Contributors. License: BSD-3-clause -Files: plat/arm/board/fvp/fvp_bl1_measured_boot.c - plat/arm/board/fvp/fvp_bl2_measured_boot.c - plat/arm/board/fvp/fvp_common_measured_boot.c - plat/arm/board/fvp/fvp_drtm_addr.c - plat/arm/board/fvp/fvp_drtm_dma_prot.c - plat/arm/board/fvp/fvp_drtm_err.c - plat/arm/board/fvp/fvp_drtm_measurement.c - plat/arm/board/fvp/fvp_drtm_stub.c - plat/arm/board/fvp/fvp_err.c -Copyright: 2015-2023, Arm Limited. +Files: plat/arm/board/fvp/fvp_bl1_setup.c + plat/arm/board/fvp/fvp_bl2_el3_setup.c + plat/arm/board/fvp/fvp_bl2u_setup.c + plat/arm/board/fvp/fvp_console.c + plat/arm/board/fvp/fvp_cpu_pwr.c + plat/arm/board/fvp/fvp_def.h + plat/arm/board/fvp/fvp_el3_spmc_logical_sp.c + plat/arm/board/fvp/fvp_gicv3.c + plat/arm/board/fvp/fvp_io_storage.c + plat/arm/board/fvp/fvp_private.h + plat/arm/board/fvp/fvp_spmd_logical_sp.c + plat/arm/board/fvp/fvp_stack_protector.c +Copyright: 2013-2024, ARM Limited and Contributors. License: BSD-3-clause Files: plat/arm/board/fvp/fvp_bl2_setup.c plat/arm/board/fvp/fvp_bl31_setup.c plat/arm/board/fvp/fvp_common.c - plat/arm/board/fvp/fvp_cpu_errata.mk plat/arm/board/fvp/fvp_el3_spmc.c - plat/arm/board/fvp/fvp_plat_attest_token.c + plat/arm/board/fvp/fvp_pm.c plat/arm/board/fvp/fvp_realm_attest_key.c plat/arm/board/fvp/fvp_security.c plat/arm/board/fvp/fvp_spmd.c + plat/arm/board/fvp/fvp_topology.c plat/arm/board/fvp/fvp_trusted_boot.c plat/arm/board/fvp/jmptbl.i plat/arm/board/fvp/platform.mk -Copyright: 2013-2023, Arm Limited and Contributors. +Copyright: 2013-2024, Arm Limited and Contributors. License: BSD-3-clause -Files: plat/arm/board/fvp/fvp_sync_traps.c -Copyright: 2015-2023, ARM Limited. +Files: plat/arm/board/fvp/fvp_cpu_errata.mk +Copyright: 2023, 2024, Arm Limited and Contributors. License: BSD-3-clause -Files: plat/arm/board/fvp/include/* -Copyright: 2015-2023, Arm Limited. +Files: plat/arm/board/fvp/fvp_el3_token_sign.c +Copyright: 2017-2022, 2024, NVIDIA Corporation. License: BSD-3-clause -Files: plat/arm/board/fvp/include/plat.ld.S +Files: plat/arm/board/fvp/fvp_plat_attest_token.c +Copyright: 2024, Linaro Limited and Contributors. + 2022-2024, Arm Limited and Contributors. +License: BSD-3-clause + +Files: plat/arm/board/fvp/fvp_sync_traps.c +Copyright: 2015-2024, ARM Limited. +License: BSD-3-clause + +Files: plat/arm/board/fvp/include/fvp_pas_def.h + plat/arm/board/fvp/include/plat.ld.S plat/arm/board/fvp/include/plat_macros.S -Copyright: 2013-2023, ARM Limited and Contributors. +Copyright: 2013-2024, ARM Limited and Contributors. License: BSD-3-clause Files: plat/arm/board/fvp/include/platform_def.h -Copyright: 2013-2023, Arm Limited and Contributors. +Copyright: 2013-2024, Arm Limited and Contributors. License: BSD-3-clause Files: plat/arm/board/fvp/sp_min/* -Copyright: 2013-2023, Arm Limited and Contributors. +Copyright: 2013-2024, Arm Limited and Contributors. +License: BSD-3-clause + +Files: plat/arm/board/fvp/tsp/* +Copyright: 2013-2024, ARM Limited and Contributors. License: BSD-3-clause -Files: plat/arm/board/fvp/trp/* -Copyright: 2015-2023, Arm Limited. +Files: plat/arm/board/fvp_r/* +Copyright: 2013-2024, ARM Limited and Contributors. +License: BSD-3-clause + +Files: plat/arm/board/fvp_r/fvp_r_bl1_main.c + plat/arm/board/fvp_r/fvp_r_bl1_setup.c + plat/arm/board/fvp_r/platform.mk +Copyright: 2013-2024, Arm Limited and Contributors. License: BSD-3-clause Files: plat/arm/board/fvp_r/fvp_r_err.c -Copyright: 2015-2023, Arm Limited. +Copyright: 2015-2024, Arm Limited. +License: BSD-3-clause + +Files: plat/arm/board/fvp_r/include/* +Copyright: 2013-2024, Arm Limited and Contributors. +License: BSD-3-clause + +Files: plat/arm/board/fvp_r/include/fvp_r_arch_helpers.h +Copyright: 2013-2024, ARM Limited and Contributors. License: BSD-3-clause Files: plat/arm/board/fvp_ve/* -Copyright: 2015-2023, Arm Limited. +Copyright: 2015-2024, Arm Limited. +License: BSD-3-clause + +Files: plat/arm/board/juno/* +Copyright: 2013-2024, ARM Limited and Contributors. License: BSD-3-clause Files: plat/arm/board/juno/cert_create_tbbr.mk plat/arm/board/juno/juno_ethosn_tzmp1_def.h -Copyright: 2015-2023, Arm Limited. +Copyright: 2015-2024, Arm Limited. License: BSD-3-clause Files: plat/arm/board/juno/certificate/* -Copyright: 2015-2023, Arm Limited. +Copyright: 2015-2024, Arm Limited. License: BSD-3-clause Files: plat/arm/board/juno/fdts/* -Copyright: 2015-2023, ARM Limited. +Copyright: 2015-2024, Arm Limited. +License: BSD-3-clause + +Files: plat/arm/board/juno/fdts/juno_tb_fw_config.dts +Copyright: 2015-2024, ARM Limited. License: BSD-3-clause Files: plat/arm/board/juno/fip/* -Copyright: 2015-2023, Arm Limited. +Copyright: 2015-2024, Arm Limited. +License: BSD-3-clause + +Files: plat/arm/board/juno/include/* +Copyright: 2013-2024, Arm Limited and Contributors. +License: BSD-3-clause + +Files: plat/arm/board/juno/include/plat_macros.S +Copyright: 2013-2024, ARM Limited and Contributors. License: BSD-3-clause Files: plat/arm/board/juno/include/plat_tbbr_img_def.h -Copyright: 2015-2023, Arm Limited. +Copyright: 2015-2024, Arm Limited. License: BSD-3-clause -Files: plat/arm/board/juno/juno_bl31_setup.c +Files: plat/arm/board/juno/juno_bl1_setup.c + plat/arm/board/juno/juno_bl31_setup.c + plat/arm/board/juno/juno_common.c plat/arm/board/juno/juno_tbbr_cot_bl2.c plat/arm/board/juno/platform.mk -Copyright: 2013-2023, Arm Limited and Contributors. +Copyright: 2013-2024, Arm Limited and Contributors. License: BSD-3-clause Files: plat/arm/board/juno/juno_trusted_boot.c -Copyright: 2015-2023, ARM Limited. +Copyright: 2015-2024, ARM Limited. License: BSD-3-clause Files: plat/arm/board/morello/* -Copyright: 2015-2023, Arm Limited. +Copyright: 2015-2024, Arm Limited. License: BSD-3-clause Files: plat/arm/board/n1sdp/* -Copyright: 2015-2023, Arm Limited. +Copyright: 2015-2024, Arm Limited. License: BSD-3-clause Files: plat/arm/board/n1sdp/aarch64/* -Copyright: 2013-2023, ARM Limited and Contributors. -License: BSD-3-clause - -Files: plat/arm/board/n1sdp/include/* -Copyright: 2013-2023, ARM Limited and Contributors. +Copyright: 2013-2024, ARM Limited and Contributors. License: BSD-3-clause -Files: plat/arm/board/n1sdp/include/platform_def.h -Copyright: 2015-2023, Arm Limited. +Files: plat/arm/board/n1sdp/include/plat_macros.S +Copyright: 2013-2024, ARM Limited and Contributors. License: BSD-3-clause Files: plat/arm/board/n1sdp/n1sdp_interconnect.c plat/arm/board/n1sdp/n1sdp_security.c plat/arm/board/n1sdp/n1sdp_topology.c -Copyright: 2013-2023, ARM Limited and Contributors. -License: BSD-3-clause - -Files: plat/arm/board/rde1edge/* -Copyright: 2015-2023, Arm Limited. -License: BSD-3-clause - -Files: plat/arm/board/rde1edge/fdts/* -Copyright: 2015-2023, ARM Limited. -License: BSD-3-clause - -Files: plat/arm/board/rde1edge/fdts/rde1edge_nt_fw_config.dts -Copyright: 2015-2023, Arm Limited. -License: BSD-3-clause - -Files: plat/arm/board/rde1edge/rde1edge_topology.c -Copyright: 2013-2023, ARM Limited and Contributors. -License: BSD-3-clause - -Files: plat/arm/board/rdn1edge/rdn1edge_err.c - plat/arm/board/rdn1edge/rdn1edge_trusted_boot.c -Copyright: 2015-2023, Arm Limited. -License: BSD-3-clause - -Files: plat/arm/board/rdn2/platform.mk -Copyright: 2013-2023, Arm Limited and Contributors. +Copyright: 2013-2024, ARM Limited and Contributors. License: BSD-3-clause -Files: plat/arm/board/rdn2/rdn2_trusted_boot.c -Copyright: 2015-2023, Arm Limited. +Files: plat/arm/board/neoverse_rd/common/nrd_bl1_setup.c +Copyright: 2015-2024, Arm Limited. License: BSD-3-clause -Files: plat/arm/board/rdv1/rdv1_trusted_boot.c -Copyright: 2015-2023, Arm Limited. -License: BSD-3-clause - -Files: plat/arm/board/rdv1mc/rdv1mc_trusted_boot.c -Copyright: 2015-2023, Arm Limited. -License: BSD-3-clause - -Files: plat/arm/board/sgi575/sgi575_err.c - plat/arm/board/sgi575/sgi575_trusted_boot.c -Copyright: 2015-2023, Arm Limited. +Files: plat/arm/board/neoverse_rd/platform/rdv3/rdv3_mhuv3.c +Copyright: 2013-2024, ARM Limited and Contributors. License: BSD-3-clause Files: plat/arm/board/tc/* -Copyright: 2015-2023, Arm Limited. +Copyright: 2015-2024, Arm Limited. License: BSD-3-clause Files: plat/arm/board/tc/include/* -Copyright: 2013-2023, ARM Limited and Contributors. +Copyright: 2013-2024, ARM Limited and Contributors. License: BSD-3-clause Files: plat/arm/board/tc/include/platform_def.h -Copyright: 2015-2023, Arm Limited. +Copyright: 2015-2024, Arm Limited. License: BSD-3-clause Files: plat/arm/board/tc/include/tc_plat.h -Copyright: 2013-2023, Arm Limited and Contributors. +Copyright: 2013-2024, Arm Limited and Contributors. License: BSD-3-clause Files: plat/arm/board/tc/plat_tc_mbedtls_config.h plat/arm/board/tc/region_defs.h - plat/arm/board/tc/rss_ap_test_stubs.c - plat/arm/board/tc/rss_ap_tests.c - plat/arm/board/tc/rss_ap_testsuites.c - plat/arm/board/tc/rss_ap_testsuites.h -Copyright: 2022, 2023, Arm Ltd. + plat/arm/board/tc/rse_ap_test_stubs.c + plat/arm/board/tc/rse_ap_tests.c + plat/arm/board/tc/rse_ap_testsuites.c + plat/arm/board/tc/rse_ap_testsuites.h +Copyright: 2022-2024, Arm Ltd. License: BSD-3-clause Files: plat/arm/board/tc/tc_bl2_setup.c -Copyright: 2015-2023, ARM Limited. +Copyright: 2015-2024, ARM Limited. License: BSD-3-clause Files: plat/arm/board/tc/tc_bl31_setup.c plat/arm/board/tc/tc_plat.c -Copyright: 2013-2023, Arm Limited and Contributors. + plat/arm/board/tc/tc_topology.c +Copyright: 2013-2024, Arm Limited and Contributors. License: BSD-3-clause Files: plat/arm/board/tc/tc_err.c plat/arm/board/tc/tc_interconnect.c - plat/arm/board/tc/tc_topology.c + plat/arm/board/tc/tc_trng.c plat/arm/board/tc/tc_trusted_boot.c -Copyright: 2013-2023, ARM Limited and Contributors. +Copyright: 2013-2024, ARM Limited and Contributors. +License: BSD-3-clause + +Files: plat/arm/common/* +Copyright: 2013-2024, ARM Limited and Contributors. License: BSD-3-clause Files: plat/arm/common/aarch64/arm_pauth.c -Copyright: 2015-2023, Arm Limited. +Copyright: 2015-2024, Arm Limited. License: BSD-3-clause Files: plat/arm/common/arm_bl1_setup.c @@ -1892,69 +2308,79 @@ Files: plat/arm/common/arm_bl1_setup.c plat/arm/common/arm_common.mk plat/arm/common/arm_dyn_cfg.c plat/arm/common/arm_dyn_cfg_helpers.c + plat/arm/common/arm_image_load.c + plat/arm/common/arm_pm.c plat/arm/common/arm_sip_svc.c -Copyright: 2013-2023, Arm Limited and Contributors. + plat/arm/common/arm_transfer_list.c +Copyright: 2013-2024, Arm Limited and Contributors. License: BSD-3-clause Files: plat/arm/common/arm_io_storage.c -Copyright: 2015-2023, ARM Limited. +Copyright: 2015-2024, ARM Limited. License: BSD-3-clause Files: plat/arm/common/fconf/arm_fconf_io.c -Copyright: 2015-2023, ARM Limited. +Copyright: 2015-2024, ARM Limited. License: BSD-3-clause Files: plat/arm/common/fconf/fconf_ethosn_getter.c plat/arm/common/fconf/fconf_nv_cntr_getter.c -Copyright: 2015-2023, Arm Limited. +Copyright: 2015-2024, Arm Limited. +License: BSD-3-clause + +Files: plat/arm/common/plat_arm_mbedtls_config.h + plat/arm/common/plat_arm_psa_mbedtls_config.h +Copyright: 2022-2024, Arm Ltd. License: BSD-3-clause Files: plat/arm/common/plat_arm_sip_svc.c -Copyright: 2015-2023, Arm Limited. +Copyright: 2023, 2024, Arm Limited. License: BSD-3-clause Files: plat/arm/common/trp/* -Copyright: 2013-2023, Arm Limited and Contributors. +Copyright: 2013-2024, Arm Limited and Contributors. License: BSD-3-clause Files: plat/arm/common/trp/arm_trp_setup.c -Copyright: 2015-2023, Arm Limited. +Copyright: 2015-2024, Arm Limited. +License: BSD-3-clause + +Files: plat/arm/css/* +Copyright: 2013-2024, ARM Limited and Contributors. License: BSD-3-clause Files: plat/arm/css/common/css_bl2_setup.c plat/arm/css/common/css_common.mk plat/arm/css/common/css_pm.c -Copyright: 2013-2023, Arm Limited and Contributors. +Copyright: 2013-2024, Arm Limited and Contributors. License: BSD-3-clause -Files: plat/arm/css/sgi/include/sgi_base_platform_def.h -Copyright: 2013-2023, Arm Limited and Contributors. -License: BSD-3-clause - -Files: plat/arm/css/sgi/sgi-common.mk - plat/arm/css/sgi/sgi_bl31_setup.c - plat/arm/css/sgi/sgi_plat.c -Copyright: 2013-2023, Arm Limited and Contributors. +Files: plat/arm/soc/* +Copyright: 2013-2024, ARM Limited and Contributors. License: BSD-3-clause Files: plat/aspeed/* Copyright: 2023, Aspeed Technology Inc. License: BSD-3-clause -Files: plat/brcm/board/* +Files: plat/brcm/* Copyright: 2015-2021, Broadcom License: BSD-3-clause Files: plat/brcm/board/common/bcm_console.c -Copyright: 2013-2023, ARM Limited and Contributors. +Copyright: 2013-2024, ARM Limited and Contributors. License: BSD-3-clause Files: plat/brcm/board/common/bcm_elog_ddr.c Copyright: 2019, 2020, Broadcom. License: BSD-3-clause +Files: plat/brcm/common/* +Copyright: 2013-2024, ARM Limited and Contributors. +License: BSD-3-clause + Files: plat/common/aarch64/plat_common.c -Copyright: 2013-2023, Arm Limited and Contributors. +Copyright: 2013-2024, Arm Limited and Contributors. License: BSD-3-clause Files: plat/common/aarch64/plat_ehf.c @@ -1963,22 +2389,23 @@ Copyright: 2020, Broadcom License: BSD-3-clause Files: plat/common/plat_bl1_common.c -Copyright: 2013-2023, Arm Limited and Contributors. + plat/common/plat_bl_common.c +Copyright: 2013-2024, Arm Limited and Contributors. License: BSD-3-clause Files: plat/common/plat_gicv2.c plat/common/plat_gicv3.c Copyright: 2021, 2022, ProvenRun S.A.S. - 2015-2023, Arm Limited and Contributors. + 2015-2024, Arm Limited and Contributors. License: BSD-3-clause Files: plat/common/plat_psci_common.c Copyright: 2015-2023, NVIDIA Corporation. - 2013-2023, ARM Limited and Contributors. + 2013-2024, ARM Limited and Contributors. License: BSD-3-clause Files: plat/common/plat_spmd_manifest.c -Copyright: 2015-2023, Arm Limited. +Copyright: 2015-2024, Arm Limited. License: BSD-3-clause Files: plat/common/ubsan.c @@ -1986,13 +2413,31 @@ Copyright: 2019, ARM Limited. 2016, Linaro Limited License: BSD-2-clause +Files: plat/hisilicon/hikey/hikey_bl31_setup.c + plat/hisilicon/hikey/platform.mk +Copyright: 2013-2024, Arm Limited and Contributors. +License: BSD-3-clause + Files: plat/hisilicon/hikey960/hikey960_bl31_setup.c -Copyright: 2013-2023, Arm Limited and Contributors. + plat/hisilicon/hikey960/platform.mk +Copyright: 2013-2024, Arm Limited and Contributors. +License: BSD-3-clause + +Files: plat/hisilicon/poplar/bl31_plat_setup.c +Copyright: 2013-2024, Arm Limited and Contributors. +License: BSD-3-clause + +Files: plat/imx/common/imx_bl31_common.c +Copyright: 2023, 2024, NXP +License: BSD-3-clause + +Files: plat/imx/common/imx_common.c +Copyright: 2024, Pengutronix, Inc. License: BSD-3-clause Files: plat/imx/common/imx_ehf.c plat/imx/common/imx_sip_handler.c -Copyright: 2016-2023, NXP +Copyright: 2016-2024, NXP License: BSD-3-clause Files: plat/imx/common/imx_sdei.c @@ -2000,6 +2445,14 @@ Copyright: 2020, NXP 2015-2023, ARM Limited and Contributors. License: BSD-3-clause +Files: plat/imx/common/include/imx_plat_common.h +Copyright: 2023, 2024, NXP +License: BSD-3-clause + +Files: plat/imx/common/include/plat_common.h +Copyright: 2024, Pengutronix, Inc. +License: BSD-3-clause + Files: plat/imx/common/include/sci/svc/misc/* Copyright: 2017-2021, NXP 2014-2016, Freescale Semiconductor, Inc. @@ -2021,7 +2474,7 @@ Copyright: 2017-2021, NXP License: BSD-3-clause Files: plat/imx/imx7/common/imx7.mk -Copyright: 2013-2023, Arm Limited and Contributors. +Copyright: 2013-2024, Arm Limited and Contributors. License: BSD-3-clause Files: plat/imx/imx7/common/imx7_helpers.S @@ -2029,18 +2482,18 @@ Copyright: Linaro 2018-2019, Limited and Contributors. License: BSD-3-clause Files: plat/imx/imx8m/* -Copyright: 2016-2023, NXP +Copyright: 2016-2024, NXP License: BSD-3-clause Files: plat/imx/imx8m/gpc_common.c plat/imx/imx8m/imx8m_image_load.c plat/imx/imx8m/imx8m_psci_common.c -Copyright: 2013-2023, ARM Limited and Contributors. +Copyright: 2013-2024, ARM Limited and Contributors. License: BSD-3-clause Files: plat/imx/imx8m/imx8m_caam.c plat/imx/imx8m/imx_rdc.c -Copyright: 2019-2022, NXP. +Copyright: 2019-2024, NXP. License: BSD-3-clause Files: plat/imx/imx8m/imx8m_ccm.c @@ -2053,12 +2506,12 @@ Copyright: 2022, Linaro. License: BSD-3-clause Files: plat/imx/imx8m/imx8m_measured_boot.c -Copyright: 2022, Linaro. - 2022, 2023, Arm Limited. +Copyright: 2022-2024, Arm Limited. + 2022, 2023, Linaro. License: BSD-3-clause Files: plat/imx/imx8m/imx8mm/* -Copyright: 2013-2023, ARM Limited and Contributors. +Copyright: 2013-2024, ARM Limited and Contributors. License: BSD-3-clause Files: plat/imx/imx8m/imx8mm/imx8mm_bl2_el3_setup.c @@ -2066,35 +2519,43 @@ Copyright: 2021, Arm 2017-2021, NXP License: BSD-3-clause -Files: plat/imx/imx8m/imx8mm/include/gpc_reg.h - plat/imx/imx8m/imx8mm/include/imx_sec_def.h -Copyright: 2016-2023, NXP +Files: plat/imx/imx8m/imx8mm/include/* +Copyright: 2016-2024, NXP +License: BSD-3-clause + +Files: plat/imx/imx8m/imx8mm/include/imx8mm_private.h + plat/imx/imx8m/imx8mm/include/platform_def.h +Copyright: 2013-2024, ARM Limited and Contributors. +License: BSD-3-clause + +Files: plat/imx/imx8m/imx8mm/platform.mk +Copyright: 2013-2024, Arm Limited and Contributors. License: BSD-3-clause Files: plat/imx/imx8m/imx8mp/imx8mp_bl2_mem_params_desc.c plat/imx/imx8m/imx8mp/imx8mp_rotpk.S plat/imx/imx8m/imx8mp/imx8mp_trusted_boot.c -Copyright: 2013-2023, Arm Limited and Contributors. +Copyright: 2013-2024, Arm Limited and Contributors. License: BSD-3-clause Files: plat/imx/imx8m/imx8mp/include/imx8mp_private.h -Copyright: 2013-2023, Arm Limited and Contributors. +Copyright: 2013-2024, Arm Limited and Contributors. License: BSD-3-clause Files: plat/imx/imx8m/imx8mq/* -Copyright: 2013-2023, ARM Limited and Contributors. +Copyright: 2013-2024, ARM Limited and Contributors. License: BSD-3-clause Files: plat/imx/imx8m/imx8mq/include/* -Copyright: 2016-2023, NXP +Copyright: 2016-2024, NXP License: BSD-3-clause Files: plat/imx/imx8m/imx8mq/include/platform_def.h -Copyright: 2013-2023, ARM Limited and Contributors. +Copyright: 2013-2024, ARM Limited and Contributors. License: BSD-3-clause Files: plat/imx/imx8m/imx_aipstz.c -Copyright: 2013-2023, Arm Limited and Contributors. +Copyright: 2013-2024, Arm Limited and Contributors. License: BSD-3-clause Files: plat/imx/imx8m/imx_hab.c @@ -2105,12 +2566,12 @@ License: BSD-3-clause Files: plat/imx/imx8m/include/gpc.h plat/imx/imx8m/include/imx8m_psci.h plat/imx/imx8m/include/imx_aipstz.h -Copyright: 2013-2023, ARM Limited and Contributors. +Copyright: 2013-2024, ARM Limited and Contributors. License: BSD-3-clause Files: plat/imx/imx8m/include/imx8m_caam.h plat/imx/imx8m/include/imx_rdc.h -Copyright: 2019-2022, NXP. +Copyright: 2019-2024, NXP. License: BSD-3-clause Files: plat/imx/imx8m/include/imx8m_ccm.h @@ -2121,111 +2582,207 @@ Files: plat/imx/imx8m/include/imx8m_measured_boot.h Copyright: 2022, Linaro License: BSD-3-clause +Files: plat/imx/imx8ulp/* +Copyright: 2016-2024, NXP +License: BSD-3-clause + +Files: plat/imx/imx8ulp/imx8ulp_caam.c +Copyright: 2019-2024, NXP. +License: BSD-3-clause + +Files: plat/imx/imx8ulp/include/imx8ulp_caam.h +Copyright: 2019-2024, NXP. +License: BSD-3-clause + +Files: plat/imx/imx8ulp/include/scmi_sensor.h +Copyright: 2013-2024, Arm Limited and Contributors. +License: BSD-3-clause + Files: plat/imx/imx93/* -Copyright: 2016-2023, NXP +Copyright: 2016-2024, NXP License: BSD-3-clause Files: plat/intel/* -Copyright: 2019-2023, Intel Corporation. +Copyright: 2019-2024, Intel Corporation. License: BSD-3-clause Files: plat/intel/soc/agilex/bl2_plat_setup.c plat/intel/soc/agilex/bl31_plat_setup.c -Copyright: 2019-2023, Intel Corporation. - 2019-2023, ARM Limited and Contributors. + plat/intel/soc/agilex/platform.mk +Copyright: 2024, Altera Corporation. + 2019-2024, ARM Limited and Contributors. + 2019-2023, Intel Corporation. License: BSD-3-clause -Files: plat/intel/soc/agilex/include/socfpga_plat_def.h -Copyright: 2019-2023, Intel Corporation. - 2019-2023, ARM Limited and Contributors. +Files: plat/intel/soc/agilex/include/agilex_memory_controller.h + plat/intel/soc/agilex/include/agilex_system_manager.h +Copyright: 2024, Altera Corporation. + 2019-2023, Intel Corporation. License: BSD-3-clause -Files: plat/intel/soc/agilex/platform.mk -Copyright: 2019-2023, ARM Limited and Contributors. +Files: plat/intel/soc/agilex/include/socfpga_plat_def.h +Copyright: 2024, Altera Corporation. + 2019-2024, ARM Limited and Contributors. 2019-2023, Intel Corporation. License: BSD-3-clause Files: plat/intel/soc/agilex5/* -Copyright: 2019-2023, Intel Corporation. - 2019-2023, ARM Limited and Contributors. +Copyright: 2024, Altera Corporation. + 2019-2023, Intel Corporation. License: BSD-3-clause -Files: plat/intel/soc/agilex5/include/* -Copyright: 2019-2023, Intel Corporation. +Files: plat/intel/soc/agilex5/bl2_plat_setup.c + plat/intel/soc/agilex5/bl31_plat_setup.c + plat/intel/soc/agilex5/platform.mk +Copyright: 2024, Altera Corporation. + 2019-2024, ARM Limited and Contributors. + 2019-2023, Intel Corporation. +License: BSD-3-clause + +Files: plat/intel/soc/agilex5/include/agilex5_memory_controller.h + plat/intel/soc/agilex5/include/agilex5_mmc.h +Copyright: 2019-2024, Intel Corporation. License: BSD-3-clause Files: plat/intel/soc/agilex5/include/socfpga_plat_def.h -Copyright: 2019-2023, Intel Corporation. - 2019-2023, ARM Limited and Contributors. +Copyright: 2024, Altera Corporation. + 2019-2024, ARM Limited and Contributors. + 2019-2023, Intel Corporation. License: BSD-3-clause -Files: plat/intel/soc/agilex5/soc/* -Copyright: 2019-2023, Intel Corporation. +Files: plat/intel/soc/agilex5/soc/agilex5_memory_controller.c + plat/intel/soc/agilex5/soc/agilex5_mmc.c +Copyright: 2019-2024, Intel Corporation. License: BSD-3-clause -Files: plat/intel/soc/common/* -Copyright: 2013-2023, ARM Limited and Contributors. +Files: plat/intel/soc/common/aarch64/* +Copyright: 2024, Altera Corporation. + 2019-2024, ARM Limited and Contributors. + 2019-2023, Intel Corporation. License: BSD-3-clause -Files: plat/intel/soc/common/drivers/* -Copyright: 2019-2023, Intel Corporation. +Files: plat/intel/soc/common/aarch64/platform_common.c +Copyright: 2013-2024, ARM Limited and Contributors. +License: BSD-3-clause + +Files: plat/intel/soc/common/bl2_plat_mem_params_desc.c + plat/intel/soc/common/socfpga_topology.c +Copyright: 2013-2024, ARM Limited and Contributors. +License: BSD-3-clause + +Files: plat/intel/soc/common/drivers/ccu/* +Copyright: 2024, Altera Corporation. + 2019-2023, Intel Corporation. License: BSD-3-clause Files: plat/intel/soc/common/drivers/qspi/* -Copyright: 2019-2023, Intel Corporation. - 2019-2023, ARM Limited and Contributors. +Copyright: 2019-2022, Intel Corporation. + 2019-2022, ARM Limited and Contributors. License: BSD-3-clause -Files: plat/intel/soc/common/include/* -Copyright: 2019-2023, Intel Corporation. +Files: plat/intel/soc/common/drivers/sdmmc/* +Copyright: 2024, Altera Corporation. + 2019-2023, Intel Corporation. License: BSD-3-clause Files: plat/intel/soc/common/include/platform_def.h -Copyright: 2019-2023, Intel Corporation. - 2019-2023, ARM Limited and Contributors. +Copyright: 2024, Altera Corporation. + 2019-2024, ARM Limited and Contributors. + 2019-2023, Intel Corporation. License: BSD-3-clause -Files: plat/intel/soc/common/sip/socfpga_sip_fcs.c -Copyright: 2019-2023, Intel Corporation. +Files: plat/intel/soc/common/include/socfpga_handoff.h + plat/intel/soc/common/include/socfpga_mailbox.h + plat/intel/soc/common/include/socfpga_private.h + plat/intel/soc/common/include/socfpga_sip_svc.h + plat/intel/soc/common/include/socfpga_system_manager.h + plat/intel/soc/common/include/socfpga_vab.h +Copyright: 2024, Altera Corporation. + 2019-2023, Intel Corporation. +License: BSD-3-clause + +Files: plat/intel/soc/common/lib/* +Copyright: 2024, Altera Corporation. +License: BSD-3-clause + +Files: plat/intel/soc/common/sip/* +Copyright: 2024, Altera Corporation. + 2019-2023, Intel Corporation. +License: BSD-3-clause + +Files: plat/intel/soc/common/sip/socfpga_sip_ecc.c +Copyright: 2013-2024, ARM Limited and Contributors. License: BSD-3-clause Files: plat/intel/soc/common/soc/* -Copyright: 2019-2023, Intel Corporation. +Copyright: 2024, Altera Corporation. + 2019-2023, Intel Corporation. +License: BSD-3-clause + +Files: plat/intel/soc/common/soc/socfpga_emac.c + plat/intel/soc/common/soc/socfpga_firewall.c +Copyright: 2019-2024, Intel Corporation. +License: BSD-3-clause + +Files: plat/intel/soc/common/socfpga_delay_timer.c + plat/intel/soc/common/socfpga_image_load.c +Copyright: 2024, Altera Corporation. + 2019-2023, ARM Limited and Contributors. License: BSD-3-clause Files: plat/intel/soc/common/socfpga_psci.c + plat/intel/soc/common/socfpga_sip_svc.c plat/intel/soc/common/socfpga_storage.c plat/intel/soc/common/socfpga_vab.c -Copyright: 2019-2023, Intel Corporation. - 2019-2023, ARM Limited and Contributors. +Copyright: 2024, Altera Corporation. + 2019-2024, ARM Limited and Contributors. + 2019-2023, Intel Corporation. +License: BSD-3-clause + +Files: plat/intel/soc/n5x/* +Copyright: 2024, Altera Corporation. + 2019-2023, Intel Corporation. License: BSD-3-clause -Files: plat/intel/soc/common/socfpga_sip_svc_v2.c -Copyright: 2019-2023, Intel Corporation. +Files: plat/intel/soc/n5x/bl31_plat_setup.c +Copyright: 2019-2024, Intel Corporation. License: BSD-3-clause Files: plat/intel/soc/n5x/include/socfpga_plat_def.h -Copyright: 2019-2023, Intel Corporation. - 2019-2023, ARM Limited and Contributors. +Copyright: 2024, Altera Corporation. + 2019-2024, ARM Limited and Contributors. + 2019-2023, Intel Corporation. +License: BSD-3-clause + +Files: plat/intel/soc/n5x/soc/* +Copyright: 2019-2024, Intel Corporation. License: BSD-3-clause Files: plat/intel/soc/stratix10/bl2_plat_setup.c plat/intel/soc/stratix10/bl31_plat_setup.c -Copyright: 2019-2023, Intel Corporation. - 2019-2023, ARM Limited and Contributors. +Copyright: 2019-2022, Intel Corporation. + 2019-2022, ARM Limited and Contributors. +License: BSD-3-clause + +Files: plat/intel/soc/stratix10/include/s10_memory_controller.h + plat/intel/soc/stratix10/include/s10_system_manager.h +Copyright: 2024, Altera Corporation. + 2019-2023, Intel Corporation. License: BSD-3-clause Files: plat/intel/soc/stratix10/include/socfpga_plat_def.h -Copyright: 2013-2023, ARM Limited and Contributors. +Copyright: 2024, Altera Corporation. + 2019-2023, ARM Limited and Contributors. License: BSD-3-clause Files: plat/intel/soc/stratix10/platform.mk -Copyright: 2019-2023, ARM Limited and Contributors. +Copyright: 2024, Altera Corporation. + 2019-2024, ARM Limited and Contributors. 2019-2023, Intel Corporation. License: BSD-3-clause Files: plat/marvell/* -Copyright: 2016-2021, Marvell International Ltd. +Copyright: 2016-2024, Marvell International Ltd. License: BSD-3-clause Files: plat/marvell/armada/a3k/common/a3700_ea.c @@ -2240,12 +2797,17 @@ Files: plat/marvell/armada/a3k/common/plat_cci.c Copyright: 2021, Marek Behun <marek.behun@nic.cz> License: BSD-3-clause -Files: plat/marvell/armada/a8k/a70x0_mochabin/board/* +Files: plat/marvell/armada/a8k/a70x0_mochabin/* Copyright: 2021, Sartura Ltd. 2021, Marvell International Ltd. 2021, Globalscale technologies, Inc. License: BSD-3-clause +Files: plat/marvell/armada/a8k/a70x0_mochabin/mvebu_def.h + plat/marvell/armada/a8k/a70x0_mochabin/platform.mk +Copyright: 2016-2024, Marvell International Ltd. +License: BSD-3-clause + Files: plat/marvell/armada/a8k/a80x0_puzzle/board/system_power.c Copyright: 2020, Sartura Ltd. License: BSD-3-clause @@ -2256,89 +2818,85 @@ Copyright: 2020, ARM Limited. License: BSD-3-clause Files: plat/marvell/armada/common/aarch64/marvell_bl2_mem_params_desc.c -Copyright: 2013-2023, ARM Limited and Contributors. +Copyright: 2013-2024, ARM Limited and Contributors. License: BSD-3-clause Files: plat/marvell/armada/common/aarch64/marvell_common.c -Copyright: 2016-2021, Marvell International Ltd. +Copyright: 2016-2024, Marvell International Ltd. License: BSD-3-clause Files: plat/marvell/armada/common/marvell_console.c plat/marvell/armada/common/marvell_image_load.c -Copyright: 2013-2023, ARM Limited and Contributors. +Copyright: 2013-2024, ARM Limited and Contributors. License: BSD-3-clause -Files: plat/marvell/octeontx/* +Files: plat/marvell/octeontx/otx2/t91/t9130_cex7_eval/* Copyright: 2021, Semihalf. 2018, Marvell International Ltd. License: BSD-3-clause -Files: plat/marvell/octeontx/otx2/t91/t9130/* -Copyright: 2016-2021, Marvell International Ltd. -License: BSD-3-clause - Files: plat/mediatek/* -Copyright: 2019-2023, MediaTek Inc. +Copyright: 2019-2024, MediaTek Inc. License: BSD-3-clause Files: plat/mediatek/common/mtk_plat_common.c plat/mediatek/common/mtk_plat_common.h plat/mediatek/common/mtk_sip_svc.c -Copyright: 2013-2023, ARM Limited and Contributors. +Copyright: 2013-2024, ARM Limited and Contributors. License: BSD-3-clause Files: plat/mediatek/drivers/audio/mt8188/audio_domain.c -Copyright: 2022, Mediatek Inc. +Copyright: 2022, 2024, Mediatek Inc. License: BSD-3-clause Files: plat/mediatek/drivers/emi_mpu/* -Copyright: 2013-2023, ARM Limited and Contributors. +Copyright: 2013-2024, ARM Limited and Contributors. License: BSD-3-clause Files: plat/mediatek/drivers/emi_mpu/rules.mk -Copyright: 2019-2023, MediaTek Inc. -License: BSD-3-clause - -Files: plat/mediatek/drivers/uart/* -Copyright: 2013-2023, ARM Limited and Contributors. +Copyright: 2019-2024, MediaTek Inc. License: BSD-3-clause -Files: plat/mediatek/drivers/uart/uart.c - plat/mediatek/drivers/uart/uart.h -Copyright: 2019-2023, MediaTek Inc. +Files: plat/mediatek/drivers/uart/8250_console.S + plat/mediatek/drivers/uart/uart8250.h +Copyright: 2013-2024, ARM Limited and Contributors. License: BSD-3-clause Files: plat/mediatek/helpers/* -Copyright: 2022, Mediatek Inc. +Copyright: 2022, 2024, Mediatek Inc. License: BSD-3-clause Files: plat/mediatek/helpers/rules.mk -Copyright: 2019-2023, MediaTek Inc. +Copyright: 2019-2024, MediaTek Inc. License: BSD-3-clause Files: plat/mediatek/include/armv8_2/* -Copyright: 2022, Mediatek Inc. +Copyright: 2022, 2024, Mediatek Inc. License: BSD-3-clause Files: plat/mediatek/include/mtk_sip_svc.h -Copyright: 2013-2023, ARM Limited and Contributors. +Copyright: 2013-2024, ARM Limited and Contributors. +License: BSD-3-clause + +Files: plat/mediatek/include/plat_helpers.h +Copyright: 2022, 2024, Mediatek Inc. License: BSD-3-clause Files: plat/mediatek/lib/pm/* -Copyright: 2022, Mediatek Inc. +Copyright: 2022, 2024, Mediatek Inc. License: BSD-3-clause Files: plat/mediatek/lib/pm/armv8_2/rules.mk -Copyright: 2019-2023, MediaTek Inc. +Copyright: 2019-2024, MediaTek Inc. License: BSD-3-clause Files: plat/mediatek/lib/pm/mtk_pm.c plat/mediatek/lib/pm/rules.mk -Copyright: 2019-2023, MediaTek Inc. +Copyright: 2019-2024, MediaTek Inc. License: BSD-3-clause Files: plat/mediatek/mt8173/* -Copyright: 2013-2023, ARM Limited and Contributors. +Copyright: 2013-2024, ARM Limited and Contributors. License: BSD-3-clause Files: plat/mediatek/mt8173/drivers/wdt/* @@ -2346,7 +2904,7 @@ Copyright: 2020, 2022, 2023, Google LLC. License: BSD-3-clause Files: plat/mediatek/mt8183/* -Copyright: 2013-2023, ARM Limited and Contributors. +Copyright: 2013-2024, ARM Limited and Contributors. License: BSD-3-clause Files: plat/mediatek/mt8183/bl31_plat_setup.c @@ -2354,43 +2912,43 @@ Files: plat/mediatek/mt8183/bl31_plat_setup.c plat/mediatek/mt8183/plat_mt_gic.c plat/mediatek/mt8183/plat_pm.c plat/mediatek/mt8183/platform.mk -Copyright: 2019-2023, MediaTek Inc. +Copyright: 2019-2024, MediaTek Inc. License: BSD-3-clause Files: plat/mediatek/mt8183/drivers/gpio/* -Copyright: 2019-2023, MediaTek Inc. +Copyright: 2019-2024, MediaTek Inc. License: BSD-3-clause Files: plat/mediatek/mt8183/drivers/pmic/* -Copyright: 2019-2023, MediaTek Inc. +Copyright: 2019-2024, MediaTek Inc. License: BSD-3-clause Files: plat/mediatek/mt8183/drivers/rtc/* -Copyright: 2019-2023, MediaTek Inc. +Copyright: 2019-2024, MediaTek Inc. License: BSD-3-clause Files: plat/mediatek/mt8183/drivers/spm/spm.h -Copyright: 2019-2023, MediaTek Inc. +Copyright: 2019-2024, MediaTek Inc. License: BSD-3-clause Files: plat/mediatek/mt8183/drivers/spmc/* -Copyright: 2019-2023, MediaTek Inc. +Copyright: 2019-2024, MediaTek Inc. License: BSD-3-clause Files: plat/mediatek/mt8183/drivers/timer/* -Copyright: 2019-2023, MediaTek Inc. +Copyright: 2019-2024, MediaTek Inc. License: BSD-3-clause Files: plat/mediatek/mt8183/include/plat_dcm.h -Copyright: 2019-2023, MediaTek Inc. +Copyright: 2019-2024, MediaTek Inc. License: BSD-3-clause Files: plat/mediatek/mt8186/aarch64/* -Copyright: 2013-2023, ARM Limited and Contributors. +Copyright: 2013-2024, ARM Limited and Contributors. License: BSD-3-clause Files: plat/mediatek/mt8186/drivers/emi_mpu/* -Copyright: 2013-2023, ARM Limited and Contributors. +Copyright: 2013-2024, ARM Limited and Contributors. License: BSD-3-clause Files: plat/mediatek/mt8186/drivers/spm/mt_spm_extern.c @@ -2398,86 +2956,83 @@ Files: plat/mediatek/mt8186/drivers/spm/mt_spm_extern.c Copyright: since 2022, ARM Limited and Contributors. License: BSD-3-clause -Files: plat/mediatek/mt8186/include/plat_helpers.h - plat/mediatek/mt8186/include/plat_macros.S +Files: plat/mediatek/mt8186/include/plat_macros.S plat/mediatek/mt8186/include/plat_private.h -Copyright: 2013-2023, ARM Limited and Contributors. +Copyright: 2013-2024, ARM Limited and Contributors. License: BSD-3-clause Files: plat/mediatek/mt8186/include/platform_def.h -Copyright: 2021, 2022, MediaTek Inc. - 2021, 2022, ARM Limited and Contributors. +Copyright: 2021-2024, MediaTek Inc. + 2021-2024, ARM Limited and Contributors. License: BSD-3-clause Files: plat/mediatek/mt8186/plat_topology.c -Copyright: 2013-2023, ARM Limited and Contributors. +Copyright: 2013-2024, ARM Limited and Contributors. License: BSD-3-clause Files: plat/mediatek/mt8188/include/* -Copyright: 2013-2023, ARM Limited and Contributors. +Copyright: 2013-2024, ARM Limited and Contributors. License: BSD-3-clause Files: plat/mediatek/mt8188/include/spm_reg.h -Copyright: 2022, Mediatek Inc. +Copyright: 2022, 2024, Mediatek Inc. License: BSD-3-clause Files: plat/mediatek/mt8192/aarch64/* -Copyright: 2013-2023, ARM Limited and Contributors. +Copyright: 2013-2024, ARM Limited and Contributors. License: BSD-3-clause Files: plat/mediatek/mt8192/drivers/devapc/* -Copyright: 2013-2023, ARM Limited and Contributors. +Copyright: 2013-2024, ARM Limited and Contributors. License: BSD-3-clause Files: plat/mediatek/mt8192/drivers/emi_mpu/* -Copyright: 2013-2023, ARM Limited and Contributors. +Copyright: 2013-2024, ARM Limited and Contributors. License: BSD-3-clause -Files: plat/mediatek/mt8192/include/plat_helpers.h - plat/mediatek/mt8192/include/plat_macros.S +Files: plat/mediatek/mt8192/include/plat_macros.S plat/mediatek/mt8192/include/plat_private.h plat/mediatek/mt8192/include/platform_def.h -Copyright: 2013-2023, ARM Limited and Contributors. +Copyright: 2013-2024, ARM Limited and Contributors. License: BSD-3-clause Files: plat/mediatek/mt8192/plat_topology.c -Copyright: 2013-2023, ARM Limited and Contributors. +Copyright: 2013-2024, ARM Limited and Contributors. License: BSD-3-clause Files: plat/mediatek/mt8195/aarch64/* -Copyright: 2013-2023, ARM Limited and Contributors. +Copyright: 2013-2024, ARM Limited and Contributors. License: BSD-3-clause Files: plat/mediatek/mt8195/drivers/apusys/* -Copyright: 2013-2023, ARM Limited and Contributors. +Copyright: 2013-2024, ARM Limited and Contributors. License: BSD-3-clause Files: plat/mediatek/mt8195/drivers/apusys/mtk_apusys.c plat/mediatek/mt8195/drivers/apusys/mtk_apusys.h -Copyright: 2019-2023, MediaTek Inc. +Copyright: 2019-2024, MediaTek Inc. License: BSD-3-clause Files: plat/mediatek/mt8195/drivers/emi_mpu/* -Copyright: 2013-2023, ARM Limited and Contributors. +Copyright: 2013-2024, ARM Limited and Contributors. License: BSD-3-clause -Files: plat/mediatek/mt8195/include/plat_helpers.h - plat/mediatek/mt8195/include/plat_macros.S +Files: plat/mediatek/mt8195/include/plat_macros.S plat/mediatek/mt8195/include/plat_private.h plat/mediatek/mt8195/include/platform_def.h -Copyright: 2013-2023, ARM Limited and Contributors. +Copyright: 2013-2024, ARM Limited and Contributors. License: BSD-3-clause Files: plat/mediatek/mt8195/plat_topology.c -Copyright: 2013-2023, ARM Limited and Contributors. +Copyright: 2013-2024, ARM Limited and Contributors. License: BSD-3-clause Files: plat/mediatek/topology/* -Copyright: 2022, Mediatek Inc. +Copyright: 2022, 2024, Mediatek Inc. License: BSD-3-clause Files: plat/mediatek/topology/rules.mk -Copyright: 2019-2023, MediaTek Inc. +Copyright: 2019-2024, MediaTek Inc. License: BSD-3-clause Files: plat/nuvoton/* @@ -2492,18 +3047,19 @@ License: BSD-3-clause Files: plat/nvidia/* Copyright: 2015-2023, NVIDIA Corporation. - 2013-2023, ARM Limited and Contributors. + 2013-2024, ARM Limited and Contributors. +License: BSD-3-clause + +Files: plat/nvidia/tegra/common/tegra_fiq_glue.c +Copyright: 2020-2023, NVIDIA Corporation. + 2013-2024, Arm Limited and Contributors. License: BSD-3-clause Files: plat/nvidia/tegra/common/tegra_gicv3.c plat/nvidia/tegra/common/tegra_io_storage.c plat/nvidia/tegra/common/tegra_sdei.c plat/nvidia/tegra/common/tegra_stack_protector.c -Copyright: 2017-2022, NVIDIA Corporation. -License: BSD-3-clause - -Files: plat/nvidia/tegra/drivers/* -Copyright: 2013-2023, ARM Limited and Contributors. +Copyright: 2017-2022, 2024, NVIDIA Corporation. License: BSD-3-clause Files: plat/nvidia/tegra/drivers/bpmp_ipc/* @@ -2511,28 +3067,7 @@ Copyright: 2017-2023, NVIDIA CORPORATION. License: BSD-3-clause Files: plat/nvidia/tegra/drivers/bpmp_ipc/ivc.h -Copyright: 2017-2022, NVIDIA Corporation. -License: BSD-3-clause - -Files: plat/nvidia/tegra/drivers/memctrl/* -Copyright: 2015-2023, NVIDIA Corporation. - 2013-2023, ARM Limited and Contributors. -License: BSD-3-clause - -Files: plat/nvidia/tegra/drivers/smmu/* -Copyright: 2015-2023, NVIDIA Corporation. - 2013-2023, ARM Limited and Contributors. -License: BSD-3-clause - -Files: plat/nvidia/tegra/drivers/spe/* -Copyright: 2015-2023, NVIDIA Corporation. - 2013-2023, ARM Limited and Contributors. -License: BSD-3-clause - -Files: plat/nvidia/tegra/include/drivers/bpmp.h - plat/nvidia/tegra/include/drivers/mce.h - plat/nvidia/tegra/include/drivers/memctrl_v1.h -Copyright: 2013-2023, ARM Limited and Contributors. +Copyright: 2017-2022, 2024, NVIDIA Corporation. License: BSD-3-clause Files: plat/nvidia/tegra/include/drivers/memctrl_v2.h @@ -2542,12 +3077,12 @@ Copyright: 2017-2020, NVIDIA CORPORATION. License: BSD-3-clause Files: plat/nvidia/tegra/include/drivers/spe.h -Copyright: 2017-2022, NVIDIA Corporation. +Copyright: 2017-2022, 2024, NVIDIA Corporation. License: BSD-3-clause Files: plat/nvidia/tegra/include/lib/* Copyright: 2020-2023, NVIDIA Corporation. - 2013-2023, Arm Limited and Contributors. + 2013-2024, Arm Limited and Contributors. License: BSD-3-clause Files: plat/nvidia/tegra/include/t186/* @@ -2556,7 +3091,7 @@ License: BSD-3-clause Files: plat/nvidia/tegra/include/t186/tegra_def.h Copyright: 2015-2023, NVIDIA Corporation. - 2013-2023, ARM Limited and Contributors. + 2013-2024, ARM Limited and Contributors. License: BSD-3-clause Files: plat/nvidia/tegra/include/t194/* @@ -2564,45 +3099,43 @@ Copyright: 2017-2023, NVIDIA CORPORATION. License: BSD-3-clause Files: plat/nvidia/tegra/include/t194/tegra194_ras_private.h -Copyright: 2017-2022, NVIDIA Corporation. +Copyright: 2017-2022, 2024, NVIDIA Corporation. License: BSD-3-clause Files: plat/nvidia/tegra/lib/* -Copyright: 2013-2023, Arm Limited and Contributors. +Copyright: 2013-2024, Arm Limited and Contributors. License: BSD-3-clause -Files: plat/nvidia/tegra/scat/* -Copyright: 2013-2023, ARM Limited and Contributors. +Files: plat/nvidia/tegra/platform.mk +Copyright: 2020-2023, NVIDIA Corporation. + 2013-2024, Arm Limited and Contributors. License: BSD-3-clause -Files: plat/nvidia/tegra/soc/t186/drivers/* -Copyright: 2013-2023, ARM Limited and Contributors. +Files: plat/nvidia/tegra/soc/* +Copyright: 2017-2023, NVIDIA CORPORATION. License: BSD-3-clause -Files: plat/nvidia/tegra/soc/t186/drivers/mce/mce.c +Files: plat/nvidia/tegra/soc/t186/* Copyright: 2015-2023, NVIDIA Corporation. - 2013-2023, ARM Limited and Contributors. + 2013-2024, ARM Limited and Contributors. License: BSD-3-clause Files: plat/nvidia/tegra/soc/t186/drivers/se/* -Copyright: 2017-2022, NVIDIA Corporation. -License: BSD-3-clause - -Files: plat/nvidia/tegra/soc/t186/plat_sip_calls.c -Copyright: 2013-2023, ARM Limited and Contributors. -License: BSD-3-clause - -Files: plat/nvidia/tegra/soc/t194/* -Copyright: 2017-2023, NVIDIA CORPORATION. +Copyright: 2017-2022, 2024, NVIDIA Corporation. License: BSD-3-clause Files: plat/nvidia/tegra/soc/t194/drivers/se/* -Copyright: 2020, ARM Limited and Contributors. - 2019, 2020, NVIDIA CORPORATION. +Copyright: 2017-2020, NVIDIA CORPORATION. + 2015-2020, ARM Limited and Contributors. License: BSD-3-clause Files: plat/nvidia/tegra/soc/t194/plat_ras.c -Copyright: 2017-2022, NVIDIA Corporation. +Copyright: 2017-2022, 2024, NVIDIA Corporation. +License: BSD-3-clause + +Files: plat/nvidia/tegra/soc/t210/* +Copyright: 2015-2023, NVIDIA Corporation. + 2013-2024, ARM Limited and Contributors. License: BSD-3-clause Files: plat/nvidia/tegra/soc/t210/drivers/* @@ -2610,26 +3143,22 @@ Copyright: 2017-2020, NVIDIA CORPORATION. 2015-2020, ARM Limited and Contributors. License: BSD-3-clause -Files: plat/nvidia/tegra/soc/t210/plat_secondary.c -Copyright: 2013-2023, ARM Limited and Contributors. -License: BSD-3-clause - Files: plat/nxp/* -Copyright: 2016-2023, NXP +Copyright: 2016-2024, NXP License: BSD-3-clause Files: plat/nxp/common/fip_handler/ddr_fip/ddr_fip_io.mk -Copyright: 2023, Arm Limited. +Copyright: 2023, 2024, Arm Limited. 2018-2020, NXP License: BSD-3-clause Files: plat/nxp/common/fip_handler/fuse_fip/fuse.mk -Copyright: 2023, Arm Limited. +Copyright: 2023, 2024, Arm Limited. 2018-2020, NXP License: BSD-3-clause Files: plat/nxp/common/plat_make_helper/plat_build_macros.mk -Copyright: 2019-2022, NXP. +Copyright: 2019-2024, NXP. License: BSD-3-clause Files: plat/nxp/soc-ls1043a/include/ns_access.h @@ -2645,36 +3174,53 @@ License: BSD-3-clause Files: plat/qemu/common/common.mk plat/qemu/common/qemu_gicv3.c plat/qemu/common/qemu_spm.c -Copyright: 2019-2023, Linaro Limited and Contributors. +Copyright: 2019-2024, Linaro Limited and Contributors. License: BSD-3-clause -Files: plat/qemu/common/qemu_bl2_setup.c +Files: plat/qemu/common/qemu_bl2_mem_params_desc.c + plat/qemu/common/qemu_bl2_setup.c + plat/qemu/common/qemu_bl31_setup.c plat/qemu/common/qemu_common.c plat/qemu/common/qemu_image_load.c + plat/qemu/common/qemu_io_storage.c plat/qemu/common/qemu_private.h -Copyright: 2013-2023, Arm Limited and Contributors. + plat/qemu/common/qemu_realm_attest_key.c +Copyright: 2013-2024, Arm Limited and Contributors. +License: BSD-3-clause + +Files: plat/qemu/common/qemu_plat_attest_token.c +Copyright: 2024, Linaro Limited and Contributors. + 2022-2024, Arm Limited and Contributors. +License: BSD-3-clause + +Files: plat/qemu/common/trp/* +Copyright: 2013-2024, Arm Limited and Contributors. +License: BSD-3-clause + +Files: plat/qemu/common/trp/qemu_trp_setup.c +Copyright: 2015-2024, Arm Limited. License: BSD-3-clause Files: plat/qemu/qemu/* -Copyright: 2013-2023, Arm Limited and Contributors. +Copyright: 2013-2024, Arm Limited and Contributors. License: BSD-3-clause Files: plat/qemu/qemu/qemu_bl1_measured_boot.c -Copyright: 2022, Linaro. - 2022, 2023, Arm Limited. + plat/qemu/qemu/qemu_measured_boot.c +Copyright: 2022-2024, Arm Limited. + 2022, 2023, Linaro. License: BSD-3-clause Files: plat/qemu/qemu/qemu_helpers.c Copyright: 2022, Linaro. License: BSD-3-clause -Files: plat/qemu/qemu/qemu_measured_boot.c -Copyright: 2022, Arm Limited. - 2022, 2023, Linaro. +Files: plat/qemu/qemu/trp/* +Copyright: 2015-2024, Arm Limited. License: BSD-3-clause Files: plat/qemu/qemu_sbsa/* -Copyright: 2019-2023, Linaro Limited and Contributors. +Copyright: 2019-2024, Linaro Limited and Contributors. License: BSD-3-clause Files: plat/qemu/qemu_sbsa/sbsa_pm.c @@ -2696,20 +3242,19 @@ Copyright: 2018-2021, The Linux Foundation. 2013-2018, ARM Limited and Contributors. License: BSD-3-clause -Files: plat/qti/common/inc/qti_cpu.h - plat/qti/common/inc/qti_interrupt_svc.h - plat/qti/common/inc/qti_rng.h -Copyright: 2018-2021, The Linux Foundation. -License: BSD-3-clause - Files: plat/qti/common/inc/spmi_arb.h Copyright: 2020, 2022, 2023, Google LLC. License: BSD-3-clause -Files: plat/qti/common/src/aarch64/qti_kryo6_gold.S - plat/qti/common/src/aarch64/qti_kryo6_silver.S +Files: plat/qti/common/src/aarch64/* Copyright: 2018-2021, The Linux Foundation. - 2015-2018, Arm Limited and Contributors. + 2015-2024, Arm Limited and Contributors. +License: BSD-3-clause + +Files: plat/qti/common/src/aarch64/qti_helpers.S + plat/qti/common/src/aarch64/qti_uart_console.S +Copyright: 2018-2021, The Linux Foundation. + 2013-2018, ARM Limited and Contributors. License: BSD-3-clause Files: plat/qti/common/src/pm_ps_hold.c @@ -2717,16 +3262,6 @@ Files: plat/qti/common/src/pm_ps_hold.c Copyright: 2020, 2022, 2023, Google LLC. License: BSD-3-clause -Files: plat/qti/common/src/qti_pm.c -Copyright: 2018, ARM Limited and Contributors. - 2018, 2020, The Linux Foundation. -License: BSD-3-clause - -Files: plat/qti/common/src/qti_rng.c - plat/qti/common/src/qti_stack_protector.c -Copyright: 2018-2021, The Linux Foundation. -License: BSD-3-clause - Files: plat/qti/msm8916/aarch32/uartdm_console.S Copyright: 2021-2023, Stephan Gerhold <stephan@gerhold.net> 2015-2021, ARM Limited and Contributors. @@ -2738,7 +3273,7 @@ Copyright: 2021-2023, Stephan Gerhold <stephan@gerhold.net> License: BSD-3-clause Files: plat/qti/msm8916/include/plat_macros.S -Copyright: 2013-2023, ARM Limited and Contributors. +Copyright: 2013-2024, ARM Limited and Contributors. License: BSD-3-clause Files: plat/qti/msm8916/msm8916_topology.c @@ -2774,7 +3309,7 @@ License: BSD-3-clause Files: plat/qti/sc7280/inc/platform_def.h Copyright: 2018-2021, The Linux Foundation. - 2015-2018, Arm Limited and Contributors. + 2015-2024, Arm Limited and Contributors. License: BSD-3-clause Files: plat/qti/sc7280/inc/qti_map_chipinfo.h @@ -2783,43 +3318,105 @@ License: BSD-3-clause Files: plat/qti/sc7280/platform.mk Copyright: 2018-2021, The Linux Foundation. - 2015-2018, Arm Limited and Contributors. + 2015-2024, Arm Limited and Contributors. License: BSD-3-clause Files: plat/renesas/* -Copyright: 2015-2023, Renesas Electronics Corporation. +Copyright: 2015-2024, Renesas Electronics Corporation. License: BSD-3-clause Files: plat/renesas/common/aarch64/* -Copyright: 2015-2021, Renesas Electronics Corporation. - 2013-2023, Arm Limited and Contributors. +Copyright: 2015-2023, Renesas Electronics Corporation. + 2013-2024, Arm Limited and Contributors. License: BSD-3-clause Files: plat/renesas/common/aarch64/platform_common.c -Copyright: 2015-2020, Renesas Electronics Corporation. +Copyright: 2015-2023, Renesas Electronics Corporation. 2013, 2014, ARM Limited and Contributors. License: BSD-3-clause Files: plat/renesas/common/bl2_plat_mem_params_desc.c plat/renesas/common/plat_image_load.c -Copyright: 2013-2023, ARM Limited and Contributors. +Copyright: 2013-2024, ARM Limited and Contributors. License: BSD-3-clause Files: plat/renesas/common/bl31_plat_setup.c -Copyright: 2015-2020, Renesas Electronics Corporation. +Copyright: 2015-2023, Renesas Electronics Corporation. 2013, 2014, ARM Limited and Contributors. License: BSD-3-clause Files: plat/renesas/common/include/plat.ld.S -Copyright: 2013-2023, ARM Limited and Contributors. +Copyright: 2013-2024, ARM Limited and Contributors. +License: BSD-3-clause + +Files: plat/rockchip/common/include/plat_pm_helpers.h +Copyright: 2024, Rockchip, Inc. +License: BSD-3-clause + +Files: plat/rockchip/common/plat_pm_helpers.c +Copyright: 2024, Rockchip, Inc. +License: BSD-3-clause + +Files: plat/rockchip/common/scmi/* +Copyright: 2024, Rockchip, Inc. License: BSD-3-clause Files: plat/rockchip/rk3288/platform.mk -Copyright: 2013-2023, Arm Limited and Contributors. +Copyright: 2013-2024, Arm Limited and Contributors. +License: BSD-3-clause + +Files: plat/rockchip/rk3399/drivers/m0/Makefile +Copyright: 2013-2024, Arm Limited and Contributors. +License: BSD-3-clause + +Files: plat/rockchip/rk3588/* +Copyright: 2024, Rockchip, Inc. +License: BSD-3-clause + +Files: plat/rpi/* +Copyright: 2013-2024, Arm Limited and Contributors. +License: BSD-3-clause + +Files: plat/rpi/common/rpi3_console_dual.c + plat/rpi/common/rpi3_console_pl011.c + plat/rpi/common/rpi_pci_svc.c +Copyright: no-info-found +License: BSD-3-clause + +Files: plat/rpi/common/rpi3_image_load.c + plat/rpi/common/rpi3_io_storage.c + plat/rpi/common/rpi3_rotpk.S + plat/rpi/common/rpi3_stack_protector.c + plat/rpi/common/rpi3_trusted_boot.c +Copyright: 2013-2024, ARM Limited and Contributors. +License: BSD-3-clause + +Files: plat/rpi/rpi3/* +Copyright: 2013-2024, ARM Limited and Contributors. +License: BSD-3-clause + +Files: plat/rpi/rpi3/include/* +Copyright: 2013-2024, Arm Limited and Contributors. License: BSD-3-clause Files: plat/rpi/rpi3/platform.mk -Copyright: 2013-2023, Arm Limited and Contributors. +Copyright: 2013-2024, Arm Limited and Contributors. +License: BSD-3-clause + +Files: plat/rpi/rpi4/include/plat.ld.S +Copyright: 2013-2024, ARM Limited and Contributors. +License: BSD-3-clause + +Files: plat/rpi/rpi5/* +Copyright: no-info-found +License: BSD-3-clause + +Files: plat/rpi/rpi5/include/plat.ld.S +Copyright: 2013-2024, Arm Limited and Contributors. +License: BSD-3-clause + +Files: plat/socionext/synquacer/platform.mk +Copyright: 2013-2024, Arm Limited and Contributors. License: BSD-3-clause Files: plat/socionext/synquacer/sq_bl2_setup.c @@ -2830,18 +3427,24 @@ Files: plat/socionext/synquacer/sq_bl2_setup.c Copyright: 2019, 2020, 2022, Socionext Inc. License: BSD-3-clause +Files: plat/socionext/uniphier/platform.mk +Copyright: 2013-2024, Arm Limited and Contributors. +License: BSD-3-clause + Files: plat/socionext/uniphier/uniphier_console_setup.c Copyright: 2019, 2020, 2022, Socionext Inc. License: BSD-3-clause Files: plat/st/* -Copyright: 2014-2023, STMicroelectronics +Copyright: 2014-2024, STMicroelectronics License: BSD-3-clause Files: plat/st/common/bl2_io_storage.c plat/st/common/plat_image_load.c + plat/st/common/stm32mp_common.c plat/st/common/stm32mp_dt.c -Copyright: 2013-2023, Arm Limited and Contributors. + plat/st/common/stm32mp_gic.c +Copyright: 2013-2024, Arm Limited and Contributors. License: BSD-3-clause Files: plat/st/common/include/stm32mp_dt.h @@ -2854,116 +3457,91 @@ Copyright: 2022, STMicroelectronics 2021, Linaro Limited License: BSD-3-clause -Files: plat/st/common/stm32mp_common.c - plat/st/common/stm32mp_gic.c -Copyright: 2013-2023, ARM Limited and Contributors. -License: BSD-3-clause - Files: plat/st/stm32mp1/bl2_plat_setup.c - plat/st/stm32mp1/plat_bl2_mem_params_desc.c - plat/st/stm32mp1/stm32mp1.S - plat/st/stm32mp1/stm32mp1.ld.S - plat/st/stm32mp1/stm32mp1_helper.S + plat/st/stm32mp1/platform.mk + plat/st/stm32mp1/stm32mp1_def.h + plat/st/stm32mp1/stm32mp1_pm.c plat/st/stm32mp1/stm32mp1_private.c - plat/st/stm32mp1/stm32mp1_stack_protector.c - plat/st/stm32mp1/stm32mp1_tbb_cert.c - plat/st/stm32mp1/stm32mp1_topology.c -Copyright: 2013-2023, ARM Limited and Contributors. +Copyright: 2013-2024, Arm Limited and Contributors. License: BSD-3-clause Files: plat/st/stm32mp1/include/platform_def.h - plat/st/stm32mp1/include/stm32mp1_private.h -Copyright: 2013-2023, ARM Limited and Contributors. +Copyright: 2013-2024, ARM Limited and Contributors. +License: BSD-3-clause + +Files: plat/st/stm32mp1/include/stm32mp1_private.h +Copyright: 2013-2024, Arm Limited and Contributors. License: BSD-3-clause Files: plat/st/stm32mp1/include/tbbr/* -Copyright: 2013-2023, ARM Limited and Contributors. +Copyright: 2013-2024, ARM Limited and Contributors. License: BSD-3-clause -Files: plat/st/stm32mp1/platform.mk - plat/st/stm32mp1/stm32mp1_def.h - plat/st/stm32mp1/stm32mp1_pm.c -Copyright: 2013-2023, Arm Limited and Contributors. +Files: plat/st/stm32mp1/plat_bl2_mem_params_desc.c + plat/st/stm32mp1/stm32mp1.S + plat/st/stm32mp1/stm32mp1.ld.S + plat/st/stm32mp1/stm32mp1_helper.S + plat/st/stm32mp1/stm32mp1_stack_protector.c + plat/st/stm32mp1/stm32mp1_tbb_cert.c + plat/st/stm32mp1/stm32mp1_topology.c +Copyright: 2013-2024, ARM Limited and Contributors. License: BSD-3-clause Files: plat/st/stm32mp1/sp_min/* -Copyright: 2013-2023, ARM Limited and Contributors. +Copyright: 2013-2024, ARM Limited and Contributors. License: BSD-3-clause Files: plat/ti/k3/common/drivers/* -Copyright: 2018, Texas Instruments Incorporated - http://www.ti.com -License: BSD-3-clause - -Files: plat/ti/k3/common/drivers/ti_sci/* -Copyright: 2018-2022, Texas Instruments Incorporated - https://www.ti.com +Copyright: 2018-2024, Texas Instruments Incorporated - https://www.ti.com License: BSD-3-clause -Files: plat/xilinx/* -Copyright: 2022, 2023, Advanced Micro Devices, Inc. - 2013-2022, Arm Limited and Contributors. +Files: plat/ti/k3/common/drivers/sec_proxy/* +Copyright: 2018, Texas Instruments Incorporated - http://www.ti.com License: BSD-3-clause -Files: plat/xilinx/common/* -Copyright: 2022, 2023, Advanced Micro Devices, Inc. +Files: plat/ti/k3/common/k3_bl31_setup.c +Copyright: 2013-2024, Arm Limited and Contributors. License: BSD-3-clause -Files: plat/xilinx/common/include/* -Copyright: 2022, 2023, Advanced Micro Devices, Inc. - 2019-2022, Xilinx, Inc. +Files: plat/xilinx/* +Copyright: 2022-2024, Advanced Micro Devices, Inc. + 2013-2024, Arm Limited and Contributors. License: BSD-3-clause Files: plat/xilinx/common/include/ipi.h Copyright: 2018, 2019, Xilinx, Inc. License: BSD-3-clause -Files: plat/xilinx/common/include/plat_common.h - plat/xilinx/common/include/plat_console.h - plat/xilinx/common/include/plat_fdt.h -Copyright: 2022, 2023, Advanced Micro Devices, Inc. -License: BSD-3-clause - -Files: plat/xilinx/common/include/plat_startup.h - plat/xilinx/common/include/pm_common.h -Copyright: 2022, 2023, Advanced Micro Devices, Inc. - 2013-2022, Arm Limited and Contributors. +Files: plat/xilinx/common/include/pm_api_sys.h + plat/xilinx/common/include/pm_defs.h + plat/xilinx/common/include/pm_node.h + plat/xilinx/common/include/pm_svc_main.h +Copyright: 2022-2024, Advanced Micro Devices, Inc. + 2019-2022, Xilinx, Inc. License: BSD-3-clause Files: plat/xilinx/common/include/pm_client.h plat/xilinx/common/include/pm_ipi.h -Copyright: 2022, 2023, Advanced Micro Devices, Inc. +Copyright: 2022-2024, Advanced Micro Devices, Inc. 2017-2022, Xilinx, Inc. - 2013-2022, Arm Limited and Contributors. + 2013-2024, Arm Limited and Contributors. License: BSD-3-clause Files: plat/xilinx/common/ipi.c -Copyright: 2022, 2023, Advanced Micro Devices, Inc. +Copyright: 2022-2024, Advanced Micro Devices, Inc. 2017-2022, Xilinx, Inc. - 2013-2022, Arm Limited and Contributors. -License: BSD-3-clause - -Files: plat/xilinx/common/ipi_mailbox_service/* -Copyright: 2022, 2023, Advanced Micro Devices, Inc. - 2013-2022, Arm Limited and Contributors. -License: BSD-3-clause - -Files: plat/xilinx/common/ipi_mailbox_service/ipi_mailbox_svc.h -Copyright: 2013-2023, Arm Limited and Contributors. -License: BSD-3-clause - -Files: plat/xilinx/common/plat_startup.c -Copyright: 2022, 2023, Advanced Micro Devices, Inc. - 2013-2022, Arm Limited and Contributors. + 2013-2024, Arm Limited and Contributors. License: BSD-3-clause Files: plat/xilinx/common/pm_service/* -Copyright: 2022, 2023, Advanced Micro Devices, Inc. +Copyright: 2022-2024, Advanced Micro Devices, Inc. 2019-2022, Xilinx, Inc. License: BSD-3-clause Files: plat/xilinx/common/pm_service/pm_ipi.c -Copyright: 2022, 2023, Advanced Micro Devices, Inc. +Copyright: 2022-2024, Advanced Micro Devices, Inc. 2017-2022, Xilinx, Inc. - 2013-2022, Arm Limited and Contributors. + 2013-2024, Arm Limited and Contributors. License: BSD-3-clause Files: plat/xilinx/common/tsp/* @@ -2972,21 +3550,17 @@ Copyright: 2023, Advanced Micro Devices. License: BSD-3-clause Files: plat/xilinx/common/tsp/tsp.mk -Copyright: 2022, 2023, Advanced Micro Devices, Inc. -License: BSD-3-clause - -Files: plat/xilinx/versal/aarch64/versal_helpers.S -Copyright: 2013-2023, Arm Limited and Contributors. +Copyright: 2022-2024, Advanced Micro Devices, Inc. License: BSD-3-clause Files: plat/xilinx/versal/bl31_versal_setup.c -Copyright: 2022, 2023, Advanced Micro Devices, Inc. +Copyright: 2022-2024, Advanced Micro Devices, Inc. 2017-2022, Xilinx, Inc. - 2013-2022, Arm Limited and Contributors. + 2013-2024, Arm Limited and Contributors. License: BSD-3-clause Files: plat/xilinx/versal/include/plat_ipi.h -Copyright: 2022, 2023, Advanced Micro Devices, Inc. +Copyright: 2022-2024, Advanced Micro Devices, Inc. 2019-2022, Xilinx, Inc. License: BSD-3-clause @@ -2996,121 +3570,41 @@ License: BSD-3-clause Files: plat/xilinx/versal/include/plat_private.h plat/xilinx/versal/include/versal_def.h -Copyright: 2022, 2023, Advanced Micro Devices, Inc. +Copyright: 2022-2024, Advanced Micro Devices, Inc. 2017-2022, Xilinx, Inc. - 2013-2022, Arm Limited and Contributors. -License: BSD-3-clause - -Files: plat/xilinx/versal/plat_topology.c - plat/xilinx/versal/plat_versal.c -Copyright: 2013-2023, Arm Limited and Contributors. + 2013-2024, Arm Limited and Contributors. License: BSD-3-clause Files: plat/xilinx/versal/pm_service/* -Copyright: 2022, 2023, Advanced Micro Devices, Inc. +Copyright: 2022-2024, Advanced Micro Devices, Inc. 2019-2022, Xilinx, Inc. License: BSD-3-clause -Files: plat/xilinx/versal/tsp/* -Copyright: 2022, 2023, Advanced Micro Devices, Inc. -License: BSD-3-clause - Files: plat/xilinx/versal/versal_ipi.c -Copyright: 2022, 2023, Advanced Micro Devices, Inc. +Copyright: 2022-2024, Advanced Micro Devices, Inc. 2019-2022, Xilinx, Inc. License: BSD-3-clause Files: plat/xilinx/versal_net/* -Copyright: 2022, 2023, Advanced Micro Devices, Inc. - 2017-2022, Xilinx, Inc. - 2013-2022, Arm Limited and Contributors. -License: BSD-3-clause - -Files: plat/xilinx/versal_net/aarch64/* -Copyright: 2022, 2023, Advanced Micro Devices, Inc. - 2021, 2022, Arm Limited and Contributors. - 2018-2022, Xilinx, Inc. -License: BSD-3-clause - -Files: plat/xilinx/versal_net/aarch64/versal_net_helpers.S -Copyright: 2022, 2023, Advanced Micro Devices, Inc. +Copyright: 2022-2024, Advanced Micro Devices, Inc. 2017-2022, Xilinx, Inc. - 2013-2022, Arm Limited and Contributors. -License: BSD-3-clause - -Files: plat/xilinx/versal_net/include/plat_ipi.h - plat/xilinx/versal_net/include/plat_pm_common.h -Copyright: 2022, Xilinx, Inc. - 2022, 2023, Advanced Micro Devices, Inc. -License: BSD-3-clause - -Files: plat/xilinx/versal_net/include/versal_net_def.h -Copyright: 2022, Arm Limited and Contributors. - 2022, 2023, Advanced Micro Devices, Inc. - 2021, 2022, Xilinx, Inc. -License: BSD-3-clause - -Files: plat/xilinx/versal_net/plat_psci_pm.c - plat/xilinx/versal_net/versal_net_ipi.c -Copyright: 2022, Xilinx, Inc. - 2022, 2023, Advanced Micro Devices, Inc. -License: BSD-3-clause - -Files: plat/xilinx/versal_net/pm_service/* -Copyright: 2022, Xilinx, Inc. - 2022, 2023, Advanced Micro Devices, Inc. -License: BSD-3-clause - -Files: plat/xilinx/versal_net/tsp/* -Copyright: 2022, 2023, Advanced Micro Devices, Inc. -License: BSD-3-clause - -Files: plat/xilinx/zynqmp/aarch64/zynqmp_helpers.S -Copyright: 2013-2023, Arm Limited and Contributors. -License: BSD-3-clause - -Files: plat/xilinx/zynqmp/custom_sip_svc.c - plat/xilinx/zynqmp/libpm.mk -Copyright: 2022, 2023, Advanced Micro Devices, Inc. -License: BSD-3-clause - -Files: plat/xilinx/zynqmp/include/custom_svc.h -Copyright: 2022, 2023, Advanced Micro Devices, Inc. + 2013-2024, Arm Limited and Contributors. License: BSD-3-clause Files: plat/xilinx/zynqmp/include/plat_ipi.h plat/xilinx/zynqmp/include/platform_def.h -Copyright: 2022, 2023, Advanced Micro Devices, Inc. +Copyright: 2022-2024, Advanced Micro Devices, Inc. 2017-2022, Xilinx, Inc. - 2013-2022, Arm Limited and Contributors. -License: BSD-3-clause - -Files: plat/xilinx/zynqmp/include/plat_macros.S - plat/xilinx/zynqmp/include/zynqmp_def.h -Copyright: 2013-2023, Arm Limited and Contributors. -License: BSD-3-clause - -Files: plat/xilinx/zynqmp/plat_topology.c - plat/xilinx/zynqmp/plat_zynqmp.c -Copyright: 2013-2023, Arm Limited and Contributors. + 2013-2024, Arm Limited and Contributors. License: BSD-3-clause Files: plat/xilinx/zynqmp/platform.mk -Copyright: 2022, 2023, Advanced Micro Devices, Inc. +Copyright: 2022-2024, Advanced Micro Devices, Inc. 2021, 2022, ProvenRun S.A.S. 2018-2022, Xilinx, Inc. 2013-2021, Arm Limited and Contributors. License: BSD-3-clause -Files: plat/xilinx/zynqmp/pm_service/pm_api_clock.h - plat/xilinx/zynqmp/pm_service/pm_api_pinctrl.h -Copyright: 2013-2023, Arm Limited and Contributors. -License: BSD-3-clause - -Files: plat/xilinx/zynqmp/tsp/* -Copyright: 2013-2023, Arm Limited and Contributors. -License: BSD-3-clause - Files: plat/xilinx/zynqmp/zynqmp_ehf.c plat/xilinx/zynqmp/zynqmp_sdei.c Copyright: 2020, 2021, Siemens AG @@ -3118,22 +3612,40 @@ Copyright: 2020, 2021, Siemens AG License: BSD-3-clause Files: plat/xilinx/zynqmp/zynqmp_ipi.c -Copyright: 2022, 2023, Advanced Micro Devices, Inc. +Copyright: 2022-2024, Advanced Micro Devices, Inc. 2017-2022, Xilinx, Inc. - 2013-2022, Arm Limited and Contributors. + 2013-2024, Arm Limited and Contributors. License: BSD-3-clause Files: services/* -Copyright: 2013-2023, ARM Limited and Contributors. +Copyright: 2013-2024, ARM Limited and Contributors. License: BSD-3-clause Files: services/arm_arch_svc/* -Copyright: 2013-2023, Arm Limited and Contributors. +Copyright: 2013-2024, Arm Limited and Contributors. +License: BSD-3-clause + +Files: services/el3/* +Copyright: 2013-2024, Arm Limited and Contributors. +License: BSD-3-clause + +Files: services/oem/* +Copyright: 2024, The ChromiumOS Authors. +License: BSD-3-clause + +Files: services/spd/opteed/* +Copyright: 2013-2024, Arm Limited and Contributors. +License: BSD-3-clause + +Files: services/spd/opteed/opteed.mk + services/spd/opteed/opteed_helpers.S + services/spd/opteed/teesmc_opteed_macros.h +Copyright: 2013-2024, ARM Limited and Contributors. License: BSD-3-clause Files: services/spd/opteed/teesmc_opteed.h Copyright: 2014-2023, ARM Limited and Contributors. - 2014, Linaro Limited. + 2014, 2019, Linaro Limited. License: BSD-3-clause Files: services/spd/pncd/* @@ -3142,72 +3654,96 @@ License: BSD-3-clause Files: services/spd/pncd/pncd_private.h Copyright: 2021, 2022, ProvenRun S.A.S. - 2016-2022, ARM Limited and Contributors. + 2016-2024, ARM Limited and Contributors. License: BSD-3-clause Files: services/spd/tlkd/tlkd_main.c services/spd/tlkd/tlkd_pm.c Copyright: 2015-2023, NVIDIA Corporation. - 2013-2023, ARM Limited and Contributors. + 2013-2024, ARM Limited and Contributors. License: BSD-3-clause Files: services/spd/trusty/smcall.h - services/spd/trusty/trusty.c Copyright: 2015-2023, NVIDIA Corporation. - 2013-2023, ARM Limited and Contributors. + 2013-2024, ARM Limited and Contributors. +License: BSD-3-clause + +Files: services/spd/trusty/trusty.c +Copyright: 2020-2023, NVIDIA Corporation. + 2013-2024, Arm Limited and Contributors. License: BSD-3-clause Files: services/std_svc/drtm/* -Copyright: 2015-2023, Arm Limited. +Copyright: 2015-2024, Arm Limited. License: BSD-3-clause Files: services/std_svc/errata_abi/* -Copyright: 2013-2023, Arm Limited and Contributors. +Copyright: 2023, 2024, Arm Limited and Contributors. License: BSD-3-clause -Files: services/std_svc/rmmd/rmmd_attest.c -Copyright: 2015-2023, Arm Limited. +Files: services/std_svc/rmmd/* +Copyright: 2013-2024, Arm Limited and Contributors. +License: BSD-3-clause + +Files: services/std_svc/rmmd/aarch64/* +Copyright: 2013-2024, ARM Limited and Contributors. License: BSD-3-clause -Files: services/std_svc/rmmd/rmmd_initial_context.h - services/std_svc/rmmd/rmmd_main.c -Copyright: 2013-2023, Arm Limited and Contributors. +Files: services/std_svc/rmmd/rmmd.mk +Copyright: 2013-2024, ARM Limited and Contributors. License: BSD-3-clause -Files: services/std_svc/rmmd/trp/* -Copyright: 2013-2023, Arm Limited and Contributors. +Files: services/std_svc/rmmd/rmmd_attest.c +Copyright: 2021-2024, NVIDIA Corporation. + 2019-2024, Arm Limited. License: BSD-3-clause Files: services/std_svc/rmmd/trp/trp_entry.S services/std_svc/rmmd/trp/trp_helpers.c -Copyright: 2015-2023, Arm Limited. +Copyright: 2015-2024, Arm Limited. License: BSD-3-clause Files: services/std_svc/sdei/sdei_event.c -Copyright: 2013-2023, Arm Limited and Contributors. + services/std_svc/sdei/sdei_intr_mgmt.c +Copyright: 2013-2024, Arm Limited and Contributors. +License: BSD-3-clause + +Files: services/std_svc/spm/el3_spmc/spmc_setup.c +Copyright: 2013-2024, Arm Limited and Contributors. +License: BSD-3-clause + +Files: services/std_svc/spm/spm_mm/spm_mm_main.c +Copyright: 2013-2024, Arm Limited and Contributors. License: BSD-3-clause Files: services/std_svc/spm/spm_mm/spm_mm_setup.c -Copyright: 2015-2023, NVIDIA Corporation. - 2013-2023, ARM Limited and Contributors. +Copyright: 2020-2023, NVIDIA Corporation. + 2013-2024, Arm Limited and Contributors. License: BSD-3-clause -Files: services/std_svc/spmd/spmd_logical_sp.c - services/std_svc/spmd/spmd_main.c - services/std_svc/spmd/spmd_private.h -Copyright: 2013-2023, Arm Limited and Contributors. +Files: services/std_svc/spmd/* +Copyright: 2013-2024, Arm Limited and Contributors. +License: BSD-3-clause + +Files: services/std_svc/spmd/aarch64/* +Copyright: 2013-2024, ARM Limited and Contributors. +License: BSD-3-clause + +Files: services/std_svc/spmd/spmd.mk + services/std_svc/spmd/spmd_pm.c +Copyright: 2013-2024, ARM Limited and Contributors. License: BSD-3-clause Files: services/std_svc/std_svc_setup.c -Copyright: 2013-2023, Arm Limited and Contributors. +Copyright: 2013-2024, Arm Limited and Contributors. License: BSD-3-clause Files: services/std_svc/trng/trng_entropy_pool.c -Copyright: 2015-2023, ARM Limited. +Copyright: 2015-2024, ARM Limited. License: BSD-3-clause Files: tools/* -Copyright: 2013-2023, ARM Limited and Contributors. +Copyright: 2015-2024, Arm Limited. License: BSD-3-clause Files: tools/amlogic/* @@ -3215,94 +3751,118 @@ Copyright: 2019, Remi Pommarel <repk@triplefau.lt> License: BSD-3-clause Files: tools/cert_create/* -Copyright: 2014-2023, Arm Limited and Contributors. -License: BSD-3-clause and/or OpenSSL - -Files: tools/cert_create/include/* -Copyright: 2013-2023, ARM Limited and Contributors. +Copyright: 2013-2024, ARM Limited and Contributors. License: BSD-3-clause +Files: tools/cert_create/Makefile +Copyright: 2014-2024, Arm Limited and Contributors. +License: BSD-3-clause or OpenSSL + Files: tools/cert_create/include/cca/* -Copyright: 2015-2023, Arm Limited. +Copyright: 2015-2024, Arm Limited. License: BSD-3-clause Files: tools/cert_create/include/dualroot/* -Copyright: 2015-2023, Arm Limited. +Copyright: 2015-2024, Arm Limited. License: BSD-3-clause Files: tools/cert_create/include/ext.h tools/cert_create/include/key.h -Copyright: 2013-2023, Arm Limited and Contributors. +Copyright: 2013-2024, Arm Limited and Contributors. License: BSD-3-clause Files: tools/cert_create/src/* -Copyright: 2013-2023, Arm Limited and Contributors. +Copyright: 2013-2024, Arm Limited and Contributors. License: BSD-3-clause Files: tools/cert_create/src/cca/* -Copyright: 2015-2023, Arm Limited. +Copyright: 2015-2024, Arm Limited. License: BSD-3-clause Files: tools/cert_create/src/cmd_opt.c tools/cert_create/src/ext.c -Copyright: 2013-2023, ARM Limited and Contributors. +Copyright: 2013-2024, ARM Limited and Contributors. License: BSD-3-clause Files: tools/cert_create/src/dualroot/* -Copyright: 2015-2023, Arm Limited. +Copyright: 2015-2024, Arm Limited. License: BSD-3-clause Files: tools/cert_create/src/tbbr/* -Copyright: 2013-2023, ARM Limited and Contributors. +Copyright: 2013-2024, ARM Limited and Contributors. +License: BSD-3-clause + +Files: tools/cert_create/src/tbbr/tbb_key.c +Copyright: 2013-2024, Arm Limited and Contributors. License: BSD-3-clause Files: tools/cert_create/src/tbbr/tbbr.mk -Copyright: 2015-2023, Arm Limited. +Copyright: 2015-2024, Arm Limited. License: BSD-3-clause Files: tools/conventional-changelog-tf-a/* Copyright: no-info-found -License: BSD-3-clause +License: BSD-3-Clause Files: tools/conventional-changelog-tf-a/index.js -Copyright: 2015-2023, Arm Limited. +Copyright: 2015-2024, Arm Limited. License: BSD-3-clause -Files: tools/encrypt_fw/* -Copyright: 2019-2022, Linaro Limited. -License: BSD-3-clause and/or OpenSSL +Files: tools/cot_dt2c/cot_dt2c/* +Copyright: 2013-2024, Arm Limited and Contributors. +License: BSD-3-clause -Files: tools/encrypt_fw/include/* -Copyright: 2019, Linaro Limited. - 2015, ARM Limited and Contributors. +Files: tools/cot_dt2c/cot_dt2c/__init__.py + tools/cot_dt2c/cot_dt2c/__main__.py +Copyright: 2015-2024, Arm Limited. License: BSD-3-clause -Files: tools/encrypt_fw/include/debug.h -Copyright: 2013-2023, ARM Limited and Contributors. +Files: tools/cot_dt2c/tests/test_util.py +Copyright: 2013-2024, Arm Limited and Contributors. License: BSD-3-clause -Files: tools/encrypt_fw/include/encrypt.h +Files: tools/encrypt_fw/* Copyright: 2019, 2020, Linaro Limited. License: BSD-3-clause -Files: tools/encrypt_fw/src/* -Copyright: 2019, 2020, Linaro Limited. +Files: tools/encrypt_fw/Makefile +Copyright: 2024, Arm Limited. + 2019-2022, Linaro Limited. +License: BSD-3-clause or OpenSSL + +Files: tools/encrypt_fw/include/cmd_opt.h +Copyright: 2014-2023, ARM Limited and Contributors. + 2014, 2019, Linaro Limited. +License: BSD-3-clause + +Files: tools/encrypt_fw/include/debug.h +Copyright: 2013-2024, ARM Limited and Contributors. License: BSD-3-clause Files: tools/encrypt_fw/src/cmd_opt.c -Copyright: 2013-2023, ARM Limited and Contributors. +Copyright: 2013-2024, ARM Limited and Contributors. +License: BSD-3-clause + +Files: tools/encrypt_fw/src/main.c +Copyright: 2024, Arm Limited and Contributors. + 2019, Linaro Limited. License: BSD-3-clause Files: tools/fiptool/Makefile -Copyright: 2014-2023, Arm Limited and Contributors. -License: BSD-3-clause and/or OpenSSL +Copyright: 2014-2024, Arm Limited and Contributors. +License: BSD-3-clause or OpenSSL -Files: tools/fiptool/Makefile.msvc -Copyright: 2015-2023, Arm Limited. +Files: tools/fiptool/fiptool.c + tools/fiptool/win_posix.c + tools/fiptool/win_posix.h +Copyright: 2013-2024, Arm Limited and Contributors. License: BSD-3-clause -Files: tools/fiptool/plat_fiptool/* -Copyright: 2015-2023, Arm Limited. +Files: tools/fiptool/fiptool.h + tools/fiptool/fiptool_platform.h + tools/fiptool/tbbr_config.c + tools/fiptool/tbbr_config.h +Copyright: 2013-2024, ARM Limited and Contributors. License: BSD-3-clause Files: tools/fiptool/plat_fiptool/arm/board/tc/plat_fiptool.mk @@ -3311,41 +3871,40 @@ Copyright: 2022, 2023, Arm Limited. License: BSD-3-clause Files: tools/fiptool/plat_fiptool/nxp/* -Copyright: 2022, 2023, Arm Limited. - 2021, NXP. +Copyright: 2016-2024, NXP License: BSD-3-clause -Files: tools/fiptool/plat_fiptool/nxp/plat_def_uuid_config.c -Copyright: 2016-2023, NXP +Files: tools/fiptool/plat_fiptool/nxp/plat_fiptool.mk +Copyright: 2022, 2023, Arm Limited. + 2021, NXP. License: BSD-3-clause Files: tools/fiptool/plat_fiptool/st/* -Copyright: 2014-2023, STMicroelectronics -License: BSD-3-clause - -Files: tools/fiptool/win_posix.c - tools/fiptool/win_posix.h -Copyright: 2013-2023, Arm Limited and Contributors. +Copyright: 2014-2024, STMicroelectronics License: BSD-3-clause -Files: tools/marvell/* -Copyright: 2016-2021, Marvell International Ltd. -License: BSD-3-clause - -Files: tools/memory/* -Copyright: 2015-2023, Arm Limited. +Files: tools/marvell/doimage/Makefile + tools/marvell/doimage/doimage.c + tools/marvell/doimage/doimage.mk +Copyright: 2016-2024, Marvell International Ltd. License: BSD-3-clause Files: tools/nxp/* -Copyright: 2016-2023, NXP +Copyright: 2016-2024, NXP License: BSD-3-clause Files: tools/renesas/* +Copyright: 2015-2024, Renesas Electronics Corporation. +License: BSD-3-clause + +Files: tools/renesas/rcar_layout_create/makefile Copyright: 2015-2023, Renesas Electronics Corporation. + 2013-2024, Arm Limited and Contributors. License: BSD-3-clause -Files: tools/sptool/* -Copyright: 2015-2023, Arm Limited. +Files: tools/renesas/rzg_layout_create/makefile +Copyright: 2015-2023, Renesas Electronics Corporation. + 2013-2024, Arm Limited and Contributors. License: BSD-3-clause Files: tools/sptool/sptool.py @@ -3353,99 +3912,162 @@ Copyright: 2022, The Hafnium Authors. 2022, Arm Limited. License: BSD-3-clause +Files: tools/stm32image/* +Copyright: 2013-2024, Arm Limited and Contributors. +License: BSD-3-clause + Files: tools/stm32image/stm32image.c -Copyright: 2014-2023, STMicroelectronics +Copyright: 2014-2024, STMicroelectronics License: BSD-3-clause -Files: .husky/pre-commit.copyright dco.txt docs/about/release-information.rst docs/change-log.md docs/components/activity-monitors.rst docs/components/cot-binding.rst docs/components/romlib-design.rst docs/design/alt-boot-flows.rst docs/design/index.rst docs/design_documents/drtm_poc.rst docs/design_documents/measured_boot.rst docs/design_documents/measured_boot_poc.rst docs/design_documents/rss.rst docs/getting_started/psci-lib-integration-guide.rst docs/getting_started/rt-svc-writers-guide.rst docs/index.rst docs/perf/performance-monitoring-unit.rst docs/perf/psci-performance-juno.rst docs/plat/stm32mp1.rst docs/porting-guide.rst docs/process/code-review-guidelines.rst docs/process/coding-guidelines.rst docs/process/commit-style.rst docs/process/platform-ports-policy.rst docs/resources/diagrams/PSA-FWU.dia docs/resources/diagrams/int_handling.dia docs/resources/diagrams/measured_boot_design.dia docs/resources/diagrams/reset_code_flow.dia docs/resources/diagrams/rmm_cold_boot_generic.dia docs/resources/diagrams/romlib_design.dia docs/resources/diagrams/romlib_wrapper.dia docs/resources/diagrams/xlat_align.dia docs/threat_model/index.rst plat/arm/board/common/rotpk/arm_rotpk_rsa.der readme.rst +Files: .husky/pre-commit.copyright dco.txt docs/about/maintainers.rst docs/about/release-information.rst docs/change-log.md docs/components/activity-monitors.rst docs/components/cot-binding.rst docs/components/romlib-design.rst docs/design/alt-boot-flows.rst docs/design/index.rst docs/design_documents/drtm_poc.rst docs/design_documents/measured_boot.rst docs/design_documents/measured_boot_poc.rst docs/design_documents/rse.rst docs/getting_started/psci-lib-integration-guide.rst docs/getting_started/rt-svc-writers-guide.rst docs/index.rst docs/perf/performance-monitoring-unit.rst docs/perf/psci-performance-juno.rst docs/plat/index.rst docs/plat/nxp/index.rst docs/plat/stm32mp1.rst docs/porting-guide.rst docs/process/code-review-guidelines.rst docs/process/coding-guidelines.rst docs/process/commit-style.rst docs/process/platform-ports-policy.rst docs/resources/diagrams/PSA-FWU.dia docs/resources/diagrams/int_handling.dia docs/resources/diagrams/measured_boot_design.dia docs/resources/diagrams/reset_code_flow.dia docs/resources/diagrams/rmm_cold_boot_generic.dia docs/resources/diagrams/romlib_design.dia docs/resources/diagrams/romlib_wrapper.dia docs/resources/diagrams/xlat_align.dia docs/threat_model/firmware_threat_model/index.rst docs/threat_model/index.rst plat/arm/board/common/rotpk/arm_rotpk_rsa.der Copyright: 1989 Regents of the University of California. - 1998 Softweyr LLC. All rights reserved. + 1998 Softweyr LLC. 2001 David E. O'Brien 2005 MontaVista Software, Inc. 2005 Nokia Corporation 2011 The FreeBSD Foundation 2012-2021 Roberto E. Vargas Caballero - 2013-2023 ARM Limited and Contributors. All rights reserved. - 2013-2023 Arm Limited and Contributors. All rights reserved. - 2014-2023 Arm Limited. All rights reserved. - 2014-2016 Freescale Semiconductor, Inc. - 2014 Linaro Limited. All rights reserved. - 2014-2023 STMicroelectronics - All Rights Reserved + 2013-2024 ARM Limited and Contributors. + 2013-2024 Arm Limited and Contributors. + 2014 Linaro Limited. 2014 STMicroelectronics International N.V. + 2014-2016 Freescale Semiconductor, Inc. + 2014-2023 Arm Limited. + 2014-2023 STMicroelectronics 2015-2021 Broadcom - 2015-2023 NVIDIA Corporation. All rights reserved. - 2015-2023 Renesas Electronics Corporation. - 2015-2023 Renesas Electronics Corporation. All rights - 2015-2023 Renesas Electronics Corporation. All rights reserved. - 2016-2020 Marvell International Ltd. - 2016-2022 Linaro Limited. All rights reserved. + 2015-2022 Xilinx Inc. + 2015-2023 NVIDIA Corporation. + 2015-2024 Arm Limited. + 2015-2024 Renesas Electronics Corporation + 2015-2024 STMicroelectronics 2016-2021 Marvell International Ltd. + 2016-2022 Linaro Limited. 2016-2023 NXP - 2017-2020 Arm Limited and Contributors. All rights reserved. - 2017-2023 NVIDIA CORPORATION. All rights reserved. + 2016-2024 ARM Limited and Contributors. + 2016-2024 STMicroelectronics + 2017-2020 Arm Limited and Contributors. 2017-2022 NXP Semiconductors + 2017-2022 Xilinx, Inc. + 2017-2023 NVIDIA CORPORATION. 2017-2023 Nuvoton Ltd. 2017-2023 Nuvoton Technology Corp. - 2017-2022 Xilinx, Inc. All rights reserved. 2018 Andre Przywara <osp@andrep.de> - 2018-2020 Arm Limited and Contributors. 2018 Icenowy Zheng <icenowy@aosc.io> 2018-2019 Linaro - 2018-2022 Texas Instruments Incorporated - https://www.ti.com/ - 2018-2021 The Linux Foundation. All rights reserved. - 2019-2023 STMicroelectronics - All Rights Reserved - 2019-2020 Broadcom. + 2018-2020 Arm Limited and Contributors. + 2018-2021 The Linux Foundation. + 2018-2022 Xilinx Inc. + 2018-2024 Arm Limited and Contributors. + 2018-2024 Arm Limited. + 2018-2024 Marvell International Ltd. + 2018-2024 STMicroelectronics + 2018-2024 Texas Instruments Incorporated - https://www.ti.com/ 2019 Carlo Caione <ccaione@baylibre.com> - 2019-2023 Intel Corporation. All rights reserved. - 2019-2022 Linaro Limited - 2019-2020 Linaro Limited and Contributors. - 2019-2023 Linaro Limited and Contributors. All rights reserved. - 2019-2023 MediaTek Inc. All rights reserved. - 2019-2022 NXP. All rights reserved. 2019 Remi Pommarel <repk@triplefau.lt> 2019 Repk repk@triplefau.lt - 2019-2022 Socionext Inc. All rights reserved. 2019 Ying-Chun Liu (PaulLiu) <paul.liu@linaro.org> - 2020-2022 ARM Limited and Contributors. All rights reserved. - 2020-2023 Google LLC. All rights reserved. + 2019-2020 Broadcom. + 2019-2020 Linaro Limited and Contributors. + 2019-2021 2024 NXP + 2019-2022 Linaro Limited + 2019-2022 Linaro Limited. + 2019-2022 NXP. + 2019-2022 Socionext Inc. + 2019-2023 Intel Corporation. + 2019-2023 Linaro Limited and Contributors. + 2019-2023 MediaTek Inc. + 2019-2023 STMicroelectronics + 2019-2024 Arm Limited and Contributors. + 2019-2024 NXP + 2019-2024 STMicroelectronics 2020 Marek Behun, CZ.NIC - 2020 Marvell Technology Group Ltd. All rights reserved. - 2020 MediaTek Inc. All rights reserved. + 2020 Marvell Technology Group Ltd. + 2020 MediaTek Inc. 2020 NXP. 2020 Nuvia Inc 2020-2021 Sartura Ltd. 2020-2021 Siemens AG - 2021 Xilinx Inc. + 2020-2023 Google LLC + 2020-2024 Arm Limited and Contributors. + 2020-2024 Arm Limited. + 2020-2024 MediaTek Inc. + 2020-2024 NXP 2021 Arm 2021 Arm Limited 2021 Globalscale technologies, Inc. 2021 Marek Behun <marek.behun@nic.cz> - 2021-2022 ProvenRun S.A.S. All rights reserved. 2021 Semihalf. 2021 Sipeed + 2021 Xilinx Inc. + 2021-2022 ProvenRun S.A.S. + 2021-2022 Xilinx Inc. + 2021-2023 Renesas Electronics Corporation. 2021-2023 Stephan Gerhold <stephan@gerhold.net> - 2022-2023 Advanced Micro Devices, Inc. All rights reserved. - 2022-2023 Arm Ltd. All rights reserved. - 2022 Fujitsu Limited and Contributors. All rights reserved. + 2021-2024 ARM Limited and Contributors. + 2021-2024 ARM Limited. + 2021-2024 Arm Limited and Contributors. + 2021-2024 Arm Limited. + 2021-2024 MediaTek Inc. + 2021-2024 NXP + 2021-2024 STMicroelectronics + 2022 Advanced Micro Devices Inc. + 2022 Arm Limited and Contributors. + 2022 Fujitsu Limited and Contributors. 2022 Leica Geosystems AG 2022 Linaro - 2022-2023 Linaro. - 2022 Mediatek Inc. All rights reserved. - 2022 Qualcomm Innovation Center, Inc. All rights reserved. + 2022 Mediatek Inc. + 2022 Qualcomm Innovation Center, Inc. 2022 The Hafnium Authors. - 2023 Advanced Micro Devices. All rights reserved. - 2023 Arm Limited. All rights reserved + 2022 Xilinx Inc. + 2022-2023 Advanced Micro Devices, Inc. + 2022-2023 Arm Ltd. + 2022-2023 Linaro. + 2022-2024 ARM Limited and Contributors. + 2022-2024 Advanced Micro Devices Inc. + 2022-2024 Arm Limited and Contributors. + 2022-2024 Arm Limited. + 2022-2024 Arm Ltd. + 2022-2024 MediaTek Inc. + 2022-2024 STMicroelectronics + 2023 ARM Limited and Contributors. + 2023 Advanced Micro Devices. + 2023 Arm Limited. 2023 Aspeed Technology Inc. - 2023 Pengutronix. All rights reserved. + 2023 MediaTek Inc. + 2023 Pengutronix. + 2023-2024 Advanced Micro Devices Inc. + 2023-2024 Arm Limited and Contributors. + 2023-2024 Arm Limited. + 2023-2024 Arm Ltd. + 2023-2024 Linaro Limited and Contributors. + 2023-2024 NXP + 2023-2024 STMicroelectronics + 2024 ARM Limited and Contributors. + 2024 ARM Limited. + 2024 Altera Corporation. + 2024 Arm Limited and Contributors. + 2024 Arm Limited. + 2024 Arm Ltd. + 2024 Intel Corporation. + 2024 Linaro Limited and Contributors. + 2024 Mario Bălănică <mariobalanica02@gmail.com> + 2024 Mediatek Inc. + 2024 NVIDIA Corporation. + 2024 NXP + 2024 Pengutronix Inc. + 2024 Rockchip Inc. + 2024 STMicroelectronics + 2024 The ChromiumOS Authors. License: BSD-3-Clause Files: fdts/arm_fpga.dts fdts/n1sdp-multi-chip.dts fdts/n1sdp-single-chip.dts fdts/n1sdp.dtsi Copyright: - 2017-2021 ARM Limited and Contributors. All rights reserved. + 2017-2021 ARM Limited and Contributors. 2018-2022 STMicroelectronics 2019-2022 Arm Limited. - 2020 Arm Limited. All rights reserved. + 2020 Arm Limited. License: GPL-2.0 or BSD-3-Clause Files: fdts/rtsm_ve-motherboard.dtsi @@ -3456,17 +4078,17 @@ License: GPL-2.0 OR MIT Files: fdts/stm32mp151a-prtt1a-fw-config.dts fdts/stm32mp151a-prtt1a.dts fdts/stm32mp157a-avenger96.dts fdts/stm32mp157c-lxa-mc1.dts fdts/stm32mp157c-odyssey-som.dtsi fdts/stm32mp157c-odyssey.dts fdts/stm32mp15xx-osd32.dtsi Copyright: 2017-2022 STMicroelectronics - 2017-2023 STMicroelectronics - All Rights Reserved + 2017-2023 STMicroelectronics 2019 Arrow Electronics 2019 Linaro Ltd 2019-2020 Marek Vasut <marex@denx.de> - 2019 STMicroelectronics. All Rights Reserved. + 2019 STMicroelectronics. 2020 Ahmad Fatoum, Pengutronix - 2020 DH electronics - All Rights Reserved + 2020 DH electronics 2021 Grzegorz Szymaszek. 2022 DH electronics GmbH 2023 Oleksij Rempel <kernel@pengutronix.de>, Pengutronix - 2023 Protonic Holland - All Rights Reserved + 2023 Protonic Holland License: GPL-2.0+ OR BSD-3-Clause Files: fdts/stm32mp15xx-dhcor-io1v8.dtsi @@ -3474,7 +4096,7 @@ Copyright: 2019 Linaro Ltd 2020 Marek Vasut <marex@denx.de> 2022 DH electronics GmbH - 2023 STMicroelectronics - All Rights Reserved + 2023 STMicroelectronics License: GPL-2.0 OR BSD-3-Clause Files: include/lib/libfdt/libfdt.h lib/libfdt/fdt_addresses.c lib/libfdt/fdt_overlay.c -- GitLab